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