技术

IT这个行当之需求与时间管理 golang结构体和包中的类型或基础类型定义方法 golang中结构体的初始化方法(new方法) 项目管理总结 python函数式编程之-装饰器(Decorators) python文件批量处理 Go,互联网时代的C Python推导式演变(Comprehensions) 项目管理感悟 golang学习简单例子 了解GitHub工作流【译】 PHP Socket的使用 Apache 日志文件格式及简单处理 Python脚本--下载合并SAE日志 PHP命名空间及自动加载 基于CSS3实现尖角面包屑 部署Ceilometer到已有环境中 OpenStack Ceilometer Collector代码解读 OpenStack Ceilometer数据存储与API源码解析 OpenStack Ceilometer中的Pipeline机制 OpenStack Ceilometer Compute Agent源码解读 学习Python动态扩展包stevedore 学习Python的ABC模块 Python包管理工具setuptools详解 OpenStack Horizon 中文本地化 WSGI学习 在虚拟机单机部署OpenStack Grizzly 学习使用python打包工具distutils python包工具之间的关系 给OpenStack创建Ubuntu镜像 OpenStack Grizzly Multihost部署文档 为什么使用pip而不是easy_install HTML中meta标签viewpoint的作用 交互式编程-IPython 页面提速之——数据缓存 给OpenStack创建Win7镜像 Ceilometer的命令行使用 部署一个ceilometer-horizon项目 给OpenStack创建Windows XP镜像 几种企业的存储系统 概念模型、逻辑模型、物理模型的区别 五中常见的开源协议整理(BSD,Apache,GPL,LGPL,MIT) OpenStack监控项目Ceilometer的一些术语 VNC和远程桌面的区别 OpenStack Ceilometer项目简介 虚拟化与云计算中KVM,Xen,Qemu的区别和联系 调试和修改OpenStack中的Horizon部分 JavaScript变量作用域 kanyun worker原理 kanyun server服务 在OpenStack中部署kanyun kanyun的api-client命令 sae下的python开发部署和一个简单例子 OpenStack Nova内部机制 PHP可变变量 JS中防止浏览器屏蔽window.open PHP操作Session的原理及提升安全性时的一个问题

标签


学习Python动态扩展包stevedore

2013年06月09日

1.stevedore作用

Python导入动态代码很容易,例如通过在运行时导入扩展插件来扩展你的应用。许多应用通过__import__或importlib实现了这个功能。stevedore的功能就是管理扩展的,但是它的实现方式是借助steuptools的entry points(我的上一篇有讲entry points功能)。

2.创建一个插件

这里以一个格式转换的例子来学习:

# stevedore/example/base.py
import abc
class FormatterBase(object):
    __metaclass__ = abc.ABCMeta

    def __init__(self, max_width=60):
        self.max_width = max_width

    @abc.abstractmethod
    def format(self, data):
        pass

首先创建一个基类,来作为虚拟基础类,供插件们继承并实现其中方法。这个例子中的关键函数为format,其子类都需要实现这个函数。

有关虚拟基础类的内容在我之前的一篇博客中也说到。

接下来是实现功能的两个插件类:

# stevedore/example/simple.py
from stevedore.example import base
class Simple(base.FormatterBase):
    def format(self, data):
        for name, value in sorted(data.items()):
            line = '{name} = {value}\n'.format(
                name=name,
                value=value,
            )
            yield line

另一个:

# stevedore/example/fields.py
import textwrap
from stevedore.example import base
class FieldList(base.FormatterBase):
    def format(self, data):
        for name, value in sorted(data.items()):
            full_text = ': {name} : {value}'.format(
                name=name,
                value=value,
            )
            wrapped_text = textwrap.fill(
                full_text,
                initial_indent='',
                subsequent_indent='    ',
                width=self.max_width,
            )
            yield wrapped_text + '\n'

这两个插件以不同的方式对传入的数据进行格式化,并且都实现了format方法,接下来是在setup.py中注册插件:

# stevedore/example/setup.py
from setuptools import setup, find_packages
setup(
    ...
    entry_points={
        'stevedore.example.formatter': [
            'simple = stevedore.example.simple:Simple',
            'field = stevedore.example.fields:FieldList',
            'plain = stevedore.example.simple:Simple',
        ],
    },
)

这个例子可以看到,我们设定了三个接口,simple/field/plain,其他应用或者自身都可以对它们进行调用。如果不用stevedore的话,直接使用pkg_resources.require()调用他们,但是stevedore有了一个更好的机制来管理和使用他们

3.导入插件

stevedore定义了一系列类来帮助更好的调用上面生成的插件

以Driver方式调用

这种方式经常被使用,即我们有多个方法可以做成一件事,但是我们只用其中一种就够了,通过stevedore的DriverManager可以做到,如下:

# stevedore/example/load_as_driver.py
from __future__ import print_function
import argparse
from stevedore import driver
if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument(
        'format',
        nargs='?',
        default='simple',
        help='the output format',
    )
    parser.add_argument(
        '--width',
        default=60,
        type=int,
        help='maximum output width for text',
    )
    parsed_args = parser.parse_args()
    data = {
        'a': 'A',
        'b': 'B',
        'long': 'word ' * 80,
    }
    mgr = driver.DriverManager(
        namespace='stevedore.example.formatter',
        name=parsed_args.format,
        invoke_on_load=True,
        invoke_args=(parsed_args.width,),
    )
    for chunk in mgr.driver.format(data):
        print(chunk, end='')

这里关键的位置在mgr生成部分,首先根据namespace获得相应entry point组,然后根据name调用响应的plugin

例如python -m stevedore.example.load_as_driver a = A即以默认的name调用plugin,默认的format为simple。python -m stevedore.example.load_as_driver field为调用field的plugin

以Extensions方式调用

另外一种常见的方式是调用多个plugin共同处理一件事情,这可以利用ExtensionManagerNamedExtensionManagerEnabledExtensionManger来实现

# stevedore/example/load_as_extension.py
from __future__ import print_function

import argparse

from stevedore import extension


if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument(
        '--width',
        default=60,
        type=int,
        help='maximum output width for text',
    )
    parsed_args = parser.parse_args()

    data = {
        'a': 'A',
        'b': 'B',
        'long': 'word ' * 80,
    }

    mgr = extension.ExtensionManager(
        namespace='stevedore.example.formatter',
        invoke_on_load=True,
        invoke_args=(parsed_args.width,),
    )

    def format_data(ext, data):
        return (ext.name, ext.obj.format(data))

    results = mgr.map(format_data, data)

    for name, result in results:
        print('Formatter: {0}'.format(name))
        for chunk in result:
            print(chunk, end='')
        print('')

这里ExtensionManger的参数只需要namespace,因为它将使用这个entry point组中的所有插件,并且通过mgr.map()来为每一个plugin传递参数

其他

除了上面提到的几种方式外,还有其他几种可以使用,具体可以自己研究了~