summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES.txt37
-rw-r--r--docs/narr/urldispatch.rst6
-rw-r--r--docs/zcml/route.rst8
-rw-r--r--repoze/bfg/configuration.py81
-rw-r--r--repoze/bfg/request.py4
-rw-r--r--repoze/bfg/router.py16
-rw-r--r--repoze/bfg/security.py2
-rw-r--r--repoze/bfg/tests/hybridapp/__init__.py1
-rw-r--r--repoze/bfg/tests/hybridapp/configure.zcml42
-rw-r--r--repoze/bfg/tests/hybridapp/views.py13
-rw-r--r--repoze/bfg/tests/test_configuration.py79
-rw-r--r--repoze/bfg/tests/test_integration.py25
-rw-r--r--repoze/bfg/tests/test_router.py12
-rw-r--r--repoze/bfg/tests/test_view.py5
-rw-r--r--repoze/bfg/tests/test_zcml.py42
-rw-r--r--repoze/bfg/view.py2
-rw-r--r--repoze/bfg/zcml.py11
17 files changed, 277 insertions, 109 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index 21668ca7d..ec8e7f19d 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,3 +1,40 @@
+Next release
+============
+
+Bug Fixes
+---------
+
+- When "hybrid mode" (both traversal and urldispatch) is in use,
+ default to finding route-related views even if a non-route-related
+ view registration has been made with a more specific context. The
+ default used to be to find views with a more specific context first.
+ Use the ``use_global_views`` argument to the route configuration to
+ get back the older behavior.
+
+Features
+--------
+
+- Add ``use_global_views`` argument to ``add_route`` method of
+ Configurator. When this argument is true, views registered for *no*
+ route will be found if no more specific view related to the route is
+ found.
+
+- Add ``use_global_views`` attribute to ZCML ``<route>`` directive
+ (see above).
+
+Internal
+--------
+
+- When registering a view, register the view adapter with the
+ "requires" interfaces as ``(request_type, context_type)`` rather
+ than ``(context_type, request_type)``. This provides for saner
+ lookup, because the registration will always be made with a specific
+ request interface, but registration may not be made with a specific
+ context interface. In general, when creating multiadapters, you
+ want to order the provides interfaces so that the the elements which
+ are more likely to have specific interfaces are ordered before those
+ which may not.
+
1.2b2 (2010-01-21)
==================
diff --git a/docs/narr/urldispatch.rst b/docs/narr/urldispatch.rst
index 05c04e03f..495b05a14 100644
--- a/docs/narr/urldispatch.rst
+++ b/docs/narr/urldispatch.rst
@@ -585,6 +585,12 @@ information.
If the ``view`` argument is not provided, this argument has no
effect.
+``use_global_views``
+ When a request matches this route, and view lookup cannot find a view
+ which has a 'route_name' predicate argument that matches the route,
+ try to fall back to using a view that otherwise matches the context,
+ request, and view name (but does not match the route name predicate).
+
Route Matching
--------------
diff --git a/docs/zcml/route.rst b/docs/zcml/route.rst
index bb842819d..169252d77 100644
--- a/docs/zcml/route.rst
+++ b/docs/zcml/route.rst
@@ -203,6 +203,14 @@ Attributes
.. note:: This feature is new as of :mod:`repoze.bfg` 1.1.
+``use_global_views``
+ When a request matches this route, and view lookup cannot find a view
+ which has a 'route_name' predicate argument that matches the route,
+ try to fall back to using a view that otherwise matches the context,
+ request, and view name (but does not match the route name predicate).
+
+ .. note:: This feature is new as of :mod:`repoze.bfg` 1.2.
+
Alternatives
~~~~~~~~~~~~
diff --git a/repoze/bfg/configuration.py b/repoze/bfg/configuration.py
index bc9bbebca..dae1afe2a 100644
--- a/repoze/bfg/configuration.py
+++ b/repoze/bfg/configuration.py
@@ -695,27 +695,39 @@ class Configurator(object):
request_method = request_type
request_type = None
- if request_type and route_name:
- raise ConfigurationError(
- 'A view cannot be configured with both the request_type and '
- 'route_name parameters: these two features when used together '
- 'causes an internal conflict.')
+ if request_type is not None:
+ if not IInterface.providedBy(request_type):
+ raise ConfigurationError(
+ 'request_type must be an interface, not %s' % request_type)
- if request_type is None:
- request_type = IRequest
+ request_iface = IRequest
if route_name is not None:
- request_type = self.registry.queryUtility(IRouteRequest,
- name=route_name)
- if request_type is None:
- request_type = route_request_iface(route_name)
- self.registry.registerUtility(request_type, IRouteRequest,
- name=route_name)
+ request_iface = self.registry.queryUtility(IRouteRequest,
+ name=route_name)
+ if request_iface is None:
+ deferred_views = getattr(self.registry,
+ 'deferred_route_views', None)
+ if deferred_views is None:
+ deferred_views = self.registry.deferred_route_views = {}
+ info = dict(
+ view=view, name=name, for_=for_, permission=permission,
+ request_type=request_type, route_name=route_name,
+ request_method=request_method, request_param=request_param,
+ containment=containment, attr=attr,
+ renderer=renderer, wrapper=wrapper, xhr=xhr, accept=accept,
+ header=header, path_info=path_info, custom_predicates=(),
+ context=context, _info=u''
+ )
+ view_info = deferred_views.setdefault(route_name, [])
+ view_info.append(info)
+ return
score, predicates = _make_predicates(
xhr=xhr, request_method=request_method, path_info=path_info,
request_param=request_param, header=header, accept=accept,
- containment=containment, custom=custom_predicates)
+ containment=containment, request_type=request_type,
+ custom=custom_predicates)
derived_view = self._derive_view(view, permission, predicates, attr,
renderer, wrapper, name, accept, score)
@@ -724,13 +736,13 @@ class Configurator(object):
context = for_
r_context = context
- r_request_type = request_type
+ r_request_iface = request_iface
if r_context is None:
r_context = Interface
if not IInterface.providedBy(r_context):
r_context = implementedBy(r_context)
- if not IInterface.providedBy(r_request_type):
- r_request_type = implementedBy(r_request_type)
+ if not IInterface.providedBy(r_request_iface):
+ r_request_iface = implementedBy(r_request_iface)
registered = self.registry.adapters.registered
@@ -755,7 +767,7 @@ class Configurator(object):
old_view = None
for view_type in (IView, ISecuredView, IMultiView):
- old_view = registered((r_context, r_request_type), view_type, name)
+ old_view = registered((r_request_iface, r_context), view_type, name)
if old_view is not None:
break
@@ -767,7 +779,8 @@ class Configurator(object):
view_iface = ISecuredView
else:
view_iface = IView
- self.registry.registerAdapter(derived_view, (context, request_type),
+ self.registry.registerAdapter(derived_view,
+ (request_iface, context),
view_iface, name, info=_info)
else:
# XXX we could try to be more efficient here and register
@@ -786,8 +799,8 @@ class Configurator(object):
for view_type in (IView, ISecuredView):
# unregister any existing views
self.registry.adapters.unregister(
- (r_context, r_request_type), view_type, name=name)
- self.registry.registerAdapter(multiview, (context, request_type),
+ (r_request_iface, r_context), view_type, name=name)
+ self.registry.registerAdapter(multiview, (request_iface, context),
IMultiView, name, info=_info)
def add_route(self, name, path, view=None, view_for=None,
@@ -801,6 +814,7 @@ class Configurator(object):
renderer=None, view_renderer=None, view_header=None,
view_accept=None, view_xhr=False,
view_path_info=None, view_context=None,
+ use_global_views=False,
_info=u''):
""" Add a :term:`route configuration` to the current
configuration state, as well as possibly a :term:`view
@@ -1022,6 +1036,16 @@ class Configurator(object):
If the ``view`` argument is not provided, this argument has no
effect.
+ use_global_views
+
+ When a request matches this route, and view lookup cannot
+ find a view which has a ``route_name`` predicate argument
+ that matches the route, try to fall back to using a view
+ that otherwise matches the context, request, and view name
+ (but which does not match the route_name predicate).
+
+ .. note:: This feature is new as of :mod:`repoze.bfg` 1.2.
+
"""
# these are route predicates; if they do not match, the next route
# in the routelist will be tried
@@ -1036,9 +1060,14 @@ class Configurator(object):
request_iface = self.registry.queryUtility(IRouteRequest, name=name)
if request_iface is None:
- request_iface = route_request_iface(name)
+ bases = use_global_views and (IRequest,) or ()
+ request_iface = route_request_iface(name, bases)
self.registry.registerUtility(
request_iface, IRouteRequest, name=name)
+ deferred_views = getattr(self.registry, 'deferred_route_views', {})
+ view_info = deferred_views.pop(name, ())
+ for info in view_info:
+ self.add_view(**info)
if view:
if view_context is None:
@@ -1349,7 +1378,7 @@ class Configurator(object):
def _make_predicates(xhr=None, request_method=None, path_info=None,
request_param=None, header=None, accept=None,
- containment=None, custom=()):
+ containment=None, request_type=None, custom=()):
# Predicates are added to the predicate list in (presumed)
# computation expense order. All predicates associated with a
# view must evaluate true for the view to "match" a request.
@@ -1441,6 +1470,12 @@ def _make_predicates(xhr=None, request_method=None, path_info=None,
weight = weight - 80
predicates.append(containment_predicate)
+ if request_type is not None:
+ def request_type_predicate(context, request):
+ return request_type.providedBy(request)
+ weight = weight - 90
+ predicates.append(request_type_predicate)
+
if custom:
for predicate in custom:
weight = weight - 100
diff --git a/repoze/bfg/request.py b/repoze/bfg/request.py
index 4379419e4..4dbbbb0df 100644
--- a/repoze/bfg/request.py
+++ b/repoze/bfg/request.py
@@ -79,8 +79,8 @@ class Request(WebobRequest):
def values(self):
return self.environ.values()
-def route_request_iface(name):
- return InterfaceClass('%s_IRequest' % name)
+def route_request_iface(name, bases=()):
+ return InterfaceClass('%s_IRequest' % name, bases=bases)
def add_global_response_headers(request, headerlist):
attrs = request.__dict__
diff --git a/repoze/bfg/router.py b/repoze/bfg/router.py
index efbd301b5..4ac2481f1 100644
--- a/repoze/bfg/router.py
+++ b/repoze/bfg/router.py
@@ -5,6 +5,7 @@ from zope.interface import providedBy
from repoze.bfg.interfaces import IDebugLogger
from repoze.bfg.interfaces import IForbiddenView
from repoze.bfg.interfaces import INotFoundView
+from repoze.bfg.interfaces import IRequest
from repoze.bfg.interfaces import IRootFactory
from repoze.bfg.interfaces import IRouteRequest
from repoze.bfg.interfaces import IRouter
@@ -70,6 +71,8 @@ class Router(object):
attrs = request.__dict__
attrs['registry'] = registry
has_listeners and registry.notify(NewRequest(request))
+
+ request_iface = IRequest
try:
# find the root
@@ -82,10 +85,10 @@ class Router(object):
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:
- alsoProvides(request, iface)
+ request_iface = registry.queryUtility(
+ IRouteRequest,
+ name=route.name,
+ default=IRequest)
root_factory = route.factory or self.root_factory
root = root_factory(request)
@@ -103,9 +106,10 @@ class Router(object):
tdict['virtual_root_path'])
attrs.update(tdict)
has_listeners and registry.notify(AfterTraversal(request))
- provides = map(providedBy, (context, request))
+ context_iface = providedBy(context)
view_callable = adapters.lookup(
- provides, IView, name=view_name, default=None)
+ (request_iface, context_iface),
+ IView, name=view_name, default=None)
# invoke the view callable
if view_callable is None:
diff --git a/repoze/bfg/security.py b/repoze/bfg/security.py
index 54609bc33..822fd9ee7 100644
--- a/repoze/bfg/security.py
+++ b/repoze/bfg/security.py
@@ -122,7 +122,7 @@ def view_execution_permitted(context, request, name=''):
reg = request.registry
except AttributeError:
reg = get_current_registry() # b/c
- provides = map(providedBy, (context, request))
+ provides = map(providedBy, (request, context))
view = reg.adapters.lookup(provides, ISecuredView, name=name)
if view is None:
return Allowed(
diff --git a/repoze/bfg/tests/hybridapp/__init__.py b/repoze/bfg/tests/hybridapp/__init__.py
new file mode 100644
index 000000000..5bb534f79
--- /dev/null
+++ b/repoze/bfg/tests/hybridapp/__init__.py
@@ -0,0 +1 @@
+# package
diff --git a/repoze/bfg/tests/hybridapp/configure.zcml b/repoze/bfg/tests/hybridapp/configure.zcml
new file mode 100644
index 000000000..80e9e4fe5
--- /dev/null
+++ b/repoze/bfg/tests/hybridapp/configure.zcml
@@ -0,0 +1,42 @@
+<configure xmlns="http://namespaces.repoze.org/bfg">
+
+ <include package="repoze.bfg.includes" />
+
+ <!-- we want this view to "win" -->
+ <route
+ view=".views.route_view"
+ path="abc"
+ name="route" />
+
+ <!-- .. even though this one has a more specific context -->
+ <view
+ view=".views.global_view"
+ context="repoze.bfg.traversal.DefaultRootFactory"
+ />
+
+ <route
+ path="def"
+ name="route2"
+ />
+
+ <!-- we want this view to win for route2 even though global view with
+ context is more specific -->
+ <view
+ route_name="route2"
+ view=".views.route2_view"
+ />
+
+ <!-- the global view should be found for this route -->
+ <route
+ path="ghi"
+ name="route3"
+ use_global_views="True"
+ />
+
+ <!-- the global view should not be found for this route -->
+ <route
+ path="jkl"
+ name="route4"
+ />
+
+</configure>
diff --git a/repoze/bfg/tests/hybridapp/views.py b/repoze/bfg/tests/hybridapp/views.py
new file mode 100644
index 000000000..06423da83
--- /dev/null
+++ b/repoze/bfg/tests/hybridapp/views.py
@@ -0,0 +1,13 @@
+from webob import Response
+
+def route_view(request):
+ """ """
+ return Response('route')
+
+def global_view(request):
+ """ """
+ return Response('global')
+
+def route2_view(request):
+ """ """
+ return Response('route2')
diff --git a/repoze/bfg/tests/test_configuration.py b/repoze/bfg/tests/test_configuration.py
index 3ddaedd70..2e4ba3a18 100644
--- a/repoze/bfg/tests/test_configuration.py
+++ b/repoze/bfg/tests/test_configuration.py
@@ -30,7 +30,7 @@ class ConfiguratorTests(unittest.TestCase):
if request_iface is None:
request_iface = IRequest
return config.registry.adapters.lookup(
- (ctx_iface, request_iface), IView, name=name,
+ (request_iface, ctx_iface), IView, name=name,
default=None)
def _getRouteRequestIface(self, config, name):
@@ -563,7 +563,7 @@ class ConfiguratorTests(unittest.TestCase):
config = self._makeOne()
config.add_view(view=view)
wrapper = config.registry.adapters.lookup(
- (Interface, IRequest), ISecuredView, name='', default=None)
+ (IRequest, Interface), ISecuredView, name='', default=None)
self.assertEqual(wrapper, view)
def test_add_view_multiview_replaces_existing_view(self):
@@ -574,7 +574,7 @@ class ConfiguratorTests(unittest.TestCase):
view = lambda *arg: 'OK'
config = self._makeOne()
config.registry.registerAdapter(
- view, (Interface, IRequest), IView, name='')
+ view, (IRequest, Interface), IView, name='')
config.add_view(view=view)
wrapper = self._getViewCallable(config)
self.failUnless(IMultiView.providedBy(wrapper))
@@ -588,7 +588,7 @@ class ConfiguratorTests(unittest.TestCase):
view = lambda *arg: 'OK'
config = self._makeOne()
config.registry.registerAdapter(
- view, (Interface, IRequest), ISecuredView, name='')
+ view, (IRequest, Interface), ISecuredView, name='')
config.add_view(view=view)
wrapper = self._getViewCallable(config)
self.failUnless(IMultiView.providedBy(wrapper))
@@ -605,7 +605,7 @@ class ConfiguratorTests(unittest.TestCase):
return 'OK2'
config = self._makeOne()
config.registry.registerAdapter(
- view, (Interface, IRequest), IView, name='')
+ view, (IRequest, Interface), IView, name='')
config.add_view(view=view2, accept='text/html')
wrapper = self._getViewCallable(config)
self.failUnless(IMultiView.providedBy(wrapper))
@@ -628,7 +628,7 @@ class ConfiguratorTests(unittest.TestCase):
view.__accept__ = 'text/html'
config = self._makeOne()
config.registry.registerAdapter(
- view, (Interface, IRequest), IView, name='')
+ view, (IRequest, Interface), IView, name='')
config.add_view(view=view2)
wrapper = self._getViewCallable(config)
self.failUnless(IMultiView.providedBy(wrapper))
@@ -645,7 +645,7 @@ class ConfiguratorTests(unittest.TestCase):
from repoze.bfg.interfaces import IMultiView
view = DummyMultiView()
config = self._makeOne()
- config.registry.registerAdapter(view, (Interface, IRequest),
+ config.registry.registerAdapter(view, (IRequest, Interface),
IMultiView, name='')
view2 = lambda *arg: 'OK2'
config.add_view(view=view2)
@@ -667,7 +667,7 @@ class ConfiguratorTests(unittest.TestCase):
view2 = lambda *arg: 'OK2'
config = self._makeOne()
config.registry.registerAdapter(
- view, (ISuper, IRequest), IView, name='')
+ view, (IRequest, ISuper), IView, name='')
config.add_view(view=view2, for_=ISub)
wrapper = self._getViewCallable(config, ISuper, IRequest)
self.failIf(IMultiView.providedBy(wrapper))
@@ -676,26 +676,6 @@ class ConfiguratorTests(unittest.TestCase):
self.failIf(IMultiView.providedBy(wrapper))
self.assertEqual(wrapper(None, None), 'OK2')
- def test_add_view_multiview_request_superclass_then_subclass(self):
- from zope.interface import Interface
- from repoze.bfg.interfaces import IRequest
- from repoze.bfg.interfaces import IView
- from repoze.bfg.interfaces import IMultiView
- class ISubRequest(IRequest):
- pass
- view = lambda *arg: 'OK'
- view2 = lambda *arg: 'OK2'
- config = self._makeOne()
- config.registry.registerAdapter(
- view, (Interface, IRequest), IView, name='')
- config.add_view(view=view2, request_type=ISubRequest)
- wrapper = self._getViewCallable(config, Interface, IRequest)
- self.failIf(IMultiView.providedBy(wrapper))
- self.assertEqual(wrapper(None, None), 'OK')
- wrapper = self._getViewCallable(config, Interface, ISubRequest)
- self.failIf(IMultiView.providedBy(wrapper))
- self.assertEqual(wrapper(None, None), 'OK2')
-
def test_add_view_multiview_call_ordering(self):
from zope.interface import directlyProvides
def view1(context, request): return 'view1'
@@ -801,31 +781,44 @@ class ConfiguratorTests(unittest.TestCase):
self.assertEqual(renderer.path, 'repoze.bfg.tests:fixtures/minimal.txt')
def test_add_view_with_request_type_as_iface(self):
+ from zope.interface import directlyProvides
def view(context, request):
return 'OK'
config = self._makeOne()
config.add_view(request_type=IDummy, view=view)
- wrapper = self._getViewCallable(config, None, IDummy)
- result = wrapper(None, None)
+ wrapper = self._getViewCallable(config, None)
+ request = self._makeRequest(config)
+ directlyProvides(request, IDummy)
+ result = wrapper(None, request)
self.assertEqual(result, 'OK')
def test_add_view_with_request_type_as_noniface(self):
- from zope.interface import providedBy
- def view(context, request):
- return 'OK'
+ from repoze.bfg.exceptions import ConfigurationError
+ view = lambda *arg: 'OK'
config = self._makeOne()
- config.add_view(request_type=object, view=view)
- request_iface = providedBy(object)
- wrapper = self._getViewCallable(config, None, request_iface)
- result = wrapper(None, None)
- self.assertEqual(result, 'OK')
+ self.assertRaises(ConfigurationError,
+ config.add_view, view, '', None, None, object)
def test_add_view_with_route_name(self):
+ from zope.component import ComponentLookupError
view = lambda *arg: 'OK'
config = self._makeOne()
config.add_view(view=view, route_name='foo')
- request_type = self._getRouteRequestIface(config, 'foo')
- wrapper = self._getViewCallable(config, None, request_type)
+ self.assertEqual(len(config.registry.deferred_route_views), 1)
+ infos = config.registry.deferred_route_views['foo']
+ self.assertEqual(len(infos), 1)
+ info = infos[0]
+ self.assertEqual(info['route_name'], 'foo')
+ self.assertEqual(info['view'], view)
+ self.assertRaises(ComponentLookupError,
+ self._getRouteRequestIface, config, 'foo')
+ wrapper = self._getViewCallable(config, None)
+ self.assertEqual(wrapper, None)
+ config.add_route('foo', '/a/b')
+ request_iface = self._getRouteRequestIface(config, 'foo')
+ self.failIfEqual(request_iface, None)
+ wrapper = self._getViewCallable(config, request_iface=request_iface)
+ self.failIfEqual(wrapper, None)
self.assertEqual(wrapper(None, None), 'OK')
def test_add_view_with_request_method_true(self):
@@ -1363,7 +1356,7 @@ class ConfiguratorTests(unittest.TestCase):
self.assertEqual(route.factory.__class__, StaticRootFactory)
iface = implementedBy(StaticRootFactory)
wrapped = config.registry.adapters.lookup(
- (iface, request_type), IView, name='')
+ (request_type, iface), IView, name='')
request = self._makeRequest(config)
self.assertEqual(wrapped(None, request).__class__, PackageURLParser)
@@ -1379,7 +1372,7 @@ class ConfiguratorTests(unittest.TestCase):
self.assertEqual(route.factory.__class__, StaticRootFactory)
iface = implementedBy(StaticRootFactory)
wrapped = config.registry.adapters.lookup(
- (iface, request_type), IView, name='')
+ (request_type, iface), IView, name='')
request = self._makeRequest(config)
self.assertEqual(wrapped(None, request).__class__, PackageURLParser)
@@ -1398,7 +1391,7 @@ class ConfiguratorTests(unittest.TestCase):
self.assertEqual(route.factory.__class__, StaticRootFactory)
iface = implementedBy(StaticRootFactory)
wrapped = config.registry.adapters.lookup(
- (iface, request_type), IView, name='')
+ (request_type, iface), IView, name='')
request = self._makeRequest(config)
self.assertEqual(wrapped(None, request).__class__, StaticURLParser)
diff --git a/repoze/bfg/tests/test_integration.py b/repoze/bfg/tests/test_integration.py
index 1c6fdd01e..6affe24e1 100644
--- a/repoze/bfg/tests/test_integration.py
+++ b/repoze/bfg/tests/test_integration.py
@@ -36,7 +36,7 @@ class WGSIAppPlusBFGViewTests(unittest.TestCase):
config = Configurator()
config.scan(test_integration)
reg = config.registry
- view = reg.adapters.lookup((INothing, IRequest), IView, name='')
+ view = reg.adapters.lookup((IRequest, INothing), IView, name='')
self.assertEqual(view, wsgiapptest)
here = os.path.dirname(__file__)
@@ -113,6 +113,29 @@ class TestCCBug(TwillBase):
self.assertEqual(browser.get_code(), 200)
self.assertEqual(browser.get_html(), 'juri')
+class TestHybridApp(TwillBase):
+ # make sure views registered for a route "win" over views registered
+ # without one, even though the context of the non-route view may
+ # be more specific than the route view.
+ config = 'repoze.bfg.tests.hybridapp:configure.zcml'
+ def test_it(self):
+ import twill.commands
+ browser = twill.commands.get_browser()
+ browser.go('http://localhost:6543/')
+ self.assertEqual(browser.get_code(), 200)
+ self.assertEqual(browser.get_html(), 'global')
+ browser.go('http://localhost:6543/abc')
+ self.assertEqual(browser.get_code(), 200)
+ self.assertEqual(browser.get_html(), 'route')
+ browser.go('http://localhost:6543/def')
+ self.assertEqual(browser.get_code(), 200)
+ self.assertEqual(browser.get_html(), 'route2')
+ browser.go('http://localhost:6543/ghi')
+ self.assertEqual(browser.get_code(), 200)
+ self.assertEqual(browser.get_html(), 'global')
+ browser.go('http://localhost:6543/jkl')
+ self.assertEqual(browser.get_code(), 404)
+
class DummyContext(object):
pass
diff --git a/repoze/bfg/tests/test_router.py b/repoze/bfg/tests/test_router.py
index c1d60ae9a..0d7bee720 100644
--- a/repoze/bfg/tests/test_router.py
+++ b/repoze/bfg/tests/test_router.py
@@ -354,7 +354,7 @@ class TestRouter(unittest.TestCase):
response.app_iter = ['Hello world']
view = DummyView(response)
environ = self._makeEnviron()
- self._registerView(view, '', IContext, IRequest)
+ self._registerView(view, '', IRequest, IContext)
router = self._makeOne()
start_response = DummyStartResponse()
result = router(environ, start_response)
@@ -381,7 +381,7 @@ class TestRouter(unittest.TestCase):
response = DummyResponse()
view = DummyView(response)
environ = self._makeEnviron()
- self._registerView(view, '', IContext, IRequest)
+ self._registerView(view, '', IRequest, IContext)
router = self._makeOne()
start_response = DummyStartResponse()
result = router(environ, start_response)
@@ -400,7 +400,7 @@ class TestRouter(unittest.TestCase):
response = DummyResponse()
view = DummyView(response, raise_unauthorized=True)
environ = self._makeEnviron()
- self._registerView(view, '', IContext, IRequest)
+ self._registerView(view, '', IRequest, IContext)
router = self._makeOne()
start_response = DummyStartResponse()
response = router(environ, start_response)
@@ -419,7 +419,7 @@ class TestRouter(unittest.TestCase):
response = DummyResponse()
view = DummyView(response, raise_notfound=True)
environ = self._makeEnviron()
- self._registerView(view, '', IContext, IRequest)
+ self._registerView(view, '', IRequest, IContext)
router = self._makeOne()
start_response = DummyStartResponse()
response = router(environ, start_response)
@@ -441,7 +441,7 @@ class TestRouter(unittest.TestCase):
request.global_response_headers = [('b', 2)]
return response
environ = self._makeEnviron()
- self._registerView(view, '', IContext, IRequest)
+ self._registerView(view, '', IRequest, IContext)
router = self._makeOne()
start_response = DummyStartResponse()
router(environ, start_response)
@@ -519,7 +519,6 @@ class TestRouter(unittest.TestCase):
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_call_route_matches_doesnt_overwrite_subscriber_iface(self):
from repoze.bfg.interfaces import INewRequest
@@ -560,7 +559,6 @@ class TestRouter(unittest.TestCase):
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))
self.failUnless(IFoo.providedBy(request))
def test_root_factory_raises_notfound(self):
diff --git a/repoze/bfg/tests/test_view.py b/repoze/bfg/tests/test_view.py
index 94b896893..994eb8c66 100644
--- a/repoze/bfg/tests/test_view.py
+++ b/repoze/bfg/tests/test_view.py
@@ -9,10 +9,9 @@ class BaseTest(object):
def tearDown(self):
cleanUp()
- def _registerView(self, reg, app, name, *for_):
+ def _registerView(self, reg, app, name):
from repoze.bfg.interfaces import IRequest
- if not for_:
- for_ = (IContext, IRequest)
+ for_ = (IRequest, IContext)
from repoze.bfg.interfaces import IView
reg.registerAdapter(app, for_, IView, name)
diff --git a/repoze/bfg/tests/test_zcml.py b/repoze/bfg/tests/test_zcml.py
index abcf8863e..4d29c8373 100644
--- a/repoze/bfg/tests/test_zcml.py
+++ b/repoze/bfg/tests/test_zcml.py
@@ -37,7 +37,7 @@ class TestViewDirective(unittest.TestCase):
register = action['callable']
register()
reg = get_current_registry()
- wrapper = reg.adapters.lookup((IDummy, IRequest), IView, name='')
+ wrapper = reg.adapters.lookup((IRequest, IDummy), IView, name='')
request = DummyRequest()
request.method = 'GET'
self.assertEqual(wrapper.__predicated__(None, request), True)
@@ -45,12 +45,12 @@ class TestViewDirective(unittest.TestCase):
self.assertEqual(wrapper.__predicated__(None, request), False)
def test_request_type_asinterfacestring(self):
+ from zope.interface import directlyProvides
from repoze.bfg.threadlocal import get_current_registry
from repoze.bfg.interfaces import IView
- from repoze.bfg.interfaces import IViewPermission
from repoze.bfg.interfaces import IRequest
context = DummyContext(IDummy)
- view = lambda *arg: None
+ view = lambda *arg: 'OK'
self._callFUT(context, 'repoze.view', IDummy, view=view,
request_type='whatever')
actions = context.actions
@@ -61,11 +61,13 @@ class TestViewDirective(unittest.TestCase):
register = actions[0]['callable']
register()
reg = get_current_registry()
- regview = reg.adapters.lookup((IDummy, IDummy), IView, name='')
- self.assertEqual(view, regview)
+ regview = reg.adapters.lookup((IRequest, IDummy), IView, name='')
+ self.assertNotEqual(view, regview)
+ request = DummyRequest()
+ directlyProvides(request, IDummy)
+ result = regview(None, request)
+ self.assertEqual(result, 'OK')
self.failIf(hasattr(view, '__call_permissive__'))
- perm = reg.adapters.lookup((IDummy, IRequest), IViewPermission, name='')
- self.assertEqual(perm, None)
def test_with_dotted_renderer(self):
from repoze.bfg.threadlocal import get_current_registry
@@ -89,7 +91,7 @@ class TestViewDirective(unittest.TestCase):
self.assertEqual(actions[0]['discriminator'], discrim)
register = actions[0]['callable']
register()
- regview = reg.adapters.lookup((IDummy, IRequest), IView, name='')
+ regview = reg.adapters.lookup((IRequest, IDummy), IView, name='')
self.assertEqual(regview(None, None).body, 'OK')
def test_with_custom_predicates(self):
@@ -114,7 +116,7 @@ class TestViewDirective(unittest.TestCase):
self.assertEqual(actions[0]['discriminator'], discrim)
register = actions[0]['callable']
register()
- regview = reg.adapters.lookup((IDummy, IRequest), IView, name='')
+ regview = reg.adapters.lookup((IRequest, IDummy), IView, name='')
self.assertEqual(regview(None, None), 'OK')
def test_context_trumps_for(self):
@@ -135,7 +137,7 @@ class TestViewDirective(unittest.TestCase):
self.assertEqual(actions[0]['discriminator'], discrim)
register = actions[0]['callable']
register()
- regview = reg.adapters.lookup((IDummy, IRequest), IView, name='')
+ regview = reg.adapters.lookup((IRequest, IDummy), IView, name='')
self.assertEqual(regview(None, None), 'OK')
def test_with_for(self):
@@ -155,7 +157,7 @@ class TestViewDirective(unittest.TestCase):
self.assertEqual(actions[0]['discriminator'], discrim)
register = actions[0]['callable']
register()
- regview = reg.adapters.lookup((IDummy, IRequest), IView, name='')
+ regview = reg.adapters.lookup((IRequest, IDummy), IView, name='')
self.assertEqual(regview(None, None), 'OK')
class TestNotFoundDirective(unittest.TestCase):
@@ -510,10 +512,10 @@ class TestRouteDirective(unittest.TestCase):
reg = get_current_registry()
request_type = reg.getUtility(IRouteRequest, 'name')
view_discriminator = view_action['discriminator']
- discrim = ('view', None, '', request_type, IView, None, None, None,
+ discrim = ('view', None, '', None, IView, None, None, None,
'name', None, False, None, None, None)
self.assertEqual(view_discriminator, discrim)
- wrapped = reg.adapters.lookup((Interface, request_type), IView, name='')
+ wrapped = reg.adapters.lookup((request_type, Interface), IView, name='')
self.failUnless(wrapped)
def test_with_view_and_view_context(self):
@@ -537,10 +539,10 @@ class TestRouteDirective(unittest.TestCase):
reg = get_current_registry()
request_type = reg.getUtility(IRouteRequest, 'name')
view_discriminator = view_action['discriminator']
- discrim = ('view', IDummy, '', request_type, IView, None, None, None,
+ discrim = ('view', IDummy, '', None, IView, None, None, None,
'name', None, False, None, None, None)
self.assertEqual(view_discriminator, discrim)
- wrapped = reg.adapters.lookup((IDummy, request_type), IView, name='')
+ wrapped = reg.adapters.lookup((request_type, IDummy), IView, name='')
self.failUnless(wrapped)
def test_with_view_context_trumps_view_for(self):
@@ -567,10 +569,10 @@ class TestRouteDirective(unittest.TestCase):
reg = get_current_registry()
request_type = reg.getUtility(IRouteRequest, 'name')
view_discriminator = view_action['discriminator']
- discrim = ('view', IDummy, '', request_type, IView, None, None, None,
+ discrim = ('view', IDummy, '', None, IView, None, None, None,
'name', None, False, None, None, None)
self.assertEqual(view_discriminator, discrim)
- wrapped = reg.adapters.lookup((IDummy, request_type), IView, name='')
+ wrapped = reg.adapters.lookup((request_type, IDummy), IView, name='')
self.failUnless(wrapped)
def test_with_dotted_renderer(self):
@@ -604,10 +606,10 @@ class TestRouteDirective(unittest.TestCase):
view_action = actions[1]
request_type = reg.getUtility(IRouteRequest, 'name')
view_discriminator = view_action['discriminator']
- discrim = ('view', None, '', request_type, IView, None, None, None,
+ discrim = ('view', None, '', None, IView, None, None, None,
'name', None, False, None, None, None)
self.assertEqual(view_discriminator, discrim)
- wrapped = reg.adapters.lookup((Interface, request_type), IView, name='')
+ wrapped = reg.adapters.lookup((request_type, Interface), IView, name='')
self.failUnless(wrapped)
request = DummyRequest()
result = wrapped(None, request)
@@ -674,7 +676,7 @@ class TestStaticDirective(unittest.TestCase):
self.assertEqual(discriminator[4], IView)
iface = implementedBy(StaticRootFactory)
request_type = reg.getUtility(IRouteRequest, 'name')
- view = reg.adapters.lookup((iface, request_type), IView, name='')
+ view = reg.adapters.lookup((request_type, iface), IView, name='')
request = DummyRequest()
self.assertEqual(view(None, request).__class__, PackageURLParser)
diff --git a/repoze/bfg/view.py b/repoze/bfg/view.py
index bb8972329..02a53824f 100644
--- a/repoze/bfg/view.py
+++ b/repoze/bfg/view.py
@@ -67,7 +67,7 @@ def render_view_to_response(context, request, name='', secure=True):
was disallowed.
If ``secure`` is ``False``, no permission checking is done."""
- provides = map(providedBy, (context, request))
+ provides = map(providedBy, (request, context))
try:
reg = request.registry
except AttributeError:
diff --git a/repoze/bfg/zcml.py b/repoze/bfg/zcml.py
index c09a52fcd..17bfbb66f 100644
--- a/repoze/bfg/zcml.py
+++ b/repoze/bfg/zcml.py
@@ -6,6 +6,7 @@ from zope.configuration.fields import GlobalInterface
from zope.configuration.fields import GlobalObject
from zope.configuration.fields import Tokens
+from zope.interface.interfaces import IInterface
from zope.interface import Interface
from zope.interface import implementedBy
from zope.interface import providedBy
@@ -181,6 +182,9 @@ def view(
if request_type is not None:
request_type = _context.resolve(request_type)
+ if not IInterface.providedBy(request_type):
+ raise ConfigurationError(
+ 'request_type must be an interface, not %s' % request_type)
if renderer and '.' in renderer:
renderer = path_spec(_context, renderer)
@@ -256,6 +260,7 @@ class IRouteDirective(Interface):
required=False,
value_type=GlobalObject()
)
+ use_global_views = Bool(title=u'use_global_views', required=False)
def route(_context, name, path, view=None, view_for=None,
permission=None, factory=None, for_=None,
@@ -265,7 +270,8 @@ def route(_context, name, path, view=None, view_for=None,
view_request_param=None, view_containment=None, view_attr=None,
renderer=None, view_renderer=None, view_header=None,
view_accept=None, view_xhr=False,
- view_path_info=None, view_context=None):
+ view_path_info=None, view_context=None,
+ use_global_views=False):
""" Handle ``route`` ZCML directives
"""
# the strange ordering of the request kw args above is for b/w
@@ -307,6 +313,7 @@ def route(_context, name, path, view=None, view_for=None,
view_accept=view_accept,
view_xhr=view_xhr,
view_path_info=view_path_info,
+ use_global_views=use_global_views,
_info=_context.info
)
@@ -327,7 +334,7 @@ def route(_context, name, path, view=None, view_for=None,
reg.registerUtility(request_iface, IRouteRequest, name=name)
_context.action(
discriminator = (
- 'view', view_context, '', request_iface, IView,
+ 'view', view_context, '', None, IView,
view_containment, view_request_param, view_request_method,
name, view_attr, view_xhr, view_accept, view_header,
view_path_info),