问题
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