From 2101bca8263998fdf21493bd8874963411ad551b Mon Sep 17 00:00:00 2001 From: Sylvain MARIE Date: Fri, 7 Jan 2022 15:30:37 +0100 Subject: [PATCH] Fixed an issue where using `partial` on a generator function in python 3.5 was raising a `SyntaxError`. Fixed #79 --- ..._latest_py.py => _main_py35_and_higher.py} | 19 -------------- src/makefun/_main_py36_and_higher.py | 26 +++++++++++++++++++ src/makefun/main.py | 4 +-- tests/test_partial_and_macros.py | 22 ++++++++++++---- 4 files changed, 45 insertions(+), 26 deletions(-) rename src/makefun/{_main_latest_py.py => _main_py35_and_higher.py} (62%) create mode 100644 src/makefun/_main_py36_and_higher.py diff --git a/src/makefun/_main_latest_py.py b/src/makefun/_main_py35_and_higher.py similarity index 62% rename from src/makefun/_main_latest_py.py rename to src/makefun/_main_py35_and_higher.py index ecf497e..df89eda 100644 --- a/src/makefun/_main_latest_py.py +++ b/src/makefun/_main_py35_and_higher.py @@ -22,22 +22,3 @@ def partial_f(*args, **kwargs): kwargs.update(preset_kwargs) # for python 3.4: explicit dict update yield from f(*chain(preset_pos_args, args), **kwargs) return partial_f - - -def make_partial_using_async_for_in_yield(new_sig, f, *preset_pos_args, **preset_kwargs): - """ - Makes a 'partial' when f is a async generator and python is new enough to support `async for v in f(): yield v` - - :param new_sig: - :param f: - :param presets: - :return: - """ - - @wraps(f, new_sig=new_sig) - async def partial_f(*args, **kwargs): - kwargs.update(preset_kwargs) - async for v in f(*chain(preset_pos_args, args), **kwargs): - yield v - - return partial_f diff --git a/src/makefun/_main_py36_and_higher.py b/src/makefun/_main_py36_and_higher.py new file mode 100644 index 0000000..c799e33 --- /dev/null +++ b/src/makefun/_main_py36_and_higher.py @@ -0,0 +1,26 @@ +# Authors: Sylvain MARIE +# + All contributors to +# +# License: 3-clause BSD, +from itertools import chain + +from makefun.main import wraps + + +def make_partial_using_async_for_in_yield(new_sig, f, *preset_pos_args, **preset_kwargs): + """ + Makes a 'partial' when f is a async generator and python is new enough to support `async for v in f(): yield v` + + :param new_sig: + :param f: + :param presets: + :return: + """ + + @wraps(f, new_sig=new_sig) + async def partial_f(*args, **kwargs): + kwargs.update(preset_kwargs) + async for v in f(*chain(preset_pos_args, args), **kwargs): + yield v + + return partial_f diff --git a/src/makefun/main.py b/src/makefun/main.py index 5aa3073..40c366f 100644 --- a/src/makefun/main.py +++ b/src/makefun/main.py @@ -1127,13 +1127,13 @@ def partial(f, # type: Callable if _is_generator_func(f): if sys.version_info >= (3, 3): - from makefun._main_latest_py import make_partial_using_yield_from + from makefun._main_py35_and_higher import make_partial_using_yield_from partial_f = make_partial_using_yield_from(new_sig, f, *preset_pos_args, **preset_kwargs) else: from makefun._main_legacy_py import make_partial_using_yield partial_f = make_partial_using_yield(new_sig, f, *preset_pos_args, **preset_kwargs) elif isasyncgenfunction(f) and sys.version_info >= (3, 6): - from makefun._main_latest_py import make_partial_using_async_for_in_yield + from makefun._main_py36_and_higher import make_partial_using_async_for_in_yield partial_f = make_partial_using_async_for_in_yield(new_sig, f, *preset_pos_args, **preset_kwargs) else: @wraps(f, new_sig=new_sig) diff --git a/tests/test_partial_and_macros.py b/tests/test_partial_and_macros.py index 1fc9e21..6fd4503 100644 --- a/tests/test_partial_and_macros.py +++ b/tests/test_partial_and_macros.py @@ -174,10 +174,19 @@ def f(a, b, c, **d): # assert str(signature(fp_ref)) == str(signature(fp)) -def test_simple_partial_copy(): - """Test that when not providing any argument to partial, it is equivalent to wraps with new sig = None""" - def f1(a): - return a + 1 +@pytest.mark.parametrize("is_generator", [False, True]) +def test_simple_partial_copy(is_generator): + """Test that when not providing any argument to partial, it is equivalent to wraps with new sig = None + + This test was extended to cover issue 79. + """ + + if is_generator: + def f1(a): + yield a + 1 + else: + def f1(a): + return a + 1 f2 = makefun.partial(f1) @@ -188,7 +197,10 @@ def f1(a): f3 = makefun.wraps(f1)(f1) assert f3.__wrapped__ == f1 - assert f2(1) == f3(1) == 2 + if is_generator: + assert next(f2(1)) == next(f3(1)) == 2 + else: + assert f2(1) == f3(1) == 2 # the func attribute is there too f4 = functools.partial(f1)