构建/测试带C扩展的Python项目

6 投票
1 回答
1083 浏览
提问于 2025-04-16 18:25

我有一个项目,里面有一个Python包和一个编译好的组件。现在的目录结构是:

<project>
  foo/
  foo/__init__.py
  foo/...

  src/
  src/c_foo.c

  tests/
  tests/test_foo.py

  setup.py

当项目构建时,distutils会创建一个build/lib目录,我要么把它添加到PYTHONPATH中,要么安装到一个虚拟环境里。最终的结构如下:

<project>
  build/lib
  build/lib/foo/__init__.py
  build/lib/foo/c_foo.so

问题是,如果我从项目根目录启动Python解释器,或者从项目根目录运行测试等,它会优先使用源代码树,而不是构建后的树。

我找到了一些现有的解决方案:

  1. 把Python源代码放在一个单独的目录下,比如lib/foomodules/foo等。这样做的缺点是,所有源文件都多了一个目录层级,而且与那些没有编译扩展的项目不一致,因为它们的Python包通常就在根目录下。

  2. 把包放在根目录,这样就需要从项目根目录chdir到其他目录(比如进入tests/目录),这样Python解释器就看不到源代码包(可以通过构建脚本或手动操作)。

  3. 把包放在根目录,但用不同的名字(比如foo-modulefoo-lib),并在setup.py中加上package_dir={'foo':'lib-foo'}这一行。这是第一种方法的变体,没有额外的目录层级,我想这基本上是一样的。

  4. 把包放在根目录,并使用setup.py build_ext --inplace,但这样会污染源代码树。

无论哪种情况,相比于一个普通的Python项目,这些方法都增加了额外的复杂性,因为在普通项目中可以直接从源代码树修改或运行代码。我很想听听大家对上述方法的优缺点的看法,以及你们在项目中使用的具体方法。

1 个回答

1

你可以试试在 distribute(以前叫setuptools) 中使用 develop 这个目标。

确保你已经安装了 distribute,然后像下面这样修改你的 setup.py 文件:

# the setuptools package name is still used
from setuptools import setup, Extension
...

接着进入你的虚拟环境,运行 develop

% source ~/virt/bin/activate
(virt)% cd ~/project
(virt)% python setup.py develop

这样你就可以在项目的根目录下运行测试了,而且每次你激活这个虚拟环境时,都可以访问这个项目的包和扩展,无论你的路径是什么:

% cd /tmp
% source ~/virt/bin/activate
(virt)% python -c 'import foo, c_foo; print foo, c_foo'

<module 'foo' from '/Users/user/project/foo/__init__.py'>
<module 'c_foo' from '/Users/user/project/c_foo.so'>

撰写回答