commit | author | age
|
5bf23f
|
1 |
import inspect |
9c8ec5
|
2 |
import itertools |
5bf23f
|
3 |
import logging |
412b4a
|
4 |
import operator |
5bf23f
|
5 |
import os |
79ef3d
|
6 |
import sys |
c15cbc
|
7 |
import threading |
5bf23f
|
8 |
import venusian |
ea824f
|
9 |
|
CM |
10 |
from webob.exc import WSGIHTTPException as WebobWSGIHTTPException |
|
11 |
|
3b5ccb
|
12 |
from pyramid.interfaces import ( |
CM |
13 |
IDebugLogger, |
|
14 |
IExceptionResponse, |
95f766
|
15 |
IPredicateList, |
568a02
|
16 |
PHASE0_CONFIG, |
95f766
|
17 |
PHASE1_CONFIG, |
568a02
|
18 |
PHASE2_CONFIG, |
MM |
19 |
PHASE3_CONFIG, |
0c29cf
|
20 |
) |
5bf23f
|
21 |
|
cfbbd6
|
22 |
from pyramid.asset import resolve_asset_spec |
ee117e
|
23 |
|
66da9b
|
24 |
from pyramid.authorization import ACLAuthorizationPolicy |
ee117e
|
25 |
|
0c29cf
|
26 |
from pyramid.compat import text_, reraise, string_types |
ee117e
|
27 |
|
5bf23f
|
28 |
from pyramid.events import ApplicationCreated |
ee117e
|
29 |
|
CM |
30 |
from pyramid.exceptions import ( |
|
31 |
ConfigurationConflictError, |
|
32 |
ConfigurationError, |
|
33 |
ConfigurationExecutionError, |
0c29cf
|
34 |
) |
ee117e
|
35 |
|
5bf23f
|
36 |
from pyramid.httpexceptions import default_exceptionresponse_view |
ee117e
|
37 |
|
0c29cf
|
38 |
from pyramid.path import caller_package, package_of |
ee117e
|
39 |
|
0c29cf
|
40 |
from pyramid.registry import Introspectable, Introspector, Registry, undefer |
ee117e
|
41 |
|
cfbbd6
|
42 |
from pyramid.router import Router |
ee117e
|
43 |
|
5bf23f
|
44 |
from pyramid.settings import aslist |
ee117e
|
45 |
|
5bf23f
|
46 |
from pyramid.threadlocal import manager |
ee117e
|
47 |
|
0c29cf
|
48 |
from pyramid.util import WeakOrderedSet, object_description |
52fde9
|
49 |
|
0c29cf
|
50 |
from pyramid.config.util import ActionInfo, PredicateList, action_method, not_ |
5bf23f
|
51 |
|
ea046b
|
52 |
from pyramid.config.adapters import AdaptersConfiguratorMixin |
CM |
53 |
from pyramid.config.assets import AssetsConfiguratorMixin |
|
54 |
from pyramid.config.factories import FactoriesConfiguratorMixin |
|
55 |
from pyramid.config.i18n import I18NConfiguratorMixin |
|
56 |
from pyramid.config.rendering import RenderingConfiguratorMixin |
|
57 |
from pyramid.config.routes import RoutesConfiguratorMixin |
|
58 |
from pyramid.config.security import SecurityConfiguratorMixin |
|
59 |
from pyramid.config.settings import SettingsConfiguratorMixin |
5bf23f
|
60 |
from pyramid.config.testing import TestingConfiguratorMixin |
CM |
61 |
from pyramid.config.tweens import TweensConfiguratorMixin |
|
62 |
from pyramid.config.views import ViewsConfiguratorMixin |
|
63 |
from pyramid.config.zca import ZCAConfiguratorMixin |
e6c2d2
|
64 |
|
56df90
|
65 |
from pyramid.path import DottedNameResolver |
CM |
66 |
|
e6c2d2
|
67 |
empty = text_('') |
e49638
|
68 |
_marker = object() |
5bf23f
|
69 |
|
52fde9
|
70 |
not_ = not_ # api |
32333e
|
71 |
|
568a02
|
72 |
PHASE0_CONFIG = PHASE0_CONFIG # api |
MM |
73 |
PHASE1_CONFIG = PHASE1_CONFIG # api |
|
74 |
PHASE2_CONFIG = PHASE2_CONFIG # api |
|
75 |
PHASE3_CONFIG = PHASE3_CONFIG # api |
32333e
|
76 |
|
0c29cf
|
77 |
|
5bf23f
|
78 |
class Configurator( |
CM |
79 |
TestingConfiguratorMixin, |
|
80 |
TweensConfiguratorMixin, |
|
81 |
SecurityConfiguratorMixin, |
|
82 |
ViewsConfiguratorMixin, |
|
83 |
RoutesConfiguratorMixin, |
|
84 |
ZCAConfiguratorMixin, |
|
85 |
I18NConfiguratorMixin, |
|
86 |
RenderingConfiguratorMixin, |
|
87 |
AssetsConfiguratorMixin, |
|
88 |
SettingsConfiguratorMixin, |
|
89 |
FactoriesConfiguratorMixin, |
|
90 |
AdaptersConfiguratorMixin, |
0c29cf
|
91 |
): |
5bf23f
|
92 |
""" |
CM |
93 |
A Configurator is used to configure a :app:`Pyramid` |
|
94 |
:term:`application registry`. |
|
95 |
|
134ef7
|
96 |
The Configurator lifecycle can be managed by using a context manager to |
MM |
97 |
automatically handle calling :meth:`pyramid.config.Configurator.begin` and |
|
98 |
:meth:`pyramid.config.Configurator.end` as well as |
|
99 |
:meth:`pyramid.config.Configurator.commit`. |
|
100 |
|
|
101 |
.. code-block:: python |
|
102 |
|
|
103 |
with Configurator(settings=settings) as config: |
|
104 |
config.add_route('home', '/') |
|
105 |
app = config.make_wsgi_app() |
|
106 |
|
0bf091
|
107 |
If the ``registry`` argument is not ``None``, it must |
adfc23
|
108 |
be an instance of the :class:`pyramid.registry.Registry` class |
CM |
109 |
representing the registry to configure. If ``registry`` is ``None``, the |
|
110 |
configurator will create a :class:`pyramid.registry.Registry` instance |
|
111 |
itself; it will also perform some default configuration that would not |
|
112 |
otherwise be done. After its construction, the configurator may be used |
|
113 |
to add further configuration to the registry. |
5bf23f
|
114 |
|
60270f
|
115 |
.. warning:: If ``registry`` is assigned the above-mentioned class |
TL |
116 |
instance, all other constructor arguments are ignored, |
|
117 |
with the exception of ``package``. |
5bf23f
|
118 |
|
adfc23
|
119 |
If the ``package`` argument is passed, it must be a reference to a Python |
CM |
120 |
:term:`package` (e.g. ``sys.modules['thepackage']``) or a :term:`dotted |
|
121 |
Python name` to the same. This value is used as a basis to convert |
|
122 |
relative paths passed to various configuration methods, such as methods |
|
123 |
which accept a ``renderer`` argument, into absolute paths. If ``None`` |
|
124 |
is passed (the default), the package is assumed to be the Python package |
|
125 |
in which the *caller* of the ``Configurator`` constructor lives. |
5bf23f
|
126 |
|
ae6c88
|
127 |
If the ``root_package`` is passed, it will propagate through the |
MM |
128 |
configuration hierarchy as a way for included packages to locate |
|
129 |
resources relative to the package in which the main ``Configurator`` was |
|
130 |
created. If ``None`` is passed (the default), the ``root_package`` will |
|
131 |
be derived from the ``package`` argument. The ``package`` attribute is |
|
132 |
always pointing at the package being included when using :meth:`.include`, |
|
133 |
whereas the ``root_package`` does not change. |
|
134 |
|
5bf23f
|
135 |
If the ``settings`` argument is passed, it should be a Python dictionary |
adfc23
|
136 |
representing the :term:`deployment settings` for this application. These |
CM |
137 |
are later retrievable using the |
|
138 |
:attr:`pyramid.registry.Registry.settings` attribute (aka |
|
139 |
``request.registry.settings``). |
5bf23f
|
140 |
|
CM |
141 |
If the ``root_factory`` argument is passed, it should be an object |
adfc23
|
142 |
representing the default :term:`root factory` for your application or a |
CM |
143 |
:term:`dotted Python name` to the same. If it is ``None``, a default |
|
144 |
root factory will be used. |
5bf23f
|
145 |
|
CM |
146 |
If ``authentication_policy`` is passed, it should be an instance |
|
147 |
of an :term:`authentication policy` or a :term:`dotted Python |
adfc23
|
148 |
name` to the same. |
5bf23f
|
149 |
|
CM |
150 |
If ``authorization_policy`` is passed, it should be an instance of |
|
151 |
an :term:`authorization policy` or a :term:`dotted Python name` to |
adfc23
|
152 |
the same. |
5bf23f
|
153 |
|
CM |
154 |
.. note:: A ``ConfigurationError`` will be raised when an |
|
155 |
authorization policy is supplied without also supplying an |
|
156 |
authentication policy (authorization requires authentication). |
|
157 |
|
3778e8
|
158 |
If ``renderers`` is ``None`` (the default), a default set of |
TL |
159 |
:term:`renderer` factories is used. Else, it should be a list of |
|
160 |
tuples representing a set of renderer factories which should be |
|
161 |
configured into this application, and each tuple representing a set of |
5bf23f
|
162 |
positional values that should be passed to |
3778e8
|
163 |
:meth:`pyramid.config.Configurator.add_renderer`. |
5bf23f
|
164 |
|
CM |
165 |
If ``debug_logger`` is not passed, a default debug logger that logs to a |
|
166 |
logger will be used (the logger name will be the package name of the |
|
167 |
*caller* of this configurator). If it is passed, it should be an |
|
168 |
instance of the :class:`logging.Logger` (PEP 282) standard library class |
|
169 |
or a Python logger name. The debug logger is used by :app:`Pyramid` |
|
170 |
itself to log warnings and authorization debugging information. |
|
171 |
|
|
172 |
If ``locale_negotiator`` is passed, it should be a :term:`locale |
|
173 |
negotiator` implementation or a :term:`dotted Python name` to |
|
174 |
same. See :ref:`custom_locale_negotiator`. |
|
175 |
|
|
176 |
If ``request_factory`` is passed, it should be a :term:`request |
adfc23
|
177 |
factory` implementation or a :term:`dotted Python name` to the same. |
5bf23f
|
178 |
See :ref:`changing_the_request_factory`. By default it is ``None``, |
CM |
179 |
which means use the default request factory. |
|
180 |
|
1236de
|
181 |
If ``response_factory`` is passed, it should be a :term:`response |
JA |
182 |
factory` implementation or a :term:`dotted Python name` to the same. |
|
183 |
See :ref:`changing_the_response_factory`. By default it is ``None``, |
|
184 |
which means use the default response factory. |
|
185 |
|
5bf23f
|
186 |
If ``default_permission`` is passed, it should be a |
CM |
187 |
:term:`permission` string to be used as the default permission for |
|
188 |
all view configuration registrations performed against this |
|
189 |
Configurator. An example of a permission string:``'view'``. |
|
190 |
Adding a default permission makes it unnecessary to protect each |
|
191 |
view configuration with an explicit permission, unless your |
|
192 |
application policy requires some exception for a particular view. |
|
193 |
By default, ``default_permission`` is ``None``, meaning that view |
|
194 |
configurations which do not explicitly declare a permission will |
|
195 |
always be executable by entirely anonymous users (any |
2033ee
|
196 |
authorization policy in effect is ignored). |
1236de
|
197 |
|
2033ee
|
198 |
.. seealso:: |
SP |
199 |
|
|
200 |
See also :ref:`setting_a_default_permission`. |
5bf23f
|
201 |
|
CM |
202 |
If ``session_factory`` is passed, it should be an object which |
|
203 |
implements the :term:`session factory` interface. If a nondefault |
|
204 |
value is passed, the ``session_factory`` will be used to create a |
|
205 |
session object when ``request.session`` is accessed. Note that |
|
206 |
the same outcome can be achieved by calling |
|
207 |
:meth:`pyramid.config.Configurator.set_session_factory`. By |
|
208 |
default, this argument is ``None``, indicating that no session |
|
209 |
factory will be configured (and thus accessing ``request.session`` |
|
210 |
will throw an error) unless ``set_session_factory`` is called later |
|
211 |
during configuration. |
|
212 |
|
|
213 |
If ``autocommit`` is ``True``, every method called on the configurator |
|
214 |
will cause an immediate action, and no configuration conflict detection |
|
215 |
will be used. If ``autocommit`` is ``False``, most methods of the |
|
216 |
configurator will defer their action until |
|
217 |
:meth:`pyramid.config.Configurator.commit` is called. When |
|
218 |
:meth:`pyramid.config.Configurator.commit` is called, the actions implied |
|
219 |
by the called methods will be checked for configuration conflicts unless |
5c351a
|
220 |
``autocommit`` is ``True``. If a conflict is detected, a |
5bf23f
|
221 |
``ConfigurationConflictError`` will be raised. Calling |
CM |
222 |
:meth:`pyramid.config.Configurator.make_wsgi_app` always implies a final |
|
223 |
commit. |
|
224 |
|
|
225 |
If ``default_view_mapper`` is passed, it will be used as the default |
|
226 |
:term:`view mapper` factory for view configurations that don't otherwise |
a3f67c
|
227 |
specify one (see :class:`pyramid.interfaces.IViewMapperFactory`). If |
d0e652
|
228 |
``default_view_mapper`` is not passed, a superdefault view mapper will be |
5bf23f
|
229 |
used. |
CM |
230 |
|
|
231 |
If ``exceptionresponse_view`` is passed, it must be a :term:`view |
|
232 |
callable` or ``None``. If it is a view callable, it will be used as an |
|
233 |
exception view callable when an :term:`exception response` is raised. If |
|
234 |
``exceptionresponse_view`` is ``None``, no exception response view will |
|
235 |
be registered, and all raised exception responses will be bubbled up to |
|
236 |
Pyramid's caller. By |
|
237 |
default, the ``pyramid.httpexceptions.default_exceptionresponse_view`` |
0b23b3
|
238 |
function is used as the ``exceptionresponse_view``. |
5bf23f
|
239 |
|
CM |
240 |
If ``route_prefix`` is passed, all routes added with |
|
241 |
:meth:`pyramid.config.Configurator.add_route` will have the specified path |
0b23b3
|
242 |
prepended to their pattern. |
5bf23f
|
243 |
|
844ed9
|
244 |
If ``introspection`` is passed, it must be a boolean value. If it's |
043ccd
|
245 |
``True``, introspection values during actions will be kept for use |
844ed9
|
246 |
for tools like the debug toolbar. If it's ``False``, introspection |
CM |
247 |
values provided by registrations will be ignored. By default, it is |
0b23b3
|
248 |
``True``. |
TL |
249 |
|
|
250 |
.. versionadded:: 1.1 |
|
251 |
The ``exceptionresponse_view`` argument. |
|
252 |
|
|
253 |
.. versionadded:: 1.2 |
|
254 |
The ``route_prefix`` argument. |
|
255 |
|
|
256 |
.. versionadded:: 1.3 |
|
257 |
The ``introspection`` argument. |
ae6c88
|
258 |
|
MM |
259 |
.. versionadded:: 1.6 |
|
260 |
The ``root_package`` argument. |
1236de
|
261 |
The ``response_factory`` argument. |
134ef7
|
262 |
|
MM |
263 |
.. versionadded:: 1.9 |
|
264 |
The ability to use the configurator as a context manager with the |
|
265 |
``with``-statement to make threadlocal configuration available for |
|
266 |
further configuration with an implicit commit. |
e49638
|
267 |
""" |
0c29cf
|
268 |
|
MM |
269 |
manager = manager # for testing injection |
|
270 |
venusian = venusian # for testing injection |
5bf23f
|
271 |
_ainfo = None |
b86fa9
|
272 |
basepath = None |
CM |
273 |
includepath = () |
|
274 |
info = '' |
8b6f09
|
275 |
object_description = staticmethod(object_description) |
e49638
|
276 |
introspectable = Introspectable |
5ad401
|
277 |
inspect = inspect |
5bf23f
|
278 |
|
0c29cf
|
279 |
def __init__( |
MM |
280 |
self, |
|
281 |
registry=None, |
|
282 |
package=None, |
|
283 |
settings=None, |
|
284 |
root_factory=None, |
|
285 |
authentication_policy=None, |
|
286 |
authorization_policy=None, |
|
287 |
renderers=None, |
|
288 |
debug_logger=None, |
|
289 |
locale_negotiator=None, |
|
290 |
request_factory=None, |
|
291 |
response_factory=None, |
|
292 |
default_permission=None, |
|
293 |
session_factory=None, |
|
294 |
default_view_mapper=None, |
|
295 |
autocommit=False, |
|
296 |
exceptionresponse_view=default_exceptionresponse_view, |
|
297 |
route_prefix=None, |
|
298 |
introspection=True, |
|
299 |
root_package=None, |
|
300 |
): |
5bf23f
|
301 |
if package is None: |
CM |
302 |
package = caller_package() |
ae6c88
|
303 |
if root_package is None: |
MM |
304 |
root_package = package |
5bf23f
|
305 |
name_resolver = DottedNameResolver(package) |
CM |
306 |
self.name_resolver = name_resolver |
078859
|
307 |
self.package_name = name_resolver.get_package_name() |
CM |
308 |
self.package = name_resolver.get_package() |
ae6c88
|
309 |
self.root_package = root_package |
5bf23f
|
310 |
self.registry = registry |
CM |
311 |
self.autocommit = autocommit |
|
312 |
self.route_prefix = route_prefix |
844ed9
|
313 |
self.introspection = introspection |
5bf23f
|
314 |
if registry is None: |
CM |
315 |
registry = Registry(self.package_name) |
|
316 |
self.registry = registry |
|
317 |
self.setup_registry( |
|
318 |
settings=settings, |
|
319 |
root_factory=root_factory, |
|
320 |
authentication_policy=authentication_policy, |
|
321 |
authorization_policy=authorization_policy, |
|
322 |
renderers=renderers, |
|
323 |
debug_logger=debug_logger, |
|
324 |
locale_negotiator=locale_negotiator, |
|
325 |
request_factory=request_factory, |
1236de
|
326 |
response_factory=response_factory, |
5bf23f
|
327 |
default_permission=default_permission, |
CM |
328 |
session_factory=session_factory, |
|
329 |
default_view_mapper=default_view_mapper, |
|
330 |
exceptionresponse_view=exceptionresponse_view, |
0c29cf
|
331 |
) |
5bf23f
|
332 |
|
0c29cf
|
333 |
def setup_registry( |
MM |
334 |
self, |
|
335 |
settings=None, |
|
336 |
root_factory=None, |
|
337 |
authentication_policy=None, |
|
338 |
authorization_policy=None, |
|
339 |
renderers=None, |
|
340 |
debug_logger=None, |
|
341 |
locale_negotiator=None, |
|
342 |
request_factory=None, |
|
343 |
response_factory=None, |
|
344 |
default_permission=None, |
|
345 |
session_factory=None, |
|
346 |
default_view_mapper=None, |
|
347 |
exceptionresponse_view=default_exceptionresponse_view, |
|
348 |
): |
5bf23f
|
349 |
""" When you pass a non-``None`` ``registry`` argument to the |
adfc23
|
350 |
:term:`Configurator` constructor, no initial setup is performed |
5bf23f
|
351 |
against the registry. This is because the registry you pass in may |
CM |
352 |
have already been initialized for use under :app:`Pyramid` via a |
|
353 |
different configurator. However, in some circumstances (such as when |
adfc23
|
354 |
you want to use a global registry instead of a registry created as a |
CM |
355 |
result of the Configurator constructor), or when you want to reset |
|
356 |
the initial setup of a registry, you *do* want to explicitly |
|
357 |
initialize the registry associated with a Configurator for use under |
|
358 |
:app:`Pyramid`. Use ``setup_registry`` to do this initialization. |
5bf23f
|
359 |
|
CM |
360 |
``setup_registry`` configures settings, a root factory, security |
|
361 |
policies, renderers, a debug logger, a locale negotiator, and various |
|
362 |
other settings using the configurator's current registry, as per the |
|
363 |
descriptions in the Configurator constructor.""" |
c1624c
|
364 |
|
5bf23f
|
365 |
registry = self.registry |
c1624c
|
366 |
|
5bf23f
|
367 |
self._fix_registry() |
e49638
|
368 |
|
5bf23f
|
369 |
self._set_settings(settings) |
CM |
370 |
|
e6c2d2
|
371 |
if isinstance(debug_logger, string_types): |
c1624c
|
372 |
debug_logger = logging.getLogger(debug_logger) |
CM |
373 |
|
5bf23f
|
374 |
if debug_logger is None: |
CM |
375 |
debug_logger = logging.getLogger(self.package_name) |
012b97
|
376 |
|
5bf23f
|
377 |
registry.registerUtility(debug_logger, IDebugLogger) |
CM |
378 |
|
27190e
|
379 |
self.add_default_response_adapters() |
b7e92d
|
380 |
self.add_default_renderers() |
121f45
|
381 |
self.add_default_accept_view_order() |
a00621
|
382 |
self.add_default_view_predicates() |
ceb1f2
|
383 |
self.add_default_view_derivers() |
9c8ec5
|
384 |
self.add_default_route_predicates() |
35b0e3
|
385 |
self.add_default_tweens() |
7c0f09
|
386 |
self.add_default_security() |
a00621
|
387 |
|
5bf23f
|
388 |
if exceptionresponse_view is not None: |
CM |
389 |
exceptionresponse_view = self.maybe_dotted(exceptionresponse_view) |
|
390 |
self.add_view(exceptionresponse_view, context=IExceptionResponse) |
0c29cf
|
391 |
self.add_view( |
MM |
392 |
exceptionresponse_view, context=WebobWSGIHTTPException |
|
393 |
) |
5bf23f
|
394 |
|
0ded4e
|
395 |
# commit below because: |
CM |
396 |
# |
|
397 |
# - the default exceptionresponse_view requires the superdefault view |
a54bc1
|
398 |
# mapper, so we need to configure it before adding |
MM |
399 |
# default_view_mapper |
0ded4e
|
400 |
# |
257f2c
|
401 |
# - superdefault renderers should be overrideable without requiring |
0ded4e
|
402 |
# the user to commit before calling config.add_renderer |
ea824f
|
403 |
|
CM |
404 |
self.commit() |
|
405 |
|
27190e
|
406 |
# self.commit() should not be called within this method after this |
CM |
407 |
# point because the following registrations should be treated as |
|
408 |
# analogues of methods called by the user after configurator |
|
409 |
# construction. Rationale: user-supplied implementations should be |
|
410 |
# preferred rather than add-on author implementations with the help of |
|
411 |
# automatic conflict resolution. |
ea824f
|
412 |
|
66da9b
|
413 |
if authentication_policy and not authorization_policy: |
0c29cf
|
414 |
authorization_policy = ACLAuthorizationPolicy() # default |
66da9b
|
415 |
|
ea824f
|
416 |
if authorization_policy: |
CM |
417 |
self.set_authorization_policy(authorization_policy) |
637bda
|
418 |
|
eb2fee
|
419 |
if authentication_policy: |
CM |
420 |
self.set_authentication_policy(authentication_policy) |
|
421 |
|
|
422 |
if default_view_mapper is not None: |
|
423 |
self.set_view_mapper(default_view_mapper) |
f67ba4
|
424 |
|
637bda
|
425 |
if renderers: |
CM |
426 |
for name, renderer in renderers: |
|
427 |
self.add_renderer(name, renderer) |
cfbbd6
|
428 |
|
2a818a
|
429 |
if root_factory is not None: |
CM |
430 |
self.set_root_factory(root_factory) |
ea824f
|
431 |
|
5bf23f
|
432 |
if locale_negotiator: |
ea824f
|
433 |
self.set_locale_negotiator(locale_negotiator) |
5bf23f
|
434 |
|
CM |
435 |
if request_factory: |
|
436 |
self.set_request_factory(request_factory) |
|
437 |
|
1236de
|
438 |
if response_factory: |
JA |
439 |
self.set_response_factory(response_factory) |
|
440 |
|
5bf23f
|
441 |
if default_permission: |
CM |
442 |
self.set_default_permission(default_permission) |
|
443 |
|
|
444 |
if session_factory is not None: |
|
445 |
self.set_session_factory(session_factory) |
|
446 |
|
25c64c
|
447 |
tweens = aslist(registry.settings.get('pyramid.tweens', [])) |
5bf23f
|
448 |
for factory in tweens: |
CM |
449 |
self._add_tween(factory, explicit=True) |
012b97
|
450 |
|
ea824f
|
451 |
includes = aslist(registry.settings.get('pyramid.includes', [])) |
CM |
452 |
for inc in includes: |
|
453 |
self.include(inc) |
|
454 |
|
5bf23f
|
455 |
def _make_spec(self, path_or_spec): |
79ef3d
|
456 |
package, filename = resolve_asset_spec(path_or_spec, self.package_name) |
5bf23f
|
457 |
if package is None: |
0c29cf
|
458 |
return filename # absolute filename |
5bf23f
|
459 |
return '%s:%s' % (package, filename) |
CM |
460 |
|
|
461 |
def _fix_registry(self): |
|
462 |
""" Fix up a ZCA component registry that is not a |
|
463 |
pyramid.registry.Registry by adding analogues of ``has_listeners``, |
|
464 |
``notify``, ``queryAdapterOrSelf``, and ``registerSelfAdapter`` |
|
465 |
through monkey-patching.""" |
|
466 |
|
|
467 |
_registry = self.registry |
|
468 |
|
|
469 |
if not hasattr(_registry, 'notify'): |
0c29cf
|
470 |
|
5bf23f
|
471 |
def notify(*events): |
0c29cf
|
472 |
[_ for _ in _registry.subscribers(events, None)] |
MM |
473 |
|
5bf23f
|
474 |
_registry.notify = notify |
CM |
475 |
|
|
476 |
if not hasattr(_registry, 'has_listeners'): |
|
477 |
_registry.has_listeners = True |
|
478 |
|
|
479 |
if not hasattr(_registry, 'queryAdapterOrSelf'): |
0c29cf
|
480 |
|
5bf23f
|
481 |
def queryAdapterOrSelf(object, interface, default=None): |
CM |
482 |
if not interface.providedBy(object): |
0c29cf
|
483 |
return _registry.queryAdapter( |
MM |
484 |
object, interface, default=default |
|
485 |
) |
5bf23f
|
486 |
return object |
0c29cf
|
487 |
|
5bf23f
|
488 |
_registry.queryAdapterOrSelf = queryAdapterOrSelf |
CM |
489 |
|
|
490 |
if not hasattr(_registry, 'registerSelfAdapter'): |
0c29cf
|
491 |
|
MM |
492 |
def registerSelfAdapter( |
|
493 |
required=None, |
|
494 |
provided=None, |
|
495 |
name=empty, |
|
496 |
info=empty, |
|
497 |
event=True, |
|
498 |
): |
|
499 |
return _registry.registerAdapter( |
|
500 |
lambda x: x, |
|
501 |
required=required, |
|
502 |
provided=provided, |
|
503 |
name=name, |
|
504 |
info=info, |
|
505 |
event=event, |
|
506 |
) |
|
507 |
|
5bf23f
|
508 |
_registry.registerSelfAdapter = registerSelfAdapter |
CM |
509 |
|
c15cbc
|
510 |
if not hasattr(_registry, '_lock'): |
CM |
511 |
_registry._lock = threading.Lock() |
|
512 |
|
|
513 |
if not hasattr(_registry, '_clear_view_lookup_cache'): |
0c29cf
|
514 |
|
c15cbc
|
515 |
def _clear_view_lookup_cache(): |
CM |
516 |
_registry._view_lookup_cache = {} |
|
517 |
|
0c29cf
|
518 |
_registry._clear_view_lookup_cache = _clear_view_lookup_cache |
c15cbc
|
519 |
|
5bf23f
|
520 |
# API |
1236de
|
521 |
|
e49638
|
522 |
def _get_introspector(self): |
CM |
523 |
introspector = getattr(self.registry, 'introspector', _marker) |
|
524 |
if introspector is _marker: |
|
525 |
introspector = Introspector() |
|
526 |
self._set_introspector(introspector) |
|
527 |
return introspector |
|
528 |
|
|
529 |
def _set_introspector(self, introspector): |
|
530 |
self.registry.introspector = introspector |
|
531 |
|
|
532 |
def _del_introspector(self): |
|
533 |
del self.registry.introspector |
|
534 |
|
078859
|
535 |
introspector = property( |
CM |
536 |
_get_introspector, _set_introspector, _del_introspector |
0c29cf
|
537 |
) |
5bf23f
|
538 |
|
405213
|
539 |
def get_predlist(self, name): |
95f766
|
540 |
predlist = self.registry.queryUtility(IPredicateList, name=name) |
CM |
541 |
if predlist is None: |
|
542 |
predlist = PredicateList() |
|
543 |
self.registry.registerUtility(predlist, IPredicateList, name=name) |
|
544 |
return predlist |
|
545 |
|
0c29cf
|
546 |
def _add_predicate( |
MM |
547 |
self, type, name, factory, weighs_more_than=None, weighs_less_than=None |
|
548 |
): |
06b839
|
549 |
factory = self.maybe_dotted(factory) |
c2c589
|
550 |
discriminator = ('%s option' % type, name) |
95f766
|
551 |
intr = self.introspectable( |
CM |
552 |
'%s predicates' % type, |
|
553 |
discriminator, |
|
554 |
'%s predicate named %s' % (type, name), |
0c29cf
|
555 |
'%s predicate' % type, |
MM |
556 |
) |
95f766
|
557 |
intr['name'] = name |
06b839
|
558 |
intr['factory'] = factory |
95f766
|
559 |
intr['weighs_more_than'] = weighs_more_than |
CM |
560 |
intr['weighs_less_than'] = weighs_less_than |
0c29cf
|
561 |
|
95f766
|
562 |
def register(): |
405213
|
563 |
predlist = self.get_predlist(type) |
0c29cf
|
564 |
predlist.add( |
MM |
565 |
name, |
|
566 |
factory, |
|
567 |
weighs_more_than=weighs_more_than, |
|
568 |
weighs_less_than=weighs_less_than, |
|
569 |
) |
|
570 |
|
|
571 |
self.action( |
|
572 |
discriminator, |
|
573 |
register, |
|
574 |
introspectables=(intr,), |
|
575 |
order=PHASE1_CONFIG, |
|
576 |
) # must be registered early |
95f766
|
577 |
|
3b5ccb
|
578 |
@property |
CM |
579 |
def action_info(self): |
0c29cf
|
580 |
info = self.info # usually a ZCML action (ParserInfo) if self.info |
3b5ccb
|
581 |
if not info: |
CM |
582 |
# Try to provide more accurate info for conflict reports |
|
583 |
if self._ainfo: |
|
584 |
info = self._ainfo[0] |
|
585 |
else: |
549cf7
|
586 |
info = ActionInfo(None, 0, '', '') |
3b5ccb
|
587 |
return info |
CM |
588 |
|
0c29cf
|
589 |
def action( |
MM |
590 |
self, |
|
591 |
discriminator, |
|
592 |
callable=None, |
|
593 |
args=(), |
|
594 |
kw=None, |
|
595 |
order=0, |
|
596 |
introspectables=(), |
|
597 |
**extra |
|
598 |
): |
5bf23f
|
599 |
""" Register an action which will be executed when |
adfc23
|
600 |
:meth:`pyramid.config.Configurator.commit` is called (or executed |
5bf23f
|
601 |
immediately if ``autocommit`` is ``True``). |
CM |
602 |
|
|
603 |
.. warning:: This method is typically only used by :app:`Pyramid` |
|
604 |
framework extension authors, not by :app:`Pyramid` application |
|
605 |
developers. |
|
606 |
|
|
607 |
The ``discriminator`` uniquely identifies the action. It must be |
|
608 |
given, but it can be ``None``, to indicate that the action never |
|
609 |
conflicts. It must be a hashable value. |
|
610 |
|
7d109d
|
611 |
The ``callable`` is a callable object which performs the task |
CM |
612 |
associated with the action when the action is executed. It is |
|
613 |
optional. |
5bf23f
|
614 |
|
7d109d
|
615 |
``args`` and ``kw`` are tuple and dict objects respectively, which |
CM |
616 |
are passed to ``callable`` when this action is executed. Both are |
|
617 |
optional. |
|
618 |
|
|
619 |
``order`` is a grouping mechanism; an action with a lower order will |
|
620 |
be executed before an action with a higher order (has no effect when |
|
621 |
autocommit is ``True``). |
|
622 |
|
|
623 |
``introspectables`` is a sequence of :term:`introspectable` objects |
|
624 |
(or the empty sequence if no introspectable objects are associated |
844ed9
|
625 |
with this action). If this configurator's ``introspection`` |
CM |
626 |
attribute is ``False``, these introspectables will be ignored. |
7d109d
|
627 |
|
CM |
628 |
``extra`` provides a facility for inserting extra keys and values |
|
629 |
into an action dictionary. |
5bf23f
|
630 |
""" |
22e0aa
|
631 |
# catch nonhashable discriminators here; most unit tests use |
CM |
632 |
# autocommit=False, which won't catch unhashable discriminators |
0c29cf
|
633 |
assert hash(discriminator) |
22e0aa
|
634 |
|
5bf23f
|
635 |
if kw is None: |
CM |
636 |
kw = {} |
|
637 |
|
b86fa9
|
638 |
autocommit = self.autocommit |
3b5ccb
|
639 |
action_info = self.action_info |
844ed9
|
640 |
|
CM |
641 |
if not self.introspection: |
|
642 |
# if we're not introspecting, ignore any introspectables passed |
|
643 |
# to us |
|
644 |
introspectables = () |
5bf23f
|
645 |
|
CM |
646 |
if autocommit: |
7401b8
|
647 |
# callables can depend on the side effects of resolving a |
CM |
648 |
# deferred discriminator |
804eb0
|
649 |
self.begin() |
MM |
650 |
try: |
|
651 |
undefer(discriminator) |
|
652 |
if callable is not None: |
|
653 |
callable(*args, **kw) |
|
654 |
for introspectable in introspectables: |
|
655 |
introspectable.register(self.introspector, action_info) |
|
656 |
finally: |
|
657 |
self.end() |
b86fa9
|
658 |
|
5bf23f
|
659 |
else: |
7d109d
|
660 |
action = extra |
CM |
661 |
action.update( |
|
662 |
dict( |
|
663 |
discriminator=discriminator, |
|
664 |
callable=callable, |
|
665 |
args=args, |
|
666 |
kw=kw, |
|
667 |
order=order, |
|
668 |
info=action_info, |
|
669 |
includepath=self.includepath, |
|
670 |
introspectables=introspectables, |
b86fa9
|
671 |
) |
0c29cf
|
672 |
) |
7d109d
|
673 |
self.action_state.action(**action) |
b86fa9
|
674 |
|
CM |
675 |
def _get_action_state(self): |
|
676 |
registry = self.registry |
|
677 |
try: |
|
678 |
state = registry.action_state |
|
679 |
except AttributeError: |
|
680 |
state = ActionState() |
|
681 |
registry.action_state = state |
|
682 |
return state |
|
683 |
|
|
684 |
def _set_action_state(self, state): |
|
685 |
self.registry.action_state = state |
|
686 |
|
|
687 |
action_state = property(_get_action_state, _set_action_state) |
5bf23f
|
688 |
|
0c29cf
|
689 |
_ctx = action_state # bw compat |
3e4f69
|
690 |
|
5bf23f
|
691 |
def commit(self): |
134ef7
|
692 |
""" |
MM |
693 |
Commit any pending configuration actions. If a configuration |
adfc23
|
694 |
conflict is detected in the pending configuration actions, this method |
5bf23f
|
695 |
will raise a :exc:`ConfigurationConflictError`; within the traceback |
CM |
696 |
of this error will be information about the source of the conflict, |
|
697 |
usually including file names and line numbers of the cause of the |
134ef7
|
698 |
configuration conflicts. |
MM |
699 |
|
|
700 |
.. warning:: |
|
701 |
You should think very carefully before manually invoking |
|
702 |
``commit()``. Especially not as part of any reusable configuration |
|
703 |
methods. Normally it should only be done by an application author at |
|
704 |
the end of configuration in order to override certain aspects of an |
|
705 |
addon. |
|
706 |
|
|
707 |
""" |
0c1c58
|
708 |
self.begin() |
MM |
709 |
try: |
|
710 |
self.action_state.execute_actions(introspector=self.introspector) |
|
711 |
finally: |
|
712 |
self.end() |
0c29cf
|
713 |
self.action_state = ActionState() # old actions have been processed |
5bf23f
|
714 |
|
CM |
715 |
def include(self, callable, route_prefix=None): |
f7c173
|
716 |
"""Include a configuration callable, to support imperative |
5bf23f
|
717 |
application extensibility. |
CM |
718 |
|
|
719 |
.. warning:: In versions of :app:`Pyramid` prior to 1.2, this |
|
720 |
function accepted ``*callables``, but this has been changed |
|
721 |
to support only a single callable. |
|
722 |
|
|
723 |
A configuration callable should be a callable that accepts a single |
|
724 |
argument named ``config``, which will be an instance of a |
2e95ac
|
725 |
:term:`Configurator`. However, be warned that it will not be the same |
TL |
726 |
configurator instance on which you call this method. The |
e09e08
|
727 |
code which runs as a result of calling the callable should invoke |
5bf23f
|
728 |
methods on the configurator passed to it which add configuration |
CM |
729 |
state. The return value of a callable will be ignored. |
|
730 |
|
|
731 |
Values allowed to be presented via the ``callable`` argument to |
|
732 |
this method: any callable Python object or any :term:`dotted Python |
|
733 |
name` which resolves to a callable Python object. It may also be a |
|
734 |
Python :term:`module`, in which case, the module will be searched for |
|
735 |
a callable named ``includeme``, which will be treated as the |
|
736 |
configuration callable. |
b86fa9
|
737 |
|
5bf23f
|
738 |
For example, if the ``includeme`` function below lives in a module |
CM |
739 |
named ``myapp.myconfig``: |
|
740 |
|
|
741 |
.. code-block:: python |
|
742 |
:linenos: |
|
743 |
|
|
744 |
# myapp.myconfig module |
|
745 |
|
|
746 |
def my_view(request): |
|
747 |
from pyramid.response import Response |
|
748 |
return Response('OK') |
|
749 |
|
|
750 |
def includeme(config): |
|
751 |
config.add_view(my_view) |
|
752 |
|
026ac8
|
753 |
You might cause it to be included within your Pyramid application like |
5bf23f
|
754 |
so: |
CM |
755 |
|
|
756 |
.. code-block:: python |
|
757 |
:linenos: |
|
758 |
|
|
759 |
from pyramid.config import Configurator |
|
760 |
|
|
761 |
def main(global_config, **settings): |
|
762 |
config = Configurator() |
|
763 |
config.include('myapp.myconfig.includeme') |
|
764 |
|
|
765 |
Because the function is named ``includeme``, the function name can |
|
766 |
also be omitted from the dotted name reference: |
|
767 |
|
|
768 |
.. code-block:: python |
|
769 |
:linenos: |
|
770 |
|
|
771 |
from pyramid.config import Configurator |
|
772 |
|
|
773 |
def main(global_config, **settings): |
|
774 |
config = Configurator() |
|
775 |
config.include('myapp.myconfig') |
|
776 |
|
|
777 |
Included configuration statements will be overridden by local |
|
778 |
configuration statements if an included callable causes a |
|
779 |
configuration conflict by registering something with the same |
|
780 |
configuration parameters. |
|
781 |
|
|
782 |
If the ``route_prefix`` is supplied, it must be a string. Any calls |
|
783 |
to :meth:`pyramid.config.Configurator.add_route` within the included |
|
784 |
callable will have their pattern prefixed with the value of |
|
785 |
``route_prefix``. This can be used to help mount a set of routes at a |
2e95ac
|
786 |
different location than the included callable's author intended, while |
5bf23f
|
787 |
still maintaining the same route names. For example: |
012b97
|
788 |
|
5bf23f
|
789 |
.. code-block:: python |
CM |
790 |
:linenos: |
|
791 |
|
|
792 |
from pyramid.config import Configurator |
|
793 |
|
|
794 |
def included(config): |
|
795 |
config.add_route('show_users', '/show') |
012b97
|
796 |
|
5bf23f
|
797 |
def main(global_config, **settings): |
CM |
798 |
config = Configurator() |
|
799 |
config.include(included, route_prefix='/users') |
|
800 |
|
|
801 |
In the above configuration, the ``show_users`` route will have an |
|
802 |
effective route pattern of ``/users/show``, instead of ``/show`` |
|
803 |
because the ``route_prefix`` argument will be prepended to the |
|
804 |
pattern. |
|
805 |
|
0b23b3
|
806 |
.. versionadded:: 1.2 |
TL |
807 |
The ``route_prefix`` parameter. |
|
808 |
|
f5ff7e
|
809 |
.. versionchanged:: 1.9 |
MM |
810 |
The included function is wrapped with a call to |
|
811 |
:meth:`pyramid.config.Configurator.begin` and |
|
812 |
:meth:`pyramid.config.Configurator.end` while it is executed. |
|
813 |
|
5bf23f
|
814 |
""" |
7a7c8c
|
815 |
# """ <-- emacs |
5bf23f
|
816 |
|
b86fa9
|
817 |
action_state = self.action_state |
5bf23f
|
818 |
|
CM |
819 |
c = self.maybe_dotted(callable) |
5ad401
|
820 |
module = self.inspect.getmodule(c) |
5bf23f
|
821 |
if module is c: |
f8bfc6
|
822 |
try: |
CM |
823 |
c = getattr(module, 'includeme') |
|
824 |
except AttributeError: |
|
825 |
raise ConfigurationError( |
0c29cf
|
826 |
"module %r has no attribute 'includeme'" |
MM |
827 |
% (module.__name__) |
|
828 |
) |
|
829 |
|
5bf23f
|
830 |
spec = module.__name__ + ':' + c.__name__ |
5ad401
|
831 |
sourcefile = self.inspect.getsourcefile(c) |
CM |
832 |
|
|
833 |
if sourcefile is None: |
|
834 |
raise ConfigurationError( |
|
835 |
'No source file for module %r (.py file must exist, ' |
0c29cf
|
836 |
'refusing to use orphan .pyc or .pyo file).' % module.__name__ |
MM |
837 |
) |
5bf23f
|
838 |
|
b86fa9
|
839 |
if action_state.processSpec(spec): |
efd61e
|
840 |
with self.route_prefix_context(route_prefix): |
HS |
841 |
configurator = self.__class__( |
|
842 |
registry=self.registry, |
|
843 |
package=package_of(module), |
|
844 |
root_package=self.root_package, |
|
845 |
autocommit=self.autocommit, |
|
846 |
route_prefix=self.route_prefix, |
0c29cf
|
847 |
) |
efd61e
|
848 |
configurator.basepath = os.path.dirname(sourcefile) |
HS |
849 |
configurator.includepath = self.includepath + (spec,) |
|
850 |
|
|
851 |
self.begin() |
|
852 |
try: |
|
853 |
c(configurator) |
|
854 |
finally: |
|
855 |
self.end() |
012b97
|
856 |
|
5bf23f
|
857 |
def add_directive(self, name, directive, action_wrap=True): |
CM |
858 |
""" |
|
859 |
Add a directive method to the configurator. |
|
860 |
|
|
861 |
.. warning:: This method is typically only used by :app:`Pyramid` |
|
862 |
framework extension authors, not by :app:`Pyramid` application |
|
863 |
developers. |
|
864 |
|
|
865 |
Framework extenders can add directive methods to a configurator by |
|
866 |
instructing their users to call ``config.add_directive('somename', |
|
867 |
'some.callable')``. This will make ``some.callable`` accessible as |
|
868 |
``config.somename``. ``some.callable`` should be a function which |
|
869 |
accepts ``config`` as a first argument, and arbitrary positional and |
|
870 |
keyword arguments following. It should use config.action as |
|
871 |
necessary to perform actions. Directive methods can then be invoked |
|
872 |
like 'built-in' directives such as ``add_view``, ``add_route``, etc. |
|
873 |
|
|
874 |
The ``action_wrap`` argument should be ``True`` for directives which |
|
875 |
perform ``config.action`` with potentially conflicting |
|
876 |
discriminators. ``action_wrap`` will cause the directive to be |
|
877 |
wrapped in a decorator which provides more accurate conflict |
|
878 |
cause information. |
012b97
|
879 |
|
5bf23f
|
880 |
``add_directive`` does not participate in conflict detection, and |
CM |
881 |
later calls to ``add_directive`` will override earlier calls. |
|
882 |
""" |
|
883 |
c = self.maybe_dotted(directive) |
|
884 |
if not hasattr(self.registry, '_directives'): |
|
885 |
self.registry._directives = {} |
|
886 |
self.registry._directives[name] = (c, action_wrap) |
|
887 |
|
|
888 |
def __getattr__(self, name): |
|
889 |
# allow directive extension names to work |
|
890 |
directives = getattr(self.registry, '_directives', {}) |
|
891 |
c = directives.get(name) |
|
892 |
if c is None: |
|
893 |
raise AttributeError(name) |
|
894 |
c, action_wrap = c |
|
895 |
if action_wrap: |
|
896 |
c = action_method(c) |
c7cc88
|
897 |
# Create a bound method (works on both Py2 and Py3) |
CM |
898 |
# http://stackoverflow.com/a/1015405/209039 |
|
899 |
m = c.__get__(self, self.__class__) |
5bf23f
|
900 |
return m |
CM |
901 |
|
|
902 |
def with_package(self, package): |
|
903 |
""" Return a new Configurator instance with the same registry |
80281d
|
904 |
as this configurator. ``package`` may be an actual Python package |
TL |
905 |
object or a :term:`dotted Python name` representing a package.""" |
b86fa9
|
906 |
configurator = self.__class__( |
CM |
907 |
registry=self.registry, |
|
908 |
package=package, |
ae6c88
|
909 |
root_package=self.root_package, |
b86fa9
|
910 |
autocommit=self.autocommit, |
CM |
911 |
route_prefix=self.route_prefix, |
844ed9
|
912 |
introspection=self.introspection, |
0c29cf
|
913 |
) |
b86fa9
|
914 |
configurator.basepath = self.basepath |
CM |
915 |
configurator.includepath = self.includepath |
|
916 |
configurator.info = self.info |
|
917 |
return configurator |
5bf23f
|
918 |
|
CM |
919 |
def maybe_dotted(self, dotted): |
|
920 |
""" Resolve the :term:`dotted Python name` ``dotted`` to a |
|
921 |
global Python object. If ``dotted`` is not a string, return |
|
922 |
it without attempting to do any name resolution. If |
|
923 |
``dotted`` is a relative dotted name (e.g. ``.foo.bar``, |
|
924 |
consider it relative to the ``package`` argument supplied to |
|
925 |
this Configurator's constructor.""" |
|
926 |
return self.name_resolver.maybe_resolve(dotted) |
|
927 |
|
|
928 |
def absolute_asset_spec(self, relative_spec): |
|
929 |
""" Resolve the potentially relative :term:`asset |
|
930 |
specification` string passed as ``relative_spec`` into an |
|
931 |
absolute asset specification string and return the string. |
|
932 |
Use the ``package`` of this configurator as the package to |
|
933 |
which the asset specification will be considered relative |
|
934 |
when generating an absolute asset specification. If the |
|
935 |
provided ``relative_spec`` argument is already absolute, or if |
|
936 |
the ``relative_spec`` is not a string, it is simply returned.""" |
e6c2d2
|
937 |
if not isinstance(relative_spec, string_types): |
5bf23f
|
938 |
return relative_spec |
CM |
939 |
return self._make_spec(relative_spec) |
|
940 |
|
0c29cf
|
941 |
absolute_resource_spec = absolute_asset_spec # b/w compat forever |
5bf23f
|
942 |
|
804eb0
|
943 |
def begin(self, request=_marker): |
5bf23f
|
944 |
""" Indicate that application or test configuration has begun. |
CM |
945 |
This pushes a dictionary containing the :term:`application |
|
946 |
registry` implied by ``registry`` attribute of this |
|
947 |
configurator and the :term:`request` implied by the |
f271a6
|
948 |
``request`` argument onto the :term:`thread local` stack |
5bf23f
|
949 |
consulted by various :mod:`pyramid.threadlocal` API |
804eb0
|
950 |
functions. |
MM |
951 |
|
|
952 |
If ``request`` is not specified and the registry owned by the |
|
953 |
configurator is already pushed as the current threadlocal registry |
|
954 |
then this method will keep the current threadlocal request unchanged. |
|
955 |
|
|
956 |
.. versionchanged:: 1.8 |
|
957 |
The current threadlocal request is propagated if the current |
|
958 |
threadlocal registry remains unchanged. |
|
959 |
|
|
960 |
""" |
|
961 |
if request is _marker: |
|
962 |
current = self.manager.get() |
|
963 |
if current['registry'] == self.registry: |
|
964 |
request = current['request'] |
|
965 |
else: |
|
966 |
request = None |
0c29cf
|
967 |
self.manager.push({'registry': self.registry, 'request': request}) |
5bf23f
|
968 |
|
CM |
969 |
def end(self): |
|
970 |
""" Indicate that application or test configuration has ended. |
f271a6
|
971 |
This pops the last value pushed onto the :term:`thread local` |
5bf23f
|
972 |
stack (usually by the ``begin`` method) and returns that |
CM |
973 |
value. |
|
974 |
""" |
|
975 |
return self.manager.pop() |
|
976 |
|
134ef7
|
977 |
def __enter__(self): |
MM |
978 |
self.begin() |
|
979 |
return self |
|
980 |
|
|
981 |
def __exit__(self, exc_type, exc_value, exc_traceback): |
|
982 |
self.end() |
|
983 |
|
|
984 |
if exc_value is None: |
|
985 |
self.commit() |
|
986 |
|
5bf23f
|
987 |
# this is *not* an action method (uses caller_package) |
0c29cf
|
988 |
def scan( |
MM |
989 |
self, package=None, categories=None, onerror=None, ignore=None, **kw |
|
990 |
): |
5bf23f
|
991 |
"""Scan a Python package and any of its subpackages for objects |
CM |
992 |
marked with :term:`configuration decoration` such as |
|
993 |
:class:`pyramid.view.view_config`. Any decorated object found will |
|
994 |
influence the current configuration state. |
|
995 |
|
|
996 |
The ``package`` argument should be a Python :term:`package` or module |
|
997 |
object (or a :term:`dotted Python name` which refers to such a |
|
998 |
package or module). If ``package`` is ``None``, the package of the |
|
999 |
*caller* is used. |
|
1000 |
|
|
1001 |
The ``categories`` argument, if provided, should be the |
|
1002 |
:term:`Venusian` 'scan categories' to use during scanning. Providing |
|
1003 |
this argument is not often necessary; specifying scan categories is |
|
1004 |
an extremely advanced usage. By default, ``categories`` is ``None`` |
|
1005 |
which will execute *all* Venusian decorator callbacks including |
|
1006 |
:app:`Pyramid`-related decorators such as |
|
1007 |
:class:`pyramid.view.view_config`. See the :term:`Venusian` |
|
1008 |
documentation for more information about limiting a scan by using an |
|
1009 |
explicit set of categories. |
|
1010 |
|
1aeae3
|
1011 |
The ``onerror`` argument, if provided, should be a Venusian |
CM |
1012 |
``onerror`` callback function. The onerror function is passed to |
|
1013 |
:meth:`venusian.Scanner.scan` to influence error behavior when an |
|
1014 |
exception is raised during the scanning process. See the |
|
1015 |
:term:`Venusian` documentation for more information about ``onerror`` |
|
1016 |
callbacks. |
|
1017 |
|
e4b8fa
|
1018 |
The ``ignore`` argument, if provided, should be a Venusian ``ignore`` |
CM |
1019 |
value. Providing an ``ignore`` argument allows the scan to ignore |
|
1020 |
particular modules, packages, or global objects during a scan. |
|
1021 |
``ignore`` can be a string or a callable, or a list containing |
|
1022 |
strings or callables. The simplest usage of ``ignore`` is to provide |
|
1023 |
a module or package by providing a full path to its dotted name. For |
|
1024 |
example: ``config.scan(ignore='my.module.subpackage')`` would ignore |
|
1025 |
the ``my.module.subpackage`` package during a scan, which would |
|
1026 |
prevent the subpackage and any of its submodules from being imported |
|
1027 |
and scanned. See the :term:`Venusian` documentation for more |
|
1028 |
information about the ``ignore`` argument. |
|
1029 |
|
5bf23f
|
1030 |
To perform a ``scan``, Pyramid creates a Venusian ``Scanner`` object. |
CM |
1031 |
The ``kw`` argument represents a set of keyword arguments to pass to |
|
1032 |
the Venusian ``Scanner`` object's constructor. See the |
|
1033 |
:term:`venusian` documentation (its ``Scanner`` class) for more |
|
1034 |
information about the constructor. By default, the only keyword |
|
1035 |
arguments passed to the Scanner constructor are ``{'config':self}`` |
|
1036 |
where ``self`` is this configurator object. This services the |
|
1037 |
requirement of all built-in Pyramid decorators, but extension systems |
|
1038 |
may require additional arguments. Providing this argument is not |
|
1039 |
often necessary; it's an advanced usage. |
|
1040 |
|
0b23b3
|
1041 |
.. versionadded:: 1.1 |
TL |
1042 |
The ``**kw`` argument. |
|
1043 |
|
|
1044 |
.. versionadded:: 1.3 |
|
1045 |
The ``ignore`` argument. |
|
1046 |
|
5bf23f
|
1047 |
""" |
CM |
1048 |
package = self.maybe_dotted(package) |
0c29cf
|
1049 |
if package is None: # pragma: no cover |
5bf23f
|
1050 |
package = caller_package() |
CM |
1051 |
|
25c64c
|
1052 |
ctorkw = {'config': self} |
1aeae3
|
1053 |
ctorkw.update(kw) |
5bf23f
|
1054 |
|
1aeae3
|
1055 |
scanner = self.venusian.Scanner(**ctorkw) |
25c64c
|
1056 |
|
0c29cf
|
1057 |
scanner.scan( |
MM |
1058 |
package, categories=categories, onerror=onerror, ignore=ignore |
|
1059 |
) |
5bf23f
|
1060 |
|
CM |
1061 |
def make_wsgi_app(self): |
|
1062 |
""" Commits any pending configuration statements, sends a |
|
1063 |
:class:`pyramid.events.ApplicationCreated` event to all listeners, |
|
1064 |
adds this configuration's registry to |
|
1065 |
:attr:`pyramid.config.global_registries`, and returns a |
|
1066 |
:app:`Pyramid` WSGI application representing the committed |
|
1067 |
configuration state.""" |
|
1068 |
self.commit() |
|
1069 |
app = Router(self.registry) |
cfbbd6
|
1070 |
|
cfb2b5
|
1071 |
# Allow tools like "pshell development.ini" to find the 'last' |
cfbbd6
|
1072 |
# registry configured. |
5bf23f
|
1073 |
global_registries.add(self.registry) |
cfbbd6
|
1074 |
|
f271a6
|
1075 |
# Push the registry onto the stack in case any code that depends on |
cfbbd6
|
1076 |
# the registry threadlocal APIs used in listeners subscribed to the |
CM |
1077 |
# IApplicationCreated event. |
0c1c58
|
1078 |
self.begin() |
5bf23f
|
1079 |
try: |
CM |
1080 |
self.registry.notify(ApplicationCreated(app)) |
|
1081 |
finally: |
0c1c58
|
1082 |
self.end() |
5bf23f
|
1083 |
|
CM |
1084 |
return app |
|
1085 |
|
25c64c
|
1086 |
|
d94e4d
|
1087 |
# this class is licensed under the ZPL (stolen from Zope) |
b86fa9
|
1088 |
class ActionState(object): |
79ef3d
|
1089 |
def __init__(self): |
da8e2a
|
1090 |
# NB "actions" is an API, dep'd upon by pyramid_zcml's load_zcml func |
25c64c
|
1091 |
self.actions = [] |
79ef3d
|
1092 |
self._seen_files = set() |
5bf23f
|
1093 |
|
CM |
1094 |
def processSpec(self, spec): |
|
1095 |
"""Check whether a callable needs to be processed. The ``spec`` |
|
1096 |
refers to a unique identifier for the callable. |
|
1097 |
|
|
1098 |
Return True if processing is needed and False otherwise. If |
|
1099 |
the callable needs to be processed, it will be marked as |
|
1100 |
processed, assuming that the caller will procces the callable if |
|
1101 |
it needs to be processed. |
|
1102 |
""" |
|
1103 |
if spec in self._seen_files: |
|
1104 |
return False |
|
1105 |
self._seen_files.add(spec) |
|
1106 |
return True |
|
1107 |
|
0c29cf
|
1108 |
def action( |
MM |
1109 |
self, |
|
1110 |
discriminator, |
|
1111 |
callable=None, |
|
1112 |
args=(), |
|
1113 |
kw=None, |
|
1114 |
order=0, |
|
1115 |
includepath=(), |
|
1116 |
info=None, |
|
1117 |
introspectables=(), |
|
1118 |
**extra |
|
1119 |
): |
79ef3d
|
1120 |
"""Add an action with the given discriminator, callable and arguments |
CM |
1121 |
""" |
b86fa9
|
1122 |
if kw is None: |
CM |
1123 |
kw = {} |
7d109d
|
1124 |
action = extra |
CM |
1125 |
action.update( |
|
1126 |
dict( |
|
1127 |
discriminator=discriminator, |
|
1128 |
callable=callable, |
|
1129 |
args=args, |
|
1130 |
kw=kw, |
|
1131 |
includepath=includepath, |
|
1132 |
info=info, |
|
1133 |
order=order, |
|
1134 |
introspectables=introspectables, |
412b4a
|
1135 |
) |
0c29cf
|
1136 |
) |
79ef3d
|
1137 |
self.actions.append(action) |
CM |
1138 |
|
3b5ccb
|
1139 |
def execute_actions(self, clear=True, introspector=None): |
79ef3d
|
1140 |
"""Execute the configuration actions |
CM |
1141 |
|
|
1142 |
This calls the action callables after resolving conflicts |
|
1143 |
|
|
1144 |
For example: |
|
1145 |
|
|
1146 |
>>> output = [] |
|
1147 |
>>> def f(*a, **k): |
|
1148 |
... output.append(('f', a, k)) |
d94e4d
|
1149 |
>>> context = ActionState() |
79ef3d
|
1150 |
>>> context.actions = [ |
CM |
1151 |
... (1, f, (1,)), |
|
1152 |
... (1, f, (11,), {}, ('x', )), |
|
1153 |
... (2, f, (2,)), |
|
1154 |
... ] |
|
1155 |
>>> context.execute_actions() |
|
1156 |
>>> output |
|
1157 |
[('f', (1,), {}), ('f', (2,), {})] |
|
1158 |
|
|
1159 |
If the action raises an error, we convert it to a |
|
1160 |
ConfigurationExecutionError. |
|
1161 |
|
|
1162 |
>>> output = [] |
|
1163 |
>>> def bad(): |
|
1164 |
... bad.xxx |
|
1165 |
>>> context.actions = [ |
|
1166 |
... (1, f, (1,)), |
|
1167 |
... (1, f, (11,), {}, ('x', )), |
|
1168 |
... (2, f, (2,)), |
|
1169 |
... (3, bad, (), {}, (), 'oops') |
|
1170 |
... ] |
|
1171 |
>>> try: |
|
1172 |
... v = context.execute_actions() |
|
1173 |
... except ConfigurationExecutionError, v: |
|
1174 |
... pass |
edfc4f
|
1175 |
>>> print(v) |
79ef3d
|
1176 |
exceptions.AttributeError: 'function' object has no attribute 'xxx' |
CM |
1177 |
in: |
|
1178 |
oops |
|
1179 |
|
|
1180 |
Note that actions executed before the error still have an effect: |
|
1181 |
|
|
1182 |
>>> output |
|
1183 |
[('f', (1,), {}), ('f', (2,), {})] |
|
1184 |
|
225364
|
1185 |
The execution is re-entrant such that actions may be added by other |
MM |
1186 |
actions with the one caveat that the order of any added actions must |
|
1187 |
be equal to or larger than the current action. |
412b4a
|
1188 |
|
225364
|
1189 |
>>> output = [] |
MM |
1190 |
>>> def f(*a, **k): |
|
1191 |
... output.append(('f', a, k)) |
|
1192 |
... context.actions.append((3, g, (8,), {})) |
|
1193 |
>>> def g(*a, **k): |
|
1194 |
... output.append(('g', a, k)) |
|
1195 |
>>> context.actions = [ |
|
1196 |
... (1, f, (1,)), |
|
1197 |
... ] |
|
1198 |
>>> context.execute_actions() |
|
1199 |
>>> output |
c56957
|
1200 |
[('f', (1,), {}), ('g', (8,), {})] |
225364
|
1201 |
|
MM |
1202 |
""" |
79ef3d
|
1203 |
try: |
a52326
|
1204 |
all_actions = [] |
225364
|
1205 |
executed_actions = [] |
0f9a56
|
1206 |
action_iter = iter([]) |
MM |
1207 |
conflict_state = ConflictResolverState() |
225364
|
1208 |
|
a52326
|
1209 |
while True: |
MM |
1210 |
# We clear the actions list prior to execution so if there |
|
1211 |
# are some new actions then we add them to the mix and resolve |
|
1212 |
# conflicts again. This orders the new actions as well as |
|
1213 |
# ensures that the previously executed actions have no new |
|
1214 |
# conflicts. |
|
1215 |
if self.actions: |
|
1216 |
all_actions.extend(self.actions) |
0f9a56
|
1217 |
action_iter = resolveConflicts( |
0c29cf
|
1218 |
self.actions, state=conflict_state |
0f9a56
|
1219 |
) |
a52326
|
1220 |
self.actions = [] |
MM |
1221 |
|
0f9a56
|
1222 |
action = next(action_iter, None) |
a52326
|
1223 |
if action is None: |
MM |
1224 |
# we are done! |
|
1225 |
break |
|
1226 |
|
412b4a
|
1227 |
callable = action['callable'] |
CM |
1228 |
args = action['args'] |
|
1229 |
kw = action['kw'] |
|
1230 |
info = action['info'] |
73b206
|
1231 |
# we use "get" below in case an action was added via a ZCML |
CM |
1232 |
# directive that did not know about introspectables |
|
1233 |
introspectables = action.get('introspectables', ()) |
ae0ff2
|
1234 |
|
79ef3d
|
1235 |
try: |
2e651e
|
1236 |
if callable is not None: |
CM |
1237 |
callable(*args, **kw) |
0f9a56
|
1238 |
except Exception: |
79ef3d
|
1239 |
t, v, tb = sys.exc_info() |
b86fa9
|
1240 |
try: |
0c29cf
|
1241 |
reraise( |
MM |
1242 |
ConfigurationExecutionError, |
|
1243 |
ConfigurationExecutionError(t, v, info), |
|
1244 |
tb, |
|
1245 |
) |
b86fa9
|
1246 |
finally: |
25c64c
|
1247 |
del t, v, tb |
ae0ff2
|
1248 |
|
5e92f3
|
1249 |
if introspector is not None: |
CM |
1250 |
for introspectable in introspectables: |
35ad08
|
1251 |
introspectable.register(introspector, info) |
225364
|
1252 |
|
MM |
1253 |
executed_actions.append(action) |
|
1254 |
|
0f9a56
|
1255 |
self.actions = all_actions |
MM |
1256 |
return executed_actions |
|
1257 |
|
79ef3d
|
1258 |
finally: |
CM |
1259 |
if clear: |
0f9a56
|
1260 |
self.actions = [] |
MM |
1261 |
|
|
1262 |
|
|
1263 |
class ConflictResolverState(object): |
|
1264 |
def __init__(self): |
|
1265 |
# keep a set of resolved discriminators to test against to ensure |
|
1266 |
# that a new action does not conflict with something already executed |
|
1267 |
self.resolved_ainfos = {} |
|
1268 |
|
|
1269 |
# actions left over from a previous iteration |
|
1270 |
self.remaining_actions = [] |
|
1271 |
|
|
1272 |
# after executing an action we memoize its order to avoid any new |
|
1273 |
# actions sending us backward |
|
1274 |
self.min_order = None |
|
1275 |
|
|
1276 |
# unique tracks the index of the action so we need it to increase |
|
1277 |
# monotonically across invocations to resolveConflicts |
|
1278 |
self.start = 0 |
|
1279 |
|
9c8ec5
|
1280 |
|
d94e4d
|
1281 |
# this function is licensed under the ZPL (stolen from Zope) |
0f9a56
|
1282 |
def resolveConflicts(actions, state=None): |
79ef3d
|
1283 |
"""Resolve conflicting actions |
CM |
1284 |
|
|
1285 |
Given an actions list, identify and try to resolve conflicting actions. |
412b4a
|
1286 |
Actions conflict if they have the same non-None discriminator. |
0f9a56
|
1287 |
|
79ef3d
|
1288 |
Conflicting actions can be resolved if the include path of one of |
CM |
1289 |
the actions is a prefix of the includepaths of the other |
|
1290 |
conflicting actions and is unequal to the include paths in the |
|
1291 |
other conflicting actions. |
0f9a56
|
1292 |
|
MM |
1293 |
Actions are resolved on a per-order basis because some discriminators |
|
1294 |
cannot be computed until earlier actions have executed. An action in an |
|
1295 |
earlier order may execute successfully only to find out later that it was |
|
1296 |
overridden by another action with a smaller include path. This will result |
|
1297 |
in a conflict as there is no way to revert the original action. |
|
1298 |
|
|
1299 |
``state`` may be an instance of ``ConflictResolverState`` that |
|
1300 |
can be used to resume execution and resolve the new actions against the |
|
1301 |
list of executed actions from a previous call. |
|
1302 |
|
79ef3d
|
1303 |
""" |
0f9a56
|
1304 |
if state is None: |
MM |
1305 |
state = ConflictResolverState() |
|
1306 |
|
|
1307 |
# pick up where we left off last time, but track the new actions as well |
|
1308 |
state.remaining_actions.extend(normalize_actions(actions)) |
|
1309 |
actions = state.remaining_actions |
79ef3d
|
1310 |
|
859e94
|
1311 |
def orderandpos(v): |
CM |
1312 |
n, v = v |
9c8ec5
|
1313 |
return (v['order'] or 0, n) |
CM |
1314 |
|
859e94
|
1315 |
def orderonly(v): |
CM |
1316 |
n, v = v |
9c8ec5
|
1317 |
return v['order'] or 0 |
CM |
1318 |
|
0f9a56
|
1319 |
sactions = sorted(enumerate(actions, start=state.start), key=orderandpos) |
9c8ec5
|
1320 |
for order, actiongroup in itertools.groupby(sactions, orderonly): |
82f677
|
1321 |
# "order" is an integer grouping. Actions in a lower order will be |
9c8ec5
|
1322 |
# executed before actions in a higher order. All of the actions in |
CM |
1323 |
# one grouping will be executed (its callable, if any will be called) |
|
1324 |
# before any of the actions in the next. |
|
1325 |
output = [] |
0f9a56
|
1326 |
unique = {} |
MM |
1327 |
|
|
1328 |
# error out if we went backward in order |
|
1329 |
if state.min_order is not None and order < state.min_order: |
0c29cf
|
1330 |
r = [ |
MM |
1331 |
'Actions were added to order={0} after execution had moved ' |
|
1332 |
'on to order={1}. Conflicting actions: '.format( |
|
1333 |
order, state.min_order |
|
1334 |
) |
|
1335 |
] |
0f9a56
|
1336 |
for i, action in actiongroup: |
MM |
1337 |
for line in str(action['info']).rstrip().split('\n'): |
|
1338 |
r.append(" " + line) |
|
1339 |
raise ConfigurationError('\n'.join(r)) |
82f677
|
1340 |
|
9c8ec5
|
1341 |
for i, action in actiongroup: |
CM |
1342 |
# Within an order, actions are executed sequentially based on |
|
1343 |
# original action ordering ("i"). |
82f677
|
1344 |
|
0f9a56
|
1345 |
# "ainfo" is a tuple of (i, action) where "i" is an integer |
MM |
1346 |
# expressing the relative position of this action in the action |
|
1347 |
# list being resolved, and "action" is an action dictionary. The |
|
1348 |
# purpose of an ainfo is to associate an "i" with a particular |
|
1349 |
# action; "i" exists for sorting after conflict resolution. |
|
1350 |
ainfo = (i, action) |
9c8ec5
|
1351 |
|
0f9a56
|
1352 |
# wait to defer discriminators until we are on their order because |
MM |
1353 |
# the discriminator may depend on state from a previous order |
9c8ec5
|
1354 |
discriminator = undefer(action['discriminator']) |
CM |
1355 |
action['discriminator'] = discriminator |
|
1356 |
|
|
1357 |
if discriminator is None: |
|
1358 |
# The discriminator is None, so this action can never conflict. |
|
1359 |
# We can add it directly to the result. |
|
1360 |
output.append(ainfo) |
|
1361 |
continue |
|
1362 |
|
|
1363 |
L = unique.setdefault(discriminator, []) |
|
1364 |
L.append(ainfo) |
|
1365 |
|
|
1366 |
# Check for conflicts |
|
1367 |
conflicts = {} |
|
1368 |
for discriminator, ainfos in unique.items(): |
0f9a56
|
1369 |
# We use (includepath, i) as a sort key because we need to |
9c8ec5
|
1370 |
# sort the actions by the paths so that the shortest path with a |
CM |
1371 |
# given prefix comes first. The "first" action is the one with the |
0f9a56
|
1372 |
# shortest include path. We break sorting ties using "i". |
9c8ec5
|
1373 |
def bypath(ainfo): |
0f9a56
|
1374 |
path, i = ainfo[1]['includepath'], ainfo[0] |
9c8ec5
|
1375 |
return path, order, i |
CM |
1376 |
|
|
1377 |
ainfos.sort(key=bypath) |
|
1378 |
ainfo, rest = ainfos[0], ainfos[1:] |
0f9a56
|
1379 |
_, action = ainfo |
735df7
|
1380 |
|
0f9a56
|
1381 |
# ensure this new action does not conflict with a previously |
MM |
1382 |
# resolved action from an earlier order / invocation |
|
1383 |
prev_ainfo = state.resolved_ainfos.get(discriminator) |
|
1384 |
if prev_ainfo is not None: |
|
1385 |
_, paction = prev_ainfo |
|
1386 |
basepath, baseinfo = paction['includepath'], paction['info'] |
|
1387 |
includepath = action['includepath'] |
|
1388 |
# if the new action conflicts with the resolved action then |
|
1389 |
# note the conflict, otherwise drop the action as it's |
|
1390 |
# effectively overriden by the previous action |
0c29cf
|
1391 |
if ( |
MM |
1392 |
includepath[: len(basepath)] != basepath |
|
1393 |
or includepath == basepath |
|
1394 |
): |
0f9a56
|
1395 |
L = conflicts.setdefault(discriminator, [baseinfo]) |
MM |
1396 |
L.append(action['info']) |
|
1397 |
|
|
1398 |
else: |
|
1399 |
output.append(ainfo) |
|
1400 |
|
|
1401 |
basepath, baseinfo = action['includepath'], action['info'] |
|
1402 |
for _, action in rest: |
9c8ec5
|
1403 |
includepath = action['includepath'] |
CM |
1404 |
# Test whether path is a prefix of opath |
0c29cf
|
1405 |
if ( |
MM |
1406 |
includepath[: len(basepath)] != basepath |
|
1407 |
or includepath == basepath # not a prefix |
|
1408 |
): |
9c8ec5
|
1409 |
L = conflicts.setdefault(discriminator, [baseinfo]) |
CM |
1410 |
L.append(action['info']) |
79ef3d
|
1411 |
|
9c8ec5
|
1412 |
if conflicts: |
CM |
1413 |
raise ConfigurationConflictError(conflicts) |
735df7
|
1414 |
|
0f9a56
|
1415 |
# sort resolved actions by "i" and yield them one by one |
MM |
1416 |
for i, action in sorted(output, key=operator.itemgetter(0)): |
|
1417 |
# do not memoize the order until we resolve an action inside it |
|
1418 |
state.min_order = action['order'] |
|
1419 |
state.start = i + 1 |
|
1420 |
state.remaining_actions.remove(action) |
|
1421 |
state.resolved_ainfos[action['discriminator']] = (i, action) |
|
1422 |
yield action |
50a8a0
|
1423 |
|
JA |
1424 |
|
0f9a56
|
1425 |
def normalize_actions(actions): |
MM |
1426 |
"""Convert old-style tuple actions to new-style dicts.""" |
|
1427 |
result = [] |
|
1428 |
for v in actions: |
|
1429 |
if not isinstance(v, dict): |
|
1430 |
v = expand_action_tuple(*v) |
|
1431 |
result.append(v) |
|
1432 |
return result |
|
1433 |
|
|
1434 |
|
|
1435 |
def expand_action_tuple( |
0c29cf
|
1436 |
discriminator, |
MM |
1437 |
callable=None, |
|
1438 |
args=(), |
|
1439 |
kw=None, |
|
1440 |
includepath=(), |
|
1441 |
info=None, |
|
1442 |
order=0, |
|
1443 |
introspectables=(), |
0f9a56
|
1444 |
): |
b86fa9
|
1445 |
if kw is None: |
CM |
1446 |
kw = {} |
412b4a
|
1447 |
return dict( |
CM |
1448 |
discriminator=discriminator, |
|
1449 |
callable=callable, |
|
1450 |
args=args, |
|
1451 |
kw=kw, |
|
1452 |
includepath=includepath, |
|
1453 |
info=info, |
|
1454 |
order=order, |
|
1455 |
introspectables=introspectables, |
0c29cf
|
1456 |
) |
79ef3d
|
1457 |
|
0f9a56
|
1458 |
|
5bf23f
|
1459 |
global_registries = WeakOrderedSet() |