Mir
Mir

Reputation: 680

Converting web.asynchronous code to gen.coroutine in tornado

I want to convert my current tornado app from using @web.asynchronous to @gen.coroutine. My asynchronous callback is called when a particular variable change happens on an IOLoop iteration. The current example in Tornado docs solves an I/O problem but in my case its the variable that I am interested in. I want the coroutine to wake up on the variable change. My app looks like the code shown below.

Note: I can only use Python2.

# A transaction is a DB change that can happen
# from another process
class Transaction:
    def __init__(self):
        self.status = 'INCOMPLETE'
        self.callback = None

# In this, I am checking the status of the DB
# before responding to the GET request
class MainHandler(web.RequestHandler):
    def initialize(self, app_reference):
        self.app_reference = app_reference

    @web.asynchronous
    def get(self):
        txn = Transaction()
        callback = functools.partial(self.do_something)
        txn.callback = callback

        self.app_reference.monitor_transaction(txn)

    def do_something(self):
        self.write("Finished GET request")
        self.finish()
# MyApp monitors a list of transactions and adds the callback
# 'transaction.callback' when transactions status changes to 
# COMPLETE state. 
class MyApp(Application):
    def __init__(self, settings):
        self.settings = settings
        self._url_patterns = self._get_url_patterns()
        self.txn_list = [] # list of all transactions being monitored
        Application.__init__(self, self._url_patterns, **self.settings)
        IOLoop.current().add_callback(self.check_status)

    def monitor_transaction(self, txn):
        self.txn_list.append(txn)

    def check_status(self):
        count = 0
        for transaction in self.txn_list:
            transaction.status = is_transaction_complete()
            if transaction.status is 'COMPLETE':
                IOLoop.current().add_callback(transaction.callback)
                self.txn_list.pop(count)
            count += 1
        if len(self.txn_list):
            IOloop.current().add_callback(self.check_status)


    # adds 'self' to url_patterns
    def _get_url_patterns(self):
        from urls import url_patterns
        modified_url_patterns = []
        for url in url_patterns:
            modified_url_patterns.append( url + ({ 'app_reference': self },))
        return modified_url_patterns

If I understand right for it to write using gen.coroutine the get should be modified as

@gen.coroutine
def get(self):
    txn = Transaction()
    response = yield wake_up_when_transaction_completes()
    # respond to GET here

My issue is I am not sure how to wake a routine only when the status changes and I cannot use a loop as it will block the tornado thread. Basically I want to notify from the IOLoop iteration.

def check_status():
    for transaction in txn_list:
        if transaction.status is 'COMPLETE':
            NOTIFY_COROUTINE

Upvotes: 0

Views: 181

Answers (1)

A. Jesse Jiryu Davis
A. Jesse Jiryu Davis

Reputation: 24007

Sounds like a job for the new tornado.locks! Released last week with Tornado 4.2:

http://tornado.readthedocs.org/en/latest/releases/v4.2.0.html#new-modules-tornado-locks-and-tornado-queues

Use an Event for this:

from tornado import locks, gen

event = locks.Event()

@gen.coroutine
def waiter():
    print("Waiting for event")
    yield event.wait()
    print("Done")

@gen.coroutine
def setter():
    print("About to set the event")
    event.set()

More info on the Event interface:

http://tornado.readthedocs.org/en/latest/locks.html#tornado.locks.Event

Upvotes: 2

Related Questions