Reputation: 15406
Let's say I have a bunch of coroutines. These coroutines should be called using yield from
. How can I detect I made a mistake and directly called the coroutine like a subroutine ?
Here is an example code :
import asyncio
@asyncio.coroutine
def A(msg):
print(msg)
yield from asyncio.sleep(1)
@asyncio.coroutine
def B():
while True :
yield from A('1')
A('2')
yield from A('3')
loop = asyncio.get_event_loop()
loop.run_until_complete(B())
And the output :
1
3
1
3
1
3
...
Calling coroutine like subroutine does nothing, but does not raise exception or block the event loop, so the failure mode is very quiet.
Upvotes: 1
Views: 259
Reputation: 8127
I added a check_iterator function to your code. If you decorate your coroutine with this function, it will print information out if your coroutine is ever directly called and is not accessed using __iter__
. Depending on your actual code, you may need to make a more complete implementation and wrap __next__
as well. That would probably be somewhat lower-performance, though.
import asyncio
import functools
class IterWrap(object):
def __init__(self, f, *args, **kwds):
self.iterated = False
self.info = [f, args, kwds]
self.f = f(*args, **kwds)
def __iter__(self):
self.iterated = True
return iter(self.f)
def __del__(self):
if not self.iterated:
print("Did not yield: %s" % self.info)
def check_iterator(f):
@functools.wraps(f)
def wrapper(*args, **kwds):
return IterWrap(f, *args, **kwds)
return wrapper
@check_iterator
@asyncio.coroutine
def A(msg):
print(msg)
yield from asyncio.sleep(1)
@asyncio.coroutine
def B():
while True :
yield from A('1')
A('2')
yield from A('3')
loop = asyncio.get_event_loop()
loop.run_until_complete(B())
The results of running this on Python 3.4 are:
1
Did not yield: [<function A at 0xb6e3189c>, ('2',), {}]
3
1
Did not yield: [<function A at 0xb6e3189c>, ('2',), {}]
3
1
Did not yield: [<function A at 0xb6e3189c>, ('2',), {}]
3
1
Did not yield: [<function A at 0xb6e3189c>, ('2',), {}]
3
Upvotes: 4