编辑代码样式/空格时如何确保没有逻辑更改?

时间:2015-02-18 23:30:06

标签: python python-3.x refactoring diff

当对Python脚本进行表面更改(例如,更改代码样式/格式/空格)时,能够检查是否(意外地)对代码进行了任何逻辑更改是很有用的。 / p>

对于C / C ++,我生成汇编程序并将其与区分开来(不是100%使用特定于平台ifdef's的傻瓜证明,但仍然有用)。虽然我可以对pyc文件进行二进制差异化处理,但这对于查看确切更改的内容并非如此有用。

有没有一种方便的方法来获取可以检查更改的AST的人类可读文本输出?

这当然可能会引发一些误报(例如,将str % bar替换为str.format(bar)),但我仍然有兴趣知道是否存在一些方便的方法。


背景信息

因为建议只是运行测试。 以下是我提出这个问题的原因。 此代码没有测试,并且它不太可能具有100%的测试覆盖率,因为它恰好是构建系统实用程序脚本。理论上,我们可以花时间添加测试套件,并找到使测试能够在不同平台上运行的方法(猴子补丁sys.platform或在VM中所有支持的平台上运行持续集成...)但我们可以&目前正在努力花费这种努力。

此外,您可能希望自己清理测试代码!

4 个答案:

答案 0 :(得分:2)

解析AST以检查逻辑差异是一个好主意。 Python使its AST非常容易使用。

import ast

original_ast = ast.parse("""
import sys
for a in range(0,10):
    print(a)
sys.exit(0)""")

altered_ast = ast.parse("""
import sys
for a in range(0,10):
    print(a + 1)
sys.exit(0)""")

ast.dump(original_ast) == ast.dump(altered_ast)

如果你想看到差异,那么Python有另一个built in diff library

答案 1 :(得分:1)

正如@ erik-e所指出的那样,你可以简单地使用ast.dump,但这会将所有内容放在一行中,这是ast.dump的修改版本,在一个从stdin中读取并打印的脚本中淘汰了。

例如:

py_to_ast < my_script.py > my_ast.txt

除了换行符和缩进之外,输出与ast.dump相同。

您可以从以下位置下载脚本: https://gitlab.com/ideasman42/dotfiles/blob/master/bin/py_to_ast.py

此脚本的

Example output贯穿其自身

#!/usr/bin/env python3

import ast


def dump(node, annotate_fields=True, include_attributes=False):
    """
    ast.dump from Python3.4 modified for pretty printing.
    """
    from ast import AST, iter_fields

    def _format(node, level):
        level_next = level + 1
        indent = level * '  '
        indent_next = level_next * '  '
        if isinstance(node, AST):
            fields = [(a, _format(b, level_next)) for a, b in iter_fields(node)]
            rv = '\n' + indent + '%s(%s' % (node.__class__.__name__, (',\n' + indent_next).join(
                ('%s=%s' % field for field in fields)
                if annotate_fields else
                (b for a, b in fields)
            ))

            if include_attributes and node._attributes:
                rv += fields and ', ' or ' '
                rv += (',\n' + indent_next).join('%s=%s' % (a, _format(getattr(node, a), level_next))
                                                 for a in node._attributes)
            return rv + ')'
        elif isinstance(node, list):
            return '[%s]' % (',\n' + indent_next).join(_format(x, level_next) for x in node)
        return repr(node)
    if not isinstance(node, AST):
        raise TypeError('expected AST, got %r' % node.__class__.__name__)
    return _format(node, 0)


import sys

def main():
    data = sys.stdin.read()
    tree = ast.parse(data)
    print(dump(tree))

if __name__ == "__main__":
    main()

答案 2 :(得分:0)

你可以很容易地创建自己的树,如果上面的相等运算符已经超载以进行比较,我建议阅读它,你还没有必须手动比较它们。

了解here

答案 3 :(得分:-1)

我们的SmartDifferencer将源代码解析为AST,并计算AST的差异。这会忽略字符串中的空格格式,注释,数字基数,转义序列。 SmartDifference适用于Python以及其他语言。

如果唯一的变化是空白差异,则AST差异为空。在这种情况下,程序在语义上是相同的。

任何实际差异都以对代码结构的合理编辑的形式报告,例如:删除,插入,移动,复制,替换标识符。