from webob import Response as WebobResponse from functools import update_wrapper from zope.interface import Interface from pyramid.interfaces import IResponse, ITraverser, IResourceURL from pyramid.util import takes_one_arg from pyramid.config.actions import action_method class AdaptersConfiguratorMixin(object): @action_method def add_subscriber(self, subscriber, iface=None, **predicates): """Add an event :term:`subscriber` for the event stream implied by the supplied ``iface`` interface. The ``subscriber`` argument represents a callable object (or a :term:`dotted Python name` which identifies a callable); it will be called with a single object ``event`` whenever :app:`Pyramid` emits an :term:`event` associated with the ``iface``, which may be an :term:`interface` or a class or a :term:`dotted Python name` to a global object representing an interface or a class. Using the default ``iface`` value, ``None`` will cause the subscriber to be registered for all event types. See :ref:`events_chapter` for more information about events and subscribers. Any number of predicate keyword arguments may be passed in ``**predicates``. Each predicate named will narrow the set of circumstances in which the subscriber will be invoked. Each named predicate must have been registered via :meth:`pyramid.config.Configurator.add_subscriber_predicate` before it can be used. See :ref:`subscriber_predicates` for more information. .. versionadded:: 1.4 The ``**predicates`` argument. """ dotted = self.maybe_dotted subscriber, iface = dotted(subscriber), dotted(iface) if iface is None: iface = (Interface,) if not isinstance(iface, (tuple, list)): iface = (iface,) def register(): predlist = self.get_predlist('subscriber') order, preds, phash = predlist.make(self, **predicates) derived_predicates = [self._derive_predicate(p) for p in preds] derived_subscriber = self._derive_subscriber( subscriber, derived_predicates ) intr.update( { 'phash': phash, 'order': order, 'predicates': preds, 'derived_predicates': derived_predicates, 'derived_subscriber': derived_subscriber, } ) self.registry.registerHandler(derived_subscriber, iface) intr = self.introspectable( 'subscribers', id(subscriber), self.object_description(subscriber), 'subscriber', ) intr['subscriber'] = subscriber intr['interfaces'] = iface self.action(None, register, introspectables=(intr,)) return subscriber def _derive_predicate(self, predicate): derived_predicate = predicate if eventonly(predicate): def derived_predicate(*arg): return predicate(arg[0]) # seems pointless to try to fix __doc__, __module__, etc as # predicate will invariably be an instance return derived_predicate def _derive_subscriber(self, subscriber, predicates): derived_subscriber = subscriber if eventonly(subscriber): def derived_subscriber(*arg): return subscriber(arg[0]) if hasattr(subscriber, '__name__'): update_wrapper(derived_subscriber, subscriber) if not predicates: return derived_subscriber def subscriber_wrapper(*arg): # We need to accept *arg and pass it along because zope subscribers # are designed awkwardly. Notification via # registry.adapter.subscribers will always call an associated # subscriber with all of the objects involved in the subscription # lookup, despite the fact that the event sender always has the # option to attach those objects to the event object itself, and # almost always does. # # The "eventonly" jazz sprinkled in this function and related # functions allows users to define subscribers and predicates which # accept only an event argument without needing to accept the rest # of the adaptation arguments. Had I been smart enough early on to # use .subscriptions to find the subscriber functions in order to # call them manually with a single "event" argument instead of # relying on .subscribers to both find and call them implicitly # with all args, the eventonly hack would not have been required. # At this point, though, using .subscriptions and manual execution # is not possible without badly breaking backwards compatibility. if all((predicate(*arg) for predicate in predicates)): return derived_subscriber(*arg) if hasattr(subscriber, '__name__'): update_wrapper(subscriber_wrapper, subscriber) return subscriber_wrapper @action_method def add_subscriber_predicate( self, name, factory, weighs_more_than=None, weighs_less_than=None ): """ .. versionadded:: 1.4 Adds a subscriber predicate factory. The associated subscriber predicate can later be named as a keyword argument to :meth:`pyramid.config.Configurator.add_subscriber` in the ``**predicates`` anonymous keyword argument dictionary. ``name`` should be the name of the predicate. It must be a valid Python identifier (it will be used as a ``**predicates`` keyword argument to :meth:`~pyramid.config.Configurator.add_subscriber`). ``factory`` should be a :term:`predicate factory` or :term:`dotted Python name` which refers to a predicate factory. See :ref:`subscriber_predicates` for more information. """ self._add_predicate( 'subscriber', name, factory, weighs_more_than=weighs_more_than, weighs_less_than=weighs_less_than, ) @action_method def add_response_adapter(self, adapter, type_or_iface): """ When an object of type (or interface) ``type_or_iface`` is returned from a view callable, Pyramid will use the adapter ``adapter`` to convert it into an object which implements the :class:`pyramid.interfaces.IResponse` interface. If ``adapter`` is None, an object returned of type (or interface) ``type_or_iface`` will itself be used as a response object. ``adapter`` and ``type_or_interface`` may be Python objects or strings representing dotted names to importable Python global objects. See :ref:`using_iresponse` for more information.""" adapter = self.maybe_dotted(adapter) type_or_iface = self.maybe_dotted(type_or_iface) def register(): reg = self.registry if adapter is None: reg.registerSelfAdapter((type_or_iface,), IResponse) else: reg.registerAdapter(adapter, (type_or_iface,), IResponse) discriminator = (IResponse, type_or_iface) intr = self.introspectable( 'response adapters', discriminator, self.object_description(adapter), 'response adapter', ) intr['adapter'] = adapter intr['type'] = type_or_iface self.action(discriminator, register, introspectables=(intr,)) def add_default_response_adapters(self): # cope with WebOb response objects that aren't decorated with IResponse self.add_response_adapter(None, WebobResponse) @action_method def add_traverser(self, adapter, iface=None): """ The superdefault :term:`traversal` algorithm that :app:`Pyramid` uses is explained in :ref:`traversal_algorithm`. Though it is rarely necessary, this default algorithm can be swapped out selectively for a different traversal pattern via configuration. The section entitled :ref:`changing_the_traverser` details how to create a traverser class. For example, to override the superdefault traverser used by Pyramid, you might do something like this: .. code-block:: python from myapp.traversal import MyCustomTraverser config.add_traverser(MyCustomTraverser) This would cause the Pyramid superdefault traverser to never be used; instead all traversal would be done using your ``MyCustomTraverser`` class, no matter which object was returned by the :term:`root factory` of this application. Note that we passed no arguments to the ``iface`` keyword parameter. The default value of ``iface``, ``None`` represents that the registered traverser should be used when no other more specific traverser is available for the object returned by the root factory. However, more than one traversal algorithm can be active at the same time. The traverser used can depend on the result of the :term:`root factory`. For instance, if your root factory returns more than one type of object conditionally, you could claim that an alternate traverser adapter should be used against one particular class or interface returned by that root factory. When the root factory returned an object that implemented that class or interface, a custom traverser would be used. Otherwise, the default traverser would be used. The ``iface`` argument represents the class of the object that the root factory might return or an :term:`interface` that the object might implement. To use a particular traverser only when the root factory returns a particular class: .. code-block:: python config.add_traverser(MyCustomTraverser, MyRootClass) When more than one traverser is active, the "most specific" traverser will be used (the one that matches the class or interface of the value returned by the root factory most closely). Note that either ``adapter`` or ``iface`` can be a :term:`dotted Python name` or a Python object. See :ref:`changing_the_traverser` for more information. """ iface = self.maybe_dotted(iface) adapter = self.maybe_dotted(adapter) def register(iface=iface): if iface is None: iface = Interface self.registry.registerAdapter(adapter, (iface,), ITraverser) discriminator = ('traverser', iface) intr = self.introspectable( 'traversers', discriminator, 'traverser for %r' % iface, 'traverser', ) intr['adapter'] = adapter intr['iface'] = iface self.action(discriminator, register, introspectables=(intr,)) @action_method def add_resource_url_adapter(self, adapter, resource_iface=None): """ .. versionadded:: 1.3 When you add a traverser as described in :ref:`changing_the_traverser`, it's convenient to continue to use the :meth:`pyramid.request.Request.resource_url` API. However, since the way traversal is done may have been modified, the URLs that ``resource_url`` generates by default may be incorrect when resources are returned by a custom traverser. If you've added a traverser, you can change how :meth:`~pyramid.request.Request.resource_url` generates a URL for a specific type of resource by calling this method. The ``adapter`` argument represents a class that implements the :class:`~pyramid.interfaces.IResourceURL` interface. The class constructor should accept two arguments in its constructor (the resource and the request) and the resulting instance should provide the attributes detailed in that interface (``virtual_path`` and ``physical_path``, in particular). The ``resource_iface`` argument represents a class or interface that the resource should possess for this url adapter to be used when :meth:`pyramid.request.Request.resource_url` looks up a resource url adapter. If ``resource_iface`` is not passed, or it is passed as ``None``, the url adapter will be used for every type of resource. See :ref:`changing_resource_url` for more information. """ adapter = self.maybe_dotted(adapter) resource_iface = self.maybe_dotted(resource_iface) def register(resource_iface=resource_iface): if resource_iface is None: resource_iface = Interface self.registry.registerAdapter( adapter, (resource_iface, Interface), IResourceURL ) discriminator = ('resource url adapter', resource_iface) intr = self.introspectable( 'resource url adapters', discriminator, 'resource url adapter for resource iface %r' % resource_iface, 'resource url adapter', ) intr['adapter'] = adapter intr['resource_iface'] = resource_iface self.action(discriminator, register, introspectables=(intr,)) def eventonly(callee): return takes_one_arg(callee, argname='event')