From 714520ace3769036509f6214cce70d397dcb95ae Mon Sep 17 00:00:00 2001 From: Ashidul Islam Date: Sat, 11 Jul 2026 13:45:01 +0530 Subject: [PATCH 1/4] [gunicorn] Use TypedDict for proxy_protocol_info --- stubs/gunicorn/gunicorn/_types.pyi | 20 +++++++++++++++++++- stubs/gunicorn/gunicorn/asgi/parser.pyi | 22 ++++------------------ stubs/gunicorn/gunicorn/asgi/uwsgi.pyi | 4 ++-- stubs/gunicorn/gunicorn/http/message.pyi | 4 ++-- stubs/gunicorn/gunicorn/http2/request.pyi | 4 ++-- stubs/gunicorn/gunicorn/uwsgi/message.pyi | 4 ++-- 6 files changed, 31 insertions(+), 27 deletions(-) diff --git a/stubs/gunicorn/gunicorn/_types.pyi b/stubs/gunicorn/gunicorn/_types.pyi index 815a1179f98c..17abcf35f072 100644 --- a/stubs/gunicorn/gunicorn/_types.pyi +++ b/stubs/gunicorn/gunicorn/_types.pyi @@ -1,7 +1,7 @@ ### This .pyi file is a helper for centralized storage types that are reused across different runtime modules. ### from _typeshed import FileDescriptor from collections.abc import Awaitable, Callable, Iterable, MutableMapping -from typing import Any, TypeAlias +from typing import Any, Literal, TypeAlias, TypedDict, type_check_only from typing_extensions import LiteralString _StatusType: TypeAlias = str @@ -21,3 +21,21 @@ _ASGIAppType: TypeAlias = Callable[[_ScopeType, _ReceiveType, _SendType], Awaita _UnixSocketPathType: TypeAlias = str _TcpAddressType: TypeAlias = tuple[LiteralString, int] # noqa: Y047 _AddressType: TypeAlias = _UnixSocketPathType | FileDescriptor | _TcpAddressType # noqa: Y047 + +@type_check_only +class _ProxyProtocolInfo(TypedDict): + proxy_protocol: Literal["TCP4", "TCP6", "UDP4", "UDP6"] + client_addr: str + client_port: int + proxy_addr: str + proxy_port: int + +@type_check_only +class _ProxyProtocolInfoUnknown(TypedDict): + proxy_protocol: Literal["UNKNOWN", "LOCAL", "UNSPEC"] + client_addr: None + client_port: None + proxy_addr: None + proxy_port: None + +_ProxyProtocolInfoDict: TypeAlias = _ProxyProtocolInfo | _ProxyProtocolInfoUnknown diff --git a/stubs/gunicorn/gunicorn/asgi/parser.pyi b/stubs/gunicorn/gunicorn/asgi/parser.pyi index 69606932ee00..7b0a995ffe76 100644 --- a/stubs/gunicorn/gunicorn/asgi/parser.pyi +++ b/stubs/gunicorn/gunicorn/asgi/parser.pyi @@ -1,6 +1,6 @@ from collections.abc import Callable, Iterable from enum import IntEnum -from typing import Any, Final, Literal, SupportsIndex, TypeAlias, TypedDict, type_check_only +from typing import Any, Final, Literal, SupportsIndex, TypeAlias from typing_extensions import Self _H1CProtocol: TypeAlias = Any # gunicorn_h1c H1CProtocol class @@ -38,21 +38,7 @@ class UnsupportedTransferCoding(ParseError): ... class InvalidChunkSize(ParseError): ... class InvalidChunkExtension(ParseError): ... -@type_check_only -class _ProxyProtocolInfo(TypedDict): - proxy_protocol: Literal["TCP4", "TCP6", "UDP4", "UDP6"] - client_addr: str - client_port: int - proxy_addr: str - proxy_port: int - -@type_check_only -class _ProxyProtocolInfoUnknown(TypedDict): - proxy_protocol: Literal["UNKNOWN", "LOCAL", "UNSPEC"] - client_addr: None - client_port: None - proxy_addr: None - proxy_port: None +from .._types import _ProxyProtocolInfoDict class PythonProtocol: __slots__ = ( @@ -114,7 +100,7 @@ class PythonProtocol: ) -> None: ... def feed(self, data: Iterable[SupportsIndex]) -> None: ... @property - def proxy_protocol_info(self) -> _ProxyProtocolInfo | _ProxyProtocolInfoUnknown | None: ... + def proxy_protocol_info(self) -> _ProxyProtocolInfoDict | None: ... def reset(self) -> None: ... def finish(self) -> None: ... @@ -149,7 +135,7 @@ class CallbackRequest: content_length: int chunked: bool must_close: bool - proxy_protocol_info: dict[str, str | int | None] | None # TODO: Use TypedDict + proxy_protocol_info: _ProxyProtocolInfoDict | None def __init__(self) -> None: ... @classmethod diff --git a/stubs/gunicorn/gunicorn/asgi/uwsgi.pyi b/stubs/gunicorn/gunicorn/asgi/uwsgi.pyi index 651dcdd416d1..389b407a0d26 100644 --- a/stubs/gunicorn/gunicorn/asgi/uwsgi.pyi +++ b/stubs/gunicorn/gunicorn/asgi/uwsgi.pyi @@ -5,7 +5,7 @@ from gunicorn.asgi.unreader import AsyncUnreader from gunicorn.config import Config from gunicorn.uwsgi.message import UWSGIRequest -from .._types import _AddressType +from .._types import _AddressType, _ProxyProtocolInfoDict class AsyncUWSGIRequest(UWSGIRequest): cfg: Config @@ -26,7 +26,7 @@ class AsyncUWSGIRequest(UWSGIRequest): uwsgi_vars: dict[str, str] modifier1: int modifier2: int - proxy_protocol_info: dict[str, str | int | None] | None # TODO: Use TypedDict + proxy_protocol_info: _ProxyProtocolInfoDict | None content_length: int chunked: bool diff --git a/stubs/gunicorn/gunicorn/http/message.pyi b/stubs/gunicorn/gunicorn/http/message.pyi index 4243ef9eb75a..c0773bf72449 100644 --- a/stubs/gunicorn/gunicorn/http/message.pyi +++ b/stubs/gunicorn/gunicorn/http/message.pyi @@ -7,7 +7,7 @@ from gunicorn.config import Config from gunicorn.http.body import Body from gunicorn.http.unreader import Unreader -from .._types import _AddressType +from .._types import _AddressType, _ProxyProtocolInfoDict PP_V2_SIGNATURE: Final = b"\x0d\x0a\x0d\x0a\x00\x0d\x0a\x51\x55\x49\x54\x0a" @@ -66,7 +66,7 @@ class Request(Message): fragment: str | None limit_request_line: int req_number: int - proxy_protocol_info: dict[str, str | int | None] | None # TODO: Use TypedDict + proxy_protocol_info: _ProxyProtocolInfoDict | None def __init__(self, cfg: Config, unreader: Unreader, peer_addr: _AddressType, req_number: int = 1) -> None: ... def get_data(self, unreader: Unreader, buf: io.BytesIO, stop: bool = False) -> None: ... diff --git a/stubs/gunicorn/gunicorn/http2/request.pyi b/stubs/gunicorn/gunicorn/http2/request.pyi index 6f933b1c0265..3075d814b8d3 100644 --- a/stubs/gunicorn/gunicorn/http2/request.pyi +++ b/stubs/gunicorn/gunicorn/http2/request.pyi @@ -5,7 +5,7 @@ from typing import Literal from gunicorn.config import Config from gunicorn.http2.stream import HTTP2Stream -from .._types import _AddressType +from .._types import _AddressType, _ProxyProtocolInfoDict class HTTP2Body: def __init__(self, data: ReadableBuffer) -> None: ... @@ -33,7 +33,7 @@ class HTTP2Request: body: HTTP2Body must_close: bool req_number: int - proxy_protocol_info: dict[str, str | int | None] | None # TODO: Use TypedDict + proxy_protocol_info: _ProxyProtocolInfoDict | None priority_weight: int priority_depends_on: int diff --git a/stubs/gunicorn/gunicorn/uwsgi/message.pyi b/stubs/gunicorn/gunicorn/uwsgi/message.pyi index fbd9f318ebc7..74a62dfdc5d5 100644 --- a/stubs/gunicorn/gunicorn/uwsgi/message.pyi +++ b/stubs/gunicorn/gunicorn/uwsgi/message.pyi @@ -4,7 +4,7 @@ from gunicorn.config import Config from gunicorn.http.body import Body from gunicorn.http.unreader import Unreader -from .._types import _AddressType +from .._types import _AddressType, _ProxyProtocolInfoDict MAX_UWSGI_VARS: Final = 1000 @@ -28,7 +28,7 @@ class UWSGIRequest: uwsgi_vars: dict[str, str] modifier1: int modifier2: int - proxy_protocol_info: dict[str, str | int | None] | None # TODO: Use TypedDict + proxy_protocol_info: _ProxyProtocolInfoDict | None def __init__(self, cfg: Config, unreader: Unreader, peer_addr: _AddressType, req_number: int = 1) -> None: ... def force_close(self) -> None: ... From 43a8014a8ee95adffcfcb22ac821c311c0047da4 Mon Sep 17 00:00:00 2001 From: Ashidul Islam Date: Sat, 11 Jul 2026 14:11:46 +0530 Subject: [PATCH 2/4] Fix import order for ruff --- stubs/gunicorn/gunicorn/asgi/parser.pyi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stubs/gunicorn/gunicorn/asgi/parser.pyi b/stubs/gunicorn/gunicorn/asgi/parser.pyi index 7b0a995ffe76..1df149293730 100644 --- a/stubs/gunicorn/gunicorn/asgi/parser.pyi +++ b/stubs/gunicorn/gunicorn/asgi/parser.pyi @@ -3,6 +3,8 @@ from enum import IntEnum from typing import Any, Final, Literal, SupportsIndex, TypeAlias from typing_extensions import Self +from .._types import _ProxyProtocolInfoDict + _H1CProtocol: TypeAlias = Any # gunicorn_h1c H1CProtocol class class ParseError(Exception): ... @@ -38,8 +40,6 @@ class UnsupportedTransferCoding(ParseError): ... class InvalidChunkSize(ParseError): ... class InvalidChunkExtension(ParseError): ... -from .._types import _ProxyProtocolInfoDict - class PythonProtocol: __slots__ = ( "_on_message_begin", From 4fb189fe76f0c62d3c1429275dc0f0942dd4c9a4 Mon Sep 17 00:00:00 2001 From: Ashidul Islam Date: Sat, 11 Jul 2026 14:23:30 +0530 Subject: [PATCH 3/4] Add noqa for type alias --- stubs/gunicorn/gunicorn/_types.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stubs/gunicorn/gunicorn/_types.pyi b/stubs/gunicorn/gunicorn/_types.pyi index 17abcf35f072..0b7eb5d6075b 100644 --- a/stubs/gunicorn/gunicorn/_types.pyi +++ b/stubs/gunicorn/gunicorn/_types.pyi @@ -38,4 +38,4 @@ class _ProxyProtocolInfoUnknown(TypedDict): proxy_addr: None proxy_port: None -_ProxyProtocolInfoDict: TypeAlias = _ProxyProtocolInfo | _ProxyProtocolInfoUnknown +_ProxyProtocolInfoDict: TypeAlias = _ProxyProtocolInfo | _ProxyProtocolInfoUnknown # noqa: Y047 From dfd7af4fee94f4a34e73dd1b0ec5181527503e8a Mon Sep 17 00:00:00 2001 From: Ashidul Islam Date: Sat, 11 Jul 2026 14:35:14 +0530 Subject: [PATCH 4/4] Fix flake8-pyi unused alias errors Moving the TypedDict definitions back to parser.pyi (where they are used internally) and importing them directly in other files avoids the need for an unused intermediate alias and any oqa pragmas, conforming to strict typeshed style. --- stubs/gunicorn/gunicorn/_types.pyi | 20 +------------------ stubs/gunicorn/gunicorn/asgi/parser.pyi | 24 ++++++++++++++++++----- stubs/gunicorn/gunicorn/asgi/uwsgi.pyi | 5 +++-- stubs/gunicorn/gunicorn/http/message.pyi | 5 +++-- stubs/gunicorn/gunicorn/http2/request.pyi | 5 +++-- stubs/gunicorn/gunicorn/uwsgi/message.pyi | 5 +++-- 6 files changed, 32 insertions(+), 32 deletions(-) diff --git a/stubs/gunicorn/gunicorn/_types.pyi b/stubs/gunicorn/gunicorn/_types.pyi index 0b7eb5d6075b..815a1179f98c 100644 --- a/stubs/gunicorn/gunicorn/_types.pyi +++ b/stubs/gunicorn/gunicorn/_types.pyi @@ -1,7 +1,7 @@ ### This .pyi file is a helper for centralized storage types that are reused across different runtime modules. ### from _typeshed import FileDescriptor from collections.abc import Awaitable, Callable, Iterable, MutableMapping -from typing import Any, Literal, TypeAlias, TypedDict, type_check_only +from typing import Any, TypeAlias from typing_extensions import LiteralString _StatusType: TypeAlias = str @@ -21,21 +21,3 @@ _ASGIAppType: TypeAlias = Callable[[_ScopeType, _ReceiveType, _SendType], Awaita _UnixSocketPathType: TypeAlias = str _TcpAddressType: TypeAlias = tuple[LiteralString, int] # noqa: Y047 _AddressType: TypeAlias = _UnixSocketPathType | FileDescriptor | _TcpAddressType # noqa: Y047 - -@type_check_only -class _ProxyProtocolInfo(TypedDict): - proxy_protocol: Literal["TCP4", "TCP6", "UDP4", "UDP6"] - client_addr: str - client_port: int - proxy_addr: str - proxy_port: int - -@type_check_only -class _ProxyProtocolInfoUnknown(TypedDict): - proxy_protocol: Literal["UNKNOWN", "LOCAL", "UNSPEC"] - client_addr: None - client_port: None - proxy_addr: None - proxy_port: None - -_ProxyProtocolInfoDict: TypeAlias = _ProxyProtocolInfo | _ProxyProtocolInfoUnknown # noqa: Y047 diff --git a/stubs/gunicorn/gunicorn/asgi/parser.pyi b/stubs/gunicorn/gunicorn/asgi/parser.pyi index 1df149293730..f98f04a0f178 100644 --- a/stubs/gunicorn/gunicorn/asgi/parser.pyi +++ b/stubs/gunicorn/gunicorn/asgi/parser.pyi @@ -1,16 +1,30 @@ from collections.abc import Callable, Iterable from enum import IntEnum -from typing import Any, Final, Literal, SupportsIndex, TypeAlias +from typing import Any, Final, Literal, SupportsIndex, TypeAlias, TypedDict, type_check_only from typing_extensions import Self -from .._types import _ProxyProtocolInfoDict - _H1CProtocol: TypeAlias = Any # gunicorn_h1c H1CProtocol class class ParseError(Exception): ... class InvalidProxyLine(ParseError): ... class InvalidProxyHeader(ParseError): ... +@type_check_only +class _ProxyProtocolInfo(TypedDict): + proxy_protocol: Literal["TCP4", "TCP6", "UDP4", "UDP6"] + client_addr: str + client_port: int + proxy_addr: str + proxy_port: int + +@type_check_only +class _ProxyProtocolInfoUnknown(TypedDict): + proxy_protocol: Literal["UNKNOWN", "LOCAL", "UNSPEC"] + client_addr: None + client_port: None + proxy_addr: None + proxy_port: None + PP_V2_SIGNATURE: Final[bytes] RFC9110_6_5_1_FORBIDDEN_TRAILER: Final[frozenset[bytes]] @@ -100,7 +114,7 @@ class PythonProtocol: ) -> None: ... def feed(self, data: Iterable[SupportsIndex]) -> None: ... @property - def proxy_protocol_info(self) -> _ProxyProtocolInfoDict | None: ... + def proxy_protocol_info(self) -> _ProxyProtocolInfo | _ProxyProtocolInfoUnknown | None: ... def reset(self) -> None: ... def finish(self) -> None: ... @@ -135,7 +149,7 @@ class CallbackRequest: content_length: int chunked: bool must_close: bool - proxy_protocol_info: _ProxyProtocolInfoDict | None + proxy_protocol_info: _ProxyProtocolInfo | _ProxyProtocolInfoUnknown | None def __init__(self) -> None: ... @classmethod diff --git a/stubs/gunicorn/gunicorn/asgi/uwsgi.pyi b/stubs/gunicorn/gunicorn/asgi/uwsgi.pyi index 389b407a0d26..c76678bf8199 100644 --- a/stubs/gunicorn/gunicorn/asgi/uwsgi.pyi +++ b/stubs/gunicorn/gunicorn/asgi/uwsgi.pyi @@ -5,7 +5,8 @@ from gunicorn.asgi.unreader import AsyncUnreader from gunicorn.config import Config from gunicorn.uwsgi.message import UWSGIRequest -from .._types import _AddressType, _ProxyProtocolInfoDict +from .._types import _AddressType +from .parser import _ProxyProtocolInfo, _ProxyProtocolInfoUnknown class AsyncUWSGIRequest(UWSGIRequest): cfg: Config @@ -26,7 +27,7 @@ class AsyncUWSGIRequest(UWSGIRequest): uwsgi_vars: dict[str, str] modifier1: int modifier2: int - proxy_protocol_info: _ProxyProtocolInfoDict | None + proxy_protocol_info: _ProxyProtocolInfo | _ProxyProtocolInfoUnknown | None content_length: int chunked: bool diff --git a/stubs/gunicorn/gunicorn/http/message.pyi b/stubs/gunicorn/gunicorn/http/message.pyi index c0773bf72449..4819ab116870 100644 --- a/stubs/gunicorn/gunicorn/http/message.pyi +++ b/stubs/gunicorn/gunicorn/http/message.pyi @@ -7,7 +7,8 @@ from gunicorn.config import Config from gunicorn.http.body import Body from gunicorn.http.unreader import Unreader -from .._types import _AddressType, _ProxyProtocolInfoDict +from .._types import _AddressType +from ..asgi.parser import _ProxyProtocolInfo, _ProxyProtocolInfoUnknown PP_V2_SIGNATURE: Final = b"\x0d\x0a\x0d\x0a\x00\x0d\x0a\x51\x55\x49\x54\x0a" @@ -66,7 +67,7 @@ class Request(Message): fragment: str | None limit_request_line: int req_number: int - proxy_protocol_info: _ProxyProtocolInfoDict | None + proxy_protocol_info: _ProxyProtocolInfo | _ProxyProtocolInfoUnknown | None def __init__(self, cfg: Config, unreader: Unreader, peer_addr: _AddressType, req_number: int = 1) -> None: ... def get_data(self, unreader: Unreader, buf: io.BytesIO, stop: bool = False) -> None: ... diff --git a/stubs/gunicorn/gunicorn/http2/request.pyi b/stubs/gunicorn/gunicorn/http2/request.pyi index 3075d814b8d3..b9a3a377d865 100644 --- a/stubs/gunicorn/gunicorn/http2/request.pyi +++ b/stubs/gunicorn/gunicorn/http2/request.pyi @@ -5,7 +5,8 @@ from typing import Literal from gunicorn.config import Config from gunicorn.http2.stream import HTTP2Stream -from .._types import _AddressType, _ProxyProtocolInfoDict +from .._types import _AddressType +from ..asgi.parser import _ProxyProtocolInfo, _ProxyProtocolInfoUnknown class HTTP2Body: def __init__(self, data: ReadableBuffer) -> None: ... @@ -33,7 +34,7 @@ class HTTP2Request: body: HTTP2Body must_close: bool req_number: int - proxy_protocol_info: _ProxyProtocolInfoDict | None + proxy_protocol_info: _ProxyProtocolInfo | _ProxyProtocolInfoUnknown | None priority_weight: int priority_depends_on: int diff --git a/stubs/gunicorn/gunicorn/uwsgi/message.pyi b/stubs/gunicorn/gunicorn/uwsgi/message.pyi index 74a62dfdc5d5..b89c893ad508 100644 --- a/stubs/gunicorn/gunicorn/uwsgi/message.pyi +++ b/stubs/gunicorn/gunicorn/uwsgi/message.pyi @@ -4,7 +4,8 @@ from gunicorn.config import Config from gunicorn.http.body import Body from gunicorn.http.unreader import Unreader -from .._types import _AddressType, _ProxyProtocolInfoDict +from .._types import _AddressType +from ..asgi.parser import _ProxyProtocolInfo, _ProxyProtocolInfoUnknown MAX_UWSGI_VARS: Final = 1000 @@ -28,7 +29,7 @@ class UWSGIRequest: uwsgi_vars: dict[str, str] modifier1: int modifier2: int - proxy_protocol_info: _ProxyProtocolInfoDict | None + proxy_protocol_info: _ProxyProtocolInfo | _ProxyProtocolInfoUnknown | None def __init__(self, cfg: Config, unreader: Unreader, peer_addr: _AddressType, req_number: int = 1) -> None: ... def force_close(self) -> None: ...