遇到一个需求,需要尽可能的尝试触发python模块里的行为,比如函数,类实例这样,感觉和java里的反射有点像;通过调研发现python里有getattr这个方法,类似于java里的反射机制,可以通过字符串比较方便的获取到类里的成员函数。通过importlib导入模块,然后通过inspect检查函数和类,最后在通过getattr获取对应对象执行。pytimer和packadds是我自写的不影响整体功能的函数,防止超时和解压文件。
"""
PyWalker:根据路径动态导入py文件,并尝试创建类实例和执行函数
"""
import sys
import importlib
import inspect
import pytimer
import packadds
class PyWalker:
"""
动态的导入python模块并尝试触发其中的不同方法
"""
timer = pytimer.PyTimer()
packer = packadds.PackProcessor()
def __init__(self):
"""
初始化
"""
@staticmethod
def read_module(module_path: str):
"""
读取py文件的路径,尝试将其导入
importlib的路径比较特殊,如test/sample.py需要转化变为
test.sample
"""
if '/' in module_path:
module_path = module_path.replace('/', '.')
if '\\' in module_path:
module_path = module_path.replace('\\', '.')
if module_path.endswith('.py'):
module_path = module_path[:-3]
module_imported = importlib.import_module(module_path)
return module_imported
@staticmethod
def get_function_name(module_imported):
"""
获取模组里的所有函数名
"""
function_name_list = []
for member_name, _ in inspect.getmembers(module_imported, inspect.isfunction):
function_name_list.append(member_name)
print("函数", function_name_list)
return function_name_list
@staticmethod
def get_class_name(module_imported):
"""
获取模组里的所有类名
"""
class_name_list = []
for member_name, _ in inspect.getmembers(module_imported, inspect.isclass):
class_name_list.append(member_name)
print("类", class_name_list)
return class_name_list
@staticmethod
def eval_function(module_imported, function_name: str, *args):
"""
根据函数名动态调用函数
"""
module_function = getattr(module_imported, function_name)
function_return = module_function(*args)
return function_return
@staticmethod
def parse_module(module_path: str):
"""
解析模组,获取函数名和类名,然后尝试执行函数
"""
print("导入", module_path, "输出:")
module_imported = PyWalker.read_module(module_path)
class_name_list = PyWalker.get_class_name(module_imported)
function_name_list = PyWalker.get_function_name(module_imported)
for each_class in class_name_list:
PyWalker.timer.count_time(PyWalker.parse_class, module_imported, each_class)
for each_function in function_name_list:
PyWalker.timer.count_time(PyWalker.eval_function, module_imported, each_function)
@staticmethod
def parse_class(module_imported, each_class):
"""
解析类,创建实例,尝试执行函数
"""
module_class = getattr(module_imported, each_class)
function_name_list = PyWalker.get_function_name(module_class)
if function_name_list[0] == '__init__':
# 因为已经创建过实例所以不再执行init
function_name_list.pop(0)
class_instance = module_class()
for each_function in function_name_list:
PyWalker.eval_function(class_instance, each_function)
if PyWalker.timer.get_errors():
print(PyWalker.timer.get_errors())
@staticmethod
def parse_packet(packet_path: str):
"""
输入一个whl或zip包的路径,尝试解压然后逐个导入运行
"""
if packet_path.endswith('.py'):
# 如果是py文件就不用解压了
pyfile_list = [packet_path]
else:
unzipped_dir = PyWalker.packer.unzip_path(packet_path)
pyfile_list = PyWalker.packer.get_dir_files(unzipped_dir, '.py')
for each_pyfile in pyfile_list:
try:
PyWalker.parse_module(each_pyfile)
except Exception as e:
print("error:", each_pyfile, e)
PyWalker.packer.delete_unzips()
if __name__ == '__main__':
file_path = sys.argv[1]
my_walker = PyWalker()
my_walker.parse_packet(file_path)