依赖cython和f2py的packages的setup.py

20 投票
2 回答
2354 浏览
提问于 2025-04-17 05:12

我想为一个包含多个子模块的Python包创建一个setup.py脚本,这些子模块依赖于cython和f2py。我尝试过使用setuptools和numpy.distutils,但到目前为止都没有成功:

使用setuptools

我可以用setuptools编译我的cython扩展(并为包的其他部分创建安装包)。但是,我一直搞不清楚如何用setuptools生成f2py扩展。经过大量搜索,我只找到了一些比较旧的信息,说f2py模块必须使用numpy.distutils来编译。

使用numpy.distutils

我可以用numpy.distutils编译我的f2py扩展(并为包的其他部分创建安装包)。不过,我一直无法让numpy.distutils编译我的cython扩展,因为它总是试图用pyrex来编译(而我使用的是专门针对cython的扩展)。我搜索了一下,发现至少在一年前,他们建议对numpy.distutils应用一个猴子补丁。看起来应用这样的猴子补丁还会限制可以传递给Cython的选项。

我的问题是:对于依赖于f2py和cython的包,编写setup.py脚本的推荐方法是什么?对numpy.distutils应用补丁真的还是最佳选择吗?

2 个回答

3

其实这个说法现在已经不成立了。现在使用 setuptoolsdistutils(至少是 numpy 的版本)时,可以使用 C、Cython 和 f2py 来创建扩展模块。唯一需要注意的是,编译 f2py 模块时,必须始终使用 numpy.distutils 来处理 setupExtension 函数。不过,安装时仍然可以使用 setuptools(比如,可以通过 python setup.py develop 来安装开发版本)。

如果你想完全使用 distutils,可以这样做:

from numpy.distutils.core import setup
from numpy.distutils.extension import Extension

如果要使用 setuptools,需要在导入 distutils 之前先导入它:

import setuptools

然后后面的代码就和之前的一样:

from numpy import get_include
from Cython.Build import cythonize

NAME = 'my_package'
NUMPY_INC = get_include()
extensions = [
    Extension(name=NAME + ".my_cython_ext", 
              include_dirs=[NUMPY_INC, "my_c_dir"]
              sources=["my_cython_ext.pyx", "my_c_dir/my_ext_c_file.c"]),
    Extension(name=NAME + ".my_f2py_ext", 
              sources=["my_f2py_ext.f"]),
]
extensions = cythonize(extensions)
setup(..., ext_modules=extensions)

显然,你需要把其他所有的内容放在 setup() 调用里面。在上面的例子中,我假设你会用 Cython 和 numpy,并且有一个外部的 C 文件(my_ext_c_file.c),这个文件会放在 my_c_dir/ 目录下,同时 f2py 模块只有一个 Fortran 文件。根据需要进行调整。

6

你可以在你的 setup.py 文件中分别调用每个内容,就像在这个链接中提到的那样:
http://answerpot.com/showthread.php?601643-cython%20and%20f2py

# Cython extension
from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext

setup(
  ext_modules = [Extension( 'cext', ['cext.pyx'] )],
  cmdclass = {'build_ext': build_ext},
  script_args = ['build_ext', '--inplace'],
)

# Fortran extension
from numpy.distutils.core import setup, Extension
setup(
  ext_modules = [Extension( 'fext', ['fext.f90'] )],
)

你调用的上下文(我觉得他们叫这个命名空间,不太确定)
需要根据当前的对象 Extension 和函数 setup() 来进行调整。

第一次调用 setup() 时,使用的是 distutils.extension.Extension
和 distutils.core.setup()。

第二次调用 setup() 时,使用的是 numpy.distutils.core.Extension
和 numpy.distutils.core.setup()。

撰写回答