summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris McDonough <chrism@agendaless.com>2009-10-30 18:56:58 +0000
committerChris McDonough <chrism@agendaless.com>2009-10-30 18:56:58 +0000
commit11644e705834ff65cb8963333855a1db6272ae1e (patch)
tree6730225c3c81d52263f37ceac0876b11a57865ae
parent1b2d8e7326cd1eaa95b3522d985e97642764c7c8 (diff)
downloadpyramid-11644e705834ff65cb8963333855a1db6272ae1e.tar.gz
pyramid-11644e705834ff65cb8963333855a1db6272ae1e.tar.bz2
pyramid-11644e705834ff65cb8963333855a1db6272ae1e.zip
Features
-------- - In previous versions of BFG, the "root factory" (the ``get_root`` callable passed to ``make_app`` or a function pointed to by the ``factory`` attribute of a route) was called with a "bare" WSGI environment. In this version, and going forward, it will be called with a ``request`` object. The request object passed to the factory implements dictionary-like methods in such a way that existing root factory code which expects to be passed an environ will continue to work. Internal -------- - The request implements dictionary-like methods that mutate and query the WSGI environ. This is only for the purpose of backwards compatibility with root factories which expect an ``environ`` rather than a request. - The ``repoze.bfg.request.create_route_request_factory`` function, which returned a request factory was removed in favor of a ``repoze.bfg.request.route_request_interface`` function, which returns an interface. - The ``repoze.bfg.request.Request`` class, which is a subclass of ``webob.Request`` now defines its own ``__setattr__``, ``__getattr__`` and ``__delattr__`` methods, which override the default WebOb behavior. The default WebOb behavior stores attributes of the request in ``self.environ['webob.adhoc_attrs']``, and retrieves them from that dictionary during a ``__getattr__``. This behavior was undesirable for speed and "expectation" reasons. Now attributes of the ``request`` are stored in ``request.__dict__`` (as you otherwise might expect from an object that did not override these methods). - Reverse the order in which the router calls the request factory and the root factory. The request factory is now called first; the resulting request is passed to the root factory. - The ``repoze.bfg.request.request_factory`` function has been removed. Its functionality is no longer required. - The "routes root factory" that wraps the default root factory when there are routes mentioned in the configuration now attaches an interface to the request via ``zope.interface.directlyProvides``. This replaces logic in the (now-gone) ``repoze.bfg.request.request_factory`` function. - The ``route`` and ``view`` ZCML directives now register an interface as a named utility (retrieved from ``repoze.bfg.request.route_request_interface``) rather than a request factory (the previous return value of the now-missing ``repoze.bfg.request.create_route_request_factory``.
-rw-r--r--CHANGES.txt41
-rw-r--r--repoze/bfg/request.py80
-rw-r--r--repoze/bfg/router.py9
-rw-r--r--repoze/bfg/tests/test_request.py168
-rw-r--r--repoze/bfg/tests/test_urldispatch.py78
-rw-r--r--repoze/bfg/tests/test_zcml.py77
-rw-r--r--repoze/bfg/urldispatch.py32
-rw-r--r--repoze/bfg/zcml.py16
8 files changed, 327 insertions, 174 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index c47693284..ca6c501fb 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -10,9 +10,31 @@ Bug Fixes
combination of a predicate value with an ``=`` sign and one without
(e.g. ``a`` vs. ``a=123``).
+Features
+--------
+
+- In previous versions of BFG, the "root factory" (the ``get_root``
+ callable passed to ``make_app`` or a function pointed to by the
+ ``factory`` attribute of a route) was called with a "bare" WSGI
+ environment. In this version, and going forward, it will be called
+ with a ``request`` object. The request object passed to the factory
+ implements dictionary-like methods in such a way that existing root
+ factory code which expects to be passed an environ will continue to
+ work.
+
Internal
--------
+- The request implements dictionary-like methods that mutate and query
+ the WSGI environ. This is only for the purpose of backwards
+ compatibility with root factories which expect an ``environ`` rather
+ than a request.
+
+- The ``repoze.bfg.request.create_route_request_factory`` function,
+ which returned a request factory was removed in favor of a
+ ``repoze.bfg.request.route_request_interface`` function, which
+ returns an interface.
+
- The ``repoze.bfg.request.Request`` class, which is a subclass of
``webob.Request`` now defines its own ``__setattr__``,
``__getattr__`` and ``__delattr__`` methods, which override the
@@ -27,6 +49,25 @@ Internal
- The router no longer calls ``repoze.bfg.traversal._traverse`` and
does its work "inline" (speed).
+- Reverse the order in which the router calls the request factory and
+ the root factory. The request factory is now called first; the
+ resulting request is passed to the root factory.
+
+- The ``repoze.bfg.request.request_factory`` function has been
+ removed. Its functionality is no longer required.
+
+- The "routes root factory" that wraps the default root factory when
+ there are routes mentioned in the configuration now attaches an
+ interface to the request via ``zope.interface.directlyProvides``.
+ This replaces logic in the (now-gone)
+ ``repoze.bfg.request.request_factory`` function.
+
+- The ``route`` and ``view`` ZCML directives now register an interface
+ as a named utility (retrieved from
+ ``repoze.bfg.request.route_request_interface``) rather than a
+ request factory (the previous return value of the now-missing
+ ``repoze.bfg.request.create_route_request_factory``.
+
1.1a8 (2009-10-27)
==================
diff --git a/repoze/bfg/request.py b/repoze/bfg/request.py
index e81710ae1..751d1fba1 100644
--- a/repoze/bfg/request.py
+++ b/repoze/bfg/request.py
@@ -24,29 +24,63 @@ class Request(WebobRequest):
__setattr__ = object.__setattr__
__delattr__ = object.__delattr__
-def request_factory(environ):
- if 'bfg.routes.route' in environ:
- route = environ['bfg.routes.route']
- factory = queryUtility(IRouteRequest, name=route.name)
- if factory is not None:
- request = factory(environ)
- request.matchdict = environ['bfg.routes.matchdict']
- return request
- return Request(environ)
-
-def create_route_request_factory(name):
- iface = InterfaceClass('%s_IRequest' % name, (IRouteRequest,))
-
- class RouteRequest(WebobRequest):
- implements(iface)
- charset = 'utf-8'
-
- # override default WebOb "environ['adhoc_attr']" mutation behavior
- __getattr__ = object.__getattribute__
- __setattr__ = object.__setattr__
- __delattr__ = object.__delattr__
-
- return RouteRequest
+ # b/c dict interface for "root factory" code that expects a bare
+ # environ. Explicitly omitted dict methods: clear (unnecessary),
+ # copy (implemented by WebOb), fromkeys (unnecessary)
+
+ def __contains__(self, k):
+ return self.environ.__contains__(k)
+
+ def __delitem__(self, k):
+ return self.environ.__delitem__(k)
+
+ def __getitem__(self, k):
+ return self.environ.__getitem__(k)
+
+ def __iter__(self):
+ return iter(self.environ)
+
+ def __setitem__(self, k, v):
+ self.environ[k] = v
+
+ def get(self, k, default=None):
+ return self.environ.get(k, default)
+
+ def has_key(self, k):
+ return self.environ.has_key(k)
+
+ def items(self):
+ return self.environ.items()
+
+ def iteritems(self):
+ return self.environ.iteritems()
+
+ def iterkeys(self):
+ return self.environ.iterkeys()
+
+ def itervalues(self):
+ return self.environ.itervalues()
+
+ def keys(self):
+ return self.environ.keys()
+
+ def pop(self, k):
+ return self.environ.pop(k)
+
+ def popitem(self):
+ return self.environ.popitem()
+
+ def setdefault(self, v, default):
+ return self.environ.setdefault(v, default)
+
+ def update(self, v, **kw):
+ return self.environ.update(v, **kw)
+
+ def values(self):
+ return self.environ.values()
+
+def route_request_iface(name):
+ return InterfaceClass('%s_IRequest' % name, (IRouteRequest,))
def add_global_response_headers(request, headerlist):
attrs = request.__dict__
diff --git a/repoze/bfg/router.py b/repoze/bfg/router.py
index 5f5214c10..3916d6627 100644
--- a/repoze/bfg/router.py
+++ b/repoze/bfg/router.py
@@ -19,7 +19,7 @@ from repoze.bfg.events import NewResponse
from repoze.bfg.events import WSGIApplicationCreatedEvent
from repoze.bfg.exceptions import Forbidden
from repoze.bfg.exceptions import NotFound
-from repoze.bfg.request import request_factory
+from repoze.bfg.request import Request
from repoze.bfg.threadlocal import manager
from repoze.bfg.traversal import ModelGraphTraverser
from repoze.bfg.view import default_forbidden_view
@@ -60,14 +60,13 @@ class Router(object):
manager.push(threadlocals)
try:
- root = self.root_factory(environ)
- request = request_factory(environ)
-
+ request = Request(environ)
+ threadlocals['request'] = request
attrs = request.__dict__
attrs['registry'] = registry
+ root = self.root_factory(request)
attrs['root'] = root
- threadlocals['request'] = request
registry.has_listeners and registry.notify(NewRequest(request))
traverser = registry.queryAdapter(root, ITraverser)
if traverser is None:
diff --git a/repoze/bfg/tests/test_request.py b/repoze/bfg/tests/test_request.py
index 8c868fcf5..2ecb060cc 100644
--- a/repoze/bfg/tests/test_request.py
+++ b/repoze/bfg/tests/test_request.py
@@ -1,5 +1,4 @@
import unittest
-from repoze.bfg.testing import cleanUp
class TestMakeRequestASCII(unittest.TestCase):
def _callFUT(self, event):
@@ -12,10 +11,14 @@ class TestMakeRequestASCII(unittest.TestCase):
self._callFUT(event)
self.assertEqual(request.charset, None)
-class RequestTestBase(object):
+class TestRequest(unittest.TestCase):
def _makeOne(self, environ):
- request = self._getTargetClass()(environ)
- return request
+ return self._getTargetClass()(environ)
+
+ def _getTargetClass(self):
+ from repoze.bfg.request import Request
+ return Request
+
def test_params_decoded_from_utf_8_by_default(self):
environ = {
@@ -44,64 +47,114 @@ class RequestTestBase(object):
inst = self._makeOne({})
self.assertTrue(IRequest.providedBy(inst))
-class TestRequest(unittest.TestCase, RequestTestBase):
- def _getTargetClass(self):
- from repoze.bfg.request import Request
- return Request
-
-class TestRouteRequest(unittest.TestCase, RequestTestBase):
- def _getTargetClass(self):
- from repoze.bfg.request import create_route_request_factory
- return create_route_request_factory('abc')
-
-class TestRequestFactory(unittest.TestCase):
- def setUp(self):
- cleanUp()
-
- def tearDown(self):
- cleanUp()
+ def test___contains__(self):
+ environ ={'zooma':1}
+ inst = self._makeOne(environ)
+ self.failUnless('zooma' in inst)
+
+ def test___delitem__(self):
+ environ = {'zooma':1}
+ inst = self._makeOne(environ)
+ del inst['zooma']
+ self.failIf('zooma' in environ)
+
+ def test___getitem__(self):
+ environ = {'zooma':1}
+ inst = self._makeOne(environ)
+ self.assertEqual(inst['zooma'], 1)
+
+ def test___iter__(self):
+ environ = {'zooma':1}
+ inst = self._makeOne(environ)
+ iterator = iter(inst)
+ self.assertEqual(list(iterator), list(iter(environ)))
+
+ def test___setitem__(self):
+ environ = {}
+ inst = self._makeOne(environ)
+ inst['zooma'] = 1
+ self.assertEqual(environ, {'zooma':1})
+
+ def test_get(self):
+ environ = {'zooma':1}
+ inst = self._makeOne(environ)
+ self.assertEqual(inst.get('zooma'), 1)
- def _callFUT(self, environ):
- from repoze.bfg.request import request_factory
- return request_factory(environ)
-
- def test_it_no_route(self):
- from repoze.bfg.interfaces import IRequest
- from repoze.bfg.request import Request
- result = self._callFUT({})
- self.assertEqual(result.__class__, Request)
- self.failUnless(IRequest.providedBy(result))
-
- def test_it_with_route_found(self):
- from zope.component import getSiteManager
- from repoze.bfg.interfaces import IRouteRequest
- sm = getSiteManager()
- sm.registerUtility(DummyRequest, IRouteRequest, 'routename')
- route = DummyRoute('routename')
- result = self._callFUT({'bfg.routes.route':route,
- 'bfg.routes.matchdict':{'match':'1'}})
- self.assertEqual(result.__class__, DummyRequest)
- self.assertEqual(result.matchdict, {'match':'1'})
-
- def test_it_with_route_notfound(self):
- from repoze.bfg.request import Request
- route = DummyRoute('routename')
- result = self._callFUT({'bfg.routes.route':route,
- 'bfg.routes.matchdict':{'match':'1'}})
- self.assertEqual(result.__class__, Request)
- self.failIf(getattr(result, 'matchdict', None) is not None)
-
-class Test_create_route_request_factory(unittest.TestCase):
+ def test_has_key(self):
+ environ = {'zooma':1}
+ inst = self._makeOne(environ)
+ self.assertEqual(inst.has_key('zooma'), True)
+
+ def test_items(self):
+ environ = {'zooma':1}
+ inst = self._makeOne(environ)
+ self.assertEqual(inst.items(), environ.items())
+
+ def test_iteritems(self):
+ environ = {'zooma':1}
+ inst = self._makeOne(environ)
+ self.assertEqual(list(inst.iteritems()), list(environ.iteritems()))
+
+ def test_iterkeys(self):
+ environ = {'zooma':1}
+ inst = self._makeOne(environ)
+ self.assertEqual(list(inst.iterkeys()), list(environ.iterkeys()))
+
+ def test_itervalues(self):
+ environ = {'zooma':1}
+ inst = self._makeOne(environ)
+ self.assertEqual(list(inst.itervalues()), list(environ.itervalues()))
+
+ def test_keys(self):
+ environ = {'zooma':1}
+ inst = self._makeOne(environ)
+ self.assertEqual(inst.keys(), environ.keys())
+
+ def test_pop(self):
+ environ = {'zooma':1}
+ inst = self._makeOne(environ)
+ popped = inst.pop('zooma')
+ self.assertEqual(environ, {})
+ self.assertEqual(popped, 1)
+
+ def test_popitem(self):
+ environ = {'zooma':1}
+ inst = self._makeOne(environ)
+ popped = inst.popitem()
+ self.assertEqual(environ, {})
+ self.assertEqual(popped, ('zooma', 1))
+
+ def test_setdefault(self):
+ environ = {}
+ inst = self._makeOne(environ)
+ marker = []
+ result = inst.setdefault('a', marker)
+ self.assertEqual(environ, {'a':marker})
+ self.assertEqual(result, marker)
+
+ def test_update(self):
+ environ = {}
+ inst = self._makeOne(environ)
+ inst.update({'a':1}, b=2)
+ self.assertEqual(environ, {'a':1, 'b':2})
+
+ def test_values(self):
+ environ = {'zooma':1}
+ inst = self._makeOne(environ)
+ result = inst.values()
+ self.assertEqual(result, environ.values())
+
+class Test_route_request_iface(unittest.TestCase):
def _callFUT(self, name):
- from repoze.bfg.request import create_route_request_factory
- return create_route_request_factory(name)
+ from repoze.bfg.request import route_request_iface
+ return route_request_iface(name)
def test_it(self):
from repoze.bfg.interfaces import IRouteRequest
from repoze.bfg.interfaces import IRequest
- factory = self._callFUT('routename')
- self.failUnless(IRouteRequest.implementedBy(factory))
- self.failUnless(IRequest.implementedBy(factory))
+ iface = self._callFUT('routename')
+ self.failUnless(iface.extends(IRouteRequest))
+ self.failUnless(iface.extends(IRequest))
class Test_add_global_response_headers(unittest.TestCase):
def _callFUT(self, request, headerlist):
@@ -115,9 +168,6 @@ class Test_add_global_response_headers(unittest.TestCase):
self._callFUT(request, [('c', 1)])
self.assertEqual(request.global_response_headers, headers + [('c', 1)])
-class DummyRoute:
- def __init__(self, name):
- self.name = name
class DummyRequest:
def __init__(self, environ=None):
if environ is None:
diff --git a/repoze/bfg/tests/test_urldispatch.py b/repoze/bfg/tests/test_urldispatch.py
index c7a6c7ff3..99d5d1a98 100644
--- a/repoze/bfg/tests/test_urldispatch.py
+++ b/repoze/bfg/tests/test_urldispatch.py
@@ -1,5 +1,5 @@
import unittest
-from repoze.bfg.testing import cleanUp
+from repoze.bfg import testing
class TestRoute(unittest.TestCase):
def _getTargetClass(self):
@@ -37,16 +37,30 @@ class TestRoute(unittest.TestCase):
class RoutesRootFactoryTests(unittest.TestCase):
def setUp(self):
- cleanUp()
+ testing.setUp()
def tearDown(self):
- cleanUp()
+ testing.tearDown()
- def _getEnviron(self, **kw):
+ def _getRequest(self, **kw):
+ from zope.component import getSiteManager
environ = {'SERVER_NAME':'localhost',
'wsgi.url_scheme':'http'}
environ.update(kw)
- return environ
+ request = DummyRequest(environ)
+ sm = getSiteManager()
+ request.registry = sm
+ return request
+
+ def _registerRouteRequest(self, name):
+ from repoze.bfg.interfaces import IRouteRequest
+ from zope.interface import Interface
+ from zope.component import getSiteManager
+ class IRequest(Interface):
+ """ """
+ sm = getSiteManager()
+ sm.registerUtility(IRequest, IRouteRequest, name=name)
+ return IRequest
def _getTargetClass(self):
from repoze.bfg.urldispatch import RoutesRootFactory
@@ -61,61 +75,80 @@ class RoutesRootFactoryTests(unittest.TestCase):
self.assertEqual(mapper.default_root_factory, None)
def test_no_route_matches(self):
- get_root = DummyRootFactory(123)
- mapper = self._makeOne(get_root)
- environ = self._getEnviron(PATH_INFO='/')
- result = mapper(environ)
+ root_factory = DummyRootFactory(123)
+ mapper = self._makeOne(root_factory)
+ request = self._getRequest(PATH_INFO='/')
+ result = mapper(request)
+ self.assertEqual(result, 123)
+
+ def test_passed_environ_returns_default(self):
+ root_factory = DummyRootFactory(123)
+ mapper = self._makeOne(root_factory)
+ request = self._getRequest(PATH_INFO='/')
+ result = mapper(request.environ)
self.assertEqual(result, 123)
def test_route_matches(self):
- get_root = DummyRootFactory(123)
- mapper = self._makeOne(get_root)
+ root_factory = DummyRootFactory(123)
+ req_iface = self._registerRouteRequest('foo')
+ mapper = self._makeOne(root_factory)
mapper.connect('archives/:action/:article', 'foo')
- environ = self._getEnviron(PATH_INFO='/archives/action1/article1')
- result = mapper(environ)
+ request = self._getRequest(PATH_INFO='/archives/action1/article1')
+ result = mapper(request)
self.assertEqual(result, 123)
+ environ = request.environ
routing_args = environ['wsgiorg.routing_args'][1]
self.assertEqual(routing_args['action'], 'action1')
self.assertEqual(routing_args['article'], 'article1')
self.assertEqual(environ['bfg.routes.matchdict'], routing_args)
self.assertEqual(environ['bfg.routes.route'].name, 'foo')
+ self.assertEqual(request.matchdict, routing_args)
+ self.failUnless(req_iface.providedBy(request))
def test_root_route_matches(self):
root_factory = DummyRootFactory(123)
+ req_iface = self._registerRouteRequest('root')
mapper = self._makeOne(root_factory)
mapper.connect('', 'root')
- environ = self._getEnviron(PATH_INFO='/')
- result = mapper(environ)
+ request = self._getRequest(PATH_INFO='/')
+ result = mapper(request)
+ environ = request.environ
self.assertEqual(result, 123)
self.assertEqual(environ['bfg.routes.route'].name, 'root')
self.assertEqual(environ['bfg.routes.matchdict'], {})
self.assertEqual(environ['wsgiorg.routing_args'], ((), {}))
+ self.assertEqual(request.matchdict, {})
+ self.failUnless(req_iface.providedBy(request))
def test_root_route_matches2(self):
root_factory = DummyRootFactory(123)
+ req_iface = self._registerRouteRequest('root')
mapper = self._makeOne(root_factory)
mapper.connect('/', 'root')
- environ = self._getEnviron(PATH_INFO='/')
- result = mapper(environ)
+ request = self._getRequest(PATH_INFO='/')
+ result = mapper(request)
+ environ = request.environ
self.assertEqual(result, 123)
self.assertEqual(environ['bfg.routes.route'].name, 'root')
self.assertEqual(environ['bfg.routes.matchdict'], {})
self.assertEqual(environ['wsgiorg.routing_args'], ((), {}))
+ self.assertEqual(request.matchdict, {})
+ self.failUnless(req_iface.providedBy(request))
def test_fallback_to_default_root_factory(self):
root_factory = DummyRootFactory(123)
mapper = self._makeOne(root_factory)
mapper.connect('wont/:be/:found', 'wont')
- environ = self._getEnviron(PATH_INFO='/archives/action1/article1')
- result = mapper(environ)
+ request = self._getRequest(PATH_INFO='/archives/action1/article1')
+ result = mapper(request)
self.assertEqual(result, 123)
def test_no_path_info(self):
root_factory = DummyRootFactory(123)
mapper = self._makeOne(root_factory)
mapper.connect('/', 'root')
- environ = self._getEnviron()
- result = mapper(environ)
+ request = self._getRequest()
+ result = mapper(request)
self.assertEqual(result, 123)
def test_has_routes(self):
@@ -230,7 +263,8 @@ class DummyContext(object):
""" """
class DummyRequest(object):
- """ """
+ def __init__(self, environ):
+ self.environ = environ
class DummyRoute(object):
def __init__(self, generator):
diff --git a/repoze/bfg/tests/test_zcml.py b/repoze/bfg/tests/test_zcml.py
index 352a53e53..8106baa03 100644
--- a/repoze/bfg/tests/test_zcml.py
+++ b/repoze/bfg/tests/test_zcml.py
@@ -517,7 +517,6 @@ class TestViewDirective(unittest.TestCase):
def test_with_route_name(self):
from zope.interface import Interface
- from zope.interface import implementedBy
from zope.component import getSiteManager
from repoze.bfg.interfaces import IView
from repoze.bfg.interfaces import IRouteRequest
@@ -534,13 +533,12 @@ class TestViewDirective(unittest.TestCase):
register = action['callable']
register()
sm = getSiteManager()
- factory = sm.getUtility(IRouteRequest, 'foo')
- request_type = implementedBy(factory)
+ request_type = sm.getUtility(IRouteRequest, 'foo')
discrim = ('view', IFoo, '', request_type, IView, None, None, None,
'foo', None, False, None, None, None)
self.assertEqual(action['discriminator'], discrim)
the_view = sm.adapters.lookup((IFoo, request_type), IView, name='')
- request = factory({})
+ request = DummyRequest({})
self.assertEqual(the_view(None, request), '123')
def test_with_request_method_true(self):
@@ -1591,7 +1589,6 @@ class TestRouteDirective(unittest.TestCase):
def test_with_view(self):
from zope.interface import Interface
- from zope.interface import implementedBy
from zope.component import getSiteManager
from repoze.bfg.interfaces import IRouteRequest
from repoze.bfg.zcml import connect_route
@@ -1608,8 +1605,7 @@ class TestRouteDirective(unittest.TestCase):
register = view_action['callable']
register()
sm = getSiteManager()
- request_factory = sm.getUtility(IRouteRequest, 'name')
- request_type = implementedBy(request_factory)
+ request_type = sm.getUtility(IRouteRequest, 'name')
view_discriminator = view_action['discriminator']
discrim = ('view', None, '', request_type, IView, None, None, None,
'name', None, False, None, None, None)
@@ -1633,7 +1629,6 @@ class TestRouteDirective(unittest.TestCase):
def test_with_view_and_view_for(self):
from zope.component import getSiteManager
- from zope.interface import implementedBy
from repoze.bfg.zcml import connect_route
from repoze.bfg.interfaces import IView
from repoze.bfg.interfaces import IRouteRequest
@@ -1649,8 +1644,7 @@ class TestRouteDirective(unittest.TestCase):
register = view_action['callable']
register()
sm = getSiteManager()
- request_factory = sm.getUtility(IRouteRequest, 'name')
- request_type = implementedBy(request_factory)
+ request_type = sm.getUtility(IRouteRequest, 'name')
view_discriminator = view_action['discriminator']
discrim = ('view', IDummy, '', request_type, IView, None, None, None,
'name', None, False, None, None, None)
@@ -1688,7 +1682,6 @@ class TestRouteDirective(unittest.TestCase):
def test_with_view_request_type(self):
from zope.component import getSiteManager
- from zope.interface import implementedBy
from repoze.bfg.zcml import connect_route
from repoze.bfg.interfaces import IView
from repoze.bfg.interfaces import IRouteRequest
@@ -1705,8 +1698,7 @@ class TestRouteDirective(unittest.TestCase):
register = view_action['callable']
register()
sm = getSiteManager()
- request_factory = sm.getUtility(IRouteRequest, 'name')
- request_type = implementedBy(request_factory)
+ request_type = sm.getUtility(IRouteRequest, 'name')
view_discriminator = view_action['discriminator']
discrim = ('view', None, '', request_type, IView, None, None, 'GET',
'name', None, False, None, None, None)
@@ -1726,7 +1718,6 @@ class TestRouteDirective(unittest.TestCase):
def test_with_view_request_type_alias(self):
from zope.component import getSiteManager
- from zope.interface import implementedBy
from repoze.bfg.zcml import connect_route
from repoze.bfg.interfaces import IView
from repoze.bfg.interfaces import IRouteRequest
@@ -1742,8 +1733,7 @@ class TestRouteDirective(unittest.TestCase):
register = view_action['callable']
register()
sm = getSiteManager()
- request_factory = sm.getUtility(IRouteRequest, 'name')
- request_type = implementedBy(request_factory)
+ request_type = sm.getUtility(IRouteRequest, 'name')
view_discriminator = view_action['discriminator']
discrim = ('view', None, '', request_type, IView, None, None, 'GET',
'name', None, False, None, None, None)
@@ -1763,7 +1753,6 @@ class TestRouteDirective(unittest.TestCase):
def test_with_view_request_method(self):
from zope.component import getSiteManager
- from zope.interface import implementedBy
from repoze.bfg.zcml import connect_route
from repoze.bfg.interfaces import IView
from repoze.bfg.interfaces import IRouteRequest
@@ -1780,8 +1769,7 @@ class TestRouteDirective(unittest.TestCase):
register = view_action['callable']
register()
sm = getSiteManager()
- request_factory = sm.getUtility(IRouteRequest, 'name')
- request_type = implementedBy(request_factory)
+ request_type = sm.getUtility(IRouteRequest, 'name')
view_discriminator = view_action['discriminator']
discrim = ('view', None, '', request_type, IView, None, None, 'GET',
'name', None, False, None, None, None)
@@ -1801,7 +1789,6 @@ class TestRouteDirective(unittest.TestCase):
def test_with_view_request_method_alias(self):
from zope.component import getSiteManager
- from zope.interface import implementedBy
from repoze.bfg.zcml import connect_route
from repoze.bfg.interfaces import IView
from repoze.bfg.interfaces import IRouteRequest
@@ -1817,8 +1804,8 @@ class TestRouteDirective(unittest.TestCase):
register = view_action['callable']
register()
sm = getSiteManager()
- request_factory = sm.getUtility(IRouteRequest, 'name')
- request_type = implementedBy(request_factory)
+ request_type = sm.getUtility(IRouteRequest, 'name')
+
view_discriminator = view_action['discriminator']
discrim = ('view', None, '', request_type, IView, None, None, 'GET',
'name', None, False, None, None, None)
@@ -1838,7 +1825,6 @@ class TestRouteDirective(unittest.TestCase):
def test_with_view_containment(self):
from zope.component import getSiteManager
- from zope.interface import implementedBy
from repoze.bfg.zcml import connect_route
from repoze.bfg.interfaces import IView
from repoze.bfg.interfaces import IRouteRequest
@@ -1854,8 +1840,7 @@ class TestRouteDirective(unittest.TestCase):
register = view_action['callable']
register()
sm = getSiteManager()
- request_factory = sm.getUtility(IRouteRequest, 'name')
- request_type = implementedBy(request_factory)
+ request_type = sm.getUtility(IRouteRequest, 'name')
view_discriminator = view_action['discriminator']
discrim = ('view', None, '', request_type, IView, True, None, None,
'name', None, False, None, None, None)
@@ -1875,7 +1860,6 @@ class TestRouteDirective(unittest.TestCase):
def test_with_view_containment_alias(self):
from zope.component import getSiteManager
- from zope.interface import implementedBy
from repoze.bfg.zcml import connect_route
from repoze.bfg.interfaces import IView
from repoze.bfg.interfaces import IRouteRequest
@@ -1891,8 +1875,7 @@ class TestRouteDirective(unittest.TestCase):
register = view_action['callable']
register()
sm = getSiteManager()
- request_factory = sm.getUtility(IRouteRequest, 'name')
- request_type = implementedBy(request_factory)
+ request_type = sm.getUtility(IRouteRequest, 'name')
view_discriminator = view_action['discriminator']
discrim = ('view', None, '', request_type, IView, True, None, None,
'name', None, False, None, None, None)
@@ -1912,7 +1895,6 @@ class TestRouteDirective(unittest.TestCase):
def test_with_view_header(self):
from zope.component import getSiteManager
- from zope.interface import implementedBy
from repoze.bfg.zcml import connect_route
from repoze.bfg.interfaces import IView
from repoze.bfg.interfaces import IRouteRequest
@@ -1928,8 +1910,7 @@ class TestRouteDirective(unittest.TestCase):
register = view_action['callable']
register()
sm = getSiteManager()
- request_factory = sm.getUtility(IRouteRequest, 'name')
- request_type = implementedBy(request_factory)
+ request_type = sm.getUtility(IRouteRequest, 'name')
view_discriminator = view_action['discriminator']
discrim = ('view', None, '', request_type, IView, None, None, None,
'name', None, False, None, 'Host', None)
@@ -1949,7 +1930,6 @@ class TestRouteDirective(unittest.TestCase):
def test_with_view_header_alias(self):
from zope.component import getSiteManager
- from zope.interface import implementedBy
from repoze.bfg.zcml import connect_route
from repoze.bfg.interfaces import IView
from repoze.bfg.interfaces import IRouteRequest
@@ -1965,8 +1945,7 @@ class TestRouteDirective(unittest.TestCase):
register = view_action['callable']
register()
sm = getSiteManager()
- request_factory = sm.getUtility(IRouteRequest, 'name')
- request_type = implementedBy(request_factory)
+ request_type = sm.getUtility(IRouteRequest, 'name')
view_discriminator = view_action['discriminator']
discrim = ('view', None, '', request_type, IView, None, None, None,
'name', None, False, None, 'Host', None)
@@ -1986,7 +1965,6 @@ class TestRouteDirective(unittest.TestCase):
def test_with_view_path_info(self):
from zope.component import getSiteManager
- from zope.interface import implementedBy
from repoze.bfg.zcml import connect_route
from repoze.bfg.interfaces import IView
from repoze.bfg.interfaces import IRouteRequest
@@ -2002,8 +1980,7 @@ class TestRouteDirective(unittest.TestCase):
register = view_action['callable']
register()
sm = getSiteManager()
- request_factory = sm.getUtility(IRouteRequest, 'name')
- request_type = implementedBy(request_factory)
+ request_type = sm.getUtility(IRouteRequest, 'name')
view_discriminator = view_action['discriminator']
discrim = ('view', None, '', request_type, IView, None, None, None,
'name', None, False, None, None, '/foo')
@@ -2023,7 +2000,6 @@ class TestRouteDirective(unittest.TestCase):
def test_with_view_xhr(self):
from zope.component import getSiteManager
- from zope.interface import implementedBy
from repoze.bfg.zcml import connect_route
from repoze.bfg.interfaces import IView
from repoze.bfg.interfaces import IRouteRequest
@@ -2039,8 +2015,7 @@ class TestRouteDirective(unittest.TestCase):
register = view_action['callable']
register()
sm = getSiteManager()
- request_factory = sm.getUtility(IRouteRequest, 'name')
- request_type = implementedBy(request_factory)
+ request_type = sm.getUtility(IRouteRequest, 'name')
view_discriminator = view_action['discriminator']
discrim = ('view', None, '', request_type, IView, None, None, None,
'name', None, True, None, None, None)
@@ -2060,7 +2035,6 @@ class TestRouteDirective(unittest.TestCase):
def test_with_view_xhr_alias(self):
from zope.component import getSiteManager
- from zope.interface import implementedBy
from repoze.bfg.zcml import connect_route
from repoze.bfg.interfaces import IView
from repoze.bfg.interfaces import IRouteRequest
@@ -2076,8 +2050,7 @@ class TestRouteDirective(unittest.TestCase):
register = view_action['callable']
register()
sm = getSiteManager()
- request_factory = sm.getUtility(IRouteRequest, 'name')
- request_type = implementedBy(request_factory)
+ request_type = sm.getUtility(IRouteRequest, 'name')
view_discriminator = view_action['discriminator']
discrim = ('view', None, '', request_type, IView, None, None, None,
'name', None, True, None, None, None)
@@ -2097,7 +2070,6 @@ class TestRouteDirective(unittest.TestCase):
def test_with_view_accept(self):
from zope.component import getSiteManager
- from zope.interface import implementedBy
from repoze.bfg.zcml import connect_route
from repoze.bfg.interfaces import IView
from repoze.bfg.interfaces import IRouteRequest
@@ -2114,8 +2086,7 @@ class TestRouteDirective(unittest.TestCase):
register = view_action['callable']
register()
sm = getSiteManager()
- request_factory = sm.getUtility(IRouteRequest, 'name')
- request_type = implementedBy(request_factory)
+ request_type = sm.getUtility(IRouteRequest, 'name')
view_discriminator = view_action['discriminator']
discrim = ('view', None, '', request_type, IView, None, None, None,
'name', None, False, 'text/xml', None, None)
@@ -2135,7 +2106,6 @@ class TestRouteDirective(unittest.TestCase):
def test_with_view_accept_alias(self):
from zope.component import getSiteManager
- from zope.interface import implementedBy
from repoze.bfg.zcml import connect_route
from repoze.bfg.interfaces import IView
from repoze.bfg.interfaces import IRouteRequest
@@ -2151,8 +2121,8 @@ class TestRouteDirective(unittest.TestCase):
register = view_action['callable']
register()
sm = getSiteManager()
- request_factory = sm.getUtility(IRouteRequest, 'name')
- request_type = implementedBy(request_factory)
+ request_type = sm.getUtility(IRouteRequest, 'name')
+
view_discriminator = view_action['discriminator']
discrim = ('view', None, '', request_type, IView, None, None, None,
'name', None, False, 'text/xml', None, None)
@@ -2206,8 +2176,7 @@ class TestStaticDirective(unittest.TestCase):
register()
sm = getSiteManager()
iface = implementedBy(StaticRootFactory)
- request_factory = sm.getUtility(IRouteRequest, 'name')
- request_type = implementedBy(request_factory)
+ request_type = sm.getUtility(IRouteRequest, 'name')
wrapped = sm.adapters.lookup((iface, request_type), IView, name='')
request = DummyRequest()
self.assertEqual(wrapped(None, request).__class__, StaticURLParser)
@@ -2241,8 +2210,7 @@ class TestStaticDirective(unittest.TestCase):
register()
sm = getSiteManager()
iface = implementedBy(StaticRootFactory)
- request_factory = sm.getUtility(IRouteRequest, 'name')
- request_type = implementedBy(request_factory)
+ request_type = sm.getUtility(IRouteRequest, 'name')
view = sm.adapters.lookup((iface, request_type), IView, name='')
request = DummyRequest()
self.assertEqual(view(None, request).__class__, PackageURLParser)
@@ -2277,8 +2245,7 @@ class TestStaticDirective(unittest.TestCase):
register()
sm = getSiteManager()
iface = implementedBy(StaticRootFactory)
- request_factory = sm.getUtility(IRouteRequest, 'name')
- request_type = implementedBy(request_factory)
+ request_type = sm.getUtility(IRouteRequest, 'name')
wrapped = sm.adapters.lookup((iface, request_type), IView, name='')
request = DummyRequest()
self.assertEqual(wrapped(None, request).__class__, PackageURLParser)
diff --git a/repoze/bfg/urldispatch.py b/repoze/bfg/urldispatch.py
index 14a81062c..4088cef12 100644
--- a/repoze/bfg/urldispatch.py
+++ b/repoze/bfg/urldispatch.py
@@ -1,6 +1,10 @@
import re
from urllib import unquote
+from zope.interface import directlyProvides
+
+from repoze.bfg.interfaces import IRouteRequest
+
from repoze.bfg.traversal import traversal_path
from repoze.bfg.traversal import quote_path_segment
from repoze.bfg.encode import url_quote
@@ -35,7 +39,29 @@ class RoutesRootFactory(object):
def generate(self, name, kw):
return self.routes[name].generate(kw)
- def __call__(self, environ):
+ def __call__(self, request):
+ try:
+ # As of BFG 1.1a9, a root factory is now typically called
+ # with a request object (instead of a WSGI environ, as in
+ # previous versions) by the router. Simultaneously, as of
+ # 1.1a9, the RoutesRootFactory *requires* that the object
+ # passed to it be a request, instead of an environ, as it
+ # uses both the ``registry`` attribute of the request, and
+ # if a route is found, it decorates the object with an
+ # interface using directlyProvides. However, existing app
+ # code "in the wild" calls the root factory explicitly
+ # with a dictionary argument (e.g. a subscriber to
+ # WSGIApplicationCreatedEvent does
+ # ``app.root_factory({})``). It makes no sense for such
+ # code to depend on the side effects of a
+ # RoutesRootFactory, for bw compat purposes we catch the
+ # exception that will be raised when passed a dictionary
+ # and just return the result of the default root factory.
+ environ = request.environ
+ registry = request.registry
+ except AttributeError:
+ return self.default_root_factory(request)
+
try:
path = environ['PATH_INFO']
except KeyError:
@@ -46,6 +72,10 @@ class RoutesRootFactory(object):
environ['wsgiorg.routing_args'] = ((), match)
environ['bfg.routes.route'] = route
environ['bfg.routes.matchdict'] = match
+ request.matchdict = match
+ iface = registry.queryUtility(IRouteRequest, name=route.name)
+ if iface is not None:
+ directlyProvides(request, iface)
factory = route.factory or self.default_root_factory
return factory(environ)
diff --git a/repoze/bfg/zcml.py b/repoze/bfg/zcml.py
index c192fddfb..c39f06bb3 100644
--- a/repoze/bfg/zcml.py
+++ b/repoze/bfg/zcml.py
@@ -38,7 +38,7 @@ from repoze.bfg.authentication import AuthTktAuthenticationPolicy
from repoze.bfg.authorization import ACLAuthorizationPolicy
from repoze.bfg.configuration import zcml_configure
from repoze.bfg.path import package_name
-from repoze.bfg.request import create_route_request_factory
+from repoze.bfg.request import route_request_iface
from repoze.bfg.resource import PackageOverrides
from repoze.bfg.resource import resource_spec
from repoze.bfg.static import StaticRootFactory
@@ -287,9 +287,8 @@ def view(
else:
request_type = queryUtility(IRouteRequest, name=route_name)
if request_type is None:
- factory = create_route_request_factory(route_name)
- request_type = implementedBy(factory)
- sm.registerUtility(factory, IRouteRequest, name=route_name)
+ request_type = route_request_iface(route_name)
+ sm.registerUtility(request_type, IRouteRequest, name=route_name)
if isinstance(request_type, basestring):
request_type = _context.resolve(request_type)
@@ -634,11 +633,10 @@ def route(_context, name, path, view=None, view_for=None,
request_type = None
if request_type is None:
- request_factory = queryUtility(IRouteRequest, name=name)
- if request_factory is None:
- request_factory = create_route_request_factory(name)
- sm.registerUtility(request_factory, IRouteRequest, name=name)
- request_type = implementedBy(request_factory)
+ request_type = queryUtility(IRouteRequest, name=name)
+ if request_type is None:
+ request_type = route_request_iface(name)
+ sm.registerUtility(request_type, IRouteRequest, name=name)
if view:
_view(_context, permission=permission, for_=for_, view=view, name='',