From 8010ac4a3bc9e60faf9dec177bce9d010d21d705 Mon Sep 17 00:00:00 2001 From: Noelle Leigh <5957867+noelleleigh@users.noreply.github.com> Date: Thu, 11 Jun 2026 08:53:38 -0400 Subject: [PATCH 1/9] Add REDIRECT_STATUS_CODES, remove unnecessary assignments --- django-stubs/test/client.pyi | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/django-stubs/test/client.pyi b/django-stubs/test/client.pyi index 320c26baf..11ec4e35d 100644 --- a/django-stubs/test/client.pyi +++ b/django-stubs/test/client.pyi @@ -1,4 +1,5 @@ from collections.abc import Mapping +from http import HTTPStatus from io import BytesIO from json import JSONEncoder from re import Pattern @@ -14,10 +15,11 @@ from django.http.request import HttpRequest from django.http.response import HttpResponse, HttpResponseBase from django.utils.functional import _StrOrPromise -BOUNDARY: str = ... -MULTIPART_CONTENT: str = ... -CONTENT_TYPE_RE: Pattern[str] = ... -JSON_CONTENT_TYPE_RE: Pattern[str] = ... +BOUNDARY: str +MULTIPART_CONTENT: str +CONTENT_TYPE_RE: Pattern[str] +JSON_CONTENT_TYPE_RE: Pattern[str] +REDIRECT_STATUS_CODES: frozenset[HTTPStatus] class RedirectCycleError(Exception): last_response: HttpResponseBase = ... From e0546950db912d8fff4cf65eafbcb0b1e442fbb4 Mon Sep 17 00:00:00 2001 From: Noelle Leigh <5957867+noelleleigh@users.noreply.github.com> Date: Thu, 11 Jun 2026 09:42:33 -0400 Subject: [PATCH 2/9] RedirectCycleError: Remove unnecessary assignments --- django-stubs/test/client.pyi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/django-stubs/test/client.pyi b/django-stubs/test/client.pyi index 11ec4e35d..1e2ac2a3b 100644 --- a/django-stubs/test/client.pyi +++ b/django-stubs/test/client.pyi @@ -22,8 +22,8 @@ JSON_CONTENT_TYPE_RE: Pattern[str] REDIRECT_STATUS_CODES: frozenset[HTTPStatus] class RedirectCycleError(Exception): - last_response: HttpResponseBase = ... - redirect_chain: list[tuple[str, int]] = ... + last_response: HttpResponseBase + redirect_chain: list[tuple[str, int]] def __init__(self, message: str, last_response: HttpResponseBase) -> None: ... class FakePayload: From c9cb125045a761adf12e1bf22453f2fa834e98d7 Mon Sep 17 00:00:00 2001 From: Noelle Leigh <5957867+noelleleigh@users.noreply.github.com> Date: Thu, 11 Jun 2026 09:43:31 -0400 Subject: [PATCH 3/9] FakePayload: Fix types and arguments --- django-stubs/test/client.pyi | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/django-stubs/test/client.pyi b/django-stubs/test/client.pyi index 1e2ac2a3b..f6c6b43e8 100644 --- a/django-stubs/test/client.pyi +++ b/django-stubs/test/client.pyi @@ -1,6 +1,6 @@ from collections.abc import Mapping from http import HTTPStatus -from io import BytesIO +from io import BytesIO, IOBase from json import JSONEncoder from re import Pattern from types import TracebackType @@ -26,12 +26,13 @@ class RedirectCycleError(Exception): redirect_chain: list[tuple[str, int]] def __init__(self, message: str, last_response: HttpResponseBase) -> None: ... -class FakePayload: - read_started: bool = ... +class FakePayload(IOBase): + read_started: bool def __init__(self, content: bytes | str | None = ...) -> None: ... def __len__(self) -> int: ... - def read(self, num_bytes: int = ...) -> bytes: ... - def write(self, content: bytes | str) -> None: ... + def read(self, size: int = ..., /) -> bytes: ... + def readline(self, size: int | None = ..., /) -> bytes: ... + def write(self, b: bytes | str, /) -> None: ... class ClientHandler(BaseHandler): enforce_csrf_checks: bool = ... From 03da44df9282214b7ed4ca45917930b3d1ca8c8d Mon Sep 17 00:00:00 2001 From: Noelle Leigh <5957867+noelleleigh@users.noreply.github.com> Date: Thu, 11 Jun 2026 09:45:02 -0400 Subject: [PATCH 4/9] Add closing_iterator_wrapper, aclosing_iterator_wrapper, move conditional_content_removal --- django-stubs/test/client.pyi | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/django-stubs/test/client.pyi b/django-stubs/test/client.pyi index f6c6b43e8..08ba47014 100644 --- a/django-stubs/test/client.pyi +++ b/django-stubs/test/client.pyi @@ -1,10 +1,10 @@ -from collections.abc import Mapping +from collections.abc import AsyncGenerator, Callable, Generator, Iterable, Mapping from http import HTTPStatus from io import BytesIO, IOBase from json import JSONEncoder from re import Pattern from types import TracebackType -from typing import Any, TypeAlias +from typing import Any, TypeAlias, TypeVar from django.contrib.auth.base_user import AbstractBaseUser from django.contrib.sessions.backends.base import SessionBase @@ -15,6 +15,8 @@ from django.http.request import HttpRequest from django.http.response import HttpResponse, HttpResponseBase from django.utils.functional import _StrOrPromise +_T = TypeVar("_T") + BOUNDARY: str MULTIPART_CONTENT: str CONTENT_TYPE_RE: Pattern[str] @@ -34,6 +36,13 @@ class FakePayload(IOBase): def readline(self, size: int | None = ..., /) -> bytes: ... def write(self, b: bytes | str, /) -> None: ... +def closing_iterator_wrapper(iterable: Iterable[_T], close: Callable) -> Generator[_T]: ... +async def aclosing_iterator_wrapper(iterable: Iterable[_T], close: Callable) -> AsyncGenerator[_T]: ... + +_Response = TypeVar("_Response", bound=HttpResponse) + +def conditional_content_removal(request: HttpRequest, response: _Response) -> _Response: ... + class ClientHandler(BaseHandler): enforce_csrf_checks: bool = ... def __init__(self, enforce_csrf_checks: bool = ..., *args: Any, **kwargs: Any) -> None: ... @@ -392,5 +401,3 @@ class AsyncClient(AsyncRequestFactory): def login(self, **credentials: Any) -> bool: ... def force_login(self, user: AbstractBaseUser, backend: str | None = ...) -> None: ... def logout(self) -> None: ... - -def conditional_content_removal(request: HttpRequest, response: HttpResponseBase) -> HttpResponse: ... From 1efe4a22dda1ba59b0846690db3b67e993da613d Mon Sep 17 00:00:00 2001 From: Noelle Leigh <5957867+noelleleigh@users.noreply.github.com> Date: Thu, 11 Jun 2026 09:45:29 -0400 Subject: [PATCH 5/9] RequestFactory: Fix arguments --- django-stubs/test/client.pyi | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/django-stubs/test/client.pyi b/django-stubs/test/client.pyi index 08ba47014..eb02171b7 100644 --- a/django-stubs/test/client.pyi +++ b/django-stubs/test/client.pyi @@ -77,8 +77,8 @@ class RequestFactory: data: _RequestData = ..., secure: bool = ..., *, - QUERY_STRING: str = ..., headers: Mapping[str, Any] | None = ..., + query_params: str | None = ..., **extra: object, ) -> WSGIRequest: ... def post( @@ -88,8 +88,8 @@ class RequestFactory: content_type: str = ..., secure: bool = ..., *, - QUERY_STRING: str = ..., headers: Mapping[str, Any] | None = ..., + query_params: str | None = ..., **extra: object, ) -> WSGIRequest: ... def head( @@ -98,8 +98,8 @@ class RequestFactory: data: _RequestData = ..., secure: bool = ..., *, - QUERY_STRING: str = ..., headers: Mapping[str, Any] | None = ..., + query_params: str | None = ..., **extra: object, ) -> WSGIRequest: ... def trace( @@ -107,8 +107,8 @@ class RequestFactory: path: _StrOrPromise, secure: bool = ..., *, - QUERY_STRING: str = ..., headers: Mapping[str, Any] | None = ..., + query_params: str | None = ..., **extra: object, ) -> WSGIRequest: ... def options( @@ -118,8 +118,8 @@ class RequestFactory: content_type: str = ..., secure: bool = ..., *, - QUERY_STRING: str = ..., headers: Mapping[str, Any] | None = ..., + query_params: str | None = ..., **extra: object, ) -> WSGIRequest: ... def put( @@ -129,8 +129,8 @@ class RequestFactory: content_type: str = ..., secure: bool = ..., *, - QUERY_STRING: str = ..., headers: Mapping[str, Any] | None = ..., + query_params: str | None = ..., **extra: object, ) -> WSGIRequest: ... def patch( @@ -140,8 +140,8 @@ class RequestFactory: content_type: str = ..., secure: bool = ..., *, - QUERY_STRING: str = ..., headers: Mapping[str, Any] | None = ..., + query_params: str | None = ..., **extra: object, ) -> WSGIRequest: ... def delete( @@ -151,8 +151,8 @@ class RequestFactory: content_type: str = ..., secure: bool = ..., *, - QUERY_STRING: str = ..., headers: Mapping[str, Any] | None = ..., + query_params: str | None = ..., **extra: object, ) -> WSGIRequest: ... def generic( @@ -163,8 +163,8 @@ class RequestFactory: content_type: str | None = ..., secure: bool = ..., *, - QUERY_STRING: str = ..., headers: Mapping[str, Any] | None = ..., + query_params: str | None = ..., **extra: object, ) -> WSGIRequest: ... From 312381fdbece60a215d7a3599d04cf6c7f51d3c9 Mon Sep 17 00:00:00 2001 From: Noelle Leigh <5957867+noelleleigh@users.noreply.github.com> Date: Thu, 11 Jun 2026 09:48:27 -0400 Subject: [PATCH 6/9] Add ClientMixin class --- django-stubs/test/client.pyi | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/django-stubs/test/client.pyi b/django-stubs/test/client.pyi index eb02171b7..04170d2b6 100644 --- a/django-stubs/test/client.pyi +++ b/django-stubs/test/client.pyi @@ -168,6 +168,19 @@ class RequestFactory: **extra: object, ) -> WSGIRequest: ... +class ClientMixin: + def store_exc_info(self, **kwargs: Any) -> None: ... + def check_exception(self, response: HttpResponse) -> None: ... + @property + def session(self) -> SessionBase: ... + async def asession(self) -> SessionBase: ... + def login(self, **credentials: Any) -> bool: ... + async def alogin(self, **credentials: Any) -> bool: ... + def force_login(self, user: AbstractBaseUser, backend: str | None = ...) -> None: ... + async def aforce_login(self, user: AbstractBaseUser, backend: str | None = ...) -> None: ... + def logout(self) -> None: ... + async def alogout(self) -> None: ... + class Client(RequestFactory): handler: ClientHandler raise_request_exception: bool From 79da188e11aad9698155b7925b21c53e538581d2 Mon Sep 17 00:00:00 2001 From: Noelle Leigh <5957867+noelleleigh@users.noreply.github.com> Date: Thu, 11 Jun 2026 09:48:48 -0400 Subject: [PATCH 7/9] Move AsyncRequestFactory to match source --- django-stubs/test/client.pyi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/django-stubs/test/client.pyi b/django-stubs/test/client.pyi index 04170d2b6..2fd81934b 100644 --- a/django-stubs/test/client.pyi +++ b/django-stubs/test/client.pyi @@ -168,6 +168,8 @@ class RequestFactory: **extra: object, ) -> WSGIRequest: ... +class AsyncRequestFactory(RequestFactory): ... + class ClientMixin: def store_exc_info(self, **kwargs: Any) -> None: ... def check_exception(self, response: HttpResponse) -> None: ... @@ -297,8 +299,6 @@ class Client(RequestFactory): def force_login(self, user: AbstractBaseUser, backend: str | None = ...) -> None: ... def logout(self) -> None: ... -class AsyncRequestFactory(RequestFactory): ... - class AsyncClient(AsyncRequestFactory): handler: AsyncClientHandler raise_request_exception: bool From ec1d694f026454d44387d349799d2828c348f55d Mon Sep 17 00:00:00 2001 From: Noelle Leigh <5957867+noelleleigh@users.noreply.github.com> Date: Thu, 11 Jun 2026 09:50:33 -0400 Subject: [PATCH 8/9] Client: Fix inheritance, args, types --- django-stubs/test/client.pyi | 40 +++++++++++++++--------------------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/django-stubs/test/client.pyi b/django-stubs/test/client.pyi index 2fd81934b..f6855fb4a 100644 --- a/django-stubs/test/client.pyi +++ b/django-stubs/test/client.pyi @@ -183,18 +183,18 @@ class ClientMixin: def logout(self) -> None: ... async def alogout(self) -> None: ... -class Client(RequestFactory): +class Client(ClientMixin, RequestFactory): handler: ClientHandler raise_request_exception: bool exc_info: tuple[type[BaseException], BaseException, TracebackType] | None - headers: dict[str, Any] + headers: dict[str, Any] | None def __init__( self, enforce_csrf_checks: bool = ..., raise_request_exception: bool = ..., *, - json_encoder: type[JSONEncoder] = ..., headers: Mapping[str, Any] | None = ..., + query_params: Mapping[str, Any] | None = ..., **defaults: Any, ) -> None: ... # Silence type warnings, since this class overrides arguments and return types in an unsafe manner. @@ -206,8 +206,8 @@ class Client(RequestFactory): follow: bool = ..., secure: bool = ..., *, - QUERY_STRING: str = ..., headers: Mapping[str, Any] | None = ..., + query_params: str | None = ..., **extra: str, ) -> HttpResponse: ... def post( # type: ignore [override] @@ -218,8 +218,8 @@ class Client(RequestFactory): follow: bool = ..., secure: bool = ..., *, - QUERY_STRING: str = ..., headers: Mapping[str, Any] | None = ..., + query_params: str | None = ..., **extra: str, ) -> HttpResponse: ... def head( # type: ignore [override] @@ -229,22 +229,23 @@ class Client(RequestFactory): follow: bool = ..., secure: bool = ..., *, - QUERY_STRING: str = ..., headers: Mapping[str, Any] | None = ..., + query_params: str | None = ..., **extra: str, ) -> HttpResponse: ... - def trace( # type: ignore [override] + def options( # type: ignore [override] self, path: _StrOrPromise, data: _RequestData = ..., + content_type: str = ..., follow: bool = ..., secure: bool = ..., *, - QUERY_STRING: str = ..., headers: Mapping[str, Any] | None = ..., + query_params: str | None = ..., **extra: str, ) -> HttpResponse: ... - def options( # type: ignore [override] + def put( # type: ignore [override] self, path: _StrOrPromise, data: _RequestData = ..., @@ -252,11 +253,11 @@ class Client(RequestFactory): follow: bool = ..., secure: bool = ..., *, - QUERY_STRING: str = ..., headers: Mapping[str, Any] | None = ..., + query_params: str | None = ..., **extra: str, ) -> HttpResponse: ... - def put( # type: ignore [override] + def patch( # type: ignore [override] self, path: _StrOrPromise, data: _RequestData = ..., @@ -264,11 +265,11 @@ class Client(RequestFactory): follow: bool = ..., secure: bool = ..., *, - QUERY_STRING: str = ..., headers: Mapping[str, Any] | None = ..., + query_params: str | None = ..., **extra: str, ) -> HttpResponse: ... - def patch( # type: ignore [override] + def delete( # type: ignore [override] self, path: _StrOrPromise, data: _RequestData = ..., @@ -276,28 +277,21 @@ class Client(RequestFactory): follow: bool = ..., secure: bool = ..., *, - QUERY_STRING: str = ..., headers: Mapping[str, Any] | None = ..., + query_params: str | None = ..., **extra: str, ) -> HttpResponse: ... - def delete( # type: ignore [override] + def trace( # type: ignore [override] self, path: _StrOrPromise, data: _RequestData = ..., - content_type: str = ..., follow: bool = ..., secure: bool = ..., *, - QUERY_STRING: str = ..., headers: Mapping[str, Any] | None = ..., + query_params: str | None = ..., **extra: str, ) -> HttpResponse: ... - def store_exc_info(self, **kwargs: Any) -> None: ... - @property - def session(self) -> SessionBase: ... - def login(self, **credentials: Any) -> bool: ... - def force_login(self, user: AbstractBaseUser, backend: str | None = ...) -> None: ... - def logout(self) -> None: ... class AsyncClient(AsyncRequestFactory): handler: AsyncClientHandler From 2d0e94a3c8377af66d49368258ab6266d8d91656 Mon Sep 17 00:00:00 2001 From: Noelle Leigh <5957867+noelleleigh@users.noreply.github.com> Date: Thu, 11 Jun 2026 09:52:04 -0400 Subject: [PATCH 9/9] AsyncClient: Fix inheritance, args, types --- django-stubs/test/client.pyi | 41 ++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 23 deletions(-) diff --git a/django-stubs/test/client.pyi b/django-stubs/test/client.pyi index f6855fb4a..07d52e72c 100644 --- a/django-stubs/test/client.pyi +++ b/django-stubs/test/client.pyi @@ -293,18 +293,19 @@ class Client(ClientMixin, RequestFactory): **extra: str, ) -> HttpResponse: ... -class AsyncClient(AsyncRequestFactory): +class AsyncClient(ClientMixin, AsyncRequestFactory): handler: AsyncClientHandler raise_request_exception: bool exc_info: tuple[type[BaseException], BaseException, TracebackType] | None - headers: dict[str, Any] + extra: dict[str, Any] | None = ... + headers: dict[str, Any] | None = ... def __init__( self, enforce_csrf_checks: bool = ..., raise_request_exception: bool = ..., *, - json_encoder: type[JSONEncoder] = ..., headers: Mapping[str, Any] | None = ..., + query_params: Mapping[str, Any] | None = ..., **defaults: Any, ) -> None: ... # Silence type warnings, since this class overrides arguments and return types in an unsafe manner. @@ -316,8 +317,8 @@ class AsyncClient(AsyncRequestFactory): follow: bool = ..., secure: bool = ..., *, - QUERY_STRING: str = ..., headers: Mapping[str, Any] | None = ..., + query_params: str | None = ..., **extra: str, ) -> HttpResponse: ... async def post( # type: ignore [override] @@ -328,8 +329,8 @@ class AsyncClient(AsyncRequestFactory): follow: bool = ..., secure: bool = ..., *, - QUERY_STRING: str = ..., headers: Mapping[str, Any] | None = ..., + query_params: str | None = ..., **extra: str, ) -> HttpResponse: ... async def head( # type: ignore [override] @@ -339,22 +340,23 @@ class AsyncClient(AsyncRequestFactory): follow: bool = ..., secure: bool = ..., *, - QUERY_STRING: str = ..., headers: Mapping[str, Any] | None = ..., + query_params: str | None = ..., **extra: str, ) -> HttpResponse: ... - async def trace( # type: ignore [override] + async def options( # type: ignore [override] self, path: _StrOrPromise, data: _RequestData = ..., + content_type: str = ..., follow: bool = ..., secure: bool = ..., *, - QUERY_STRING: str = ..., headers: Mapping[str, Any] | None = ..., + query_params: str | None = ..., **extra: str, ) -> HttpResponse: ... - async def options( # type: ignore [override] + async def put( # type: ignore [override] self, path: _StrOrPromise, data: _RequestData = ..., @@ -362,11 +364,11 @@ class AsyncClient(AsyncRequestFactory): follow: bool = ..., secure: bool = ..., *, - QUERY_STRING: str = ..., headers: Mapping[str, Any] | None = ..., + query_params: str | None = ..., **extra: str, ) -> HttpResponse: ... - async def put( # type: ignore [override] + async def patch( # type: ignore [override] self, path: _StrOrPromise, data: _RequestData = ..., @@ -374,11 +376,11 @@ class AsyncClient(AsyncRequestFactory): follow: bool = ..., secure: bool = ..., *, - QUERY_STRING: str = ..., headers: Mapping[str, Any] | None = ..., + query_params: str | None = ..., **extra: str, ) -> HttpResponse: ... - async def patch( # type: ignore [override] + async def delete( # type: ignore [override] self, path: _StrOrPromise, data: _RequestData = ..., @@ -386,25 +388,18 @@ class AsyncClient(AsyncRequestFactory): follow: bool = ..., secure: bool = ..., *, - QUERY_STRING: str = ..., headers: Mapping[str, Any] | None = ..., + query_params: str | None = ..., **extra: str, ) -> HttpResponse: ... - async def delete( # type: ignore [override] + async def trace( # type: ignore [override] self, path: _StrOrPromise, data: _RequestData = ..., - content_type: str = ..., follow: bool = ..., secure: bool = ..., *, - QUERY_STRING: str = ..., headers: Mapping[str, Any] | None = ..., + query_params: str | None = ..., **extra: str, ) -> HttpResponse: ... - def store_exc_info(self, **kwargs: Any) -> None: ... - @property - def session(self) -> SessionBase: ... - def login(self, **credentials: Any) -> bool: ... - def force_login(self, user: AbstractBaseUser, backend: str | None = ...) -> None: ... - def logout(self) -> None: ...