我正在尝试在python脚本中获取函数的源代码,被调用者列表,默认值,关键字,args和varargs。
目前,我正在导入模块并使用python inspect
模块的getmembers
函数并传递isfunction
参数,如下所示:
members = inspect.getmembers(myModule, inspect.isfunction)
但是,如果我无法使用myModule
的导入,则此方法无效(因为必须首先导入myModule
)。
我尝试使用python ast
模块到parse
和dump
语法树,但是获取函数源涉及非常hacky技术和/或可疑而且远离可维护的第三方库。我相信我已经彻底搜索了文档和stackoverflow,但未能找到合适的解决方案。我错过了什么吗?
答案 0 :(得分:0)
一种可能的解决方法是使用自定义函数对: test -| a b | a b + ;
函数进行monkeypatch,该函数从不抛出ImportError并返回虚拟模块:
__import__
这将允许您导入import builtins
def force_import(module):
original_import = __import__
def fake_import(*args):
try:
return original_import(*args)
except ImportError:
return builtins
builtins.__import__ = fake_import
module = original_import(module)
builtins.__import__ = original_import
return module
,即使无法导入其依赖项。然后你可以像往常一样使用myModule
:
inspect.getmembers
此解决方案的一个问题是它只适用于失败的导入。如果myModule = force_import('myModule')
members = inspect.getmembers(myModule, inspect.isfunction)
尝试访问导入模块的任何成员,则导入将失败:
myModule
# myModule.py
import this_module_doesnt_exist # works
print(this_module_doesnt_exist.variable) # fails
为了解决这个问题,你可以创建一个永远不会抛出AttributeError的虚拟类:
force_import('myModule')
# AttributeError: module 'builtins' has no attribute 'variable'
(有关您可能必须实施的dunder方法的列表,请参阅this question。)
现在,如果class DummyValue:
def __call__(self, *args, **kwargs):
return self
__getitem__ = __setitem__ = __delitem__ = __call__
__len__ = __length_hint__ = __bool__ = __call__
__iter__ = __next__ = __call__
__getattribute__ = __call__
__enter__ = __leave__ = __call__
__str__ = __repr__ = __format__ = __bytes__ = __call__
# etc
返回此类的实例(将force_import
更改为return builtins
),则导入return DummyValue()
将会成功。
答案 1 :(得分:0)
所以我更多地环顾四周并迅速使用this dude's answer解决了每个函数的来源。它还没有接近完美,但是如果你感兴趣的话就在这里:
===========================================================================
---Run main --
===========================================================================
./stackoverflow/libcallback_strings/lib/libcallback_strings.exe
Shut up, K-9!
show_string mystring[my test string] gTestStr[my test string]
cbfnString test_str[my test string] gTestStr[my test string]
expect[expect test:1==1]
cbfnExpect format[expect test:%s] format.length[14]
cbfnExpect res[expect test:(null)] gExpectResultStr[expect test:1==1]
**
ERROR:/media/george/SharedData/Projects/Vala/LanguageServer/stackoverflow/libcallback_strings/main.vala:15:cbfnExpect: assertion failed: (res == gExpectResultStr)
stackoverflow/so_examples.mk:252: recipe for target 'libcallback_strings' failed
make: *** [libcallback_strings] Aborted
The terminal process terminated with exit code: 2
输出:
import ast
import re
import json
st = open('filename.py').read()
tree = ast.parse(st)
functions_info = {}
def parse(function):
global st
global functions_info
fn_info = {}
fn_info['Args'] = []
fn_info['Source'] = []
fn_info['Callees'] = []
print(function.name)
for arg in function.args.args:
fn_info['Args'].append(arg.arg)
lastBody = function.body[-1]
while isinstance (lastBody,(ast.For,ast.While,ast.If)):
lastBody = lastBody.Body[-1]
lastLine = lastBody.lineno
if isinstance(st,str):
st = st.split("\n")
for i , line in enumerate(st,1):
if i in range(function.lineno,lastLine+1):
# print(line)
fn_info['Source'].append(line)
for line in fn_info['Source']:
if not line.lstrip().startswith('#'):
fn_pattern = r'(\w+)\('
match = re.search(fn_pattern, line)
if match:
callee = match.group(1)
fn_info['Callees'].append(callee)
functions_info[function.name] = fn_info
for obj in tree.body:
if isinstance(obj, ast.ClassDef):
for func in obj.body:
if isinstance(func, (ast.FunctionDef)):
parse(func)
if isinstance(obj, ast.FunctionDef):
parse(obj)
print(json.dumps(functions_info, indent=4))