Python插件之stevedore

Python插件之stevedore

1. 插件的基本使用

1. 简介

​ stevedore是用来实现动态加载代码的开源模块。它是在OpenStack中用来加载插件的公共模块。可以独立于OpenStack而安装使用:https://pypi.python.org/pypi/stevedore/

​ stevedore使用setuptools的entry points来定义并加载插件。entry point引用的是定义在模块中的对象,比如类、函数、实例等,只要在import模块时能够被创建的对象都可以。

​ 一般来讲,entry point的名字是公开的,用户可见的,经常出现在配置文件中。而命名空间,也就是entry point组名却是一种实现细节,一般是面向开发者而非最终用户的。可以用Python的包名作为entry point命名空间,以保证唯一性,但这不是必须的。

entry points的主要特征就是,它可以是独立注册的,也就是说插件的开发和安装可以完全独立于使用它的应用,只要开发者和使用者在命名空间和API上达成一致即可。

命名空间被用来搜索entry points。entry points的名字在给定的发布包中必须是唯一的,但在一个命名空间中可以不唯一。也就是说,同一个发布包内不允许出现同名的entry point,但是如果是两个独立的发布包,却可以使用完全相同的entrypoint组名和entry point名来注册插件。

2. 分类

​ 在stevedore中,有三种使用插件的方式:Drivers、Hooks、Extensions

Drivers

​ 一个名字对应一个entry point。使用时根据插件的命名空间和名字,定位到单独的插件:
在这里插入图片描述

Extensions

​ 多个名字,多个entry point。给定命名空间,加载该命名空间中所有的插件,当然也允许同一个命名空间中的插件具有相同的名字。
在这里插入图片描述

Hooks

​ 一个名字对应多个entry point。允许同一个命名空间中的插件具有相同的名字,根据给定的命名空间和名字,加载该名字对应的多个插件。
在这里插入图片描述

3. 结构

stevedore通过提供manager类来实现动态加载扩展插件的管理,因此在实现stevedore时,首先为其他父类定义了一个manager基类ExtensionManager类。ExtensionManager类是一个所有其他manager类的基类,其主要的属性和方法如下:

namespace:string类型,命名空间,表示entry points的命名空间。
invoke_on_load:bool类型,表示是否自动加载扩展插件。
invoke_args:tuple类型,表示自动加载extension时传入的参数。
invoke_kwds:dict类型,表示自动加载extension时传入的参数。
propagate_map_exceptions:bool类型,表示使用map调用时,是否向上传递调用信息。
on_load_failure_callback:func类型,表示加载失败时调用的方法。
verify_requirements:bool类型,表示是否使用setuptools安装插件所需要的依赖。
map(func, *args, kwds):为每一个extension触发func()函数。
map_method(method_name, *args, kwds):为每一个extension触发method_name指定的函数。
names():获取所有发现的extension名称。
entry_points_names():返回所有entry_points的名称列表,每个列表元素是一个有entry points的名称和entry points列表的map对象。
list_entry_points():某个命名空间的所有entry points列表。

​ stevedore中其他所有manager类都需要继承ExtensionManager类,而ExtensionManager类初始化时便会通过namespace等加载所有extension,并对插件进行初始化。

def __init__(self, namespace,
             invoke_on_load=False,
             invoke_args=(),
             invoke_kwds={},
             propagate_map_exceptions=False,
             on_load_failure_callback=None,
             verify_requirements=False):
  self._init_attributes(
    namespace,
    propagate_map_exceptions=propagate_map_exceptions,
    on_load_failure_callback=on_load_failure_callback)
  extensions = self._load_plugins(invoke_on_load,
                                  invoke_args,
                                  invoke_kwds,
                                  verify_requirements)

