高级配置

为了支持应用程序的可扩展性, Pyramid Configurator 默认情况下,检测配置冲突,并允许您强制包含来自其他包或模块的配置。默认情况下,它还将在两个单独的阶段执行配置。这允许您在某些情况下忽略相对配置语句排序。

冲突检测

下面是一个常见的最简单的例子 Pyramid 应用程序,强制配置:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
from wsgiref.simple_server import make_server
from pyramid.config import Configurator
from pyramid.response import Response

def hello_world(request):
    return Response('Hello world!')

if __name__ == '__main__':
    config = Configurator()
    config.add_view(hello_world)
    app = config.make_wsgi_app()
    server = make_server('0.0.0.0', 8080, app)
    server.serve_forever()

当您启动此应用程序时,一切都将正常。但是,如果我们尝试使用相同的 predicate 我们已经添加的参数?

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
from wsgiref.simple_server import make_server
from pyramid.config import Configurator
from pyramid.response import Response

def hello_world(request):
    return Response('Hello world!')

def goodbye_world(request):
    return Response('Goodbye world!')

if __name__ == '__main__':
    config = Configurator()

    config.add_view(hello_world, name='hello')

    # conflicting view configuration
    config.add_view(goodbye_world, name='hello')

    app = config.make_wsgi_app()
    server = make_server('0.0.0.0', 8080, app)
    server.serve_forever()

应用程序现在有两个冲突的视图配置语句。当我们试图再次启动它时,它不会启动。相反,我们将收到一个这样结束的回溯:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
Traceback (most recent call last):
  File "app.py", line 12, in <module>
    app = config.make_wsgi_app()
  File "pyramid/config.py", line 839, in make_wsgi_app
    self.commit()
  File "pyramid/pyramid/config.py", line 473, in commit
    self._ctx.execute_actions()
  ... more code ...
pyramid.exceptions.ConfigurationConflictError:
        Conflicting configuration actions
  For: ('view', None, '', None, <InterfaceClass pyramid.interfaces.IView>,
        None, None, None, None, None, False, None, None, None)
  Line 14 of file app.py in <module>: 'config.add_view(hello_world)'
  Line 17 of file app.py in <module>: 'config.add_view(goodbye_world)'

这个回溯试图告诉我们:

  • 我们有一组视图配置语句的冲突信息 For: 线)。
  • 有两种说法冲突,显示在 For: 线: config.add_view(hello_world. 'hello') 在线14 app.pyconfig.add_view(goodbye_world, 'hello') 在线17 app.py .

这两个配置语句发生冲突,因为我们试图告诉系统 predicate 两种视图配置的值完全相同。两个 hello_worldgoodbye_world 视图配置为在同一组情况下响应。这种情况下, view name 代表 name= 谓语是 hello .

这就产生了一种歧义,即 Pyramid 无法解决。在默认情况下,金字塔不允许环境未报告,而是提出一个 ConfigurationConflictError 错误并阻止应用程序运行。

任何类型的配置都会发生冲突检测:强制配置或执行 scan .

手动解决冲突

有许多方法可以手动解决冲突:将注册更改为不冲突,策略性地使用 pyramid.config.Configurator.commit() 或使用“自动提交”配置程序。

正确的事情

解决冲突的最正确方法是“做必要的”:将配置代码更改为没有冲突的配置语句。如何完成此操作的详细信息完全取决于应用程序所做的配置语句。使用中提供的详细信息 ConfigurationConflictError 要跟踪有问题的冲突并相应地修改配置代码。

如果在尝试扩展现有应用程序时遇到冲突,并且该应用程序具有执行如下配置的函数:

1
2
def add_routes(config):
    config.add_route(...)

不要直接用 config 作为一个论点。相反,使用 pyramid.config.Configurator.include()

1
config.include(add_routes)

使用 include() 与直接调用函数不同,调用代码中定义的配置语句将覆盖所包含函数的配置语句,从而提供了一点自动的冲突解决方法。

使用 config.commit()

您可以使用 commit() 配置调用之间的方法。承诺之后,更多 configuration declaration S可以添加到 configurator . 新声明不会与已提交的声明冲突。新声明将重写已提交的声明。

例如,我们通过添加 commit . 下面是产生冲突的应用程序:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
from wsgiref.simple_server import make_server
from pyramid.config import Configurator
from pyramid.response import Response

def hello_world(request):
    return Response('Hello world!')

def goodbye_world(request):
    return Response('Goodbye world!')

if __name__ == '__main__':
    config = Configurator()

    config.add_view(hello_world, name='hello')

    # conflicting view configuration
    config.add_view(goodbye_world, name='hello')

    app = config.make_wsgi_app()
    server = make_server('0.0.0.0', 8080, app)
    server.serve_forever()

我们可以阻止两个 add_view 从发出呼叫到的冲突呼叫 commit() 它们之间:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from wsgiref.simple_server import make_server
from pyramid.config import Configurator
from pyramid.response import Response

