# First, let's create an awaitable object. # In this case it's a very dumb container of integers. # Any time a coroutine runs `await Thing(n)` it just resolves to n # However, we could resolve it to something completely different if we wanted to class Thing: def __init__(self, n): self._n = n def __await__(self): return (yield self) # Simple runner of the coroutine, which ignores a bunch of # things like error handling etc. def thing_interceptor(coro): value_to_send = None while True: try: thing = coro.send(value_to_send) # At this point, we assume everything we get back # is a Thing object. But we could actually accept # asyncio coroutines here if we wanted to, and relay # them to the event loop. This would make it possible # for the coroutine to use anything in asyncio # (eg asyncio.sleep) value_to_send = thing._n except StopIteration as exc: return exc.value # This is just the standard recursive Fibonacci definition # It serves the purpose of how you can have a deep call stack # and still make it possible to interact back with a sort of # "context", which in this case is a very silly thing that just # resolves Thing(n) to n, but in theory could be something a # lot more complex async def f(n): if n <= 1: value = await Thing(n) else: value = await f(n-2) + await f(n-1) return value coro = f(10) print(thing_interceptor(coro))