2014-02-21 101 views
7

在我目前的工作环境中,我们生产大量供内部使用的Python包(如果不是100s,则为10s)。每个软件包都有一些依赖关系,通常是内部和外部软件包的混合体,其中一些依赖关系是共享的。Python API兼容性检查器

当我们接近dependency hell时,更新依赖关系成为一个耗时的过程。虽然我们关心新版本可能引入的功能更改,但同等(如果不是更多)重要的是破坏代码的API更改。

虽然针对较新版本的依赖项运行单元/集成测试有助于我们发现一些问题,但我们的覆盖率还不足以达到100%,因此无法实现这一稳健策略。发行说明和更改日志可帮助识别高级别的主要更改,但这些更改很少存在于内部开发的工具中,或者详细了解新版本对(公共)API的影响。

我正在看其他方面来自动执行此过程。

我希望能够自动比较两个版本的Python软件包并报告它们之间的API差异。特别是,这将包括向后不兼容的更改,例如删除函数/方法/类/模块,向函数/方法/类添加位置参数以及更改函数/方法返回的项目数。作为一名开发人员,基于此报告生成的报告,我应该更深入地了解此版本更改将引入的代码级别影响,因此需要整合它。

在其他地方,我们使用C++ abi-compliance-checker并正在寻找Java api-compliance-checker来帮助完成此过程。是否有类似的工具可用于Python?我发现了很多皮棉/分析/重构工具,但没有提供这种级别的功能。我知道Python的动态输入将会使综合报告变得不可能。

如果这样的工具不存在,他们是否可以帮助实施解决方案的任何库?例如,我目前的方法是使用ast.NodeVisitor来遍历包并构建一个树,其中每个节点表示一个模块/类/方法/函数,然后将该树与另一个版本的树相比较,以查找相同的包。

编辑:自发布问题我发现pysdiff其中涵盖了我的一些要求,但有兴趣看到替代品。

编辑:也找到了Upstream-Tracker这是我想结束的那种信息的一个很好的例子。

回答

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)) 

这会给你的“公共”(不带下划线开始)与他们的参数列表的函数列表中开始。您可以添加更多的东西打印kwargs,类等

一旦你所有你关心的封装/模块,在新老版本的运行,你将有两个列表如下:

myPackage.myModule.myFunction1(foo, bar) 
myPackage.myModule.myFunction2(baz) 

然后,您可以对它们进行排序和比较,或者在Python中编写一些更智能的工具来实际比较所有名称,例如允许附加的可选参数但拒绝新的强制性参数。

+1

这种方法看起来相当简单,并且很容易扩展到覆盖types.ClassType和types.MethodType等。但我宁愿不必导入正在检查的模块(我应该把它放在原始问题中)。我认为静态分析会更好,因为它简化了比较的运行时环境(例如,我认为pylint是这样工作的)。 –

0

查看zope.interfaces(你可以从PyPI获取它)。然后,您可以将模块支持接口的单元测试合并到您的单元测试中。可能需要一段时间才能适应复古风格 - 这也不是银弹。

+0

所以这个想法是将zope.interface对象中的每个依赖包装在一起,构成我针对新版本运行的验收单元测试套件的一部分?这听起来像是对绿地项目非常有效,但我认为我现在的状况会持续太久。 –

+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) 

的astdump可能工作(可PyPI上)的AST节点(描述http://docs.python.org/2/library/ast.html

这种过时漂亮打印机 http://code.activestate.com/recipes/533146-ast-pretty-printer/

的文档上的走法工具狮身人面像也提取您正在寻找的信息。也许给一看。

因此,走AST并用你想要的信息构建一棵树。一旦你有了一棵树,你可以腌制它,稍后再进行比较,或者将树转换为 文本文件中的文本表示,您可以使用difftools或某个外部diff程序进行比较。

ast有parse()和compile()方法。唯一的问题是,我并不完全确定解析后有多少信息可用(因为您不想编译())。