在我目前的工作环境中,我们会生成大量内部使用的Python包(如果不是100则为10s)。每个包都有一些依赖关系,通常是内部和外部包的混合,并且这些依赖关系中的一些是共享的。
当我们接近dependency hell时,更新依赖关系变得非常耗时。虽然我们关心新版本可能引入的功能更改,但重要的是(如果不是更多)重要的API更改会破坏代码。
尽管针对较新版本的依赖项运行单元/集成测试有助于我们捕获一些问题,但我们的覆盖率并不足以达到100%才能使其成为一个强大的策略。发行说明和更改日志有助于识别高级别的主要更改,但这些更改很少存在于内部开发的工具中,或者详细了解新版本对(公共)API的影响。
我正在寻找其他方法来自动化这个过程。
我希望能够自动比较Python包的两个版本并报告它们之间的API差异。特别是这将包括向后不兼容的更改,例如删除函数/方法/类/模块,向函数/方法/类添加位置参数以及更改函数/方法返回的项数。作为开发人员,根据生成的报告,我应该对此版本更改将引入的代码级别含义有更深入的了解,因此需要时间来集成它。
在其他地方,我们使用C++ abi-compliance-checker并正在查看Java api-compliance-checker以帮助完成此过程。是否有适用于Python的类似工具?我发现了大量的lint / analysis / refactor工具,但没有提供这种功能。我知道Python的动态类型将无法提供全面的报告。
如果这样的工具不存在,那么它们是否可以帮助实施解决方案?例如,我当前的方法是使用ast.NodeVisitor遍历包并构建一个树,其中每个节点代表一个模块/类/方法/函数,然后将该树与同一个包的另一个版本的树进行比较
修改:自发布问题后我发现pysdiff涵盖了我的一些要求,但仍有兴趣看到替代方案。
修改:同时发现Upstream-Tracker也是我最终希望得到的信息的一个很好的例子。
答案 0 :(得分:5)
使用AST模块解析文件怎么样?
import ast
with file("test.py") as f:
python_src = f.read()
node = ast.parse(python_src) # Note: doesn't compile the src
print ast.dump(node)
ast节点上有walk方法(描述为http://docs.python.org/2/library/ast.html)
astdump可能有效(在pypi上可用)
这个过时的漂亮打印机 http://code.activestate.com/recipes/533146-ast-pretty-printer/
文档工具Sphinx还会提取您要查找的信息。或许可以看看。
所以走AST并用你想要的信息构建一棵树。一旦你有了一棵树,你可以腌制它并稍后进行差异化或将树转换为文本表示形式 您可以使用difftools或某些外部差异程序进行差异的文本文件。
ast有parse()和compile()方法。唯一的问题是我不能完全确定解析后有多少信息可供使用(因为你不想编译())。
答案 1 :(得分:2)
也许你可以先使用inspect
模块
import inspect
import types
def genFunctions(module):
moduleDict = module.__dict__
for name in dir(module):
if name.startswith('_'):
continue
element = moduleDict[name]
if isinstance(element, types.FunctionType):
argSpec = inspect.getargspec(element)
argList = argSpec.args
print "{}.{}({})".format(module.__name__, name, ", ".join(argList))
这将为您提供一个“public”列表(不以下划线开头)及其参数列表。您可以添加更多内容来打印kwargs,类等。
一旦你在所有你关心的软件包/模块上运行它,无论是旧版本还是新版本,你都会有两个这样的列表:
myPackage.myModule.myFunction1(foo, bar)
myPackage.myModule.myFunction2(baz)
然后你可以对它们进行排序和区分,或者在Python中编写一些更智能的工具来实际比较所有的名称,例如:允许其他可选参数但拒绝新的强制参数。
答案 2 :(得分:0)
查看zope.interfaces(您可以从PyPI获取)。然后,您可以将模块支持接口的单元测试合并到单元测试中。可能需要一段时间来适应复古 - 但它也不是银弹。