Cython binary package compile issues

家住魔仙堡 提交于 2021-02-11 18:24:46

问题


I would like to compile a python3 package into a distributable binary form (without sourcecode) from within the x64 Native Tools Command Prompt for VS 2019 using python 3.6 (64bit). However, i have problems specifying the correct paths to the files the package should contain. The produced folder structure in the site-packages directory is all wrong and not what i expect from my source folder structure and my setup.py. It seems for me that the modules from the top level package are treated differently, but i don't know exactly why this is the case.

My dir structure is the following:

.
├── hello
│   ├── hello1.py
│   ├── hello2.py
│   └── __init__.py
│   bye
│   ├── bye1.py
│   ├── bye2.py
│   └── __init__.py
├── setup.py
└── test
    └── test.py

The __init__.py from the hello package contains the line from . import hello1, hello2, bye, the __init__.py from the bye package contains the line from . import bye1, bye2. The hello1.py and hello2.py contains a function print_hello and print_hello2, respectively, the bye1.py and bye2.py the functions print_bye as well as print_bye2.

To accomplish the compilation i set-up the following setup.py:

from setuptools import Extension, setup, find_packages
from Cython.Build import cythonize

setup(name='hello',
      version='0.3',
      url='https://someurl',
      license='somelicense',
      author='Pepper Wutz',
      author_email='wutz@gmail.com',
      description='Prints "Hello World"',
      ext_modules=cythonize([Extension("hello", ["hello/*.py"]), Extension("hello.bye", ["hello/bye/*.py"]), language_level=3),
      zip_safe=False)

When i run this module from the top_level folder:

py -3.6 setup.py bdist_wheel

and then install it (from within the tests subfolder):

py -3.6 -m pip install dist/hello-0.3-cp36-cp36m-win_amd64.whl

i obtain the following output files:

Verzeichnis von C:\Users\User\AppData\Roaming\Python\Python36\site-packages

18.01.2021  17:32    <DIR>          hello
18.01.2021  17:32    <DIR>          hello-0.3.dist-info
18.01.2021  17:32            20.480 hello.cp36-win_amd64.pyd
               1 Datei(en),         20.480 Bytes
               2 Verzeichnis(se), 15.070.900.224 Bytes frei

Verzeichnis von C:\Users\User\AppData\Roaming\Python\Python36\site-packages\hello

18.01.2021  17:32    <DIR>          .
18.01.2021  17:32    <DIR>          ..
18.01.2021  17:32            20.480 bye.cp36-win_amd64.pyd
               1 Datei(en),         20.480 Bytes
               2 Verzeichnis(se), 15.070.142.464 Bytes frei

Obviously, the folder structure of the hello package in the site-packages strongly differs from my original folder structure. E.g, the hello.cp36-win_amd64.pyd is put into the top-level scope and the bye package is put into the hello subdirectory respectively subscope. The hello1 and hello2 modules are not put into the hello package, but only the bye package. This completely messes up the relative imports which work when running the code from the interpreter:

C:\Users\User\source\repos\cython_tests\test>py -3.6 -c "import hello; print(dir(hello))"
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "hello\__init__.py", line 1, in init hello
ImportError: attempted relative import with no known parent package

Suspiciously, the hello folder containing the hello sources has only the __init__.c file but not hello1.c and hello2.c. The same goes for the hello\bye folder.

18.01.2021  17:27    <DIR>          .
18.01.2021  17:27    <DIR>          ..
18.01.2021  17:27    <DIR>          bye
18.01.2021  10:58               147 hello1.py
15.01.2021  20:11               150 hello2.py
18.01.2021  17:27           103.044 __init__.c
18.01.2021  17:20                35 __init__.py
               4 Datei(en),        103.376 Bytes
               3 Verzeichnis(se), 15.068.041.216 Bytes frei

I'm simply lost in writing the appropriate setup.py, especially specifying the ext_modules. I don't see what i did wrong. Maybe some other sees it. What i want to obtain is a binary package which i can import using import hello and from which i can call the functions in hello1 and hello2 like this: hello.hello1.print_hello(), hello.hello2.print_hello2() as well as hello.bye.bye1.print_bye(), hello.bye.bye2.print_bye2().


回答1:


What you probably want to do is create a package containing an extension module for for each .py file.

setup.py would contain:

      ext_modules=cythonize([
           Extension("hello.hello1", ["hello/hello1.py"]),
           Extension("hello.hello2", ["hello/hello2.py"]),
           Extension("bye.bye1", ["bye/bye1.py"]),
           Extension("bye.bye2", ["bye/bye2.py"]), language_level=3),

I've skipped the __init__.py files because there's usually little value in compiling them with Cython, and you have to work around a setuptools bug on Windows.

The upshot is that Python imports extension modules by searching for the module name first, then seeing if it has a PyInit_<module_name> function to call. When Cython compiles a file (e.g. hello1.py) it'll therefore create a PyInit_hello1 function.

Therefore your supposed combined "hello" module ends up containing PyInit_hello1, PyInit_hello2, but no PyInit_hello and so doesn't import.

If you really want to bundle multiple modules together into a single .so file then you can follow the instructions in Collapse multiple submodules to one Cython extension. You'll note that it isn't a built-in feature and it involves a lot of fine details of the Python import mechanism. Alternatively you could use some third-party tools designed to automated this (e.g. https://github.com/smok-serwis/snakehouse). I don't recommend this because it's complicated and likely a bit fragile.



来源:https://stackoverflow.com/questions/65772805/cython-binary-package-compile-issues

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!