给定对函数的引用,生成它的源代码而不在签名中注释
Given a reference to function, produce it's source code without annotations in the signature
应保留默认值。
装饰器也应该被删除,但这不是什么大问题。
原始出处:
# Comments
@decorator1
@decorator2(
a=1,
b=1,
)
def my_func(
a: typing.List = 14,
b: 'CustomType' = None,
c: Whatever('foo') = 42,
d: Whatever('foo') = some_function,
) -> typing.NamedTuple(
'Dummy',
[
('r1': 'CustomType'),
('21': 'CustomType2'),
]
):
...
所需来源:
def my_func(
a = 14,
b = None,
c = 42,
d = some_function,
):
...
我可以使用 inspect.getsource(my_func)
获取源代码,但我需要删除注释。
我怎样才能做到这一点?最好使用标准模块。
可以使用ast.parse
将source解析成AST,如果是arg
节点则使用ast.walk
遍历树使annotation
无效化returns
和 decorator_list
如果它是 FunctionDef
节点。使用 astunparse
将树解析回源代码:
import inspect
import ast
import typing
import astunparse
from unittest.mock import patch
@patch('builtins.print')
def my_func(
a: int = 1,
b: typing.List = []
) -> bool:
pass
tree = ast.parse(inspect.getsource(my_func), '', 'exec')
for node in ast.walk(tree):
if isinstance(node, ast.arg):
node.annotation = None
elif isinstance(node, ast.FunctionDef):
node.returns = None
node.decorator_list = []
print(astunparse.unparse(tree))
这输出:
def my_func(a=1, b=[]):
pass
您可以子类化 lib2to3.refactor.RefactoringTool
以使用作为 lib2to3.fixer_base.BaseFix
子类的修复器重构代码,其模式查找类型化参数、带注释返回值的函数声明、或装饰定义,以及从子节点中删除注释和装饰器索引的 transform
方法:
from lib2to3 import fixer_base, refactor
class FixParameterAnnotations(fixer_base.BaseFix):
PATTERN = "name=tname | func=funcdef< any+ '->' any+ > | decorated"
def transform(self, node, results):
if 'name' in results:
del node.children[1:] # delete annotation to typed argument
elif 'func' in results:
del node.children[-4:-2] # delete annotation to function declaration
else:
del node.children[0] # delete decorators
return node
class Refactor(refactor.RefactoringTool):
def __init__(self, fixers):
self._fixers= [cls(None, None) for cls in fixers]
super().__init__(None)
def get_fixers(self):
return self._fixers, []
这样:
import inspect
import typing
from unittest.mock import patch
@patch('builtins.print')
def my_func(
a: int = 1, # some comment
b: typing.List = [] # more comment
) -> bool:
''' some docstring'''
pass
print(Refactor([FixParameterAnnotations]).refactor_string(inspect.getsource(my_func), ''))
输出:
def my_func(
a = 1, # some comment
b = [] # more comment
):
''' some docstring'''
pass
演示:https://repl.it/@blhsing/BrightWhirlwindBoolean
lib2to3
是双向稳定的,因此在转换后保留所有注释和空格。您可以在 lib2to3
模块的 Grammar.txt
中找到 Python 语法的定义。
应保留默认值。
装饰器也应该被删除,但这不是什么大问题。
原始出处:
# Comments
@decorator1
@decorator2(
a=1,
b=1,
)
def my_func(
a: typing.List = 14,
b: 'CustomType' = None,
c: Whatever('foo') = 42,
d: Whatever('foo') = some_function,
) -> typing.NamedTuple(
'Dummy',
[
('r1': 'CustomType'),
('21': 'CustomType2'),
]
):
...
所需来源:
def my_func(
a = 14,
b = None,
c = 42,
d = some_function,
):
...
我可以使用 inspect.getsource(my_func)
获取源代码,但我需要删除注释。
我怎样才能做到这一点?最好使用标准模块。
可以使用ast.parse
将source解析成AST,如果是arg
节点则使用ast.walk
遍历树使annotation
无效化returns
和 decorator_list
如果它是 FunctionDef
节点。使用 astunparse
将树解析回源代码:
import inspect
import ast
import typing
import astunparse
from unittest.mock import patch
@patch('builtins.print')
def my_func(
a: int = 1,
b: typing.List = []
) -> bool:
pass
tree = ast.parse(inspect.getsource(my_func), '', 'exec')
for node in ast.walk(tree):
if isinstance(node, ast.arg):
node.annotation = None
elif isinstance(node, ast.FunctionDef):
node.returns = None
node.decorator_list = []
print(astunparse.unparse(tree))
这输出:
def my_func(a=1, b=[]):
pass
您可以子类化 lib2to3.refactor.RefactoringTool
以使用作为 lib2to3.fixer_base.BaseFix
子类的修复器重构代码,其模式查找类型化参数、带注释返回值的函数声明、或装饰定义,以及从子节点中删除注释和装饰器索引的 transform
方法:
from lib2to3 import fixer_base, refactor
class FixParameterAnnotations(fixer_base.BaseFix):
PATTERN = "name=tname | func=funcdef< any+ '->' any+ > | decorated"
def transform(self, node, results):
if 'name' in results:
del node.children[1:] # delete annotation to typed argument
elif 'func' in results:
del node.children[-4:-2] # delete annotation to function declaration
else:
del node.children[0] # delete decorators
return node
class Refactor(refactor.RefactoringTool):
def __init__(self, fixers):
self._fixers= [cls(None, None) for cls in fixers]
super().__init__(None)
def get_fixers(self):
return self._fixers, []
这样:
import inspect
import typing
from unittest.mock import patch
@patch('builtins.print')
def my_func(
a: int = 1, # some comment
b: typing.List = [] # more comment
) -> bool:
''' some docstring'''
pass
print(Refactor([FixParameterAnnotations]).refactor_string(inspect.getsource(my_func), ''))
输出:
def my_func(
a = 1, # some comment
b = [] # more comment
):
''' some docstring'''
pass
演示:https://repl.it/@blhsing/BrightWhirlwindBoolean
lib2to3
是双向稳定的,因此在转换后保留所有注释和空格。您可以在 lib2to3
模块的 Grammar.txt
中找到 Python 语法的定义。