最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Python pkg_resources模塊動(dòng)態(tài)加載插件實(shí)例分析

 更新時(shí)間:2022年08月04日 10:22:04   作者:藍(lán)綠色~菠菜  
當(dāng)編寫應(yīng)用軟件時(shí),我們通常希望程序具有一定的擴(kuò)展性,額外的功能——甚至所有非核心的功能,都能通過插件實(shí)現(xiàn),具有可插拔性。特別是使用 Python 編寫的程序,由于語言本身的動(dòng)態(tài)特性,為我們的插件方案提供了很多種實(shí)現(xiàn)方式

使用標(biāo)準(zhǔn)庫(kù)importlibimport_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)文章

最新評(píng)論

大连市| 封开县| 安国市| 湛江市| 城固县| 余姚市| 大城县| 通山县| 尚义县| 义马市| 平遥县| 兰考县| 孟村| 大渡口区| 丁青县| 内乡县| 乐昌市| 若尔盖县| 罗城| 丹棱县| 晋城| 香格里拉县| 健康| 兴和县| 迁安市| 武安市| 育儿| 清新县| 灵台县| 洛阳市| 鱼台县| 琼结县| 通山县| 洪泽县| 金秀| 横山县| 滦南县| 镇坪县| 垦利县| 新巴尔虎左旗| 五莲县|