-
Notifications
You must be signed in to change notification settings - Fork 3
/
agent.py
78 lines (58 loc) · 1.84 KB
/
agent.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
import asyncio
import functools
__version__ = '0.1.2'
__all__ = (
'gen',
'anext',
'Result',
'AsyncGenerator',
)
async def anext(gen):
"""The equivilent of next for async iterators
Usage:
>>> result = await anext(aiterable)
"""
return await gen.__anext__()
def async_generator(func):
"""A decorator that turns the given function into an AsyncGenerator"""
@functools.wraps(func)
def wrapper(*args, **kwargs):
return AsyncGenerator(func(*args, **kwargs))
return wrapper
class Result:
"""Wraps futures, or other values, so them may be properly yielded"""
def __init__(self, inner):
self.inner = inner
class AsyncGenerator:
def __init__(self, gen):
self.gen = gen
async def __aiter__(self):
"""Makes this class an async *iteratable*
Needs to return an async *iterator* which AsyncGenerator is
See https://docs.python.org/3/glossary.html#term-asynchronous-iterable
:returns: self
"""
return self
async def __anext__(self):
"""Makes this class an async *iterator*
Spins the interal coroutine then either:
Awaits futures returned and recalls itself
Unwraps Result classes
Otherwise returns the yielded value
"""
try:
item = next(self.gen)
except StopIteration:
raise StopAsyncIteration
if isinstance(item, Result):
return item.inner
# Note: anything that is "yield from"ed or awaited
# techincally just yields a Future
# Impossible to actually tell if this was a yielded future or
# A "yield from"ed future, hence Result
if isinstance(item, asyncio.Future):
await item
return await self.__anext__()
return item
# Nice aliases
gen = async_generator