跳转到主要内容

动态配置

项目描述

此软件包提供了一种配置器,它设计用于在创建Zope3组件之后对其进行扩展。

配置器

配置器设计用于在创建组件之后对其进行扩展。传统上,这是通过监听ObjectCreatedEvent事件来完成的。然而,这种方法不足以满足需求,因为配置通常依赖于其他配置步骤,并且通常需要额外的数据来完成配置。这就是配置器的用武之地。它使用一个独立的插件机制来实现所提到的功能。

在我们可以演示配置机制之前,我们必须创建一个接口和一个组件,配置可以在其上操作

>>> import zope.interface
>>> class ISomething(zope.interface.Interface):
...     """Some interesting interface."""
>>> @zope.interface.implementer(ISomething)
... class Something(object):
...     """Implementation of something."""
>>> something = Something()

现在我们可以让配置对组件进行操作

>>> from z3c import configurator
>>> configurator.configure(something, {})

第二个参数是数据字典,可以用来传递在配置过程中可能需要的附加信息。每个插件都负责解析这些数据。

当然,由于没有注册任何配置插件,所以没有任何操作。现在我们创建一个新的配置插件,为组件设置一个新的属性

>>> import zope.component
>>> from z3c.configurator import interfaces
>>> class AddFooAttribute(configurator.ConfigurationPluginBase):
...     zope.component.adapts(ISomething)
...
...     def __call__(self, data):
...         setattr(self.context, 'foo', data.get('foo'))
>>> zope.component.provideAdapter(AddFooAttribute, name='add foo')

如果我们再次执行配置,将添加该属性

>>> configurator.configure(something, {'foo': u'my value'})
>>> something.foo
u'my value'

依赖关系

现在我们已经有了简单的配置插件,我们也可以开发依赖于其他插件的插件。让我们创建一个配置插件,为foo属性添加一些附加数据。显然,在执行此步骤之前,foo属性必须存在。可以使用dependencies属性通过名称指定所有插件依赖项

>>> class ExtendFooAttribute(configurator.ConfigurationPluginBase):
...     zope.component.adapts(ISomething)
...     dependencies = ('add foo',)
...
...     def __call__(self, data):
...         self.context.foo = u'Text: ' + self.context.foo
>>> zope.component.provideAdapter(ExtendFooAttribute, name='extend foo')

如果我们现在再次执行配置,应该可以看到扩展的结果

>>> something = Something()
>>> configurator.configure(something, {'foo': u'my value'})
>>> something.foo
u'Text: my value'

数据模式

出于纯粹的信息目的,插件上使用了一个schema属性来描述插件期望从数据字典中获取的字段。为了添加另一个简单属性,这可能如下所示

>>> import zope.schema
>>> class IAddBar(zope.interface.Interface):
...     bar = zope.schema.Text(title=u'Bar')
>>> class AddBarAttribute(configurator.SchemaConfigurationPluginBase):
...     zope.component.adapts(ISomething)
...     schema = IAddBar
...
...     def __call__(self, data):
...         self.verify(data)
...         setattr(self.context, 'bar', data.get('bar'))
>>> zope.component.provideAdapter(AddBarAttribute, name='add bar')

使用基类的好处是它提供了一个verify()方法,允许您验证数据是否与模式匹配。我们现在可以再次运行配置

>>> something = Something()
>>> configurator.configure(something, {'foo': u'my value', 'bar': u'value'})
>>> something.bar
u'value'

值必须存在且有效

>>> something = Something()
>>> configurator.configure(something, {'foo': u'my value'})
Traceback (most recent call last):
...
RequiredMissing: bar
>>> something = Something()
>>> configurator.configure(something, {'foo': u'my value', 'bar': 1})
Traceback (most recent call last):
...
WrongType: (1, <type 'unicode'>, 'bar')

数据命名空间

为了避免两个共享相同名称的插件混淆属性名称,可以将数据作为字典的字典传递。字典的键是插件注册的名称

>>> something = Something()
>>> data = {u'add foo': {'foo': u'foo value'},
...         u'add bar': {'bar': u'bar value'}}
>>> configurator.configure(something, data, useNameSpaces=True)
>>> something.foo, something.bar
(u'Text: foo value', u'bar value')

命名配置

有时我们不想执行所有已注册的配置插件。可以通过向configure函数提供names参数来实现这一点

让我们创建一个新的东西

>>> something = Something()

如果我们现在不指定名称来配置它,我们将设置两个属性

>>> configurator.configure(something, {'foo': u'my value', 'bar': u'asdf'})
>>> sorted(something.__dict__.items())
[('bar', 'asdf'), ('foo', 'Text: my value')]

现在让我们只配置插件‘add bar’。

>>> something = Something()
>>> configurator.configure(something, {'foo': u'my value', 'bar': u'asdf'},
...     names=['add bar'])
>>> something.__dict__
{'bar': u'asdf'}

插件依赖项总是执行,不需要添加到`names`参数中。

>>> something = Something()
>>> configurator.configure(something, {'foo': u'my value'},
...     names=['extend foo'])
>>> something.foo
u'Text: my value'

命名配置在手动通过网页调用时很有用(参见browser/README.txt)。当调用两次时,配置器包不会检查配置是否已经应用。这是插件的责任,要意识到它不会重复执行或删除操作。

