显示调用特定方法的行
Show lines where a particular method is called
假设您有来自特定模块的特定方法(函数)
(特定 class,可选)。是否可以通过库源代码的自省打印调用(使用)该方法的所有行?
它可以在内部调用(使用 self.method_name())或外部调用
(源文件1中的object1.method_name(),源文件中的object2.method_name()
文件 2, ... 和源文件 N 中的 objectN.method_name()).
可以在 re
模块及其方法 re.findall
上显示示例。
我尝试用 grep
打印行,但这是一个问题
具有相同名称的方法(例如,我尝试使用名为 connect() 的方法,
但是 24 classes 有一个名为 connect 的方法......我想过滤这个
特别是 class(and/or 模块)。
你可能知道,但我不能冒你不知道的风险:Python 不是强类型语言。
因为像 objectn.connect()
这样的东西不关心 objectn
是什么(它可以是一个模块,一个 class,一个获取属性的函数,...) .它也不关心 connect
是一个方法还是恰好是可调用的 class 或函数工厂。当您尝试获取属性 connect
时,它会很乐意接受任何以某种方式返回可调用对象的 objectn
。
不仅如此,还有很多方法可以调用方法,假设是这样的:
class Fun(object):
def connect(self):
return 100
objectn = Fun()
(lambda x: x())(getattr(objectn, '{0}t'.format('co' + {0:'nnec'}[0])))
您无法可靠地搜索 objectn.connect()
并匹配 (lambda x: x())(getattr(objectn, '{0}t'.format('co' + {0:'nnec'}[0])))
,但两者都会调用 objectn
的方法 connect
。
所以我很遗憾地说,即使使用抽象语法树、(可选)注释和静态代码分析,也(几乎?)不可能找到特定方法的所有地方 class 被调用。
我经常 grep 查找函数的使用情况。幸运的是,我从来没有对如此大量重复的东西感兴趣。
如果 Class.method
的错误命中太常见而无法手动过滤,我可能会这样做,而不是编写一次性代码。首先 grep for class Class
以找到具有 class 定义的 module
并记下行的范围。然后 grep 该模块以获得 self.method
并删除或忽略该范围之外的命中。然后为 import module
和 from module
搜索所有感兴趣的模块,以查找可能使用 class 和方法的模块。然后根据导入的具体形式grep成组的模块。
正如其他人指出的那样,即使这样也会错过使用别名作为方法名称的调用。但是只有您知道这是否是您的场景的问题。据我所知,我所做的并不是这样。
一种完全不同的方法(不依赖于名称)是在使用动态内省确定调用者之后,使用记录其调用的代码来检测函数。 (我相信有很多关于这个的问答。)
您可以使用 ast 或编译器模块来挖掘编译后的代码,找出显式调用函数的地方。
您也可以只使用带 ast 标志的 compile() 编译代码,并将其解析为抽象语法树。那你去看看里面什么地方叫什么
但是您可以使用 sys、inspect 和 traceback 模块中的一些技巧来追踪代码执行期间发生的所有事情。
例如,您可以设置您的跟踪函数,在执行之前捕获每个解释器帧:
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代码匹配,可以使用tokenize模块。
当标记化文件出现在跟踪中时,您可以对其进行缓存,并在需要时从相应行中获取 Python 代码。
使用所有提到的东西,你可以四处走动,包括编写字节码反编译器,像在 C 中使用 goto 一样跳转你的代码,
强行中断线程(如果您不完全知道自己在做什么,则不推荐),跟踪哪个函数调用了您的函数(对于流媒体服务器识别正在赶上他们的流部分的客户端非常有用),
以及各种疯狂的东西。
我不得不说的高级疯狂的东西。
不要以这种方式搞乱代码流,除非它是绝对必要的并且你不知道你在做什么。
我会因为我提到这样的事情甚至是可能的而被否决。
动态检测哪个 client() 实例试图获取内容的示例:
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()
现在,您可以在跟踪函数而不是固定方法中编写此代码,但要巧妙地不依赖变量名,而是依赖地址和其他内容,您可以跟踪何时何地发生了什么。伟大的工作,但可能。
我将此添加为另一个答案,因为代码太大,无法在第一个答案中全部压缩。
这是一个非常简单的示例,用于使用抽象语法树找出调用了哪个函数。
要将此应用于对象,您必须在输入它们时堆叠它们,然后跳转到它们的 class 并在遇到对函数的调用时说它是从该特定对象调用的。
你可以看到当涉及到模块时这会变得多么复杂。
应输入每个模块并映射其子模块和所有功能,以便您可以跟踪对它们的调用等。
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
假设您有来自特定模块的特定方法(函数) (特定 class,可选)。是否可以通过库源代码的自省打印调用(使用)该方法的所有行? 它可以在内部调用(使用 self.method_name())或外部调用 (源文件1中的object1.method_name(),源文件中的object2.method_name() 文件 2, ... 和源文件 N 中的 objectN.method_name()).
可以在 re
模块及其方法 re.findall
上显示示例。
我尝试用 grep
打印行,但这是一个问题
具有相同名称的方法(例如,我尝试使用名为 connect() 的方法,
但是 24 classes 有一个名为 connect 的方法......我想过滤这个
特别是 class(and/or 模块)。
你可能知道,但我不能冒你不知道的风险:Python 不是强类型语言。
因为像 objectn.connect()
这样的东西不关心 objectn
是什么(它可以是一个模块,一个 class,一个获取属性的函数,...) .它也不关心 connect
是一个方法还是恰好是可调用的 class 或函数工厂。当您尝试获取属性 connect
时,它会很乐意接受任何以某种方式返回可调用对象的 objectn
。
不仅如此,还有很多方法可以调用方法,假设是这样的:
class Fun(object):
def connect(self):
return 100
objectn = Fun()
(lambda x: x())(getattr(objectn, '{0}t'.format('co' + {0:'nnec'}[0])))
您无法可靠地搜索 objectn.connect()
并匹配 (lambda x: x())(getattr(objectn, '{0}t'.format('co' + {0:'nnec'}[0])))
,但两者都会调用 objectn
的方法 connect
。
所以我很遗憾地说,即使使用抽象语法树、(可选)注释和静态代码分析,也(几乎?)不可能找到特定方法的所有地方 class 被调用。
我经常 grep 查找函数的使用情况。幸运的是,我从来没有对如此大量重复的东西感兴趣。
如果 Class.method
的错误命中太常见而无法手动过滤,我可能会这样做,而不是编写一次性代码。首先 grep for class Class
以找到具有 class 定义的 module
并记下行的范围。然后 grep 该模块以获得 self.method
并删除或忽略该范围之外的命中。然后为 import module
和 from module
搜索所有感兴趣的模块,以查找可能使用 class 和方法的模块。然后根据导入的具体形式grep成组的模块。
正如其他人指出的那样,即使这样也会错过使用别名作为方法名称的调用。但是只有您知道这是否是您的场景的问题。据我所知,我所做的并不是这样。
一种完全不同的方法(不依赖于名称)是在使用动态内省确定调用者之后,使用记录其调用的代码来检测函数。 (我相信有很多关于这个的问答。)
您可以使用 ast 或编译器模块来挖掘编译后的代码,找出显式调用函数的地方。
您也可以只使用带 ast 标志的 compile() 编译代码,并将其解析为抽象语法树。那你去看看里面什么地方叫什么
但是您可以使用 sys、inspect 和 traceback 模块中的一些技巧来追踪代码执行期间发生的所有事情。
例如,您可以设置您的跟踪函数,在执行之前捕获每个解释器帧:
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代码匹配,可以使用tokenize模块。 当标记化文件出现在跟踪中时,您可以对其进行缓存,并在需要时从相应行中获取 Python 代码。
使用所有提到的东西,你可以四处走动,包括编写字节码反编译器,像在 C 中使用 goto 一样跳转你的代码, 强行中断线程(如果您不完全知道自己在做什么,则不推荐),跟踪哪个函数调用了您的函数(对于流媒体服务器识别正在赶上他们的流部分的客户端非常有用), 以及各种疯狂的东西。
我不得不说的高级疯狂的东西。 不要以这种方式搞乱代码流,除非它是绝对必要的并且你不知道你在做什么。
我会因为我提到这样的事情甚至是可能的而被否决。
动态检测哪个 client() 实例试图获取内容的示例:
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()
现在,您可以在跟踪函数而不是固定方法中编写此代码,但要巧妙地不依赖变量名,而是依赖地址和其他内容,您可以跟踪何时何地发生了什么。伟大的工作,但可能。
我将此添加为另一个答案,因为代码太大,无法在第一个答案中全部压缩。
这是一个非常简单的示例,用于使用抽象语法树找出调用了哪个函数。
要将此应用于对象,您必须在输入它们时堆叠它们,然后跳转到它们的 class 并在遇到对函数的调用时说它是从该特定对象调用的。
你可以看到当涉及到模块时这会变得多么复杂。 应输入每个模块并映射其子模块和所有功能,以便您可以跟踪对它们的调用等。
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