【问题标题】:Cython binary package compile issuesCython 二进制包编译问题
【发布时间】:2021-04-22 15:35:13
【问题描述】:

我想使用 python 3.6(64 位)从 x64 Native Tools Command Prompt for VS 2019 中将 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_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)

当我从顶层文件夹运行这个模块时:

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

可疑的是,包含 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() 以及hello.bye.bye1.print_bye()hello.bye.bye2.print_bye2()

【问题讨论】:

  • 我曾建议 this question 重复,但仔细一看,我认为这不是您想要的。
  • 对于这个问题以及由此引起的刺激感到非常抱歉。今天,我在这个编译问题上投入了几个小时,向前走了两步,向后走,以此类推。 @DavidW:我不介意将每个 py 模块编译为单个 .so(或 .pyd),只要维护包层次结构并且该过程不会向站点包的根目录发送垃圾邮件。

标签: python-3.x compilation cython setuptools binaryfiles


【解决方案1】:

您可能想要做的是为每个 .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_&lt;module_name&gt; 函数。当 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)。我不推荐这个,因为它很复杂而且可能有点脆弱。

【讨论】:

  • 首先感谢您的回答!我看到我的通配符使用可能是不当行为的原因,因为它自动包含了非预期的 init.py 文件。但是,当我有多个文件时,如何解决通配符的使用?我是否需要使用 os 模块扫描目录和文件来构建扩展对象?
  • 如果我这样做,我可能会使用 os 模块扫描目录和文件。我不知道是否支持任何更具体的通配符语法。也许[a-zA-Z]*.py 但我怀疑它不起作用?
  • 根据文档支持 linux 风格的 glob 模式。也不要认为这会有所帮助。想想,我明天要实现那个目录扫描器。
猜你喜欢
  • 2017-01-22
  • 1970-01-01
  • 2019-07-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多