CHANGES.txt | ●●●●● patch | view | raw | blame | history | |
pyramid/config/views.py | ●●●●● patch | view | raw | blame | history | |
pyramid/scaffolds/copydir.py | ●●●●● patch | view | raw | blame | history | |
pyramid/tests/test_config/test_views.py | ●●●●● patch | view | raw | blame | history | |
pyramid/tests/test_view.py | ●●●●● patch | view | raw | blame | history |
CHANGES.txt
@@ -37,6 +37,11 @@ permit limited composition reuse of the decorator by other software that wants to provide custom decorators that are much like view_config. - Allow an iterable of decorators to be passed to ``pyramid.config.Configurator.add_view``. This allows views to be wrapped by more than one decorator without requiring combining the decorators yourself. Bug Fixes --------- pyramid/config/views.py
@@ -42,6 +42,7 @@ url_quote, WIN, is_bound_method, is_nonstr_iter ) from pyramid.exceptions import ( @@ -837,13 +838,39 @@ decorator A :term:`dotted Python name` to function (or the function itself) 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. The view callable it is passed will accept ``(context, request)``. The decorator must return a A :term:`dotted Python name` to function (or the function itself, or an iterable of the aforementioned) which will be used to decorate the registered :term:`view callable`. The decorator function(s) will be called with the view callable as a single argument. The view callable it is passed will accept ``(context, request)``. The decorator(s) must return a replacement view callable which also accepts ``(context, request)``. If decorator is an iterable, the callables will be combined and used in the order provided as a decorator. For example:: @view_config(..., decorator=(decorator2, decorator1)) def myview(request): .... Is similar to doing:: @view_config(...) @decorator2 @decorator1 def myview(request): ... Except with the existing benefits of ``decorator=`` (having a common decorator syntax for all view calling conventions and not having to think about preserving function attributes such as ``__name__`` and ``__module__`` within decorator logic). Passing an iterable is only supported as of :app:`Pyramid` 1.4a4. mapper @@ -1071,7 +1098,19 @@ for_ = self.maybe_dotted(for_) containment = self.maybe_dotted(containment) mapper = self.maybe_dotted(mapper) decorator = self.maybe_dotted(decorator) def combine(*decorators): def decorated(view_callable): # reversed() is allows a more natural ordering in the api for decorator in reversed(decorators): view_callable = decorator(view_callable) return view_callable return decorated if is_nonstr_iter(decorator): decorator = combine(*map(self.maybe_dotted, decorator)) else: decorator = self.maybe_dotted(decorator) if not view: if renderer: pyramid/scaffolds/copydir.py
@@ -245,7 +245,7 @@ def makedirs(dir, verbosity, pad): parent = os.path.dirname(os.path.abspath(dir)) if not os.path.exists(parent): makedirs(parent, verbosity, pad) makedirs(parent, verbosity, pad) # pragma: no cover os.mkdir(dir) def substitute_filename(fn, vars): pyramid/tests/test_config/test_views.py
@@ -185,6 +185,28 @@ result = wrapper(None, None) self.assertEqual(result, 'OK') def test_add_view_with_decorator_tuple(self): from pyramid.renderers import null_renderer def view(request): """ ABC """ return 'OK' def view_wrapper1(fn): def inner(context, request): return 'wrapped1' + fn(context, request) return inner def view_wrapper2(fn): def inner(context, request): return 'wrapped2' + fn(context, request) return inner config = self._makeOne(autocommit=True) config.add_view(view=view, decorator=(view_wrapper2, view_wrapper1), renderer=null_renderer) wrapper = self._getViewCallable(config) self.assertFalse(wrapper is view) self.assertEqual(wrapper.__doc__, view.__doc__) result = wrapper(None, None) self.assertEqual(result, 'wrapped2wrapped1OK') def test_add_view_with_http_cache(self): import datetime from pyramid.response import Response pyramid/tests/test_view.py
@@ -372,6 +372,10 @@ def test_create_with_other_predicates(self): decorator = self._makeOne(foo=1) self.assertEqual(decorator.foo, 1) def test_create_decorator_tuple(self): decorator = self._makeOne(decorator=('decorator1', 'decorator2')) self.assertEqual(decorator.decorator, ('decorator1', 'decorator2')) def test_call_function(self): decorator = self._makeOne()