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

Asyncio integration #85

Closed
tailhook opened this issue May 16, 2014 · 8 comments
Closed

Asyncio integration #85

tailhook opened this issue May 16, 2014 · 8 comments

Comments

@tailhook
Copy link

It would be nice if I could do:

@click.command()
@ascyncio.coroutine
def command():
    yield from whatever

I think the only place that should be changed to make that work would be Context.invoke (check if function is coroutine, do loop.run_until_complete()). But it's hard to subclass context in current click.

Would you mind making this functionality built-in?

@mitsuhiko
Copy link
Contributor

Why does that have to be in click? Just have a custom decorator that does asyncio.coroutine + whatever wrapper logic necessary. Eg:

import asyncio
from functools import update_wrapper

def coro(f):
    f = asyncio.coroutine(f)
    def wrapper(*args, **kwargs):
        loop = asyncio.get_event_loop()
        return loop.run_until_complete(f(*args, **kwargs))
    return update_wrapper(wrapper, f)

Would be used like this then:

@click.command()
@coro
def command():
    yield from whatever

For more complex cases you need to accept the loop anyways or get it from somewhere.

Generally I don't want to put builtin support for asyncio into click. It really does not belong there and by pure user count someone will come and ask for twisted or whatever support next :)

@tailhook
Copy link
Author

Ah, Thanks. I don't know why I didn't find this solution before :)

@jamesstidard
Copy link

Just curious if this is still the recommended practice now that async/await is a first-class language feature.

I feel a little dirty having to redundantly decorate all my commands with a @coroutine wrapper as well as a async def. Or maybe theirs a neater way that I've not thought of where I don't need to decorate each command.

Thanks - minor thing I know.

@Tails

This comment was marked as off-topic.

@pallets pallets deleted a comment from yashbhutwala Nov 26, 2018
@amitkot

This comment was marked as off-topic.

@jodal
Copy link
Contributor

jodal commented Jun 19, 2019

This is an updated version making use of asyncio.run() from Python 3.7:

import asyncio
from functools import wraps

def coro(f):
    @wraps(f)
    def wrapper(*args, **kwargs):
        return asyncio.run(f(*args, **kwargs))

    return wrapper

Usage:

@click.command()
@coro
async def command():
    await asyncio.sleep(1)
    click.echo("Delayed hello")

@vartagg
Copy link

vartagg commented Jul 5, 2019

BTW, there is the fork of click, which allows to use it with asyncio https://github.com/python-trio/trio-click

@rabernat
Copy link

New click user here. Thanks for this great tool.

I naively tried decorating a coroutine with @click.command() and received the cryptic error:

sys:1: RuntimeWarning: coroutine 'main' was never awaited

I believe it would be reasonable for @click.command() to examine the function it is decorating and, if a coroutine is found, automatically wrap it with the @coro decorator suggested above. In the meantime, the workaround worked great.

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

No branches or pull requests

8 participants