Python pkg_resources模塊動(dòng)態(tài)加載插件實(shí)例分析
使用標(biāo)準(zhǔn)庫(kù)importlib的import_module()函數(shù)、django的import_string(),它們都可以動(dòng)態(tài)加載指定的 Python 模塊。
舉兩個(gè)動(dòng)態(tài)加載例子:
舉例一:
在你項(xiàng)目中有個(gè)test函數(shù),位于your_project/demo/test.py中,那么你可以使用import_module來動(dòng)態(tài)加載并調(diào)用這個(gè)函數(shù)而不需要在使用的地方通過import導(dǎo)入。
module_path = 'your_project/demo' module = import_module(module_path) module.test()
舉例二:
django的中間件都用過吧,只需要在setting中配置好django就能自動(dòng)被調(diào)用,這也是利用import_string動(dòng)態(tài)加載的。
#settings.py
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
...
]
# 動(dòng)態(tài)加載調(diào)用處代碼
for middleware_path in reversed(settings.MIDDLEWARE):
middleware = import_string(middleware_path)
...以上方式會(huì)有一些缺點(diǎn):
- 所引用模塊不存在時(shí),在項(xiàng)目啟動(dòng)時(shí)不會(huì)及時(shí)拋出錯(cuò)誤,只有在真正調(diào)用時(shí)才能被發(fā)現(xiàn)
- 所引用模塊要事先寫好并放到指定位置,不能刪
- 半自動(dòng)的可插拔性,想禁用某個(gè)插件功能需要手動(dòng)修改代碼。如想去掉django的SessionMiddleware功能,那需要手動(dòng)去修改settings配置文件
pkg_resources實(shí)現(xiàn)動(dòng)態(tài)加載插件
下面介紹另外一種動(dòng)態(tài) 載插件的方法,與安裝庫(kù)setuptools一并安裝的軟件庫(kù)pkg_resources,它基本解決了上述的問題,并且事實(shí)上成為了流行的插件實(shí)現(xiàn)方式。 pkg_resources操作的主要單位就是 Distribution(包分發(fā)),關(guān)于Distribution可以參考這里。Python 腳本啟動(dòng)時(shí),pkg_resources識(shí)別出搜索路徑中的所有 Distribution 的命名空間包,因此,我們會(huì)發(fā)現(xiàn)sys.path包含了很多pip安裝的軟件包的路徑,并且可以正確執(zhí)行import操作。
pkg_resources自帶一個(gè)全局的WorkingSet對(duì)象,代表默認(rèn)的搜索路徑的工作集,也就是我們常用的sys.path工作集。有了這個(gè)工作集,那就能輕松實(shí)現(xiàn)動(dòng)態(tài)導(dǎo)入任何模塊了。
下面上案例:
這個(gè)案例是ansible-runner的一個(gè)事件處理插件,項(xiàng)目地址GitHub - ansible/ansible-runner-http。只需要把這個(gè)包安裝到你的虛擬環(huán)境,ansible-runner就會(huì)自動(dòng)識(shí)別并調(diào)用status_handler、event_handler兩個(gè)事件處理函數(shù)。當(dāng)卸載這個(gè)包后,ansible-runner就會(huì)使用默認(rèn)的方式處理事件。相比前面介紹的import_module方式,這種動(dòng)態(tài)加載方式好在對(duì)源代碼侵入性少,實(shí)現(xiàn)真正的即插即用。下面分析它是怎么利用pkg_resources做到的。
ansible-runner-http項(xiàng)目源碼目錄結(jié)構(gòu):
├── README.md
├── ansible_runner_http
│├── __init__.py
│└── events.py
└── setup.py
event.py:
...
def status_handler(runner_config, data):
plugin_config = get_configuration(runner_config)
if plugin_config['runner_url'] is not None:
status = send_request(plugin_config['runner_url'],
data=data,
headers=plugin_config['runner_headers'],
urlpath=plugin_config['runner_path'])
logger.debug("POST Response {}".format(status))
else:
logger.info("HTTP Plugin Skipped")
def event_handler(runner_config, data):
status_handler(runner_config, data)__init__.py:
from .events import status_handler, event_handler # noqa
setup.py:
from setuptools import setup, find_packages
with open('README.md', 'r') as f:
long_description = f.read()
setup(
name="ansible-runner-http",
version="1.0.0",
author="Red Hat Ansible",
url="https://github.com/ansible/ansible-runner-http",
license='Apache',
packages=find_packages(),
long_description=long_description,
long_description_content_type='text/markdown',
install_requires=[
'requests',
'requests-unixsocket',
],
#方式一:具體到某個(gè)module
entry_points={'ansible_runner.plugins': ['http = ansible_runner_http']},
#方式二:具體到某個(gè)module下的函數(shù)
#entry_points={'ansible_runner.plugins': [
# 'status_handler = ansible_runner_http:status_handler',
# 'event_handler = ansible_runner_http:event_handler',
# ]
#},
zip_safe=False,
)重點(diǎn)在setup中的entry_points選項(xiàng):
- 組名,以點(diǎn)號(hào)分隔便于組織層次,但與 Package 沒有關(guān)聯(lián),如
ansible_runner.plugin - 名字,如 http
- Distribution 中的位置,可以指向一個(gè)module如ansible_runner_http。也可以指向module下某個(gè)函數(shù)如ansible_runner_http:status_handler,前面是 Module,后面是模塊內(nèi)的函數(shù)
這樣一來一旦這個(gè)包被安裝后,pkg_resources就可以動(dòng)態(tài)識(shí)別這個(gè)插件了。
下面看調(diào)用方:
...
plugins = {
#調(diào)用load方法,獲取指向python的對(duì)象
entry_point.name: entry_point.load()
for entry_point
#調(diào)用WorkingSet.iter_entry_points方法遍歷所有EntryPoint,參數(shù)為組名
in pkg_resources.iter_entry_points('ansible_runner.plugins')
}
...
def event_callback(self, event_data):
'''
Invoked for every Ansible event to collect stdout with the event data and store it for
later use
'''
for plugin in plugins:
plugins[plugin].event_handler(self.config, event_data)
...方式一寫法得到的plugins:

