diff options
| -rw-r--r-- | CHANGES.txt | 20 | ||||
| -rw-r--r-- | TODO.txt | 4 | ||||
| -rw-r--r-- | docs/glossary.rst | 7 | ||||
| -rw-r--r-- | pyramid/config.py | 306 | ||||
| -rw-r--r-- | pyramid/tests/test_config.py | 1337 | ||||
| -rw-r--r-- | pyramid/tests/test_view.py | 3 | ||||
| -rw-r--r-- | pyramid/view.py | 4 |
7 files changed, 918 insertions, 763 deletions
diff --git a/CHANGES.txt b/CHANGES.txt index 6fe68fb48..4279f950d 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -15,12 +15,21 @@ Features - ``config.add_view`` now accepts a ``decorator`` keyword argument, a callable which will decorate the view callable before it is added to the registry. +- add a ``add_view_mapper`` API to Configurator. This API allows you to add + a named implementation of a ``pyramid.interfaces.IViewMapperFactory`` + interface. Its name can be passed as a ``view_mapper`` argument to + ``config.add_view``. A view mapper allows objects that are meant to be + used as view callables to have an arbitrary argument list and an arbitrary + result. This feature will be used by Pyramid extension developers, not by + "civilians". + +- New constructor argument to Configurator: ``default_view_mapper``. Useful + to create systems that have view callables with alternate default calling + conventions. + - ``config.add_view`` now accepts a ``view_mapper`` keyword argument, which - should be a class which implements the new - ``pyramid.interfaces.IViewMapperFactory`` interface. Use of an alternate - view mapper allows objects that are meant to be used as view callables to - have an arbitrary argument list and an arbitrary result. This feature will - be used by Pyramid extension developers, not by "civilians". + should either be ``None`` or the name of a view mapper previously + registered via ``add_view_mapper``. - If a handler class provides an ``__action_decorator__`` attribute (usually a classmethod or staticmethod), use that as the decorator for each view @@ -90,6 +99,7 @@ Internals promoted to an API method. If you were overriding this method, you'll now need to override it as ``unauthenticated_userid`` instead. +- Remove (non-API) function of config.py named _map_view. 1.0a8 (2010-12-27) ================== @@ -16,6 +16,10 @@ Must-Have (before 1.0) - Allow ``decorator=`` and ``view_mapper=`` to be passed via ZCML and the ``view_config`` decorator. +- Document ``Configurator.add_view_mapper``. + +- Consider the ability to defer the choice of view mapper. + Should-Have ----------- diff --git a/docs/glossary.rst b/docs/glossary.rst index 49d273197..4d0c53d60 100644 --- a/docs/glossary.rst +++ b/docs/glossary.rst @@ -850,6 +850,11 @@ Glossary WSGI middleware which can display debuggable traceback information in the browser when an exception is raised by a Pyramid application. See http://pypi.python.org/pypi/WebError . - + view mapper + + A view mapper is a class which implements the + :class:`pyramid.interfaces.IViewMapperFactory` interface, which performs + view argument and return value mapping. This is a plug point for + extension builders, not normally used by "civilians". diff --git a/pyramid/config.py b/pyramid/config.py index 01d453cd1..c890a0c59 100644 --- a/pyramid/config.py +++ b/pyramid/config.py @@ -246,7 +246,13 @@ class Configurator(object): ``autocommit`` is ``True``. If a conflict is detected a ``ConfigurationConflictError`` will be raised. Calling :meth:`pyramid.config.Configurator.make_wsgi_app` always implies a final - commit.""" + commit. + + If ``default_view_mapper`` is passed, it will be used as the default + view mapper factory for view configurations that don't otherwise specify + one (see :class:`pyramid.interfaces.IViewMapperFactory`). + If a default_view_mapper is not passed, a superdefault view mapper will + be used. """ manager = manager # for testing injection venusian = venusian # for testing injection @@ -267,6 +273,7 @@ class Configurator(object): renderer_globals_factory=None, default_permission=None, session_factory=None, + default_view_mapper=None, autocommit=False, ): if package is None: @@ -292,6 +299,7 @@ class Configurator(object): renderer_globals_factory=renderer_globals_factory, default_permission=default_permission, session_factory=session_factory, + default_view_mapper=default_view_mapper, ) def _set_settings(self, mapping): @@ -596,9 +604,8 @@ class Configurator(object): authentication_policy=None, authorization_policy=None, renderers=DEFAULT_RENDERERS, debug_logger=None, locale_negotiator=None, request_factory=None, - renderer_globals_factory=None, - default_permission=None, - session_factory=None): + renderer_globals_factory=None, default_permission=None, + session_factory=None, default_view_mapper=None): """ When you pass a non-``None`` ``registry`` argument to the :term:`Configurator` constructor, no initial 'setup' is performed against the registry. This is because the registry you pass in may @@ -644,7 +651,13 @@ class Configurator(object): self.set_default_permission(default_permission) if session_factory is not None: self.set_session_factory(session_factory) + # commit before adding default_view_mapper, as the + # default_exceptionresponse_view above requires the superdefault view + # mapper self.commit() + if default_view_mapper is not None: + self.add_view_mapper(None, default_view_mapper) + self.commit() # getSiteManager is a unit testing dep injection def hook_zca(self, getSiteManager=None): @@ -1140,11 +1153,11 @@ class Configurator(object): decorator A function which will be used to decorate the registered - :term:`view callable`. The decorator function will be - called with the view callable as a single argument, and it - must return a replacement view callable which accepts the - same arguments and returns the same type of values as the - original function. + :term:`view callable`. The decorator function will be called with + the view callable as a single argument. The view callable it is + passed will accept ``(context, request)`. The decorator must + return a replacement view callable which also accepts ``(context, + request)``. Predicate Arguments @@ -1285,14 +1298,12 @@ class Configurator(object): view_mapper - A class implementing the - :class:`pyramid.interfaces.IViewMapperFactory` interface, which - performs view argument and response mapping. By default it is - ``None``, which indicates that the view should use the default view - mapper. This plug-point is useful for Pyramid extension - developers, but it's not very useful for 'civilians' who are - just developing stock Pyramid applications. Pay no attention to - the man behind the curtain. + The name of a previously registered :term:`view mapper` or + ``None``. By default it is ``None``, which indicates that the view + should use the default view mapper. This plug-point is useful for + Pyramid extension developers, but it's not very useful for + 'civilians' who are just developing stock Pyramid applications. + Pay no attention to the man behind the curtain. """ view = self.maybe_dotted(view) @@ -1369,19 +1380,20 @@ class Configurator(object): # intent: will be None if no default permission is registered permission = self.registry.queryUtility(IDefaultPermission) - # NO_PERMISSION_REQUIRED handled by _secure_view - derived_view = ViewDeriver(registry=self.registry, - permission=permission, - predicates=predicates, - attr=attr, - renderer=renderer, - wrapper_viewname=wrapper, - viewname=name, - accept=accept, - order=order, - phash=phash, - decorator=decorator, - view_mapper=view_mapper)(view) + # __no_permission_required__ handled by _secure_view + deriver = ViewDeriver(registry=self.registry, + permission=permission, + predicates=predicates, + attr=attr, + renderer=renderer, + wrapper_viewname=wrapper, + viewname=name, + accept=accept, + order=order, + phash=phash, + decorator=decorator, + view_mapper=view_mapper) + derived_view = deriver(view) registered = self.registry.adapters.registered @@ -2163,6 +2175,36 @@ class Configurator(object): self.action(IDefaultPermission, None) @action_method + def add_view_mapper(self, name, mapper): + """ + Adding a :term:`view mapper` makes it possible to make use of + :term:`view callable` objects which implement a different call + signature than the ones described in the :app:`Pyramid` + documentation. This is an advanced feature, not usually consumed by + 'civilians'. + + ``name`` must be a string or ``None``. If ``name`` is a string, the + view mapper will be registered under the specified name for + consumption by extensions. If ``name`` is ``None``, the provided + mapper will become the *default* view mapper to be used by all + subsequent :term:`view configuration` registrations, as if you had + passed a ``default_view_mapper`` argument to the + :class:`pyramid.config.Configurator` constructor. + + The ``mapper`` should argument be an object implementing + :class:`pyramid.interfaces.IViewMapperFactory` or a :term:`dotted + Python name` to such an object. + + See also :ref:`using_an_alternate_view_mapper`. + """ + if mapper is not None: + mapper = self.maybe_dotted(mapper) + if name is None: + name = '' + self.registry.registerUtility(mapper, IViewMapperFactory, name=name) + self.action((IViewMapperFactory, name), None) + + @action_method def set_session_factory(self, session_factory): """ Configure the application with a :term:`session factory`. If @@ -2738,19 +2780,29 @@ class ViewDeriver(object): self.logger = self.registry.queryUtility(IDebugLogger) def __call__(self, view): - mapper = self.kw.get('view_mapper') - if mapper is None: - mapper = DefaultViewMapper - view = mapper(**self.kw)(view) return self.attr_wrapped_view( self.predicated_view( self.authdebug_view( self.secured_view( - self.owrap_view( - view))))) + self.owrapped_view( + self.decorated_view( + self.rendered_view( + self.mapped_view(view)))))))) @wraps_view - def owrap_view(self, view): + def mapped_view(self, view): + mapper_name = self.kw.get('view_mapper') + mapper = self.registry.queryUtility(IViewMapperFactory, + name=mapper_name) + + if mapper is None: + mapper = DefaultViewMapper + + mapped_view = mapper(**self.kw)(view) + return mapped_view + + @wraps_view + def owrapped_view(self, view): wrapper_viewname = self.kw.get('wrapper_viewname') viewname = self.kw.get('viewname') if not wrapper_viewname: @@ -2864,29 +2916,53 @@ class ViewDeriver(object): attr_view.__phash__ = phash return attr_view + @wraps_view + def rendered_view(self, view): + wrapped_view = view + renderer = self.kw.get('renderer') + if renderer is None: + return view + + def _rendered_view(context, request): + response = wrapped_view(context, request) + if not is_response(response): + attrs = getattr(request, '__dict__', {}) + if '__view__' in attrs: + view_inst = attrs.pop('__view__') + else: + view_inst = getattr(wrapped_view, '__original_view__', + wrapped_view) + return renderer.render_view(request, response, view_inst, + context) + return response + + return _rendered_view + + @wraps_view + def decorated_view(self, view): + decorator = self.kw.get('decorator') + if decorator is None: + return view + return decorator(view) + class DefaultViewMapper(object): implements(IViewMapperFactory) def __init__(self, **kw): - self.renderer = kw.get('renderer') self.attr = kw.get('attr') - self.decorator = kw.get('decorator') def __call__(self, view): - decorator = self.decorator if inspect.isclass(view): - view = preserve_view_attrs(view, self.map_class(view)) + view = self.map_class(view) else: - view = preserve_view_attrs(view, self.map_nonclass(view)) - if decorator is not None: - view = preserve_view_attrs(view, decorator(view)) + view = self.map_nonclass(view) return view def map_class(self, view): - ronly = self.requestonly(view) + ronly = requestonly(view, self.attr) if ronly: - mapped_view = self._map_class_requestonly(view) + mapped_view = self.map_class_requestonly(view) else: - mapped_view = self._map_class_native(view) + mapped_view = self.map_class_native(view) return mapped_view def map_nonclass(self, view): @@ -2894,47 +2970,41 @@ class DefaultViewMapper(object): # view unless it actually requires wrapping (to avoid function call # overhead). mapped_view = view - ronly = self.requestonly(view) + ronly = requestonly(view, self.attr) if ronly: - mapped_view = self._map_nonclass_requestonly(view) + mapped_view = self.map_nonclass_requestonly(view) elif self.attr: - mapped_view = self._map_nonclass_attr(view) - elif self.renderer is not None: - mapped_view = self._map_nonclass_rendered(view) + mapped_view = self.map_nonclass_attr(view) return mapped_view - def _map_class_requestonly(self, view): + def map_class_requestonly(self, view): # its a class that has an __init__ which only accepts request attr = self.attr def _class_requestonly_view(context, request): inst = view(request) + request.__view__ = inst if attr is None: response = inst() else: response = getattr(inst, attr)() - if self.renderer is not None and not is_response(response): - response = self.renderer.render_view(request, response, view, - context) return response return _class_requestonly_view - def _map_class_native(self, view): + def map_class_native(self, view): # its a class that has an __init__ which accepts both context and # request attr = self.attr def _class_view(context, request): inst = view(context, request) + request.__view__ = inst if attr is None: response = inst() else: response = getattr(inst, attr)() - if self.renderer is not None and not is_response(response): - response = self.renderer.render_view(request, response, view, - context) return response return _class_view - def _map_nonclass_requestonly(self, view): + def map_nonclass_requestonly(self, view): # its a function that has a __call__ which accepts only a single # request argument attr = self.attr @@ -2943,86 +3013,58 @@ class DefaultViewMapper(object): response = view(request) else: response = getattr(view, attr)(request) - if self.renderer is not None and not is_response(response): - response = self.renderer.render_view(request, response, view, - context) return response return _requestonly_view - def _map_nonclass_attr(self, view): + def map_nonclass_attr(self, view): # its a function that has a __call__ which accepts both context and # request, but still has an attr - attr = self.attr def _attr_view(context, request): - response = getattr(view, attr)(context, request) - if self.renderer is not None and not is_response(response): - response = self.renderer.render_view(request, response, view, - context) + response = getattr(view, self.attr)(context, request) return response return _attr_view - def _map_nonclass_rendered(self, view): - # it's a function that has a __call__ that accepts both context and - # request, but requires rendering - def _rendered_view(context, request): - response = view(context, request) - if self.renderer is not None and not is_response(response): - response = self.renderer.render_view(request, response, view, - context) - return response - return _rendered_view - - def requestonly(self, view): - attr = self.attr - if attr is None: - attr = '__call__' - if inspect.isfunction(view): - fn = view - elif inspect.isclass(view): - try: - fn = view.__init__ - except AttributeError: - return False - else: - try: - fn = getattr(view, attr) - except AttributeError: - return False - +def requestonly(view, attr=None): + if attr is None: + attr = '__call__' + if inspect.isfunction(view): + fn = view + elif inspect.isclass(view): try: - argspec = inspect.getargspec(fn) - except TypeError: + fn = view.__init__ + except AttributeError: return False - - args = argspec[0] - defaults = argspec[3] - - if hasattr(fn, 'im_func'): - # it's an instance method - if not args: - return False - args = args[1:] - if not args: + else: + try: + fn = getattr(view, attr) + except AttributeError: return False - if len(args) == 1: - return True + try: + argspec = inspect.getargspec(fn) + except TypeError: + return False - elif args[0] == 'request': - if len(args) - len(defaults) == 1: - return True + args = argspec[0] + defaults = argspec[3] + if hasattr(fn, 'im_func'): + # it's an instance method + if not args: + return False + args = args[1:] + if not args: return False + if len(args) == 1: + return True -def isexception(o): - if IInterface.providedBy(o): - if IException.isEqualOrExtendedBy(o): + elif args[0] == 'request': + if len(args) - len(defaults) == 1: return True - return ( - isinstance(o, Exception) or - (inspect.isclass(o) and (issubclass(o, Exception))) - ) + + return False + class ActionPredicate(object): action_name = 'action' @@ -3069,20 +3111,18 @@ def translator(msg): localizer = get_localizer(request) return localizer.translate(msg) -# b/c -def _map_view(view, registry, attr=None, renderer=None): - return DefaultViewMapper(registry=registry, attr=attr, - renderer=renderer)(view) - -# b/c -def requestonly(view, attr=None): - """ Return true of the class or callable accepts only a request argument, - as opposed to something that accepts context, request """ - return DefaultViewMapper(attr=attr).requestonly(view) - def is_response(ob): if ( hasattr(ob, 'app_iter') and hasattr(ob, 'headerlist') and hasattr(ob, 'status') ): return True return False +def isexception(o): + if IInterface.providedBy(o): + if IException.isEqualOrExtendedBy(o): + return True + return ( + isinstance(o, Exception) or + (inspect.isclass(o) and (issubclass(o, Exception))) + ) + diff --git a/pyramid/tests/test_config.py b/pyramid/tests/test_config.py index 1760d119a..ceb62612f 100644 --- a/pyramid/tests/test_config.py +++ b/pyramid/tests/test_config.py @@ -63,27 +63,11 @@ class ConfiguratorTests(unittest.TestCase): config.registry.registerHandler(subscriber, (event_iface,)) return L - def _registerLogger(self, config): - from pyramid.interfaces import IDebugLogger - logger = DummyLogger() - config.registry.registerUtility(logger, IDebugLogger) - return logger - def _makeRequest(self, config): request = DummyRequest() request.registry = config.registry return request - def _registerSecurityPolicy(self, config, permissive): - from pyramid.interfaces import IAuthenticationPolicy - from pyramid.interfaces import IAuthorizationPolicy - policy = DummySecurityPolicy(permissive) - config.registry.registerUtility(policy, IAuthenticationPolicy) - config.registry.registerUtility(policy, IAuthorizationPolicy) - - def _registerSettings(self, config, **settings): - config.registry.settings = settings - def test_ctor_no_registry(self): import sys from pyramid.interfaces import ISettings @@ -196,6 +180,13 @@ class ConfiguratorTests(unittest.TestCase): config = self._makeOne(session_factory='factory') self.assertEqual(config.registry.getUtility(ISessionFactory), 'factory') + def test_ctor_default_view_mapper(self): + from pyramid.interfaces import IViewMapperFactory + mapper = object() + config = self._makeOne(default_view_mapper=mapper) + self.assertEqual(config.registry.getUtility(IViewMapperFactory), + mapper) + def test_with_package_module(self): from pyramid.tests import test_configuration import pyramid.tests @@ -790,8 +781,10 @@ class ConfiguratorTests(unittest.TestCase): config = self._makeOne(autocommit=True) config.add_view(view=view) wrapper = self._getViewCallable(config) - result = wrapper(None, None) + request = self._makeRequest(config) + result = wrapper(None, request) self.assertEqual(result, 'OK') + self.assertEqual(request.__view__.__class__, view) def test_add_view_as_oldstyle_class_requestonly(self): class view: @@ -803,8 +796,11 @@ class ConfiguratorTests(unittest.TestCase): config = self._makeOne(autocommit=True) config.add_view(view=view) wrapper = self._getViewCallable(config) - result = wrapper(None, None) + + request = self._makeRequest(config) + result = wrapper(None, request) self.assertEqual(result, 'OK') + self.assertEqual(request.__view__.__class__, view) def test_add_view_context_as_class(self): from zope.interface import implementedBy @@ -1427,8 +1423,6 @@ class ConfiguratorTests(unittest.TestCase): self.assertEqual(result.settings, settings) def test_add_view_with_default_renderer(self): - import pyramid.tests - from pyramid.interfaces import ISettings class view(object): def __init__(self, context, request): self.request = request @@ -2340,7 +2334,8 @@ class ConfiguratorTests(unittest.TestCase): request_type = self._getRouteRequestIface(config, 'name') wrapper = self._getViewCallable(config, None, request_type) self._assertRoute(config, 'name', 'path') - self.assertEqual(wrapper(None, None), 'OK') + request = self._makeRequest(config) + self.assertEqual(wrapper(None, request), 'OK') def test_add_route_with_view_renderer_alias(self): config = self._makeOne(autocommit=True) @@ -2437,6 +2432,52 @@ class ConfiguratorTests(unittest.TestCase): else: # pragma: no cover raise AssertionError + def test_derive_view_function(self): + def view(request): + return 'OK' + config = self._makeOne() + result = config.derive_view(view) + self.failIf(result is view) + self.assertEqual(result(None, None), 'OK') + + def test_derive_view_dottedname(self): + config = self._makeOne() + result = config.derive_view( + 'pyramid.tests.test_config.dummy_view') + self.failIf(result is dummy_view) + self.assertEqual(result(None, None), 'OK') + + def test_derive_view_with_default_renderer_no_explicit_renderer(self): + config = self._makeOne() + class moo(object): + def __init__(self, view): + pass + def __call__(self, *arg, **kw): + return 'moo' + config.add_renderer(None, moo) + def view(request): + return 'OK' + result = config.derive_view(view) + self.failIf(result is view) + self.assertEqual(result(None, None).body, 'moo') + + def test_derive_view_with_default_renderer_with_explicit_renderer(self): + class moo(object): pass + class foo(object): + def __init__(self, view): + pass + def __call__(self, *arg, **kw): + return 'foo' + def view(request): + return 'OK' + config = self._makeOne() + config.add_renderer(None, moo) + config.add_renderer('foo', foo) + result = config.derive_view(view, renderer='foo') + self.failIf(result is view) + request = self._makeRequest(config) + self.assertEqual(result(None, request).body, 'foo') + def test__override_not_yet_registered(self): from pyramid.interfaces import IPackageOverrides package = DummyPackage('package') @@ -2633,6 +2674,22 @@ class ConfiguratorTests(unittest.TestCase): self.assertEqual(config.registry.getUtility(IDefaultPermission), 'view') + def test_add_view_mapper(self): + from pyramid.interfaces import IViewMapperFactory + config = self._makeOne(autocommit=True) + mapper = object() + config.add_view_mapper('mapper', mapper) + result = config.registry.getUtility(IViewMapperFactory, name='mapper') + self.assertEqual(result, mapper) + + def test_add_view_mapper_dottedname(self): + from pyramid.interfaces import IViewMapperFactory + config = self._makeOne(autocommit=True) + config.add_view_mapper('mapper', 'pyramid.tests.test_config') + result = config.registry.getUtility(IViewMapperFactory, name='mapper') + from pyramid.tests import test_config + self.assertEqual(result, test_config) + def test_set_session_factory(self): from pyramid.interfaces import ISessionFactory config = self._makeOne(autocommit=True) @@ -2680,404 +2737,6 @@ class ConfiguratorTests(unittest.TestCase): self.assertEqual(config.registry.getUtility(ITranslationDirectories), [locale]) - def test_derive_view_function(self): - def view(request): - return 'OK' - config = self._makeOne() - result = config.derive_view(view) - self.failIf(result is view) - self.assertEqual(result(None, None), 'OK') - - def test_derive_view_dottedname(self): - config = self._makeOne() - result = config.derive_view( - 'pyramid.tests.test_config.dummy_view') - self.failIf(result is dummy_view) - self.assertEqual(result(None, None), 'OK') - - def test_derive_view_with_renderer(self): - def view(request): - return 'OK' - config = self._makeOne(autocommit=True) - class moo(object): - def __init__(self, *arg, **kw): - pass - def __call__(self, *arg, **kw): - return 'moo' - config.add_renderer('moo', moo) - result = config.derive_view(view, renderer='moo') - self.failIf(result is view) - self.assertEqual(result(None, None).body, 'moo') - - def test_derive_view_with_default_renderer_no_explicit_renderer(self): - def view(request): - return 'OK' - config = self._makeOne(autocommit=True) - class moo(object): - def __init__(self, *arg, **kw): - pass - def __call__(self, *arg, **kw): - return 'moo' - config.add_renderer(None, moo) - result = config.derive_view(view) - self.failIf(result is view) - self.assertEqual(result(None, None).body, 'moo') - - def test_derive_view_with_default_renderer_with_explicit_renderer(self): - def view(request): - return 'OK' - config = self._makeOne(autocommit=True) - class moo(object): pass - class foo(object): - def __init__(self, *arg, **kw): - pass - def __call__(self, *arg, **kw): - return 'foo' - config.add_renderer(None, moo) - config.add_renderer('foo', foo) - result = config.derive_view(view, renderer='foo') - self.failIf(result is view) - self.assertEqual(result(None, None).body, 'foo') - - def test_derive_view_class_without_attr(self): - class View(object): - def __init__(self, request): - pass - def __call__(self): - return 'OK' - config = self._makeOne() - result = config.derive_view(View) - self.assertEqual(result(None, None), 'OK') - - def test_derive_view_class_with_attr(self): - class View(object): - def __init__(self, request): - pass - def another(self): - return 'OK' - config = self._makeOne() - result = config.derive_view(View, attr='another') - self.assertEqual(result(None, None), 'OK') - - def test__derive_view_as_function_context_and_request(self): - def view(context, request): - return 'OK' - config = self._makeOne() - result = config._derive_view(view) - self.failUnless(result is view) - self.failIf(hasattr(result, '__call_permissive__')) - self.assertEqual(view(None, None), 'OK') - - def test__derive_view_as_function_requestonly(self): - def view(request): - return 'OK' - config = self._makeOne() - result = config._derive_view(view) - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.failIf(hasattr(result, '__call_permissive__')) - self.assertEqual(result(None, None), 'OK') - - def test__derive_view_as_newstyle_class_context_and_request(self): - class view(object): - def __init__(self, context, request): - pass - def __call__(self): - return 'OK' - config = self._makeOne() - result = config._derive_view(view) - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.failIf(hasattr(result, '__call_permissive__')) - self.assertEqual(result(None, None), 'OK') - - def test__derive_view_as_newstyle_class_requestonly(self): - class view(object): - def __init__(self, context, request): - pass - def __call__(self): - return 'OK' - config = self._makeOne() - result = config._derive_view(view) - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.failIf(hasattr(result, '__call_permissive__')) - self.assertEqual(result(None, None), 'OK') - - def test__derive_view_as_oldstyle_class_context_and_request(self): - class view: - def __init__(self, context, request): - pass - def __call__(self): - return 'OK' - config = self._makeOne() - result = config._derive_view(view) - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.failIf(hasattr(result, '__call_permissive__')) - self.assertEqual(result(None, None), 'OK') - - def test__derive_view_as_oldstyle_class_requestonly(self): - class view: - def __init__(self, context, request): - pass - def __call__(self): - return 'OK' - config = self._makeOne() - result = config._derive_view(view) - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.failIf(hasattr(result, '__call_permissive__')) - self.assertEqual(result(None, None), 'OK') - - def test__derive_view_as_instance_context_and_request(self): - class View: - def __call__(self, context, request): - return 'OK' - view = View() - config = self._makeOne() - result = config._derive_view(view) - self.failUnless(result is view) - self.failIf(hasattr(result, '__call_permissive__')) - self.assertEqual(result(None, None), 'OK') - - def test__derive_view_as_instance_requestonly(self): - class View: - def __call__(self, request): - return 'OK' - view = View() - config = self._makeOne() - result = config._derive_view(view) - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.failUnless('instance' in result.__name__) - self.failIf(hasattr(result, '__call_permissive__')) - self.assertEqual(result(None, None), 'OK') - - def test__derive_view_with_debug_authorization_no_authpol(self): - view = lambda *arg: 'OK' - config = self._makeOne() - self._registerSettings(config, - debug_authorization=True, reload_templates=True) - logger = self._registerLogger(config) - result = config._derive_view(view, permission='view') - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.failIf(hasattr(result, '__call_permissive__')) - request = self._makeRequest(config) - request.view_name = 'view_name' - request.url = 'url' - self.assertEqual(result(None, request), 'OK') - self.assertEqual(len(logger.messages), 1) - self.assertEqual(logger.messages[0], - "debug_authorization of url url (view name " - "'view_name' against context None): Allowed " - "(no authorization policy in use)") - - def test__derive_view_with_debug_authorization_no_permission(self): - view = lambda *arg: 'OK' - config = self._makeOne() - self._registerSettings(config, - debug_authorization=True, reload_templates=True) - self._registerSecurityPolicy(config, True) - logger = self._registerLogger(config) - result = config._derive_view(view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.failIf(hasattr(result, '__call_permissive__')) - request = self._makeRequest(config) - request.view_name = 'view_name' - request.url = 'url' - self.assertEqual(result(None, request), 'OK') - self.assertEqual(len(logger.messages), 1) - self.assertEqual(logger.messages[0], - "debug_authorization of url url (view name " - "'view_name' against context None): Allowed (" - "no permission registered)") - - def test__derive_view_debug_auth_permission_authpol_permitted(self): - view = lambda *arg: 'OK' - config = self._makeOne() - self._registerSettings(config, debug_authorization=True, - reload_templates=True) - logger = self._registerLogger(config) - self._registerSecurityPolicy(config, True) - result = config._derive_view(view, permission='view') - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.assertEqual(result.__call_permissive__, view) - request = self._makeRequest(config) - request.view_name = 'view_name' - request.url = 'url' - self.assertEqual(result(None, request), 'OK') - self.assertEqual(len(logger.messages), 1) - self.assertEqual(logger.messages[0], - "debug_authorization of url url (view name " - "'view_name' against context None): True") - - def test__derive_view_debug_auth_permission_authpol_denied(self): - from pyramid.exceptions import Forbidden - view = lambda *arg: 'OK' - config = self._makeOne() - self._registerSettings(config, - debug_authorization=True, reload_templates=True) - logger = self._registerLogger(config) - self._registerSecurityPolicy(config, False) - result = config._derive_view(view, permission='view') - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.assertEqual(result.__call_permissive__, view) - request = self._makeRequest(config) - request.view_name = 'view_name' - request.url = 'url' - self.assertRaises(Forbidden, result, None, request) - self.assertEqual(len(logger.messages), 1) - self.assertEqual(logger.messages[0], - "debug_authorization of url url (view name " - "'view_name' against context None): False") - - def test__derive_view_debug_auth_permission_authpol_denied2(self): - view = lambda *arg: 'OK' - config = self._makeOne() - self._registerSettings(config, - debug_authorization=True, reload_templates=True) - self._registerLogger(config) - self._registerSecurityPolicy(config, False) - result = config._derive_view(view, permission='view') - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - request = self._makeRequest(config) - request.view_name = 'view_name' - request.url = 'url' - permitted = result.__permitted__(None, None) - self.assertEqual(permitted, False) - - def test__derive_view_debug_auth_permission_authpol_overridden(self): - view = lambda *arg: 'OK' - config = self._makeOne() - self._registerSettings(config, - debug_authorization=True, reload_templates=True) - logger = self._registerLogger(config) - self._registerSecurityPolicy(config, False) - result = config._derive_view(view, - permission='__no_permission_required__') - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.failIf(hasattr(result, '__call_permissive__')) - request = self._makeRequest(config) - request.view_name = 'view_name' - request.url = 'url' - self.assertEqual(result(None, request), 'OK') - self.assertEqual(len(logger.messages), 1) - self.assertEqual(logger.messages[0], - "debug_authorization of url url (view name " - "'view_name' against context None): False") - - def test__derive_view_with_predicates_all(self): - view = lambda *arg: 'OK' - predicates = [] - def predicate1(context, request): - predicates.append(True) - return True - def predicate2(context, request): - predicates.append(True) - return True - config = self._makeOne() - result = config._derive_view(view, predicates=[predicate1, predicate2]) - request = self._makeRequest(config) - request.method = 'POST' - next = result(None, None) - self.assertEqual(next, 'OK') - self.assertEqual(predicates, [True, True]) - - def test__derive_view_with_predicates_checker(self): - view = lambda *arg: 'OK' - predicates = [] - def predicate1(context, request): - predicates.append(True) - return True - def predicate2(context, request): - predicates.append(True) - return True - config = self._makeOne() - result = config._derive_view(view, predicates=[predicate1, predicate2]) - request = self._makeRequest(config) - request.method = 'POST' - next = result.__predicated__(None, None) - self.assertEqual(next, True) - self.assertEqual(predicates, [True, True]) - - def test__derive_view_with_predicates_notall(self): - from pyramid.exceptions import NotFound - view = lambda *arg: 'OK' - predicates = [] - def predicate1(context, request): - predicates.append(True) - return True - def predicate2(context, request): - predicates.append(True) - return False - config = self._makeOne() - result = config._derive_view(view, predicates=[predicate1, predicate2]) - request = self._makeRequest(config) - request.method = 'POST' - self.assertRaises(NotFound, result, None, None) - self.assertEqual(predicates, [True, True]) - - def test__derive_view_with_wrapper_viewname(self): - from webob import Response - from pyramid.interfaces import IView - from pyramid.interfaces import IViewClassifier - inner_response = Response('OK') - def inner_view(context, request): - return inner_response - def outer_view(context, request): - self.assertEqual(request.wrapped_response, inner_response) - self.assertEqual(request.wrapped_body, inner_response.body) - self.assertEqual(request.wrapped_view, inner_view) - return Response('outer ' + request.wrapped_body) - config = self._makeOne() - config.registry.registerAdapter( - outer_view, (IViewClassifier, None, None), IView, 'owrap') - result = config._derive_view(inner_view, viewname='inner', - wrapper_viewname='owrap') - self.failIf(result is inner_view) - self.assertEqual(inner_view.__module__, result.__module__) - self.assertEqual(inner_view.__doc__, result.__doc__) - request = self._makeRequest(config) - request.registry = config.registry - response = result(None, request) - self.assertEqual(response.body, 'outer OK') - - def test__derive_view_with_wrapper_viewname_notfound(self): - from webob import Response - inner_response = Response('OK') - def inner_view(context, request): - return inner_response - config = self._makeOne() - request = self._makeRequest(config) - request.registry = config.registry - wrapped = config._derive_view( - inner_view, viewname='inner', wrapper_viewname='owrap') - self.assertRaises(ValueError, wrapped, None, request) - def test_override_asset_samename(self): from pyramid.exceptions import ConfigurationError config = self._makeOne() @@ -3557,334 +3216,755 @@ class ConfiguratorTests(unittest.TestCase): for confinst in conflict: yield confinst[2] -class Test__map_view(unittest.TestCase): +class TestViewDeriver(unittest.TestCase): def setUp(self): - from pyramid.registry import Registry - self.registry = Registry() - testing.setUp(registry=self.registry) + self.config = testing.setUp() def tearDown(self): - del self.registry - testing.tearDown() - - def _registerRenderer(self, typ='.txt'): - from pyramid.interfaces import IRendererFactory - from pyramid.interfaces import ITemplateRenderer - from pyramid.renderers import RendererHelper - from zope.interface import implements - class DummyRenderer: - implements(ITemplateRenderer) - def __init__(self, path): - self.__class__.path = path - def __call__(self, *arg): - return 'Hello!' - self.registry.registerUtility(DummyRenderer, IRendererFactory, name=typ) - renderer = RendererHelper(name='abc' + typ, registry=self.registry) - return renderer - + self.config = None + + def _makeOne(self, **kw): + kw['registry'] = self.config.registry + from pyramid.config import ViewDeriver + return ViewDeriver(**kw) + def _makeRequest(self): request = DummyRequest() - request.registry = self.registry + request.registry = self.config.registry return request - def _callFUT(self, view, **kw): - from pyramid.config import _map_view - return _map_view(view, self.registry, **kw) + def _registerLogger(self): + from pyramid.interfaces import IDebugLogger + logger = DummyLogger() + self.config.registry.registerUtility(logger, IDebugLogger) + return logger - def test__map_view_as_function_context_and_request(self): - def view(context, request): - return 'OK' - result = self._callFUT(view) - self.failUnless(result is view) - self.assertEqual(result(None, None), 'OK') + def _registerSecurityPolicy(self, permissive): + from pyramid.interfaces import IAuthenticationPolicy + from pyramid.interfaces import IAuthorizationPolicy + policy = DummySecurityPolicy(permissive) + self.config.registry.registerUtility(policy, IAuthenticationPolicy) + self.config.registry.registerUtility(policy, IAuthorizationPolicy) - def test__map_view_as_function_with_attr(self): - def view(context, request): - """ """ - result = self._callFUT(view, attr='__name__') + def test_requestonly_function(self): + def view(request): + return 'OK' + deriver = self._makeOne() + result = deriver(view) self.failIf(result is view) - self.assertRaises(TypeError, result, None, None) + self.assertEqual(result(None, None), 'OK') - def test__map_view_as_function_with_attr_and_renderer(self): - renderer = self._registerRenderer() - view = lambda *arg: 'OK' - result = self._callFUT(view, attr='__name__', renderer=renderer) + def test_requestonly_function_with_renderer(self): + class moo(object): + def render_view(inself, req, resp, view_inst, ctx): + self.assertEqual(req, request) + self.assertEqual(resp, 'OK') + self.assertEqual(view_inst, view) + self.assertEqual(ctx, context) + return 'moo' + def view(request): + return 'OK' + deriver = self._makeOne(renderer=moo()) + result = deriver(view) self.failIf(result is view) - self.assertRaises(TypeError, result, None, None) + request = self._makeRequest() + context = testing.DummyResource() + self.assertEqual(result(context, request), 'moo') - def test__map_view_as_function_requestonly(self): + def test_requestonly_function_with_renderer_request_has_view(self): + class moo(object): + def render_view(inself, req, resp, view_inst, ctx): + self.assertEqual(req, request) + self.assertEqual(resp, 'OK') + self.assertEqual(view_inst, 'view') + self.assertEqual(ctx, context) + return 'moo' def view(request): return 'OK' - result = self._callFUT(view) + deriver = self._makeOne(renderer=moo()) + result = deriver(view) self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.assertEqual(result(None, None), 'OK') + request = self._makeRequest() + request.__view__ = 'view' + context = testing.DummyResource() + self.assertEqual(result(context, request), 'moo') + self.failIf(hasattr(request, '__view__')) - def test__map_view_as_function_requestonly_with_attr(self): + def test_class_without_attr(self): + class View(object): + def __init__(self, request): + pass + def __call__(self): + return 'OK' + deriver = self._makeOne() + result = deriver(View) + request = self._makeRequest() + self.assertEqual(result(None, request), 'OK') + self.assertEqual(request.__view__.__class__, View) + + def test_class_with_attr(self): + class View(object): + def __init__(self, request): + pass + def another(self): + return 'OK' + deriver = self._makeOne(attr='another') + result = deriver(View) + request = self._makeRequest() + self.assertEqual(result(None, request), 'OK') + self.assertEqual(request.__view__.__class__, View) + + def test_as_function_context_and_request(self): + def view(context, request): + return 'OK' + deriver = self._makeOne() + result = deriver(view) + self.failUnless(result is view) + self.failIf(hasattr(result, '__call_permissive__')) + self.assertEqual(view(None, None), 'OK') + + def test_as_function_requestonly(self): def view(request): - """ """ - result = self._callFUT(view, attr='__name__') + return 'OK' + deriver = self._makeOne() + result = deriver(view) self.failIf(result is view) self.assertEqual(view.__module__, result.__module__) self.assertEqual(view.__doc__, result.__doc__) self.assertEqual(view.__name__, result.__name__) - self.assertRaises(TypeError, result, None, None) + self.failIf(hasattr(result, '__call_permissive__')) + self.assertEqual(result(None, None), 'OK') - def test__map_view_as_newstyle_class_context_and_request(self): + def test_as_newstyle_class_context_and_request(self): class view(object): def __init__(self, context, request): pass def __call__(self): return 'OK' - result = self._callFUT(view) + deriver = self._makeOne() + result = deriver(view) self.failIf(result is view) self.assertEqual(view.__module__, result.__module__) self.assertEqual(view.__doc__, result.__doc__) self.assertEqual(view.__name__, result.__name__) - self.assertEqual(result(None, None), 'OK') + self.failIf(hasattr(result, '__call_permissive__')) + request = self._makeRequest() + self.assertEqual(result(None, request), 'OK') + self.assertEqual(request.__view__.__class__, view) - def test__map_view_as_newstyle_class_context_and_request_with_attr(self): + def test_as_newstyle_class_requestonly(self): class view(object): def __init__(self, context, request): pass - def index(self): + def __call__(self): return 'OK' - result = self._callFUT(view, attr='index') + deriver = self._makeOne() + result = deriver(view) self.failIf(result is view) self.assertEqual(view.__module__, result.__module__) self.assertEqual(view.__doc__, result.__doc__) self.assertEqual(view.__name__, result.__name__) - self.assertEqual(result(None, None), 'OK') + self.failIf(hasattr(result, '__call_permissive__')) + request = self._makeRequest() + self.assertEqual(result(None, request), 'OK') + self.assertEqual(request.__view__.__class__, view) - def test__map_view_as_newstyle_class_context_and_request_attr_and_renderer( - self): - renderer = self._registerRenderer() - class view(object): + def test_as_oldstyle_class_context_and_request(self): + class view: def __init__(self, context, request): pass - def index(self): - return {'a':'1'} - result = self._callFUT(view, attr='index', renderer=renderer) + def __call__(self): + return 'OK' + deriver = self._makeOne() + result = deriver(view) self.failIf(result is view) self.assertEqual(view.__module__, result.__module__) self.assertEqual(view.__doc__, result.__doc__) self.assertEqual(view.__name__, result.__name__) + self.failIf(hasattr(result, '__call_permissive__')) request = self._makeRequest() - self.assertEqual(result(None, request).body, 'Hello!') + self.assertEqual(result(None, request), 'OK') + self.assertEqual(request.__view__.__class__, view) - def test__map_view_as_newstyle_class_requestonly(self): - class view(object): - def __init__(self, request): + def test_as_oldstyle_class_requestonly(self): + class view: + def __init__(self, context, request): pass def __call__(self): return 'OK' - result = self._callFUT(view) + deriver = self._makeOne() + result = deriver(view) self.failIf(result is view) self.assertEqual(view.__module__, result.__module__) self.assertEqual(view.__doc__, result.__doc__) self.assertEqual(view.__name__, result.__name__) + self.failIf(hasattr(result, '__call_permissive__')) + request = self._makeRequest() + self.assertEqual(result(None, request), 'OK') + self.assertEqual(request.__view__.__class__, view) + + def test_as_instance_context_and_request(self): + class View: + def __call__(self, context, request): + return 'OK' + view = View() + deriver = self._makeOne() + result = deriver(view) + self.failUnless(result is view) + self.failIf(hasattr(result, '__call_permissive__')) self.assertEqual(result(None, None), 'OK') - def test__map_view_as_newstyle_class_requestonly_with_attr(self): - class view(object): - def __init__(self, request): - pass - def index(self): + def test_as_instance_requestonly(self): + class View: + def __call__(self, request): return 'OK' - result = self._callFUT(view, attr='index') + view = View() + deriver = self._makeOne() + result = deriver(view) self.failIf(result is view) self.assertEqual(view.__module__, result.__module__) self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) + self.failUnless('instance' in result.__name__) + self.failIf(hasattr(result, '__call_permissive__')) self.assertEqual(result(None, None), 'OK') - def test__map_view_as_newstyle_class_requestonly_attr_and_renderer(self): - renderer = self._registerRenderer() - class view(object): + def test_with_debug_authorization_no_authpol(self): + view = lambda *arg: 'OK' + self.config.registry.settings = dict( + debug_authorization=True, reload_templates=True) + logger = self._registerLogger() + deriver = self._makeOne(permission='view') + result = deriver(view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.failIf(hasattr(result, '__call_permissive__')) + request = self._makeRequest() + request.view_name = 'view_name' + request.url = 'url' + self.assertEqual(result(None, request), 'OK') + self.assertEqual(len(logger.messages), 1) + self.assertEqual(logger.messages[0], + "debug_authorization of url url (view name " + "'view_name' against context None): Allowed " + "(no authorization policy in use)") + + def test_with_debug_authorization_no_permission(self): + view = lambda *arg: 'OK' + self.config.registry.settings = dict( + debug_authorization=True, reload_templates=True) + self._registerSecurityPolicy(True) + logger = self._registerLogger() + deriver = self._makeOne() + result = deriver(view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.failIf(hasattr(result, '__call_permissive__')) + request = self._makeRequest() + request.view_name = 'view_name' + request.url = 'url' + self.assertEqual(result(None, request), 'OK') + self.assertEqual(len(logger.messages), 1) + self.assertEqual(logger.messages[0], + "debug_authorization of url url (view name " + "'view_name' against context None): Allowed (" + "no permission registered)") + + def test_debug_auth_permission_authpol_permitted(self): + view = lambda *arg: 'OK' + self.config.registry.settings = dict( + debug_authorization=True, reload_templates=True) + logger = self._registerLogger() + self._registerSecurityPolicy(True) + deriver = self._makeOne(permission='view') + result = deriver(view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.assertEqual(result.__call_permissive__, view) + request = self._makeRequest() + request.view_name = 'view_name' + request.url = 'url' + self.assertEqual(result(None, request), 'OK') + self.assertEqual(len(logger.messages), 1) + self.assertEqual(logger.messages[0], + "debug_authorization of url url (view name " + "'view_name' against context None): True") + + def test_debug_auth_permission_authpol_denied(self): + from pyramid.exceptions import Forbidden + view = lambda *arg: 'OK' + self.config.registry.settings = dict( + debug_authorization=True, reload_templates=True) + logger = self._registerLogger() + self._registerSecurityPolicy(False) + deriver = self._makeOne(permission='view') + result = deriver(view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.assertEqual(result.__call_permissive__, view) + request = self._makeRequest() + request.view_name = 'view_name' + request.url = 'url' + self.assertRaises(Forbidden, result, None, request) + self.assertEqual(len(logger.messages), 1) + self.assertEqual(logger.messages[0], + "debug_authorization of url url (view name " + "'view_name' against context None): False") + + def test_debug_auth_permission_authpol_denied2(self): + view = lambda *arg: 'OK' + self.config.registry.settings = dict( + debug_authorization=True, reload_templates=True) + self._registerLogger() + self._registerSecurityPolicy(False) + deriver = self._makeOne(permission='view') + result = deriver(view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + request = self._makeRequest() + request.view_name = 'view_name' + request.url = 'url' + permitted = result.__permitted__(None, None) + self.assertEqual(permitted, False) + + def test_debug_auth_permission_authpol_overridden(self): + view = lambda *arg: 'OK' + self.config.registry.settings = dict( + debug_authorization=True, reload_templates=True) + logger = self._registerLogger() + self._registerSecurityPolicy(False) + deriver = self._makeOne(permission='__no_permission_required__') + result = deriver(view) + self.assertEqual(view.__module__, result.__module__) + self.assertEqual(view.__doc__, result.__doc__) + self.assertEqual(view.__name__, result.__name__) + self.failIf(hasattr(result, '__call_permissive__')) + request = self._makeRequest() + request.view_name = 'view_name' + request.url = 'url' + self.assertEqual(result(None, request), 'OK') + self.assertEqual(len(logger.messages), 1) + self.assertEqual(logger.messages[0], + "debug_authorization of url url (view name " + "'view_name' against context None): False") + + def test_with_predicates_all(self): + view = lambda *arg: 'OK' + predicates = [] + def predicate1(context, request): + predicates.append(True) + return True + def predicate2(context, request): + predicates.append(True) + return True + deriver = self._makeOne(predicates=[predicate1, predicate2]) + result = deriver(view) + request = self._makeRequest() + request.method = 'POST' + next = result(None, None) + self.assertEqual(next, 'OK') + self.assertEqual(predicates, [True, True]) + + def test_with_predicates_checker(self): + view = lambda *arg: 'OK' + predicates = [] + def predicate1(context, request): + predicates.append(True) + return True + def predicate2(context, request): + predicates.append(True) + return True + deriver = self._makeOne(predicates=[predicate1, predicate2]) + result = deriver(view) + request = self._makeRequest() + request.method = 'POST' + next = result.__predicated__(None, None) + self.assertEqual(next, True) + self.assertEqual(predicates, [True, True]) + + def test_with_predicates_notall(self): + from pyramid.exceptions import NotFound + view = lambda *arg: 'OK' + predicates = [] + def predicate1(context, request): + predicates.append(True) + return True + def predicate2(context, request): + predicates.append(True) + return False + deriver = self._makeOne(predicates=[predicate1, predicate2]) + result = deriver(view) + request = self._makeRequest() + request.method = 'POST' + self.assertRaises(NotFound, result, None, None) + self.assertEqual(predicates, [True, True]) + + def test_with_wrapper_viewname(self): + from webob import Response + from pyramid.interfaces import IView + from pyramid.interfaces import IViewClassifier + inner_response = Response('OK') + def inner_view(context, request): + return inner_response + def outer_view(context, request): + self.assertEqual(request.wrapped_response, inner_response) + self.assertEqual(request.wrapped_body, inner_response.body) + self.assertEqual(request.wrapped_view, inner_view) + return Response('outer ' + request.wrapped_body) + self.config.registry.registerAdapter( + outer_view, (IViewClassifier, None, None), IView, 'owrap') + deriver = self._makeOne(viewname='inner', + wrapper_viewname='owrap') + result = deriver(inner_view) + self.failIf(result is inner_view) + self.assertEqual(inner_view.__module__, result.__module__) + self.assertEqual(inner_view.__doc__, result.__doc__) + request = self._makeRequest() + response = result(None, request) + self.assertEqual(response.body, 'outer OK') + + def test_with_wrapper_viewname_notfound(self): + from webob import Response + inner_response = Response('OK') + def inner_view(context, request): + return inner_response + deriver = self._makeOne(viewname='inner', wrapper_viewname='owrap') + wrapped = deriver(inner_view) + request = self._makeRequest() + self.assertRaises(ValueError, wrapped, None, request) + + def test_as_newstyle_class_context_and_request_attr_and_renderer(self): + class renderer(object): + def render_view(inself, req, resp, view_inst, ctx): + self.assertEqual(req, request) + self.assertEqual(resp, {'a':'1'}) + self.assertEqual(view_inst.__class__, View) + self.assertEqual(ctx, context) + return resp + class View(object): + def __init__(self, context, request): + pass + def index(self): + return {'a':'1'} + deriver = self._makeOne(renderer=renderer(), attr='index') + result = deriver(View) + self.failIf(result is View) + self.assertEqual(result.__module__, View.__module__) + self.assertEqual(result.__doc__, View.__doc__) + self.assertEqual(result.__name__, View.__name__) + request = self._makeRequest() + context = testing.DummyResource() + self.assertEqual(result(context, request), {'a':'1'}) + + def test_as_newstyle_class_requestonly_attr_and_renderer(self): + class renderer(object): + def render_view(inself, req, resp, view_inst, ctx): + self.assertEqual(req, request) + self.assertEqual(resp, {'a':'1'}) + self.assertEqual(view_inst.__class__, View) + self.assertEqual(ctx, context) + return resp + class View(object): + def __init__(self, request): + pass + def index(self): + return {'a':'1'} + deriver = self._makeOne(renderer=renderer(), attr='index') + result = deriver(View) + self.failIf(result is View) + self.assertEqual(result.__module__, View.__module__) + self.assertEqual(result.__doc__, View.__doc__) + self.assertEqual(result.__name__, View.__name__) + request = self._makeRequest() + context = testing.DummyResource() + self.assertEqual(result(context, request), {'a':'1'}) + + def test_as_oldstyle_cls_context_request_attr_and_renderer(self): + class renderer(object): + def render_view(inself, req, resp, view_inst, ctx): + self.assertEqual(req, request) + self.assertEqual(resp, {'a':'1'}) + self.assertEqual(view_inst.__class__, View) + self.assertEqual(ctx, context) + return resp + class View: + def __init__(self, context, request): + pass + def index(self): + return {'a':'1'} + deriver = self._makeOne(renderer=renderer(), attr='index') + result = deriver(View) + self.failIf(result is View) + self.assertEqual(result.__module__, View.__module__) + self.assertEqual(result.__doc__, View.__doc__) + self.assertEqual(result.__name__, View.__name__) + request = self._makeRequest() + context = testing.DummyResource() + self.assertEqual(result(context, request), {'a':'1'}) + + def test_as_oldstyle_cls_requestonly_attr_and_renderer(self): + class renderer(object): + def render_view(inself, req, resp, view_inst, ctx): + self.assertEqual(req, request) + self.assertEqual(resp, {'a':'1'}) + self.assertEqual(view_inst.__class__, View) + self.assertEqual(ctx, context) + return resp + class View: def __init__(self, request): pass def index(self): return {'a':'1'} - result = self._callFUT(view, attr='index', renderer=renderer) + deriver = self._makeOne(renderer=renderer(), attr='index') + result = deriver(View) + self.failIf(result is View) + self.assertEqual(result.__module__, View.__module__) + self.assertEqual(result.__doc__, View.__doc__) + self.assertEqual(result.__name__, View.__name__) + request = self._makeRequest() + context = testing.DummyResource() + self.assertEqual(result(context, request), {'a':'1'}) + + def test_as_instance_context_and_request_attr_and_renderer(self): + class renderer(object): + def render_view(inself, req, resp, view_inst, ctx): + self.assertEqual(req, request) + self.assertEqual(resp, {'a':'1'}) + self.assertEqual(view_inst, view) + self.assertEqual(ctx, context) + return resp + class View: + def index(self, context, request): + return {'a':'1'} + deriver = self._makeOne(renderer=renderer(), attr='index') + view = View() + result = deriver(view) self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) + self.assertEqual(result.__module__, view.__module__) + self.assertEqual(result.__doc__, view.__doc__) + request = self._makeRequest() + context = testing.DummyResource() + self.assertEqual(result(context, request), {'a':'1'}) + + def test_as_instance_requestonly_attr_and_renderer(self): + class renderer(object): + def render_view(inself, req, resp, view_inst, ctx): + self.assertEqual(req, request) + self.assertEqual(resp, {'a':'1'}) + self.assertEqual(view_inst, view) + self.assertEqual(ctx, context) + return resp + class View: + def index(self, request): + return {'a':'1'} + deriver = self._makeOne(renderer=renderer(), attr='index') + view = View() + result = deriver(view) + self.failIf(result is view) + self.assertEqual(result.__module__, view.__module__) + self.assertEqual(result.__doc__, view.__doc__) request = self._makeRequest() - self.assertEqual(result(None, request).body, 'Hello!') + context = testing.DummyResource() + self.assertEqual(result(context, request), {'a':'1'}) - def test__map_view_as_oldstyle_class_context_and_request(self): - class view: +class TestDefaultViewMapper(unittest.TestCase): + def setUp(self): + self.config = testing.setUp() + self.registry = self.config.registry + + def tearDown(self): + del self.registry + testing.tearDown() + + def _makeOne(self, **kw): + from pyramid.config import DefaultViewMapper + kw['registry'] = self.registry + return DefaultViewMapper(**kw) + + def _makeRequest(self): + request = DummyRequest() + request.registry = self.registry + return request + + def test_view_as_function_context_and_request(self): + def view(context, request): + return 'OK' + mapper = self._makeOne() + result = mapper(view) + self.failUnless(result is view) + request = self._makeRequest() + self.assertEqual(result(None, request), 'OK') + + def test__view_as_function_with_attr(self): + def view(context, request): + """ """ + mapper = self._makeOne(attr='__name__') + result = mapper(view) + self.failIf(result is view) + request = self._makeRequest() + self.assertRaises(TypeError, result, None, request) + + def test_view_as_function_requestonly(self): + def view(request): + return 'OK' + mapper = self._makeOne() + result = mapper(view) + self.failIf(result is view) + request = self._makeRequest() + self.assertEqual(result(None, request), 'OK') + + def test_view_as_function_requestonly_with_attr(self): + def view(request): + """ """ + mapper = self._makeOne(attr='__name__') + result = mapper(view) + self.failIf(result is view) + request = self._makeRequest() + self.assertRaises(TypeError, result, None, request) + + def test_view_as_newstyle_class_context_and_request(self): + class view(object): def __init__(self, context, request): pass def __call__(self): return 'OK' - result = self._callFUT(view) + mapper = self._makeOne() + result = mapper(view) self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.assertEqual(result(None, None), 'OK') + request = self._makeRequest() + self.assertEqual(result(None, request), 'OK') - def test__map_view_as_oldstyle_class_context_and_request_with_attr(self): - class view: + def test_view_as_newstyle_class_context_and_request_with_attr(self): + class view(object): def __init__(self, context, request): pass def index(self): return 'OK' - result = self._callFUT(view, attr='index') + mapper = self._makeOne(attr='index') + result = mapper(view) self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.assertEqual(result(None, None), 'OK') + request = self._makeRequest() + self.assertEqual(result(None, request), 'OK') - def test__map_view_as_oldstyle_cls_context_request_attr_and_renderer(self): - renderer = self._registerRenderer() - class view: - def __init__(self, context, request): + def test_view_as_newstyle_class_requestonly(self): + class view(object): + def __init__(self, request): + pass + def __call__(self): + return 'OK' + mapper = self._makeOne() + result = mapper(view) + self.failIf(result is view) + request = self._makeRequest() + self.assertEqual(result(None, request), 'OK') + + def test_view_as_newstyle_class_requestonly_with_attr(self): + class view(object): + def __init__(self, request): pass def index(self): - return {'a':'1'} - result = self._callFUT(view, attr='index', renderer=renderer) + return 'OK' + mapper = self._makeOne(attr='index') + result = mapper(view) self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) request = self._makeRequest() - self.assertEqual(result(None, request).body, 'Hello!') + self.assertEqual(result(None, request), 'OK') - def test__map_view_as_oldstyle_class_requestonly(self): + def test_view_as_oldstyle_class_context_and_request(self): class view: - def __init__(self, request): + def __init__(self, context, request): pass def __call__(self): return 'OK' - result = self._callFUT(view) + mapper = self._makeOne() + result = mapper(view) self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.assertEqual(result(None, None), 'OK') + request = self._makeRequest() + self.assertEqual(result(None, request), 'OK') - def test__map_view_as_oldstyle_class_requestonly_with_attr(self): + def test_view_as_oldstyle_class_context_and_request_with_attr(self): class view: - def __init__(self, request): + def __init__(self, context, request): pass def index(self): return 'OK' - result = self._callFUT(view, attr='index') + mapper = self._makeOne(attr='index') + result = mapper(view) self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) - self.assertEqual(result(None, None), 'OK') + request = self._makeRequest() + self.assertEqual(result(None, request), 'OK') + + def test_view_as_oldstyle_class_requestonly(self): + class view: + def __init__(self, request): + pass + def __call__(self): + return 'OK' + mapper = self._makeOne() + result = mapper(view) + self.failIf(result is view) + request = self._makeRequest() + self.assertEqual(result(None, request), 'OK') - def test__map_view_as_oldstyle_class_requestonly_attr_and_renderer(self): - renderer = self._registerRenderer() + def test_view_as_oldstyle_class_requestonly_with_attr(self): class view: def __init__(self, request): pass def index(self): - return {'a':'1'} - result = self._callFUT(view, attr='index', renderer=renderer) + return 'OK' + mapper = self._makeOne(attr='index') + result = mapper(view) self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.assertEqual(view.__name__, result.__name__) request = self._makeRequest() - self.assertEqual(result(None, request).body, 'Hello!') + self.assertEqual(result(None, request), 'OK') - def test__map_view_as_instance_context_and_request(self): + def test_view_as_instance_context_and_request(self): class View: def __call__(self, context, request): return 'OK' view = View() - result = self._callFUT(view) + mapper = self._makeOne() + result = mapper(view) self.failUnless(result is view) - self.assertEqual(result(None, None), 'OK') + request = self._makeRequest() + self.assertEqual(result(None, request), 'OK') - def test__map_view_as_instance_context_and_request_and_attr(self): + def test_view_as_instance_context_and_request_and_attr(self): class View: def index(self, context, request): return 'OK' view = View() - result = self._callFUT(view, attr='index') - self.failIf(result is view) - self.assertEqual(result(None, None), 'OK') - - def test__map_view_as_instance_context_and_request_attr_and_renderer(self): - renderer = self._registerRenderer() - class View: - def index(self, context, request): - return {'a':'1'} - view = View() - result = self._callFUT(view, attr='index', renderer=renderer) + mapper = self._makeOne(attr='index') + result = mapper(view) self.failIf(result is view) request = self._makeRequest() - self.assertEqual(result(None, request).body, 'Hello!') + self.assertEqual(result(None, request), 'OK') - def test__map_view_as_instance_requestonly(self): + def test_view_as_instance_requestonly(self): class View: def __call__(self, request): return 'OK' view = View() - result = self._callFUT(view) + mapper = self._makeOne() + result = mapper(view) self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.failUnless('instance' in result.__name__) - self.assertEqual(result(None, None), 'OK') + request = self._makeRequest() + self.assertEqual(result(None, request), 'OK') - def test__map_view_as_instance_requestonly_with_attr(self): + def test_view_as_instance_requestonly_with_attr(self): class View: def index(self, request): return 'OK' view = View() - result = self._callFUT(view, attr='index') + mapper = self._makeOne(attr='index') + result = mapper(view) self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.failUnless('instance' in result.__name__) - self.assertEqual(result(None, None), 'OK') - - def test__map_view_as_instance_requestonly_with_attr_and_renderer(self): - renderer = self._registerRenderer() - class View: - def index(self, request): - return {'a':'1'} - view = View() - result = self._callFUT(view, attr='index', renderer=renderer) - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - self.failUnless('instance' in result.__name__) - request = self._makeRequest() - self.assertEqual(result(None, request).body, 'Hello!') - - def test__map_view_rendereronly(self): - renderer = self._registerRenderer() - def view(context, request): - return {'a':'1'} - result = self._callFUT(view, renderer=renderer) - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) - request = self._makeRequest() - self.assertEqual(result(None, request).body, 'Hello!') - - def test__map_view_with_registry(self): - renderer = self._registerRenderer() - def view(context, request): - return {'a':'1'} - result = self._callFUT(view, renderer=renderer) - self.failIf(result is view) - self.assertEqual(view.__module__, result.__module__) - self.assertEqual(view.__doc__, result.__doc__) request = self._makeRequest() - self.assertEqual(result(None, request).body, 'Hello!') + self.assertEqual(result(None, request), 'OK') class Test_preserve_view_attrs(unittest.TestCase): def _callFUT(self, view, wrapped_view): @@ -4400,24 +4480,23 @@ class TestMultiView(unittest.TestCase): response = mv(context, request) self.assertEqual(response, expected_response) - -class TestRequestOnly(unittest.TestCase): - def _callFUT(self, arg): +class Test_requestonly(unittest.TestCase): + def _callFUT(self, view, attr=None): from pyramid.config import requestonly - return requestonly(arg) + return requestonly(view, attr) - def test_newstyle_class_no_init(self): + def test_requestonly_newstyle_class_no_init(self): class foo(object): """ """ self.assertFalse(self._callFUT(foo)) - def test_newstyle_class_init_toomanyargs(self): + def test_requestonly_newstyle_class_init_toomanyargs(self): class foo(object): def __init__(self, context, request): """ """ self.assertFalse(self._callFUT(foo)) - def test_newstyle_class_init_onearg_named_request(self): + def test_requestonly_newstyle_class_init_onearg_named_request(self): class foo(object): def __init__(self, request): """ """ @@ -4493,6 +4572,22 @@ class TestRequestOnly(unittest.TestCase): """ """ self.assertFalse(self._callFUT(foo)) + def test_function_with_attr_false(self): + def bar(context, request): + """ """ + def foo(context, request): + """ """ + foo.bar = bar + self.assertFalse(self._callFUT(foo, 'bar')) + + def test_function_with_attr_true(self): + def bar(context, request): + """ """ + def foo(request): + """ """ + foo.bar = bar + self.assertTrue(self._callFUT(foo, 'bar')) + def test_function_onearg_named_request(self): def foo(request): """ """ diff --git a/pyramid/tests/test_view.py b/pyramid/tests/test_view.py index 7fc066319..69d74ee6e 100644 --- a/pyramid/tests/test_view.py +++ b/pyramid/tests/test_view.py @@ -229,11 +229,12 @@ class TestViewConfigDecorator(unittest.TestCase): def test_create_nondefaults(self): decorator = self._makeOne(name=None, request_type=None, for_=None, - permission='foo') + permission='foo', view_mapper='mapper') self.assertEqual(decorator.name, None) self.assertEqual(decorator.request_type, None) self.assertEqual(decorator.context, None) self.assertEqual(decorator.permission, 'foo') + self.assertEqual(decorator.view_mapper, 'mapper') def test_call_function(self): decorator = self._makeOne() diff --git a/pyramid/view.py b/pyramid/view.py index 776185d8b..8f201f6d1 100644 --- a/pyramid/view.py +++ b/pyramid/view.py @@ -17,7 +17,6 @@ from zope.interface import providedBy from pyramid.interfaces import IRoutesMapper from pyramid.interfaces import IView from pyramid.interfaces import IViewClassifier -from pyramid.interfaces import IRendererFactory from pyramid.httpexceptions import HTTPFound from pyramid.renderers import RendererHelper @@ -384,7 +383,7 @@ class view_config(object): route_name=None, request_method=None, request_param=None, containment=None, attr=None, renderer=None, wrapper=None, xhr=False, accept=None, header=None, path_info=None, - custom_predicates=(), context=None): + custom_predicates=(), context=None, view_mapper=None): self.name = name self.request_type = request_type self.context = context or for_ @@ -401,6 +400,7 @@ class view_config(object): self.header = header self.path_info = path_info self.custom_predicates = custom_predicates + self.view_mapper = view_mapper def __call__(self, wrapped): settings = self.__dict__.copy() |
