Skip to content

Commit 2dc14df

Browse files
authored
Merge pull request #78 from broglep-work/support-asyncgen
Add support for async generator functions
2 parents d9757b0 + 0b82141 commit 2dc14df

4 files changed

Lines changed: 81 additions & 0 deletions

File tree

src/makefun/_main_latest_py.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,22 @@ def partial_f(*args, **kwargs):
2222
kwargs.update(preset_kwargs) # for python 3.4: explicit dict update
2323
yield from f(*chain(preset_pos_args, args), **kwargs)
2424
return partial_f
25+
26+
27+
def make_partial_using_async_for_in_yield(new_sig, f, *preset_pos_args, **preset_kwargs):
28+
"""
29+
Makes a 'partial' when f is a async generator and python is new enough to support `async for v in f(): yield v`
30+
31+
:param new_sig:
32+
:param f:
33+
:param presets:
34+
:return:
35+
"""
36+
37+
@wraps(f, new_sig=new_sig)
38+
async def partial_f(*args, **kwargs):
39+
kwargs.update(preset_kwargs)
40+
async for v in f(*chain(preset_pos_args, args), **kwargs):
41+
yield v
42+
43+
return partial_f

src/makefun/main.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,13 @@ def iscoroutinefunction(f):
3333
def isgeneratorfunction(f):
3434
return False
3535

36+
try:
37+
from inspect import isasyncgenfunction
38+
except ImportError:
39+
# assume no generator function in old Python versions
40+
def isasyncgenfunction(f):
41+
return False
42+
3643
try: # python 3.5+
3744
from typing import Callable, Any, Union, Iterable, Dict, Tuple, Mapping
3845
except ImportError:
@@ -246,6 +253,8 @@ def create_function(func_signature, # type: Union[str, Signature]
246253
else:
247254
from makefun._main_legacy_py import get_legacy_py_generator_body_template
248255
body = get_legacy_py_generator_body_template() % (func_signature_str, params_str)
256+
elif isasyncgenfunction(func_impl):
257+
body = "async def %s\n async for y in _func_impl_(%s):\n yield y\n" % (func_signature_str, params_str)
249258
else:
250259
body = "def %s\n return _func_impl_(%s)\n" % (func_signature_str, params_str)
251260

@@ -1123,6 +1132,9 @@ def partial(f, # type: Callable
11231132
else:
11241133
from makefun._main_legacy_py import make_partial_using_yield
11251134
partial_f = make_partial_using_yield(new_sig, f, *preset_pos_args, **preset_kwargs)
1135+
elif isasyncgenfunction(f) and sys.version_info >= (3, 6):
1136+
from makefun._main_latest_py import make_partial_using_async_for_in_yield
1137+
partial_f = make_partial_using_async_for_in_yield(new_sig, f, *preset_pos_args, **preset_kwargs)
11261138
else:
11271139
@wraps(f, new_sig=new_sig)
11281140
def partial_f(*args, **kwargs):

tests/_test_py36.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
from makefun import wraps, partial
2+
3+
4+
def make_async_generator():
5+
async def f(v):
6+
yield v
7+
8+
return f
9+
10+
11+
def make_async_generator_wrapper(f):
12+
@wraps(f)
13+
async def wrapper(*args, **kwargs):
14+
async for v in f(*args, **kwargs):
15+
yield v
16+
17+
return wrapper
18+
19+
20+
def make_async_generator_partial(f, *args, **kwargs):
21+
return partial(f, *args, **kwargs)

tests/test_issues.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import inspect
12
import sys
23

34
import pytest
@@ -224,3 +225,31 @@ def f(a):
224225

225226
f2 = create_function("zoo(a)", f, func=f)
226227
assert f2(3) == 4
228+
229+
230+
@pytest.mark.skipif(sys.version_info < (3, 6), reason="requires python 3.6 or higher (async generator)")
231+
def test_issue_77_async_generator_wraps():
232+
import asyncio
233+
from ._test_py36 import make_async_generator, make_async_generator_wrapper
234+
235+
f = make_async_generator()
236+
wrapper = make_async_generator_wrapper(f)
237+
238+
assert inspect.isasyncgenfunction(f)
239+
assert inspect.isasyncgenfunction(wrapper)
240+
241+
assert asyncio.get_event_loop().run_until_complete(asyncio.ensure_future(wrapper(1).__anext__())) == 1
242+
243+
244+
@pytest.mark.skipif(sys.version_info < (3, 6), reason="requires python 3.6 or higher (async generator)")
245+
def test_issue_77_async_generator_partial():
246+
import asyncio
247+
from ._test_py36 import make_async_generator, make_async_generator_partial
248+
249+
f = make_async_generator()
250+
f_partial = make_async_generator_partial(f, v=1)
251+
252+
assert inspect.isasyncgenfunction(f)
253+
assert inspect.isasyncgenfunction(f_partial)
254+
255+
assert asyncio.get_event_loop().run_until_complete(asyncio.ensure_future(f_partial().__anext__())) == 1

0 commit comments

Comments
 (0)