使python的编译区docstrings但不断言

时间:2018-07-09 14:59:21

标签: python

Python的-O开关从其编译的代码中剥离断言。

Python的-OO开关会执行此操作,还会剥离文档字符串。

有什么方法可以制作Python条形文档字符串,但不是断言吗?

特别是可以通过命令行还是使用内置的编译功能来实现?

1 个答案:

答案 0 :(得分:1)

有点黑,但是您可以为代码生成Abstract Syntax Trees(AST),删除看起来 docstring的所有内容,然后传递更改后的内容向compile的资产。

给出此模块:

$  cat func.py 
"""
This is  module-level docstring.
"""

def f(x):
    """
    This is a doc string
    """
    # This is a comment
    return 2 * x

首先,从模块源代码生成ast。

>>> import ast
>>> with open('func.py') as f:
...     src = f.read()
... 
>>> tree = ast.parse(src)

转储ast显示存在文档字符串(注释不包含在ast中)

>>> ast.dump(tree)
"Module(body=[Expr(value=Str(s='\\nThis is  module-level docstring.\\n')), FunctionDef(name='f', args=arguments(args=[arg(arg='x', annotation=None)], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]), body=[Expr(value=Str(s='\\n    This is a doc string\\n    ')), Return(value=BinOp(left=Num(n=2), op=Mult(), right=Name(id='x', ctx=Load())))], decorator_list=[], returns=None)])"

现在是hacky部分:定义一个访问者,它将访问ast中的每个节点,并删除文档字符串。幼稚的实现只能删除不属于分配的只是字符串的任何表达式。

>>> class Transformer(ast.NodeTransformer):
...
...     def visit_Expr(self, node):
...         if isinstance(node.value, ast.Str):                      
...             return None
...         return node

如果代码包含多行字符串(尽管我尚未对此进行测试),则可能会出现问题。

如果该节点是一个表达式节点并且其值是一个字符串节点(如果将字符串绑定到一个名称,则该节点将是一个分配节点),那么更安全的实现可能会从任何模块,函数或类定义中删除第一个节点,而不是表达式)。

class Transformer(ast.NodeTransformer):

    def visit_Module(self, node):
        self.generic_visit(node)
        return self._visit_docstring_parent(node)

    def visit_FunctionDef(self, node):
        self.generic_visit(node)
        return self._visit_docstring_parent(node)

    def visit_ClassDef(self, node):
        self.generic_visit(node)
        return self._visit_docstring_parent(node)

    def _visit_docstring_parent(self, node):
        # Common docstring removal code.
        # Assumes docstrings will always be first node in
        # module/class/function body.
        new_body = []
        for i, child_node in enumerate(node.body):
            if i == 0 and isinstance(child_node, ast.Expr) and isinstance(child_node.value, ast.Str):
                pass
            else:
                new_body.append(child_node)
        node.body = new_body
        return node

>>> # Transformer performs an in-place transformation.
>>> Transformer().visit(tree)

请注意新ast中不再存在文档字符串:

>>> ast.dump(tree)
"Module(body=[FunctionDef(name='f', args=arguments(args=[arg(arg='x', annotation=None)], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[]), body=[Return(value=BinOp(left=Num(n=2), op=Mult(), right=Name(id='x', ctx=Load())))], decorator_list=[], returns=None)])"

新的ast可以编译为代码对象并执行:

>>> ast.fix_missing_locations(new_tree)
>>> code_obj = compile(new_tree, '<string>', mode='exec')

>>> exec(code_obj, globals(), locals())
>>> globals()['f']
<function f at 0x7face8bc2158>
>>> globals()['f'](5)
10