方式二寫法得到的plugins:

到此這篇關(guān)于Python pkg_resources模塊動(dòng)態(tài)加載插件實(shí)例分析的文章就介紹到這了,更多相關(guān)Python 動(dòng)態(tài)加載插件內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
對(duì)Python的Django框架中的項(xiàng)目進(jìn)行單元測(cè)試的方法
這篇文章主要介紹了對(duì)Python的Django框架中的項(xiàng)目進(jìn)行單元測(cè)試的方法,使用Django中的tests.py模塊可以輕松地檢測(cè)出一些常見錯(cuò)誤,需要的朋友可以參考下2016-04-04
在Python的Django框架中實(shí)現(xiàn)Hacker News的一些功能
這篇文章主要介紹了在Python的Django框架中實(shí)現(xiàn)Hacker News的一些功能,包括投票“頂”評(píng)論等功能,需要的朋友可以參考下2015-04-04
解決項(xiàng)目pycharm能運(yùn)行,在終端卻無法運(yùn)行的問題
今天小編就為大家分享一篇解決項(xiàng)目pycharm能運(yùn)行,在終端卻無法運(yùn)行的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2019-01-01
NumPy 數(shù)學(xué)函數(shù)及代數(shù)運(yùn)算的實(shí)現(xiàn)代碼
這篇文章主要介紹了NumPy 數(shù)學(xué)函數(shù)及代數(shù)運(yùn)算的實(shí)現(xiàn)代碼,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-07-07
pandas如何篩選某個(gè)列值是否位于某個(gè)列表內(nèi)
這篇文章主要介紹了pandas如何篩選某個(gè)列值是否位于某個(gè)列表內(nèi)問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-02-02

