为什么添加 __init__.py 会改变 Cython build_ext --inplace 行为?

Why does adding an __init__.py change Cython build_ext --inplace behavior?

这是我遇到的问题的玩具版本。作为大型项目的一部分,我正在尝试使用 setuptools 就地编译一些 Cython 代码。

我的顶层目录是 test。它包含:

hello_world.pyx

def say_hi():
     print('meh')

和setup.py

from setuptools import setup
from Cython.Build import cythonize

setup(
    ext_modules=cythonize("hello.pyx"),
)

如果我使用 python3 setup.py build_ext --inplace 就地构建这些模块,一切都会按预期进行。

但是如果在我的 topdir 添加一个空的 __init__.py,我会得到以下错误:

copying build/lib.linux-x86_64-3.8/Code/test/hello.cpython-38-x86_64-linux-gnu.so -> Code/test
error: could not create 'Code/test/hello.cpython-38-x86_64-linux-gnu.so': No such file or directory

为什么会这样?有没有标准的修复方法?我希望这些已编译的 Cython 模块与其他一些仍在开发中的 C/Python 代码一起使用。

what is happening?

关于正在发生的事情的完整描述可以在这个 GitHub 问题中找到:https://github.com/pypa/setuptools/issues/3343#issuecomment-1143527672

TL;DR:当您在顶级文件夹中添加 __init__.py 文件时,它会干扰 Cython 计算扩展名称的方式。这会导致名称不正确(由于缺少文件夹而与您的项目布局不兼容)并导致 setuptools 崩溃。

And is there a standard way of fixing it?

如果您真的想将 __init__.py 文件保留在您的 top-level 项目文件夹下[^1],您可以显式使用 Extension 名称为 proper/correct 的对象而不是依赖于 Cython 使用的自动命名算法:

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

extensions = [Extension("hello", ["hello.pyx"])]

setup(
    ext_modules=cythonize(extensions),
)

[^1] 请注意,这在 Python 包装生态系统中并不常见。