微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Cython 二进制包编译问题

如何解决Cython 二进制包编译问题

我想从 x64 Native Tools Command Prompt for VS 2019 使用 python 3.6(64 位)将 python3 包编译为可分发的二进制形式(无源代码)。但是,我在指定包应包含的文件的正确路径时遇到问题。 site-packages 目录中生成文件夹结构都是错误的,而不是我对源文件夹结构和 setup.py 的期望。 在我看来,顶级包中的模块被区别对待,但我不知道为什么会这样。

我的目录结构如下:

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

__init__.py 包中的 hello 包含行 from . import hello1,hello2,bye__init__.py 包中的 bye 包含行 from . import bye1,bye2hello1.pyhello2.py 分别包含函数 print_helloprint_hello2bye1.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

然后安装它(从测试子文件夹中):

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

我获得以下输出文件

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),15.070.142.464 Bytes frei

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

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",in init hello
ImportError: attempted relative import with no kNown parent package

令人怀疑的是,包含 hello 源的 hello 文件夹只有 __init__.c 文件,而没有 hello1.chello2.chello\bye 文件夹也是如此。

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() 以及 {{ 1}}、hello.bye.bye1.print_bye()

解决方法

您可能想要做的是为每个 .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_hello1PyInit_hello2,但没有 PyInit_hello,因此不会导入。

如果您真的想将多个模块捆绑到一个 .so 文件中,那么您可以按照 Collapse multiple submodules to one Cython extension 中的说明进行操作。您会注意到它不是一个内置功能,它涉及 Python 导入机制的许多细节。或者,您可以使用一些旨在自动执行此操作的第三方工具(例如 https://github.com/smok-serwis/snakehouse)。我不建议这样做,因为它很复杂,而且可能有点脆弱。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。