diff options
| -rw-r--r-- | CHANGES.txt | 37 | ||||
| -rw-r--r-- | docs/narr/urldispatch.rst | 6 | ||||
| -rw-r--r-- | docs/zcml/route.rst | 8 | ||||
| -rw-r--r-- | repoze/bfg/configuration.py | 81 | ||||
| -rw-r--r-- | repoze/bfg/request.py | 4 | ||||
| -rw-r--r-- | repoze/bfg/router.py | 16 | ||||
| -rw-r--r-- | repoze/bfg/security.py | 2 | ||||
| -rw-r--r-- | repoze/bfg/tests/hybridapp/__init__.py | 1 | ||||
| -rw-r--r-- | repoze/bfg/tests/hybridapp/configure.zcml | 42 | ||||
| -rw-r--r-- | repoze/bfg/tests/hybridapp/views.py | 13 | ||||
| -rw-r--r-- | repoze/bfg/tests/test_configuration.py | 79 | ||||
| -rw-r--r-- | repoze/bfg/tests/test_integration.py | 25 | ||||
| -rw-r--r-- | repoze/bfg/tests/test_router.py | 12 | ||||
| -rw-r--r-- | repoze/bfg/tests/test_view.py | 5 | ||||
| -rw-r--r-- | repoze/bfg/tests/test_zcml.py | 42 | ||||
| -rw-r--r-- | repoze/bfg/view.py | 2 | ||||
| -rw-r--r-- | repoze/bfg/zcml.py | 11 |
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), |
