4 ms·
> Function colouring is a big problem in Python Not when you know how to call sync functions from async functions and vice versa. An sync function can call an
by compressedgas 6y ago
> Function colouring is a big problem in Python
Not when you know how to call sync functions from async functions and vice versa.
An sync function can call an async function via:
loop = asyncio.new_event_loop()
result = loop.run_until_complete(asyncio.ensure_future(red(x)))
A async function can call a sync function via:
loop = asyncio.get_event_loop()
result = await loop.run_in_executor(None, blue, x)
Where red and blue are defined as:
async def red(x):
pass
def blue(x):
pass
Note that the documentation is wrong about recommending create_task over ensure_future. That recommendation results in more restrictive code as create_task only accepts a coroutine and not a task.
This works for regular functions I don't know how it works for generators.
- ghostwriter 6y agoalternatively, one can use gevent and get a transparent asyncio from a modified runtime - something that a high-level language should've provided out of the box.
- jordic 6y agoHiding awaitables from the language, sounds like against the zen (explicit better than implicit) For example, when someone access a descriptor in Django.. this could end being a query to the db (transparent) but dangerous. With asyncio you explicitly await something to return the execution to the event loop. At least for me sounds like a safer behaviour
- ghostwriter 6y ago> Hiding awaitables from the language, sounds like against the zen (explicit better than implicit) Zen is not respected by explicit asyncio, just try to compose asyncio with iterators [1] [1] https://stackoverflow.com/questions/42448664/async-generator-is-not-an-iterator https://stackoverflow.com/questions/42448664/async-generator... This problem doesn't exist with gevent, and composability is a desired thing in any programming language. Python's asyncio fractioned the community that was previously doing implicit asyncio with sync interfaces, and the current state of API is not an example of composable primitives that follow the Zen of Python: > Beautiful is better than ugly. > Simple is better than complex. > Readability counts. > Special cases aren't special enough to break the rules.
- joshuamorton 6y agoRead that thread in full, it's also a case of explicit is better than implicit. (Async for vs for loops, essentially)
- jlokier 6y agoHiding awaits would be against the zen. As you explained, it's a behaviour difference that might matter. The await forces you to think about it, and that's safer. But the difference between asyncio.run(red(x)) and blue(x)... isn't. There's no difference which matters. They are just different implementations of the same behaviour. If red and blue are both the same DB query, with the only difference being red is async-style and blue sync-style, these two lines have exactly the same program behaviour: result = asyncio.run(red(x)) and result = blue(x) So the asyncio.run is just cognitive fog. It forces you to think about the type difference, but doesn't add any safety. It's almost the opposite of Python's usual duck-typing parsimony, which normally allows equivalent things to be used in place of each other without ceremony.
- 1337shadow 6y agoShould I start changing my code, from "foo.bar" to "foo.__getattribute__('bar')" ? Probably a bad compare, but I'm looking for someone to tell me why. Meanwhile, my pretty python foo = bar().something has gone all foo = (await bar()).something
- pansa2 6y agoWhat does gevent do - give Python something similar to Goroutines?
- ghostwriter 6y agoyes, pretty much, with a few specifics - https://sdiehl.github.io/gevent-tutorial/#greenlets https://sdiehl.github.io/gevent-tutorial/#greenlets
- WoodenChair 6y agoYou perfectly illustrated why this is a problem. Calling functions from one side to the other involves ceremony. Ceremony adds cognitive overhead and decreases readability.
- PaulHoule 6y agoI've written Python functions that "call" another function either async or not depending on how the function inspects. For instance, imagine a "maybe_await" method that just calls sync if is synchronous or otherwise awaits.
- mattbillenstein 6y agoIt's very heavy to do this is it not? Like you inspect the function on each call to figure out if it's async or not?
- PaulHoule 6y agoFor things that happen at UI speed it isn't bad. If I wanted to do something a million times a second I'd worry about it. Some Javascript frameworks, such as Vue, often do something similar in that you can pass either a sync or async callback and it does the right thing for either. In that case you could potentially inspect the function once and call it many times.
- 1337shadow 6y agoYou don't inspect the function, but the result. Is it a coroutine ? If so, it needs to be awaited (see my example above). I believe this also would allow non-async function to return a coroutine I suppose. Anyway, in this case chances are that there will be no performance overhead if there's any io bound operation running in the coroutine so it should run the iscoroutine check of the result and the await call before the function is done.
- 1337shadow 6y agoThat's trivial: result = yourfunc() if inspect.iscoroutine(result): result = asyncio.run(result) If I have code like that, it's at one place per library, didn't need to encapsulate it in a maybe_await function.
- mattbillenstein 6y agoWriting such code to call between sync/async makes me cry man - this is so ugly. I'd still consider it a problem.
- 1337shadow 6y agoActually Python now offers an asyncio.run function. Your example may now be: result = asyncio.run(red(x))