假设您有特定模块的特定方法(函数) (特定类别,可选)。是否可以通过内省库源代码打印调用(使用)该方法的所有行? 它可以在内部调用(使用self.method_name())或在外部调用 (源文件1中的object1.method_name(),源中的object2.method_name() 源文件N)中的文件2,...和objectN.method_name()。
可以在re
模块上显示一个示例,它的方法为re.findall
。
我尝试用grep
打印这些行,但这是一个问题
具有相同名称的方法(例如,我尝试使用名为connect()的方法,
但是24个类有一个名为connect的方法...我想过滤这个
特定的类(和/或模块)。
答案 0 :(得分:3)
我非常经常地使用函数。幸运的是,我从未对那些会被重复的东西感兴趣。
如果Class.method
的错误点击太频繁而无法手动过滤,那么我可能会做的不是编写一次性代码。 class Class
的第一个grep,用类定义查找module
并记下行的范围。然后grep该模块self.method
并删除或忽略该范围之外的命中。然后grep import module
和from module
感兴趣的所有模块,以查找可能使用该类和方法的模块。然后根据具体的导入形式grep模块组。
正如其他人所指出的那样,即使这样也会错过使用别名作为方法名称的调用。但只有您可以知道这是否适用于您的方案。我所知道的不是我所做的。
完全不同的方法,不依赖于名称,是在使用动态内省确定调用者之后,使用记录其调用的代码来检测函数。 (我相信还有SO Q&关于这一点。)
答案 1 :(得分:1)
你可能知道,但我不能冒你不知道的风险:Python不是强类型语言。
像objectn.connect()
之类的东西并不关心objectn
是什么(它可能是一个模块,一个类,一个获取属性的函数,......)。它也不关心connect
是一个方法,还是一个碰巧可以调用的类或函数的工厂。当你试图获得属性objectn
时,它会愉快地接受以某种方式回复可调用的任何connect
。
不仅如此,有很多方法可以调用方法,只需要假设:
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])))
的匹配,但两者都会调用connect
的方法objectn
。
所以我很遗憾地说,即使使用抽象语法树,(可选)注释和静态代码分析,也几乎不可能找到调用特定类的特定方法的所有地方。 / p>
答案 2 :(得分:1)
您可以使用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()
现在,您在跟踪函数中编写此代码而不是固定方法,但是巧妙地,不依赖于变量名称而是依赖于地址和内容,您可以跟踪在何时何地发生的事情。很大的工作,但可能。
答案 3 :(得分:1)
我将此作为另一个答案添加,因为代码太大而无法在第一个代码中将所有内容组合在一起。
这是一个非常简单的例子,用于找出使用抽象语法树调用哪个函数。
要在输入它们时必须堆叠的对象上应用它,然后跳转到它们的类,并在遇到对函数的调用时说它是从该特定对象调用的。
您可以看到模块涉及时会变得多么复杂。 应输入每个模块,并映射其子模块和所有功能,以便您可以跟踪对它们的调用等。
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