2 回答
TA贡献1735条经验 获得超5个赞
这里有两种可能的方法。
静态方法:
您可以使用 ast 模块解析代码库以识别所有函数调用并一致地存储调用的来源和目标。您必须标识所有类和函数定义以跟踪每个调用的当前上下文。这里的限制是,如果您调用实例方法,则没有简单的方法来识别该方法实际属于哪个类。如果您使用引用模块的变量,则相同
这是一个访问者子类,它可以读取 Python 源文件并构建一个 dict {caller: callee}:
class CallMapper(ast.NodeVisitor):
def __init__(self):
self.ctx = []
self.funcs = []
self.calls = collections.defaultdict(set)
def process(self, filename):
self.ctx = [('M', os.path.basename(filename)[:-3])]
tree = ast.parse(open(filename).read(), filename)
self.visit(tree)
self.ctx.pop()
def visit_ClassDef(self, node):
print('ClassDef', node.name, node.lineno, self.ctx)
self.ctx.append(('C', node.name))
self.generic_visit(node)
self.ctx.pop()
def visit_FunctionDef(self, node):
print('FunctionDef', node.name, node.lineno, self.ctx)
self.ctx.append(('F', node.name))
self.funcs.append('.'.join([elt[1] for elt in self.ctx]))
self.generic_visit(node)
self.ctx.pop()
def visit_Call(self, node):
print('Call', vars(node.func), node.lineno, self.ctx)
try:
id = node.func.id
except AttributeError:
id = '*.' + node.func.attr
self.calls['.'.join([elt[1] for elt in self.ctx])].add(id)
self.generic_visit(node)
动态方法:
如果您真的想确定调用了什么方法,当多个方法可以共享相同的名称时,您将不得不使用动态方法。您可以装饰类中的单个函数或所有方法,以计算它们被调用的次数,以及它们的调用位置。然后您将开始测试并检查实际发生的情况。
这是一个函数,它将装饰类中的所有方法,以便所有调用的数字都将存储在字典中:
def tracemethods(cls, track):
def tracker(func, track):
def inner(*args, **kwargs):
if func.__qualname__ in track:
track[func.__qualname__] += 1
else:
track[func.__qualname__] = 1
return func(*args, *kwargs)
inner.__doc__ = func.__doc__
inner.__signature__ = inspect.signature(func)
return inner
for name, func in inspect.getmembers(cls, inspect.isfunction):
setattr(cls, name, tracker(func, track))
您可以调整该代码以浏览解释器堆栈以识别每个调用的调用者,但这不是很容易,因为您获得调用者函数的非限定名称,并且必须使用文件名和行号来唯一标识调用者.
TA贡献1825条经验 获得超6个赞
好吧,这是一个开始。您将使用几个标准库:
import dis
import inspect
假设您对此源代码感兴趣:myfolder/myfile.py 然后执行以下操作:
import myfolder.myfile
def some_func():
''
loads = {'LOAD_GLOBAL', 'LOAD_ATTR'}
name_to_member = dict(inspect.getmembers(myfolder.myfile))
for name, member in name_to_member.items():
if type(member) == type(some_func):
print(name)
for ins in dis.get_instructions(member):
if ins.opname in loads:
print(name, ins.opname, ins.argval)
其他有趣的事情:运行dis.dis(member),或打印出来dis.code_info(member)。
这将让您访问文件中定义的每个函数,并访问每个可执行语句以查看它是否可能是您关心的方法调用。然后由您来使用潜在的测试方法做正确的事情。
添加回答
举报
