如何使用Python自省查找对方法的调用?
我刚刚在一个项目中发现了一些测试方法,这些方法没有必要的“test_u2;”前缀来确保它们实际运行。应该可以通过一点绒线来避免这种情况:如何使用Python自省查找对方法的调用?,python,python-3.x,introspection,Python,Python 3.x,Introspection,我刚刚在一个项目中发现了一些测试方法,这些方法没有必要的“test_u2;”前缀来确保它们实际运行。应该可以通过一点绒线来避免这种情况: 在代码库中查找所有TestCase断言调用 在调用层次结构中查找名称以“test\”开头的方法 如果没有此类方法,请打印错误消息 我想知道如何做前两个,这基本上归结为一个问题:如何在我的代码库中找到对特定方法的所有调用? Greping或其他文本搜索将不起作用,因为我需要反省结果并查找父方法等,直到我找到测试方法或没有其他调用方为止。我需要获得该方法的引用,以
TestCase
断言调用Greping或其他文本搜索将不起作用,因为我需要反省结果并查找父方法等,直到我找到测试方法或没有其他调用方为止。我需要获得该方法的引用,以避免匹配与我正在查找的方法同名的方法。好吧,这是一个开始。您将使用两个标准库:
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(成员)
,或打印出dis.code\u信息(成员)
这将允许您访问文件中定义的每个函数,
并访问每个可执行语句,看看它是否是您关心的方法调用。
然后,您就可以使用潜在的测试方法来做正确的事情。这里有两种可能的方法
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))
您可以调整该代码以浏览解释程序堆栈来识别每个调用的调用方,但这并不容易,因为您将获得调用方函数的非限定名称,并且必须使用文件名和行号来唯一地识别调用方对于所有与python相关的问题,请始终使用generic[python]标记。根据您的判断使用特定于版本的标记。这似乎给了我
指令
包含字符串的对象,但我不想依赖字符串匹配方法。这些库能给我一个被调用的方法的引用吗?嗯,这是python源代码中唯一的一种方法描述。当然,在调用getmembers
之前设置与运行时相同的所有导入会带来不便,我同意您的看法。但是,一旦设置到位,您就可以完全执行解释器在运行时执行的操作,在globals()中查找LOAD\u全局字符串或在locals()中查找LOAD\u ATTR。看看例如dir(globals()[''内置]
。持有这样一个引用,您可以查询\uuuuuuuuu文件
,\uuuuuuuuu名称
,以及类似的内容。有关详细信息,请参阅Allison Kaptur令人惊叹的纯python文章和Byterun的实现。但第二种方法看起来确实很有趣。