Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.python > #10953 > unrolled thread

How do I implement two decorators in Python both of which would eventually want to call the calling function

Started byDevraj <devraj@gmail.com>
First post2011-08-05 22:49 -0700
Last post2011-08-06 07:19 -0500
Articles 5 — 5 participants

Back to article view | Back to comp.lang.python


Contents

  How do I implement two decorators in Python both of which would eventually want to call the calling function Devraj <devraj@gmail.com> - 2011-08-05 22:49 -0700
    Re: How do I implement two decorators in Python both of which would eventually want to call the calling function Rafael Durán Castañeda <rafadurancastaneda@gmail.com> - 2011-08-06 09:49 +0200
    Re: How do I implement two decorators in Python both of which would eventually want to call the calling function Chris Rebert <clp2@rebertia.com> - 2011-08-06 00:49 -0700
    Re: How do I implement two decorators in Python both of which would eventually want to call the calling function Peter Otten <__peter__@web.de> - 2011-08-06 09:59 +0200
    Re: How do I implement two decorators in Python both of which would eventually want to call the calling function Tim Chase <python.list@tim.thechases.com> - 2011-08-06 07:19 -0500

#10953 — How do I implement two decorators in Python both of which would eventually want to call the calling function

FromDevraj <devraj@gmail.com>
Date2011-08-05 22:49 -0700
SubjectHow do I implement two decorators in Python both of which would eventually want to call the calling function
Message-ID<8b087624-4e05-4d2e-bf4f-78383202b7e1@28g2000pry.googlegroups.com>
Hi all,

I am trying to simply my Web application handlers, by using Python
decorators.

Essentially I want to use decorators to abstract code that checks for
authenticated sessions and the other that checks to see if the cache
provider (Memcache in this instance) has a suitable response.

Consider this method definition with the decorators:

@auth.login_required
@cache.clear
def post(self, facility_type_id = None):

auth.login_required checks to see if the user is logged in, otherwise
returns an appropriate error message, or executes the original
function.

cache.clear would check to to see if the cache has a particular key
and drop that, before it executes the calling method.

Both auth.login_required and cache.clear would want to eventually
execute the calling method (post).

From what I've read both, doing what I am doing now would execute the
calling method (post) twice.

My question, how do I chain decorators that end up executing the
calling method, but ensure that it's only called once.

Appreciate any pointers and thanks for your time.

[toc] | [next] | [standalone]


#10960

FromRafael Durán Castañeda <rafadurancastaneda@gmail.com>
Date2011-08-06 09:49 +0200
Message-ID<mailman.1964.1312616975.1164.python-list@python.org>
In reply to#10953
You don't need doing something special, each decorator returns a new 
function that and only the result function formed by all decorators will 
be run. Consider this:

def clear_cache(func):
     def decorator(*args, **kwargs):
         print "cache cleared"
         return func(*args, **kwargs)
     return decorator


def login(func):
     def decorator(*args, **kwargs):
         print "login"
         return func(*args, **kwargs)
     return decorator

@login
@clear_cache
def post(msg= "POST", *args):
     print msg
     return args


result = post("POST", "something")
print result

The output will be:

login
cache cleared
POST
('something',)

On 06/08/11 07:49, Devraj wrote:
> Hi all,
>
> I am trying to simply my Web application handlers, by using Python
> decorators.
>
> Essentially I want to use decorators to abstract code that checks for
> authenticated sessions and the other that checks to see if the cache
> provider (Memcache in this instance) has a suitable response.
>
> Consider this method definition with the decorators:
>
> @auth.login_required
> @cache.clear
> def post(self, facility_type_id = None):
>
> auth.login_required checks to see if the user is logged in, otherwise
> returns an appropriate error message, or executes the original
> function.
>
> cache.clear would check to to see if the cache has a particular key
> and drop that, before it executes the calling method.
>
> Both auth.login_required and cache.clear would want to eventually
> execute the calling method (post).
>
> > From what I've read both, doing what I am doing now would execute the
> calling method (post) twice.
>
> My question, how do I chain decorators that end up executing the
> calling method, but ensure that it's only called once.
>
> Appreciate any pointers and thanks for your time.

[toc] | [prev] | [next] | [standalone]


#10961

FromChris Rebert <clp2@rebertia.com>
Date2011-08-06 00:49 -0700
Message-ID<mailman.1965.1312617002.1164.python-list@python.org>
In reply to#10953
On Fri, Aug 5, 2011 at 10:49 PM, Devraj <devraj@gmail.com> wrote:
> Hi all,
>
> I am trying to simply my Web application handlers, by using Python
> decorators.
>
> Essentially I want to use decorators to abstract code that checks for
> authenticated sessions and the other that checks to see if the cache
> provider (Memcache in this instance) has a suitable response.
>
> Consider this method definition with the decorators:
>
> @auth.login_required
> @cache.clear
> def post(self, facility_type_id = None):
>
> auth.login_required checks to see if the user is logged in, otherwise
> returns an appropriate error message, or executes the original
> function.
>
> cache.clear would check to to see if the cache has a particular key
> and drop that, before it executes the calling method.
>
> Both auth.login_required and cache.clear would want to eventually
> execute the calling method (post).
>
> >From what I've read both, doing what I am doing now would execute the
> calling method (post) twice.

That's incorrect unless the decorators you're using are weird.

> My question, how do I chain decorators that end up executing the
> calling method, but ensure that it's only called once.

That's how it works normally; decorators stack (and order is therefore
important). With normal wrapping decorators, only the first decorator
gets access to the original function and is able to call it.
Subsequent decorators only get access to the already-wrapped function.

Example:

def decorator_A(func):
    def decorated(*args, **kwds):
        print "In decorator A"
        return func(*args, **kwds)
    return decorated

def decorator_B(func):
    def decorated(*args, **kwds):
        print "In decorator B"
        return func(*args, **kwds)
    return decorated

@decorator_B
@decorator_A
def myfunc(arg):
    print "hello", arg

>>> myfunc('bob')
In decorator B
In decorator A
hello bob


Notice that myfunc() only got executed once.

Cheers,
Chris
--
http://rebertia.com

[toc] | [prev] | [next] | [standalone]


#10963

FromPeter Otten <__peter__@web.de>
Date2011-08-06 09:59 +0200
Message-ID<j1is7n$2hc$1@solani.org>
In reply to#10953
Devraj wrote:

> Hi all,
> 
> I am trying to simply my Web application handlers, by using Python
> decorators.
> 
> Essentially I want to use decorators to abstract code that checks for
> authenticated sessions and the other that checks to see if the cache
> provider (Memcache in this instance) has a suitable response.
> 
> Consider this method definition with the decorators:
> 
> @auth.login_required
> @cache.clear
> def post(self, facility_type_id = None):
> 
> auth.login_required checks to see if the user is logged in, otherwise
> returns an appropriate error message, or executes the original
> function.
> 
> cache.clear would check to to see if the cache has a particular key
> and drop that, before it executes the calling method.
> 
> Both auth.login_required and cache.clear would want to eventually
> execute the calling method (post).
> 
> From what I've read both, doing what I am doing now would execute the
> calling method (post) twice.
> 
> My question, how do I chain decorators that end up executing the
> calling method, but ensure that it's only called once.

You typically don't need to do anything special for the above to work:

>>> def v(f):
...     print "decorator v, wrapping", f.__name__, "into a"
...     def a(*args, **kw):
...             print "calling", f.__name__, "from a"
...             return f(*args, **kw)
...     return a
...
>>> def w(f):
...     print "decorator w, wrapping", f.__name__, "into b"
...     def b(*args, **kw):
...             print "calling", f.__name__, "from b"
...             return f(*args, **kw)
...     return b
...
>>> @v
... @w
... def f(s):
...     print s
...
decorator w, wrapping f into b
decorator v, wrapping b into a
>>> f("hello")
calling b from a
calling f from b
hello

The output shows that w wraps f into b, but v then doesn't get to see the 
original f, it wraps b into a. Put another way

@v
@w
def f(): ...

is the same as

def f(): ...

f = v(w(f))

and calling f() now is equivalent to calling a() which may or may not invoke 
b() which may or may not invoke the original f().

Translated into your example:

def post(self, facility_type_id = None): ...
post = auth.login_required(cache.clear(post))

The cache should only be cleared after a successful login, and the original 
post() will only be invoked after a successful login and with a cleared 
cache.

[toc] | [prev] | [next] | [standalone]


#10973

FromTim Chase <python.list@tim.thechases.com>
Date2011-08-06 07:19 -0500
Message-ID<mailman.1978.1312633195.1164.python-list@python.org>
In reply to#10953
On 08/06/2011 02:49 AM, Chris Rebert wrote:
> On Fri, Aug 5, 2011 at 10:49 PM, Devraj<devraj@gmail.com>  wrote:
>> My question, how do I chain decorators that end up executing the
>> calling method, but ensure that it's only called once.
>
> That's how it works normally; decorators stack (and order is therefore
> important). With normal wrapping decorators, only the first decorator
> gets access to the original function and is able to call it.

I'd clarify "first decorator" here as the one closest to the 
decorated function which is actually the *last* one in the list 
of decorators, but first-to-decorate.  In Chris's example below, 
decorator_A is the only one that calls myfunc().

> Subsequent decorators only get access to the already-wrapped function.
>
> Example:
>
> def decorator_A(func):
>      def decorated(*args, **kwds):
>          print "In decorator A"
>          return func(*args, **kwds)
>      return decorated
>
> def decorator_B(func):
>      def decorated(*args, **kwds):
>          print "In decorator B"
>          return func(*args, **kwds)
>      return decorated
>
> @decorator_B
> @decorator_A
> def myfunc(arg):
>      print "hello", arg
>
>>>> myfunc('bob')
> In decorator B
> In decorator A
> hello bob
>
>
> Notice that myfunc() only got executed once.

-tkc


[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.python


csiph-web