Michael Merickel
2018-10-04 35b2e933813ea596262f15d3672b5ab5e4a13c04
commit | author | age
10fd8f 1 .. index::
CM 2    pair: advanced; configuration
3
4 .. _advconfig_narr:
5
6 Advanced Configuration
7 ======================
8
1205ec 9 To support application extensibility, the :app:`Pyramid` :term:`Configurator`
SP 10 by default detects configuration conflicts and allows you to include
11 configuration imperatively from other packages or modules.  It also by default
12 performs configuration in two separate phases.  This allows you to ignore
13 relative configuration statement ordering in some circumstances.
10fd8f 14
CM 15 .. index::
6ce1e0 16    pair: configuration; conflict detection
10fd8f 17
CM 18 .. _conflict_detection:
19
20 Conflict Detection
21 ------------------
22
23 Here's a familiar example of one of the simplest :app:`Pyramid` applications,
24 configured imperatively:
25
26 .. code-block:: python
adf1fa 27     :linenos:
10fd8f 28
adf1fa 29     from wsgiref.simple_server import make_server
SP 30     from pyramid.config import Configurator
31     from pyramid.response import Response
10fd8f 32
adf1fa 33     def hello_world(request):
SP 34         return Response('Hello world!')
10fd8f 35
adf1fa 36     if __name__ == '__main__':
SP 37         config = Configurator()
38         config.add_view(hello_world)
39         app = config.make_wsgi_app()
40         server = make_server('0.0.0.0', 8080, app)
41         server.serve_forever()
10fd8f 42
CM 43 When you start this application, all will be OK.  However, what happens if we
44 try to add another view to the configuration with the same set of
45 :term:`predicate` arguments as one we've already added?
46
47 .. code-block:: python
adf1fa 48     :linenos:
10fd8f 49
adf1fa 50     from wsgiref.simple_server import make_server
SP 51     from pyramid.config import Configurator
52     from pyramid.response import Response
10fd8f 53
adf1fa 54     def hello_world(request):
SP 55         return Response('Hello world!')
10fd8f 56
adf1fa 57     def goodbye_world(request):
SP 58         return Response('Goodbye world!')
10fd8f 59
adf1fa 60     if __name__ == '__main__':
SP 61         config = Configurator()
10fd8f 62
adf1fa 63         config.add_view(hello_world, name='hello')
10fd8f 64
adf1fa 65         # conflicting view configuration
SP 66         config.add_view(goodbye_world, name='hello')
10fd8f 67
adf1fa 68         app = config.make_wsgi_app()
SP 69         server = make_server('0.0.0.0', 8080, app)
70         server.serve_forever()
10fd8f 71
1205ec 72 The application now has two conflicting view configuration statements.  When we
SP 73 try to start it again, it won't start.  Instead we'll receive a traceback that
74 ends something like this:
10fd8f 75
1205ec 76 .. code-block:: text
adf1fa 77     :linenos:
10fd8f 78
adf1fa 79     Traceback (most recent call last):
SP 80       File "app.py", line 12, in <module>
81         app = config.make_wsgi_app()
82       File "pyramid/config.py", line 839, in make_wsgi_app
83         self.commit()
84       File "pyramid/pyramid/config.py", line 473, in commit
85         self._ctx.execute_actions()
86       ... more code ...
87     pyramid.exceptions.ConfigurationConflictError:
88             Conflicting configuration actions
89       For: ('view', None, '', None, <InterfaceClass pyramid.interfaces.IView>,
90             None, None, None, None, None, False, None, None, None)
91       Line 14 of file app.py in <module>: 'config.add_view(hello_world)'
92       Line 17 of file app.py in <module>: 'config.add_view(goodbye_world)'
10fd8f 93
CM 94 This traceback is trying to tell us:
95
1205ec 96 - We've got conflicting information for a set of view configuration statements
SP 97   (The ``For:`` line).
10fd8f 98
CM 99 - There are two statements which conflict, shown beneath the ``For:`` line:
100   ``config.add_view(hello_world. 'hello')`` on line 14 of ``app.py``, and
101   ``config.add_view(goodbye_world, 'hello')`` on line 17 of ``app.py``.
102
1205ec 103 These two configuration statements are in conflict because we've tried to tell
SP 104 the system that the set of :term:`predicate` values for both view
10fd8f 105 configurations are exactly the same.  Both the ``hello_world`` and
CM 106 ``goodbye_world`` views are configured to respond under the same set of
1205ec 107 circumstances.  This circumstance, the :term:`view name` represented by the
SP 108 ``name=`` predicate, is ``hello``.
10fd8f 109
CM 110 This presents an ambiguity that :app:`Pyramid` cannot resolve. Rather than
111 allowing the circumstance to go unreported, by default Pyramid raises a
112 :exc:`ConfigurationConflictError` error and prevents the application from
113 running.
114
115 Conflict detection happens for any kind of configuration: imperative
c9c3c4 116 configuration or configuration that results from the execution of a
CM 117 :term:`scan`.
c4503b 118
CM 119 .. _manually_resolving_conflicts:
d1432f 120
10fd8f 121 Manually Resolving Conflicts
CM 122 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
123
20ecd3 124 There are a number of ways to manually resolve conflicts: by changing
CM 125 registrations to not conflict, by strategically using
126 :meth:`pyramid.config.Configurator.commit`, or by using an "autocommitting"
127 configurator.
10fd8f 128
CM 129 The Right Thing
130 +++++++++++++++
131
132 The most correct way to resolve conflicts is to "do the needful": change your
133 configuration code to not have conflicting configuration statements.  The
134 details of how this is done depends entirely on the configuration statements
135 made by your application.  Use the detail provided in the
136 :exc:`ConfigurationConflictError` to track down the offending conflicts and
137 modify your configuration code accordingly.
138
139 If you're getting a conflict while trying to extend an existing application,
1205ec 140 and that application has a function which performs configuration like this one:
10fd8f 141
CM 142 .. code-block:: python
adf1fa 143     :linenos:
10fd8f 144
adf1fa 145     def add_routes(config):
SP 146         config.add_route(...)
10fd8f 147
1205ec 148 Don't call this function directly with ``config`` as an argument.  Instead, use
SP 149 :meth:`pyramid.config.Configurator.include`:
10fd8f 150
CM 151 .. code-block:: python
adf1fa 152     :linenos:
10fd8f 153
adf1fa 154     config.include(add_routes)
10fd8f 155
f5eb75 156 Using :meth:`~pyramid.config.Configurator.include` instead of calling the
1205ec 157 function directly provides a modicum of automated conflict resolution, with the
SP 158 configuration statements you define in the calling code overriding those of the
159 included function.
2033ee 160
SP 161 .. seealso::
162
163     See also :ref:`automatic_conflict_resolution` and
164     :ref:`including_configuration`.
10fd8f 165
CM 166 Using ``config.commit()``
167 +++++++++++++++++++++++++
168
169 You can manually commit a configuration by using the
1205ec 170 :meth:`~pyramid.config.Configurator.commit` method between configuration calls.
SP 171 For example, we prevent conflicts from occurring in the application we examined
172 previously as the result of adding a ``commit``.  Here's the application that
173 generates conflicts:
10fd8f 174
CM 175 .. code-block:: python
adf1fa 176     :linenos:
10fd8f 177
adf1fa 178     from wsgiref.simple_server import make_server
SP 179     from pyramid.config import Configurator
180     from pyramid.response import Response
10fd8f 181
adf1fa 182     def hello_world(request):
SP 183         return Response('Hello world!')
10fd8f 184
adf1fa 185     def goodbye_world(request):
SP 186         return Response('Goodbye world!')
10fd8f 187
adf1fa 188     if __name__ == '__main__':
SP 189         config = Configurator()
10fd8f 190
adf1fa 191         config.add_view(hello_world, name='hello')
10fd8f 192
adf1fa 193         # conflicting view configuration
SP 194         config.add_view(goodbye_world, name='hello')
10fd8f 195
adf1fa 196         app = config.make_wsgi_app()
SP 197         server = make_server('0.0.0.0', 8080, app)
198         server.serve_forever()
10fd8f 199
1205ec 200 We can prevent the two ``add_view`` calls from conflicting by issuing a call to
SP 201 :meth:`~pyramid.config.Configurator.commit` between them:
10fd8f 202
CM 203 .. code-block:: python
adf1fa 204     :linenos:
SP 205     :emphasize-lines: 16
10fd8f 206
adf1fa 207     from wsgiref.simple_server import make_server
SP 208     from pyramid.config import Configurator
209     from pyramid.response import Response
10fd8f 210
adf1fa 211     def hello_world(request):
SP 212         return Response('Hello world!')
10fd8f 213
adf1fa 214     def goodbye_world(request):
SP 215         return Response('Goodbye world!')
10fd8f 216
adf1fa 217     if __name__ == '__main__':
SP 218         config = Configurator()
10fd8f 219
adf1fa 220         config.add_view(hello_world, name='hello')
10fd8f 221
adf1fa 222         config.commit() # commit any pending configuration actions
10fd8f 223
adf1fa 224         # no-longer-conflicting view configuration
SP 225         config.add_view(goodbye_world, name='hello')
10fd8f 226
adf1fa 227         app = config.make_wsgi_app()
SP 228         server = make_server('0.0.0.0', 8080, app)
229         server.serve_forever()
10fd8f 230
CM 231 In the above example we've issued a call to
1205ec 232 :meth:`~pyramid.config.Configurator.commit` between the two ``add_view`` calls.
SP 233 :meth:`~pyramid.config.Configurator.commit` will execute any pending
10fd8f 234 configuration statements.
CM 235
236 Calling :meth:`~pyramid.config.Configurator.commit` is safe at any time.  It
1205ec 237 executes all pending configuration actions and leaves the configuration action
SP 238 list "clean".
10fd8f 239
1205ec 240 Note that :meth:`~pyramid.config.Configurator.commit` has no effect when you're
SP 241 using an *autocommitting* configurator (see :ref:`autocommitting_configurator`).
ef492d 242
10fd8f 243 .. _autocommitting_configurator:
CM 244
1205ec 245 Using an Autocommitting Configurator
10fd8f 246 ++++++++++++++++++++++++++++++++++++
CM 247
248 You can also use a heavy hammer to circumvent conflict detection by using a
249 configurator constructor parameter: ``autocommit=True``.  For example:
250
251 .. code-block:: python
adf1fa 252     :linenos:
10fd8f 253
adf1fa 254     from pyramid.config import Configurator
10fd8f 255
adf1fa 256     if __name__ == '__main__':
SP 257         config = Configurator(autocommit=True)
10fd8f 258
CM 259 When the ``autocommit`` parameter passed to the Configurator is ``True``,
ef492d 260 conflict detection (and :ref:`twophase_config`) is disabled.  Configuration
CM 261 statements will be executed immediately, and succeeding statements will
262 override preceding ones.
263
70acd2 264 :meth:`~pyramid.config.Configurator.commit` has no effect when ``autocommit``
ef492d 265 is ``True``.
10fd8f 266
CM 267 If you use a Configurator in code that performs unit testing, it's usually a
268 good idea to use an autocommitting Configurator, because you are usually
269 unconcerned about conflict detection or two-phase configuration in test code.
270
271 .. _automatic_conflict_resolution:
272
273 Automatic Conflict Resolution
274 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
275
70acd2 276 If your code uses the :meth:`~pyramid.config.Configurator.include` method to
10fd8f 277 include external configuration, some conflicts are automatically resolved.
CM 278 Configuration statements that are made as the result of an "include" will be
1205ec 279 overridden by configuration statements that happen within the caller of the
SP 280 "include" method.
10fd8f 281
1205ec 282 Automatic conflict resolution supports this goal.  If a user wants to reuse a
10fd8f 283 Pyramid application, and they want to customize the configuration of this
ef492d 284 application without hacking its code "from outside", they can "include" a
CM 285 configuration function from the package and override only some of its
286 configuration statements within the code that does the include.  No conflicts
1d03bb 287 will be generated by configuration statements within the code that does the
1205ec 288 including, even if configuration statements in the included code would conflict
SP 289 if it was moved "up" to the calling code.
10fd8f 290
CM 291 Methods Which Provide Conflict Detection
292 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
293
294 These are the methods of the configurator which provide conflict detection:
295
296 :meth:`~pyramid.config.Configurator.add_view`,
297 :meth:`~pyramid.config.Configurator.add_route`,
298 :meth:`~pyramid.config.Configurator.add_renderer`,
023c88 299 :meth:`~pyramid.config.Configurator.add_request_method`,
10fd8f 300 :meth:`~pyramid.config.Configurator.set_request_factory`,
20ecd3 301 :meth:`~pyramid.config.Configurator.set_session_factory`,
CM 302 :meth:`~pyramid.config.Configurator.set_root_factory`,
303 :meth:`~pyramid.config.Configurator.set_view_mapper`,
304 :meth:`~pyramid.config.Configurator.set_authentication_policy`,
305 :meth:`~pyramid.config.Configurator.set_authorization_policy`,
306 :meth:`~pyramid.config.Configurator.set_locale_negotiator`,
307 :meth:`~pyramid.config.Configurator.set_default_permission`,
308 :meth:`~pyramid.config.Configurator.add_traverser`,
309 :meth:`~pyramid.config.Configurator.add_resource_url_adapter`,
310 and :meth:`~pyramid.config.Configurator.add_response_adapter`.
10fd8f 311
1205ec 312 :meth:`~pyramid.config.Configurator.add_static_view` also indirectly provides
SP 313 conflict detection, because it's implemented in terms of the conflict-aware
314 ``add_route`` and ``add_view`` methods.
10fd8f 315
6ce1e0 316 .. index::
CM 317    pair: configuration; including from external sources
318
10fd8f 319 .. _including_configuration:
CM 320
321 Including Configuration from External Sources
322 ---------------------------------------------
323
1205ec 324 Some application programmers will factor their configuration code in such a way
SP 325 that it is easy to reuse and override configuration statements.  For example,
326 such a developer might factor out a function used to add routes to their
327 application:
10fd8f 328
CM 329 .. code-block:: python
adf1fa 330     :linenos:
10fd8f 331
adf1fa 332     def add_routes(config):
SP 333         config.add_route(...)
10fd8f 334
1205ec 335 Rather than calling this function directly with ``config`` as an argument,
SP 336 instead use :meth:`pyramid.config.Configurator.include`:
10fd8f 337
CM 338 .. code-block:: python
adf1fa 339     :linenos:
10fd8f 340
adf1fa 341     config.include(add_routes)
10fd8f 342
CM 343 Using ``include`` rather than calling the function directly will allow
344 :ref:`automatic_conflict_resolution` to work.
345
f5eb75 346 :meth:`~pyramid.config.Configurator.include` can also accept a :term:`module`
314283 347 as an argument:
CM 348
349 .. code-block:: python
adf1fa 350     :linenos:
314283 351
adf1fa 352     import myapp
314283 353
adf1fa 354     config.include(myapp)
314283 355
CM 356 For this to work properly, the ``myapp`` module must contain a callable with
357 the special name ``includeme``, which should perform configuration (like the
358 ``add_routes`` callable we showed above as an example).
359
f5eb75 360 :meth:`~pyramid.config.Configurator.include` can also accept a :term:`dotted
314283 361 Python name` to a function or a module.
CM 362
1205ec 363 .. note:: See :ref:`the_include_tag` for a declarative alternative to the
SP 364    :meth:`~pyramid.config.Configurator.include` method.
d1432f 365
10fd8f 366 .. _twophase_config:
CM 367
368 Two-Phase Configuration
369 -----------------------
370
1205ec 371 When a non-autocommitting :term:`Configurator` is used to do configuration (the
SP 372 default), configuration execution happens in two phases.  In the first phase,
373 "eager" configuration actions (actions that must happen before all others, such
374 as registering a renderer) are executed, and *discriminators* are computed for
375 each of the actions that depend on the result of the eager actions.  In the
376 second phase, the discriminators of all actions are compared to do conflict
377 detection.
10fd8f 378
CM 379 Due to this, for configuration methods that have no internal ordering
380 constraints, execution order of configuration method calls is not important.
381 For example, the relative ordering of
70acd2 382 :meth:`~pyramid.config.Configurator.add_view` and
CM 383 :meth:`~pyramid.config.Configurator.add_renderer` is unimportant when a
10fd8f 384 non-autocommitting configurator is used.  This code snippet:
CM 385
386 .. code-block:: python
adf1fa 387     :linenos:
10fd8f 388
adf1fa 389     config.add_view('some.view', renderer='path_to_custom/renderer.rn')
SP 390     config.add_renderer('.rn', SomeCustomRendererFactory)
10fd8f 391
CM 392 Has the same result as:
393
394 .. code-block:: python
adf1fa 395     :linenos:
10fd8f 396
adf1fa 397     config.add_renderer('.rn', SomeCustomRendererFactory)
SP 398     config.add_view('some.view', renderer='path_to_custom/renderer.rn')
10fd8f 399
CM 400 Even though the view statement depends on the registration of a custom
1205ec 401 renderer, due to two-phase configuration, the order in which the configuration
SP 402 statements are issued is not important.  ``add_view`` will be able to find the
403 ``.rn`` renderer even if ``add_renderer`` is called after ``add_view``.
10fd8f 404
CM 405 The same is untrue when you use an *autocommitting* configurator (see
406 :ref:`autocommitting_configurator`).  When an autocommitting configurator is
1205ec 407 used, two-phase configuration is disabled, and configuration statements must be
SP 408 ordered in dependency order.
10fd8f 409
c1b032 410 Some configuration methods, such as
70acd2 411 :meth:`~pyramid.config.Configurator.add_route` have internal ordering
6430c7 412 constraints: the routes they imply require relative ordering.  Such ordering
c1b032 413 constraints are not absolved by two-phase configuration.  Routes are still
CM 414 added in configuration execution order.
10fd8f 415
d501b7 416 More Information
CM 417 ----------------
418
34515f 419 For more information, see the article :ref:`A Whirlwind Tour of Advanced
SP 420 Configuration Tactics <cookbook:whirlwind-adv-conf>` in the Pyramid Community
421 Cookbook.