扩展金字塔配置

金字塔允许您使用自定义指令扩展其配置程序。自定义指令可以使用其他指令,它们可以添加自定义 action ,他们可以参与 conflict resolution 它们可以提供一些 introspectable 物体。

通过向配置器添加方法 add_directive

框架扩展编写器可以将任意方法添加到 Configurator 通过使用 pyramid.config.Configurator.add_directive() 配置器的方法。使用 add_directive() 使以任意方式扩展金字塔配置器成为可能,并允许它更简洁地执行特定于应用程序的任务。

这个 add_directive() 方法接受两个位置参数:方法名和可调用对象。可调用对象通常是一个以配置器实例为第一个参数并接受其他任意位置和关键字参数的函数。例如:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
from pyramid.events import NewRequest
from pyramid.config import Configurator

def add_newrequest_subscriber(config, subscriber):
    config.add_subscriber(subscriber, NewRequest)

if __name__ == '__main__':
    config = Configurator()
    config.add_directive('add_newrequest_subscriber',
                         add_newrequest_subscriber)

一次 add_directive() 然后,用户可以使用其给定名称调用添加的指令,就像它是配置程序的内置方法一样:

1
2
3
4
def mysubscriber(event):
    print(event.request)

config.add_newrequest_subscriber(mysubscriber)

呼叫 add_directive() 通常“隐藏”在 includeme “frameworky”包中的函数,应根据 包括来自外部源的配置 通过 include() . 例如,如果将此代码放入名为 pyramid_subscriberhelpers

1
2
3
def includeme(config):
    config.add_directive('add_newrequest_subscriber',
                         add_newrequest_subscriber)

附加程序包的用户 pyramid_subscriberhelpers 然后将能够安装它,然后执行以下操作:

1
2
3
4
5
6
7
def mysubscriber(event):
    print(event.request)

from pyramid.config import Configurator
config = Configurator()
config.include('pyramid_subscriberhelpers')
config.add_newrequest_subscriber(mysubscriber)

使用 config.action 在指示中

如果一个自定义指令不能以现有的配置器方法(例如 pyramid.config.Configurator.add_subscriber() 如上所述),指令可能需要使用 pyramid.config.Configurator.action() 方法。此方法将一个条目添加到金字塔将尝试处理的“操作”列表中,当 pyramid.config.Configurator.commit() 被称为。动作只是一本字典,其中包含 discriminator 可能是回调函数,也可能是金字塔操作系统使用的其他元数据。

下面是一个使用“action”方法的示例指令:

1
2
3
4
5
6
7
8
def add_jammyjam(config, jammyjam):
    def register():
        config.registry.jammyjam = jammyjam
    config.action('jammyjam', register)

if __name__ == '__main__':
    config = Configurator()
    config.add_directive('add_jammyjam', add_jammyjam)

很好,但它有什么作用?action方法接受多个参数。在上面名为 add_jammyjam 我们称之为 action() 有两个参数:字符串 jammyjam 作为名为 discriminator 和名为 register 作为名为 callable .

