I created solution, that do a job close to the needed. Idea is to use wrapper for Browser() which uses __getattr__ and __call__ to collect action (like getting attribute or call) and return self to catch next one action. After all actions collected, we "catch" yiled from wrapper using __iter__ and process all collected actions.
import asyncio def chain(obj): """ Enables coroutines chain for obj. Usage: text = yield from chain(obj).go().click().attr Note: Returns not coroutine, but object that can be yield from. """ class Chain: _obj = obj _queue = [] # Collect getattr of call to queue: def __getattr__(self, name): Chain._queue.append({'type': 'getattr', 'name': name}) return self def __call__(self, *args, **kwargs): Chain._queue.append({'type': 'call', 'params': [args, kwargs]}) return self # On iter process queue: def __iter__(self): res = Chain._obj while Chain._queue: action = Chain._queue.pop(0) if action['type'] == 'getattr': res = getattr(res, action['name']) elif action['type'] == 'call': args, kwargs = action['params'] res = res(*args, **kwargs) if asyncio.iscoroutine(res): res = yield from res return res return Chain()
Usage:
class Browser: @asyncio.coroutine def go(self): print('go') return self @asyncio.coroutine def click(self): print('click') return self def text(self): print('text') return 5 @asyncio.coroutine def main(): text = yield from chain(Browser()).go().click().go().text() print(text) loop = asyncio.get_event_loop() loop.run_until_complete(main())
Output:
go click go text 5
Note, that chain() doesn't return real coroutine, but object that can be used like coroutine on yield from. We should wrap result of chain() to get normal coroutine, which can be passed to any asyncio function that requires coroutine:
@asyncio.coroutine def chain_to_coro(chain): return (yield from chain) @asyncio.coroutine def main(): ch = chain(Browser()).go().click().go().text() coro = chain_to_coro(ch) results = yield from asyncio.gather(*[coro], return_exceptions=True) print(results)
Output:
go click go text [5]
getattr.