11

I have a Request handler and a decorator, I would like to work with the self object inside the decorator

class MyHandler(webapp.RequestHandler):

    @myDecorator
        def get(self):
            #code

Update: Please notice the difference between the first and second self

class myDecorator(object):

    def __init__(self, f):
        self.f = f

    def __call__(self):
        #work with self
  1. MyHandler > get ( function ) > self ( argument )
  2. myDecorator > __call__ ( function ) > self ( argument )

the self arguments mentioned above are different. My intention is to access the first self from inside __call__ function, or find a way to do something similar.

Hi can I access MyHandlers self argument from get function inside the decorator?

Update2: I want to implement a decorator to work with a custom login in google app engine:

I have a class ( requestHandler ):

class SomeHandler(webapp.RequestHandler):
    @only_registered_users
    def get(self):
        #do stuff here

And I want to decorate the get function in order to check out if the user is logged in or not:

from util.sessions import Session
import logging

class only_registered_users(object):

    def __init__(self, f):
        self.f = f

    def __call__(self):
        def decorated_get(self):
        logging.debug("request object:", self.request)
        session = Session()

        if hasattr(session, 'user_key'):
            return self.f(self)
        else:
            self.request.redirect("/login")


    return decorated_get

I know if a user is logged in if has the property 'user_key' in a session Object.

That's the main goal I'm interested in on this specific case

Let me know your suggestions / opinions if I'm doing something wrong!

Thanks!

Lesmana
  • 25,663
  • 9
  • 82
  • 87
Mc-
  • 3,968
  • 10
  • 38
  • 61
  • Also, putting "Google App Engine" in front of a question that is only about decorators might make your question get fewer views. Generic items are viewed a lot, questions about specific platforms get viewed and answered more slowly. – marr75 Jun 24 '11 at 15:44
  • 1
    No problem, if your question gets viewed more, my answer can get up-voted more. It's a win win. – marr75 Jun 25 '11 at 19:21

5 Answers5

11

I'm not entirely clear what it is you want, but if you just want to use the decorated function's arguments, then that is exactly what a basic decorator does. So to access say, self.request from a decorator you could do:

def log_request(fn):
    def decorated_get(self):
        logging.debug("request object:", self.request)
        return fn(self)
    return decorated_get

class MyHandler(webapp. RequestHandler):
    @log_request
    def get(self):
        self.response.out.write('hello world')

If you are trying to access the class the decorated function is attached to, then it's a bit tricker and you'll have to cheat a bit using the inspect module.

import inspect

def class_printer(fn):
    cls = inspect.getouterframes(inspect.currentframe())[1][3]
    def decorated_fn(self, msg):
        fn(self,cls+" says: "+msg)
    return decorated_fn

class MyClass():
    @class_printer
    def say(self, msg):
        print msg

In the example above we fetch the name of the class from the currentframe (during the execution of the decorator) and then store that in the decorated function. Here we are just prepending the class-name to whatever the msg variable is before passing it on to the original say function, but you can use your imagination to do what ever you like once you have the class name.

>>> MyClass().say('hello')
MyClass says: hello
Chris Farmiloe
  • 13,935
  • 5
  • 48
  • 57
  • I just updated the problem in order to make it more clear, I see I can use inspect for the class, but I would like to use the argument of the decorated function. Is that possible? thx @Chris – Mc- Jun 24 '11 at 16:15
  • Sorry still not too clear what you are trying to achieve... but I updated the answer with some more info anyway – Chris Farmiloe Jun 24 '11 at 16:47
  • I added a new Update (2) with the specific case, please let me know your opinion. Thanks a lot! – Mc- Jun 24 '11 at 22:12
2

source

def p_decorate(func):
   def func_wrapper(name):
       return "<p>{0}</p>".format(func(name))
   return func_wrapper

@p_decorate
def get_text(name):
   return "lorem ipsum, {0} dolor sit amet".format(name)

print get_text("John")

# Outputs <p>lorem ipsum, John dolor sit amet</p>
Massimo Variolo
  • 4,669
  • 6
  • 38
  • 64
1

Try this approach: Can a Python decorator of an instance method access the class?

Not the exact same question but you should be able to use the same approach to create a reference to self or a reference to a dictionary with objects of a certain class in it that you can get out in your decorator.

Community
  • 1
  • 1
marr75
  • 5,666
  • 1
  • 27
  • 41
1
import random

#decorator to the get function in order to check out if the user is logged in or not
def only_registered_users(func):
    def wrapper(handler):
        print 'Checking if user is logged in'
        if random.randint(0, 1):
            print 'User is logged in. Calling the original function.'
            func(handler)
        else:
            print 'User is NOT logged in. Redirecting...'
            # redirect code here
    return wrapper


class MyHandler(object):

    @only_registered_users
    def get(self):
        print 'Get function called'



m = MyHandler()
m.get()
warvariuc
  • 57,116
  • 41
  • 173
  • 227
0

The self argument to __call__ will be populated with the instance the decorated method is being called on. There's no way to access the decorator object from here - __call__ is a bound method, and what you're asking for would require it to be, in effect, 'doubly bound'. Python doesn't support this, so the decorator instance gets replaced with the decorated function's first argument.

The easiest way to work around this is to use nested functions, as @Chris recommends.

Nick Johnson
  • 100,655
  • 16
  • 128
  • 198