summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris McDonough <chrism@plope.com>2011-08-05 11:57:26 -0400
committerChris McDonough <chrism@plope.com>2011-08-05 11:57:26 -0400
commite25fae8ebdc5b77ec1578186d11653430835f76e (patch)
tree648beec1891812fcb2bb25c956f3f99052071f91
parent2df6bee0cf734187b70f3362affc63d04809c581 (diff)
downloadpyramid-e25fae8ebdc5b77ec1578186d11653430835f76e.tar.gz
pyramid-e25fae8ebdc5b77ec1578186d11653430835f76e.tar.bz2
pyramid-e25fae8ebdc5b77ec1578186d11653430835f76e.zip
add docs, return only items in the list
-rw-r--r--pyramid/config.py116
-rw-r--r--pyramid/tests/test_util.py4
-rw-r--r--pyramid/util.py2
3 files changed, 105 insertions, 17 deletions
diff --git a/pyramid/config.py b/pyramid/config.py
index 159422c22..e9f2dd2a5 100644
--- a/pyramid/config.py
+++ b/pyramid/config.py
@@ -898,17 +898,18 @@ class Configurator(object):
return self._derive_view(view, attr=attr, renderer=renderer)
@action_method
- def add_request_handler(self, handler_factory, name):
+ def add_request_handler(self, handler_factory, before=None,
+ after=None, name=None):
"""
Add a request handler factory. A request handler factory is used to
- wrap the Pyramid router's primary request handling function. This is
+ wrap the Pyramid router's main request handling function. This is
a feature that may be used by framework extensions, to provide, for
example, view timing support and as a convenient place to hang
bookkeeping code that examines exceptions before they are returned to
the server.
A request handler factory (passed as ``handler_factory``) must be a
- callable (or a :term:`dotted Python name` to a callable) which
+ callable (or a :term:`dotted Python name` to such a callable) which
accepts two arguments: ``handler`` and ``registry``. ``handler``
will be the request handler being wrapped. ``registry`` will be the
Pyramid :term:`application registry` represented by this
@@ -940,29 +941,116 @@ class Configurator(object):
# if timing support is not enabled, return the original handler
return handler
- config.add_request_handler(timing_handler_factory, 'timing')
+ config.add_request_handler(timing_handler_factory)
The ``request`` argument to the handler will be the request created
by Pyramid's router when it receives a WSGI request.
If more than one request handler factory is registered into a single
- configuration, the request handlers will be chained together. The
- first request handler factory added (in code execution order) will be
- called with the default Pyramid request handler, the second handler
- factory added will be called with the result of the first handler
- factory, ad infinitum. The Pyramid router will use the outermost
- wrapper in this chain (which is a bit like a WSGI middleware
- "pipeline") as its handler function.
-
- The ``name`` argument to this function is required. The name is used
+ configuration, request handlers will be chained together. The first
+ request handler factory in the chain will be called with the default
+ Pyramid request handler, the second handler factory added will be
+ called with the result of the first handler factory, ad
+ infinitum. The Pyramid router will use the outermost wrapper in this
+ chain (which is a bit like a WSGI middleware "pipeline") as its
+ handler function.
+
+ The deploying user can specify request handler inclusion and ordering
+ in his Pyramid configuration using the ``pyramid.handlers``
+ configuration value. However, he is not required to do so. Instead,
+ he can rely on hinting information provided by you (the framework
+ extender) which provides some clues about where in his request
+ handler pipeline a request handler should wind up. This is achieved
+ by using the ``before`` and/or ``after`` arguments to
+ ``add_request_handler``.
+
+ The ``before`` and ``after`` arguments provide hints to Pyramid about
+ which position in the handler chain this handler must assume when the
+ deploying user has not defined a ``pyramid.handlers`` configuration
+ value. ``before`` and ``after`` are relative to request *ingress*
+ order. Therefore, ``before`` means 'closer to
+ request ingress' and ``after`` means 'closer to Pyramid's main request
+ handler'.
+
+ ``before`` or ``after`` may be either the name of another handler,
+ the name ``main`` representing Pyramid's main request handler, the
+ name ``ingress`` representing the request ingress point, or a sequence
+ of handler names (the sequence can itself include ``main`` or
+ ``ingress``). For example, if you'd like to hint that this handler
+ should always come before the
+ ``pyramid.router.exception_view_handler_factory`` handler but before
+ the ``pyramid_retry.retry_handler_factory`` handler:
+
+ .. code-block:: python
+ :linenos:
+
+ config.add_request_handler(
+ 'mypkg.handler_factory',
+ before='pyramid.router.exception_view_handler_factory',
+ after='pyramid_retry.retry_handler_factory')
+
+ Or you might rather hint that a handler should always come before the
+ ``main`` handler but after retry and transaction management handlers:
+
+ .. code-block:: python
+ :linenos:
+
+ config.add_handler('mypkg.handler', before='main',
+ after=('pyramid_retry.retry_handler_factory',
+ 'pyramid_tm.tm_handler_factory')
+
+ Or you might hint that a handler should come after a browser
+ id-generating handler, but you don't want to hint that it comes
+ before anything:
+
+ .. code-block:: python
+ :linenos:
+
+ config.add_handler(
+ 'mypkg.handler',
+ after='pyramid_browserid.browserid_handler_factory')
+
+ The names provided to ``before`` and ``after`` don't need to
+ represent handlers that actually exist in the current deployment.
+ For example, if you say
+ ``before='pyramid_retry.retry_handler_factory'`` and the
+ ``pyramid_retry.retry_handler_factory`` handler is not configured
+ into the current deployment at all, no error will be raised when the
+ add_request_handler statement is executed; instead, the hint will be
+ effectively ignored.
+
+ It is an error to specify an ``after`` value of ``main`` or a
+ ``before`` value of ``ingress``.
+
+ Pyramid does its best to configure a sensible handlers pipeline when
+ the user does not provide a ``pyramid.handlers`` configuration
+ setting in his Pyramid configuration, even in the face of conflicting
+ hints, by using a topological sort on all handlers configured into
+ the system using ``before`` and ``after`` hinting as input to the
+ sort. However, ``before`` and ``after`` hinting are ignored
+ completely when the deploying user has specified an explicit
+ ``pyramid.handlers`` configuration value; this value specifies an
+ unambigous inclusion and ordering for handlers in a given deployment;
+ when it is provided, the topological sort that uses the ``before``
+ and ``after`` hint information is never performed.
+
+ A user can get both the effective and hinted handler ordering by
+ using the ``paster phandlers`` command.
+
+ The ``name`` argument to this function is optional. The name is used
as a key for conflict detection. No two request handler factories
may share the same name in the same configuration (unless
:ref:`automatic_conflict_resolution` is able to resolve the conflict
- or this is an autocommitting configurator).
+ or this is an autocommitting configurator). By default, a name will
+ be the :term:`Python dotted name` of the object passed as
+ ``handler_factory``.
.. note:: This feature is new as of Pyramid 1.1.1.
"""
handler_factory = self.maybe_dotted(handler_factory)
+ if name is None:
+ name = '.'.join(
+ (handler_factory.__module__, handler_factory.__name__))
def register():
registry = self.registry
registry.registerUtility(handler_factory, IRequestHandlerFactory,
diff --git a/pyramid/tests/test_util.py b/pyramid/tests/test_util.py
index 2dd2dbd7f..62477ed9f 100644
--- a/pyramid/tests/test_util.py
+++ b/pyramid/tests/test_util.py
@@ -277,11 +277,11 @@ class Test_topological_sort(unittest.TestCase):
def test_partial_order_missing_partial_a(self):
result = self._callFUT(['a', 'b', 'c', 'd'], [('d', 'c'), ('f', 'c')])
- self.assertEqual(result, ['a', 'b', 'd', 'f', 'c'])
+ self.assertEqual(result, ['a', 'b', 'd', 'c'])
def test_partial_order_missing_partial_b(self):
result = self._callFUT(['a', 'b', 'c', 'd'], [('d', 'c'), ('c', 'f')])
- self.assertEqual(result, ['a', 'b', 'd', 'c', 'f'])
+ self.assertEqual(result, ['a', 'b', 'd', 'c'])
def test_cycle_direct(self):
from pyramid.util import CyclicDependencyError
diff --git a/pyramid/util.py b/pyramid/util.py
index fa885b81e..5de8aa37a 100644
--- a/pyramid/util.py
+++ b/pyramid/util.py
@@ -268,7 +268,7 @@ def topological_sort(items, partial_order, ignore_missing_partials=True):
cycledeps[k] = v[1:]
raise CyclicDependencyError(cycledeps)
- return sorted
+ return [ x for x in sorted if x in items ]
class CyclicDependencyError(ConfigurationError):
def __init__(self, cycles):