Michael Merickel
2018-10-15 433efe06191a7007ca8c5bf8fafee5c7c1439ebb
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()