错误的实现

配置器必须提供一个__call__方法。

>>> class CallNotImplemented(configurator.ConfigurationPluginBase):
...     zope.component.adapts(ISomething)
>>> zope.component.provideAdapter(CallNotImplemented, name='no call')
>>> configurator.configure(something, None,  names=['no call'])
Traceback (most recent call last):
...
NotImplementedError

对于基于模式的配置器也必须如此。

>>> class SchemaCallNotImplemented(configurator.SchemaConfigurationPluginBase):
...     zope.component.adapts(ISomething)
>>> zope.component.provideAdapter(SchemaCallNotImplemented, name='schema no call')
>>> configurator.configure(something, None,  names=['schema no call'])
Traceback (most recent call last):
...
NotImplementedError

无递归

可以定义递归依赖项而不会遇到递归错误。让我们定义一个新的插件free对象

>>> class IFoo(zope.interface.Interface):
...     """Just a foo interface."""
>>> @zope.interface.implementer(IFoo)
... class Foo(object):
...     """Implementation of foo."""

让我们定义另一个名为的插件,它依赖于名为的插件。

>>> log = []
>>> class FirstPlugin(configurator.ConfigurationPluginBase):
...     zope.component.adapts(IFoo)
...     dependencies = ('second',)
...
...     def __call__(self, data):
...         log.append('FirstPlugin called')
>>> zope.component.provideAdapter(FirstPlugin, name='first')

然后定义一个名为的插件,它依赖于

>>> class SecondPlugin(configurator.ConfigurationPluginBase):
...     zope.component.adapts(IFoo)
...     dependencies = ('first',)
...
...     def __call__(self, data):
...         log.append('SecondPlugin called')
>>> zope.component.provideAdapter(SecondPlugin, name='second')
>>> foo = Foo()
>>> configurator.configure(foo, {})
>>> for msg in sorted(log): print(msg)
FirstPlugin called
SecondPlugin called

在TTW中调用配置器

注册了一个配置视图,以在任意对象上应用命名配置。我们定义了两个示例配置器,现在我们将它们应用到site对象上。

>>> from zope.testbrowser.wsgi import Browser
>>> browser = Browser(wsgi_app=layer.make_wsgi_app())
>>> browser.addHeader('Authorization','Basic mgr:mgrpw')
>>> browser.handleErrors = False
>>> browser.open('https://127.0.0.1/manage')
>>> browser.url
'https://127.0.0.1/@@contents.html'

视图注册在zmi_views菜单中

>>> browser.getLink(u'Configurators').click()
>>> viewURL = browser.url
>>> viewURL
'https://127.0.0.1/@@configurators.html'
>>> sel = browser.getControl(name="form.pluginNames.to")

首先,我们可以从注册的命名插件中选择。

>>> plugs = browser.getControl(name="form.pluginNames.from").options
>>> sorted(plugs)
['z3c.configurator.testing.setdescription', 'z3c.configurator.testing.settitle']
>>> browser.open(viewURL + '?form.pluginNames=z3c.configurator.testing.settitle')

我们选择了一个插件,所以现在我们有一个表单来填写所需的参数。

>>> browser.getControl('Some Argument').value
''
>>> browser.getControl('Some Argument').value = "New Title"
>>> browser.getControl('Apply').click()

XXX form.pluginNames必须设置,但我们不能,因为小部件使用了javascript。

变更记录

3.0 (2023-02-17)

  • 停止支持Python 2.6、2.7、3.3。

  • 添加对Python 3.7、3.8、3.9、3.10、3.11的支持。

  • 停止使用python setup.py test运行测试。

2.0.0 (2015-11-09)

  • 标准化namespace __init__

2.0.0a1 (2013-03-04)

  • 添加了对Python 3.3的支持,删除了对Python 2.4和2.5的支持。

1.3.0 (2010-12-12)

  • 更新测试设置以使用ZTK 1.0。

版本 1.2.1 (2009-12-27)

  • 将浏览器依赖项移动到‘zmi’额外插件

版本 1.2.0 (2009-12-19)

  • 使浏览器视图的注册有条件

  • 将测试所需的包移动到‘test’额外插件

  • 删除旧的zpkg-related SETUP.cfg文件。

  • 版权“Zope Foundation and Contributors”

版本 1.1.2 (2009-01-04)

  • 在configure中添加了仅应用特定命名插件的可能性。

  • 配置的新选项允许使用命名空间数据来解决命名冲突。

  • 添加了一个页面以调用TTW配置器。这是将z3c.configurator和z3c.sampledata合并为一个包的第一步。

  • 在Pypi主页上添加了文档。

  • 错误修复:在IConfigurationPlugin依赖项中定义递归依赖名称,会导致递归插件查找。

  • 错误修复:SchemaConfigurationPluginBase现在实现了ISchemaConfigurationPluginBase。

版本 1.1.1 (未知)

  • 初始版本

项目详情


下载文件

下载适用于您的平台的文件。如果您不确定选择哪个,请了解更多关于安装包的信息。

源分布

z3c.configurator-3.0.tar.gz (17.5 kB 查看哈希)

上传时间:

构建分布

z3c.configurator-3.0-py3-none-any.whl (19.4 kB 查看哈希)

上传时间: Python 3

由支持