Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added gen.task decorator #580

Closed
wants to merge 1 commit into from
Closed

Conversation

ei-grad
Copy link
Contributor

@ei-grad ei-grad commented Aug 9, 2012

It provides more straightforward usage of gen.Task interface.

Example:

from functools import wraps

from tornado import gen, web 
from tornado.ioloop import IOLoop
from tornado.httpserver import HTTPServer


@gen.task
def worker(name, callback):
    callback('Hello, %s!' % name)


class MainHandler(web.RequestHandler):

    @web.asynchronous
    @gen.engine
    def get(self):
        result = yield worker('world')
        self.finish(result)


application = web.Application([
    (r"/", MainHandler),
])  

if __name__ == "__main__":
    server = HTTPServer(application)
    server.bind(8888)
    server.start(4)
    IOLoop.instance().start()

It provides more straight-forward usage of gen.Task interface.
@bdarnell
Copy link
Member

I don't like this approach because it reduces flexibility and has consequences for consistency. A function with your @task decorator can only be used as a gen.Task, and not with gen.Callback/gen.Wait, or from a non-@gen.engine function. You may decide you don't need that flexibility in your own application, but then calling your own code looks different from calling someone else's code (i.e. I wouldn't apply this decorator to AsyncHTTPClient.fetch, so you'd still need an explicit gen.Task for that function).

@bdarnell bdarnell closed this Aug 11, 2012
@finiteloop
Copy link
Contributor

An approach like this might be more flexible and equally useful:

def task(method):
    """Seamless support for yield-based coroutines.                             

    If the keyword arg "callback" is given, we execute a callback-style         
    call to the wrapped method. Otherwise, we return a tornado.gen.Task         
    for use with tornado.gen.engine.                                            
    """
    @functools.wraps(method)
    def wrapper(*args, **kwargs):
        if "callback" not in kwargs:
            return tornado.gen.Task(method, *args, **kwargs)
        else:
            return method(*args, **kwargs)
    return wrapper

I use this a lot in my own projects to avoid having to choose between the two programming styles in my libraries.

@bdarnell
Copy link
Member

Yeah, checking for a callback kwarg is a nice way of discriminating between the two styles. I've been thinking about doing something similar with my branch where I'm playing with concurrent.futures integration.

@ei-grad
Copy link
Contributor Author

ei-grad commented Dec 14, 2012

Are there any chances for something like a task decorator from @finiteloop's comment to be included in tornado.gen in the tornado 3.0 release?

@bdarnell
Copy link
Member

There's something like it in there now, although it returns a Future instead of a gen.Task (in 3.0 Futures can be yielded in gen.engine functions). It's currently called tornado.concurrent.future_wrap, although I'm not very happy with that name and may change it before the 3.0 release is finalized.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants