Skip to content

Added support for passing multiple decorators to add_view. #627

Closed
wants to merge 2 commits into from

5 participants

@xrotwang

Implements the functionality specified in Issue #623 pretty much as described in http://docs.pylonsproject.org/projects/pyramid_cookbook/en/latest/views/chaining_decorators.html

@mcdonc
Pylons Project member
mcdonc commented Jun 26, 2012

I started to merge this, then I ran into problems making a decision.

With this patch (derived from the recipe code) this:

 @view_config(..., decorator=[decorator1, decorator2])
 def myview(request):
     ....

Means (effectively) this:

 @view_config(...)
 @decorator2
 @decorator1
 def myview(request):
     ...

Note that the ordering of decorator composition might be the reverse of what most people might expect (although what-they-expect is maybe unknown really), which is that the decorators are expected to be listed in "top-down" order rather than functional-composition order.

Which way is right? Functional composition order (where original view callable is wrapped first in leftmost decorator in list) or top-down order (where original view callable is wrapped first in rightmost decorator in list)? Beats me, at least at 3:29am.

FWIW, I also came up with this for the changelog:

- The ``decorator=`` argument to ``view_config`` and ``config.add_view`` now
  accepts a tuple or list of callables instead of just a single callable.  If
  the argument is a tuple or list of callables, the callables will be
  combined and used in the order provided as a decorator.  For example::

     @view_config(..., decorator=[decorator1, decorator2])
     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).

  This feature is effectively a
  canonicalization of the recipe at
  http://docs.pylonsproject.org/projects/pyramid_cookbook/en/latest/views/chaining_decorators.html

  See also
  https://github.com/Pylons/pyramid/issues/623
  and
  https://github.com/Pylons/pyramid/pull/627

Some of this (at least the explanatory example-ish bits of it) should be adapted for the docstrings of the affected code.

@mcdonc
Pylons Project member
mcdonc commented Jun 26, 2012

References issue #623.

@xrotwang

i realized this problem with order of decorators, too. Unfortunately, I don't have much of an opinion, here. - I just wanted to pick an issue to try hacking on pyramid :)
But at least as long as the cookbook entry stays as it is, I think the order as implemented in my pull request makes sense, since this could well be the way people arrive at this feature.

@mcdonc
Pylons Project member
mcdonc commented Jun 26, 2012

I suppose it doesn't matter much, as either way we'll need to document it. If I had to pick one for no apparent reason, it would be functional-composition order.

@xrotwang

After looking at your examples, I'd also argue that

@view_config(...)
     @decorator2
     @decorator1
     def myview(request):
         ...

could be interpreted as "decorator1 is closer to myview, so will be applied first".

@avanov
avanov commented Jun 30, 2012

xrotwang, thanks for the implementation!

Hello, Chris.

Which way is right? Functional composition order (where original view callable is wrapped first in leftmost decorator in > list) or top-down order (where original view callable is wrapped first in rightmost decorator in list)?

The functional composition provides an unnatural order. If someone does not know the actual meaning of the "functional composition", it will be quite difficult to explain him the call ordering. On the other hand, everybody knows that in order to get to the princess you have to defeat all the guards one by one:

class BardsFairyTale:
    @view_config(decorator=(
        first_crush_the_hordes_of_goblins,
        then_face_with_skeletons,
        and_slay_its_lord_vampire,
        at_last_cut_dragons_dome(
            beware_his=(
                'tail',
                'and fireball',
                'his fetid breath',
                'and tricky claw'
            ),
            you_must_be_brave=True,
            and_full_of_heart=True,
            unless_you=knight.state_of_mind.READY_TO_BE_GULPED
        )
    ))
    def eat_them_all_and_save_the_princess(self):
        pass

And also:

  • it mimics the order of language-level decorators;
  • it corresponds to the order in which erlang's (and other functional languages) guard sequences are evaluated. So for people with such background the top-down order is quite natural as well.
@mcdonc
Pylons Project member
mcdonc commented Jul 1, 2012

Welp, not gonna think that hard about this. Deferring.

@mmerickel
Pylons Project member
@view_config(
    name='edit',
    request_method='POST',
    decorator=(
        login_required,
        check_csrf,
    )
)
def edit_thing(request):
    pass

vs

@view_config(name='edit', request_method='POST')
@login_required
@check_csrf
def edit_things(request):
    pass

Seems to correlate well to me, but I can definitely see the functional composition argument.

@pjenvey
Pylons Project member
pjenvey commented Nov 5, 2012

I agree w/ 2nd & Michael, the top down order is more natural being its the same order you would 'see' when using regular decorators.

The old DecoratorTools library (that TurboGears used to use) made the same decision:

http://peak.telecommunity.com/DevCenter/DecoratorTools#id1

@mmerickel mmerickel added a commit that closed this pull request Nov 5, 2012
@mmerickel mmerickel update changelog, close #627 1701243
@mmerickel mmerickel closed this in 1701243 Nov 5, 2012
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.