[gunicorn] Update to 25.0.* (#15363)

This commit is contained in:
Semyon Moroz
2026-02-03 09:13:33 +00:00
committed by GitHub
parent e79e6af99c
commit 11ff7e1126
26 changed files with 828 additions and 27 deletions
+1
View File
@@ -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",
+1 -1
View File
@@ -1,4 +1,4 @@
version = "24.1.*"
version = "25.0.*"
upstream_repository = "https://github.com/benoitc/gunicorn"
requires = ["types-gevent"]
+8
View File
@@ -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: ...
+1 -1
View File
@@ -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: ...
+38
View File
@@ -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: ...
+162 -18
View File
@@ -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]
@@ -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",
]
+17
View File
@@ -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: ...
+43
View File
@@ -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: ...
+74
View File
@@ -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: ...
+55
View File
@@ -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: ...
@@ -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]: ...
+36
View File
@@ -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
+11 -1
View File
@@ -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"]
+5
View File
@@ -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]
@@ -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",
]
@@ -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"]
@@ -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"]
+70
View File
@@ -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",
]
+49
View File
@@ -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"]
+58
View File
@@ -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"]
+2
View File
@@ -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: ...
+1 -1
View File
@@ -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
@@ -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: ...
+1 -1
View File
@@ -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: ...
+8 -3
View File
@@ -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: ...