From c42458a3249c764f5f350061ec4db1c6f6c745c3 Mon Sep 17 00:00:00 2001 From: Stephen Rosen Date: Fri, 30 Jan 2026 16:37:18 -0600 Subject: [PATCH] Remove the separate async parser class for v9.0 The separate AsyncParser class is no longer needed, as async parsing is now handled by the main Parser class. This transition was achieved during 8.x versions and therefore AsyncParser had to remain as a shim which replaces `parse` (the sync function on the base class) with a call to the async variant, `async_parse`. A battery of tests with called `await parser.parse(...)` on an async parser are trivially updated to `await parser.async_parse(...)`, but no other changes are needed. --- CHANGELOG.rst | 5 +++++ src/webargs/aiohttpparser.py | 3 +-- src/webargs/asyncparser.py | 40 ------------------------------------ tests/apps/aiohttp_app.py | 34 +++++++++++++++--------------- tests/test_aiohttpparser.py | 4 ++-- 5 files changed, 25 insertions(+), 61 deletions(-) delete mode 100644 src/webargs/asyncparser.py diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 3fa23dead..ffd38f4bc 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -12,6 +12,11 @@ Features: Other changes: +* *Backwards-incompatible*: The ``AsyncParser`` class has been removed, since + ``Parser`` now provides async functionality. + Users should use ``await parser.async_parse()`` to access the async features + of ``Parser``. + * Drop support for Python 3.9, which is EOL (:pr:`1019`). * Drop support for Bottle < 0.13 (:pr:`1019`). * Drop support for Flask < 3.1.0 (:pr:`1023`). diff --git a/src/webargs/aiohttpparser.py b/src/webargs/aiohttpparser.py index d2669863f..00660614a 100644 --- a/src/webargs/aiohttpparser.py +++ b/src/webargs/aiohttpparser.py @@ -30,7 +30,6 @@ def index(request, args): from marshmallow import RAISE, Schema, ValidationError from webargs import core -from webargs.asyncparser import AsyncParser from webargs.core import json from webargs.multidictproxy import MultiDictProxy @@ -70,7 +69,7 @@ def _find_exceptions() -> None: del _find_exceptions -class AIOHTTPParser(AsyncParser[web.Request]): +class AIOHTTPParser(core.Parser[web.Request]): """aiohttp request argument parser.""" DEFAULT_UNKNOWN_BY_LOCATION: dict[str, str | None] = { diff --git a/src/webargs/asyncparser.py b/src/webargs/asyncparser.py deleted file mode 100644 index beaeb340a..000000000 --- a/src/webargs/asyncparser.py +++ /dev/null @@ -1,40 +0,0 @@ -"""Asynchronous request parser.""" - -from __future__ import annotations - -import typing - -from webargs import core - - -class AsyncParser(core.Parser[core.Request]): - """Asynchronous variant of `webargs.core.Parser`. - - The ``parse`` method is redefined to be ``async``. - """ - - async def parse( - self, - argmap: core.ArgMap, - req: core.Request | None = None, - *, - location: str | None = None, - unknown: str | None = core._UNKNOWN_DEFAULT_PARAM, - validate: core.ValidateArg = None, - error_status_code: int | None = None, - error_headers: typing.Mapping[str, str] | None = None, - ) -> typing.Any: - """Coroutine variant of `webargs.core.Parser`. - - Receives the same arguments as `webargs.core.Parser.parse`. - """ - data = await self.async_parse( - argmap, - req, - location=location, - unknown=unknown, - validate=validate, - error_status_code=error_status_code, - error_headers=error_headers, - ) - return data diff --git a/tests/apps/aiohttp_app.py b/tests/apps/aiohttp_app.py index 02a02672b..2d67ca415 100644 --- a/tests/apps/aiohttp_app.py +++ b/tests/apps/aiohttp_app.py @@ -24,18 +24,18 @@ class HelloSchema(ma.Schema): async def echo(request): - parsed = await parser.parse(hello_args, request, location="query") + parsed = await parser.async_parse(hello_args, request, location="query") return json_response(parsed) async def echo_form(request): - parsed = await parser.parse(hello_args, request, location="form") + parsed = await parser.async_parse(hello_args, request, location="form") return json_response(parsed) async def echo_json(request): try: - parsed = await parser.parse(hello_args, request, location="json") + parsed = await parser.async_parse(hello_args, request, location="json") except json.JSONDecodeError as exc: raise aiohttp.web.HTTPBadRequest( text=json.dumps(["Invalid JSON."]), @@ -46,7 +46,7 @@ async def echo_json(request): async def echo_json_or_form(request): try: - parsed = await parser.parse(hello_args, request, location="json_or_form") + parsed = await parser.async_parse(hello_args, request, location="json_or_form") except json.JSONDecodeError as exc: raise aiohttp.web.HTTPBadRequest( text=json.dumps(["Invalid JSON."]), @@ -74,27 +74,27 @@ async def echo_use_args_validated(request, args): async def echo_ignoring_extra_data(request): return json_response( - await parser.parse(hello_exclude_schema, request, unknown=None) + await parser.async_parse(hello_exclude_schema, request, unknown=None) ) async def echo_multi(request): - parsed = await parser.parse(hello_multiple, request, location="query") + parsed = await parser.async_parse(hello_multiple, request, location="query") return json_response(parsed) async def echo_multi_form(request): - parsed = await parser.parse(hello_multiple, request, location="form") + parsed = await parser.async_parse(hello_multiple, request, location="form") return json_response(parsed) async def echo_multi_json(request): - parsed = await parser.parse(hello_multiple, request) + parsed = await parser.async_parse(hello_multiple, request) return json_response(parsed) async def echo_many_schema(request): - parsed = await parser.parse(hello_many_schema, request) + parsed = await parser.async_parse(hello_many_schema, request) return json_response(parsed) @@ -119,29 +119,29 @@ def always_fail(value): raise ma.ValidationError("something went wrong") args = {"text": fields.Str(validate=always_fail)} - parsed = await parser.parse(args, request) + parsed = await parser.async_parse(args, request) return json_response(parsed) async def echo_headers(request): - parsed = await parser.parse(hello_args, request, location="headers") + parsed = await parser.async_parse(hello_args, request, location="headers") return json_response(parsed) async def echo_cookie(request): - parsed = await parser.parse(hello_args, request, location="cookies") + parsed = await parser.async_parse(hello_args, request, location="cookies") return json_response(parsed) async def echo_nested(request): args = {"name": fields.Nested({"first": fields.Str(), "last": fields.Str()})} - parsed = await parser.parse(args, request) + parsed = await parser.async_parse(args, request) return json_response(parsed) async def echo_multiple_args(request): args = {"first": fields.Str(), "last": fields.Str()} - parsed = await parser.parse(args, request) + parsed = await parser.async_parse(args, request) return json_response(parsed) @@ -149,7 +149,7 @@ async def echo_nested_many(request): args = { "users": fields.Nested({"id": fields.Int(), "name": fields.Str()}, many=True) } - parsed = await parser.parse(args, request) + parsed = await parser.async_parse(args, request) return json_response(parsed) @@ -157,12 +157,12 @@ async def echo_nested_many_data_key(request): args = { "x_field": fields.Nested({"id": fields.Int()}, many=True, data_key="X-Field") } - parsed = await parser.parse(args, request) + parsed = await parser.async_parse(args, request) return json_response(parsed) async def echo_match_info(request): - parsed = await parser.parse( + parsed = await parser.async_parse( {"mymatch": fields.Int()}, request, location="match_info" ) return json_response(parsed) diff --git a/tests/test_aiohttpparser.py b/tests/test_aiohttpparser.py index dc0211d4c..6c7856856 100644 --- a/tests/test_aiohttpparser.py +++ b/tests/test_aiohttpparser.py @@ -97,7 +97,7 @@ def custom_handle_error(error, req, schema, *, error_status_code, error_headers) raise CustomError("foo") with pytest.raises(CustomError): - await parser.parse( + await parser.async_parse( {"foo": fields.Int(required=True)}, web_request, location="query" ) @@ -119,6 +119,6 @@ async def inner(): await inner() with pytest.raises(CustomError): - await parser.parse( + await parser.async_parse( {"foo": fields.Int(required=True)}, web_request, location="query" )