Show lines where a particular method is called
假设您有来自特定模块的特定方法(函数)(属于特定类别,可选)。是否可以通过库源代码的内省来打印调用(使用)该方法的所有行?它可以在内部(使用self.method_name())调用,也可以在外部使用(源文件1中的object1.method_name(),源文件中的object2.method_name()。文件2,…和源文件n中的objectn.method_name()。
示例可以在
我试着用
对于函数的使用,我会比较定期地进行grep。幸运的是,我从来没有对如此大量复制的东西感兴趣。
如果
正如其他人指出的,即使这样也会错过对方法名使用别名的调用。但只有您才能知道这是否是您的场景的问题。据我所知,这不是因为我所做的。
一种完全不同的方法(不依赖于名称)是在使用动态内省来确定调用方之后,使用记录调用的代码来检测函数。(我相信关于这个问题有很多疑问。)
我将此作为另一个答案添加,因为代码太大,无法在第一个代码中将所有代码放在一起。
这是一个非常简单的例子,用来找出使用抽象语法树调用的函数。
要将此应用于对象,您必须在输入对象时进行堆栈,然后跳转到它们的类,并且在遇到对函数的调用时,表示它是从该特定对象调用的。
当涉及到模块时,您可以看到这变得多么复杂。应输入每个模块及其子模块和映射的所有函数,以便跟踪对它们的调用等。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | import ast def walk (node): """ast.walk() skips the order, just walks, so tracing is not possible with it.""" end = [] end.append(node) for n in ast.iter_child_nodes(node): # Consider it a leaf: if isinstance(n, ast.Call): end.append(n) continue end += walk(n) return end def calls (tree): """Prints out exactly where are the calls and what functions are called.""" tree = walk(tree) # Arrange it into our list # First get all functions in our code: functions = {} for node in tree: if isinstance(node, (ast.FunctionDef, ast.Lambda)): functions[node.name] = node # Find where are all called functions: stack = [] for node in tree: if isinstance(node, (ast.FunctionDef, ast.Lambda)): # Entering function stack.append(node) elif stack and hasattr(node,"col_offset"): if node.col_offset<=stack[-1].col_offset: # Exit the function stack.pop() if isinstance(node, ast.Call): if isinstance(node.func, ast.Attribute): fname = node.func.value.id+"."+node.func.attr+"()" else: fname = node.func.id+"()" try: ln = functions[fname[:-2]].lineno ln ="at line %i" % ln except: ln ="" print"Line", node.lineno,"--> Call to", fname, ln if stack: print"from within", stack[-1].name+"()","that starts on line", stack[-1].lineno else: print"directly from root" code =""" import os def f1 (): print"I am function 1" return"This is for function 2" def f2 (): print f1() def f3 (): print"I am a function inside a function!" f3() f2() print"My PID:", os.getpid() """ tree = ast.parse(code) calls(tree) The output is: Line 9 --> Call to f1() at line 4 from within f2() that starts on line 8 Line 12 --> Call to f3() at line 10 from within f2() that starts on line 8 Line 13 --> Call to f2() at line 8 directly from root Line 14 --> Call to os.getpid() directly from root |
您可以使用AST或编译器模块来挖掘编译后的代码,并找出显式调用函数的位置。
也可以使用带有ast标志的compile()编译代码,并将其解析为抽象语法树。然后你去看看里面叫什么。
但是,您可以使用来自sys、inspect和traceback模块的一些技巧来跟踪代码执行期间发生的所有事情。
例如,您可以设置跟踪函数,该函数将在让每个解释器帧执行之前抓取它:
1 2 3 4 5 6 7 | import dis import sys def tracefunc (frame, evt, arg): print frame.f_code.co_filename, frame.f_lineno, evt print frame.f_code.co_name, frame.f_code.co_firstlineno #print dis.dis(f.f_code) sys.settrace(tracefunc) |
号
在这段代码之后,每个完成的步骤都将用包含代码的文件、步骤的行、代码对象开始的位置进行打印,并将其反汇编,这样您就可以看到正在执行的所有操作,或者也可以在后台执行(如果您取消对其的注释)。
如果要将执行的字节码与Python代码匹配,可以使用标记化模块。当标记化文件出现在跟踪中时,可以缓存它们,并在需要时从相应的行中提取python代码。
使用上面提到的所有东西,你可以做一些漫游,包括编写字节代码解压器,像在C中使用goto一样在代码上跳来跳去,强制中断线程(如果您不知道自己在做什么,则不推荐),跟踪哪个函数调用了您的函数(对于流服务器来说,很好地识别客户机占用了它们的流部分)。还有各种疯狂的事情。
我不得不说的高级疯狂的东西。不要以这种方式破坏代码流,除非它是绝对必要的,而且您不知道自己在做什么。
我会投反对票的,因为我提到这样的事情是可能的。
动态检测哪一个client()实例尝试获取内容的示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | from thread import get_ident import sys class Distributer: def read (self): # Who called me: cf = sys._current_frames() tid = get_ident() # Make it thread safe frame = cf[tid] # Now, I was called in one frame back so # go back and find the 'self' variable of a method that called me # and self, of course, contains the instance from which I was called client = frame.f_back.f_locals["self"] print"I was called by", client class Client: def __init__ (self, name): self.name = name def snatch (self): # Now client gets his content: content.read() def __str__ (self): return self.name content = Distributer() clients = [Client("First"), Client("Second"), Client("Third"), Client("Fourth"), Client("Etc...")] for client in clients: client.snatch() |
现在,您可以在跟踪函数中编写它,而不是固定方法,但是非常聪明,不依赖变量名,而是依赖地址和内容,并且可以跟踪何时何地发生的事情。大工作,但可能。
你可能知道,但我不能冒你不知道的风险:Python不是强类型语言。
因此,像
不仅如此,还有很多方法可以调用方法,只要假设如下:
1 2 3 4 5 6 7 | class Fun(object): def connect(self): return 100 objectn = Fun() (lambda x: x())(getattr(objectn, '{0}t'.format('co' + {0:'nnec'}[0]))) |
你不可能可靠地搜索
所以我很抱歉地说,即使有抽象语法树,(可选)注释和静态代码分析,它(几乎是?)找不到调用特定类的特定方法的所有位置。