summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris McDonough <chrism@plope.com>2011-08-06 18:58:26 -0400
committerChris McDonough <chrism@plope.com>2011-08-06 18:58:26 -0400
commit1311321d454ded6226b09f929ebc9e4aa2c12771 (patch)
tree21f29844f8bb92f8ebc644ddf87d1650e962ccf2
parent1dc57d53c54338df467e6c1e8201589eab73022f (diff)
downloadpyramid-1311321d454ded6226b09f929ebc9e4aa2c12771.tar.gz
pyramid-1311321d454ded6226b09f929ebc9e4aa2c12771.tar.bz2
pyramid-1311321d454ded6226b09f929ebc9e4aa2c12771.zip
add tweens module, add docs for ptweens and tweens to hooks
-rw-r--r--docs/api.rst1
-rw-r--r--docs/api/config.rst2
-rw-r--r--docs/api/tweens.rst8
-rw-r--r--docs/narr/commandline.rst75
-rw-r--r--docs/narr/hooks.rst128
-rw-r--r--pyramid/config.py90
-rw-r--r--pyramid/router.py38
-rw-r--r--pyramid/tests/test_config.py12
-rw-r--r--pyramid/tweens.py45
9 files changed, 265 insertions, 134 deletions
diff --git a/docs/api.rst b/docs/api.rst
index a7e1566d3..6ff6e9fb1 100644
--- a/docs/api.rst
+++ b/docs/api.rst
@@ -32,6 +32,7 @@ documentation is organized alphabetically by module name.
api/testing
api/threadlocal
api/traversal
+ api/tweens
api/url
api/view
api/wsgi
diff --git a/docs/api/config.rst b/docs/api/config.rst
index 21e2b828d..1a9bb6ba4 100644
--- a/docs/api/config.rst
+++ b/docs/api/config.rst
@@ -78,7 +78,7 @@
.. automethod:: set_view_mapper
- .. automethod:: add_request_handler
+ .. automethod:: add_tween
.. automethod:: testing_securitypolicy
diff --git a/docs/api/tweens.rst b/docs/api/tweens.rst
new file mode 100644
index 000000000..5fc15cb00
--- /dev/null
+++ b/docs/api/tweens.rst
@@ -0,0 +1,8 @@
+.. _tweens_module:
+
+:mod:`pyramid.tweens`
+---------------------
+
+.. automodule:: pyramid.tweens
+
+ .. autofunction:: excview_tween_factory
diff --git a/docs/narr/commandline.rst b/docs/narr/commandline.rst
index 509af7dd3..6f969196f 100644
--- a/docs/narr/commandline.rst
+++ b/docs/narr/commandline.rst
@@ -297,8 +297,79 @@ application, nothing will be printed to the console when ``paster proutes``
is executed.
.. index::
- single: scripting
- single: bootstrap
+ pair: tweens; printing
+ single: paster ptweens
+ single: ptweens
+
+.. _displaying_tweens:
+
+Displaying "Tweens"
+-------------------
+
+A user can get a representation of both the implicit :term:`tween` ordering
+(the ordering specified by calls to
+:meth:`pyramid.config.Configurator.add_tween`) and the explicit tween
+ordering (specified by the ``pyramid.tweens`` configuration setting)
+orderings using the ``paster ptweens`` command. Handler factories which are
+functions or classes will show up as a standard Python dotted name in the
+``paster ptweens`` output. Tween factories which are *instances* will show
+their module and class name; the Python object id of the instance will be
+appended.
+
+For example, here's the ``paster pwteens`` command run against a system
+configured without any explicit tweens:
+
+.. code-block:: text
+ :linenos:
+
+ [chrism@thinko starter]$ ../bin/paster ptweens development.ini
+ "pyramid.tweens" config value NOT set (implicitly ordered tweens used)
+
+ Position Name
+ -------- ----
+ 0 pyramid.router.excview_tween_factory
+
+Here's the ``paster pwteens`` command run against a system configured *with*
+explicit tweens defined in its ``development.ini`` file:
+
+.. code-block:: text
+ :linenos:
+
+ [chrism@thinko starter]$ ../bin/paster ptweens development.ini
+ "pyramid.tweens" config value set (explicitly ordered tweens used)
+
+ Explicit Tween Chain (used)
+
+ Position Name
+ -------- ----
+ 0 pyramid.tweens.excview_tween_factory
+ 1 starter.tween_factory1
+ 2 starter.tween_factory2
+
+ Implicit Tween Chain (not used)
+
+ Position Name
+ -------- ----
+ 0 pyramid.tweens.excview_tween_factory
+
+Here's the application configuration section of the ``development.ini`` used
+by the above ``paster ptweens`` command which reprorts that the explicit
+tween chain is used:
+
+.. code-block:: text
+ :linenos:
+
+ [app:starter]
+ use = egg:starter
+ pyramid.reload_templates = true
+ pyramid.debug_authorization = false
+ pyramid.debug_notfound = false
+ pyramid.debug_routematch = false
+ pyramid.debug_templates = true
+ pyramid.default_locale_name = en
+ pyramid.tweens = pyramid.tweens.excview_tween_factory
+ starter.tween_factory1
+ starter.tween_factory2
.. _writing_a_script:
diff --git a/docs/narr/hooks.rst b/docs/narr/hooks.rst
index 4f493c854..7290876e6 100644
--- a/docs/narr/hooks.rst
+++ b/docs/narr/hooks.rst
@@ -828,3 +828,131 @@ performed, enabling you to set up the utility in advance:
For full details, please read the `Venusian documentation
<http://docs.repoze.org/venusian>`_.
+.. _registering_tweens:
+
+Registering "Tweens"
+--------------------
+
+.. note:: Tweens are a feature which were added in Pyramid 1.1.1. They are
+ not available in any previous version.
+
+A :term:`tween` (think: "between") is a bit of code that sits between the
+Pyramid router's main request handling function and the upstream WSGI
+component that uses :app:`Pyramid` as its "app". This is a feature that may
+be used by Pyramid framework extensions, to provide, for example,
+Pyramid-specific view timing support bookkeeping code that examines
+exceptions before they are returned to the upstream WSGI application. Tweens
+behave a bit like :term:`WSGI` middleware but they have the benefit of
+running in a context in which they have access to the Pyramid
+:term:`application registry` as well as the Pyramid rendering machinery.
+
+To make use of tweens, you must construct a "tween factory". A tween factory
+must be a callable (or a :term:`dotted Python name` to such a callable) which
+accepts two arguments: ``handler`` and ``registry``. ``handler`` will be the
+either the main Pyramid request handling function or another tween (if more
+than one tween is configured into the request handling chain). ``registry``
+will be the Pyramid :term:`application registry` represented by this
+Configurator. A tween factory must return a tween when it is called.
+
+A tween is a callable which accepts a :term:`request` object and returns a
+two-tuple consisting of a :term:`request` object and a :term:`response`
+object.
+
+Once you've created a tween factory, you can register it using the
+:meth:`pyramid.config.Configurator.add_tween` method.
+
+Here's an example creating a tween factory and registering it:
+
+.. code-block:: python
+ :lineno:
+
+ import time
+ from pyramid.settings import asbool
+ import logging
+
+ log = logging.getLogger(__name__)
+
+ def timing_tween_factory(handler, registry):
+ if asbool(registry.settings.get('do_timing')):
+ # if timing support is enabled, return a wrapper
+ def timing_tween(request):
+ start = time.time()
+ try:
+ request, response = handler(request)
+ finally:
+ end = time.time()
+ log.debug('The request took %s seconds' %
+ (end - start))
+ return request, response
+ return timing_tween
+ # if timing support is not enabled, return the original
+ # handler
+ return handler
+
+ from pyramid.config import Configurator
+
+ config = Configurator()
+ config.add_tween(timing_tween_factory)
+
+The ``request`` argument to a tween will be the request created by Pyramid's
+router when it receives a WSGI request.
+
+If more than one call to :meth:`pyramid.config.Configurator.add_tween` is
+made within a single application configuration, the added tweens will be
+chained together. The first tween factory added will be called with the
+default Pyramid request handler as its ``handler`` argument, the second tween
+factory added will be called with the result of the first tween factory as
+its ``handler`` argument, and so on, ad infinitum. The Pyramid router will
+use the outermost tween produced by this chain (the tween generated by the
+very last tween factory added) as its request handler function.
+
+Pyramid will prevent the same tween factory from being added to the implicit
+tween chain more than once using configuration conflict detection. If you
+wish to add the same tween factory more than once in a configuration, you
+should either: a) use a tween factory that is an instance rather than a
+function or class, b) use a function or class as a tween factory with the
+same logic as the other tween factory it conflicts with but with a different
+``__name__`` attribute or c) call :meth:`pyramid.config.Configurator.commit`
+between calls to :meth:`pyramid.config.Configurator.add_tween`.
+
+By default, the ordering of the chain is controlled entirely by the relative
+ordering of calls to :meth:`pyramid.config.Configurator.add_tween`. However,
+the deploying user can override tween inclusion and ordering entirely in his
+Pyramid configuration using the ``pyramid.tweens`` settings value. When
+used, this settings value will be a list of Python dotted names which imply
+the ordering (and inclusion) of tween factories in the tween chain. For
+example:
+
+.. code-block:: ini
+ :linenos:
+
+ [app:main]
+ use = egg:MyApp
+ pyramid.reload_templates = true
+ pyramid.debug_authorization = false
+ pyramid.debug_notfound = false
+ pyramid.debug_routematch = false
+ pyramid.debug_templates = true
+ pyramid.tweens = pyramid.tweens.excview_tween_factory
+ myapp.my_cool_tween_factory
+
+In the above configuration, calls made during configuration to
+:meth:`pyramid.config.Configurator.add_tween` are ignored, and the user is
+telling the system to use the tween factories he has listed in the
+``pyramid.tweens`` configuration setting (each is a:term:`Python dotted name`
+which points to a tween factory). The *last* tween factory in the
+``pyramid.tweens`` list will be used as the producer of the effective
+:app:`Pyramid` request handling function; it will wrap the tween factory
+declared directly "above" it, ad infinitum.
+
+.. note:: Pyramid's own :term:`exception view` handling logic is implemented
+ as a tween factory function: :func:`pyramid.tweens.excview_tween_factory`.
+ If Pyramid exception view handling is desired, and tween factories are
+ specified via the ``pyramid.tweens`` configuration setting, the
+ :func:`pyramid.tweens.excview_tween_factory` function must be added to the
+ ``pyramid.tweens`` configuration setting list explicitly. If it is not
+ present, Pyramid will not perform exception view handling.
+
+The ``paster ptweens`` command-line utility can be used to report the current
+tween chain used by an application. See :ref:`displaying_tweens`.
+
diff --git a/pyramid/config.py b/pyramid/config.py
index 918e3bce4..ebbea35ea 100644
--- a/pyramid/config.py
+++ b/pyramid/config.py
@@ -920,93 +920,7 @@ class Configurator(object):
context in which they have access to the Pyramid :term:`application
registry` as well as the Pyramid rendering machinery.
- A tween factory (passed as ``tween_factory``) must be a callable (or
- a :term:`dotted Python name` to such a callable) which accepts two
- arguments: ``handler`` and ``registry``. ``handler`` will be the
- either the main Pyramid request handling function or another tween
- (if more than one tween is configured into the request handling
- chain). ``registry`` will be the Pyramid :term:`application
- registry` represented by this Configurator. A tween factory must
- return a tween when it is called.
-
- A tween is a callable which accepts a :term:`request` object and
- returns a two-tuple consisting of a :term:`request` object and a
- :term:`response` object.
-
- Here's an example creating a tween factory and registering it:
-
- .. code-block:: python
-
- import time
- from pyramid.settings import asbool
- import logging
-
- log = logging.getLogger(__name__)
-
- def timing_tween_factory(handler, registry):
- if asbool(registry.settings.get('do_timing')):
- # if timing support is enabled, return a wrapper
- def timing_tween(request):
- start = time.time()
- try:
- request, response = handler(request)
- finally:
- end = time.time()
- log.debug('The request took %s seconds' %
- (end - start))
- return request, response
- return timing_tween
- # if timing support is not enabled, return the original
- # handler
- return handler
-
- config.add_tween(timing_tween_factory)
-
- The ``request`` argument to a tween will be the request created by
- Pyramid's router when it receives a WSGI request.
-
- If more than one call to ``add_tween`` is made within a single
- application configuration, the added tweens will be chained together.
- The first tween factory added will be called with the default Pyramid
- request handler as its ``handler`` argument, the second tween factory
- added will be called with the result of the first tween factory as
- its ``handler`` argument, and so on, ad infinitum. The Pyramid router
- will use the outermost tween produced by this chain (the tween
- generated by the very last tween factory added) as its request
- handler function.
-
- By default, the ordering of the chain is controlled entirely by the
- relative ordering of calls to ``add_tween``. However, the deploying
- user can override tween inclusion and ordering in his Pyramid
- configuration using the ``pyramid.tweens`` settings value. When
- used, this settings value will be a list of Python dotted names which
- imply the ordering (and inclusion) of tween factories in the tween
- chain.
-
- Note that Pyramid's own exception view handling logic is implemented
- as a tween factory: ``pyramid.router.excview_tween_factory``. If
- Pyramid exception view handling is desired, and explicit tween
- factories are specified via ``pyramid.tweens``, this function must be
- added to the ``pyramid.tweens`` setting. If it is not present,
- Pyramid will not perform exception view handling.
-
- Pyramid will prevent the same tween factory from being added to the
- tween chain more than once using configuration conflict detection.
- If you wish to add the same tween factory more than once in a
- configuration, you should either: a) use a tween factory that is an
- instance rather than a function or class, b) use a function or
- class as a tween factory with the same logic as the other tween
- factory it conflicts with but with a different ``__name__``
- attribute or c) call config.commit() between calls to ``add_tween``.
-
- A user can get a representation of both the implicit tween ordering
- (the ordering specified by calls to ``add_tween``) and the explicit
- request handler ordering (specified by the ``pyramid.tweens``
- setting) orderings using the ``paster ptweens`` command. Handler
- factories which are functions or classes will show up as a standard
- Python dotted name in the ``ptweens`` output. Tween factories
- which are *instances* will show their module and class name; the
- Python object id of the instance will be appended.
+ For more information, see :ref:`registering_tweens`.
.. note:: This feature is new as of Pyramid 1.1.1.
"""
@@ -1032,7 +946,7 @@ class Configurator(object):
registry = self.registry
handler_manager = registry.getUtility(ITweens)
handler_manager.add(name, tween_factory, explicit)
- self.action(('tween', name), register)
+ self.action(('tween', name, explicit), register)
@action_method
def add_subscriber(self, subscriber, iface=None):
diff --git a/pyramid/router.py b/pyramid/router.py
index dcf828377..5faafb4bb 100644
--- a/pyramid/router.py
+++ b/pyramid/router.py
@@ -1,10 +1,7 @@
-import sys
-
from zope.interface import implements
from zope.interface import providedBy
from pyramid.interfaces import IDebugLogger
-from pyramid.interfaces import IExceptionViewClassifier
from pyramid.interfaces import IRequest
from pyramid.interfaces import IRootFactory
from pyramid.interfaces import IRouteRequest
@@ -24,6 +21,7 @@ from pyramid.request import Request
from pyramid.threadlocal import manager
from pyramid.traversal import DefaultRootFactory
from pyramid.traversal import ResourceTreeTraverser
+from pyramid.tweens import excview_tween_factory
class Router(object):
implements(IRouter)
@@ -190,37 +188,3 @@ class Router(object):
finally:
manager.pop()
-def excview_tween_factory(handler, registry):
- has_listeners = registry.has_listeners
- adapters = registry.adapters
- notify = registry.notify
-
- def exception_view_handler(request):
- attrs = request.__dict__
- try:
- request, response = handler(request)
- except Exception, exc:
- # WARNING: do not assign the result of sys.exc_info() to a
- # local var here, doing so will cause a leak
- attrs['exc_info'] = sys.exc_info()
- attrs['exception'] = exc
- # clear old generated request.response, if any; it may
- # have been mutated by the view, and its state is not
- # sane (e.g. caching headers)
- if 'response' in attrs:
- del attrs['response']
- request_iface = attrs['request_iface']
- provides = providedBy(exc)
- for_ = (IExceptionViewClassifier, request_iface.combined, provides)
- view_callable = adapters.lookup(for_, IView, default=None)
- if view_callable is None:
- raise
- response = view_callable(exc, request)
- has_listeners and notify(NewResponse(request, response))
- finally:
- attrs['exc_info'] = None
-
- return request, response
-
- return exception_view_handler
-
diff --git a/pyramid/tests/test_config.py b/pyramid/tests/test_config.py
index 8b11bb22a..d73fd7f7d 100644
--- a/pyramid/tests/test_config.py
+++ b/pyramid/tests/test_config.py
@@ -630,7 +630,7 @@ pyramid.tests.test_config.dummy_include2""",
def test_add_tweens_names_distinct(self):
from pyramid.interfaces import ITweens
- from pyramid.router import excview_tween_factory
+ from pyramid.tweens import excview_tween_factory
def factory1(handler, registry): return handler
def factory2(handler, registry): return handler
config = self._makeOne()
@@ -640,13 +640,13 @@ pyramid.tests.test_config.dummy_include2""",
tweens = config.registry.queryUtility(ITweens)
self.assertEqual(
tweens.implicit,
- [('pyramid.router.excview_tween_factory', excview_tween_factory),
+ [('pyramid.tweens.excview_tween_factory', excview_tween_factory),
('pyramid.tests.test_config.factory1', factory1),
('pyramid.tests.test_config.factory2', factory2)])
def test_add_tween_dottedname(self):
from pyramid.interfaces import ITweens
- from pyramid.router import excview_tween_factory
+ from pyramid.tweens import excview_tween_factory
config = self._makeOne()
config.add_tween('pyramid.tests.test_config.dummy_tween_factory')
config.commit()
@@ -654,14 +654,14 @@ pyramid.tests.test_config.dummy_include2""",
self.assertEqual(
tweens.implicit,
[
- ('pyramid.router.excview_tween_factory', excview_tween_factory),
+ ('pyramid.tweens.excview_tween_factory', excview_tween_factory),
('pyramid.tests.test_config.dummy_tween_factory',
dummy_tween_factory)
])
def test_add_tween_instance(self):
from pyramid.interfaces import ITweens
- from pyramid.router import excview_tween_factory
+ from pyramid.tweens import excview_tween_factory
class ATween(object): pass
atween = ATween()
config = self._makeOne()
@@ -671,7 +671,7 @@ pyramid.tests.test_config.dummy_include2""",
self.assertEqual(len(tweens.implicit), 2)
self.assertEqual(
tweens.implicit[0],
- ('pyramid.router.excview_tween_factory', excview_tween_factory))
+ ('pyramid.tweens.excview_tween_factory', excview_tween_factory))
self.assertTrue(
tweens.implicit[1][0].startswith('pyramid.tests.test_config.ATween.'))
self.assertEqual(tweens.implicit[1][1], atween)
diff --git a/pyramid/tweens.py b/pyramid/tweens.py
new file mode 100644
index 000000000..209768198
--- /dev/null
+++ b/pyramid/tweens.py
@@ -0,0 +1,45 @@
+import sys
+from pyramid.interfaces import IExceptionViewClassifier
+from pyramid.interfaces import IView
+
+from pyramid.events import NewResponse
+from zope.interface import providedBy
+
+def excview_tween_factory(handler, registry):
+ """ A :term:`tween` factory which produces a tween that catches an
+ exception raised by downstream tweens (or the main Pyramid request
+ handler) and, if possible, converts it into a Response using an
+ :term:`exception view`."""
+ has_listeners = registry.has_listeners
+ adapters = registry.adapters
+ notify = registry.notify
+
+ def excview_tween(request):
+ attrs = request.__dict__
+ try:
+ request, response = handler(request)
+ except Exception, exc:
+ # WARNING: do not assign the result of sys.exc_info() to a
+ # local var here, doing so will cause a leak
+ attrs['exc_info'] = sys.exc_info()
+ attrs['exception'] = exc
+ # clear old generated request.response, if any; it may
+ # have been mutated by the view, and its state is not
+ # sane (e.g. caching headers)
+ if 'response' in attrs:
+ del attrs['response']
+ request_iface = attrs['request_iface']
+ provides = providedBy(exc)
+ for_ = (IExceptionViewClassifier, request_iface.combined, provides)
+ view_callable = adapters.lookup(for_, IView, default=None)
+ if view_callable is None:
+ raise
+ response = view_callable(exc, request)
+ has_listeners and notify(NewResponse(request, response))
+ finally:
+ attrs['exc_info'] = None
+
+ return request, response
+
+ return excview_tween
+