How to work with Futures.

1,119 views
Skip to first unread message

wsantos

unread,
May 4, 2013, 12:48:51 PM5/4/13
to python-...@googlegroups.com
i'm tring to make some queries on my model to clean my RequestHandlers i can get it working but i dont know if is the right way


Query
class Rates():
    def latest_rates(self, db):
        future = Future()

        def handle_response(response, error):
            import ipdb; ipdb.set_trace()
            if response.error:
                future.set_exception(response.error)
            else:
                future.set_result(response)

        db.rates.find_one({}, callback=handle_response)
        return future

RequestHandler

class MainHandler(RatingHandler):
    """"""
    @tornado.web.asynchronous
    @gen.coroutine
    def get(self):

        rate = model.Rate()
        t = yield rate.latest_rates(self.db)

it is working, but i really need to create a handle_response for every method in my model ? and how i can chain another query inside latest_rates ?

regrads.

Ben Darnell

unread,
May 4, 2013, 3:37:03 PM5/4/13
to Tornado Mailing List
In general, I would recommend using one of Tornado's two decorators @gen.coroutine and @tornado.concurrent.return_future instead of creating a Future manually. Your latest_rates function would then look like one of these:

  @gen.coroutine
  def latest_rates(self, db):
    response = yield gen.Task(db.rates.find_one, {})
    if response.error:
      raise response.error
    else:
      raise gen.Return(response)

  @return_future
  def latest_rates(self, db, callback):
    def handle_response(response, error):
      if response.error:
        raise response.error
      else:
        callback(response)
    db.rates.find_one({}, callback=handle_response)

-Ben


--
You received this message because you are subscribed to the Google Groups "Tornado Web Server" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python-tornad...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Waldecir Santos

unread,
May 4, 2013, 4:16:51 PM5/4/13
to python-...@googlegroups.com
Nice, i like the @return_future, but exisits a way to define the handle_response to be used in more than one function ?

Grato,

Waldecir

Ben Darnell

unread,
May 4, 2013, 4:30:27 PM5/4/13
to Tornado Mailing List
Of course.  The function can be defined wherever you want.  You can use a functools.partial or maybe a bound self pointer to get any state you need (the callback in this example) into handle_response.

-Ben

Waldecir Santos

unread,
May 4, 2013, 4:49:16 PM5/4/13
to python-...@googlegroups.com
like this  ?

class Rate(Model):

    def handle_response(self, response, error, callback):
        assert callback, iscallable
        if error:
            raise error
        else:
            callback(response)

    @return_future
    def latest_rates(self, db, callback):
        db.rates.find_one({}, callback=functools.partial(self.handle_response, callback=callback))


is that right ? its running here, but i dont know if i can have bad performance impact.

 

Grato,

Waldecir

Ben Darnell

unread,
May 4, 2013, 5:06:06 PM5/4/13
to Tornado Mailing List
Yes, that looks fine.

-Ben

Waldecir Santos

unread,
May 5, 2013, 10:33:58 AM5/5/13
to python-...@googlegroups.com
Thanks Ben, now i need more one  advice, latest_rates returns something like this

{
user_id : ObjectID(XXXXXXXXXX), 
rate: { xxx: yyyyy, zzzz: ccccc}
]
 

 I need to loop over the find in latest_rates to query user and inject/join the user

Grato,

Waldecir

Waldecir Santos

unread,
May 5, 2013, 12:17:24 PM5/5/13
to python-...@googlegroups.com
Well i can do preatty much, but i cant get latest_rates to return a value


    @return_future
    def get(self, db, id, callback):
        db.users.find_one({"_id": id}, callback=functools.partial(self.handle_response, callback=callback))


    @return_future
    @gen.coroutine
    def latest_rates(self, db, callback):
        rates = []
        cursor = db.rates.find()
        while (yield cursor.fetch_next):
            m_rate = cursor.next_object()
            m_user = yield self.get(db, m_rate['rater_id'])
            rates.append("xxx")
        callback(rates)


