From 7727f6589e29ea7be8bef1791d03d674ab08efb1 Mon Sep 17 00:00:00 2001 From: Noelle Leigh <5957867+noelleleigh@users.noreply.github.com> Date: Tue, 31 Oct 2023 08:26:21 -0400 Subject: [PATCH 01/25] cookie: Refactor to include generic type --- django-stubs/http/cookie.pyi | 4 +++- django-stubs/http/response.pyi | 2 +- django-stubs/test/client.pyi | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/django-stubs/http/cookie.pyi b/django-stubs/http/cookie.pyi index efa3fb93a..2f9214ac0 100644 --- a/django-stubs/http/cookie.pyi +++ b/django-stubs/http/cookie.pyi @@ -1,3 +1,5 @@ -from http.cookies import SimpleCookie as SimpleCookie +from http import cookies + +SimpleCookie = cookies.SimpleCookie[str] def parse_cookie(cookie: str) -> dict[str, str]: ... diff --git a/django-stubs/http/response.pyi b/django-stubs/http/response.pyi index 97e3a9a81..b3c1b454b 100644 --- a/django-stubs/http/response.pyi +++ b/django-stubs/http/response.pyi @@ -14,7 +14,7 @@ class BadHeaderError(ValueError): ... class HttpResponseBase(Iterable[Any]): status_code: int = ... - cookies: SimpleCookie[str] = ... + cookies: SimpleCookie = ... reason_phrase: str = ... charset: str = ... closed: bool = ... diff --git a/django-stubs/test/client.pyi b/django-stubs/test/client.pyi index da5d5471b..5758239f1 100644 --- a/django-stubs/test/client.pyi +++ b/django-stubs/test/client.pyi @@ -44,7 +44,7 @@ _RequestData = Any | None class RequestFactory: json_encoder: type[JSONEncoder] defaults: dict[str, str] - cookies: SimpleCookie[str] + cookies: SimpleCookie errors: BytesIO def __init__( self, *, json_encoder: type[JSONEncoder] = ..., **defaults: Any From 76763e838db7d7fc2ef3ceb054e7f13b23afe8d7 Mon Sep 17 00:00:00 2001 From: Noelle Leigh <5957867+noelleleigh@users.noreply.github.com> Date: Tue, 31 Oct 2023 08:38:14 -0400 Subject: [PATCH 02/25] datastructures: CaseInsensitiveMapping keys are always str --- django-stubs/utils/datastructures.pyi | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/django-stubs/utils/datastructures.pyi b/django-stubs/utils/datastructures.pyi index 5d701ebdc..17dca2fe9 100644 --- a/django-stubs/utils/datastructures.pyi +++ b/django-stubs/utils/datastructures.pyi @@ -76,9 +76,9 @@ class DictWrapper(dict[str, _V]): self, data: Iterable[tuple[str, _V]], func: Callable[[_V], _V], prefix: str ) -> None: ... -class CaseInsensitiveMapping(Mapping[_K, _V]): +class CaseInsensitiveMapping(Mapping[str, _V]): def __init__(self, data: Any) -> None: ... - def __getitem__(self, key: _K) -> Any: ... + def __getitem__(self, key: str) -> Any: ... def __len__(self) -> int: ... - def __iter__(self) -> Iterator[_K]: ... + def __iter__(self) -> Iterator[str]: ... def copy(self) -> Self: ... From 19c81d28ce11b726a5d187a6dd3bf92690bf77ad Mon Sep 17 00:00:00 2001 From: Noelle Leigh <5957867+noelleleigh@users.noreply.github.com> Date: Tue, 31 Oct 2023 08:38:25 -0400 Subject: [PATCH 03/25] response: Add ResponseHeaders --- django-stubs/http/response.pyi | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/django-stubs/http/response.pyi b/django-stubs/http/response.pyi index b3c1b454b..9b450b698 100644 --- a/django-stubs/http/response.pyi +++ b/django-stubs/http/response.pyi @@ -9,6 +9,11 @@ from django.http.cookie import SimpleCookie from django.template import Context, Template from django.test.client import Client from django.urls import ResolverMatch +from django.utils.datastructures import CaseInsensitiveMapping + +class ResponseHeaders(CaseInsensitiveMapping[str]): + def pop(self, key: str, default: str | None = ...)-> str: ... + def setdefault(self, key: str, value: str): ... class BadHeaderError(ValueError): ... From 55689c4822e063933c156882ef499b442b0116ca Mon Sep 17 00:00:00 2001 From: Noelle Leigh <5957867+noelleleigh@users.noreply.github.com> Date: Tue, 31 Oct 2023 08:45:25 -0400 Subject: [PATCH 04/25] HttpResponseBase: Add more properties, make typing stricter --- django-stubs/http/response.pyi | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/django-stubs/http/response.pyi b/django-stubs/http/response.pyi index 9b450b698..a45583aed 100644 --- a/django-stubs/http/response.pyi +++ b/django-stubs/http/response.pyi @@ -17,12 +17,13 @@ class ResponseHeaders(CaseInsensitiveMapping[str]): class BadHeaderError(ValueError): ... -class HttpResponseBase(Iterable[Any]): - status_code: int = ... - cookies: SimpleCookie = ... - reason_phrase: str = ... - charset: str = ... - closed: bool = ... +class HttpResponseBase: + cookies: SimpleCookie + headers: ResponseHeaders + charset: str + status_code: int + reason_phrase: str + closed: bool def __init__( self, content_type: str | None = ..., From 6da03257517663a468ddbcbc75d0b1ee55e8ef9e Mon Sep 17 00:00:00 2001 From: Noelle Leigh <5957867+noelleleigh@users.noreply.github.com> Date: Tue, 31 Oct 2023 08:47:18 -0400 Subject: [PATCH 05/25] HttpResponse: Make bytes iterable --- django-stubs/http/response.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/django-stubs/http/response.pyi b/django-stubs/http/response.pyi index a45583aed..82228d401 100644 --- a/django-stubs/http/response.pyi +++ b/django-stubs/http/response.pyi @@ -71,7 +71,7 @@ class HttpResponseBase: def writelines(self, lines: Iterable[object]) -> Any: ... def __iter__(self) -> Iterator[Any]: ... -class HttpResponse(HttpResponseBase): +class HttpResponse(HttpResponseBase, Iterable[bytes]): content: Any csrf_cookie_set: bool redirect_chain: list[tuple[str, int]] From d7749ed0ffbdc4ab55e93038071615a23130a8f8 Mon Sep 17 00:00:00 2001 From: Noelle Leigh <5957867+noelleleigh@users.noreply.github.com> Date: Tue, 31 Oct 2023 08:47:49 -0400 Subject: [PATCH 06/25] HttpResponse: content is always bytes --- django-stubs/http/response.pyi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/django-stubs/http/response.pyi b/django-stubs/http/response.pyi index 82228d401..5be6c3d6f 100644 --- a/django-stubs/http/response.pyi +++ b/django-stubs/http/response.pyi @@ -72,7 +72,7 @@ class HttpResponseBase: def __iter__(self) -> Iterator[Any]: ... class HttpResponse(HttpResponseBase, Iterable[bytes]): - content: Any + content: bytes csrf_cookie_set: bool redirect_chain: list[tuple[str, int]] sameorigin: bool @@ -80,7 +80,7 @@ class HttpResponse(HttpResponseBase, Iterable[bytes]): test_was_secure_request: bool xframe_options_exempt: bool streaming: bool = ... - def __init__(self, content: object = ..., *args: Any, **kwargs: Any) -> None: ... + def __init__(self, content: bytes = ..., *args: Any, **kwargs: Any) -> None: ... def serialize(self) -> bytes: ... @property def url(self) -> str: ... From 0712142c54fa1f951085bdb0f5513fbf21897345 Mon Sep 17 00:00:00 2001 From: Noelle Leigh <5957867+noelleleigh@users.noreply.github.com> Date: Tue, 31 Oct 2023 08:48:19 -0400 Subject: [PATCH 07/25] HttpResponse: streaming is always bool --- django-stubs/http/response.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/django-stubs/http/response.pyi b/django-stubs/http/response.pyi index 5be6c3d6f..32dfbeb6c 100644 --- a/django-stubs/http/response.pyi +++ b/django-stubs/http/response.pyi @@ -79,7 +79,7 @@ class HttpResponse(HttpResponseBase, Iterable[bytes]): test_server_port: str test_was_secure_request: bool xframe_options_exempt: bool - streaming: bool = ... + streaming: bool def __init__(self, content: bytes = ..., *args: Any, **kwargs: Any) -> None: ... def serialize(self) -> bytes: ... @property From e863bb79f73f9e4e53abef7a5910115ce3969b2f Mon Sep 17 00:00:00 2001 From: Noelle Leigh <5957867+noelleleigh@users.noreply.github.com> Date: Tue, 31 Oct 2023 09:05:38 -0400 Subject: [PATCH 08/25] url property only exists on HttpResponseRedirectBase --- django-stubs/http/response.pyi | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/django-stubs/http/response.pyi b/django-stubs/http/response.pyi index 32dfbeb6c..3a3c8d4a6 100644 --- a/django-stubs/http/response.pyi +++ b/django-stubs/http/response.pyi @@ -83,7 +83,6 @@ class HttpResponse(HttpResponseBase, Iterable[bytes]): def __init__(self, content: bytes = ..., *args: Any, **kwargs: Any) -> None: ... def serialize(self) -> bytes: ... @property - def url(self) -> str: ... # Attributes assigned by monkey-patching in test client ClientHandler.__call__() wsgi_request: WSGIRequest # Attributes assigned by monkey-patching in test client Client.request() @@ -126,6 +125,8 @@ class FileResponse(StreamingHttpResponse): class HttpResponseRedirectBase(HttpResponse): allowed_schemes: list[str] = ... def __init__(self, redirect_to: str, *args: Any, **kwargs: Any) -> None: ... + @property + def url(self) -> str: ... class HttpResponseRedirect(HttpResponseRedirectBase): ... class HttpResponsePermanentRedirect(HttpResponseRedirectBase): ... From 4484a5db198ca0a22806391f6c49bccf041ebf56 Mon Sep 17 00:00:00 2001 From: Noelle Leigh <5957867+noelleleigh@users.noreply.github.com> Date: Tue, 31 Oct 2023 09:08:42 -0400 Subject: [PATCH 09/25] Use Never type for methods and properties that always raise an Exception https://docs.python.org/3/library/typing.html#typing.Never --- django-stubs/http/response.pyi | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/django-stubs/http/response.pyi b/django-stubs/http/response.pyi index 3a3c8d4a6..19b2b6e1e 100644 --- a/django-stubs/http/response.pyi +++ b/django-stubs/http/response.pyi @@ -10,6 +10,7 @@ from django.template import Context, Template from django.test.client import Client from django.urls import ResolverMatch from django.utils.datastructures import CaseInsensitiveMapping +from typing_extensions import Never class ResponseHeaders(CaseInsensitiveMapping[str]): def pop(self, key: str, default: str | None = ...)-> str: ... @@ -62,13 +63,13 @@ class HttpResponseBase: ) -> None: ... def make_bytes(self, value: object) -> bytes: ... def close(self) -> None: ... - def write(self, content: str | bytes) -> None: ... + def write(self, content: str | bytes) -> Never: ... def flush(self) -> None: ... - def tell(self) -> int: ... + def tell(self) -> Never: ... def readable(self) -> bool: ... def seekable(self) -> bool: ... def writable(self) -> bool: ... - def writelines(self, lines: Iterable[object]) -> Any: ... + def writelines(self, lines: Iterable[str | bytes]) -> Never: ... def __iter__(self) -> Iterator[Any]: ... class HttpResponse(HttpResponseBase, Iterable[bytes]): @@ -95,7 +96,6 @@ class HttpResponse(HttpResponseBase, Iterable[bytes]): def getvalue(self) -> bytes: ... class StreamingHttpResponse(HttpResponseBase): - content: Any streaming_content: Iterable[bytes] | AsyncIterable[bytes] def __init__( self, @@ -104,6 +104,8 @@ class StreamingHttpResponse(HttpResponseBase): **kwargs: Any ) -> None: ... def getvalue(self) -> bytes: ... + @property + def content(self) -> Never: ... class FileResponse(StreamingHttpResponse): client: Client From 27b90a8094cfffeec7c79e0490d0f001c63c9957 Mon Sep 17 00:00:00 2001 From: Noelle Leigh <5957867+noelleleigh@users.noreply.github.com> Date: Tue, 31 Oct 2023 09:09:26 -0400 Subject: [PATCH 10/25] ResponseHeaders: code formatting --- django-stubs/http/response.pyi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/django-stubs/http/response.pyi b/django-stubs/http/response.pyi index 19b2b6e1e..a8f36cc43 100644 --- a/django-stubs/http/response.pyi +++ b/django-stubs/http/response.pyi @@ -13,8 +13,8 @@ from django.utils.datastructures import CaseInsensitiveMapping from typing_extensions import Never class ResponseHeaders(CaseInsensitiveMapping[str]): - def pop(self, key: str, default: str | None = ...)-> str: ... - def setdefault(self, key: str, value: str): ... + def pop(self, key: str, default: str | None = ...) -> str: ... + def setdefault(self, key: str, value: str) -> None: ... class BadHeaderError(ValueError): ... From ce2b0076ee4a8ea1fd0f4b806a159b60bc00d5b9 Mon Sep 17 00:00:00 2001 From: Noelle Leigh <5957867+noelleleigh@users.noreply.github.com> Date: Tue, 31 Oct 2023 09:11:11 -0400 Subject: [PATCH 11/25] HttpResponseBase: Change attributes into properties --- django-stubs/http/response.pyi | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/django-stubs/http/response.pyi b/django-stubs/http/response.pyi index a8f36cc43..59a957678 100644 --- a/django-stubs/http/response.pyi +++ b/django-stubs/http/response.pyi @@ -21,9 +21,7 @@ class BadHeaderError(ValueError): ... class HttpResponseBase: cookies: SimpleCookie headers: ResponseHeaders - charset: str status_code: int - reason_phrase: str closed: bool def __init__( self, @@ -32,6 +30,10 @@ class HttpResponseBase: reason: str | None = ..., charset: str | None = ..., ) -> None: ... + @property + def reason_phrase(self) -> str: ... + @property + def charset(self) -> str: ... def serialize_headers(self) -> bytes: ... def __setitem__(self, header: str | bytes, value: str | bytes | int) -> None: ... def __delitem__(self, header: str | bytes) -> None: ... From ad504998d18627685d326c4547bba1fa0e1fbbe9 Mon Sep 17 00:00:00 2001 From: Noelle Leigh <5957867+noelleleigh@users.noreply.github.com> Date: Tue, 31 Oct 2023 09:11:53 -0400 Subject: [PATCH 12/25] HttpResponse: Convert attributes into properties --- django-stubs/http/response.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/django-stubs/http/response.pyi b/django-stubs/http/response.pyi index 59a957678..bd22cc364 100644 --- a/django-stubs/http/response.pyi +++ b/django-stubs/http/response.pyi @@ -75,7 +75,6 @@ class HttpResponseBase: def __iter__(self) -> Iterator[Any]: ... class HttpResponse(HttpResponseBase, Iterable[bytes]): - content: bytes csrf_cookie_set: bool redirect_chain: list[tuple[str, int]] sameorigin: bool @@ -86,6 +85,7 @@ class HttpResponse(HttpResponseBase, Iterable[bytes]): def __init__(self, content: bytes = ..., *args: Any, **kwargs: Any) -> None: ... def serialize(self) -> bytes: ... @property + def content(self) -> bytes: ... # Attributes assigned by monkey-patching in test client ClientHandler.__call__() wsgi_request: WSGIRequest # Attributes assigned by monkey-patching in test client Client.request() From 3a4b3a5c4f3379622c5039e39fec000e0a8b091f Mon Sep 17 00:00:00 2001 From: Noelle Leigh <5957867+noelleleigh@users.noreply.github.com> Date: Tue, 31 Oct 2023 09:12:19 -0400 Subject: [PATCH 13/25] HttpResponse: Add overridden methods --- django-stubs/http/response.pyi | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/django-stubs/http/response.pyi b/django-stubs/http/response.pyi index bd22cc364..b982c3ad4 100644 --- a/django-stubs/http/response.pyi +++ b/django-stubs/http/response.pyi @@ -86,6 +86,12 @@ class HttpResponse(HttpResponseBase, Iterable[bytes]): def serialize(self) -> bytes: ... @property def content(self) -> bytes: ... + def write(self, content: str | bytes) -> None: ... + def tell(self) -> int: ... + def getvalue(self) -> bytes: ... + def writable(self) -> bool: ... + def writelines(self, lines: Iterable[str | bytes]) -> None: ... + # Attributes assigned by monkey-patching in test client ClientHandler.__call__() wsgi_request: WSGIRequest # Attributes assigned by monkey-patching in test client Client.request() @@ -95,7 +101,6 @@ class HttpResponse(HttpResponseBase, Iterable[bytes]): context: Context resolver_match: ResolverMatch def json(self) -> Any: ... - def getvalue(self) -> bytes: ... class StreamingHttpResponse(HttpResponseBase): streaming_content: Iterable[bytes] | AsyncIterable[bytes] From 35bf13256b73187a7b058024ebdc39d38fd4b757 Mon Sep 17 00:00:00 2001 From: Noelle Leigh <5957867+noelleleigh@users.noreply.github.com> Date: Tue, 31 Oct 2023 09:12:37 -0400 Subject: [PATCH 14/25] StreamingHttpResponse: Add streaming --- django-stubs/http/response.pyi | 1 + 1 file changed, 1 insertion(+) diff --git a/django-stubs/http/response.pyi b/django-stubs/http/response.pyi index b982c3ad4..d33b02408 100644 --- a/django-stubs/http/response.pyi +++ b/django-stubs/http/response.pyi @@ -103,6 +103,7 @@ class HttpResponse(HttpResponseBase, Iterable[bytes]): def json(self) -> Any: ... class StreamingHttpResponse(HttpResponseBase): + streaming: bool streaming_content: Iterable[bytes] | AsyncIterable[bytes] def __init__( self, From fc150794acc5e49977f02517e9ee3a84944d0005 Mon Sep 17 00:00:00 2001 From: Noelle Leigh <5957867+noelleleigh@users.noreply.github.com> Date: Tue, 31 Oct 2023 09:13:01 -0400 Subject: [PATCH 15/25] StreamingHttpResponse: Add Iterable and AsyncIterable parents --- django-stubs/http/response.pyi | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/django-stubs/http/response.pyi b/django-stubs/http/response.pyi index d33b02408..58b2cb908 100644 --- a/django-stubs/http/response.pyi +++ b/django-stubs/http/response.pyi @@ -1,5 +1,5 @@ import datetime -from collections.abc import AsyncIterable, Iterable, Iterator +from collections.abc import AsyncIterable, AsyncIterator, Iterable, Iterator from io import BytesIO from json import JSONEncoder from typing import Any, overload @@ -102,7 +102,7 @@ class HttpResponse(HttpResponseBase, Iterable[bytes]): resolver_match: ResolverMatch def json(self) -> Any: ... -class StreamingHttpResponse(HttpResponseBase): +class StreamingHttpResponse(HttpResponseBase, Iterable[bytes], AsyncIterable[bytes]): streaming: bool streaming_content: Iterable[bytes] | AsyncIterable[bytes] def __init__( @@ -114,6 +114,8 @@ class StreamingHttpResponse(HttpResponseBase): def getvalue(self) -> bytes: ... @property def content(self) -> Never: ... + def __iter__(self) -> Iterator[bytes]: ... + def __aiter__(self) -> AsyncIterator[bytes]: ... class FileResponse(StreamingHttpResponse): client: Client From 05cf49e69c065d6cca8125eaa162a4b6a60f919d Mon Sep 17 00:00:00 2001 From: Noelle Leigh <5957867+noelleleigh@users.noreply.github.com> Date: Tue, 31 Oct 2023 09:18:09 -0400 Subject: [PATCH 16/25] FileResponse: Remove monkey-patched and non-public attributes --- django-stubs/http/response.pyi | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/django-stubs/http/response.pyi b/django-stubs/http/response.pyi index 58b2cb908..0ca9d2a59 100644 --- a/django-stubs/http/response.pyi +++ b/django-stubs/http/response.pyi @@ -118,21 +118,13 @@ class StreamingHttpResponse(HttpResponseBase, Iterable[bytes], AsyncIterable[byt def __aiter__(self) -> AsyncIterator[bytes]: ... class FileResponse(StreamingHttpResponse): - client: Client - context: None - file_to_stream: BytesIO | None - request: dict[str, str] - resolver_match: ResolverMatch - templates: list[Any] - wsgi_request: WSGIRequest - block_size: int = ... - as_attachment: bool = ... - filename: str = ... + block_size: int + as_attachment: bool + filename: str def __init__( self, *args: Any, as_attachment: bool = ..., filename: str = ..., **kwargs: Any ) -> None: ... def set_headers(self, filelike: BytesIO) -> None: ... - def json(self) -> dict[str, Any]: ... class HttpResponseRedirectBase(HttpResponse): allowed_schemes: list[str] = ... From 3716b5dafa338a60e2231ed55d46e85d862799e4 Mon Sep 17 00:00:00 2001 From: Noelle Leigh <5957867+noelleleigh@users.noreply.github.com> Date: Tue, 31 Oct 2023 09:36:24 -0400 Subject: [PATCH 17/25] UnreadablePostError is an OSError --- django-stubs/http/request.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/django-stubs/http/request.pyi b/django-stubs/http/request.pyi index 1b0fdd3f8..65ca0247f 100644 --- a/django-stubs/http/request.pyi +++ b/django-stubs/http/request.pyi @@ -19,7 +19,7 @@ from django.utils.datastructures import ( RAISE_ERROR: object = ... host_validation_re: Pattern[str] = ... -class UnreadablePostError(IOError): ... +class UnreadablePostError(OSError): ... class RawPostDataException(Exception): ... UploadHandlerList = ( From e9a6a79e5acde1edef1c4d76a5b94f55834c384e Mon Sep 17 00:00:00 2001 From: Noelle Leigh <5957867+noelleleigh@users.noreply.github.com> Date: Tue, 31 Oct 2023 09:37:03 -0400 Subject: [PATCH 18/25] HttpHeaders: Use single generic type Change was made to CaseInsensitiveMapping in previous commit. --- django-stubs/http/request.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/django-stubs/http/request.pyi b/django-stubs/http/request.pyi index 65ca0247f..b7fb7bd1a 100644 --- a/django-stubs/http/request.pyi +++ b/django-stubs/http/request.pyi @@ -27,7 +27,7 @@ UploadHandlerList = ( | ImmutableList[uploadhandler.FileUploadHandler] ) -class HttpHeaders(CaseInsensitiveMapping[str, str]): +class HttpHeaders(CaseInsensitiveMapping[str]): HTTP_PREFIX: str = ... UNPREFIXED_HEADERS: set[str] = ... def __init__(self, environ: Mapping[str, Any]) -> None: ... From b4536e82699be9a1e8b344a86a300bd2563c4862 Mon Sep 17 00:00:00 2001 From: Noelle Leigh <5957867+noelleleigh@users.noreply.github.com> Date: Tue, 31 Oct 2023 09:37:31 -0400 Subject: [PATCH 19/25] HttpHeaders: Add methods --- django-stubs/http/request.pyi | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/django-stubs/http/request.pyi b/django-stubs/http/request.pyi index b7fb7bd1a..35a53bac8 100644 --- a/django-stubs/http/request.pyi +++ b/django-stubs/http/request.pyi @@ -1,7 +1,7 @@ from collections.abc import Iterable, Mapping from io import BytesIO from re import Pattern -from typing import Any, BinaryIO, overload +from typing import Any, BinaryIO, TypeVar, overload from typing_extensions import Self from django.contrib.auth.base_user import AbstractBaseUser @@ -27,12 +27,22 @@ UploadHandlerList = ( | ImmutableList[uploadhandler.FileUploadHandler] ) +T = TypeVar("T") + class HttpHeaders(CaseInsensitiveMapping[str]): HTTP_PREFIX: str = ... UNPREFIXED_HEADERS: set[str] = ... def __init__(self, environ: Mapping[str, Any]) -> None: ... @classmethod def parse_header_name(cls, header: str) -> str | None: ... + @classmethod + def to_wsgi_name(cls, header: str) -> str: ... + @classmethod + def to_asgi_name(cls, header: str) -> str: ... + @classmethod + def to_wsgi_names(cls, headers: Mapping[str, T]) -> Mapping[str, T]: ... + @classmethod + def to_asgi_names(cls, headers: Mapping[str, T]) -> Mapping[str, T]: ... class HttpRequest(BytesIO): GET: QueryDict = ... From def0a3012793e29f8c3a7796eb636c84ca7ec66f Mon Sep 17 00:00:00 2001 From: Noelle Leigh <5957867+noelleleigh@users.noreply.github.com> Date: Tue, 31 Oct 2023 09:38:12 -0400 Subject: [PATCH 20/25] HttpRequest: Move middleware attributes to bottom --- django-stubs/http/request.pyi | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/django-stubs/http/request.pyi b/django-stubs/http/request.pyi index 35a53bac8..73ceff105 100644 --- a/django-stubs/http/request.pyi +++ b/django-stubs/http/request.pyi @@ -56,9 +56,6 @@ class HttpRequest(BytesIO): resolver_match: ResolverMatch = ... content_type: str | None = ... content_params: dict[str, str] | None = ... - user: AbstractBaseUser | AnonymousUser - site: Site - session: SessionBase encoding: str | None = ... upload_handlers: UploadHandlerList = ... def __init__(self) -> None: ... @@ -88,6 +85,11 @@ class HttpRequest(BytesIO): def body(self) -> bytes: ... def _load_post_and_files(self) -> None: ... + # Attributes added by commonly-used middleware + user: AbstractBaseUser | AnonymousUser + site: Site + session: SessionBase + class QueryDict(MultiValueDict[str, str]): encoding: str = ... _mutable: bool = ... From ec1aeb6e51092ccf3a551ce52de07bd2f9a29f87 Mon Sep 17 00:00:00 2001 From: Noelle Leigh <5957867+noelleleigh@users.noreply.github.com> Date: Tue, 31 Oct 2023 09:44:32 -0400 Subject: [PATCH 21/25] HttpRequest isn't a BytesIO subclass --- django-stubs/http/request.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/django-stubs/http/request.pyi b/django-stubs/http/request.pyi index 73ceff105..d1c316a00 100644 --- a/django-stubs/http/request.pyi +++ b/django-stubs/http/request.pyi @@ -44,7 +44,7 @@ class HttpHeaders(CaseInsensitiveMapping[str]): @classmethod def to_asgi_names(cls, headers: Mapping[str, T]) -> Mapping[str, T]: ... -class HttpRequest(BytesIO): +class HttpRequest: GET: QueryDict = ... POST: QueryDict = ... COOKIES: dict[str, str] = ... From a1e99b9abc4b7b7464848ab94203da6b80ca2ea0 Mon Sep 17 00:00:00 2001 From: Noelle Leigh <5957867+noelleleigh@users.noreply.github.com> Date: Tue, 31 Oct 2023 09:48:26 -0400 Subject: [PATCH 22/25] HttpRequest: Flesh out types --- django-stubs/http/request.pyi | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/django-stubs/http/request.pyi b/django-stubs/http/request.pyi index d1c316a00..e6cefb69f 100644 --- a/django-stubs/http/request.pyi +++ b/django-stubs/http/request.pyi @@ -44,6 +44,8 @@ class HttpHeaders(CaseInsensitiveMapping[str]): @classmethod def to_asgi_names(cls, headers: Mapping[str, T]) -> Mapping[str, T]: ... +class MediaType: ... + class HttpRequest: GET: QueryDict = ... POST: QueryDict = ... @@ -53,12 +55,13 @@ class HttpRequest: path: str = ... path_info: str = ... method: str | None = ... - resolver_match: ResolverMatch = ... + resolver_match: ResolverMatch | None = ... content_type: str | None = ... content_params: dict[str, str] | None = ... - encoding: str | None = ... - upload_handlers: UploadHandlerList = ... def __init__(self) -> None: ... + @property + def accepted_types(self) -> list[MediaType]: ... + def accepts(self, media_type: str) -> bool: ... def get_host(self) -> str: ... def get_port(self) -> str: ... def get_full_path(self, force_append_slash: bool = ...) -> str: ... @@ -66,15 +69,19 @@ class HttpRequest: def get_signed_cookie( self, key: str, - default: Any = ..., + default: T = ..., salt: str = ..., max_age: int | None = ..., - ) -> str | None: ... + ) -> str | T: ... def get_raw_uri(self) -> str: ... def build_absolute_uri(self, location: str | None = ...) -> str: ... @property - def scheme(self) -> str | None: ... + def scheme(self) -> str: ... def is_secure(self) -> bool: ... + @property + def encoding(self) -> str: ... + @property + def upload_handlers(self) -> UploadHandlerList: ... def is_ajax(self) -> bool: ... def parse_file_upload( self, META: Mapping[str, Any], post_data: BinaryIO @@ -84,6 +91,11 @@ class HttpRequest: @property def body(self) -> bytes: ... def _load_post_and_files(self) -> None: ... + def close(self) -> None: ... + def read(self, *args, **kwargs) -> bytes: ... + def readline(self, *args, **kwargs) -> bytes: ... + def __iter__(self) -> Iterable[bytes]: ... + def readlines(self) -> list[bytes]: ... # Attributes added by commonly-used middleware user: AbstractBaseUser | AnonymousUser From 4cf4bf16054089fc85be69f07270f86b37acfd62 Mon Sep 17 00:00:00 2001 From: Noelle Leigh <5957867+noelleleigh@users.noreply.github.com> Date: Tue, 31 Oct 2023 09:52:45 -0400 Subject: [PATCH 23/25] HttpResponseBase isn't iterable --- django-stubs/http/response.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/django-stubs/http/response.pyi b/django-stubs/http/response.pyi index 0ca9d2a59..db6c5f830 100644 --- a/django-stubs/http/response.pyi +++ b/django-stubs/http/response.pyi @@ -72,7 +72,6 @@ class HttpResponseBase: def seekable(self) -> bool: ... def writable(self) -> bool: ... def writelines(self, lines: Iterable[str | bytes]) -> Never: ... - def __iter__(self) -> Iterator[Any]: ... class HttpResponse(HttpResponseBase, Iterable[bytes]): csrf_cookie_set: bool @@ -86,6 +85,7 @@ class HttpResponse(HttpResponseBase, Iterable[bytes]): def serialize(self) -> bytes: ... @property def content(self) -> bytes: ... + def __iter__(self) -> Iterator[bytes]: ... def write(self, content: str | bytes) -> None: ... def tell(self) -> int: ... def getvalue(self) -> bytes: ... From 7aa66893c7820bee2f7da719bf17d98cbad802fc Mon Sep 17 00:00:00 2001 From: Noelle Leigh <5957867+noelleleigh@users.noreply.github.com> Date: Tue, 31 Oct 2023 12:47:58 -0400 Subject: [PATCH 24/25] ./s/lint results --- django-stubs/db/models/functions/comparison.pyi | 2 ++ django-stubs/db/models/manager.pyi | 4 +++- django-stubs/http/request.pyi | 5 ++--- django-stubs/http/response.pyi | 2 +- django-stubs/test/testcases.pyi | 2 +- 5 files changed, 9 insertions(+), 6 deletions(-) diff --git a/django-stubs/db/models/functions/comparison.pyi b/django-stubs/db/models/functions/comparison.pyi index 58247e38f..68f287b49 100644 --- a/django-stubs/db/models/functions/comparison.pyi +++ b/django-stubs/db/models/functions/comparison.pyi @@ -9,10 +9,12 @@ class Cast(Func): ) -> None: ... class Coalesce(Func): ... + class Collate(Func): def __init__(self, expression: Any, collation: str) -> None: ... class Greatest(Func): ... + class JSONObject(Func): def __init__(self, **fields: Any) -> None: ... diff --git a/django-stubs/db/models/manager.pyi b/django-stubs/db/models/manager.pyi index 3ed250aaa..7dc395065 100644 --- a/django-stubs/db/models/manager.pyi +++ b/django-stubs/db/models/manager.pyi @@ -19,7 +19,9 @@ class BaseManager(QuerySet[_T]): def __init__(self) -> None: ... def deconstruct( self, - ) -> tuple[bool, str | None, str | None, tuple[Any, ...] | None, dict[str, Any] | None]: ... + ) -> tuple[ + bool, str | None, str | None, tuple[Any, ...] | None, dict[str, Any] | None + ]: ... def check(self, **kwargs: Any) -> list[Any]: ... @classmethod def from_queryset( diff --git a/django-stubs/http/request.pyi b/django-stubs/http/request.pyi index e6cefb69f..19211e86e 100644 --- a/django-stubs/http/request.pyi +++ b/django-stubs/http/request.pyi @@ -1,5 +1,4 @@ from collections.abc import Iterable, Mapping -from io import BytesIO from re import Pattern from typing import Any, BinaryIO, TypeVar, overload from typing_extensions import Self @@ -92,8 +91,8 @@ class HttpRequest: def body(self) -> bytes: ... def _load_post_and_files(self) -> None: ... def close(self) -> None: ... - def read(self, *args, **kwargs) -> bytes: ... - def readline(self, *args, **kwargs) -> bytes: ... + def read(self, *args: Any, **kwargs: Any) -> bytes: ... + def readline(self, *args: Any, **kwargs: Any) -> bytes: ... def __iter__(self) -> Iterable[bytes]: ... def readlines(self) -> list[bytes]: ... diff --git a/django-stubs/http/response.pyi b/django-stubs/http/response.pyi index db6c5f830..4d23ab4e1 100644 --- a/django-stubs/http/response.pyi +++ b/django-stubs/http/response.pyi @@ -3,6 +3,7 @@ from collections.abc import AsyncIterable, AsyncIterator, Iterable, Iterator from io import BytesIO from json import JSONEncoder from typing import Any, overload +from typing_extensions import Never from django.core.handlers.wsgi import WSGIRequest from django.http.cookie import SimpleCookie @@ -10,7 +11,6 @@ from django.template import Context, Template from django.test.client import Client from django.urls import ResolverMatch from django.utils.datastructures import CaseInsensitiveMapping -from typing_extensions import Never class ResponseHeaders(CaseInsensitiveMapping[str]): def pop(self, key: str, default: str | None = ...) -> str: ... diff --git a/django-stubs/test/testcases.pyi b/django-stubs/test/testcases.pyi index 583cb1401..89e2b7463 100644 --- a/django-stubs/test/testcases.pyi +++ b/django-stubs/test/testcases.pyi @@ -9,7 +9,7 @@ from django.core.servers.basehttp import ThreadedWSGIServer, WSGIRequestHandler from django.db import connections as connections # noqa: F401 from django.db.backends.sqlite3.base import DatabaseWrapper from django.db.models import Model -from django.db.models.query import _BaseQuerySet, ValuesQuerySet +from django.db.models.query import ValuesQuerySet, _BaseQuerySet from django.forms.fields import EmailField from django.http.response import HttpResponse, HttpResponseBase from django.template.base import Template From 8500198fc6dafbc1da397984af3d1e3eb8624d79 Mon Sep 17 00:00:00 2001 From: Noelle Leigh <5957867+noelleleigh@users.noreply.github.com> Date: Tue, 31 Oct 2023 12:59:55 -0400 Subject: [PATCH 25/25] HttpResponseBase: Remove Never methods mypy doesn't like how the return types changed in HttpResponse. --- django-stubs/http/response.pyi | 3 --- 1 file changed, 3 deletions(-) diff --git a/django-stubs/http/response.pyi b/django-stubs/http/response.pyi index 4d23ab4e1..878e6529d 100644 --- a/django-stubs/http/response.pyi +++ b/django-stubs/http/response.pyi @@ -65,13 +65,10 @@ class HttpResponseBase: ) -> None: ... def make_bytes(self, value: object) -> bytes: ... def close(self) -> None: ... - def write(self, content: str | bytes) -> Never: ... def flush(self) -> None: ... - def tell(self) -> Never: ... def readable(self) -> bool: ... def seekable(self) -> bool: ... def writable(self) -> bool: ... - def writelines(self, lines: Iterable[str | bytes]) -> Never: ... class HttpResponse(HttpResponseBase, Iterable[bytes]): csrf_cookie_set: bool