Async Python is Ugly

(edited)

Hey everyone,

I think we can all agree on something: standard Python asyncio is painful. It requires significant boilerplate. You have to manage event loops. You have to color your functions with async and await carefully. You have to manually orchestrate gather and create_task. It gets messy fast.

wove.png

I recently found a library that looks very promising: Wove.
https://github.com/curvedinf/wove

The Promise

Wove bills itself as "Beautiful Python async." It attempts to abstract away the "ugliness" of the event loop and task management.

It uses a context manager to handle the heavy lifting. You define tasks inside a weave block. Wove then builds a dependency graph based on your function signatures. It determines what can run in parallel and what needs to wait for data from previous steps.

Why it looks cool

  • Top-to-Bottom Readability: You write code in a logical, linear order. Wove handles the asynchronous execution flow.
  • Implicit Parallelism: You don't need asyncio.gather(). If two functions don't depend on each other, Wove runs them at the same time automatically.
  • Mix Sync and Async: You can mix standard def and async def functions. Wove runs synchronous functions in a thread pool to keep the loop non-blocking.
  • Zero Dependencies: It relies entirely on the standard library.

A Quick Example

Here is how you might fetch two things and combine them The Wove Way:

from wove import weave
import time

# This looks like normal code
with weave() as w:

    @w.do
    def get_user_data():
        time.sleep(1) # Simulating IO
        return {"name": "Michael"}

    @w.do
    def get_posts():
        time.sleep(1)
        return ["Post 1", "Post 2"]

    # This automatically waits for the previous two because
    # the arguments match the function names above.
    @w.do
    def render_page(get_user_data, get_posts):
        return f"User: {get_user_data['name']}, Posts: {len(get_posts)}"

# Execution happens when the block exits
print(w.results.final)

It looks remarkably clean. It handles the concurrency logic implicitly through variable names.

I haven't deployed this to production in anything yet (I just discovered it recently via an email), but for tools and scripts where I want speed without the asyncio headache, this looks like it might be a winner. I plan on putting it through it's paces and seeing how it works out, I just might end up loving it.

As always,
Michael Garcia a.k.a. TheCrazyGM

0.10005425 BEE
3 comments

untitled.gif

!PIMP
!PIZZA

0.00042365 BEE

Here is that exact example with standard asyncio

import asyncio

async def get_user_data():
    # We must use await and asyncio.sleep. 
    # Standard time.sleep() would block the entire program here.
    await asyncio.sleep(1) 
    return {"name": "Michael"}

async def get_posts():
    await asyncio.sleep(1)
    return ["Post 1", "Post 2"]

def render_page(user_data, posts):
    return f"User: {user_data['name']}, Posts: {len(posts)}"

async def main():
    # We have to explicitly tell asyncio to run these together.
    # If we just awaited them one by one, they would run sequentially.
    user_data, posts = await asyncio.gather(get_user_data(), get_posts())
    
    # Then we manually pass the results to the next function
    result = render_page(user_data, posts)
    print(result)

if __name__ == "__main__":
    # We have to manually start the event loop
    asyncio.run(main())

Mainly the having to manage the async def function and those can not be used with normal def function (at least not easily) and how the main() loop works in that one, where you have to await asyncio.gather() and asyncio.run() it can start to get REALLY complicated REALLY fast.

wove makes it clean and easy.

0.00425044 BEE

Finding something that makes something else work better is always a plus indeed! Why would the original asyncio be designed in such a cumbersome manner that it needs another library to make it work the way you want without a hassle? that's rhetorical of course, but seriously! 😁🙏💚✨🤙

0.00040675 BEE

PIZZA!

$PIZZA slices delivered:
@ecoinstant(3/20) tipped @thecrazygm

Please vote for pizza.witness!

0.00040948 BEE