Python 實現最小插件框架

文章目錄

  • 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 abcclass PluginBase(abc.ABC):"""所有插件的基類"""@classmethod@abc.abstractmethoddef initialize(cls):"""插件初始化方法"""pass@abc.abstractmethoddef execute(self, *args, **kwargs):"""插件執行方法"""pass

plugins/hello.py - 示例插件1

from plugin_base import PluginBaseclass HelloPlugin(PluginBase):@classmethoddef initialize(cls):print("HelloPlugin initialized")return cls()def execute(self, name="World"):print(f"Hello, {name}!")

plugins/goodbye.py - 示例插件2

from plugin_base import PluginBaseclass GoodbyePlugin(PluginBase):@classmethoddef 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 PluginBaseclass 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] = objprint(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.abstractmethoddef initialize(cls, config=None):"""支持傳入配置"""pass

2.2 插件依賴管理

在插件類中添加:

class HelloPlugin(PluginBase):REQUIRED_PLUGINS = ['some_dependency']@classmethoddef 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_packagessetup(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_pointsclass 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_classexcept 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"] = modulespec.loader.exec_module(module)return module

5. 最小完整示例(無目錄結構要求)

如果你想要一個真正最小化的實現(單文件):

# mini_plugin.py
import importlib
from pathlib import Pathclass Plugin:def execute(self):raise NotImplementedErrorclass PluginManager:def __init__(self):self.plugins = {}def load_plugin(self, plugin_path):module_name = Path(plugin_path).stemspec = 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 Noneif __name__ == "__main__":manager = PluginManager()plugin = manager.load_plugin("hello_plugin.py")  # 假設同級目錄下有這個文件if plugin:plugin.execute()

配套的最簡插件文件 hello_plugin.py:

from mini_plugin import Pluginclass HelloPlugin(Plugin):def execute(self):print("Hello from minimal plugin!")

這個最小實現只有不到30行代碼,但包含了插件框架的核心功能。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/78724.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/78724.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/78724.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

電感詳解:定義、作用、分類與使用要點

一、電感的基本定義 電感(Inductor) 是由導線繞制而成的儲能元件,其核心特性是阻礙電流變化,將電能轉化為磁能存儲。 基本公式: 自感電動勢: E -L * (di/dt) (L:電感值&#xff0c…

運行一次性任務與定時任務

運行一次性任務與定時任務 文章目錄 運行一次性任務與定時任務[toc]一、使用Job運行一次性任務1.創建一次性任務2.測試一次性任務3.刪除Job 二、使用CronJob運行定時任務1.創建定時任務2.測試定時任務3.刪除CronJob 一、使用Job運行一次性任務 1.創建一次性任務 (…

對話記憶(Conversational Memory)

一、引言 在與大型語言模型(LLM)交互的場景中,對話記憶(Conversational Memory)指的是模型能夠在多輪對話中保留、檢索并利用先前上下文信息的能力。這一機制使得對話系統不再僅僅是“問答機”,而是能夠持…

【HD-RK3576-PI】VNC 遠程桌面連接

在當今數字化時代,高效便捷的操作方式是技術愛好者與專業人士的共同追求。對于使用 HD-RK3576-PI微型單板計算機的用戶而言,當面臨沒有顯示屏的場景時,如何實現遠程操作桌面系統呢?別擔心,VNC 遠程桌面連接將為你解決這…

【unity游戲開發介紹之UGUI篇】UGUI概述和基礎使用

注意:考慮到UGUI的內容比較多,我將UGUI的內容分開,并全部整合放在【unity游戲開發介紹之UGUI篇】專欄里,感興趣的小伙伴可以前往逐一查看學習。 文章目錄 前言1、UI系統的重要性2、UGUI概述2.1 基本定義2.2 UGUI發展歷史 3、學習U…

Ubuntu 系統深度清理:徹底卸載 Redis 服務及殘留配置

Ubuntu 系統深度清理:徹底卸載 Redis 服務及殘留配置 在Ubuntu系統中,Redis是一種廣泛使用的內存數據存儲系統,用于緩存和消息傳遞等場景。然而,有時候我們需要徹底卸載Redis,以清理系統資源或為其他應用騰出空間。本…

[ARC196A] Adjacent Delete 題解

假設 n n n 是偶數。如果我們忽略刪除相鄰數的條件,即可以任選兩個數相減,那么答案應該是前 n 2 \frac{n}{2} 2n? 大的數(記作“較大數”)的和減去前 n 2 \frac{n}{2} 2n? 小的數(記作“較小數”)的和…

Linux上位機開發實踐(關于Qt的移植)

【 聲明:版權所有,歡迎轉載,請勿用于商業用途。 聯系信箱:feixiaoxing 163.com】 linux平臺上面,很多界面應用,都是基于qt開發的。不管是x86平臺,還是arm平臺,qt使用的地方都比較多。…

”插入排序“”選擇排序“

文章目錄 插入排序1. 直接插入排序(O(n^2))舉例1:舉例2:直插排序的"代碼"直插排序的“時間復雜度” 2. 希爾排序(O(n^1.3))方法一方法二(時間復雜度更優) 選擇排序堆排序直接選擇排序 我們學過冒泡排序,堆排序等等。(回…

FPGA_BD Block Design學習(一)

PS端開發流程詳細步驟 1.第一步:打開Vivado軟件,創建或打開一個工程。 2.第二步:在Block Design中添加arm核心,并將其配置為IP核。 3.第三步:配置arm核心的外設信息,如DDR接口、時鐘頻率、UART接口等。 …

【Python] pip制作離線包

制作離線安裝包是一種非常實用的方法,尤其是在網絡環境受限或需要在多臺機器上部署相同環境時。以下是詳細的步驟,幫助您創建一個包含所有依賴項的離線安裝包,并在后續環境中復用。 步驟 1:準備工具和環境 確保您有一臺可以訪問互…

為啥物聯網用MQTT?

前言 都說物聯網用MQTT,那分別使用Http和Mqtt發送“Hello”,比較一下就知道啦 HTTP HTTP請求報文由請求行、頭部字段和消息體組成。一個最簡單的HTTP POST請求如下: POST / HTTP/1.1 Host: example.com Content-Length: 5 Content-Type: …

操作系統 ------ 五種IO模型

阻塞IO:一個IO請求操作,準備階段和復制階段都會阻塞應用程序,直到操作完全完成 非阻塞IO:一個IO操作請求,先判斷準備階段是否完成,如果未完成立即返回,否則,進入復制階段&#xff0…

service和endpoints是如何關聯的?

在Kubernetes中,Service 和 Endpoints 是兩個密切關聯的對象,它們共同實現了服務發現和負載均衡的功能。以下是它們之間的關聯和工作原理: 1. Service 的定義 Service 是一種抽象,定義了一組邏輯上相關的 Pod,以及用…

程序化廣告行業(78/89):多因素交織下的行業剖析與展望

程序化廣告行業(78/89):多因素交織下的行業剖析與展望 在程序化廣告這片充滿活力又不斷變化的領域,持續學習和知識共享是我們緊跟潮流、實現突破的關鍵。一直以來,我都渴望能與大家一同探索這個行業的奧秘&#xff0c…

數智化重構供應商管理

當供應鏈韌性成為核心競爭力,你的供應商管理還在 “摸著石頭過河” 嗎? 在傳統模式下,供應商管理高度依賴人工經驗與紙質流程: 入庫篩選如“大海撈針”:供應商資質審核停留在Excel表格比對,資質造假、歷史…

網絡互連與互聯網

1.在路由表中找不到目標網絡時使用默認路由,默認路由通常指本地網關的地址。 2.OSPF最主要的特征是使用分布式鏈路狀態協議,而RIP使用的是距離向量協議。 3.OSPF使用鏈路狀態公告LSA擴散路由信息 4.內部網關路由協議IGRP是一種動態距離矢量路由協議&a…

Raymarching Textures In Depth

本節課最主要的就是學會hlsl中使用紋理采樣 float4 color Texture2DSample(Texobj, TexobjSampler, uv); return color; 課程中的代碼(沒有這張圖我就沒做) 課程代碼產生深度的原因是uv偏移,黑色區域會不斷向左偏移,直到找到白色…

【MQTT-協議原理】

MQTT-協議原理 ■ MQTT-協議原理■ MQTT-服務器 稱為"消息代理"(Broker)■ MQTT協議中的訂閱、主題、會話■ 一、訂閱(Subscription)■ 二、會話(Session)■ 三、主題名(Topic Name&a…

docker容器安裝的可道云掛接宿主機的硬盤目錄:解決群暉 威聯通 飛牛云等nas的硬盤掛接問題

基于Docker部署可道云(KodCloud)時,通過掛載宿主機其他磁盤目錄可實現高效、安全的數據管理。具體而言,使用綁定掛載(Bind Mounts)將宿主機目錄(如/data/disk2)映射到容器內的可道云…