From 74feb5276b4e0633ccd89ae8642b430565d1ebb4 Mon Sep 17 00:00:00 2001 From: donbarbos Date: Tue, 3 Feb 2026 12:59:42 +0400 Subject: [PATCH] [gunicorn] Update to 25.0.* Closes: #15356 --- pyrightconfig.stricter.json | 1 + stubs/gunicorn/METADATA.toml | 2 +- stubs/gunicorn/gunicorn/arbiter.pyi | 8 + stubs/gunicorn/gunicorn/asgi/message.pyi | 2 +- stubs/gunicorn/gunicorn/asgi/uwsgi.pyi | 38 ++++ stubs/gunicorn/gunicorn/config.pyi | 180 ++++++++++++++++-- stubs/gunicorn/gunicorn/dirty/__init__.pyi | 37 ++++ stubs/gunicorn/gunicorn/dirty/app.pyi | 17 ++ stubs/gunicorn/gunicorn/dirty/arbiter.pyi | 43 +++++ stubs/gunicorn/gunicorn/dirty/client.pyi | 74 +++++++ stubs/gunicorn/gunicorn/dirty/errors.pyi | 55 ++++++ stubs/gunicorn/gunicorn/dirty/protocol.pyi | 40 ++++ stubs/gunicorn/gunicorn/dirty/worker.pyi | 36 ++++ stubs/gunicorn/gunicorn/http/__init__.pyi | 12 +- stubs/gunicorn/gunicorn/http/errors.pyi | 5 + stubs/gunicorn/gunicorn/http2/__init__.pyi | 19 ++ .../gunicorn/http2/async_connection.pyi | 42 ++++ stubs/gunicorn/gunicorn/http2/connection.pyi | 43 +++++ stubs/gunicorn/gunicorn/http2/errors.pyi | 70 +++++++ stubs/gunicorn/gunicorn/http2/request.pyi | 49 +++++ stubs/gunicorn/gunicorn/http2/stream.pyi | 58 ++++++ stubs/gunicorn/gunicorn/sock.pyi | 2 + stubs/gunicorn/gunicorn/workers/__init__.pyi | 2 +- .../gunicorn/gunicorn/workers/base_async.pyi | 7 +- stubs/gunicorn/gunicorn/workers/ggevent.pyi | 2 +- stubs/gunicorn/gunicorn/workers/gthread.pyi | 11 +- 26 files changed, 828 insertions(+), 27 deletions(-) create mode 100644 stubs/gunicorn/gunicorn/asgi/uwsgi.pyi create mode 100644 stubs/gunicorn/gunicorn/dirty/__init__.pyi create mode 100644 stubs/gunicorn/gunicorn/dirty/app.pyi create mode 100644 stubs/gunicorn/gunicorn/dirty/arbiter.pyi create mode 100644 stubs/gunicorn/gunicorn/dirty/client.pyi create mode 100644 stubs/gunicorn/gunicorn/dirty/errors.pyi create mode 100644 stubs/gunicorn/gunicorn/dirty/protocol.pyi create mode 100644 stubs/gunicorn/gunicorn/dirty/worker.pyi create mode 100644 stubs/gunicorn/gunicorn/http2/__init__.pyi create mode 100644 stubs/gunicorn/gunicorn/http2/async_connection.pyi create mode 100644 stubs/gunicorn/gunicorn/http2/connection.pyi create mode 100644 stubs/gunicorn/gunicorn/http2/errors.pyi create mode 100644 stubs/gunicorn/gunicorn/http2/request.pyi create mode 100644 stubs/gunicorn/gunicorn/http2/stream.pyi diff --git a/pyrightconfig.stricter.json b/pyrightconfig.stricter.json index 7afdcc3eab14..c8ade412980c 100644 --- a/pyrightconfig.stricter.json +++ b/pyrightconfig.stricter.json @@ -44,6 +44,7 @@ "stubs/grpcio-reflection/grpc_reflection/v1alpha", "stubs/grpcio-status/grpc_status", "stubs/grpcio/grpc/__init__.pyi", + "stubs/gunicorn/gunicorn/dirty", "stubs/hdbcli/hdbcli/dbapi.pyi", "stubs/html5lib", "stubs/httplib2", diff --git a/stubs/gunicorn/METADATA.toml b/stubs/gunicorn/METADATA.toml index 16f90a25b5a2..91b6f67282ab 100644 --- a/stubs/gunicorn/METADATA.toml +++ b/stubs/gunicorn/METADATA.toml @@ -1,4 +1,4 @@ -version = "24.1.*" +version = "25.0.*" upstream_repository = "https://github.com/benoitc/gunicorn" requires = ["types-gevent"] diff --git a/stubs/gunicorn/gunicorn/arbiter.pyi b/stubs/gunicorn/gunicorn/arbiter.pyi index 169fe2136097..15710a89669d 100644 --- a/stubs/gunicorn/gunicorn/arbiter.pyi +++ b/stubs/gunicorn/gunicorn/arbiter.pyi @@ -4,6 +4,7 @@ from typing import ClassVar from gunicorn.app.base import BaseApplication from gunicorn.config import Config +from gunicorn.dirty import DirtyArbiter from gunicorn.glogging import Logger as GLogger from gunicorn.sock import BaseSocket from gunicorn.workers.base import Worker @@ -28,6 +29,9 @@ class Arbiter: reexec_pid: int master_pid: int master_name: str + dirty_arbiter_pid: int + dirty_arbiter: DirtyArbiter | None + dirty_pidfile: str | None pid: int app: BaseApplication cfg: Config @@ -69,3 +73,7 @@ class Arbiter: def spawn_workers(self) -> None: ... def kill_workers(self, sig: int) -> None: ... def kill_worker(self, pid: int, sig: int) -> None: ... + def spawn_dirty_arbiter(self) -> int | None: ... + def kill_dirty_arbiter(self, sig: int) -> None: ... + def reap_dirty_arbiter(self) -> None: ... + def manage_dirty_arbiter(self) -> None: ... diff --git a/stubs/gunicorn/gunicorn/asgi/message.pyi b/stubs/gunicorn/gunicorn/asgi/message.pyi index 79cd0eecd889..a279e34cdd91 100644 --- a/stubs/gunicorn/gunicorn/asgi/message.pyi +++ b/stubs/gunicorn/gunicorn/asgi/message.pyi @@ -45,6 +45,6 @@ class AsyncRequest: async def parse(cls, cfg: Config, unreader: AsyncUnreader, peer_addr: _AddressType, req_number: int = 1) -> Self: ... def force_close(self) -> None: ... def should_close(self) -> bool: ... - def get_header(self, name: str) -> str: ... + def get_header(self, name: str) -> str | None: ... async def read_body(self, size: int = 8192) -> bytes: ... async def drain_body(self) -> None: ... diff --git a/stubs/gunicorn/gunicorn/asgi/uwsgi.pyi b/stubs/gunicorn/gunicorn/asgi/uwsgi.pyi new file mode 100644 index 000000000000..651dcdd416d1 --- /dev/null +++ b/stubs/gunicorn/gunicorn/asgi/uwsgi.pyi @@ -0,0 +1,38 @@ +from typing import Literal +from typing_extensions import Self + +from gunicorn.asgi.unreader import AsyncUnreader +from gunicorn.config import Config +from gunicorn.uwsgi.message import UWSGIRequest + +from .._types import _AddressType + +class AsyncUWSGIRequest(UWSGIRequest): + cfg: Config + unreader: AsyncUnreader # type: ignore[assignment] + peer_addr: _AddressType + remote_addr: _AddressType + req_number: int + method: str | None + uri: str | None + path: str | None + query: str | None + fragment: str | None + version: tuple[int, int] + headers: list[tuple[str, str]] + trailers: list[tuple[str, str]] + scheme: Literal["https", "http"] + must_close: bool + uwsgi_vars: dict[str, str] + modifier1: int + modifier2: int + proxy_protocol_info: dict[str, str | int | None] | None # TODO: Use TypedDict + content_length: int + chunked: bool + + def __init__(self, cfg: Config, unreader: AsyncUnreader, peer_addr: _AddressType, req_number: int = 1) -> None: ... + @classmethod + async def parse(cls, cfg: Config, unreader: AsyncUnreader, peer_addr: _AddressType, req_number: int = 1) -> Self: ... # type: ignore[override] + async def read_body(self, size: int = 8192) -> bytes: ... + async def drain_body(self) -> None: ... + def get_header(self, name: str) -> str | None: ... diff --git a/stubs/gunicorn/gunicorn/config.pyi b/stubs/gunicorn/gunicorn/config.pyi index 9875d85e088b..f0acc340e177 100644 --- a/stubs/gunicorn/gunicorn/config.pyi +++ b/stubs/gunicorn/gunicorn/config.pyi @@ -2,7 +2,7 @@ import argparse from _typeshed import ConvertibleToInt from collections.abc import Callable, Container from ssl import SSLContext, _SSLMethod -from typing import Annotated, Any, ClassVar, overload +from typing import Annotated, Any, ClassVar, Final, overload from typing_extensions import TypeAlias from gunicorn.arbiter import Arbiter @@ -34,6 +34,10 @@ _WorkerExitHookType: TypeAlias = Callable[[Arbiter, Worker], object] _NumWorkersChangedHookType: TypeAlias = Callable[[Arbiter, int, int | None], object] _OnExitHookType: TypeAlias = Callable[[Arbiter], object] _SSLContextHookType: TypeAlias = Callable[[Config, Callable[[], SSLContext]], SSLContext] +_OnDirtyStartingHookType: TypeAlias = Callable[[Arbiter], object] +_DirtyPostForkHookType: TypeAlias = Callable[[Arbiter, Worker], object] +_DirtyWorkerInitHookType: TypeAlias = Callable[[Worker], object] +_DirtyWorkerExitHookType: TypeAlias = Callable[[Arbiter, Worker], object] _HookType: TypeAlias = ( _OnStartingHookType @@ -52,12 +56,16 @@ _HookType: TypeAlias = ( | _NumWorkersChangedHookType | _OnExitHookType | _SSLContextHookType + | _OnDirtyStartingHookType + | _DirtyPostForkHookType + | _DirtyWorkerInitHookType + | _DirtyWorkerExitHookType ) # Validators _BoolValidatorType: TypeAlias = Callable[[bool | str | None], bool | None] _StringValidatorType: TypeAlias = Callable[[str | None], str | None] _ListStringValidatorType: TypeAlias = Callable[[str | list[str] | None], list[str]] -_IntValidatorType: TypeAlias = Callable[[int | ConvertibleToInt], int] +_IntValidatorType: TypeAlias = Callable[[ConvertibleToInt], int] _DictValidatorType: TypeAlias = Callable[[dict[str, Any]], dict[str, Any]] _ClassValidatorType: TypeAlias = Callable[[object | str | None], type[Any] | None] _UserGroupValidatorType: TypeAlias = Callable[[str | int | None], int] @@ -66,6 +74,8 @@ _CallableValidatorType: TypeAlias = Callable[[str | _HookType], _HookType] _ProxyProtocolValidatorType: TypeAlias = Callable[[str | bool | None], str] _ASGILoopValidatorType: TypeAlias = Callable[[str | None], str] _ASGILifespanValidatorType: TypeAlias = Callable[[str | None], str] +_HTTP2FrameSizeValidatorType: TypeAlias = Callable[[ConvertibleToInt], int] +_HTTPProtocolsValidatorType: TypeAlias = Callable[[str | None], list[str]] _ValidatorType: TypeAlias = ( # noqa: Y047 _BoolValidatorType @@ -80,6 +90,8 @@ _ValidatorType: TypeAlias = ( # noqa: Y047 | _ProxyProtocolValidatorType | _ASGILoopValidatorType | _ASGILifespanValidatorType + | _HTTP2FrameSizeValidatorType + | _HTTPProtocolsValidatorType ) KNOWN_SETTINGS: list[Setting] @@ -163,10 +175,8 @@ def validate_bool(val: None) -> None: ... @overload def validate_bool(val: Annotated[str, "Case-insensitive boolean string ('true'/'false' in any case)"]) -> bool: ... def validate_dict(val: dict[str, Any]) -> dict[str, Any]: ... -@overload -def validate_pos_int(val: int) -> int: ... -@overload def validate_pos_int(val: ConvertibleToInt) -> int: ... +def validate_http2_frame_size(val: ConvertibleToInt) -> int: ... def validate_ssl_version(val: _SSLMethod) -> _SSLMethod: ... @overload def validate_string(val: str) -> str: ... @@ -176,18 +186,8 @@ def validate_string(val: None) -> None: ... def validate_file_exists(val: str) -> str: ... @overload def validate_file_exists(val: None) -> None: ... -@overload -def validate_list_string(val: str) -> list[str]: ... -@overload -def validate_list_string(val: list[str]) -> list[str]: ... -@overload -def validate_list_string(val: None) -> list[str]: ... -@overload -def validate_list_of_existing_files(val: str) -> list[str]: ... -@overload -def validate_list_of_existing_files(val: list[str]) -> list[str]: ... -@overload -def validate_list_of_existing_files(val: None) -> list[str]: ... +def validate_list_string(val: str | list[str] | None) -> list[str]: ... +def validate_list_of_existing_files(val: str | list[str] | None) -> list[str]: ... def validate_string_to_addr_list(val: str | None) -> list[str]: ... def validate_string_to_list(val: str | None) -> list[str]: ... @overload @@ -766,7 +766,7 @@ class Paste(Setting): class OnStarting(Setting): name: ClassVar[str] section: ClassVar[str] - validator: ClassVar[_CallableValidatorType] = ... + validator: ClassVar[_CallableValidatorType] type: ClassVar[Callable[..., Any]] default: ClassVar[_OnStartingHookType] desc: ClassVar[str] @@ -1029,6 +1029,60 @@ class Ciphers(Setting): default: ClassVar[None] desc: ClassVar[str] +VALID_HTTP_PROTOCOLS: Final[frozenset[str]] +ALPN_PROTOCOL_MAP: Final[dict[str, str]] + +def validate_http_protocols(val: str | None) -> list[str]: ... + +class HTTPProtocols(Setting): + name: ClassVar[str] + section: ClassVar[str] + cli: ClassVar[list[str]] + meta: ClassVar[str] + validator: ClassVar[_HTTPProtocolsValidatorType] + default: ClassVar[str] + desc: ClassVar[str] + +class HTTP2MaxConcurrentStreams(Setting): + name: ClassVar[str] + section: ClassVar[str] + cli: ClassVar[list[str]] + meta: ClassVar[str] + validator: ClassVar[_IntValidatorType] + type: ClassVar[type[int]] + default: ClassVar[int] + desc: ClassVar[str] + +class HTTP2InitialWindowSize(Setting): + name: ClassVar[str] + section: ClassVar[str] + cli: ClassVar[list[str]] + meta: ClassVar[str] + validator: ClassVar[_IntValidatorType] + type: ClassVar[type[int]] + default: ClassVar[int] + desc: ClassVar[str] + +class HTTP2MaxFrameSize(Setting): + name: ClassVar[str] + section: ClassVar[str] + cli: ClassVar[list[str]] + meta: ClassVar[str] + validator: ClassVar[_HTTP2FrameSizeValidatorType] + type: ClassVar[type[int]] + default: ClassVar[int] + desc: ClassVar[str] + +class HTTP2MaxHeaderListSize(Setting): + name: ClassVar[str] + section: ClassVar[str] + cli: ClassVar[list[str]] + meta: ClassVar[str] + validator: ClassVar[_IntValidatorType] + type: ClassVar[type[int]] + default: ClassVar[int] + desc: ClassVar[str] + class PasteGlobalConf(Setting): name: ClassVar[str] action: ClassVar[str] @@ -1129,3 +1183,93 @@ class RootPath(Setting): validator: ClassVar[_StringValidatorType] default: ClassVar[str] desc: ClassVar[str] + +class DirtyApps(Setting): + name: ClassVar[str] + section: ClassVar[str] + cli: ClassVar[list[str]] + action: ClassVar[str] + meta: ClassVar[str] + validator: ClassVar[_ListStringValidatorType] + default: ClassVar[list[str]] + desc: ClassVar[str] + +class DirtyWorkers(Setting): + name: ClassVar[str] + section: ClassVar[str] + cli: ClassVar[list[str]] + meta: ClassVar[str] + validator: ClassVar[_IntValidatorType] + type: ClassVar[type[int]] + default: ClassVar[int] + desc: ClassVar[str] + +class DirtyTimeout(Setting): + name: ClassVar[str] + section: ClassVar[str] + cli: ClassVar[list[str]] + meta: ClassVar[str] + validator: ClassVar[_IntValidatorType] + type: ClassVar[type[int]] + default: ClassVar[int] + desc: ClassVar[str] + +class DirtyThreads(Setting): + name: ClassVar[str] + section: ClassVar[str] + cli: ClassVar[list[str]] + meta: ClassVar[str] + validator: ClassVar[_IntValidatorType] + type: ClassVar[type[int]] + default: ClassVar[int] + desc: ClassVar[str] + +class DirtyGracefulTimeout(Setting): + name: ClassVar[str] + section: ClassVar[str] + cli: ClassVar[list[str]] + meta: ClassVar[str] + validator: ClassVar[_IntValidatorType] + type: ClassVar[type[int]] + default: ClassVar[int] + desc: ClassVar[str] + +class OnDirtyStarting(Setting): + name: ClassVar[str] + section: ClassVar[str] + validator: ClassVar[_CallableValidatorType] + type: ClassVar[Callable[..., Any]] + default: ClassVar[_OnDirtyStartingHookType] + desc: ClassVar[str] + + def on_dirty_starting(arbiter: Arbiter) -> None: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] + +class DirtyPostFork(Setting): + name: ClassVar[str] + section: ClassVar[str] + validator: ClassVar[_CallableValidatorType] + type: ClassVar[Callable[..., Any]] + default: ClassVar[_DirtyPostForkHookType] + desc: ClassVar[str] + + def dirty_post_fork(arbiter: Arbiter, worker: Worker) -> None: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] + +class DirtyWorkerInit(Setting): + name: ClassVar[str] + section: ClassVar[str] + validator: ClassVar[_CallableValidatorType] + type: ClassVar[Callable[..., Any]] + default: ClassVar[_DirtyWorkerInitHookType] + desc: ClassVar[str] + + def dirty_worker_init(worker: Worker) -> None: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] + +class DirtyWorkerExit(Setting): + name: ClassVar[str] + section: ClassVar[str] + validator: ClassVar[_CallableValidatorType] + type: ClassVar[Callable[..., Any]] + default: ClassVar[_DirtyWorkerExitHookType] + desc: ClassVar[str] + + def dirty_worker_exit(arbiter: Arbiter, worker: Worker) -> None: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] diff --git a/stubs/gunicorn/gunicorn/dirty/__init__.pyi b/stubs/gunicorn/gunicorn/dirty/__init__.pyi new file mode 100644 index 000000000000..bce1f1dfe24a --- /dev/null +++ b/stubs/gunicorn/gunicorn/dirty/__init__.pyi @@ -0,0 +1,37 @@ +from .app import DirtyApp as DirtyApp +from .arbiter import DirtyArbiter as DirtyArbiter +from .client import ( + DirtyClient as DirtyClient, + close_dirty_client as close_dirty_client, + close_dirty_client_async as close_dirty_client_async, + get_dirty_client as get_dirty_client, + get_dirty_client_async as get_dirty_client_async, + set_dirty_socket_path as set_dirty_socket_path, +) +from .errors import ( + DirtyAppError as DirtyAppError, + DirtyAppNotFoundError as DirtyAppNotFoundError, + DirtyConnectionError as DirtyConnectionError, + DirtyError as DirtyError, + DirtyProtocolError as DirtyProtocolError, + DirtyTimeoutError as DirtyTimeoutError, + DirtyWorkerError as DirtyWorkerError, +) + +__all__ = [ + "DirtyError", + "DirtyTimeoutError", + "DirtyConnectionError", + "DirtyWorkerError", + "DirtyAppError", + "DirtyAppNotFoundError", + "DirtyProtocolError", + "DirtyApp", + "DirtyClient", + "get_dirty_client", + "get_dirty_client_async", + "close_dirty_client", + "close_dirty_client_async", + "DirtyArbiter", + "set_dirty_socket_path", +] diff --git a/stubs/gunicorn/gunicorn/dirty/app.pyi b/stubs/gunicorn/gunicorn/dirty/app.pyi new file mode 100644 index 000000000000..c2355e8c438c --- /dev/null +++ b/stubs/gunicorn/gunicorn/dirty/app.pyi @@ -0,0 +1,17 @@ +from _typeshed import Incomplete +from collections.abc import Iterable +from typing import Any + +class DirtyApp: + workers: Incomplete | None + + def init(self) -> None: ... + def __call__( + self, action: str, *args: Any, **kwargs: Any + ) -> Any: ... # Arguments and result depend on method name passed to action + def close(self) -> None: ... + +def parse_dirty_app_spec(spec: str) -> tuple[str, int | None]: ... +def load_dirty_app(import_path: str): ... +def load_dirty_apps(import_paths: Iterable[str]) -> dict[str, Incomplete]: ... +def get_app_workers_attribute(import_path: str) -> int | None: ... diff --git a/stubs/gunicorn/gunicorn/dirty/arbiter.pyi b/stubs/gunicorn/gunicorn/dirty/arbiter.pyi new file mode 100644 index 000000000000..250f66db9e1a --- /dev/null +++ b/stubs/gunicorn/gunicorn/dirty/arbiter.pyi @@ -0,0 +1,43 @@ +import asyncio +from _typeshed import Incomplete +from asyncio import StreamReader, StreamWriter +from signal import Signals +from typing import ClassVar + +from gunicorn.config import Config +from gunicorn.dirty.worker import DirtyWorker +from gunicorn.glogging import Logger as GLogger + +class DirtyArbiter: + SIGNALS: ClassVar[list[Signals]] + WORKER_BOOT_ERROR: ClassVar[int] + cfg: Config + log: GLogger + pid: int | None + ppid: int + pidfile: str | None + tmpdir: str + socket_path: str + workers: dict[int, DirtyWorker] + worker_sockets: dict[int, str] + worker_connections: dict[int, tuple[Incomplete, Incomplete]] + worker_queues: dict[int, asyncio.Queue[Incomplete]] + worker_consumers: dict[int, asyncio.Task[None]] + worker_age: int + alive: bool + app_specs: dict[str, dict[Incomplete, Incomplete]] + app_worker_map: dict[str, set[Incomplete]] + worker_app_map: dict[int, list[Incomplete]] + + def __init__(self, cfg: Config, log: GLogger, socket_path: str | None = None, pidfile: str | None = None) -> None: ... + def run(self) -> None: ... + def init_signals(self) -> None: ... + async def handle_client(self, reader: StreamReader, writer: StreamWriter) -> None: ... + async def route_request(self, request: dict[str, Incomplete], client_writer: StreamWriter) -> None: ... + async def manage_workers(self) -> None: ... + def spawn_worker(self) -> int | None: ... + def kill_worker(self, pid: int, sig: int) -> None: ... + async def murder_workers(self) -> None: ... + def reap_workers(self) -> None: ... + async def reload(self) -> None: ... + async def stop(self, graceful: bool = True) -> None: ... diff --git a/stubs/gunicorn/gunicorn/dirty/client.pyi b/stubs/gunicorn/gunicorn/dirty/client.pyi new file mode 100644 index 000000000000..3d77eb9a31df --- /dev/null +++ b/stubs/gunicorn/gunicorn/dirty/client.pyi @@ -0,0 +1,74 @@ +from _typeshed import Incomplete +from types import TracebackType +from typing import Any, ClassVar +from typing_extensions import Self + +class DirtyClient: + socket_path: str + timeout: float + + def __init__(self, socket_path: str, timeout: float = 30.0) -> None: ... + def connect(self) -> None: ... + # Arguments and result depend on app path and method name passed to action + def execute(self, app_path: str, action: str, *args: Any, **kwargs: Any) -> Any: ... + def stream(self, app_path: str, action: str, *args: Any, **kwargs: Any) -> DirtyStreamIterator: ... + def close(self) -> None: ... + async def connect_async(self) -> None: ... + async def execute_async(self, app_path: str, action: str, *args: Any, **kwargs: Any) -> Any: ... + def stream_async(self, app_path: str, action: str, *args: Any, **kwargs: Any) -> DirtyAsyncStreamIterator: ... + async def close_async(self) -> None: ... + def __enter__(self) -> Self: ... + def __exit__( + self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None + ) -> None: ... + async def __aenter__(self) -> Self: ... + async def __aexit__( + self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None + ) -> None: ... + +class DirtyStreamIterator: + DEFAULT_IDLE_TIMEOUT: ClassVar[float] + client: DirtyClient + app_path: str + action: str + args: tuple[Incomplete, ...] + kwargs: dict[str, Incomplete] + + def __init__( + self, + client: DirtyClient, + app_path: str, + action: str, + args: tuple[Incomplete, ...], + kwargs: dict[str, Incomplete], + idle_timeout: float | None = None, + ) -> None: ... + def __iter__(self) -> Self: ... + def __next__(self) -> Incomplete | None: ... + +class DirtyAsyncStreamIterator: + DEFAULT_IDLE_TIMEOUT: ClassVar[float] + client: DirtyClient + app_path: str + action: str + args: tuple[Incomplete, ...] + kwargs: dict[str, Incomplete] + + def __init__( + self, + client: DirtyClient, + app_path: str, + action: str, + args: tuple[Incomplete, ...], + kwargs: dict[str, Incomplete], + idle_timeout: float | None = None, + ) -> None: ... + def __aiter__(self) -> Self: ... + async def __anext__(self) -> Incomplete | None: ... + +def set_dirty_socket_path(path: str) -> None: ... +def get_dirty_socket_path() -> str: ... +def get_dirty_client(timeout: float = 30.0) -> DirtyClient: ... +async def get_dirty_client_async(timeout: float = 30.0) -> DirtyClient: ... +def close_dirty_client() -> None: ... +async def close_dirty_client_async() -> None: ... diff --git a/stubs/gunicorn/gunicorn/dirty/errors.pyi b/stubs/gunicorn/gunicorn/dirty/errors.pyi new file mode 100644 index 000000000000..0bfd9ed341ab --- /dev/null +++ b/stubs/gunicorn/gunicorn/dirty/errors.pyi @@ -0,0 +1,55 @@ +from _typeshed import Incomplete +from typing import TypedDict, type_check_only + +@type_check_only +class _DirtyErrorDict(TypedDict): + error_type: str + message: str + details: dict[str, Incomplete] + +class DirtyError(Exception): + message: str + details: dict[str, Incomplete] + + def __init__(self, message: str, details: dict[str, Incomplete] | None = None) -> None: ... + def to_dict(self) -> _DirtyErrorDict: ... + @classmethod + def from_dict(cls, data: dict[str, Incomplete]) -> DirtyError: ... + +class DirtyTimeoutError(DirtyError): + timeout: float | None + + def __init__(self, message: str = "Operation timed out", timeout: float | None = None) -> None: ... + +class DirtyConnectionError(DirtyError): + socket_path: str | None + + def __init__(self, message: str = "Connection failed", socket_path: str | None = None) -> None: ... + +class DirtyWorkerError(DirtyError): + worker_id: int | None + traceback: str | None + + def __init__(self, message: str, worker_id: int | None = None, traceback: str | None = None) -> None: ... + +class DirtyAppError(DirtyError): + app_path: str | None + action: str | None + traceback: str | None + + def __init__( + self, message: str, app_path: str | None = None, action: str | None = None, traceback: str | None = None + ) -> None: ... + +class DirtyAppNotFoundError(DirtyAppError): + app_path: str + + def __init__(self, app_path: str) -> None: ... + +class DirtyNoWorkersAvailableError(DirtyError): + app_path: str + + def __init__(self, app_path: str, message: str | None = None) -> None: ... + +class DirtyProtocolError(DirtyError): + def __init__(self, message: str = "Protocol error", raw_data: str | bytes | None = None) -> None: ... diff --git a/stubs/gunicorn/gunicorn/dirty/protocol.pyi b/stubs/gunicorn/gunicorn/dirty/protocol.pyi new file mode 100644 index 000000000000..ef13bf4c7ebd --- /dev/null +++ b/stubs/gunicorn/gunicorn/dirty/protocol.pyi @@ -0,0 +1,40 @@ +import asyncio +import socket +from _typeshed import Incomplete +from typing import ClassVar + +class DirtyProtocol: + HEADER_FORMAT: ClassVar[str] + HEADER_SIZE: ClassVar[int] + MAX_MESSAGE_SIZE: ClassVar[int] + MSG_TYPE_REQUEST: ClassVar[str] + MSG_TYPE_RESPONSE: ClassVar[str] + MSG_TYPE_ERROR: ClassVar[str] + MSG_TYPE_CHUNK: ClassVar[str] + MSG_TYPE_END: ClassVar[str] + + @staticmethod + def encode(message: dict[Incomplete, Incomplete]) -> bytes: ... + @staticmethod + def decode(data: bytes) -> dict[Incomplete, Incomplete]: ... + @staticmethod + async def read_message_async(reader: asyncio.StreamReader) -> dict[Incomplete, Incomplete]: ... + @staticmethod + async def write_message_async(writer: asyncio.StreamWriter, message: dict[Incomplete, Incomplete]) -> None: ... + @staticmethod + def read_message(sock: socket.socket) -> dict[Incomplete, Incomplete]: ... + @staticmethod + def write_message(sock: socket.socket, message: dict[Incomplete, Incomplete]) -> None: ... + +# TODO: Use TypedDict for results +def make_request( + request_id: str, + app_path: str, + action: str, + args: tuple[Incomplete, ...] | None = None, + kwargs: dict[str, Incomplete] | None = None, +) -> dict[str, Incomplete]: ... +def make_response(request_id: str, result) -> dict[str, Incomplete]: ... +def make_error_response(request_id: str, error) -> dict[str, Incomplete]: ... +def make_chunk_message(request_id: str, data) -> dict[str, Incomplete]: ... +def make_end_message(request_id: str) -> dict[str, Incomplete]: ... diff --git a/stubs/gunicorn/gunicorn/dirty/worker.pyi b/stubs/gunicorn/gunicorn/dirty/worker.pyi new file mode 100644 index 000000000000..2d6d93817d4c --- /dev/null +++ b/stubs/gunicorn/gunicorn/dirty/worker.pyi @@ -0,0 +1,36 @@ +from _typeshed import Incomplete +from asyncio import StreamReader, StreamWriter +from collections.abc import Iterable, Mapping +from signal import Signals +from typing import Any, ClassVar, Literal + +from gunicorn.config import Config +from gunicorn.glogging import Logger as GLogger +from gunicorn.workers.workertmp import WorkerTmp + +class DirtyWorker: + SIGNALS: ClassVar[list[Signals]] + age: int + pid: int | Literal["[booting]"] + ppid: int + app_paths: list[str] + cfg: Config + log: GLogger + socket_path: str + booted: bool + aborted: bool + alive: bool + tmp: WorkerTmp + apps: dict[str, Incomplete] + + def __init__(self, age: int, ppid: int, app_paths: list[str], cfg: Config, log: GLogger, socket_path: str) -> None: ... + def notify(self) -> None: ... + def init_process(self) -> None: ... + def init_signals(self) -> None: ... + def load_apps(self) -> None: ... + def run(self) -> None: ... + async def handle_connection(self, reader: StreamReader, writer: StreamWriter) -> None: ... + async def handle_request(self, message: dict[str, Incomplete], writer: StreamWriter) -> None: ... + async def execute( + self, app_path: str, action: str, args: Iterable[Any], kwargs: Mapping[str, Any] + ) -> Any: ... # Arguments and result depend on method name passed to action diff --git a/stubs/gunicorn/gunicorn/http/__init__.pyi b/stubs/gunicorn/gunicorn/http/__init__.pyi index 9b35cbbe56cc..8265485b044a 100644 --- a/stubs/gunicorn/gunicorn/http/__init__.pyi +++ b/stubs/gunicorn/gunicorn/http/__init__.pyi @@ -1,15 +1,25 @@ import socket from collections.abc import Iterable +from typing import Literal, overload from gunicorn.config import Config from gunicorn.http.message import Message as Message, Request as Request from gunicorn.http.parser import RequestParser as RequestParser +from gunicorn.http2.connection import HTTP2ServerConnection from gunicorn.uwsgi.parser import UWSGIParser from .._types import _AddressType +@overload def get_parser( - cfg: Config, source: socket.socket | Iterable[bytes], source_addr: _AddressType + cfg: Config, + source: socket.socket | Iterable[bytes], + source_addr: _AddressType, + http2_connection: Literal[False] | None = False, ) -> UWSGIParser | RequestParser: ... +@overload +def get_parser( + cfg: Config, source: socket.socket | Iterable[bytes], source_addr: _AddressType, http2_connection: Literal[True] = ... +) -> HTTP2ServerConnection: ... __all__ = ["Message", "Request", "RequestParser", "get_parser"] diff --git a/stubs/gunicorn/gunicorn/http/errors.pyi b/stubs/gunicorn/gunicorn/http/errors.pyi index 69cfd9ff8aef..ec80a8ff30eb 100644 --- a/stubs/gunicorn/gunicorn/http/errors.pyi +++ b/stubs/gunicorn/gunicorn/http/errors.pyi @@ -26,6 +26,11 @@ class InvalidRequestMethod(ParseException): def __init__(self, method: str) -> None: ... +class ExpectationFailed(ParseException): + expect: str + + def __init__(self, expect: str) -> None: ... + class InvalidHTTPVersion(ParseException): version: str | tuple[int, int] diff --git a/stubs/gunicorn/gunicorn/http2/__init__.pyi b/stubs/gunicorn/gunicorn/http2/__init__.pyi new file mode 100644 index 000000000000..3ad38b85146f --- /dev/null +++ b/stubs/gunicorn/gunicorn/http2/__init__.pyi @@ -0,0 +1,19 @@ +from typing import Final + +from .async_connection import AsyncHTTP2Connection +from .connection import HTTP2ServerConnection + +H2_MIN_VERSION: Final[tuple[int, int, int]] + +def is_http2_available() -> bool: ... +def get_h2_version() -> tuple[int, int, int]: ... +def get_http2_connection_class() -> type[HTTP2ServerConnection]: ... +def get_async_http2_connection_class() -> type[AsyncHTTP2Connection]: ... + +__all__ = [ + "is_http2_available", + "get_h2_version", + "get_http2_connection_class", + "get_async_http2_connection_class", + "H2_MIN_VERSION", +] diff --git a/stubs/gunicorn/gunicorn/http2/async_connection.pyi b/stubs/gunicorn/gunicorn/http2/async_connection.pyi new file mode 100644 index 000000000000..12fbc8524366 --- /dev/null +++ b/stubs/gunicorn/gunicorn/http2/async_connection.pyi @@ -0,0 +1,42 @@ +from _typeshed import Incomplete +from asyncio import StreamReader, StreamWriter +from collections.abc import Iterable +from typing import ClassVar + +from gunicorn.config import Config +from gunicorn.http2.connection import _H2Connection +from gunicorn.http2.request import HTTP2Request +from gunicorn.http2.stream import HTTP2Stream + +from .._types import _AddressType + +class AsyncHTTP2Connection: + READ_BUFFER_SIZE: ClassVar[int] + cfg: Config + reader: StreamReader + writer: StreamWriter + client_addr: _AddressType + streams: dict[int, HTTP2Stream] + initial_window_size: int + max_concurrent_streams: int + max_frame_size: int + max_header_list_size: int + h2_conn: _H2Connection + + def __init__(self, cfg: Config, reader: StreamReader, writer: StreamWriter, client_addr: _AddressType) -> None: ... + async def initiate_connection(self) -> None: ... + async def receive_data(self, timeout: float | None = None) -> list[HTTP2Request]: ... + async def send_informational(self, stream_id: int, status: int, headers: Iterable[tuple[str, Incomplete]]) -> None: ... + async def send_response( + self, stream_id: int, status: int, headers: Iterable[tuple[str, Incomplete]], body: bytes | None = None + ) -> bool: ... + async def send_data(self, stream_id: int, data: bytes, end_stream: bool = False) -> bool: ... + async def send_trailers(self, stream_id: int, trailers: Iterable[tuple[str, Incomplete]]) -> bool: ... + async def send_error(self, stream_id: int, status_code: int, message: str | None = None) -> None: ... + async def reset_stream(self, stream_id: int, error_code: int = 0x8) -> None: ... + async def close(self, error_code: int = 0x0, last_stream_id: int | None = None) -> None: ... + @property + def is_closed(self) -> bool: ... + def cleanup_stream(self, stream_id: int) -> None: ... + +__all__ = ["AsyncHTTP2Connection"] diff --git a/stubs/gunicorn/gunicorn/http2/connection.pyi b/stubs/gunicorn/gunicorn/http2/connection.pyi new file mode 100644 index 000000000000..ee5f50a0dbaa --- /dev/null +++ b/stubs/gunicorn/gunicorn/http2/connection.pyi @@ -0,0 +1,43 @@ +from _typeshed import Incomplete +from collections.abc import Iterable +from ssl import SSLSocket +from typing import Any, ClassVar +from typing_extensions import TypeAlias + +from gunicorn.config import Config +from gunicorn.http2.request import HTTP2Request +from gunicorn.http2.stream import HTTP2Stream + +from .._types import _AddressType + +_H2Connection: TypeAlias = Any # h2.connection.H2Connection class + +class HTTP2ServerConnection: + READ_BUFFER_SIZE: ClassVar[int] + cfg: Config + sock: SSLSocket + client_addr: _AddressType + streams: dict[int, HTTP2Stream] + initial_window_size: int + max_concurrent_streams: int + max_frame_size: int + max_header_list_size: int + h2_conn: _H2Connection + + def __init__(self, cfg: Config, sock: SSLSocket, client_addr: _AddressType) -> None: ... + def initiate_connection(self) -> None: ... + def receive_data(self, data: bytes | None = None) -> list[HTTP2Request]: ... + def send_informational(self, stream_id: int, status: int, headers: Iterable[tuple[str, Incomplete]]) -> None: ... + def send_response( + self, stream_id: int, status: int, headers: Iterable[tuple[str, Incomplete]], body: bytes | None = None + ) -> bool: ... + def send_data(self, stream_id: int, data: bytes, end_stream: bool = False) -> bool: ... + def send_trailers(self, stream_id: int, trailers: Iterable[tuple[str, Incomplete]]) -> bool: ... + def send_error(self, stream_id: int, status_code: int, message: str | None = None) -> None: ... + def reset_stream(self, stream_id: int, error_code: int = 0x8) -> None: ... + def close(self, error_code: int = 0x0, last_stream_id: int | None = None) -> None: ... + @property + def is_closed(self) -> bool: ... + def cleanup_stream(self, stream_id: int) -> None: ... + +__all__ = ["HTTP2ServerConnection"] diff --git a/stubs/gunicorn/gunicorn/http2/errors.pyi b/stubs/gunicorn/gunicorn/http2/errors.pyi new file mode 100644 index 000000000000..7e033d23f7a1 --- /dev/null +++ b/stubs/gunicorn/gunicorn/http2/errors.pyi @@ -0,0 +1,70 @@ +from typing import Final + +class HTTP2ErrorCode: + NO_ERROR: Final = 0x0 + PROTOCOL_ERROR: Final = 0x1 + INTERNAL_ERROR: Final = 0x2 + FLOW_CONTROL_ERROR: Final = 0x3 + SETTINGS_TIMEOUT: Final = 0x4 + STREAM_CLOSED: Final = 0x5 + FRAME_SIZE_ERROR: Final = 0x6 + REFUSED_STREAM: Final = 0x7 + CANCEL: Final = 0x8 + COMPRESSION_ERROR: Final = 0x9 + CONNECT_ERROR: Final = 0xA + ENHANCE_YOUR_CALM: Final = 0xB + INADEQUATE_SECURITY: Final = 0xC + HTTP_1_1_REQUIRED: Final = 0xD + +class HTTP2Error(Exception): + message: str + error_code: int + + def __init__(self, message: str | None = None, error_code: int | None = None) -> None: ... + +class HTTP2ProtocolError(HTTP2Error): ... +class HTTP2InternalError(HTTP2Error): ... +class HTTP2FlowControlError(HTTP2Error): ... +class HTTP2SettingsTimeout(HTTP2Error): ... +class HTTP2StreamClosed(HTTP2Error): ... +class HTTP2FrameSizeError(HTTP2Error): ... +class HTTP2RefusedStream(HTTP2Error): ... +class HTTP2Cancel(HTTP2Error): ... +class HTTP2CompressionError(HTTP2Error): ... +class HTTP2ConnectError(HTTP2Error): ... +class HTTP2EnhanceYourCalm(HTTP2Error): ... +class HTTP2InadequateSecurity(HTTP2Error): ... +class HTTP2RequiresHTTP11(HTTP2Error): ... + +class HTTP2StreamError(HTTP2Error): + stream_id: int + + def __init__(self, stream_id: int, message: str | None = None, error_code: int | None = None) -> None: ... + +class HTTP2ConnectionError(HTTP2Error): ... +class HTTP2ConfigurationError(HTTP2Error): ... + +class HTTP2NotAvailable(HTTP2Error): + def __init__(self, message: str | None = None) -> None: ... + +__all__ = [ + "HTTP2ErrorCode", + "HTTP2Error", + "HTTP2ProtocolError", + "HTTP2InternalError", + "HTTP2FlowControlError", + "HTTP2SettingsTimeout", + "HTTP2StreamClosed", + "HTTP2FrameSizeError", + "HTTP2RefusedStream", + "HTTP2Cancel", + "HTTP2CompressionError", + "HTTP2ConnectError", + "HTTP2EnhanceYourCalm", + "HTTP2InadequateSecurity", + "HTTP2RequiresHTTP11", + "HTTP2StreamError", + "HTTP2ConnectionError", + "HTTP2ConfigurationError", + "HTTP2NotAvailable", +] diff --git a/stubs/gunicorn/gunicorn/http2/request.pyi b/stubs/gunicorn/gunicorn/http2/request.pyi new file mode 100644 index 000000000000..6f933b1c0265 --- /dev/null +++ b/stubs/gunicorn/gunicorn/http2/request.pyi @@ -0,0 +1,49 @@ +from _typeshed import Incomplete, ReadableBuffer +from collections.abc import Iterator +from typing import Literal + +from gunicorn.config import Config +from gunicorn.http2.stream import HTTP2Stream + +from .._types import _AddressType + +class HTTP2Body: + def __init__(self, data: ReadableBuffer) -> None: ... + def read(self, size: int | None = None) -> bytes: ... + def readline(self, size: int | None = None) -> bytes: ... + def readlines(self, hint: int | None = None) -> list[bytes]: ... + def __iter__(self) -> Iterator[bytes]: ... + def __len__(self) -> int: ... + def close(self) -> None: ... + +class HTTP2Request: + stream: HTTP2Stream + cfg: Config + peer_addr: _AddressType + remote_addr: _AddressType + version: tuple[int, int] + method: str + scheme: Literal["https", "http"] + uri: str + path: str + query: str + fragment: str + headers: list[tuple[str, str]] + trailers: list[tuple[str, Incomplete]] + body: HTTP2Body + must_close: bool + req_number: int + proxy_protocol_info: dict[str, str | int | None] | None # TODO: Use TypedDict + priority_weight: int + priority_depends_on: int + + def __init__(self, stream: HTTP2Stream, cfg: Config, peer_addr: _AddressType) -> None: ... + def force_close(self) -> None: ... + def should_close(self) -> bool: ... + def get_header(self, name: str) -> str | None: ... + @property + def content_length(self) -> int | None: ... + @property + def content_type(self) -> str | None: ... + +__all__ = ["HTTP2Request", "HTTP2Body"] diff --git a/stubs/gunicorn/gunicorn/http2/stream.pyi b/stubs/gunicorn/gunicorn/http2/stream.pyi new file mode 100644 index 000000000000..7f1aa378cd3f --- /dev/null +++ b/stubs/gunicorn/gunicorn/http2/stream.pyi @@ -0,0 +1,58 @@ +from _typeshed import Incomplete, ReadableBuffer +from collections.abc import Iterable +from enum import Enum +from io import BytesIO + +from gunicorn.http2.connection import HTTP2ServerConnection + +class StreamState(Enum): + IDLE = 1 + RESERVED_LOCAL = 2 + RESERVED_REMOTE = 3 + OPEN = 4 + HALF_CLOSED_LOCAL = 5 + HALF_CLOSED_REMOTE = 6 + CLOSED = 7 + +class HTTP2Stream: + stream_id: int + connection: HTTP2ServerConnection + state: StreamState + request_headers: list[tuple[str, Incomplete]] + request_body: BytesIO + request_complete: bool + response_started: bool + response_headers_sent: bool + response_complete: bool + window_size: int + trailers: list[tuple[str, Incomplete]] | None + response_trailers: list[tuple[str, Incomplete]] | None + priority_weight: int + priority_depends_on: int + priority_exclusive: bool + + def __init__(self, stream_id: int, connection: HTTP2ServerConnection) -> None: ... + @property + def is_client_stream(self) -> bool: ... + @property + def is_server_stream(self) -> bool: ... + @property + def can_receive(self) -> bool: ... + @property + def can_send(self) -> bool: ... + def receive_headers(self, headers: Iterable[tuple[str, Incomplete]], end_stream: bool | None = False) -> None: ... + def receive_data(self, data: ReadableBuffer, end_stream: bool | None = False) -> None: ... + def receive_trailers(self, trailers: list[tuple[str, Incomplete]]) -> None: ... + def send_headers(self, headers: Iterable[tuple[str, Incomplete]], end_stream: bool | None = False) -> None: ... + def send_data(self, data: ReadableBuffer, end_stream: bool | None = False) -> None: ... + def send_trailers(self, trailers: list[tuple[str, Incomplete]]) -> None: ... + def reset(self, error_code: int = 0x8) -> None: ... + def close(self) -> None: ... + def update_priority( + self, weight: int | None = None, depends_on: int | None = None, exclusive: bool | None = None + ) -> None: ... + def get_request_body(self) -> bytes: ... + def get_pseudo_headers(self) -> dict[str, Incomplete]: ... + def get_regular_headers(self) -> list[tuple[str, Incomplete]]: ... + +__all__ = ["HTTP2Stream", "StreamState"] diff --git a/stubs/gunicorn/gunicorn/sock.pyi b/stubs/gunicorn/gunicorn/sock.pyi index 5afa7ba185c6..0fd5963d0777 100644 --- a/stubs/gunicorn/gunicorn/sock.pyi +++ b/stubs/gunicorn/gunicorn/sock.pyi @@ -42,3 +42,5 @@ def create_sockets(conf: Config, log: GLogger, fds: Iterable[SupportsIndex] | No def close_sockets(listeners: Iterable[socket.socket], unlink: bool = True) -> None: ... def ssl_context(conf: Config) -> SSLContext: ... def ssl_wrap_socket(sock: socket.socket, conf: Config) -> SSLSocket: ... +def get_negotiated_protocol(ssl_socket: SSLSocket) -> str | None: ... +def is_http2_negotiated(ssl_socket: SSLSocket) -> bool: ... diff --git a/stubs/gunicorn/gunicorn/workers/__init__.pyi b/stubs/gunicorn/gunicorn/workers/__init__.pyi index 0223bc5118db..3976a8727e97 100644 --- a/stubs/gunicorn/gunicorn/workers/__init__.pyi +++ b/stubs/gunicorn/gunicorn/workers/__init__.pyi @@ -3,7 +3,7 @@ from typing import TypedDict, type_check_only @type_check_only class _SupportedWorkers(TypedDict): sync: str - eventlet: str + eventlet: str # deprecated: will be removed in 26.0 gevent: str gevent_wsgi: str gevent_pywsgi: str diff --git a/stubs/gunicorn/gunicorn/workers/base_async.pyi b/stubs/gunicorn/gunicorn/workers/base_async.pyi index 6b8523770077..8a24aaf53b67 100644 --- a/stubs/gunicorn/gunicorn/workers/base_async.pyi +++ b/stubs/gunicorn/gunicorn/workers/base_async.pyi @@ -1,6 +1,7 @@ import socket from gunicorn.http import Request +from gunicorn.http2.connection import HTTP2ServerConnection from gunicorn.workers import base from .._types import _AddressType @@ -14,4 +15,8 @@ class AsyncWorker(base.Worker): def timeout_ctx(self) -> None: ... def is_already_handled(self, respiter: object) -> bool: ... def handle(self, listener: socket.socket, client: socket.socket, addr: _AddressType) -> None: ... - def handle_request(self, listener_name: str, req: Request, sock: socket.socket, addr: _AddressType) -> bool: ... + def handle_http2(self, listener: socket.socket, client: socket.socket, addr: _AddressType) -> None: ... + def handle_http2_request( + self, listener_name: _AddressType, req: Request, sock: socket.socket, addr: _AddressType, h2_conn: HTTP2ServerConnection + ) -> None: ... + def handle_request(self, listener_name: _AddressType, req: Request, sock: socket.socket, addr: _AddressType) -> bool: ... diff --git a/stubs/gunicorn/gunicorn/workers/ggevent.pyi b/stubs/gunicorn/gunicorn/workers/ggevent.pyi index 4f16eaea8a44..6e2aa6da142d 100644 --- a/stubs/gunicorn/gunicorn/workers/ggevent.pyi +++ b/stubs/gunicorn/gunicorn/workers/ggevent.pyi @@ -22,7 +22,7 @@ class GeventWorker(AsyncWorker): def timeout_ctx(self) -> None: ... def run(self) -> None: ... def handle(self, listener: GeventSocket, client: GeventSocket, addr: _AddressType) -> None: ... - def handle_request(self, listener_name: str, req: Request, sock: GeventSocket, addr: _AddressType) -> bool: ... + def handle_request(self, listener_name: _AddressType, req: Request, sock: GeventSocket, addr: _AddressType) -> bool: ... def handle_quit(self, sig: int, frame: FrameType | None) -> None: ... def handle_usr1(self, sig: int, frame: FrameType | None) -> None: ... def init_process(self) -> None: ... diff --git a/stubs/gunicorn/gunicorn/workers/gthread.pyi b/stubs/gunicorn/gunicorn/workers/gthread.pyi index 5629df3c28c8..3c6c230bb340 100644 --- a/stubs/gunicorn/gunicorn/workers/gthread.pyi +++ b/stubs/gunicorn/gunicorn/workers/gthread.pyi @@ -8,7 +8,9 @@ from types import FrameType from gunicorn.config import Config from gunicorn.glogging import Logger as GLogger -from gunicorn.http import RequestParser +from gunicorn.http import Request, RequestParser +from gunicorn.http2.connection import HTTP2ServerConnection +from gunicorn.uwsgi.parser import UWSGIParser from .._types import _AddressType from . import base @@ -19,8 +21,9 @@ class TConn: client: _AddressType server: _AddressType timeout: float | None - parser: RequestParser | None + parser: HTTP2ServerConnection | UWSGIParser | RequestParser | None initialized: bool + is_http2: bool def __init__(self, cfg: Config, sock: socket.socket, client: _AddressType, server: _AddressType) -> None: ... def init(self) -> None: ... @@ -64,4 +67,6 @@ class ThreadWorker(base.Worker): def run(self) -> None: ... def finish_request(self, conn: TConn, fs: Future[bool]) -> None: ... def handle(self, conn: TConn) -> bool: ... - def handle_request(self, req: RequestParser, conn: TConn) -> bool: ... + def handle_http2(self, conn: TConn) -> bool: ... + def handle_http2_request(self, req: Request, conn: TConn, h2_conn: HTTP2ServerConnection) -> None: ... + def handle_request(self, req: Request, conn: TConn) -> bool: ...