Im getting a exception 

    Traceback (most recent call last):
      File "/Users/wsantos/.virtualenvs/rating/lib/python2.7/site-packages/tornado/web.py", line 1055, in _stack_context_handle_exception
        raise_exc_info((type, value, traceback))
      File "/Users/wsantos/.virtualenvs/rating/lib/python2.7/site-packages/tornado/stack_context.py", line 239, in wrapped
        callback(*args, **kwargs)
      File "/Users/wsantos/.virtualenvs/rating/lib/python2.7/site-packages/tornado/web.py", line 1187, in future_complete
        f.result()
      File "/Users/wsantos/.virtualenvs/rating/lib/python2.7/site-packages/tornado/concurrent.py", line 129, in result
        raise_exc_info(self.__exc_info)
      File "/Users/wsantos/.virtualenvs/rating/lib/python2.7/site-packages/tornado/stack_context.py", line 270, in _nested
        if exit(*exc):
      File "/Users/wsantos/.virtualenvs/rating/lib/python2.7/site-packages/tornado/stack_context.py", line 180, in __exit__
        return self.exception_handler(type, value, traceback)
      File "/Users/wsantos/.virtualenvs/rating/lib/python2.7/site-packages/tornado/concurrent.py", line 211, in handle_error
        future.set_exc_info((typ, value, tb))
      File "/Users/wsantos/.virtualenvs/rating/lib/python2.7/site-packages/tornado/concurrent.py", line 125, in set_exc_info
        self.set_exception(exc_info[1])
      File "/Users/wsantos/.virtualenvs/rating/lib/python2.7/site-packages/tornado/concurrent.py", line 87, in set_exception
        self._set_done()
      File "/Users/wsantos/.virtualenvs/rating/lib/python2.7/site-packages/tornado/concurrent.py", line 95, in _set_done
        for cb in self._callbacks:
    TypeError: 'NoneType' object is not iterable


if i remove @return_futures and callback from latest_rates i can print all fine, i dont know to return now., and i dont know why documentation say to not user coroutine and return_future together, can you explain Ben, i think would be good to explain too in docs. 




Grato,

Waldecir

Ben Darnell

unread,
May 5, 2013, 12:30:10 PM5/5/13
to Tornado Mailing List
This error is not very helpful but what it means is that there was an error (which is not being printed) after the Future was already set as done.
 

if i remove @return_futures and callback from latest_rates i can print all fine, i dont know to return now., and i dont know why documentation say to not user coroutine and return_future together, can you explain Ben, i think would be good to explain too in docs. 

Using gen.coroutine and return_future at the same time should work (I think), but it's redundant because @gen.coroutine already returns a Future.  The docs for gen.coroutine say "@gen.coroutine is similar to the combination of @return_future and @gen.engine.".  In most cases I would recommend just using @gen.coroutine; only use @return_future if you prefer to have the callback argument exposed explicitly.

To return from a function using @gen.coroutine, raise gen.Return(value).  (in Python 3.3 it is possible to just use the return statement normally)

The problem you're seeing with the two decorators together is that @gen.coroutine hides the callback from the decorated function, and requires that it be passed by keyword instead of by position.  I think if you removed the callback argument and used gen.Return it would work with both decorators (but again there is no benefit to doing so).

-Ben

Waldecir Santos

unread,
May 5, 2013, 12:47:11 PM5/5/13
to python-...@googlegroups.com
Nice, i can get it working like this

    @gen.coroutine
    def latest_rates(self, db):
        rates = []
        cursor = db.rates.find()
        while (yield cursor.fetch_next):
            m_rate = cursor.next_object()
            m_user = yield self.get(db, m_rate['rater_id'])
            rates.append("xxx")
        raise gen.Return(rates)


But im counfused, is this the "right way/patterns" to follow  ? can i return value without the raise, and i dont get when you say " (but again there is no benefit to doing so)."



Grato,

Waldecir
Reply all
Reply to author
Forward
0 new messages