Chris McDonough
2012-11-11 a3adaeb87ceb2e7ae55bd9e56fcb0accee7c3d85
Merge branch 'master' of github.com:Pylons/pyramid
5 files modified
84 ■■■■■ changed files
CHANGES.txt 5 ●●●●● patch | view | raw | blame | history
pyramid/config/views.py 51 ●●●● patch | view | raw | blame | history
pyramid/scaffolds/copydir.py 2 ●●● patch | view | raw | blame | history
pyramid/tests/test_config/test_views.py 22 ●●●●● patch | view | raw | blame | history
pyramid/tests/test_view.py 4 ●●●● 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()