文章目录
- Python 实现最小插件框架
- 1. 基础实现
- 项目结构
- plugin_base.py - 插件基类
- plugins/hello.py - 示例插件1
- plugins/goodbye.py - 示例插件2
- main.py - 主程序
- 2. 更高级的特性扩展
- 2.1 插件配置支持
- 2.2 插件依赖管理
- 2.3 插件热加载
- 3. 使用 setuptools 的入口点发现插件
- 3.1 修改项目结构
- 3.2 setup.py 示例
- 3.3 修改插件管理器
- 4. 插件隔离(使用importlib)
- 5. 最小完整示例(无目录结构要求)
Python 实现最小插件框架
一个非常简洁但功能完整的 Python 插件框架实现,这个框架具有以下特点:
- 动态加载插件
- 插件自动发现
- 简单的插件接口
- 支持插件隔离
1. 基础实现
项目结构
my_app/
├── main.py # 主程序
├── plugins/ # 插件目录
│ ├── __init__.py
│ ├── hello.py # 示例插件
│ └── goodbye.py # 示例插件
└── plugin_base.py # 插件基类
plugin_base.py - 插件基类
import abc
class PluginBase(abc.ABC):
"""所有插件的基类"""
@classmethod
@abc.abstractmethod
def initialize(cls):
"""插件初始化方法"""
pass
@abc.abstractmethod
def execute(self, *args, **kwargs):
"""插件执行方法"""
pass
plugins/hello.py - 示例插件1
from plugin_base import PluginBase
class HelloPlugin(PluginBase):
@classmethod
def initialize(cls):
print("HelloPlugin initialized")
return cls()
def execute(self, name="World"):
print(f"Hello, {name}!")
plugins/goodbye.py - 示例插件2
from plugin_base import PluginBase
class GoodbyePlugin(PluginBase):
@classmethod
def initialize(cls):
print("GoodbyePlugin initialized")
return cls()
def execute(self, name="World"):
print(f"Goodbye, {name}!")
main.py - 主程序
import importlib
import pkgutil
from pathlib import Path
from plugin_base import PluginBase
class PluginManager:
def __init__(self):
self.plugins = {}
def discover_plugins(self, plugin_dir="plugins"):
"""自动发现插件"""
plugin_path = Path(plugin_dir)
# 遍历插件目录
for finder, name, _ in pkgutil.iter_modules([str(plugin_path)]):
try:
module = importlib.import_module(f"{plugin_dir}.{name}")
for item in dir(module):
obj = getattr(module, item)
if (
isinstance(obj, type)
and issubclass(obj, PluginBase)
and obj != PluginBase
):
self.plugins[name] = obj
print(f"Found plugin: {name}")
except ImportError as e:
print(f"Failed to import plugin {name}: {e}")
def initialize_plugins(self):
"""初始化所有插件"""
return {
name: plugin.initialize()
for name, plugin in self.plugins.items()
}
if __name__ == "__main__":
manager = PluginManager()
manager.discover_plugins()
plugins = manager.initialize_plugins()
# 使用插件
plugins["hello"].execute("Python")
plugins["goodbye"].execute("Python")
2. 更高级的特性扩展
如果你想增强这个框架,可以考虑添加以下特性:
2.1 插件配置支持
修改 plugin_base.py
:
class PluginBase(abc.ABC):
@classmethod
@abc.abstractmethod
def initialize(cls, config=None):
"""支持传入配置"""
pass
2.2 插件依赖管理
在插件类中添加:
class HelloPlugin(PluginBase):
REQUIRED_PLUGINS = ['some_dependency']
@classmethod
def initialize(cls):
# 检查依赖
pass
2.3 插件热加载
添加热加载方法:
def reload_plugin(self, plugin_name):
"""重新加载插件"""
if plugin_name in self.plugins:
module = importlib.import_module(f"plugins.{plugin_name}")
importlib.reload(module)
self.plugins[plugin_name] = getattr(module, plugin_name.capitalize() + "Plugin")
3. 使用 setuptools 的入口点发现插件
更Pythonic的方式是使用setuptools的entry_points:
3.1 修改项目结构
my_app/
├── setup.py
├── my_app/
│ ├── __init__.py
│ ├── main.py
│ └── plugin_base.py
└── plugins/
├── hello.py
└── goodbye.py
3.2 setup.py 示例
from setuptools import setup, find_packages
setup(
name="my_app",
version="0.1",
packages=find_packages(),
entry_points={
'my_app.plugins': [
'hello = plugins.hello:HelloPlugin',
'goodbye = plugins.goodbye:GoodbyePlugin',
],
},
)
3.3 修改插件管理器
from importlib.metadata import entry_points
class PluginManager:
def discover_plugins(self):
"""使用entry_points发现插件"""
discovered_plugins = entry_points(group='my_app.plugins')
for ep in discovered_plugins:
try:
plugin_class = ep.load()
if (
isinstance(plugin_class, type)
and issubclass(plugin_class, PluginBase)
and plugin_class != PluginBase
):
self.plugins[ep.name] = plugin_class
except Exception as e:
print(f"Failed to load plugin {ep.name}: {e}")
4. 插件隔离(使用importlib)
如果需要更强的隔离,可以这样加载插件:
def load_plugin_with_isolation(plugin_path):
"""隔离加载插件"""
spec = importlib.util.spec_from_file_location(
"isolated_plugin",
plugin_path
)
module = importlib.util.module_from_spec(spec)
sys.modules["isolated_plugin"] = module
spec.loader.exec_module(module)
return module
5. 最小完整示例(无目录结构要求)
如果你想要一个真正最小化的实现(单文件):
# mini_plugin.py
import importlib
from pathlib import Path
class Plugin:
def execute(self):
raise NotImplementedError
class PluginManager:
def __init__(self):
self.plugins = {}
def load_plugin(self, plugin_path):
module_name = Path(plugin_path).stem
spec = importlib.util.spec_from_file_location(module_name, plugin_path)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
for name in dir(module):
obj = getattr(module, name)
if isinstance(obj, type) and issubclass(obj, Plugin) and obj is not Plugin:
self.plugins[module_name] = obj()
return obj()
return None
if __name__ == "__main__":
manager = PluginManager()
plugin = manager.load_plugin("hello_plugin.py") # 假设同级目录下有这个文件
if plugin:
plugin.execute()
配套的最简插件文件 hello_plugin.py
:
from mini_plugin import Plugin
class HelloPlugin(Plugin):
def execute(self):
print("Hello from minimal plugin!")
这个最小实现只有不到30行代码,但包含了插件框架的核心功能。