Python pkg_resources模块动态加载插件实例分析
作者:蓝绿色~菠菜 发布时间:2023-10-31 16:48:39
使用标准库importlib
的import_module()
函数、django的import_string(),它们都可以动态加载指定的 Python 模块。
举两个动态加载例子:
举例一:
在你项目中有个test函数,位于your_project/demo/test.py中,那么你可以使用import_module来动态加载并调用这个函数而不需要在使用的地方通过import导入。
module_path = 'your_project/demo'
module = import_module(module_path)
module.test()
举例二:
django的中间件都用过吧,只需要在setting中配置好django就能自动被调用,这也是利用import_string动态加载的。
#settings.py
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
...
]
# 动态加载调用处代码
for middleware_path in reversed(settings.MIDDLEWARE):
middleware = import_string(middleware_path)
...
以上方式会有一些缺点:
所引用模块不存在时,在项目启动时不会及时抛出错误,只有在真正调用时才能被发现
所引用模块要事先写好并放到指定位置,不能删
半自动的可插拔性,想禁用某个插件功能需要手动修改代码。如想去掉django的SessionMiddleware功能,那需要手动去修改settings配置文件
pkg_resources实现动态加载插件
下面介绍另外一种动态 载插件的方法,与安装库setuptools一并安装的软件库pkg_resources,它基本解决了上述的问题,并且事实上成为了流行的插件实现方式。 pkg_resources操作的主要单位就是 Distribution(包分发),关于Distribution可以参考这里。Python 脚本启动时,pkg_resources识别出搜索路径中的所有 Distribution 的命名空间包,因此,我们会发现sys.path包含了很多pip安装的软件包的路径,并且可以正确执行import操作。
pkg_resources自带一个全局的WorkingSet对象,代表默认的搜索路径的工作集,也就是我们常用的sys.path工作集。有了这个工作集,那就能轻松实现动态导入任何模块了。
下面上案例:
这个案例是ansible-runner的一个事件处理插件,项目地址GitHub - ansible/ansible-runner-http。只需要把这个包安装到你的虚拟环境,ansible-runner就会自动识别并调用status_handler、event_handler两个事件处理函数。当卸载这个包后,ansible-runner就会使用默认的方式处理事件。相比前面介绍的import_module方式,这种动态加载方式好在对源代码侵入性少,实现真正的即插即用。下面分析它是怎么利用pkg_resources做到的。
ansible-runner-http项目源码目录结构:
├── 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',
],
#方式一:具体到某个module
entry_points={'ansible_runner.plugins': ['http = ansible_runner_http']},
#方式二:具体到某个module下的函数
#entry_points={'ansible_runner.plugins': [
# 'status_handler = ansible_runner_http:status_handler',
# 'event_handler = ansible_runner_http:event_handler',
# ]
#},
zip_safe=False,
)
重点在setup中的entry_points选项:
组名,以点号分隔便于组织层次,但与 Package 没有关联,如
ansible_runner.plugin
名字,如 http
Distribution 中的位置,可以指向一个module如ansible_runner_http。也可以指向module下某个函数如ansible_runner_http:status_handler,前面是 Module,后面是模块内的函数
这样一来一旦这个包被安装后,pkg_resources就可以动态识别这个插件了。
下面看调用方:
...
plugins = {
#调用load方法,获取指向python的对象
entry_point.name: entry_point.load()
for entry_point
#调用WorkingSet.iter_entry_points方法遍历所有EntryPoint,参数为组名
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:
来源:https://blog.csdn.net/bocai_xiaodaidai/article/details/125971394


猜你喜欢
- 安装py2app打开终端输入pip3命令安装py2app工具pip3 install py2pp安装完成后,默认会装到/Library/Fr
- 在制作一个 Python 分发包时经常需要把一些文件添加到包中。最常见的例子是你希望通过 pip install 命令安装 Pyt
- 我们用python 打包的exe文件的时候,每次运行后面都有一个黑框框,比如我的这个:用tkinter做的图形界面,打包成exe文件,每次运
- 实验室老师让给数据画一张线性拟合图。不会matlab,就琢磨着用python。参照了网上的一些文章,查看了帮助文档,成功的写了出来这里用到了
- 本文实例讲述了Python获取网页上图片下载地址的方法。分享给大家供大家参考。具体如下:这里获取网页上图片的下载地址是正在写的数据采集中的一
- 一、MySQL-Proxy基础MySQL Proxy是一个处于你的Client端和MySQL server端之间的简单程序,它可以监测、分析
- 前言python画图,如果用英文显示基本没有问题,但是中文可能会有乱码或者不显示的情况。经过个人的测试,下图中“横轴&a
- 使用PyQt5应用程序制作PDF转换成图片的小工具,可以导入PDF文档后一键生成对应的PNG图片。PDF图片转换小工具使用的中间件:pyth
- MySQL是一个多线程的,结构化查询语言(SQL)数据库服务器。SQL在世界上是最流行的数据库语言。MySQL的执行性能非常高,运行速度非常
- (1) variable = a if exper else b(2)variable = (exper and [b] or [c])[0
- 需要分件html源代码 此例中的被抓取的html源代码如下 <p align=left>2004年8月24日星期二;白天:晴有时
- PHP simplexml_load_string() 函数实例转换形式良好的 XML 字符串为 SimpleXMLElement 对象,然
- 模块的的作用主要是用于字符串和文本处理,查找,搜索,替换等复习一下基本的正则表达式吧 .:匹配除了换行符以为的任意单个字符&nbs
- 如何为XHTML做好准备,XHTML与HTML 4.01标准没有太多的不同。所以将你的代码升级至4.01是个不错的开始。HTML 4.01参
- 1、之前那个说淘宝交易评价“有问题”的文章已经有了一百多个评论。(某些开口就骂的评论已被删除)近期去评论的人都是从搜索引擎过来的,他们不是产
- 遇到一个小问题,记录一下问题:在微信小程序中使用scroll-view标签时,用height:cale(xx - xx)设置高度无效,在pa
- 问题(来自Udacity机器学习工程师纳米学位预览课程)用 Python 实现函数 count_words(),该函数输入字符串 s 和数字
- W3C终于发布了第一个HTML5草案,大家还沉溺在HTML2XHTML转换的快乐和痛苦中时,却又突然发现,HTML5和XHTML2,到底谁是
- 1. 引言之前已经有博客专门介绍了Python中的列表生成式,可能大家还不太擅长。这里推荐九个Python列表生成式的面试题(从简单到困难排
- 具体代码如下所示:import osfrom PIL import ImageUNIT_SIZE = 220 # the size of i