维护不同Python版本的代码库的工作流程
我正在开发一个开源应用程序,叫做 GarlicSim。
到目前为止,我只为 Python 2.6 开发这个应用,其他版本似乎都不太能用。
我觉得有必要制作支持其他 Python 版本的版本。我在考虑为 2.5、3.1,甚至可能还有 2.4 制作版本。
所以我有几个问题:
- 我该如何组织我的代码库文件夹结构,以便包含这些不同的版本?
- 我该如何将我在一个版本中做的修改“合并”到其他版本中?我知道如何在我的版本控制系统(也就是 git)中进行合并,但这些文件夹都在同一个代码库里,我想在它们之间进行合并。当然,也可以为每个版本建立一个单独的代码库,但我觉得这样不好。
有没有人有什么建议?
4 个回答
如果你的代码在处理异常时对运行速度的要求不是特别高,你甚至可以不为Python 3单独写一份代码。我一直在为我的所有Python 2.x版本维护一个pyparsing的版本,虽然我不得不采用一种“最低共同点”的方法,这意味着我不能使用一些像生成器表达式和上下文管理器这样的特性。我用字典代替集合,并把所有的生成器表达式都改成列表推导式,这样它们在Python 2.3中也能正常工作。我在代码的开头有一段处理一些Python 2和3之间差异的代码(由pyparsing用户Robert A Clark提供):
_PY3K = sys.version_info[0] > 2
if _PY3K:
_MAX_INT = sys.maxsize
basestring = str
unichr = chr
unicode = str
_str2dict = set
alphas = string.ascii_lowercase + string.ascii_uppercase
else:
_MAX_INT = sys.maxint
range = xrange
def _str2dict(strg):
return dict( [(c,0) for c in strg] )
alphas = string.lowercase + string.uppercase
我遇到的最大困难是Python 3引入的捕获异常的语法不兼容,它从
except exceptiontype,varname:
变成了
except exceptionType as varname:
当然,如果你不需要异常变量,你可以直接写:
except exceptionType:
这样在Python 2和3中都能工作。但是如果你需要访问异常,你仍然可以找到一种跨版本兼容的语法,如下:
except exceptionType:
exceptionvar = sys.exc_info()[1]
这会有一点运行时的性能损失,这使得在pyparsing的某些地方无法使用,所以我还是得维护Python 2和Python 3的两个版本。为了合并源代码,我使用了一个叫做WinMerge的工具,我觉得它非常适合保持源代码目录的同步。
所以即使我保留了两个版本的代码,这些统一的方法也帮助我把差异控制到绝对最小的程度。
我会尽量保持一个分支来支持所有的 Python 2.4 到 2.6 版本。
其实它们之间的差别并不大。如果你为了在 2.4 版本上做一些在 2.6 版本中很简单的事情而写了很多额外的代码,从长远来看,使用 2.4 版本来兼容 2.5 和 2.6 会更省事。
Python 3 应该有一个不同的分支,但你还是应该尽量保持代码的共通性。
你只在非常少见的情况下需要为不同的版本创建独立的分支。你提到的上下文管理器(context managers)确实很棒,不用它们会很麻烦,你说得对。不过,如果你想支持Python 2.4,就得不使用上下文管理器。这会让事情变得麻烦。所以,如果你想兼容Python 2.4,你就得写一个不使用上下文管理器的版本。不过,这个版本在Python 2.6下也能运行,所以在这方面没有必要分开版本。
至于Python 3,虽然创建一个独立的分支可以解决问题,但通常来说这不是最好的选择。为了支持Python 3,有个工具叫做2to3,它可以把你的Python 2代码转换成Python 3代码。这个工具并不完美,所以你经常需要修改Python 2的代码,才能生成更好的Python 3代码。不过,修改后Python 2的代码通常会变得更好。
使用Distribute(一个维护中的setuptools分支),你可以在安装时自动进行这种转换。这样你就不需要为Python 3单独创建分支了。有关详细信息,可以查看http://bitbucket.org/tarek/distribute/src/tip/docs/python3.txt。
正如Paul McGuire所说,实际上可以用同一段代码同时支持Python 3和Python 2,而不使用2to3,但如果你想支持其他版本(比如2.6和3.x以外的版本),我不推荐这样做。这样会出现很多丑陋的特殊处理。对于2.6来说,和Python 3之间有足够的向前兼容性,可以写出看起来不错的代码,同时支持Python 2.6和3.x,但不支持Python 2.5和3.x。