Cython二进制包编译问题



我想使用python 3.6(64位)从VS 2019x64 Native Tools命令提示符内将python3包编译成可分发的二进制形式(没有源代码)。但是,我在指定包应该包含的文件的正确路径时遇到了问题。site-packages目录中生成的文件夹结构都是错误的,而不是我对源文件夹结构和setup.py的期望。对我来说,似乎来自顶层包的模块被不同地对待,但我不知道为什么会这样。

目录结构如下:

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

hello包中的__init__.py包含from . import hello1, hello2, bye行,bye包中的__init__.py包含from . import bye1, bye2行。hello1.pyhello2.py分别包含print_helloprint_hello2函数,bye1.pybye2.py包含print_byeprint_bye2函数。

为了完成编译,我设置了以下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)

当我从top_level文件夹中运行这个模块时:

py -3.6 setup.py bdist_wheel

,然后安装它(从tests子文件夹中):

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

得到如下输出文件:

Verzeichnis von C:UsersUserAppDataRoamingPythonPython36site-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:UsersUserAppDataRoamingPythonPython36site-packageshello
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

显然,site-packages中hello包的文件夹结构与我原来的文件夹结构有很大的不同。例如,将hello.cp36-win_amd64.pyd放入顶级作用域,将bye包分别放入hello子目录的子作用域。hello1hello2模块不放在hello包中,只放在bye包中。这完全搞乱了从解释器运行代码时工作的相对导入:

C:UsersUsersourcereposcython_teststest>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

值得怀疑的是,包含hello源代码的hello文件夹中只有__init__.c文件,而没有hello1.chello2.c文件。hellobye文件夹也是如此。

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

我只是迷失在写适当的setup.py,特别是指定ext_modules。我不知道我做错了什么。也许其他人看到了。我想要获得的是一个二进制包,我可以使用import hello导入,从中我可以调用hello1和hello2中的函数,像这样:hello.hello1.print_hello(),hello.hello2.print_hello2()以及hello.bye.bye1.print_bye(),hello.bye.bye2.print_bye2()

您可能想做的是为每个.py文件创建一个包含扩展模块的包。

setup.py包含:

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),

我跳过了__init__.py文件,因为在Cython中编译它们通常没有什么价值,而且你必须绕过Windows上的setuptools错误。

结果是Python通过首先搜索模块名称来导入扩展模块,然后查看它是否有一个PyInit_<module_name>函数可以调用。因此,当Cython编译一个文件(例如hello1.py)时,它将创建一个PyInit_hello1函数。

因此你假定的组合"hello"模块最终包含PyInit_hello1,PyInit_hello2,但没有PyInit_hello,因此不导入。

如果你真的想要将多个模块捆绑到一个。so文件中,那么你可以按照下面的说明将多个子模块合并到一个Cython扩展中。您会注意到它不是一个内置特性,它涉及到Python导入机制的许多细节。或者,你也可以使用一些第三方工具来实现自动化(例如https://github.com/smok-serwis/snakehouse)。我不建议这样做,因为它很复杂,而且可能有点脆弱。

相关内容

  • 没有找到相关文章

最新更新