stevedore

展开目录
stevedore
  通常,一个成熟的程序不会自己实现所有的功能,即通常所说的不要重复造轮子,而是会通过调用标准库或者第三方库来使用某些已经实现了的功能。可以使用import来导入特定的模块,还可以使用pkg_resources,通过Entry Points的方式来使用特定的Python对象(例如函数和类)。除此之外,还有一个能动态管理扩展的库stevedore
  stevedore本质上也是通过Entry Points来动态加载对应的库,它比pkg_resources更灵活,功能更强大。下面通过两个简单的例子来介绍stevedore的使用。
驱动driver
  例如我们要开发一个系统,这个系统的功能是对搜索引擎的搜索结果进行分析。显然,现在已经有很多搜索引擎了,例如谷歌Google、必应Bing、维基百科Wiki等等。最直接的方式就是先使用一个搜索引擎例如谷歌Google,即在程序中直接使用谷歌Google获取搜索结果,然后进行分析。如果用户希望分析的是必应Bing,则再修改程序,改成使用必应Bing获取搜索结果。这样的设计,看着简单,实际上有很多问题。最大的问题在于,每个搜索引擎都对应于一个代码版本,工作量很大。
  一个更好的设计是使用驱动方式,英文叫做driver的方式。即我们定义一个接口,每个对接口的实现称为一个驱动driver。例如通过谷歌Google实现的接口称为一个谷歌Google的驱动driver,通过必应Bing实现的接口称为一个必应Bing的驱动driver。
  可以通过一个抽象类来表示接口。新建文件夹test_stevedore,在文件夹test_stevedore中新建base.py,内容如下:

# -*- coding: utf-8 -*-

import six
import abc

@six.add_metaclass(abc.ABCMeta)
class Base(object):
    """
    The base class for search engine
    """
    
    @abc.abstractmethod
    def get_search_result(self, keyword):
        """Return search result for keyword.
        :param keyword: the word for search.
        """

  要基于上述的接口实现驱动,需要从Base派生出新类,并且实现其方法get_search_result()。
  下面是一个谷歌Google驱动driver的例子。
    在文件夹test_stevedore中,新建文件夹GoogleDriver。在文件夹GoogleDriver下创建以下文件夹和文件:
GoogleDriver
├── google_driver
│   ├── google_driver.py
│   └── __init__.py
└── setup.py

  setup.py文件的内容:

from setuptools import setup, find_packages

setup(
    name='google_driver',
    version='1.0',
    
    packages=find_packages(),
    
    entry_points={
        'search_engine.driver': [
            'google = google_driver.google_driver:GoogleDriver',
        ],
    },
)


  __init__.py文件的内容:

import google_driver


  google_driver.py文件的内容:

import base

class GoogleDriver(base.Base):
    def get_search_result(self, keyword):
        return('The result for {} from Google.'.format(keyword))

    通过命令python setup.py install可以安装此驱动。

  下面是一个必应Bing驱动driver的例子。
  在文件夹test_stevedore中,新建文件夹BingDriver。在文件夹BingDriver下创建以下文件夹和文件:
BingDriver
├── bing_driver
│   ├── bing_driver.py
│   └── __init__.py
└── setup.py

  setup.py文件的内容:

from setuptools import setup, find_packages

setup(
    name='bing_driver',
    version='1.0',
    
    packages=find_packages(),
    
    entry_points={
        'search_engine.driver': [
            'bing = bing_driver.bing_driver:BingDriver',
        ],
    },
)


  __init__.py文件的内容:

import bing_driver


  google_driver.py文件的内容:

import base

class BingDriver(base.Base):
    def get_search_result(self, keyword):
        return('The result for {} from Bing.'.format(keyword))

    通过命令python setup.py install可以安装此驱动。
    
  完整的目录结构为:
test_stevedore/
├── base.py
├── BingDriver
│   ├── bing_driver
│   │   ├── bing_driver.py
│   │   └── __init__.py
│   └── setup.py
└── GoogleDriver
    ├── google_driver
    │   ├── google_driver.py
    │   └── __init__.py
    └── setup.py
    
  在目录test_stevedore/下打开python命令行,使用以下命令可以测试对两个驱动的使用:

>>> from stevedore import driver
>>> mgr = driver.DriverManager(namespace = "search_engine.driver", name = "google", invoke_on_load = True, invoke_args = ())
>>> mgr.driver.get_search_result('Linux')
'The result for Linux from Google.'
>>>
>>> mgr = driver.DriverManager(namespace = "search_engine.driver", name = "bing", invoke_on_load = True, invoke_args = ())
>>> mgr.driver.get_search_result('Linux')
'The result for Linux from Bing.'
>>>

插件extension
  驱动driver类型有一个特点,就是多选一,上面的例子就是在多个搜索引擎中选择一个。如果我们要开发的系统同一个时间并不是只对一个搜索引擎感兴趣,而是希望一次搜集所有系统中注册的搜索引擎的搜索结果,这时就可以使用插件extension类型。插件extension类型,顾名思义,就是一次将所有插件都加载。
  在上面驱动driver例子的基础上,通过stevedore中的类ExtensionManager就可以实现一次加载所有插件。
  在目录test_stevedore/下打开python命令行,使用以下命令可以验证插件extension的使用:

>>> from stevedore import ExtensionManager
>>> def run(hook):
...     return (hook.name, hook.obj)
...
>>> mgr=ExtensionManager(namespace='search_engine.driver', invoke_on_load=True,
invoke_args=())
>>> m = mgr.map(run)
>>> for name, obj in m:
...     print('{}: {}'.format(name, obj.get_search_result('Linux')))
...
bing: The result for Linux from Bing.
google: The result for Linux from Google.
>>>