From 46b521dfbffb090ccca64ab6a1e0da3dec513f1b Mon Sep 17 00:00:00 2001 From: Flaviu Tamas Date: Mon, 10 Sep 2018 16:11:51 -0400 Subject: [PATCH] Build hunspell in py3 The verbose old-style intialization is used because GCC 4.8 & MSVC don't support the nicer `.foo = bar` style of initialization. --- setup/build.py | 27 ++-- src/calibre/utils/spell/hunspell_wrapper.cpp | 127 +++++++++++-------- 2 files changed, 91 insertions(+), 63 deletions(-) diff --git a/setup/build.py b/setup/build.py index 6b5ee5216c..369c6f4c54 100644 --- a/setup/build.py +++ b/setup/build.py @@ -38,12 +38,16 @@ class Extension(object): if iswindows: self.cflags.append('/DCALIBRE_MODINIT_FUNC=PyMODINIT_FUNC') else: - if self.needs_cxx: - self.cflags.append('-DCALIBRE_MODINIT_FUNC=extern "C" __attribute__ ((visibility ("default"))) void') - else: - self.cflags.append('-DCALIBRE_MODINIT_FUNC=__attribute__ ((visibility ("default"))) void') - if kwargs.get('needs_c99'): - self.cflags.insert(0, '-std=c99') + return_type = 'PyObject*' if sys.version_info >= (3,) else 'void' + extern_decl = 'extern "C"' if self.needs_cxx else '' + + self.cflags.append( + '-DCALIBRE_MODINIT_FUNC=' + '{} __attribute__ ((visibility ("default"))) {}'.format(extern_decl, return_type)) + + if not self.needs_cxx and kwargs.get('needs_c99'): + self.cflags.insert(0, '-std=c99') + self.ldflags = d['ldflags'] = kwargs.get('ldflags', []) self.optional = d['options'] = kwargs.get('optional', False) of = kwargs.get('optimize_level', None) @@ -162,20 +166,21 @@ def init_env(): if islinux: cflags.append('-pthread') ldflags.append('-shared') - cflags.append('-I'+sysconfig.get_python_inc()) - ldflags.append('-lpython'+sysconfig.get_python_version()) if isbsd: cflags.append('-pthread') ldflags.append('-shared') - cflags.append('-I'+sysconfig.get_python_inc()) - ldflags.append('-lpython'+sysconfig.get_python_version()) if ishaiku: cflags.append('-lpthread') ldflags.append('-shared') + + if islinux or isbsd or ishaiku: cflags.append('-I'+sysconfig.get_python_inc()) - ldflags.append('-lpython'+sysconfig.get_python_version()) + # getattr(..., 'abiflags') is for PY2 compat, since PY2 has no abiflags + # member + ldflags.append('-lpython{}{}'.format( + sysconfig.get_config_var('VERSION'), getattr(sys, 'abiflags', ''))) if isosx: cflags.append('-D_OSX') diff --git a/src/calibre/utils/spell/hunspell_wrapper.cpp b/src/calibre/utils/spell/hunspell_wrapper.cpp index 09b3bdaf65..87dc7083b2 100644 --- a/src/calibre/utils/spell/hunspell_wrapper.cpp +++ b/src/calibre/utils/spell/hunspell_wrapper.cpp @@ -53,7 +53,7 @@ dealloc(Dictionary *self) { if (self->handle != NULL) delete self->handle; /* We do not free encoding, since it is managed by hunspell */ self->encoding = NULL; self->handle = NULL; - self->ob_type->tp_free((PyObject *)self); + Py_TYPE(self)->tp_free((PyObject *)self); } static PyObject * @@ -112,9 +112,15 @@ remove_word(Dictionary *self, PyObject *args) { static PyMethodDef HunSpell_methods[] = { {"recognized", (PyCFunction)recognized, METH_VARARGS, - "Checks the spelling of the given word. The word must be a unicode object. If encoding of the word to the encoding of the dictionary fails, a UnicodeEncodeError is raised. Returns False if the input word is not recognized."}, + "Checks the spelling of the given word. The word must be a unicode " + "object. If encoding of the word to the encoding of the dictionary fails, " + "a UnicodeEncodeError is raised. Returns False if the input word is not " + "recognized."}, {"suggest", (PyCFunction)suggest, METH_VARARGS, - "Provide suggestions for the given word. The input word must be a unicode object. If encoding of the word to the encoding of the dictionary fails, a UnicodeEncodeError is raised. Returns the list of suggested words as unicode objects."}, + "Provide suggestions for the given word. The input word must be a unicode " + "object. If encoding of the word to the encoding of the dictionary fails, " + "a UnicodeEncodeError is raised. Returns the list of suggested words as " + "unicode objects."}, {"add", (PyCFunction)add, METH_VARARGS, "Adds the given word into the runtime dictionary"}, {"remove", (PyCFunction)remove_word, METH_VARARGS, @@ -123,65 +129,82 @@ static PyMethodDef HunSpell_methods[] = { }; static PyTypeObject DictionaryType = { - PyObject_HEAD_INIT(NULL) - 0, /* ob_size */ - "Dictionary", /* tp_name */ - sizeof(Dictionary), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor) dealloc, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_compare */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - "Dictionary object", /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - HunSpell_methods, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - (initproc) init_type, /* tp_init */ - 0, /* tp_alloc */ - 0, /* tp_new */ + PyVarObject_HEAD_INIT(NULL, 0) + /* tp_name */ "Dictionary", + /* tp_basicsize */ sizeof(Dictionary), + /* tp_itemsize */ 0, + /* tp_dealloc */ (destructor) dealloc, + /* tp_print */ 0, + /* tp_getattr */ 0, + /* tp_setattr */ 0, + /* tp_compare */ 0, + /* tp_repr */ 0, + /* tp_as_number */ 0, + /* tp_as_sequence */ 0, + /* tp_as_mapping */ 0, + /* tp_hash */ 0, + /* tp_call */ 0, + /* tp_str */ 0, + /* tp_getattro */ 0, + /* tp_setattro */ 0, + /* tp_as_buffer */ 0, + /* tp_flags */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + /* tp_doc */ "Dictionary object", + /* tp_traverse */ 0, + /* tp_clear */ 0, + /* tp_richcompare */ 0, + /* tp_weaklistoffset */ 0, + /* tp_iter */ 0, + /* tp_iternext */ 0, + /* tp_methods */ HunSpell_methods, + /* tp_members */ 0, + /* tp_getset */ 0, + /* tp_base */ 0, + /* tp_dict */ 0, + /* tp_descr_get */ 0, + /* tp_descr_set */ 0, + /* tp_dictoffset */ 0, + /* tp_init */ (initproc) init_type, + /* tp_alloc */ 0, + /* tp_new */ 0, }; +#if PY_MAJOR_VERSION >= 3 +#define INITERROR return NULL +static struct PyModuleDef hunspell_module = { + /* m_base */ PyModuleDef_HEAD_INIT, + /* m_name */ "hunspell", + /* m_doc */ "A wrapper for the hunspell spell checking library", + /* m_size */ -1, + /* m_methods */ 0, + /* m_slots */ 0, + /* m_traverse */ 0, + /* m_clear */ 0, + /* m_free */ 0, +}; -CALIBRE_MODINIT_FUNC -inithunspell(void) { - PyObject *mod; - - // Create the module - mod = Py_InitModule3("hunspell", NULL, - "A wrapper for the hunspell spell checking library"); - if (mod == NULL) return; +CALIBRE_MODINIT_FUNC PyInit_hunspell(void) { + PyObject *mod = PyModule_Create(&hunspell_module); +#else +#define INITERROR return +CALIBRE_MODINIT_FUNC inithunspell(void) { + PyObject *mod = Py_InitModule3("hunspell", NULL, + "A wrapper for the hunspell spell checking library"); +#endif + if (mod == NULL) INITERROR; HunspellError = PyErr_NewException((char*)"hunspell.HunspellError", NULL, NULL); - if (HunspellError == NULL) return; + if (HunspellError == NULL) INITERROR; PyModule_AddObject(mod, "HunspellError", HunspellError); // Fill in some slots in the type, and make it ready DictionaryType.tp_new = PyType_GenericNew; - if (PyType_Ready(&DictionaryType) < 0) return; + if (PyType_Ready(&DictionaryType) < 0) INITERROR; // Add the type to the module. Py_INCREF(&DictionaryType); PyModule_AddObject(mod, "Dictionary", (PyObject *)&DictionaryType); + +#if PY_MAJOR_VERSION >= 3 + return mod; +#endif }