def hello_world(request):
    return Response('Hello world!')

def goodbye_world(request):
    return Response('Goodbye world!')

if __name__ == '__main__':
    config = Configurator()

    config.add_view(hello_world, name='hello')

    config.commit() # commit any pending configuration actions

    # no-longer-conflicting view configuration
    config.add_view(goodbye_world, name='hello')

    app = config.make_wsgi_app()
    server = make_server('0.0.0.0', 8080, app)
    server.serve_forever()

在上面的示例中,我们向 commit() 两者之间 add_view 电话。 commit() 将执行任何挂起的配置语句。

调用 commit() 在任何时候都是安全的。它执行所有挂起的配置操作,并使配置操作列表保持“干净”。

注意 commit() 当您使用 自动提交 配置器(参见 使用自动提交配置程序

使用自动提交配置程序

您还可以使用重锤通过使用配置器构造函数参数来绕过冲突检测: autocommit=True . 例如:

1
2
3
4
from pyramid.config import Configurator

if __name__ == '__main__':
    config = Configurator(autocommit=True)

autocommit 传递给配置程序的参数是 True ,冲突检测(和 两相配置 )已禁用。配置语句将立即执行,后续语句将覆盖前面的语句。

commit() 在以下情况下无效 autocommitTrue .

如果在执行单元测试的代码中使用配置程序,通常最好使用自动提交配置程序,因为您通常不关心测试代码中的冲突检测或两阶段配置。

自动冲突解决

如果代码使用 include() 方法包括外部配置,一些冲突将自动解决。由于“include”而生成的配置语句将被发生在“include”方法调用方中的配置语句重写。

自动冲突解决支持此目标。如果用户希望重用金字塔应用程序,并且希望自定义此应用程序的配置,而不“从外部”对其代码进行黑客攻击,则可以从包中“包含”配置函数,并仅重写包含该应用程序的代码中的某些配置语句。执行include的代码中的配置语句不会生成冲突,即使所包含代码中的配置语句在“向上”移动到调用代码时会发生冲突。

提供冲突检测的方法

这些是配置程序提供冲突检测的方法:

add_view()add_route()add_renderer()add_request_method()set_request_factory()set_session_factory()set_root_factory()set_view_mapper()set_authentication_policy()set_authorization_policy()set_locale_negotiator()set_default_permission()add_traverser()add_resource_url_adapter()add_response_adapter() .

add_static_view() 也间接地提供冲突检测,因为它是根据冲突感知来实现的 add_routeadd_view 方法。

包括来自外部源的配置

一些应用程序编程人员会以这样一种方式考虑他们的配置代码,即很容易重用和重写配置语句。例如,这样的开发人员可能会考虑将路由添加到其应用程序的函数:

1
2
def add_routes(config):
    config.add_route(...)

而不是直接用 config 作为参数,使用 pyramid.config.Configurator.include()

1
config.include(add_routes)

使用 include 而不是直接调用函数将允许 自动冲突解决 工作。

include() 也可以接受 module 作为一个论点:

1
2
3
import myapp

config.include(myapp)

为了使其正常工作, myapp 模块必须包含具有特殊名称的可调用文件 includeme ,应该执行配置(如 add_routes 作为一个例子,我们在上面展示了Callable)。

include() 也可以接受 dotted Python name 功能或模块。

注解

The <include> Tag 对于声明性替代 include() 方法。

两相配置

当非自动提交 Configurator 用于进行配置(默认),配置执行分两个阶段进行。在第一阶段中,“渴望”配置操作(必须在所有其他操作(如注册渲染器)执行之前执行的操作)以及 鉴别器 为依赖于所需操作结果的每个操作计算。在第二阶段,比较所有行为的鉴别器,进行冲突检测。

因此,对于没有内部排序约束的配置方法,配置方法调用的执行顺序并不重要。例如,相对于 add_view()add_renderer() 当使用非自动提交配置程序时不重要。此代码段:

1
2
config.add_view('some.view', renderer='path_to_custom/renderer.rn')
config.add_renderer('.rn', SomeCustomRendererFactory)

结果与以下结果相同:

1
2
config.add_renderer('.rn', SomeCustomRendererFactory)
config.add_view('some.view', renderer='path_to_custom/renderer.rn')

尽管VIEW语句依赖于自定义渲染器的注册,但由于是两阶段配置,因此发出配置语句的顺序并不重要。 add_view 将能够找到 .rn 渲染器,即使 add_renderer 后称 add_view .

当你使用 自动提交 配置器(参见 使用自动提交配置程序 )当使用自动提交配置程序时,将禁用两阶段配置,并且必须按依赖顺序排列配置语句。

一些配置方法,例如 add_route() 具有内部排序约束:它们所暗示的路由需要相对排序。这种排序约束不能通过两相配置来解除。路由仍按配置执行顺序添加。

更多信息

有关更多信息,请参阅文章 A Whirlwind Tour of Advanced Configuration Tactics 在金字塔社区食谱中。