Coverage for /home/martinb/.local/share/virtualenvs/camcops/lib/python3.6/site-packages/pyramid/config/tweens.py : 77%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1from zope.interface import implementer
3from pyramid.interfaces import ITweens
5from pyramid.compat import string_types, is_nonstr_iter
7from pyramid.exceptions import ConfigurationError
9from pyramid.tweens import MAIN, INGRESS, EXCVIEW
11from pyramid.util import is_string_or_iterable, TopologicalSorter
13from pyramid.config.actions import action_method
16class TweensConfiguratorMixin(object):
17 def add_tween(self, tween_factory, under=None, over=None):
18 """
19 .. versionadded:: 1.2
21 Add a 'tween factory'. A :term:`tween` (a contraction of 'between')
22 is a bit of code that sits between the Pyramid router's main request
23 handling function and the upstream WSGI component that uses
24 :app:`Pyramid` as its 'app'. Tweens are a feature that may be used
25 by Pyramid framework extensions, to provide, for example,
26 Pyramid-specific view timing support, bookkeeping code that examines
27 exceptions before they are returned to the upstream WSGI application,
28 or a variety of other features. Tweens behave a bit like
29 :term:`WSGI` 'middleware' but they have the benefit of running in a
30 context in which they have access to the Pyramid :term:`application
31 registry` as well as the Pyramid rendering machinery.
33 .. note:: You can view the tween ordering configured into a given
34 Pyramid application by using the ``ptweens``
35 command. See :ref:`displaying_tweens`.
37 The ``tween_factory`` argument must be a :term:`dotted Python name`
38 to a global object representing the tween factory.
40 The ``under`` and ``over`` arguments allow the caller of
41 ``add_tween`` to provide a hint about where in the tween chain this
42 tween factory should be placed when an implicit tween chain is used.
43 These hints are only used when an explicit tween chain is not used
44 (when the ``pyramid.tweens`` configuration value is not set).
45 Allowable values for ``under`` or ``over`` (or both) are:
47 - ``None`` (the default).
49 - A :term:`dotted Python name` to a tween factory: a string
50 representing the dotted name of a tween factory added in a call to
51 ``add_tween`` in the same configuration session.
53 - One of the constants :attr:`pyramid.tweens.MAIN`,
54 :attr:`pyramid.tweens.INGRESS`, or :attr:`pyramid.tweens.EXCVIEW`.
56 - An iterable of any combination of the above. This allows the user
57 to specify fallbacks if the desired tween is not included, as well
58 as compatibility with multiple other tweens.
60 ``under`` means 'closer to the main Pyramid application than',
61 ``over`` means 'closer to the request ingress than'.
63 For example, calling ``add_tween('myapp.tfactory',
64 over=pyramid.tweens.MAIN)`` will attempt to place the tween factory
65 represented by the dotted name ``myapp.tfactory`` directly 'above'
66 (in ``ptweens`` order) the main Pyramid request handler.
67 Likewise, calling ``add_tween('myapp.tfactory',
68 over=pyramid.tweens.MAIN, under='mypkg.someothertween')`` will
69 attempt to place this tween factory 'above' the main handler but
70 'below' (a fictional) 'mypkg.someothertween' tween factory.
72 If all options for ``under`` (or ``over``) cannot be found in the
73 current configuration, it is an error. If some options are specified
74 purely for compatibilty with other tweens, just add a fallback of
75 MAIN or INGRESS. For example, ``under=('mypkg.someothertween',
76 'mypkg.someothertween2', INGRESS)``. This constraint will require
77 the tween to be located under both the 'mypkg.someothertween' tween,
78 the 'mypkg.someothertween2' tween, and INGRESS. If any of these is
79 not in the current configuration, this constraint will only organize
80 itself based on the tweens that are present.
82 Specifying neither ``over`` nor ``under`` is equivalent to specifying
83 ``under=INGRESS``.
85 Implicit tween ordering is obviously only best-effort. Pyramid will
86 attempt to present an implicit order of tweens as best it can, but
87 the only surefire way to get any particular ordering is to use an
88 explicit tween order. A user may always override the implicit tween
89 ordering by using an explicit ``pyramid.tweens`` configuration value
90 setting.
92 ``under``, and ``over`` arguments are ignored when an explicit tween
93 chain is specified using the ``pyramid.tweens`` configuration value.
95 For more information, see :ref:`registering_tweens`.
97 """
98 return self._add_tween(
99 tween_factory, under=under, over=over, explicit=False
100 )
102 def add_default_tweens(self):
103 self.add_tween(EXCVIEW)
105 @action_method
106 def _add_tween(self, tween_factory, under=None, over=None, explicit=False):
108 if not isinstance(tween_factory, string_types):
109 raise ConfigurationError(
110 'The "tween_factory" argument to add_tween must be a '
111 'dotted name to a globally importable object, not %r'
112 % tween_factory
113 )
115 name = tween_factory
117 if name in (MAIN, INGRESS):
118 raise ConfigurationError('%s is a reserved tween name' % name)
120 tween_factory = self.maybe_dotted(tween_factory)
122 for t, p in [('over', over), ('under', under)]:
123 if p is not None:
124 if not is_string_or_iterable(p):
125 raise ConfigurationError(
126 '"%s" must be a string or iterable, not %s' % (t, p)
127 )
129 if over is INGRESS or is_nonstr_iter(over) and INGRESS in over:
130 raise ConfigurationError('%s cannot be over INGRESS' % name)
132 if under is MAIN or is_nonstr_iter(under) and MAIN in under:
133 raise ConfigurationError('%s cannot be under MAIN' % name)
135 registry = self.registry
136 introspectables = []
138 tweens = registry.queryUtility(ITweens)
139 if tweens is None:
140 tweens = Tweens()
141 registry.registerUtility(tweens, ITweens)
143 def register():
144 if explicit:
145 tweens.add_explicit(name, tween_factory)
146 else:
147 tweens.add_implicit(
148 name, tween_factory, under=under, over=over
149 )
151 discriminator = ('tween', name, explicit)
152 tween_type = explicit and 'explicit' or 'implicit'
154 intr = self.introspectable(
155 'tweens', discriminator, name, '%s tween' % tween_type
156 )
157 intr['name'] = name
158 intr['factory'] = tween_factory
159 intr['type'] = tween_type
160 intr['under'] = under
161 intr['over'] = over
162 introspectables.append(intr)
163 self.action(discriminator, register, introspectables=introspectables)
166@implementer(ITweens)
167class Tweens(object):
168 def __init__(self):
169 self.sorter = TopologicalSorter(
170 default_before=None,
171 default_after=INGRESS,
172 first=INGRESS,
173 last=MAIN,
174 )
175 self.explicit = []
177 def add_explicit(self, name, factory):
178 self.explicit.append((name, factory))
180 def add_implicit(self, name, factory, under=None, over=None):
181 self.sorter.add(name, factory, after=under, before=over)
183 def implicit(self):
184 return self.sorter.sorted()
186 def __call__(self, handler, registry):
187 if self.explicit:
188 use = self.explicit
189 else:
190 use = self.implicit()
191 for name, factory in use[::-1]:
192 handler = factory(handler, registry)
193 return handler