action() 方法,它将一个操作附加到挂起的配置操作列表中。具有相同鉴别器值的所有挂起操作都可能相互冲突(请参阅 冲突检测 )当 commit() 调用配置器的方法(显式调用或作为调用的结果调用 make_wsgi_app() ,冲突行为可能会根据自动解决 自动冲突解决 . 如果无法自动解决冲突,则 pyramid.exceptions.ConfigurationConflictError 引发并阻止应用程序启动。

因此,在我们上面的示例中,如果 add_jammyjam 指令是这样做的:

config.add_jammyjam('first')
config.add_jammyjam('second')

当由于上述调用集而提交操作列表时,我们的用户应用程序将不会启动,因为两个调用生成的操作的鉴别器直接冲突。自动冲突解决无法解决冲突(因为没有 config.include 用户没有提供中间产品 pyramid.config.Configurator.commit() 呼叫之间的呼叫 add_jammyjam 以确保连续调用不会相互冲突。

这演示了action方法的discriminator参数的用途:它用于指示action的唯一性约束。除非冲突自动或手动解决,否则具有相同鉴别器的两个操作将发生冲突。鉴别器可以是任何哈希对象,但通常是字符串或元组。 使用鉴别器声明性地确保用户不提供不明确的配置语句。

但是让我们想象一下 add_jammyjam 使用时不会产生配置冲突。

config.add_jammyjam('first')

现在怎么办?当 add_jammyjam 方法,一个操作将附加到挂起的操作列表中。处理挂起的配置操作时 commit() 并且没有冲突发生, 可赎回的 作为第二个参数提供给 action() 内方法 add_jammyjam 调用时没有参数。可调用的 add_jammyjamregister 关闭功能。它只是设置值 config.registry.jammyjam 无论用户作为 jammyjam 论据 add_jammyjam 功能。因此,用户调用我们的指令的结果将设置 jammyjam 注册表到字符串的属性 first . 指令使用可调用项来延迟用户调用该指令的结果,直到冲突检测有机会完成其工作为止。 .

其他参数存在于 action() 方法,包括 argskworderintrospectables .

argskw 作为值存在,如果传递该值,则将用作 callable 函数。例如,我们的指令可能会这样使用它们:

1
2
3
4
5
6
def add_jammyjam(config, jammyjam):
    def register(*arg, **kw):
        config.registry.jammyjam_args = arg
        config.registry.jammyjam_kw = kw
        config.registry.jammyjam = jammyjam
    config.action('jammyjam', register, args=('one',), kw={'two':'two'})

在上面的示例中,当使用此指令生成操作并且该操作已提交时, config.registry.jammyjam_args 将被设置为 ('one',)config.registry.jammyjam_kw 将被设置为 {{'two':'two'}} . argskw 当你 callable 是一个闭包函数,因为您通常已经可以访问指令中的每个本地函数,而不需要将它们传递回。但是,如果不使用闭包作为可调用的闭包,它们可能很有用。

order 是一个粗糙的命令控制机制。 order 默认为整数 0 ;可以设置为任何其他整数。共享订单的所有操作将在共享更高订单的其他操作之前调用。这使得编写一个具有可调用逻辑的指令成为可能,该指令依赖于先执行另一个指令的可调用执行。例如,金字塔 pyramid.config.Configurator.add_view() 指令以高于 pyramid.config.Configurator.add_route() 方法。因此, add_view 方法的可调用性可以假定,如果 route_name 已传递给它,此名称的路由已由注册 add_route ,如果这样的路由尚未注册,则是配置错误(视图通过 route_name 永远不会调用参数)。

在 1.6 版更改: 从金字塔1.6开始,一个动作可以调用另一个动作。见 排序操作 更多信息。

最后, introspectables 是一个序列 introspectable 物体。您可以将一系列自省传递给 action() 方法,它允许您增强金字塔的配置内省系统。

排序操作

在金字塔里每 action 相对于其他动作具有固有的顺序。动作中的逻辑延迟到调用 pyramid.config.Configurator.commit() (由自动调用) pyramid.config.Configurator.make_wsgi_app() )这意味着你可以打电话 config.add_view(route_name='foo') 之前 config.add_route('foo', '/foo') 因为在提交时间之前什么都不会发生。在提交周期中,冲突被解决,动作被命令和执行。

默认情况下,金字塔中几乎每个动作都有一个 order 属于 pyramid.config.PHASE3_CONFIG . 同一订单级别内的每个操作都将按其调用的顺序执行。这意味着,如果一个动作必须在另一个动作之前或之后可靠地执行,那么 order 必须明确定义才能使此工作。例如,视图依赖于正在定义的路由。由此产生的作用 pyramid.config.Configurator.add_route() 有一个 order 属于 pyramid.config.PHASE2_CONFIG .

从操作调用操作

1.6 新版功能.

金字塔的配置器允许在提交周期中添加动作,只要它们被添加到当前或更高版本。 order 相位。这意味着您的自定义操作可以将决策推迟到提交时间,然后执行诸如invoke之类的操作 pyramid.config.Configurator.add_route() . 如果您的加载项需要调用多个其他操作,它还可以提供更好的冲突检测。

例如,让我们制作一个调用 add_routeadd_view ,但我们希望它与任何其他调用我们的加载项冲突:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
from pyramid.config import PHASE0_CONFIG

def includeme(config):
    config.add_directive('add_auto_route', add_auto_route)

def add_auto_route(config, name, view):
    def register():
        config.add_view(route_name=name, view=view)
        config.add_route(name, '/' + name)
    config.action(('auto route', name), register, order=PHASE0_CONFIG)

现在其他人可以使用您的加载项,并在该路由与其他路由或两个呼叫之间发生冲突时得到通知。 add_auto_route . 注意我们是如何调用我们的行为的 之前 add_viewadd_route . 如果我们随后尝试调用此函数,则随后调用 add_viewadd_route 会导致冲突,因为该阶段已被执行,并且配置程序无法在提交周期中及时返回以添加更多视图。

1
2
3
4
5
6
7
8
9
from pyramid.config import Configurator

def main(global_config, **settings):
    config = Configurator()
    config.include('auto_route_addon')
    config.add_auto_route('foo', my_view)

def my_view(request):
    return request.response

添加配置自省

1.3 新版功能.

金字塔提供了一个配置自省系统,调试工具可以使用它来提供对正在运行的应用程序配置的可见性。

所有内置的金字塔指令(例如 pyramid.config.Configurator.add_view()pyramid.config.Configurator.add_route() )调用时注册一组自省。例如,当您通过 add_view ,该指令注册了至少一个可内省的:一个关于视图注册本身的可内省的,为传递给它的参数提供人类可消费的值。稍后,您可以使用自省查询系统来确定特定视图是否使用呈现器,或者特定视图是否仅限于特定请求方法,或者特定视图注册的路由。金字塔“调试工具栏”以各种方式利用内省系统向金字塔开发者显示信息。

当一个序列 introspectable 对象传递到 action() 方法。下面是一个使用自省的指令示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
def add_jammyjam(config, value):
    def register():
        config.registry.jammyjam = value
    intr = config.introspectable(category_name='jammyjams',
                                 discriminator='jammyjam',
                                 title='a jammyjam',
                                 type_name=None)
    intr['value'] = value
    config.action('jammyjam', register, introspectables=(intr,))

if __name__ == '__main__':
    config = Configurator()
    config.add_directive('add_jammyjam', add_jammyjam)

如果您注意到,上面的指令使用 introspectable 配置器的属性 (pyramid.config.Configurator.introspectable )创建一个可内省的对象。自省对象的构造函数至少需要四个参数: category_name , the discriminator , the titletype_name .

这个 category_name 是表示此自省的逻辑类别的字符串。通常,类别名称是通过操作添加的对象类型的复数形式。

这个 discriminator 值是否唯一 在类别内 (不同于动作鉴别器,它在整个动作集中必须是唯一的)。它通常是一个字符串或元组,表示该类别中可内省的唯一值。它用于生成链接,并作为其他自省对象的关系形成目标的一部分。

这个 title 是一个人类可消费的字符串,内省系统前端可以使用它来显示这个内省的友好摘要。

这个 type_name 是一个值,可用于在其类别中对该自省进行子类型化,以便进行排序和表示。它可以是任何价值。

反省也是字典式的。它可以包含任何一组键/值对,通常与传递给其相关指令的参数相关。而 category_namediscriminatortitletype_name元数据 关于内省,作为键/值对提供的值是内省提供的实际数据。在上面的示例中,我们设置 value 关键在于 value 传递给指令的参数。

我们上面的指示改变了内省,并将其传递给 action 方法作为元组的第一个元素作为 introspectable 关键字参数。这将反省与行动联系起来。然后,自省工具将在其索引中显示这种自省。

反省的关系

两个自省者之间可能有关系。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
def add_jammyjam(config, value, template):
    def register():
        config.registry.jammyjam = (value, template)
    intr = config.introspectable(category_name='jammyjams',
                                 discriminator='jammyjam',
                                 title='a jammyjam',
                                 type_name=None)
    intr['value'] = value
    tmpl_intr = config.introspectable(category_name='jammyjam templates',
                                      discriminator=template,
                                      title=template,
                                      type_name=None)
    tmpl_intr['value'] = template
    intr.relate('jammyjam templates', template)
    config.action('jammyjam', register, introspectables=(intr, tmpl_intr))

if __name__ == '__main__':
    config = Configurator()
    config.add_directive('add_jammyjam', add_jammyjam)

在上面的示例中, add_jammyjam 指令注册了两个自省项:第一个与 value 传递给指令,第二个与 template 传递给指令。如果您认为指令中的一个概念非常重要,足以使其具有自省性,那么您可以使同一个指令注册多个自省性概念,为“主要概念”注册一个自省性概念,为相关概念注册另一个自省性概念。

呼唤 intr.relate 在上面 (pyramid.interfaces.IIntrospectable.relate() )传递两个参数:类别名称和指令。上面的示例有效地表明指令希望在 intr 反省和 tmpl_intr 反省的;传递给 relatetmpl_intr 可反省的

不需要在由同一指令创建的两个自省之间建立关系。相反,可以通过调用 relate 在另一个指令的类别名称和鉴别器的两侧。但是,如果您试图将一个自省与另一个不存在的自省关联起来,那么在配置提交时会引发一个错误。

自省关系将显示在前端系统自省值的呈现中。例如,如果一个视图注册命名一个路由名称,那么与该视图可调用相关的自省将显示对与其相关的路由的引用,反之亦然。