在ExtensionManager实例化对象时,首先调用_init_attributes()方法初始化namespace等参数,然后会调用_load_plugins()方法加载所有的extension插件;最后会调用_init_plugins()方法设置对象的属性。
在定义ExtensionManager时,还涉及到一个重要的类Extension,该类表示一个extension,该类主要包含如下属性:

  • name:表示一个entry point的名称。
  • entry_point:表示从pkg_resources获得的一个EntryPoint对象。
  • plugin:通过调用entry_point.load()方法返回的plugin类。
  • obj:extension被manager类加载时,会调用plugin(*args, **kwds)返回一个plugin对象。

在ExtensionManager的map()方法中,为每一个entry point调用func()函数,而func()函数的第一个参数即为Extension对象。

4. 加载方式

根据entry points配置的不同,stevedore提供了三种加载插件的方式:ExtensionManager、DriverManager、HookManager。下面将分别介绍这三种加载插件的方式:
ExtensionManager:一种通用的加载方式。这种方式下,对于给定的命名空间,会加载该命名空间下的所有插件,同时也允许同一个命名空间下的插件拥有相同的名称,其实现即为stevedore.extension.ExtensionManager类。
HookManager:在这种加载方式下,对于给定的命名空间,允许同一个命名空间下的插件拥有相同的名称,程序可以根据给定的命名空间和名称加载该名称对应的多个插件,其实现为stevedore.hook.HookManager类。
DriverManager:在这种加载方式下,对于给定的命名空间,一个名字只能对应一个entry point,对于同一类资源有多个不同插件的情况,只能选择一个进行注册;这样,在使用时就可以根据命名空间和名称定位到某一个插件,其实现为stevedore.driver.DriverManager类。
在实现这些加载方式的类时,stevedore还定义了多个其他类型的辅助manager类,这些manager类之间的关系如下图所示。
在这里插入图片描述

由图可知,ExtensionManager类时所有stevedore的manager类的父类,DriverManager类和HookManager类是ExtensionManager子类NamedExtensionManager类的子类。而NamedExtensionManager类中增加了一个属性names,所以DriverManager类和HookManager类在加载对应插件时,只加载names属性所包含的名称的entry point插件。除了这几个类之外,stevedore还定义了其他三个辅助的manager类:

  • EnabledExtensionManager类:该类在ExtensionManager类的基础上添加了一个check_func属性,表示一个验证方法,因此在加载时只加载通过check_func()方法验证的extension插件。
  • DispatchExtensionManager类:该类继承自EnabledExtensionManager类,该类重写了ExtensionManger类中定义的map()和map_method()方法,其为这两个方法添加了filter_func参数,表示只对通过filter_func()方法过滤的extension才会执行func()函数。
  • NameDispatcherExtensionManager类:该类继承自DispathExtensionManager类,该类也定义了一个names属性,在使用时,只有names包含的名称的extension执行map()和map_method()方法时才会执行对应的func()方法。

2. 插件使用示范

先建立项目目录: mystevedore

然后新建一个目录: people,该目录

下面分别包含:

init.py

chinese.py
english.py
pluginBase.py
文件

init.py 文件内容为空

pluginBase.py文件内容如下

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import abc
import six
 
@six.add_metaclass(abc.ABCMeta)
class PeopleBase(object):
    def __init__(self):
        pass
 
    @abc.abstractmethod
	  def say(self):
      	return "say hi"

chinese.py文件内容如下

#!/usr/bin/env python
# -*- coding: utf-8 -*-
 
from pluginBase import PeopleBase
 
class Chinese(PeopleBase):
    def say(self):
        return "ni hao"

english.py文件内容如下

#!/usr/bin/env python
# -*- coding: utf-8 -*-
 
from pluginBase import PeopleBase
 
class Chinese(PeopleBase):
    def say(self):
        return "hello"

项目目录: mystevedore项目目录下新建一个文件setup.py,文件内容如下

#!/usr/bin/env python
# -*- coding: utf-8 -*-
 
from setuptools import find_packages
from setuptools import setup
 
