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

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 webob import Response as WebobResponse
3from functools import update_wrapper
5from zope.interface import Interface
7from pyramid.interfaces import IResponse, ITraverser, IResourceURL
9from pyramid.util import takes_one_arg
11from pyramid.config.actions import action_method
14class AdaptersConfiguratorMixin(object):
15 @action_method
16 def add_subscriber(self, subscriber, iface=None, **predicates):
17 """Add an event :term:`subscriber` for the event stream
18 implied by the supplied ``iface`` interface.
20 The ``subscriber`` argument represents a callable object (or a
21 :term:`dotted Python name` which identifies a callable); it will be
22 called with a single object ``event`` whenever :app:`Pyramid` emits
23 an :term:`event` associated with the ``iface``, which may be an
24 :term:`interface` or a class or a :term:`dotted Python name` to a
25 global object representing an interface or a class.
27 Using the default ``iface`` value, ``None`` will cause the subscriber
28 to be registered for all event types. See :ref:`events_chapter` for
29 more information about events and subscribers.
31 Any number of predicate keyword arguments may be passed in
32 ``**predicates``. Each predicate named will narrow the set of
33 circumstances in which the subscriber will be invoked. Each named
34 predicate must have been registered via
35 :meth:`pyramid.config.Configurator.add_subscriber_predicate` before it
36 can be used. See :ref:`subscriber_predicates` for more information.
38 .. versionadded:: 1.4
39 The ``**predicates`` argument.
40 """
41 dotted = self.maybe_dotted
42 subscriber, iface = dotted(subscriber), dotted(iface)
43 if iface is None:
44 iface = (Interface,)
45 if not isinstance(iface, (tuple, list)):
46 iface = (iface,)
48 def register():
49 predlist = self.get_predlist('subscriber')
50 order, preds, phash = predlist.make(self, **predicates)
52 derived_predicates = [self._derive_predicate(p) for p in preds]
53 derived_subscriber = self._derive_subscriber(
54 subscriber, derived_predicates
55 )
57 intr.update(
58 {
59 'phash': phash,
60 'order': order,
61 'predicates': preds,
62 'derived_predicates': derived_predicates,
63 'derived_subscriber': derived_subscriber,
64 }
65 )
67 self.registry.registerHandler(derived_subscriber, iface)
69 intr = self.introspectable(
70 'subscribers',
71 id(subscriber),
72 self.object_description(subscriber),
73 'subscriber',
74 )
76 intr['subscriber'] = subscriber
77 intr['interfaces'] = iface
79 self.action(None, register, introspectables=(intr,))
80 return subscriber
82 def _derive_predicate(self, predicate):
83 derived_predicate = predicate
85 if eventonly(predicate):
87 def derived_predicate(*arg):
88 return predicate(arg[0])
90 # seems pointless to try to fix __doc__, __module__, etc as
91 # predicate will invariably be an instance
93 return derived_predicate
95 def _derive_subscriber(self, subscriber, predicates):
96 derived_subscriber = subscriber
98 if eventonly(subscriber):
100 def derived_subscriber(*arg):
101 return subscriber(arg[0])
103 if hasattr(subscriber, '__name__'):
104 update_wrapper(derived_subscriber, subscriber)
106 if not predicates:
107 return derived_subscriber
109 def subscriber_wrapper(*arg):
110 # We need to accept *arg and pass it along because zope subscribers
111 # are designed awkwardly. Notification via
112 # registry.adapter.subscribers will always call an associated
113 # subscriber with all of the objects involved in the subscription
114 # lookup, despite the fact that the event sender always has the
115 # option to attach those objects to the event object itself, and
116 # almost always does.
117 #
118 # The "eventonly" jazz sprinkled in this function and related
119 # functions allows users to define subscribers and predicates which
120 # accept only an event argument without needing to accept the rest
121 # of the adaptation arguments. Had I been smart enough early on to
122 # use .subscriptions to find the subscriber functions in order to
123 # call them manually with a single "event" argument instead of
124 # relying on .subscribers to both find and call them implicitly
125 # with all args, the eventonly hack would not have been required.
126 # At this point, though, using .subscriptions and manual execution
127 # is not possible without badly breaking backwards compatibility.
128 if all((predicate(*arg) for predicate in predicates)):
129 return derived_subscriber(*arg)
131 if hasattr(subscriber, '__name__'):
132 update_wrapper(subscriber_wrapper, subscriber)
134 return subscriber_wrapper
136 @action_method
137 def add_subscriber_predicate(
138 self, name, factory, weighs_more_than=None, weighs_less_than=None
139 ):
140 """
141 .. versionadded:: 1.4
143 Adds a subscriber predicate factory. The associated subscriber
144 predicate can later be named as a keyword argument to
145 :meth:`pyramid.config.Configurator.add_subscriber` in the
146 ``**predicates`` anonymous keyword argument dictionary.
148 ``name`` should be the name of the predicate. It must be a valid
149 Python identifier (it will be used as a ``**predicates`` keyword
150 argument to :meth:`~pyramid.config.Configurator.add_subscriber`).
152 ``factory`` should be a :term:`predicate factory` or :term:`dotted
153 Python name` which refers to a predicate factory.
155 See :ref:`subscriber_predicates` for more information.
157 """
158 self._add_predicate(
159 'subscriber',
160 name,
161 factory,
162 weighs_more_than=weighs_more_than,
163 weighs_less_than=weighs_less_than,
164 )
166 @action_method
167 def add_response_adapter(self, adapter, type_or_iface):
168 """ When an object of type (or interface) ``type_or_iface`` is
169 returned from a view callable, Pyramid will use the adapter
170 ``adapter`` to convert it into an object which implements the
171 :class:`pyramid.interfaces.IResponse` interface. If ``adapter`` is
172 None, an object returned of type (or interface) ``type_or_iface``
173 will itself be used as a response object.
175 ``adapter`` and ``type_or_interface`` may be Python objects or
176 strings representing dotted names to importable Python global
177 objects.
179 See :ref:`using_iresponse` for more information."""
180 adapter = self.maybe_dotted(adapter)
181 type_or_iface = self.maybe_dotted(type_or_iface)
183 def register():
184 reg = self.registry
185 if adapter is None:
186 reg.registerSelfAdapter((type_or_iface,), IResponse)
187 else:
188 reg.registerAdapter(adapter, (type_or_iface,), IResponse)
190 discriminator = (IResponse, type_or_iface)
191 intr = self.introspectable(
192 'response adapters',
193 discriminator,
194 self.object_description(adapter),
195 'response adapter',
196 )
197 intr['adapter'] = adapter
198 intr['type'] = type_or_iface
199 self.action(discriminator, register, introspectables=(intr,))
201 def add_default_response_adapters(self):
202 # cope with WebOb response objects that aren't decorated with IResponse
203 self.add_response_adapter(None, WebobResponse)
205 @action_method
206 def add_traverser(self, adapter, iface=None):
207 """
208 The superdefault :term:`traversal` algorithm that :app:`Pyramid` uses
209 is explained in :ref:`traversal_algorithm`. Though it is rarely
210 necessary, this default algorithm can be swapped out selectively for
211 a different traversal pattern via configuration. The section
212 entitled :ref:`changing_the_traverser` details how to create a
213 traverser class.
215 For example, to override the superdefault traverser used by Pyramid,
216 you might do something like this:
218 .. code-block:: python
220 from myapp.traversal import MyCustomTraverser
221 config.add_traverser(MyCustomTraverser)
223 This would cause the Pyramid superdefault traverser to never be used;
224 instead all traversal would be done using your ``MyCustomTraverser``
225 class, no matter which object was returned by the :term:`root
226 factory` of this application. Note that we passed no arguments to
227 the ``iface`` keyword parameter. The default value of ``iface``,
228 ``None`` represents that the registered traverser should be used when
229 no other more specific traverser is available for the object returned
230 by the root factory.
232 However, more than one traversal algorithm can be active at the same
233 time. The traverser used can depend on the result of the :term:`root
234 factory`. For instance, if your root factory returns more than one
235 type of object conditionally, you could claim that an alternate
236 traverser adapter should be used against one particular class or
237 interface returned by that root factory. When the root factory
238 returned an object that implemented that class or interface, a custom
239 traverser would be used. Otherwise, the default traverser would be
240 used. The ``iface`` argument represents the class of the object that
241 the root factory might return or an :term:`interface` that the object
242 might implement.
244 To use a particular traverser only when the root factory returns a
245 particular class:
247 .. code-block:: python
249 config.add_traverser(MyCustomTraverser, MyRootClass)
251 When more than one traverser is active, the "most specific" traverser
252 will be used (the one that matches the class or interface of the
253 value returned by the root factory most closely).
255 Note that either ``adapter`` or ``iface`` can be a :term:`dotted
256 Python name` or a Python object.
258 See :ref:`changing_the_traverser` for more information.
259 """
260 iface = self.maybe_dotted(iface)
261 adapter = self.maybe_dotted(adapter)
263 def register(iface=iface):
264 if iface is None:
265 iface = Interface
266 self.registry.registerAdapter(adapter, (iface,), ITraverser)
268 discriminator = ('traverser', iface)
269 intr = self.introspectable(
270 'traversers',
271 discriminator,
272 'traverser for %r' % iface,
273 'traverser',
274 )
275 intr['adapter'] = adapter
276 intr['iface'] = iface
277 self.action(discriminator, register, introspectables=(intr,))
279 @action_method
280 def add_resource_url_adapter(self, adapter, resource_iface=None):
281 """
282 .. versionadded:: 1.3
284 When you add a traverser as described in
285 :ref:`changing_the_traverser`, it's convenient to continue to use the
286 :meth:`pyramid.request.Request.resource_url` API. However, since the
287 way traversal is done may have been modified, the URLs that
288 ``resource_url`` generates by default may be incorrect when resources
289 are returned by a custom traverser.
291 If you've added a traverser, you can change how
292 :meth:`~pyramid.request.Request.resource_url` generates a URL for a
293 specific type of resource by calling this method.
295 The ``adapter`` argument represents a class that implements the
296 :class:`~pyramid.interfaces.IResourceURL` interface. The class
297 constructor should accept two arguments in its constructor (the
298 resource and the request) and the resulting instance should provide
299 the attributes detailed in that interface (``virtual_path`` and
300 ``physical_path``, in particular).
302 The ``resource_iface`` argument represents a class or interface that
303 the resource should possess for this url adapter to be used when
304 :meth:`pyramid.request.Request.resource_url` looks up a resource url
305 adapter. If ``resource_iface`` is not passed, or it is passed as
306 ``None``, the url adapter will be used for every type of resource.
308 See :ref:`changing_resource_url` for more information.
309 """
310 adapter = self.maybe_dotted(adapter)
311 resource_iface = self.maybe_dotted(resource_iface)
313 def register(resource_iface=resource_iface):
314 if resource_iface is None:
315 resource_iface = Interface
316 self.registry.registerAdapter(
317 adapter, (resource_iface, Interface), IResourceURL
318 )
320 discriminator = ('resource url adapter', resource_iface)
321 intr = self.introspectable(
322 'resource url adapters',
323 discriminator,
324 'resource url adapter for resource iface %r' % resource_iface,
325 'resource url adapter',
326 )
327 intr['adapter'] = adapter
328 intr['resource_iface'] = resource_iface
329 self.action(discriminator, register, introspectables=(intr,))
332def eventonly(callee):
333 return takes_one_arg(callee, argname='event')