遇到一个需求,需要尽可能的尝试触发python模块里的行为,比如函数,类实例这样,感觉和java里的反射有点像;通过调研发现python里有getattr这个方法,类似于java里的反射机制,可以通过字符串比较方便的获取到类里的成员函数。通过importlib导入模块,然后通过inspect检查函数和类,最后在通过getattr获取对应对象执行。pytimer和packadds是我自写的不影响整体功能的函数,防止超时和解压文件。
"""
PyWalker:根据路径动态导入py文件,并尝试创建类实例和执行函数
"""
import sys
import importlib
import inspect
import pytimer
import packaddsclass PyWalker:"""动态的导入python模块并尝试触发其中的不同方法"""timer = pytimer.PyTimer()packer = packadds.PackProcessor()def __init__(self):"""初始化"""@staticmethoddef 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@staticmethoddef 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@staticmethoddef 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@staticmethoddef eval_function(module_imported, function_name: str, *args):"""根据函数名动态调用函数"""module_function = getattr(module_imported, function_name)function_return = module_function(*args)return function_return@staticmethoddef 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)@staticmethoddef 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__':# 因为已经创建过实例所以不再执行initfunction_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())@staticmethoddef 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)