From dd3b0d4ad721909c84e1eff4f0fb08b117a72105 Mon Sep 17 00:00:00 2001 From: BobTheBuidler <70677534+BobTheBuidler@users.noreply.github.com> Date: Fri, 6 Sep 2024 05:16:32 -0400 Subject: [PATCH] Revert "feat: remove deprecated `ASyncIterable.wrap` method (#314)" (#318) This reverts commit ccdd23bfa8a4eeb8d3ae857e80aa16a333774f75. --- a_sync/iter.py | 13 ++++++------- tests/test_iter.py | 43 +++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 47 insertions(+), 9 deletions(-) diff --git a/a_sync/iter.py b/a_sync/iter.py index 0d0164ad..7432ebb1 100644 --- a/a_sync/iter.py +++ b/a_sync/iter.py @@ -101,17 +101,17 @@ class ASyncIterable(_AwaitableAsyncIterableMixin[T], Iterable[T]): The class achieves this by implementing both `__iter__` and `__aiter__` methods, enabling it to return appropriate iterator objects that can handle synchronous and asynchronous iteration, respectively. This dual functionality is particularly useful in codebases that are transitioning between synchronous and asynchronous code, or in libraries that aim to support both synchronous and asynchronous usage patterns without requiring the user to manage different types of iterable objects. """ - + @classmethod + def wrap(cls, wrapped: AsyncIterable[T]) -> "ASyncIterable[T]": + "Class method to wrap an AsyncIterable for backward compatibility." + logger.warning("ASyncIterable.wrap will be removed soon. Please replace uses with simple instantiation ie `ASyncIterable(wrapped)`") + return cls(wrapped) def __init__(self, async_iterable: AsyncIterable[T]): - """ - Initializes the ASyncIterable with an async iterable. - """ + "Initializes the ASyncIterable with an async iterable." if not isinstance(async_iterable, AsyncIterable): raise TypeError(f"`async_iterable` must be an AsyncIterable. You passed {async_iterable}") - self.__wrapped__ = async_iterable "The wrapped async iterable object." - def __repr__(self) -> str: return f"<{type(self).__name__} for {self.__wrapped__} at {hex(id(self))}>" @@ -122,7 +122,6 @@ def __aiter__(self) -> AsyncIterator[T]: def __iter__(self) -> Iterator[T]: "Returns an iterator for the wrapped async iterable." yield from ASyncIterator(self.__aiter__()) - __slots__ = "__wrapped__", AsyncGenFunc = Callable[P, Union[AsyncGenerator[T, None], AsyncIterator[T]]] diff --git a/tests/test_iter.py b/tests/test_iter.py index 88598ac2..1143b562 100644 --- a/tests/test_iter.py +++ b/tests/test_iter.py @@ -38,12 +38,13 @@ async def async_err_gen(): @test_both -def test_init(cls_to_test, async_generator): +def test_wrap_types(cls_to_test, async_generator): assert isinstance(cls_to_test(async_generator()), cls_to_test) + assert isinstance(cls_to_test.wrap(async_generator()), cls_to_test) @test_both def test_sync(cls_to_test, async_generator): - # sourcery skip: for-append-to-extend, identity-comprehension, list-comprehension, simplify-generator + # sourcery skip: identity-comprehension, list-comprehension # comprehension assert [i for i in cls_to_test(async_generator())] == [0, 1, 2] @@ -53,11 +54,15 @@ def test_sync(cls_to_test, async_generator): result.append(item) assert result == [0, 1, 2] + # wrap + assert list(cls_to_test.wrap(async_generator())) == [0, 1, 2] + # list assert list(cls_to_test(async_generator())) == [0, 1, 2] # helper method assert cls_to_test(async_generator()).materialized == [0, 1, 2] + assert cls_to_test.wrap(async_generator()).materialized == [0, 1, 2] @test_both @pytest.mark.asyncio_cooperative @@ -81,6 +86,10 @@ async def test_async(cls_to_test, async_generator): # await assert await cls_to_test(async_generator()) == [0, 1, 2] + # wrap + assert [i async for i in cls_to_test.wrap(async_generator())] == [0, 1, 2] + assert await cls_to_test.wrap(async_generator()) == [0, 1, 2] + @test_both def test_sync_empty(cls_to_test, async_generator_empty): @@ -153,6 +162,20 @@ async def test_stop_iteration_async(cls_to_test, async_generator): # Test decorator +def test_aiterable_decorated_func_sync(): + with pytest.raises(TypeError, match="`async_iterable` must be an AsyncIterable. You passed "): + @ASyncIterable.wrap + async def decorated(): + yield 0 + +@pytest.mark.asyncio_cooperative +async def test_aiterable_decorated_func_async(async_generator): + with pytest.raises(TypeError, match="`async_iterable` must be an AsyncIterable. You passed "): + @ASyncIterable.wrap + async def decorated(): + yield 0 + + def test_aiterator_decorated_func_sync(async_generator): @ASyncIterator.wrap async def decorated(): @@ -173,6 +196,22 @@ async def decorated(): assert await retval == [0, 1, 2] +def test_aiterable_decorated_method_sync(): + with pytest.raises(TypeError, match=""): + class Test: + @ASyncIterable.wrap + async def decorated(self): + yield 0 + +@pytest.mark.asyncio_cooperative +async def test_aiterable_decorated_method_async(): + with pytest.raises(TypeError, match=""): + class Test: + @ASyncIterable.wrap + async def decorated(self): + yield 0 + + def test_aiterator_decorated_method_sync(async_generator): class Test: @ASyncIterator.wrap