From 29c9c98ea4b741cd338137ec9ba4c4c635cf3ece Mon Sep 17 00:00:00 2001 From: Lev Vereshchagin Date: Fri, 22 Nov 2024 11:24:45 +0300 Subject: [PATCH] Fix `AsyncResponse.iter_lines()` (#182) Doesn't work now. Causes an exception:`TypeError: 'async for' requires an object with __aiter__ method, got coroutine`. MRE: ```python import asyncio import niquests async def main() -> None: async with niquests.AsyncSession() as session: response: niquests.AsyncResponse = session.post(..., stream=True) async for line in response.iter_lines(): print(line) asyncio.run(main()) ``` --------- Co-authored-by: Ahmed TAHRI --- HISTORY.md | 6 +++++ docs/user/quickstart.rst | 16 +++++++++++++ src/niquests/__version__.py | 4 ++-- src/niquests/models.py | 4 ++-- tests/test_async.py | 45 +++++++++++++++++++------------------ 5 files changed, 49 insertions(+), 26 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index a096d0cd98..6a47c4313e 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,6 +1,12 @@ Release History =============== +3.11.1 (2024-11-22) +------------------- + +**Fixed** +- async version of ``iter_line``. (#182) + 3.11.0 (2024-11-20) ------------------- diff --git a/docs/user/quickstart.rst b/docs/user/quickstart.rst index 8b0690d5b1..deb82503bc 100644 --- a/docs/user/quickstart.rst +++ b/docs/user/quickstart.rst @@ -806,6 +806,22 @@ Delaying the content consumption in an async context can be easily achieved usin asyncio.run(main()) +Or using the ``iter_line`` method as such:: + + import niquests + import asyncio + + async def main() -> None: + + async with niquests.AsyncSession() as s: + r = await s.get("https://pie.dev/get", stream=True) + + async for chunk in r.iter_line(): + print(chunk) + + if __name__ == "__main__": + asyncio.run(main()) + Or simply by doing:: import niquests diff --git a/src/niquests/__version__.py b/src/niquests/__version__.py index f3332a04e3..3de8b4490a 100644 --- a/src/niquests/__version__.py +++ b/src/niquests/__version__.py @@ -9,9 +9,9 @@ __url__: str = "https://niquests.readthedocs.io" __version__: str -__version__ = "3.11.0" +__version__ = "3.11.1" -__build__: int = 0x031100 +__build__: int = 0x031101 __author__: str = "Kenneth Reitz" __author_email__: str = "me@kennethreitz.org" __license__: str = "Apache-2.0" diff --git a/src/niquests/models.py b/src/niquests/models.py index 39f5603abf..e1633d496a 100644 --- a/src/niquests/models.py +++ b/src/niquests/models.py @@ -1800,7 +1800,7 @@ async def iter_lines( # type: ignore[misc] pending = None - async for chunk in self.iter_content( # type: ignore[call-overload] + async for chunk in await self.iter_content( # type: ignore[call-overload] chunk_size=chunk_size, decode_unicode=decode_unicode ): if pending is not None: @@ -1816,7 +1816,7 @@ async def iter_lines( # type: ignore[misc] else: pending = None - async for line in lines: + for line in lines: yield line if pending is not None: diff --git a/tests/test_async.py b/tests/test_async.py index 0b18a4afa0..a94ee9dd7a 100644 --- a/tests/test_async.py +++ b/tests/test_async.py @@ -106,28 +106,29 @@ async def callback_on_early(early_resp) -> None: assert resp.status_code == 200 assert received_early_response is True - # async def test_http_trailer_preload(self) -> None: - # async with AsyncSession() as s: - # r = await s.get("https://httpbingo.org/trailers?foo=baz") - # - # assert r.ok - # assert r.trailers - # assert "foo" in r.trailers - # assert r.trailers["foo"] == "baz" - # - # async def test_http_trailer_no_preload(self) -> None: - # async with AsyncSession() as s: - # r = await s.get("https://httpbingo.org/trailers?foo=baz", stream=True) - # - # assert r.ok - # assert not r.trailers - # assert "foo" not in r.trailers - # - # await r.content - # - # assert r.trailers - # assert "foo" in r.trailers - # assert r.trailers["foo"] == "baz" + async def test_iter_line(self) -> None: + async with AsyncSession() as s: + r = await s.get("https://httpbingo.org/html", stream=True) + content = b"" + + async for line in r.iter_lines(): + assert isinstance(line, bytes) + content += line + + assert content + assert b"Herman Melville - Moby-Dick" in content + + async def test_iter_line_decode(self) -> None: + async with AsyncSession() as s: + r = await s.get("https://httpbingo.org/html", stream=True) + content = "" + + async for line in r.iter_lines(decode_unicode=True): + assert isinstance(line, str) + content += line + + assert content + assert "Herman Melville - Moby-Dick" in content @pytest.mark.usefixtures("requires_wan")