Python代码组织问题:Eggs + 包 + Buildout + 单元测试 + SVN
我有几个Python项目,它们都用到了相同的模块。到现在为止,我一直在...嗯...手动维护这些公共代码的多个副本,真是麻烦。不过,我显然希望能有更好的办法。
现在看来,zc.Buildout可能正是我需要的。我想我应该把系统中每个可重用的部分放到一个单独的“蛋”(egg)里,然后用buildout把它们组合成项目。
我还在想,对于某个特定的模块,我应该把单元测试放到一个单独的包或蛋里,这样我就不需要在每个项目中都安装这个模块的单元测试。我只想在开发库的地方进行单元测试,而不是在仅仅使用它的地方。
所以我可能想要这样的结构:
projects
lib1
tests
code
lib2
tests
code
app1
tests
appcode
app2
tests
appcode
等等。
在这个结构中,app1和app2是独立的应用程序,各自有自己的代码和测试,但它们也会包含和使用lib1和lib2。而lib1/test、lib1/code、lib2/test、lib2/code、app1和app2都是独立的蛋。这样说对吗?
不过,我现在有点困惑。我假设当我开发app1时,我希望buildout把lib1、lib2和app1的副本拉到一个单独的工作目录,而不是直接把这些库放在app1下面。但这和我的SVN源代码管理是怎么结合起来的呢?如果工作目录是用buildout动态构建的,那它就不能是一个可以实时更新的SVN目录,我也不能把更改提交回去吗?
我是不是误解了buildout的使用方式?我是不是应该考虑完全不同的方法?在项目之间如何混合源代码管理和模块重用呢?
更新:感谢目前回答这个问题的两位朋友。我正在进一步实验这个。
4 个回答
这就是为什么你需要用到 site 模块的原因。它会设置内部的 sys.path
,让所有的包和模块都能被找到,具体包括:
lib/site-packages
-- 这里面包括了文件夹、egg 文件和.pth
文件。PYTHONPATH
这样的话,你的库就只有一个有效的副本。
利用这个方法有很多种方式。这里介绍两种。
在每个库里写一个
setup.py
文件,确保你的库能够正确部署。当你做了修改后,运行svn up
来获取最新的更改,然后用python setup.py install
来部署每个应用共享的那个有效副本。在每个应用中,可以依赖于
PYTHONPATH
环境变量中的内容。确保projects/lib1
和projects/lib2
都在PYTHONPATH
里。这样每个应用就可以共享这些库的一个有效副本。
我在SVN中使用了以下结构,效果很好。
Lib1/
branches/
tags/
trunk/
lib1/
tests/
setup.py
Lib2
branches/
tags/
trunk/
lib2/
tests/
setup.py
App1
branches/
tags/
trunk/
app1/
tests/
setup.py
App2
branches/
tags/
trunk/
app2/
tests/
setup.py
接下来,我会创建我的开发工作区(我用的是eclipse/pydev),可以从主干或某个分支上检出代码。
Lib1/
lib1/
tests/
setup.py
Lib2/
lib2/
tests/
setup.py
App1/
app1/
tests/
setup.py
App2/
app2/
tests/
setup.py
然后,我会使用eclipse的项目依赖设置来配置Python路径,这样可以很好地支持eclipse的代码补全功能。虽然setup.py也能用,但对多个工作区的支持不太好。
在部署时,我会创建一个包含以下结构的压缩包。
App1/
lib1-1.1.0-py2.5.egg/
lib2-1.1.0-py2.5.egg/
app1/
sitecustomize.py
App2/
lib1-1.2.0-py2.5.egg/
lib2-1.2.0-py2.5.egg/
app2/
sitecustomize.py
我不使用setup install,因为我想支持多个版本的应用程序。此外,我对运行环境有一些控制,所以在部署时不把Python打包进去,但如果需要的话,添加Python到部署包里应该很简单。
不要把测试和你的代码分开,最好把它们放在一起。测试占用的磁盘空间和内存其实并不多!而且,测试对使用你库的用户来说非常有帮助。
对于库包,最好在你的包里包含一个 buildout.cfg
文件和一个 bootstrap.py
文件,这样可以方便地运行测试。比如,你可以看看 plone.reload 包,它是如何使用 zc.recipe.testrunner 的部分来创建一个自动发现并运行测试的脚本。这样,你就能确保你的库包始终经过测试!
然后,你的应用包只需要测试集成和特定于应用的代码。同样,测试也要和包放在一起,这样在写代码时就不会忘记测试。可以在你的 buildout 中使用 zc.recipe.testrunner
的部分来发现和运行这些测试。
最后,使用 mr.developer 来管理你的包。通过 mr.developer,你可以在开发时检出包,或者如果不需要修改代码,可以依赖发布的版本。大项目通常会有很多依赖,其中很多不需要你去修改代码。使用 mr.developer,你可以随意拉取源代码,并将其转化为开发版本,直到你发布了这些代码,然后可以不再需要检出了。
想看看这样的项目构建实例,可以参考 Plone 核心开发构建。
sources.cfg
文件里列出了各种包的很多 SCM 位置,但通常使用的是发布版本的包,直到你明确激活你打算工作的包。checkouts.cfg
列出了默认检出的所有包;这些包有一些改动,将会成为 Plone 下一个版本的一部分,但还没有发布。如果你在做 Plone 的工作,这些包是很重要的,因为你不能忽视这些改动。而 testing.cfg
列出了如果你想测试 Plone 需要的所有包,数量可不少。
需要注意的是,Plone 的源代码来自很多不同的地方。一旦你开始使用 buildout 和 mr.developer 来管理你的包,你就可以自由地从任何地方拉取源代码。