setup(
    name='mm_stevedore_test',
    version='1.0',
    packages=find_packages(),
    entry_points={
        'people.say': [
            'chinese = people.chinese:Chinese',
            'english = people.english:English'
        ],
    }
)

然后执行如下命令安装该上述编写的插件

python setup.py install

最后编写调用插件的文件 main.py,内容如下

#!/usr/bin/env python
# -*- coding: utf-8 -*-
 
from stevedore import driver
 
'''
关键:
1 stevedore
含义:动态加载代码的库,允许在运行时通过发现和加载扩展(插件)应用。
     使用setuptools的entry points来定义并加载插件。
     entry point引用的是定义在模块中的对象(类,对象,实例),只要在import模块时能够被创建
2 entry points
  entry points:本质上是提供给外界调用的程序或者类
  entry points可以独立注册,需要开发者和使用者在命名空间和插件名称上一致
3 插件
插件种类:
1) stevedore.driver.DriverManager :一个名字对应一个entry point。根据插件命名空间和名字,定位到单独插件
stevedore.driver.DriverManager(namespace, name, invoke_on_load, invoke_args=(), invoke_kwds={})
namespace: 命名空间
name: 插件名称
invoke_on_load:如果为True,表示会实例化该插件的类
invoke_args:调用插件对象时传入的位置参数
invoke_kwds:传入的字典参数
2) stevedore.hook.HookManager :一个名字对应多个entry point,根据给定命名空间和名字,加载名字对应的多个插件
3) stevedore.extension.ExtensionManager: 多个名字,多个entry point。给定命名空间,加载该命名空间所有插件
4 插件格式
命名空间 = 
  插件名称=模块:可导入对象
插件例子:
ceilometer.compute.virt = 
    libvirt = ceilometer.compute.virt.libvirt.inspector:LibvirtInspector
    hyperv = ceilometer.compute.virt.hyperv.inspector:HyperVInspector
解释:
ceilometer.compute.virt 是命名空间
libvirt 是插件名称
ceilometer.compute.virt.libvirt.inspector 是模块
LibvirtInspector 是可导入对象,实际是一个类
5 使用插件步骤:
1) 使用abc模块创建抽象基类定义插件API行为
2) 通过继承基类并实现必要的方法来创建插件
3) 为每个api定义一个命名空间,可将应用,API名字结合起来
  例如: people.say
4) setup.py中注册:
例如:
from setuptools import find_packages,setup
setup(
    name='mm_stevedore_test',
    version='1.0',
    packages=find_packages(),
    entry_points={
        'people.say': [
            'chinese = people.chinese:Chinese',
            'english = people.english:English'
        ],
    }
)
5) 安装插件所在模块,即在插件代码下执行如下命令:
python setup.py install
6)调用插件:
编写一个.py文件,该.py文件可以不和插件模块代码在同一目录,调用形式类似如下
mgr = stevedore.driver.DriverManager(
    namespace='people.say',
    name='chinese',
    invoke_on_load=True,
    invoke_args=(),
)
result = mgr.driver.say()
总结:
stevedore库可以用于注册插件,之所以使用插件是因为可以使自己的程序可以调用别人编写的代码,进行解藕。插件格式是:
命名空间= 
  插件名称=模块:可导入对象
参考:
[1] http://blog.csdn.net/gqtcgq/article/details/49620279
[2] https://blog.csdn.net/happyanger6/article/details/54798870
'''
 
def process():
    mgr = driver.DriverManager(
        namespace='people.say',
        name='chinese',
        invoke_on_load=True,
        invoke_args=(),
    )
    # 注意: 这里调用的是 mgr.driver.say() 而不是 mgr.say(),不要漏了driver
    result = mgr.driver.say()
    print result
 
if __name__ == "__main__":
    process()

摘自:

https://www.cnblogs.com/gqtcgq/p/7247099.html
https://blog.csdn.net/qingyuanluofeng/article/details/83536546
https://blog.csdn.net/Bill_Xiang_/article/details/78852717

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值