commit | author | age
|
3b7334
|
1 |
from zope.interface import implementer |
b397ac
|
2 |
|
5bf23f
|
3 |
from pyramid.interfaces import ITweens |
CM |
4 |
|
0c29cf
|
5 |
from pyramid.compat import string_types, is_nonstr_iter |
ee117e
|
6 |
|
5bf23f
|
7 |
from pyramid.exceptions import ConfigurationError |
ee117e
|
8 |
|
0c29cf
|
9 |
from pyramid.tweens import MAIN, INGRESS, EXCVIEW |
5bf23f
|
10 |
|
0c29cf
|
11 |
from pyramid.util import is_string_or_iterable, TopologicalSorter |
52fde9
|
12 |
|
d579f2
|
13 |
from pyramid.config.actions import action_method |
0c29cf
|
14 |
|
5bf23f
|
15 |
|
CM |
16 |
class TweensConfiguratorMixin(object): |
4f070b
|
17 |
def add_tween(self, tween_factory, under=None, over=None): |
5bf23f
|
18 |
""" |
0b23b3
|
19 |
.. versionadded:: 1.2 |
adfc23
|
20 |
|
5bf23f
|
21 |
Add a 'tween factory'. A :term:`tween` (a contraction of 'between') |
CM |
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. |
|
32 |
|
|
33 |
.. note:: You can view the tween ordering configured into a given |
cfb2b5
|
34 |
Pyramid application by using the ``ptweens`` |
5bf23f
|
35 |
command. See :ref:`displaying_tweens`. |
CM |
36 |
|
|
37 |
The ``tween_factory`` argument must be a :term:`dotted Python name` |
|
38 |
to a global object representing the tween factory. |
|
39 |
|
|
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: |
|
46 |
|
|
47 |
- ``None`` (the default). |
|
48 |
|
|
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. |
|
52 |
|
|
53 |
- One of the constants :attr:`pyramid.tweens.MAIN`, |
|
54 |
:attr:`pyramid.tweens.INGRESS`, or :attr:`pyramid.tweens.EXCVIEW`. |
|
55 |
|
|
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. |
a54bc1
|
59 |
|
5bf23f
|
60 |
``under`` means 'closer to the main Pyramid application than', |
CM |
61 |
``over`` means 'closer to the request ingress than'. |
|
62 |
|
|
63 |
For example, calling ``add_tween('myapp.tfactory', |
|
64 |
over=pyramid.tweens.MAIN)`` will attempt to place the tween factory |
4f070b
|
65 |
represented by the dotted name ``myapp.tfactory`` directly 'above' |
cfb2b5
|
66 |
(in ``ptweens`` order) the main Pyramid request handler. |
5bf23f
|
67 |
Likewise, calling ``add_tween('myapp.tfactory', |
4f070b
|
68 |
over=pyramid.tweens.MAIN, under='mypkg.someothertween')`` will |
CM |
69 |
attempt to place this tween factory 'above' the main handler but |
|
70 |
'below' (a fictional) 'mypkg.someothertween' tween factory. |
5bf23f
|
71 |
|
CM |
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 |
4f070b
|
75 |
MAIN or INGRESS. For example, ``under=('mypkg.someothertween', |
CM |
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. |
5bf23f
|
81 |
|
CM |
82 |
Specifying neither ``over`` nor ``under`` is equivalent to specifying |
|
83 |
``under=INGRESS``. |
|
84 |
|
|
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. |
|
91 |
|
4f070b
|
92 |
``under``, and ``over`` arguments are ignored when an explicit tween |
CM |
93 |
chain is specified using the ``pyramid.tweens`` configuration value. |
5bf23f
|
94 |
|
CM |
95 |
For more information, see :ref:`registering_tweens`. |
|
96 |
|
|
97 |
""" |
0c29cf
|
98 |
return self._add_tween( |
MM |
99 |
tween_factory, under=under, over=over, explicit=False |
|
100 |
) |
5bf23f
|
101 |
|
35b0e3
|
102 |
def add_default_tweens(self): |
MM |
103 |
self.add_tween(EXCVIEW) |
|
104 |
|
79ebb6
|
105 |
@action_method |
4f070b
|
106 |
def _add_tween(self, tween_factory, under=None, over=None, explicit=False): |
5bf23f
|
107 |
|
8e606d
|
108 |
if not isinstance(tween_factory, string_types): |
5bf23f
|
109 |
raise ConfigurationError( |
CM |
110 |
'The "tween_factory" argument to add_tween must be a ' |
0c29cf
|
111 |
'dotted name to a globally importable object, not %r' |
MM |
112 |
% tween_factory |
|
113 |
) |
5bf23f
|
114 |
|
CM |
115 |
name = tween_factory |
4f070b
|
116 |
|
CM |
117 |
if name in (MAIN, INGRESS): |
|
118 |
raise ConfigurationError('%s is a reserved tween name' % name) |
|
119 |
|
5bf23f
|
120 |
tween_factory = self.maybe_dotted(tween_factory) |
CM |
121 |
|
|
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( |
0c29cf
|
126 |
'"%s" must be a string or iterable, not %s' % (t, p) |
MM |
127 |
) |
5bf23f
|
128 |
|
475532
|
129 |
if over is INGRESS or is_nonstr_iter(over) and INGRESS in over: |
5bf23f
|
130 |
raise ConfigurationError('%s cannot be over INGRESS' % name) |
CM |
131 |
|
475532
|
132 |
if under is MAIN or is_nonstr_iter(under) and MAIN in under: |
5bf23f
|
133 |
raise ConfigurationError('%s cannot be under MAIN' % name) |
CM |
134 |
|
|
135 |
registry = self.registry |
47e1f7
|
136 |
introspectables = [] |
eb2fee
|
137 |
|
5bf23f
|
138 |
tweens = registry.queryUtility(ITweens) |
CM |
139 |
if tweens is None: |
|
140 |
tweens = Tweens() |
|
141 |
registry.registerUtility(tweens, ITweens) |
|
142 |
|
eb2fee
|
143 |
def register(): |
CM |
144 |
if explicit: |
|
145 |
tweens.add_explicit(name, tween_factory) |
|
146 |
else: |
0c29cf
|
147 |
tweens.add_implicit( |
MM |
148 |
name, tween_factory, under=under, over=over |
|
149 |
) |
eb2fee
|
150 |
|
87f8d2
|
151 |
discriminator = ('tween', name, explicit) |
CM |
152 |
tween_type = explicit and 'explicit' or 'implicit' |
|
153 |
|
0c29cf
|
154 |
intr = self.introspectable( |
MM |
155 |
'tweens', discriminator, name, '%s tween' % tween_type |
|
156 |
) |
58c01f
|
157 |
intr['name'] = name |
87f8d2
|
158 |
intr['factory'] = tween_factory |
CM |
159 |
intr['type'] = tween_type |
|
160 |
intr['under'] = under |
|
161 |
intr['over'] = over |
47e1f7
|
162 |
introspectables.append(intr) |
CM |
163 |
self.action(discriminator, register, introspectables=introspectables) |
b397ac
|
164 |
|
0c29cf
|
165 |
|
3b7334
|
166 |
@implementer(ITweens) |
b397ac
|
167 |
class Tweens(object): |
CM |
168 |
def __init__(self): |
fc3f23
|
169 |
self.sorter = TopologicalSorter( |
CM |
170 |
default_before=None, |
|
171 |
default_after=INGRESS, |
|
172 |
first=INGRESS, |
0c29cf
|
173 |
last=MAIN, |
MM |
174 |
) |
b397ac
|
175 |
self.explicit = [] |
CM |
176 |
|
|
177 |
def add_explicit(self, name, factory): |
|
178 |
self.explicit.append((name, factory)) |
|
179 |
|
4f070b
|
180 |
def add_implicit(self, name, factory, under=None, over=None): |
fc3f23
|
181 |
self.sorter.add(name, factory, after=under, before=over) |
b397ac
|
182 |
|
CM |
183 |
def implicit(self): |
fc3f23
|
184 |
return self.sorter.sorted() |
b397ac
|
185 |
|
CM |
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 |