加载插件¶
根据您的需求,插件的启用和调用模式有几种不同的方式。
加载驱动程序¶
插件最常见的用法是作为单独的驱动程序。在这种情况下,可能有许多插件选项可供选择,但只需要加载和调用一个。 DriverManager 类支持这种模式。
这个示例程序使用一个 DriverManager 来加载 stevedore 示例中定义的格式化程序。然后它使用格式化程序将数据结构转换为文本格式,并将其打印出来。
# stevedore/example/load_as_driver.py
# Copyright (C) 2020 Red Hat, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
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='')
管理器接受插件命名空间和名称作为参数,并使用它们来查找插件。然后,由于 invoke_on_load 为 true,它会调用加载的对象。在这种情况下,该对象是注册为格式化程序的插件类。 invoke_args 是传递给类构造函数的 positional 参数,用于设置最大宽度参数。
default=60,
type=int,
help='maximum output width for text',
)
parsed_args = parser.parse_args()
在创建管理器之后,它会保存对通过调用为插件注册的代码返回的单个对象的引用。该对象是实际的驱动程序,在这种情况下是插件中格式化程序类的一个实例。可以通过管理器的 driver 属性访问单个驱动程序,然后可以直接调用其方法。
parsed_args = parser.parse_args()
运行示例程序会产生以下输出
$ python -m stevedore.example.load_as_driver a = A
b = B
long = word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word
$ python -m stevedore.example.load_as_driver field
: a : A
: b : B
: long : word word word word word word word word word word
word word word word word word word word word word word
word word word word word word word word word word word
word word word word word word word word word word word
word word word word word word word word word word word
word word word word word word word word word word word
word word word word word word word word word word word
word word word word
$ python -m stevedore.example.load_as_driver field --width 30
: a : A
: b : B
: long : word word word word
word word word word word
word word word word word
word word word word word
word word word word word
word word word word word
word word word word word
word word word word word
word word word word word
word word word word word
word word word word word
word word word word word
word word word word word
word word word word word
word word word word word
word word word word word
word
加载扩展¶
另一种常见用法是同时加载多个扩展,并对所有扩展执行某些操作。 许多其他的管理器类支持这种调用模式,包括 ExtensionManager、NamedExtensionManager 和 EnabledExtensionManager。
# stevedore/example/load_as_extension.py
# Copyright (C) 2020 Red Hat, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
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: {}'.format(name))
for chunk in result:
print(chunk, end='')
print('')
ExtensionManager 的创建方式与 DriverManager 略有不同,因为它不需要提前知道要加载哪个插件。它加载它找到的所有插件。
default=60,
type=int,
help='maximum output width for text',
)
parsed_args = parser.parse_args()
要调用插件,请使用 map() 方法,传递一个要为每个扩展调用的 callable。 在此示例中与 map() 一起使用的 format_data() 函数接受两个参数:Extension 和传递给 map() 的数据参数。
data = {
'a': 'A',
'b': 'B',
'long': 'word ' * 80,
传递给 format_data() 的 Extension 是 stevedore 定义的一个类,它封装了插件。它包括插件的名称、importlib.metadata 返回的 EntryPoint 以及插件本身(插件定义引用的命名对象)。当 invoke_on_load 为 true 时,Extension 也会有一个 obj 属性,其中包含插件被调用时返回的值。
map() 返回一个由回调函数返回的值序列。 在这种情况下,format_data() 返回一个元组,其中包含扩展名和生成要打印的文本的可迭代对象。 在处理结果时,将打印每个插件的名称,然后是格式化的数据。
'long': 'word ' * 80,
}
mgr = extension.ExtensionManager(
namespace='stevedore.example.formatter',
插件加载的顺序是不确定的,取决于在导入路径上找到的包的顺序以及读取元数据文件的方式。 如果扩展的使用顺序很重要,请尝试 NamedExtensionManager。
$ python -m stevedore.example.load_as_extension --width 30
Formatter: simple
a = A
b = B
long = word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word
Formatter: field
: a : A
: b : B
: long : word word word word
word word word word word
word word word word word
word word word word word
word word word word word
word word word word word
word word word word word
word word word word word
word word word word word
word word word word word
word word word word word
word word word word word
word word word word word
word word word word word
word word word word word
word word word word word
word
Formatter: plain
a = A
b = B
long = word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word
为什么不直接调用插件?¶
使用单独的 callable 参数传递给 map(),而不是直接调用插件,这在您的应用程序代码和插件之间引入了分离。 这种分离的好处体现在应用程序代码设计和插件 API 设计中。
如果 map() 直接调用插件,那么每个插件都必须是一个 callable。 这意味着对于实际上只是插件的方法,需要一个单独的命名空间。 通过使用单独的 callable 参数,插件 API 不需要完全匹配应用程序中的任何特定用例。 这使您可以创建更细粒度的 API,其中有更多的单个方法可以以不同的方式调用以实现不同的目标。