Liviu
Liviu

Reputation: 1093

Tornado asynchronous actions in custom decorator

I am trying to build a caching decorator using redis and the tornadis library for my Tornado route handlers. This is what I have so far:

def rediscache(route):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            result = yield redis.call("GET", "latest_info")
            print(result)
            func(*args, **kwargs)
        return wrapper
    return decorator

Inside my route handler I am using it like so:

class MainHandler(tornado.web.RequestHandler):

    def initialize(self, db, redis):
        self.db = db
        self.quakes = self.db.quakes
        self.redis = redis

    @gen.coroutine
    @rediscache('route_is_here')
    def get(self):
        ''' ... handler logic here ... '''

Problem is that if I use my decorator, I stop seeing output to the web from my handler. If instead of ...

result = yield redis.call("GET", "latest_info")

I do ...

result = redis.call("GET", "latest_info")

Then I start to see output in my browser again but is this the proper way to do it? Is this still asynchronous? If not what is the propper way to do it? Thanks!

Upvotes: 1

Views: 485

Answers (1)

kwarunek
kwarunek

Reputation: 12587

Wrapper in your decorator should be gen.coroutine if you want to yield coroutines:

def rediscache(route):
    def decorator(func):
        @tornado.gen.coroutine
        @wraps(func)
        def wrapper(*args, **kwargs):
            result = yield redis.call("GET", "latest_info")
            print(result)
            if not result:
                new = yield func(*args, **kwargs)
                # save to
        return wrapper
    return decorator

You need also change the order of decorators to:

@rediscache('route_is_here')
@gen.coroutine
def get(self):
    ''' ... handler logic here ... '''

edit

Explanation of the decorators order

@second
@first
def my_func():
    pass

is the same as

my_func = second(first(my_func))

So if you want to await (yield) your original get in decorator, you have to pass coroutine therefore it must be before rediscache.

More info about decorators - https://wiki.python.org/moin/PythonDecorators

Upvotes: 2

Related Questions