给定一个包含特定模块的python包,我想找到包中定义的方法和函数的所有用法,我想用pycharms find usages这样的东西,在给定的函数或方法中,它会显示调用此方法/函数的所有行。
假设我的包有很多模块,我想查找module_x
中定义的函数和方法的用法。
import inspect
callables = [method_name for method_name in dir(module)
if callable(getattr(module, method_name))]
module_inspected = inspect.getmodule(module)
module_file = module_inspected.__file__
module_x_callables = []
for name, member in inspect.getmembers(module):
# to see if the definitions are defined/imported in the member_file that we are looking
if name in callables:
module_x_callables.append(member)
member_file = inspect.getmodule(member).__file__
# print('{}: {},{}'.format(name, member, callable(member)))
print('{}'.format(name))
print('{}'.format(member))
# print('parent: {}'.format(inspect.getmodule(member)))
print('member_file: {}'.format(member_file))
if member_file == module_file:
source, line_no = inspect.findsource(member)
print(line_no)
print('\n')
注意:这种方法不会捕获类中的方法,但没关系,假设我想找到module_x
中定义的函数的所有用法。
我的问题是:我如何扫描包中的其他模块,看看它们是否使用了module_x
中的任何def,如果使用了,请返回行号。
我试着使用ast
,遍历树并试图找到所有的ast.Call
。这实际上返回了我所有的调用,但我不知道如何检查是否返回了所有在module_x
中定义的结果。甚至,我想使用regex,但例如,在两个不同的模块中可能有两个名为test_func
的函数。使用这种方法,我怎么知道我在打哪个电话?
string_code = open(file,'r').read()
tree = ast.parse(string_code)
for node in ast.walk(tree):
#print(node)
if isinstance(node, ast.Call):
print('call')
print(ast.dump(node))
print(inspect.getmodule(node))
print(func.value)
print(func.attr)
print('\n')
所以,总而言之,我的问题是:我怎样才能浏览一个文件或一个模块,并找到module_x
中定义的函数和方法的所有用法和行号。谢谢;)
1条答案
按热度按时间ffscu2ro1#
您只需要关心实际导入到您当前正在检查的模块中的名称。请注意,这里有一些复杂的情况:
bar
模块中的import foo
使得bar.foo
从外部可用。所以from bar import foo
实际上和import foo
是一样的。调用
foo.bar
对象。可以通过AST分析跟踪其中的一些使用,但Python是一种高度动态的语言,很快就会遇到限制。例如,你无法确定spam[0] = random.choice([foo.bar, foo.baz])
将生成什么。global
和nonlocal
语句,嵌套函数作用域可以改变父作用域中的名称。将导入模块
foo
并将其添加到全局名称空间,但仅在调用bar()
时。跟踪这一点很困难,因为您需要跟踪实际调用bar()
的时间。这甚至可能发生在当前模块(import weirdmodule; weirdmodule.bar()
)之外。如果忽略这些复杂性,只关注
import
语句中使用的名称,那么需要跟踪Import
和ImportFrom
节点,并跟踪作用域(这样就可以知道本地名称是否屏蔽了全局名称,或者导入的名称是否导入到了本地作用域),然后查找引用导入名称的Name(..., Load)
节点。我之前已经讨论过跟踪作用域,请参阅从Python AST中获取对应于给定名称的特定变量的所有节点。对于这个操作,我们可以将其简化为一个字典堆栈(封装在一个
collections.ChainMap()
示例中),并添加导入:现在,给定一个模块名称
foo.bar
和foo
包中的一个模块的以下源代码文件:您可以解析上面的代码,并使用以下代码提取所有
foo.bar
引用:请注意,
sitamet
作用域中的modalias1
名称不会被视为对导入模块的实际引用,因为它被用作本地名称。