Collapse multiple submodules to one Cython extension
First off, I should note that it's impossible to compile a single .so
file with sub packages using Cython. So if you want sub packages, you're going to have to generate multiple .so
files, as each .so
can only represent a single module.
Second, it doesn't appear that you can compile multiple Cython/Python files (I'm using the Cython language specifically) and link them into a single module at all.
I've tried to compile multiply Cython files into a single .so
every which way, both with distutils
and with manual compilation, and it always fails to import at runtime.
It seems that it's fine to link a compiled Cython file with other libraries, or even other C files, but something goes wrong when linking together two compiled Cython files, and the result isn't a proper Python extension.
The only solution I can see is to compile everything as a single Cython file. In my case, I've edited my setup.py
to generate a single .pyx
file which in turn include
s every .pyx
file in my source directory:
includesContents = ""
for f in os.listdir("src-dir"):
if f.endswith(".pyx"):
includesContents += "include \"" + f + "\"\n"
includesFile = open("src/extension-name.pyx", "w")
includesFile.write(includesContents)
includesFile.close()
Then I just compile extension-name.pyx
. Of course this breaks incremental and parallel compilation, and you could end up with extra naming conflicts since everything gets pasted into the same file. On the bright side, you don't have to write any .pyd
files.
I certainly wouldn't call this a preferable build method, but if everything absolutely has to be in one extension module, this is the only way I can see to do it.
How to build multiple Cython files into one python extension
Turn out it is pretty easy. I even don't need to change the setup.py
I just moved the children classes' code to their own files and include them from the parent class.
It would became something like this
- Child1.pyx
- Child2.pyx
- Child3.pyx
- MyLibrary.pyx
and in the MyLibrary.pyx
.
.
.
include Child1.pyx
include Child2.pyx
include Child3.pyx
class parent:
.
.
.
Calling a cython library with multiple pyx files through c++
There's a number of similar looking questions about bundling multiple Cython modules together (e.g. 1, 2) which isn't really viable because Python uses file paths to handle modules. However, this question isn't quite the same because you're calling it from C++, which gives you an extra option.
You need to use the C API function PyImport_AppendInittab
to Python to treat impl_file1
as a builtin module so it doesn't search the path for a file to import. Start by providing a declaration of the import function (since you won't get that from your header file):
extern "C" {
// PyObject* PyInit_impl_file1(); // Python 3
void initimpl_file1(); // Python 2
}
Then, in main
, before Py_Initialize
, add:
PyImport_AppendInittab("impl_file1", initimpl_file1); // change the name for Python 3
project structure for wrapping many c++ classes in cython to a single shared object
While waiting for a definitive answer, I kept playing around with organizing my code. The including of pyx
files into a single pyx
for compilation has been working so far.
My setup.py
is simple like:
ext_modules = [
Extension(
"openni",
["src/openni.pyx"],
language="c++",
include_dirs=['src/', '/usr/include/ni'],
libraries=['OpenNI'],
)
],
The main openni.pyx
looks like:
include "constants.pyx"
include "exceptions.pyx"
include "context.pyx"
...
I have a common libopenni.pxd
to provide declaration-only externs to the rest of the modules.
I name my cppclass
declarations a different pxd
name than the pyx
class definitions to avoid name collision:
xncontext.pxd
cdef extern from "XnCppWrapper.h" namespace "xn":
cdef cppclass Context:
...
context.pyx:
from libopenni cimport *
from xncontext cimport Context as c_Context
cdef class Context:
cdef c_Context *handle
...
Related Topics
How to Find the Last Occurrence of an Item in a Python List
Break // in X Axis of Matplotlib
How to Verify If One List Is a Subset of Another
Using Pandas .Append Within for Loop
How to Pass a Default Argument Value of an Instance Member to a Method
How to Append One String to Another in Python
Moving Matplotlib Legend Outside of the Axis Makes It Cutoff by the Figure Box
Converting String with Utc Offset to a Datetime Object
Differencebetween Slice Assignment That Slices the Whole List and Direct Assignment
What Does It Mean to "Call" a Function in Python
Zip Variable Empty After First Use
List to Dictionary Conversion with Multiple Values Per Key
How to Make a Tkinter Window Jump to the Front
Making Python Loggers Output All Messages to Stdout in Addition to Log File