python - 如何使用 Python 内省(introspection)查找对方法的调用?

标签 python python-3.x introspection

我刚刚在项目中发现了一些测试方法,这些方法没有所需的“test_”前缀来确保它们实际运行。通过一些 linting 应该可以避免这种情况:

  1. 在代码库中查找所有 TestCase 断言调用。
  2. 在调用层次结构中查找名称以“test_”开头的方法。
  3. 如果没有这样的方法,则打印错误消息。

我想知道如何执行前两个操作,这基本上可以归结为一个问题:如何在我的代码库中找到对特定方法的所有调用?

Grepping 或其他文本搜索不起作用,因为我需要内省(introspection)结果并查找父方法等,直到我到达测试方法或没有更多调用者。我需要获取该方法的引用,以避免匹配与我正在查找的方法同名的方法。

最佳答案

这里有两种可能的方法。

  1. 静态方法:

    您可以使用 ast 模块解析代码库来识别所有函数调用并一致地存储调用的来源和目标。您必须识别所有类和函数定义才能跟踪每个调用的当前上下文。这里的限制是,如果您调用实例方法,则没有简单的方法来识别该方法实际属于哪个类。如果您使用引用模块的变量,则相同

    这是一个 Visitor 子类,它可以读取 Python 源文件并构建一个字典 {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)
    
  2. 动态方法:

    如果您确实想确定调用了什么方法,当多个方法可以共享相同的名称时,您将不得不使用动态方法。您可以装饰类中的各个函数或所有方法,以便计算它们被调用的次数,以及(可选)它们是从哪里调用的。然后您将开始测试并检查实际发生的情况。

    这是一个函数,它将装饰类中的所有方法,以便所有调用的数字都将存储在字典中:

    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 - 如何使用 Python 内省(introspection)查找对方法的调用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55368638/

相关文章:

python - Pandas merge_asof 问题 : one-to-multiple merge

Django + Channels + Daphne + Caddy + 管理文件上传 = 413 错误

python - 内省(introspection)类属性以查看它是否被修饰,而不访问属性值

python - 当 help() 列出方法签名时,斜杠意味着什么?

apache-flex - Actionscript 3 自省(introspection)——函数名称

python - 从任务栏删除窗口,Windows 7

python - np.random.permutation, np.random.choice 的时间表现

Python ElementTree 从根目录中删除元素时出错

Python turtle 图

python-3.x - Pandas append 返回带有 NaN 值的 DF