summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris McDonough <chrism@plope.com>2011-08-06 17:38:37 -0400
committerChris McDonough <chrism@plope.com>2011-08-06 17:38:37 -0400
commitb4843bc087524320460fb3fca9d33688cafa0dbb (patch)
treeb9bf55f14ca474b62cd49119972ef570dec0e320
parent4f321df3ae4f09445555b653ef421f6b6fd97069 (diff)
downloadpyramid-b4843bc087524320460fb3fca9d33688cafa0dbb.tar.gz
pyramid-b4843bc087524320460fb3fca9d33688cafa0dbb.tar.bz2
pyramid-b4843bc087524320460fb3fca9d33688cafa0dbb.zip
requesthandlers->tweens; add ptweens command
-rw-r--r--pyramid/config.py183
-rw-r--r--pyramid/interfaces.py4
-rw-r--r--pyramid/paster.py66
-rw-r--r--pyramid/router.py14
-rw-r--r--pyramid/tests/test_config.py4
-rw-r--r--pyramid/tests/test_router.py24
-rw-r--r--setup.py1
7 files changed, 185 insertions, 111 deletions
diff --git a/pyramid/config.py b/pyramid/config.py
index 6a7b4d328..259be7688 100644
--- a/pyramid/config.py
+++ b/pyramid/config.py
@@ -38,7 +38,7 @@ from pyramid.interfaces import IRendererFactory
from pyramid.interfaces import IRendererGlobalsFactory
from pyramid.interfaces import IRequest
from pyramid.interfaces import IRequestFactory
-from pyramid.interfaces import IRequestHandlers
+from pyramid.interfaces import ITweens
from pyramid.interfaces import IResponse
from pyramid.interfaces import IRootFactory
from pyramid.interfaces import IRouteRequest
@@ -719,12 +719,11 @@ class Configurator(object):
if settings:
includes = settings.get('pyramid.include', '')
includes = [x.strip() for x in includes.splitlines()]
- expl_handler_factories = settings.get('pyramid.request_handlers','')
- expl_handler_factories = [x.strip() for x in
- expl_handler_factories.splitlines()]
+ expl_tweens = settings.get('pyramid.tweens','')
+ expl_tweens = [x.strip() for x in expl_tweens.splitlines()]
else:
includes = []
- expl_handler_factories = []
+ expl_tweens = []
registry = self.registry
self._fix_registry()
self._set_settings(settings)
@@ -735,10 +734,10 @@ class Configurator(object):
from webob.exc import WSGIHTTPException as WebobWSGIHTTPException
registry.registerSelfAdapter((WebobResponse,), IResponse)
# add a handler manager
- handler_manager = RequestHandlers()
- registry.registerUtility(handler_manager, IRequestHandlers)
- self._add_request_handler('pyramid.router.exc_view_handler_factory',
- explicit=False)
+ tweens = Tweens()
+ registry.registerUtility(tweens, ITweens)
+ self._add_tween('pyramid.router.exc_view_tween_factory',
+ explicit=False)
if debug_logger is None:
debug_logger = logging.getLogger(self.package_name)
@@ -785,8 +784,8 @@ class Configurator(object):
if default_view_mapper is not None:
self.set_view_mapper(default_view_mapper)
self.commit()
- for factory in expl_handler_factories:
- self._add_request_handler(factory, explicit=True)
+ for factory in expl_tweens:
+ self._add_tween(factory, explicit=True)
for inc in includes:
self.include(inc)
@@ -908,31 +907,32 @@ class Configurator(object):
return self._derive_view(view, attr=attr, renderer=renderer)
@action_method
- def add_request_handler(self, handler_factory):
+ def add_tween(self, tween_factory):
"""
- Add a request handler factory. A request handler factory is used to
- 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
+ Add a 'tween factory'. A '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 framework extensions,
+ to provide, for example, Pyramid-specific view timing support
bookkeeping code that examines exceptions before they are returned to
- the WSGI server. Request handlers behave a bit like :mod:`WSGI`
+ the upstream WSGI application. Tweens behave a bit like :mod:`WSGI`
'middleware' but they have the benefit of running in a context in
- which they have access tot he Pyramid :term:`application registry` as
+ which they have access to the Pyramid :term:`application registry` as
well as the Pyramid rendering machinery.
- A request handler factory (passed as ``handler_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 request handler being wrapped. ``registry`` will be the
- Pyramid :term:`application registry` represented by this
- Configurator. A request handler factory must return a request
- handler when it is called.
+ 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 request handler accepts a :term:`request` object and returns a
- :term:`response` object.
+ A tween 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 handler factory and registering the
- handler factory:
+ Here's an example creating a tween factory and registering it:
.. code-block:: python
@@ -942,83 +942,92 @@ class Configurator(object):
log = logging.getLogger(__name__)
- def timing_handler_factory(handler, registry):
+ def timing_tween_factory(handler, registry):
if asbool(registry.settings.get('do_timing')):
# if timing support is enabled, return a wrapper
- def timing_handler(request):
+ def timing_tween(request):
start = time.time()
try:
- response = handler(request)
+ request, response = handler(request)
finally:
end = time.time()
log.debug('The request took %s seconds' %
(end - start))
- return response
- return timing_handler
+ return request, response
+ return timing_tween
# if timing support is not enabled, return the original
# handler
return handler
- config.add_request_handler(timing_handler_factory)
+ config.add_tween(timing_tween_factory)
- The ``request`` argument to the handler will be the request created
- by Pyramid's router when it receives a WSGI request.
+ 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_request_handler`` is made within a
- single application configuration, request handlers will be chained
- together. The first request handler factory added will be called
- with the default Pyramid request handler as its ``handler`` argument,
- the second handler factory added will be called with the result of
- the first handler factory, and so on, ad infinitum. The Pyramid
- router will use the outermost handler produced by this chain (the
- very last handler added) as its handler function.
+ 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 very last
+ tween added) as its request handler function.
By default, the ordering of the chain is controlled entirely by the
- relative ordering of calls to ``add_request_handler``. However, the
- deploying user can override request handler inclusion and ordering in
- his Pyramid configuration using the ``pyramid.request_handlers``
- settings value. When used, this settings value should be a list of
- Python dotted names which imply the ordering (and inclusion) of
- handler factories in the request handler chain.
-
- Pyramid will prevent the same request handler factory from being
- added to the request handling chain more than once using
- configuration conflict detection. If you wish to add the same
- handler factory more than once in a configuration, you should either:
- a) use a handler that is an *instance* rather than a function or
- class, or b) use a function or class with the same logic as the other
- it conflicts with but with a different ``__name__`` attribute.
-
- A user can get a representation of both the implicit request handler
- ordering (the ordering specified by calls to ``add_request_handler``)
- and the explicit request handler ordering (specified by the
- ``pyramid.request_handlers`` setting) orderings using the ``paster
- phandlers`` command. Handler factories which are functions or
- classes will show up as a standard Python dotted name in the
- ``phandlers`` output. Handler factories which are *instances* will
- show their module and class name; the Python object id of the
- instance will be appended.
+ 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 should be a list of Python dotted names
+ which imply the ordering (and inclusion) of tween factories in the
+ tween chain.
+
+ 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, or 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.
+
+ 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.
.. note:: This feature is new as of Pyramid 1.1.1.
"""
- return self._add_request_handler(handler_factory, explicit=False)
+ return self._add_tween(tween_factory, explicit=False)
- def _add_request_handler(self, handler_factory, explicit):
- handler_factory = self.maybe_dotted(handler_factory)
- if hasattr(handler_factory, '__name__'):
+ # XXX temporary bw compat for debugtoolbar
+ def add_request_handler(self, factory, name):
+ return self._add_tween(factory, explicit=False)
+
+ def _add_tween(self, tween_factory, explicit):
+ tween_factory = self.maybe_dotted(tween_factory)
+ if (hasattr(tween_factory, '__name__') and
+ hasattr(tween_factory, '__module__')):
# function or class
- name = '.'.join((handler_factory.__module__,
- handler_factory.__name__))
- else:
+ name = '.'.join([tween_factory.__module__,
+ tween_factory.__name__])
+ elif hasattr(tween_factory, '__module__'):
# instance
- name = '.'.join(handler_factory.__module__,
- handler_factory.__class__.__name__,
- str(id(handler_factory)))
+ name = '.'.join([tween_factory.__module__,
+ tween_factory.__class__.__name__,
+ str(id(tween_factory))])
+ else:
+ raise ConfigurationError(
+ 'A tween factory must be a class, an instance, or a function; '
+ '%s is not a suitable tween factory' % tween_factory)
def register():
registry = self.registry
- handler_manager = registry.getUtility(IRequestHandlers)
- handler_manager.add(name, handler_factory, explicit)
- self.action(('request_handler', name), register)
+ handler_manager = registry.getUtility(ITweens)
+ handler_manager.add(name, tween_factory, explicit)
+ self.action(('tween', name), register)
@action_method
def add_subscriber(self, subscriber, iface=None):
@@ -3455,8 +3464,8 @@ def isexception(o):
global_registries = WeakOrderedSet()
-class RequestHandlers(object):
- implements(IRequestHandlers)
+class Tweens(object):
+ implements(ITweens)
def __init__(self):
self.explicit = []
self.implicit = []
@@ -3468,10 +3477,10 @@ class RequestHandlers(object):
self.implicit.append((name, factory))
def __call__(self, handler, registry):
- handler_factories = self.implicit
+ factories = self.implicit
if self.explicit:
- handler_factories = self.explicit
- for name, factory in handler_factories:
+ factories = self.explicit
+ for name, factory in factories:
handler = factory(handler, registry)
return handler
diff --git a/pyramid/interfaces.py b/pyramid/interfaces.py
index c4bacd46d..d97632018 100644
--- a/pyramid/interfaces.py
+++ b/pyramid/interfaces.py
@@ -446,9 +446,9 @@ class IMultiDict(Interface): # docs-only interface
class IRequest(Interface):
""" Request type interface attached to all request objects """
-class IRequestHandlers(Interface):
+class ITweens(Interface):
""" Marker interface for utility registration representing the ordered
- set of a configuration's request handler factories"""
+ set of a configuration's tween factories"""
class IRequestHandler(Interface):
""" """
diff --git a/pyramid/paster.py b/pyramid/paster.py
index 3143fa91e..54f5a51a6 100644
--- a/pyramid/paster.py
+++ b/pyramid/paster.py
@@ -9,8 +9,8 @@ from paste.deploy import loadapp
from paste.script.command import Command
from pyramid.interfaces import IMultiView
+from pyramid.interfaces import ITweens
-from pyramid.scripting import get_root
from pyramid.scripting import prepare
from pyramid.util import DottedNameResolver
@@ -534,3 +534,67 @@ class PViewsCommand(PCommand):
self.out(" Not found.")
self.out('')
+
+class PTweensCommand(PCommand):
+ """Print all implicit and explicit :term:`tween` objects used by a
+ Pyramid application. The handler output includes whether the system is
+ using an explicit tweens ordering (will be true when the
+ ``pyramid.tweens`` setting is used) or an implicit tweens ordering (will
+ be true when the ``pyramid.tweens`` setting is *not* used).
+
+ This command accepts one positional argument:
+
+ ``config_uri`` -- specifies the PasteDeploy config file to use for the
+ interactive shell. The format is ``inifile#name``. If the name is left
+ off, ``main`` will be assumed.
+
+ Example::
+
+ $ paster ptweens myapp.ini#main
+
+ """
+ summary = "Print all tweens related to a Pyramid application"
+ min_args = 1
+ max_args = 1
+ stdout = sys.stdout
+
+ parser = Command.standard_parser(simulate=True)
+
+ def _get_tweens(self, registry):
+ from pyramid.config import Configurator
+ config = Configurator(registry = registry)
+ return config.registry.queryUtility(ITweens)
+
+ def out(self, msg): # pragma: no cover
+ print msg
+
+ def command(self):
+ config_uri = self.args[0]
+ env = self.bootstrap[0](config_uri)
+ registry = env['registry']
+ tweens = self._get_tweens(registry)
+ if tweens is not None:
+ ordering = []
+ if tweens.explicit:
+ self.out('"pyramid.tweens" config value set '
+ '(explicitly ordered tweens used)')
+ self.out('')
+ ordering.append((tweens.explicit,
+ 'Explicit Tween Chain (used)'))
+ ordering.append((tweens.implicit,
+ 'Implicit Tween Chain (not used)'))
+ else:
+ self.out('"pyramid.tweens" config value NOT set '
+ '(implicitly ordered tweens used)')
+ self.out('')
+ ordering.append((tweens.implicit, ''))
+ for L, title in ordering:
+ if title:
+ self.out(title)
+ self.out('')
+ fmt = '%-8s %-30s'
+ self.out(fmt % ('Position', 'Name'))
+ self.out(fmt % ('-'*len('Position'), '-'*len('Name')))
+ for pos, (name, item) in enumerate(L):
+ self.out(fmt % (pos, name))
+ self.out('')
diff --git a/pyramid/router.py b/pyramid/router.py
index d8f5a08da..8cac733fb 100644
--- a/pyramid/router.py
+++ b/pyramid/router.py
@@ -14,7 +14,7 @@ from pyramid.interfaces import IRoutesMapper
from pyramid.interfaces import ITraverser
from pyramid.interfaces import IView
from pyramid.interfaces import IViewClassifier
-from pyramid.interfaces import IRequestHandlers
+from pyramid.interfaces import ITweens
from pyramid.events import ContextFound
from pyramid.events import NewRequest
@@ -39,12 +39,12 @@ class Router(object):
self.root_factory = q(IRootFactory, default=DefaultRootFactory)
self.routes_mapper = q(IRoutesMapper)
self.request_factory = q(IRequestFactory, default=Request)
- handler_manager = q(IRequestHandlers)
- if handler_manager is None:
- self.handle_request = exc_view_handler_factory(self.handle_request,
- registry)
+ tweens = q(ITweens)
+ if tweens is None:
+ self.handle_request = exc_view_tween_factory(self.handle_request,
+ registry)
else:
- self.handle_request = handler_manager(self.handle_request, registry)
+ self.handle_request = tweens(self.handle_request, registry)
self.root_policy = self.root_factory # b/w compat
self.registry = registry
@@ -190,7 +190,7 @@ class Router(object):
finally:
manager.pop()
-def exc_view_handler_factory(handler, registry):
+def exc_view_tween_factory(handler, registry):
has_listeners = registry.has_listeners
adapters = registry.adapters
notify = registry.notify
diff --git a/pyramid/tests/test_config.py b/pyramid/tests/test_config.py
index ebc3f85cc..ef2bb24d7 100644
--- a/pyramid/tests/test_config.py
+++ b/pyramid/tests/test_config.py
@@ -336,7 +336,7 @@ class ConfiguratorTests(unittest.TestCase):
reg = DummyRegistry()
config = self._makeOne(reg)
config.add_view = lambda *arg, **kw: False
- config._add_request_handler = lambda *arg, **kw: False
+ config._add_tween = lambda *arg, **kw: False
config.setup_registry()
self.assertEqual(reg.has_listeners, True)
@@ -348,7 +348,7 @@ class ConfiguratorTests(unittest.TestCase):
config = self._makeOne(reg)
views = []
config.add_view = lambda *arg, **kw: views.append((arg, kw))
- config._add_request_handler = lambda *arg, **kw: False
+ config._add_tween = lambda *arg, **kw: False
config.setup_registry()
self.assertEqual(views[0], ((default_exceptionresponse_view,),
{'context':IExceptionResponse}))
diff --git a/pyramid/tests/test_router.py b/pyramid/tests/test_router.py
index 6921de179..6b0354468 100644
--- a/pyramid/tests/test_router.py
+++ b/pyramid/tests/test_router.py
@@ -134,13 +134,16 @@ class TestRouter(unittest.TestCase):
router = self._makeOne()
self.assertEqual(router.request_factory, DummyRequestFactory)
- def test_request_handler_factories(self):
- from pyramid.interfaces import IRequestHandlers
- from pyramid.config import RequestHandlers
- handler_manager = RequestHandlers()
- self.registry.registerUtility(handler_manager, IRequestHandlers)
+ def test_tween_factories(self):
+ from pyramid.interfaces import ITweens
+ from pyramid.config import Tweens
+ from pyramid.response import Response
+ from pyramid.interfaces import IViewClassifier
+ from pyramid.interfaces import IResponse
+ tweens = Tweens()
+ self.registry.registerUtility(tweens, ITweens)
L = []
- def handler_factory1(handler, registry):
+ def tween_factory1(handler, registry):
L.append((handler, registry))
def wrapper(request):
request.environ['handled'].append('one')
@@ -148,7 +151,7 @@ class TestRouter(unittest.TestCase):
wrapper.name = 'one'
wrapper.child = handler
return wrapper
- def handler_factory2(handler, registry):
+ def tween_factory2(handler, registry):
L.append((handler, registry))
def wrapper(request):
request.environ['handled'] = ['two']
@@ -156,16 +159,13 @@ class TestRouter(unittest.TestCase):
wrapper.name = 'two'
wrapper.child = handler
return wrapper
- handler_manager.add('one', handler_factory1)
- handler_manager.add('two', handler_factory2)
+ tweens.add('one', tween_factory1)
+ tweens.add('two', tween_factory2)
router = self._makeOne()
self.assertEqual(router.handle_request.name, 'two')
self.assertEqual(router.handle_request.child.name, 'one')
self.assertEqual(router.handle_request.child.child.__name__,
'handle_request')
- from pyramid.response import Response
- from pyramid.interfaces import IViewClassifier
- from pyramid.interfaces import IResponse
context = DummyContext()
self._registerTraverserFactory(context)
environ = self._makeEnviron()
diff --git a/setup.py b/setup.py
index 109be6951..5e362ea86 100644
--- a/setup.py
+++ b/setup.py
@@ -86,6 +86,7 @@ setup(name='pyramid',
pshell=pyramid.paster:PShellCommand
proutes=pyramid.paster:PRoutesCommand
pviews=pyramid.paster:PViewsCommand
+ ptweens=pyramid.paster:PTweensCommand
[console_scripts]
bfg2pyramid = pyramid.fixers.fix_bfg_imports:main
"""