Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/283.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/python-3.x/17.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何使用Python自省查找对方法的调用?_Python_Python 3.x_Introspection - Fatal编程技术网

如何使用Python自省查找对方法的调用?

如何使用Python自省查找对方法的调用?,python,python-3.x,introspection,Python,Python 3.x,Introspection,我刚刚在一个项目中发现了一些测试方法,这些方法没有必要的“test_u2;”前缀来确保它们实际运行。应该可以通过一点绒线来避免这种情况: 在代码库中查找所有TestCase断言调用 在调用层次结构中查找名称以“test\”开头的方法 如果没有此类方法,请打印错误消息 我想知道如何做前两个,这基本上归结为一个问题:如何在我的代码库中找到对特定方法的所有调用? Greping或其他文本搜索将不起作用,因为我需要反省结果并查找父方法等,直到我找到测试方法或没有其他调用方为止。我需要获得该方法的引用,以

我刚刚在一个项目中发现了一些测试方法,这些方法没有必要的“test_u2;”前缀来确保它们实际运行。应该可以通过一点绒线来避免这种情况:

  • 在代码库中查找所有
    TestCase
    断言调用
  • 在调用层次结构中查找名称以“test\”开头的方法
  • 如果没有此类方法,请打印错误消息
  • 我想知道如何做前两个,这基本上归结为一个问题:如何在我的代码库中找到对特定方法的所有调用?


    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信息(成员)

    这将允许您访问文件中定义的每个函数, 并访问每个可执行语句,看看它是否是您关心的方法调用。
    然后,您就可以使用潜在的测试方法来做正确的事情。

    这里有两种可能的方法

  • 静态方法:

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

    下面是一个Visitor子类,它可以读取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))
    
    您可以调整该代码以浏览解释程序堆栈来识别每个调用的调用方,但这并不容易,因为您将获得调用方函数的非限定名称,并且必须使用文件名和行号来唯一地识别调用方


  • 对于所有与python相关的问题,请始终使用generic[python]标记。根据您的判断使用特定于版本的标记。这似乎给了我
    指令
    包含字符串的对象,但我不想依赖字符串匹配方法。这些库能给我一个被调用的方法的引用吗?嗯,这是python源代码中唯一的一种方法描述。当然,在调用
    getmembers
    之前设置与运行时相同的所有导入会带来不便,我同意您的看法。但是,一旦设置到位,您就可以完全执行解释器在运行时执行的操作,在globals()中查找LOAD\u全局字符串或在locals()中查找LOAD\u ATTR。看看例如
    dir(globals()[''内置]
    。持有这样一个引用,您可以查询
    \uuuuuuuuu文件
    \uuuuuuuuu名称
    ,以及类似的内容。有关详细信息,请参阅Allison Kaptur令人惊叹的纯python文章和Byterun的实现。但第二种方法看起来确实很有趣。