diff --git a/MANIFEST.in b/MANIFEST.in index 85e39f5c4beafc6059d93f11c4384c71a2756074..5b2a10f6436e7e872a60783ac226cd7dd05ec924 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,3 +1,4 @@ include LICENSE include README.md include CONTRIBUTORS.md +include snakehouse/*.template diff --git a/setup.py b/setup.py index 203e2a939cbfa3c903a06eff41e0f04b509a1b01..c797b8e32debb9395a8e70fdca578bd61348c1f3 100644 --- a/setup.py +++ b/setup.py @@ -9,4 +9,8 @@ setup(keywords=['cython', 'extension', 'multiple', 'pyx'], 'Cython' ], python_requires='!=2.7.*,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*', + package_data={ + 'snakehouse': ['*.template'] + }, + package_dir={'snakehouse': 'snakehouse'}, ) diff --git a/snakehouse/bootstrap.template b/snakehouse/bootstrap.template new file mode 100644 index 0000000000000000000000000000000000000000..2df11b8002e94239c572027e5fd049272a25e79c --- /dev/null +++ b/snakehouse/bootstrap.template @@ -0,0 +1,53 @@ +cdef extern from "Python.h": + ctypedef struct PyModuleDef: + const char* m_name; + + void Py_INCREF(object) + object PyModule_FromDefAndSpec(PyModuleDef *definition, object spec) + int PyModule_ExecDef(object module, PyModuleDef* definition) + +{cdef_section} + +cdef object get_definition_by_name(str name): +{get_definition_section} + +import sys + +cdef class CythonPackageLoader: + cdef PyModuleDef* definition + cdef object def_o + cdef str name + + def __init__(self, name): + self.def_o = get_definition_by_name(name) + self.definition = <PyModuleDef*>self.def_o + self.name = name + Py_INCREF(self.def_o) + + def load_module(self, fullname): + raise ImportError + + def create_module(self, spec): + if spec.name != self.name: + raise ImportError() + return PyModule_FromDefAndSpec(self.definition, spec) + + def exec_module(self, module): + PyModule_ExecDef(module, self.definition) + + +class CythonPackageMetaPathFinder: + def __init__(self, modules_set): + self.modules_set = modules_set + + def find_module(self, fullname, path): + if fullname not in self.modules_set: + return None + return CythonPackageLoader(fullname) + + def invalidate_caches(self): + pass + +def bootstrap_cython_submodules(): + modules_set = {module_set} + sys.meta_path.append(CythonPackageMetaPathFinder(modules_set)) diff --git a/snakehouse/cdef.template b/snakehouse/cdef.template new file mode 100644 index 0000000000000000000000000000000000000000..0ff30ea3714eb10a2926a4eb42de5bac1d5f7d22 --- /dev/null +++ b/snakehouse/cdef.template @@ -0,0 +1,2 @@ +cdef extern from "%s": + object PyInit_%s() diff --git a/snakehouse/constants.py b/snakehouse/constants.py index df9665c43eafbcdc5d9985abe80d10cc6e532a46..ff692348da9aa90b284df34b80e519d232d9753a 100644 --- a/snakehouse/constants.py +++ b/snakehouse/constants.py @@ -3,74 +3,6 @@ import typing as tp logger = logging.getLogger(__name__) -BOOTSTRAP_PYX_HEADER = """ -cdef extern from "Python.h": - ctypedef struct PyModuleDef: - const char* m_name; - - void Py_INCREF(object) - object PyModule_FromDefAndSpec(PyModuleDef *definition, object spec) - int PyModule_ExecDef(object module, PyModuleDef* definition) - -""" - -BOOTSTRAP_PYX_PACKAGE_LOADER = """ -import sys - -cdef class CythonPackageLoader: - cdef PyModuleDef* definition - cdef object def_o - cdef str name - - def __init__(self, name): - self.def_o = get_definition_by_name(name) - self.definition = <PyModuleDef*>self.def_o - self.name = name - Py_INCREF(self.def_o) - - def load_module(self, fullname): - raise ImportError - - def create_module(self, spec): - if spec.name != self.name: - raise ImportError() - return PyModule_FromDefAndSpec(self.definition, spec) - - def exec_module(self, module): - PyModule_ExecDef(module, self.definition) - - -class CythonPackageMetaPathFinder: - def __init__(self, modules_set): - self.modules_set = modules_set - - def find_module(self, fullname, path): - if fullname not in self.modules_set: - return None - return CythonPackageLoader(fullname) - - def invalidate_caches(self): - pass - -def bootstrap_cython_submodules(): - modules_set = %s - sys.meta_path.append(CythonPackageMetaPathFinder(modules_set)) - -""" - -INIT_PY_CONTENTS = """ -from %s.__bootstrap__ import bootstrap_cython_submodules -bootstrap_cython_submodules() -""" - -BOOTSTRAP_PYX_CDEF = """ -cdef extern from "%s": - object PyInit_%s() -""" - -BOOTSTRAP_PYX_GET_DEFINITION_HEADER = """ -cdef object get_definition_by_name(str name): -""" BOOTSTRAP_PYX_GET_DEFINITION_IF = """ if name == %s: return %s @@ -82,5 +14,5 @@ BOOTSTRAP_PYX_GET_DEFINITION_ELIF = """ elif name == %s: INCLUDE_PYTHON_H = '#include "Python.h"\n' -INCLUDE_PYINIT = 'PyObject* PyInit_%s();' +INCLUDE_PYINIT = 'PyObject* PyInit_%s(void);' diff --git a/snakehouse/initpy.template b/snakehouse/initpy.template new file mode 100644 index 0000000000000000000000000000000000000000..20a09167ca8d172a4e29a7e3e5df17ba862e9f8a --- /dev/null +++ b/snakehouse/initpy.template @@ -0,0 +1,2 @@ +from {module_name}.__bootstrap__ import bootstrap_cython_submodules +bootstrap_cython_submodules() diff --git a/snakehouse/multibuild.py b/snakehouse/multibuild.py index 7a499c82f1478e77e3d33b22bfa43c8f22d3cdae..2cda25fbec87f0b7474c30c15985ce80d74abe94 100644 --- a/snakehouse/multibuild.py +++ b/snakehouse/multibuild.py @@ -1,8 +1,7 @@ import os - +import pkg_resources from setuptools import Extension -from .constants import BOOTSTRAP_PYX_HEADER, BOOTSTRAP_PYX_PACKAGE_LOADER, INIT_PY_CONTENTS, \ - BOOTSTRAP_PYX_CDEF, BOOTSTRAP_PYX_GET_DEFINITION_HEADER, BOOTSTRAP_PYX_GET_DEFINITION_IF, \ +from .constants import BOOTSTRAP_PYX_GET_DEFINITION_IF, \ BOOTSTRAP_PYX_GET_DEFINITION_ELIF, INCLUDE_PYTHON_H, INCLUDE_PYINIT @@ -53,7 +52,8 @@ class Multibuild: f_out.write(data) def generate_bootstrap(self) -> str: - bootstrap_contents = [BOOTSTRAP_PYX_HEADER] + bootstrap_contents = pkg_resources.resource_string('snakehouse', 'bootstrap.template').decode('utf8') + cdef_section = [] for filename in self.pyx_files: path, name = os.path.split(filename) if path.startswith(self.bootstrap_directory): @@ -64,7 +64,8 @@ class Multibuild: replace('\\', '\\\\') else: h_path_name = name.replace('.pyx', '.h') - bootstrap_contents.append(BOOTSTRAP_PYX_CDEF % (h_path_name, module_name)) + cdef_template = pkg_resources.resource_string('snakehouse', 'cdef.template').decode('utf8') + cdef_section.append(cdef_template % (h_path_name, module_name)) if path: complete_module_name = self.extension_name+'.'+'.'.join(path[1:].split( @@ -74,25 +75,25 @@ class Multibuild: self.modules.add((complete_module_name, 'PyInit_%s()' % (module_name, ))) - bootstrap_contents.append(BOOTSTRAP_PYX_GET_DEFINITION_HEADER) + get_definition = [] modules = iter(self.modules) mod_name, init_fun_name = next(modules) - bootstrap_contents.append(BOOTSTRAP_PYX_GET_DEFINITION_IF % (repr(mod_name), init_fun_name)) + get_definition.append(BOOTSTRAP_PYX_GET_DEFINITION_IF % (repr(mod_name), init_fun_name)) for mod_name, init_fun_name in modules: - bootstrap_contents.append(BOOTSTRAP_PYX_GET_DEFINITION_ELIF % ( + get_definition.append(BOOTSTRAP_PYX_GET_DEFINITION_ELIF % ( repr(mod_name), init_fun_name)) - bootstrap_contents.append('\n') - bootstrap_contents.append(BOOTSTRAP_PYX_PACKAGE_LOADER % ( - repr(set(x[0] for x in self.modules)), )) - - return ''.join(bootstrap_contents) + return bootstrap_contents.format(cdef_section=''.join(cdef_section), + get_definition_section=''.join(get_definition), + module_set=repr(set(x[0] for x in self.modules))) def write_bootstrap_file(self): with open(os.path.join(self.bootstrap_directory, '__bootstrap__.pyx'), 'w') as f_out: f_out.write(self.generate_bootstrap()) def alter_init(self): + pyinit_contents = pkg_resources.resource_string('snakehouse', 'initpy.template').decode('utf8') + if os.path.exists(os.path.join(self.bootstrap_directory, '__init__.py')): with open(os.path.join(self.bootstrap_directory, '__init__.py'), 'r') as f_in: data = f_in.read() @@ -100,7 +101,7 @@ class Multibuild: data = '' if 'bootstrap_cython_submodules' not in data: - data = (INIT_PY_CONTENTS % (self.extension_name, )) + data + data = pyinit_contents.format(module_name=self.extension_name) + data with open(os.path.join(self.bootstrap_directory, '__init__.py'), 'w') as f_out: f_out.write(data)