中文

杂谈 os包的功能 Amaze UI中的组件 Skeleton CSS的用武之地 SVG Introduction mysql数据库迁移 How agent and message bus work in the CloudStack platform? 剖析cloudstack中虚机的创建过程 AngularJS与LoopBack LoopBack实战 Sails使用手册 在NodeJS中使用MySQL数据库 在NodeJS中使用Sqlite 实战XLRD类库 上下文中给模板中的变量赋值 使用Pecan创建restful API 服务 使用python语言操作excel文件 Magento性能调优 Celery的世界 Linux下如何快速查找文件和内容 MySQL常用资源 为PHP设置服务器(Apache/Nginx)环境变量 MySQL大量数据插入各种方法性能分析与比较 Laravel中使用Redis作为队列系统的工作流程 使用Supervisor来管理你的Laravel队列 在Laravel中使用自己的类库三种方式 用Xdebug和Sublime调试PHP代码 用Laravel+Grunt+Bower管理你的应用 PHP Socket的使用 Python脚本--下载合并SAE日志 PHP命名空间及自动加载 SVN 常用资源 Shell 常用资源 Python 常用资源 PHP 常用资源 jQuery 常用资源 JavaScript 常用资源 HTML 常用资源 Git 常用资源 Linux下多个命令连续执行方法 基于CSS3实现尖角面包屑 部署Ceilometer到已有环境中 更新前端框架到Bootstrap3 OpenStack Ceilometer数据存储与API源码解析 OpenStack Ceilometer中的Pipeline机制 OpenStack Ceilometer Compute Agent源码解读 学习Python动态扩展包stevedore 学习Python的ABC模块 Python包管理工具setuptools详解 OpenStack Horizon 中文本地化 安装MySQL和MongoDB的WEB管理界面 给Git或者APT设置goagent代理 WSGI学习 在虚拟机单机部署OpenStack Grizzly python包工具之间的关系 给OpenStack创建Ubuntu镜像 OpenStack Grizzly Multihost部署文档 HTML中meta标签viewpoint的作用 交互式编程-IPython 页面提速之——数据缓存 给OpenStack创建Win7镜像 Ceilometer的命令行使用 给OpenStack创建Windows XP镜像 概念模型、逻辑模型、物理模型的区别 Bootstrap常用资源 OpenStack监控项目Ceilometer的一些术语 服务器自动化部署及运维常见工具 Linux下开启Libvirtd的tcp监控 VNC和远程桌面的区别 调试和修改OpenStack中的Horizon部分 win7快速打开应用程序或文件 JavaScript变量作用域 git创建远程库 MySQL远程访问 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传递参数

其他

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