构建 Python 个包含多个 Cython 扩展的包
Building Python package containing multiple Cython extensions
我的目录结构如下:
testcython/
setup.py
testcython/
__init__.py
foo.pyx
stuff.py
bar/
__init__.pxd
__init__.py
bar.pxd
bar.pyx
其中文件内容如下:
bar.pxd
# cython: language_level=3
cdef int square(int x)
bar.pyx
# cython: language_level=3
cdef int square(int x):
return x * x
foo.pyx
# cython: language_level=3
import cython
cimport numpy as np
import numpy as np
from .Bar cimport square
def do_square(x):
return square(x)
stuff.py
from __future__ import print_function
from .Foo import do_square
def do():
print(do_square(2))
setup.py
import os, sys
from Cython.Build import build_ext, cythonize
from setuptools import setup, Extension, find_packages
def ext_modules():
import numpy as np
include_dirs = ['.', np.get_include()]
root_dir = os.path.abspath(os.path.dirname(__file__))
bar_ext = Extension(
"Bar",
sources=[root_dir + "/testcython/bar/bar.pyx"],
include_dirs=include_dirs,
)
foo_ext = Extension(
"Foo",
sources=[root_dir + "/testcython/foo.pyx"],
include_dirs=include_dirs
)
exts = [bar_ext, foo_ext]
return cythonize(exts)
REQUIREMENTS = [
"numpy",
"cython"
]
setup(
name="testcython",
packages=find_packages(),
ext_package="testcython",
ext_modules=ext_modules(),
cmdclass={"build_ext" : build_ext},
zip_safe=False,
install_requires=REQUIREMENTS
)
问题
问题是,当我尝试安装它时(pip install -e .
在顶部 testcython
目录中),我从 Cython 收到以下错误:
Complete output from command python setup.py egg_info:
Error compiling Cython file:
------------------------------------------------------------
...
import cython
cimport numpy as np
import numpy as np
from .Bar cimport square
^
------------------------------------------------------------
testcython/foo.pyx:7:0: relative cimport beyond main package is not allowed
Error compiling Cython file:
------------------------------------------------------------
...
import numpy as np
from .Bar cimport square
def do_square(x):
return square(x)
^
------------------------------------------------------------
这个答案 () 意味着在 Extension
对象的 include_dirs
参数中包含根目录 ('.'
) 应该可以解决问题.
虽然 this part of the Cython documentation 提到在使用 setuptools
包时在 setup
的参数中使用 zip_safe=False
。
正如您从我上面的 setup.py
文件中看到的那样,我已经包含了这两个 - 但我仍然收到上面的错误。
注意: 如果我将扩展名(在 Extension
构造函数中)从 Bar
和 Foo
更改为 testcython.Bar
和 testcython.Foo
,然后我得到一个不同的错误:
Complete output from command python setup.py egg_info:
Error compiling Cython file:
------------------------------------------------------------
...
import cython
cimport numpy as np
import numpy as np
from .Bar cimport square
^
------------------------------------------------------------
testcython/foo.pyx:7:0: 'testcython/Bar/square.pxd' not found
Error compiling Cython file:
------------------------------------------------------------
...
import numpy as np
from .Bar cimport square
def do_square(x):
return square(x)
^
------------------------------------------------------------
我在同事的帮助下解决了这个问题,所以我会在这里提及解决方案,以防将来对人们有所帮助。
问题与 Cython 模块的导入方式有关,更具体地说 - .so
文件在构建扩展时的放置位置。最初,Bar.so
文件是在 testcython
目录中生成的 - 因此当尝试从 bar
sub-module 导入时,找不到相应的共享对象文件。
为了解决这个问题,我需要在创建此扩展时使用名称 "bar.bar"
,这会导致 .so
文件生成到 testcython/bar
目录中。然后,在 foo.pyx
中,要使用此 bar
模块中的成员,必须将导入更改为 from testcython.bar.bar cimport <name>
。
注:
此外,问题中显示的函数 square
不能以这种形式从另一个 Cython 模块使用,因为没有 __pyx_capi__
是免费 cdef
函数生成的。相反,此函数必须作为静态方法包装在一些 cdef
class 中,以便从另一个 Cython 模块使用它,即:
cdef class Square:
@staticmethod
cdef int square(int x)
然后可以导入,例如foo.pyx
,用from testcython.bar.bar cimport Square
。 class Square
本质上就像 "namespace".
我的目录结构如下:
testcython/
setup.py
testcython/
__init__.py
foo.pyx
stuff.py
bar/
__init__.pxd
__init__.py
bar.pxd
bar.pyx
其中文件内容如下:
bar.pxd
# cython: language_level=3
cdef int square(int x)
bar.pyx
# cython: language_level=3
cdef int square(int x):
return x * x
foo.pyx
# cython: language_level=3
import cython
cimport numpy as np
import numpy as np
from .Bar cimport square
def do_square(x):
return square(x)
stuff.py
from __future__ import print_function
from .Foo import do_square
def do():
print(do_square(2))
setup.py
import os, sys
from Cython.Build import build_ext, cythonize
from setuptools import setup, Extension, find_packages
def ext_modules():
import numpy as np
include_dirs = ['.', np.get_include()]
root_dir = os.path.abspath(os.path.dirname(__file__))
bar_ext = Extension(
"Bar",
sources=[root_dir + "/testcython/bar/bar.pyx"],
include_dirs=include_dirs,
)
foo_ext = Extension(
"Foo",
sources=[root_dir + "/testcython/foo.pyx"],
include_dirs=include_dirs
)
exts = [bar_ext, foo_ext]
return cythonize(exts)
REQUIREMENTS = [
"numpy",
"cython"
]
setup(
name="testcython",
packages=find_packages(),
ext_package="testcython",
ext_modules=ext_modules(),
cmdclass={"build_ext" : build_ext},
zip_safe=False,
install_requires=REQUIREMENTS
)
问题
问题是,当我尝试安装它时(pip install -e .
在顶部 testcython
目录中),我从 Cython 收到以下错误:
Complete output from command python setup.py egg_info:
Error compiling Cython file:
------------------------------------------------------------
...
import cython
cimport numpy as np
import numpy as np
from .Bar cimport square
^
------------------------------------------------------------
testcython/foo.pyx:7:0: relative cimport beyond main package is not allowed
Error compiling Cython file:
------------------------------------------------------------
...
import numpy as np
from .Bar cimport square
def do_square(x):
return square(x)
^
------------------------------------------------------------
这个答案 (Extension
对象的 include_dirs
参数中包含根目录 ('.'
) 应该可以解决问题.
虽然 this part of the Cython documentation 提到在使用 setuptools
包时在 setup
的参数中使用 zip_safe=False
。
正如您从我上面的 setup.py
文件中看到的那样,我已经包含了这两个 - 但我仍然收到上面的错误。
注意: 如果我将扩展名(在 Extension
构造函数中)从 Bar
和 Foo
更改为 testcython.Bar
和 testcython.Foo
,然后我得到一个不同的错误:
Complete output from command python setup.py egg_info:
Error compiling Cython file:
------------------------------------------------------------
...
import cython
cimport numpy as np
import numpy as np
from .Bar cimport square
^
------------------------------------------------------------
testcython/foo.pyx:7:0: 'testcython/Bar/square.pxd' not found
Error compiling Cython file:
------------------------------------------------------------
...
import numpy as np
from .Bar cimport square
def do_square(x):
return square(x)
^
------------------------------------------------------------
我在同事的帮助下解决了这个问题,所以我会在这里提及解决方案,以防将来对人们有所帮助。
问题与 Cython 模块的导入方式有关,更具体地说 - .so
文件在构建扩展时的放置位置。最初,Bar.so
文件是在 testcython
目录中生成的 - 因此当尝试从 bar
sub-module 导入时,找不到相应的共享对象文件。
为了解决这个问题,我需要在创建此扩展时使用名称 "bar.bar"
,这会导致 .so
文件生成到 testcython/bar
目录中。然后,在 foo.pyx
中,要使用此 bar
模块中的成员,必须将导入更改为 from testcython.bar.bar cimport <name>
。
注:
此外,问题中显示的函数 square
不能以这种形式从另一个 Cython 模块使用,因为没有 __pyx_capi__
是免费 cdef
函数生成的。相反,此函数必须作为静态方法包装在一些 cdef
class 中,以便从另一个 Cython 模块使用它,即:
cdef class Square:
@staticmethod
cdef int square(int x)
然后可以导入,例如foo.pyx
,用from testcython.bar.bar cimport Square
。 class Square
本质上就像 "namespace".