From ce34077e7b836c44eea657d188883249b5b93b91 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Piotr=20Ma=C5=9Blanka?= <piotr.maslanka@henrietta.com.pl>
Date: Wed, 18 Mar 2020 20:28:59 +0100
Subject: [PATCH] Feature/template (#3)

* templating

* templating

* templating

* fixed templating

* fixed templating

* fixed noargs
---
 MANIFEST.in                   |  1 +
 setup.py                      |  4 ++
 snakehouse/bootstrap.template | 53 ++++++++++++++++++++++++++
 snakehouse/cdef.template      |  2 +
 snakehouse/constants.py       | 70 +----------------------------------
 snakehouse/initpy.template    |  2 +
 snakehouse/multibuild.py      | 29 ++++++++-------
 7 files changed, 78 insertions(+), 83 deletions(-)
 create mode 100644 snakehouse/bootstrap.template
 create mode 100644 snakehouse/cdef.template
 create mode 100644 snakehouse/initpy.template

diff --git a/MANIFEST.in b/MANIFEST.in
index 85e39f5..5b2a10f 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 203e2a9..c797b8e 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 0000000..2df11b8
--- /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 0000000..0ff30ea
--- /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 df9665c..ff69234 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 0000000..20a0916
--- /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 7a499c8..2cda25f 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)
-- 
GitLab