python动态加载插件
获取当前脚本路径
根据当前位置获取绝对路径,区分打包后的可执行文件和开发环境
def get_plugins_dir(relative_path: str = "plugins") -> str:
"""
获取应用程序的插件目录
如果是打包后的exe,返回exe所在目录
如果是脚本,返回脚本所在目录
"""
if getattr(sys, "frozen", False):
# 打包后的exe
pos = os.path.dirname(sys.executable)
else:
# 开发环境
pos = os.path.dirname(os.path.abspath(__file__))
pos = os.path.join(pos, relative_path)
if not os.path.exists(pos):
os.makedirs(pos)
# 可选添加示例插件
return pos
加载环境变量
使用闭包,允许热加载功能
def make_env_manager():
"""
设置动态加载包的环境变量
"""
env_path = []
def inner(full_path: str):
nonlocal env_path
# 清空
for path in list(env_path):
sys.path.remove(path)
# 记录环境路径
env_path.append(os.path.dirname(full_path))
for root, dirs, files in os.walk(full_path):
if root not in sys.path:
env_path.append(root)
# 添加
for path in env_path:
sys.path.insert(0, path)
return inner
加载插件
区分加载单个文件和包
def load_file(file_path: str, validate_func: Optional[Callable] = None):
"""
加载单个插件文件
"""
def validate_plugin(module):
if not hasattr(module, "execute"):
sys.modules.pop(module)
raise ImportError(f"插件 {name} 缺少 execute() 函数")
return True
if validate_func is None:
validate_func = validate_plugin
try:
name = os.path.splitext(os.path.basename(file_path))[0]
spec = importlib.util.spec_from_file_location(name, file_path)
if spec is None:
raise ImportError(f"无法从文件创建模块规范: {file_path}")
module = importlib.util.module_from_spec(spec)
sys.modules[name] = module
spec.loader.exec_module(module)
return module if validate_func(module) else None
except Exception as e:
# log error
traceback.print_exc()
return None
def load_package(package_path: str, validate_func: Optional[Callable] = None):
"""
加载单个插件包
"""
def validate_plugin(module):
if not hasattr(module, "execute"):
sys.modules.pop(module)
raise ImportError(f"插件 {name} 缺少 execute() 函数")
return True
if validate_func is None:
validate_func = validate_plugin
try:
name = os.path.basename(package_path)
if package_path not in sys.path:
sys.path.insert(0, package_path)
module = importlib.import_module(name)
if validate_func(module):
return module
try:
main_module = importlib.import_module(f"{name}.main")
return main_module if validate_func(main_module) else None
except ImportError:
raise ImportError(f"插件包 {name} 中未找到可用的主模块")
except Exception as e:
traceback.print_exc()
return None
获取所有插件
根据传入路经筛选插件
def scan_plugins(path: str) -> list[str]:
file_ans = list(
filter(
lambda x: x != "",
map(
lambda name: (
os.path.join(path, name)
if not name.startswith(".")
and not name.startswith("__")
and os.path.splitext(name)[1] in [".py", ""]
else ""
),
os.listdir(path),
),
)
)
return file_ans
Example
-
获取插件文件夹路径
-
获取所有插件
-
加载环境变量
-
加载插件
if __name__ == "__main__":
base_path = get_plugins_dir()
p = scan_plugins(base_path)
env_manager = make_env_manager()
env_manager(base_path)
loaded_plugins = load_plugins(p)
print(loaded_plugins)
本文来自博客园,作者:ling-yuan,转载请注明原文链接:https://www.cnblogs.com/ling-yuan/p/19174469

浙公网安备 33010602011771号