diff --git a/CHANGES.rst b/CHANGES.rst index 98070f6..b4f7c2d 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -9,6 +9,9 @@ Unreleased **Changed** - Improved exception message when ``asyncio.TimeoutError`` is raised. +- Fixed type hinting for ``data`` and ``params`` in ``Requestor.request()``. +- Pass ``files`` to ``aiohttp.ClientSession.request()`` in ``Requestor.request()`` + correctly again. 3.0.2 (2025/08/06) ------------------ diff --git a/asyncprawcore/sessions.py b/asyncprawcore/sessions.py index 4399725..77b6628 100644 --- a/asyncprawcore/sessions.py +++ b/asyncprawcore/sessions.py @@ -153,13 +153,14 @@ def _log_request( log.debug("Params: %s", pformat(params)) @staticmethod - def _preprocess_dict(data: dict[str, object]) -> dict[str, object]: + def _preprocess_dict(data: dict[str, object] | None) -> dict[str, object]: new_data = {} - for key, value in data.items(): - if isinstance(value, bool): - new_data[key] = str(value).lower() - elif value is not None: - new_data[key] = str(value) if not isinstance(value, str) else value + if data: + for key, value in data.items(): + if isinstance(value, bool): + new_data[key] = str(value).lower() + elif value is not None: + new_data[key] = str(value) if not isinstance(value, str) else value return new_data @property @@ -196,6 +197,7 @@ async def _do_retry( self, *, data: list[tuple[str, object]] | None, + files: dict[str, BinaryIO | TextIO] | None, json: dict[str, object] | None, method: str, params: dict[str, object], @@ -207,6 +209,7 @@ async def _do_retry( log.warning("Retrying due to %s: %s %s", status, method, url) return await self._request_with_retries( data=data, + files=files, json=json, method=method, params=params, @@ -220,6 +223,7 @@ async def _do_retry( async def _make_request( self, data: list[tuple[str, object]] | None, + files: dict[str, BinaryIO | TextIO] | None, json: dict[str, object] | None, method: str, params: dict[str, object], @@ -233,6 +237,7 @@ async def _make_request( url, allow_redirects=False, data=data, + files=files, json=json, params=params, timeout=timeout, @@ -248,36 +253,7 @@ async def _make_request( ) yield response - def _preprocess_data( - self, - data: dict[str, object], - files: dict[str, BinaryIO | TextIO] | None, - ) -> dict[str, object]: - """Preprocess data and files before request. - - This is to convert requests that are formatted for the ``requests`` package to - be compatible with the ``aiohttp`` package. The motivation for this is so that - ``praw`` and ``asyncpraw`` can remain as similar as possible and thus making - contributions to ``asyncpraw`` simpler. - - This method does the following: - - - Removes keys that have a value of ``None`` from ``data``. - - Moves ``files`` into ``data``. - - :param data: Dictionary, bytes, or file-like object to send in the body of the - request. - :param files: Dictionary, mapping ``filename`` to file-like object to add to - ``data``. - - """ - if isinstance(data, dict): - data = self._preprocess_dict(data) - if files is not None: - data.update(files) - return data - - def _preprocess_params(self, params: dict[str, object]) -> dict[str, object]: + def _preprocess_params(self, params: dict[str, object] | None) -> dict[str, object]: """Preprocess params before request. This is to convert requests that are formatted for the ``requests`` package to @@ -299,6 +275,7 @@ async def _request_with_retries( # noqa: PLR0912 self, *, data: list[tuple[str, object]] | None, + files: dict[str, BinaryIO | TextIO] | None, json: dict[str, object] | None, method: str, params: dict[str, object], @@ -315,6 +292,7 @@ async def _request_with_retries( # noqa: PLR0912 try: async with self._make_request( data=data, + files=files, json=json, method=method, params=params, @@ -332,6 +310,7 @@ async def _request_with_retries( # noqa: PLR0912 if retry_status is not None and retry_strategy_state.should_retry_on_failure(): return await self._do_retry( data=data, + files=files, json=json, method=method, params=params, @@ -361,6 +340,7 @@ async def _request_with_retries( # noqa: PLR0912 ): return await self._do_retry( data=data, + files=files, json=json, method=method, params=params, @@ -413,7 +393,7 @@ async def request( params = self._preprocess_params(deepcopy(params) or {}) params["raw_json"] = "1" if isinstance(data, dict): - data = self._preprocess_data(deepcopy(data), files) + data = self._preprocess_dict(deepcopy(data)) data["api_type"] = "json" data_list = sorted(data.items()) else: @@ -424,6 +404,7 @@ async def request( url = urljoin(self._requestor.oauth_url, path) return await self._request_with_retries( data=data_list, + files=files, json=json, method=method, params=params, diff --git a/pyproject.toml b/pyproject.toml index d72f661..a27cb17 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,18 +13,16 @@ lint = [ test = [ "aiohttp <4", "coverage>=7.6.10", - "pyright", "pytest>=8.3.4", - "pytest-asyncio ==0.18.*", - "pytest-vcr ==1.*", - "urllib3 ==1.*", - "vcrpy ==4.2.1" + "pytest-asyncio==1.2.*", + "pytest-vcr==1.*", + "vcrpy==7.0.0" ] type = [ - "aiohttp <4", + "aiohttp<4", "pyright>=1.1.393", "pytest>=8.3.4", - "pytest-asyncio ==0.18.*" + "pytest-asyncio==1.2.*" ] [project] diff --git a/uv.lock b/uv.lock index 6771df9..5873a4a 100644 --- a/uv.lock +++ b/uv.lock @@ -1,6 +1,12 @@ version = 1 revision = 3 requires-python = ">=3.9" +resolution-markers = [ + "python_full_version >= '3.10' and platform_python_implementation == 'PyPy'", + "python_full_version >= '3.10' and platform_python_implementation != 'PyPy'", + "python_full_version < '3.10' and platform_python_implementation == 'PyPy'", + "python_full_version < '3.10' and platform_python_implementation != 'PyPy'", +] [[package]] name = "aiohappyeyeballs" @@ -155,11 +161,9 @@ lint = [ test = [ { name = "aiohttp" }, { name = "coverage" }, - { name = "pyright" }, { name = "pytest" }, { name = "pytest-asyncio" }, { name = "pytest-vcr" }, - { name = "urllib3" }, { name = "vcrpy" }, ] type = [ @@ -184,18 +188,16 @@ lint = [ test = [ { name = "aiohttp", specifier = "<4" }, { name = "coverage", specifier = ">=7.6.10" }, - { name = "pyright" }, { name = "pytest", specifier = ">=8.3.4" }, - { name = "pytest-asyncio", specifier = "==0.18.*" }, + { name = "pytest-asyncio", specifier = "==1.2.*" }, { name = "pytest-vcr", specifier = "==1.*" }, - { name = "urllib3", specifier = "==1.*" }, - { name = "vcrpy", specifier = "==4.2.1" }, + { name = "vcrpy", specifier = "==7.0.0" }, ] type = [ { name = "aiohttp", specifier = "<4" }, { name = "pyright", specifier = ">=1.1.393" }, { name = "pytest", specifier = ">=8.3.4" }, - { name = "pytest-asyncio", specifier = "==0.18.*" }, + { name = "pytest-asyncio", specifier = "==1.2.*" }, ] [[package]] @@ -207,6 +209,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/77/06/bb80f5f86020c4551da315d78b3ab75e8228f89f0162f2c3a819e407941a/attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3", size = 63815, upload-time = "2025-03-13T11:10:21.14Z" }, ] +[[package]] +name = "backports-asyncio-runner" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8e/ff/70dca7d7cb1cbc0edb2c6cc0c38b65cba36cccc491eca64cabd5fe7f8670/backports_asyncio_runner-1.2.0.tar.gz", hash = "sha256:a5aa7b2b7d8f8bfcaa2b57313f70792df84e32a2a746f585213373f900b42162", size = 69893, upload-time = "2025-07-02T02:27:15.685Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/59/76ab57e3fe74484f48a53f8e337171b4a2349e506eabe136d7e01d059086/backports_asyncio_runner-1.2.0-py3-none-any.whl", hash = "sha256:0da0a936a8aeb554eccb426dc55af3ba63bcdc69fa1a600b5bb305413a4477b5", size = 12313, upload-time = "2025-07-02T02:27:14.263Z" }, +] + [[package]] name = "cachetools" version = "6.1.0" @@ -838,15 +849,16 @@ wheels = [ [[package]] name = "pytest-asyncio" -version = "0.18.3" +version = "1.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ + { name = "backports-asyncio-runner", marker = "python_full_version < '3.11'" }, { name = "pytest" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/4d/73/769d29676fb36a36e5a57c198154171081aabcfd08112a24a4e3fb5c9f10/pytest-asyncio-0.18.3.tar.gz", hash = "sha256:7659bdb0a9eb9c6e3ef992eef11a2b3e69697800ad02fb06374a210d85b29f91", size = 28052, upload-time = "2022-03-25T09:43:58.406Z" } +sdist = { url = "https://files.pythonhosted.org/packages/42/86/9e3c5f48f7b7b638b216e4b9e645f54d199d7abbbab7a64a13b4e12ba10f/pytest_asyncio-1.2.0.tar.gz", hash = "sha256:c609a64a2a8768462d0c99811ddb8bd2583c33fd33cf7f21af1c142e824ffb57", size = 50119, upload-time = "2025-09-12T07:33:53.816Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8b/d6/4ecdd0c5b49a2209131b6af78baa643cec35f213abbc54d0eb1542b3786d/pytest_asyncio-0.18.3-1-py3-none-any.whl", hash = "sha256:16cf40bdf2b4fb7fc8e4b82bd05ce3fbcd454cbf7b92afc445fe299dabb88213", size = 14768, upload-time = "2022-03-28T13:53:15.727Z" }, - { url = "https://files.pythonhosted.org/packages/ac/4b/7c400506ec484ec999b10133aa8e31af39dfc727042dc6944cd45fd927d0/pytest_asyncio-0.18.3-py3-none-any.whl", hash = "sha256:8fafa6c52161addfd41ee7ab35f11836c5a16ec208f93ee388f752bea3493a84", size = 14597, upload-time = "2022-03-25T09:43:57.106Z" }, + { url = "https://files.pythonhosted.org/packages/04/93/2fa34714b7a4ae72f2f8dad66ba17dd9a2c793220719e736dda28b7aec27/pytest_asyncio-1.2.0-py3-none-any.whl", hash = "sha256:8e17ae5e46d8e7efe51ab6494dd2010f4ca8dae51652aa3c8d55acf50bfb2e99", size = 15095, upload-time = "2025-09-12T07:33:52.639Z" }, ] [[package]] @@ -940,15 +952,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/4c/9b/0b8aa09817b63e78d94b4977f18b1fcaead3165a5ee49251c5d5c245bb2d/ruff-0.12.7-py3-none-win_arm64.whl", hash = "sha256:dfce05101dbd11833a0776716d5d1578641b7fddb537fe7fa956ab85d1769b69", size = 11982083, upload-time = "2025-07-29T22:32:33.881Z" }, ] -[[package]] -name = "six" -version = "1.17.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, -] - [[package]] name = "tomli" version = "2.2.1" @@ -1071,17 +1074,17 @@ wheels = [ [[package]] name = "vcrpy" -version = "4.2.1" +version = "7.0.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pyyaml" }, - { name = "six" }, + { name = "urllib3" }, { name = "wrapt" }, { name = "yarl" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/74/ae/642573c8f79999a6da22e78d80c53924e0faa140bfca3dcc41240dcd567f/vcrpy-4.2.1.tar.gz", hash = "sha256:7cd3e81a2c492e01c281f180bcc2a86b520b173d2b656cb5d89d99475423e013", size = 78895, upload-time = "2022-08-31T19:18:17.841Z" } +sdist = { url = "https://files.pythonhosted.org/packages/25/d3/856e06184d4572aada1dd559ddec3bedc46df1f2edc5ab2c91121a2cccdb/vcrpy-7.0.0.tar.gz", hash = "sha256:176391ad0425edde1680c5b20738ea3dc7fb942520a48d2993448050986b3a50", size = 85502, upload-time = "2024-12-31T00:07:57.894Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8b/c5/f9efe3fea61a844ef1c47c800139d02984442a3a61ab4608fb2a682bc78d/vcrpy-4.2.1-py2.py3-none-any.whl", hash = "sha256:efac3e2e0b2af7686f83a266518180af7a048619b2f696e7bad9520f5e2eac09", size = 40815, upload-time = "2022-08-31T19:18:15.869Z" }, + { url = "https://files.pythonhosted.org/packages/13/5d/1f15b252890c968d42b348d1e9b0aa12d5bf3e776704178ec37cceccdb63/vcrpy-7.0.0-py2.py3-none-any.whl", hash = "sha256:55791e26c18daa363435054d8b35bd41a4ac441b6676167635d1b37a71dbe124", size = 42321, upload-time = "2024-12-31T00:07:55.277Z" }, ] [[package]]