[gunicorn] Update to 25.2.0 (#15555)

This commit is contained in:
Semyon Moroz
2026-03-26 00:34:25 +04:00
committed by GitHub
parent 1bf663e749
commit 82533e5fcd
12 changed files with 188 additions and 13 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
version = "25.1.0"
version = "25.2.0"
upstream_repository = "https://github.com/benoitc/gunicorn"
requires = ["types-gevent"]
+109
View File
@@ -0,0 +1,109 @@
from collections.abc import Callable, Iterable
from typing import Any, Literal, SupportsIndex
from typing_extensions import Self, TypeAlias
_H1CProtocol: TypeAlias = Any # gunicorn_h1c H1CProtocol class
class ParseError(Exception): ...
class LimitRequestLine(ParseError): ...
class LimitRequestHeaders(ParseError): ...
class InvalidRequestMethod(ParseError): ...
class InvalidHTTPVersion(ParseError): ...
class InvalidHeaderName(ParseError): ...
class InvalidHeader(ParseError): ...
class PythonProtocol:
__slots__ = (
"_on_message_begin",
"_on_url",
"_on_header",
"_on_headers_complete",
"_on_body",
"_on_message_complete",
"_state",
"_buffer",
"_headers_list",
"method",
"path",
"http_version",
"headers",
"content_length",
"is_chunked",
"should_keep_alive",
"is_complete",
"_body_remaining",
"_skip_body",
"_chunk_state",
"_chunk_size",
"_chunk_remaining",
"_limit_request_line",
"_limit_request_fields",
"_limit_request_field_size",
"_permit_unconventional_http_method",
"_permit_unconventional_http_version",
"_header_count",
)
method: bytes | None
path: bytes | None
http_version: tuple[int, int] | None
headers: list[tuple[bytes, bytes]]
content_length: int | None
is_chunked: bool
should_keep_alive: bool
is_complete: bool
def __init__(
self,
on_message_begin: Callable[[], object] | None = None,
on_url: Callable[[bytes], object] | None = None,
on_header: Callable[[bytes, bytes], object] | None = None,
on_headers_complete: Callable[[], bool] | None = None,
on_body: Callable[[bytes], object] | None = None,
on_message_complete: Callable[[], object] | None = None,
limit_request_line: int = 8190,
limit_request_fields: int = 100,
limit_request_field_size: int = 8190,
permit_unconventional_http_method: bool = False,
permit_unconventional_http_version: bool = False,
) -> None: ...
def feed(self, data: Iterable[SupportsIndex]) -> None: ...
def reset(self) -> None: ...
class CallbackRequest:
__slots__ = (
"method",
"uri",
"path",
"query",
"fragment",
"version",
"headers",
"headers_bytes",
"scheme",
"raw_path",
"content_length",
"chunked",
"must_close",
"proxy_protocol_info",
"_expect_100_continue",
)
method: str | None
uri: str | None
path: str | None
query: str | None
fragment: str | None
version: tuple[int, int] | None
headers: list[tuple[str, str]]
headers_bytes: list[tuple[bytes, bytes]]
scheme: Literal["https", "http"]
raw_path: bytes
content_length: int
chunked: bool
must_close: bool
proxy_protocol_info: dict[str, str | int | None] | None # TODO: Use TypedDict
def __init__(self) -> None: ...
@classmethod
def from_parser(cls, parser: _H1CProtocol | PythonProtocol, is_ssl: bool = False) -> Self: ...
def should_close(self) -> bool: ...
def get_header(self, name: str) -> str | None: ...
+28
View File
@@ -1,12 +1,29 @@
import asyncio
from _typeshed import Incomplete
from collections.abc import Iterable
from typing import Final
from gunicorn.asgi.parser import CallbackRequest
from gunicorn.config import Config
from gunicorn.glogging import Logger as GLogger
from gunicorn.workers.gasgi import ASGIWorker
from .._types import _ASGIAppType
HIGH_WATER_LIMIT: Final = 65536
class FlowControl:
__slots__ = ("_transport", "read_paused", "write_paused", "_is_writable_event")
read_paused: bool
write_paused: bool
def __init__(self, transport: asyncio.BaseTransport) -> None: ...
async def drain(self) -> None: ...
def pause_reading(self) -> None: ...
def resume_reading(self) -> None: ...
def pause_writing(self) -> None: ...
def resume_writing(self) -> None: ...
class ASGIResponseInfo:
status: str | int
sent: int
@@ -14,6 +31,17 @@ class ASGIResponseInfo:
def __init__(self, status: str | int, headers: Iterable[tuple[str | bytes, str | bytes]], sent: int) -> None: ...
class BodyReceiver:
__slots__ = ("_chunks", "_complete", "_body_finished", "_closed", "_waiter", "request", "protocol")
request: CallbackRequest
protocol: ASGIProtocol
def __init__(self, request: CallbackRequest, protocol: ASGIProtocol) -> None: ...
def feed(self, chunk: bytes) -> None: ...
def set_complete(self) -> None: ...
def signal_disconnect(self) -> None: ...
async def receive(self) -> dict[str, Incomplete]: ... # TODO: Use TypedDict
class ASGIProtocol(asyncio.Protocol):
worker: ASGIWorker
cfg: Config
+3 -4
View File
@@ -26,7 +26,6 @@ WS_GUID: Final = b"258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
class WebSocketProtocol:
transport: asyncio.Transport
reader: asyncio.StreamReader
scope: _ScopeType
app: _ASGIAppType
log: GLogger
@@ -35,7 +34,7 @@ class WebSocketProtocol:
close_code: int | None
close_reason: str | None
def __init__(
self, transport: asyncio.Transport, reader: asyncio.StreamReader, scope: _ScopeType, app: _ASGIAppType, log: GLogger
) -> None: ...
def __init__(self, transport: asyncio.Transport, scope: _ScopeType, app: _ASGIAppType, log: GLogger) -> None: ...
def feed_data(self, data: bytes) -> None: ...
def feed_eof(self) -> None: ...
async def run(self) -> None: ...
+13
View File
@@ -76,6 +76,7 @@ _ASGILoopValidatorType: TypeAlias = Callable[[str | None], str]
_ASGILifespanValidatorType: TypeAlias = Callable[[str | None], str]
_HTTP2FrameSizeValidatorType: TypeAlias = Callable[[ConvertibleToInt], int]
_HTTPProtocolsValidatorType: TypeAlias = Callable[[str | None], list[str]]
_HttpParserValidatorType: TypeAlias = Callable[[str | None], str]
_ValidatorType: TypeAlias = ( # noqa: Y047
_BoolValidatorType
@@ -92,6 +93,7 @@ _ValidatorType: TypeAlias = ( # noqa: Y047
| _ASGILifespanValidatorType
| _HTTP2FrameSizeValidatorType
| _HTTPProtocolsValidatorType
| _HttpParserValidatorType
)
KNOWN_SETTINGS: list[Setting]
@@ -1156,6 +1158,7 @@ class HeaderMap(Setting):
def validate_asgi_loop(val: str | None) -> str: ...
def validate_asgi_lifespan(val: str | None) -> str: ...
def validate_http_parser(val: str | None) -> str: ...
class ASGILoop(Setting):
name: ClassVar[str]
@@ -1185,6 +1188,15 @@ class ASGIDisconnectGracePeriod(Setting):
default: ClassVar[int]
desc: ClassVar[str]
class HttpParser(Setting):
name: ClassVar[str]
section: ClassVar[str]
cli: ClassVar[list[str]]
meta: ClassVar[str]
validator: ClassVar[_HttpParserValidatorType]
default: ClassVar[str]
desc: ClassVar[str]
class RootPath(Setting):
name: ClassVar[str]
section: ClassVar[str]
@@ -1291,6 +1303,7 @@ class ControlSocket(Setting):
meta: ClassVar[str]
validator: ClassVar[_StringValidatorType]
default: ClassVar[str]
default_doc: ClassVar[str]
desc: ClassVar[str]
class ControlSocketMode(Setting):
+2
View File
@@ -151,6 +151,8 @@ class Logger:
extra: Mapping[str, object] | None = None,
) -> None: ...
def atoms(self, resp: Response, req: Request, environ: _EnvironType, request_time: timedelta) -> _AtomsDict: ...
@property
def access_log_enabled(self) -> bool: ...
def access(self, resp: Response, req: Request, environ: _EnvironType, request_time: timedelta) -> None: ...
def now(self) -> str: ...
def reopen_files(self) -> None: ...
+2 -2
View File
@@ -70,9 +70,9 @@ class ChunkMissingTerminator(IOError):
class LimitRequestLine(ParseException):
size: int
max_size: int
max_size: int | None
def __init__(self, size: int, max_size: int) -> None: ...
def __init__(self, size: int, max_size: int | None = None) -> None: ...
class LimitRequestHeaders(ParseException):
msg: str
+16 -4
View File
@@ -2,9 +2,10 @@ import io
import logging
import re
import socket
from _typeshed import ReadableBuffer
from _typeshed import ReadableBuffer, Unused
from collections.abc import Callable
from typing import Any, Final
from typing import Any, Final, Protocol, type_check_only
from typing_extensions import Self
from gunicorn.config import Config
from gunicorn.http import Request
@@ -15,13 +16,24 @@ BLKSIZE: Final = 0x3FFFFFFF
HEADER_VALUE_RE: Final[re.Pattern[str]]
log: logging.Logger
@type_check_only
class _FileLikeProtocol(Protocol):
def read(self, size: int, /) -> bytes: ...
def seek(self, offset: int, /) -> object: ...
# optional fields:
# def close(self) -> None: ...
# def fileno(self) -> int: ...
class FileWrapper:
filelike: io.IOBase
blksize: int
close: Callable[[], None] | None
def __init__(self, filelike: io.IOBase, blksize: int = 8192) -> None: ...
def __getitem__(self, key: Any) -> bytes: ...
def __init__(self, filelike: _FileLikeProtocol, blksize: int = 8192) -> None: ...
def __getitem__(self, key: Unused) -> bytes: ...
def __iter__(self) -> Self: ...
def __next__(self) -> bytes: ...
class WSGIErrorsWrapper(io.RawIOBase):
streams: list[io.TextIOBase]
+1
View File
@@ -52,6 +52,7 @@ class HTTP2Stream:
self, weight: int | None = None, depends_on: int | None = None, exclusive: bool | None = None
) -> None: ...
def get_request_body(self) -> bytes: ...
async def read_body_chunk(self) -> bytes | None: ...
def get_pseudo_headers(self) -> dict[str, Incomplete]: ...
def get_regular_headers(self) -> list[tuple[str, Incomplete]]: ...
+3
View File
@@ -1,6 +1,9 @@
from _typeshed import StrOrBytesPath
class Pidfile:
fname: StrOrBytesPath
pid: int | None
def __init__(self, fname: StrOrBytesPath) -> None: ...
def create(self, pid: int) -> None: ...
def rename(self, path: StrOrBytesPath) -> None: ...
+2 -2
View File
@@ -1,5 +1,5 @@
import types
from _typeshed import FileDescriptorLike, FileDescriptorOrPath, HasFileno, StrOrBytesPath
from _typeshed import FileDescriptorLike, FileDescriptorOrPath, StrOrBytesPath
from inspect import _IntrospectableCallable, _ParameterKind
from socket import socket
from typing import Any, Literal, NoReturn
@@ -39,7 +39,7 @@ def daemonize(enable_stdio_inheritance: bool = False) -> None: ...
def seed() -> None: ...
def check_is_writable(path: FileDescriptorOrPath) -> None: ...
def to_bytestring(value: str | bytes, encoding: str = "utf8") -> bytes: ...
def has_fileno(obj: HasFileno) -> bool: ...
def has_fileno(obj: object) -> bool: ...
def warn(msg: str) -> None: ...
def make_fail_app(msg: str) -> _WSGIAppType: ...
def split_request_uri(uri: str) -> SplitResult: ...
@@ -5,6 +5,7 @@ from collections.abc import Callable
from concurrent.futures import Future, ThreadPoolExecutor
from selectors import DefaultSelector
from types import FrameType
from typing import Final
from gunicorn.config import Config
from gunicorn.glogging import Logger as GLogger
@@ -15,6 +16,8 @@ from gunicorn.uwsgi.parser import UWSGIParser
from .._types import _AddressType
from . import base
DEFAULT_WORKER_DATA_TIMEOUT: Final = 5.0
class TConn:
cfg: Config
sock: socket.socket
@@ -24,10 +27,12 @@ class TConn:
parser: HTTP2ServerConnection | UWSGIParser | RequestParser | None
initialized: bool
is_http2: bool
data_ready: bool
def __init__(self, cfg: Config, sock: socket.socket, client: _AddressType, server: _AddressType) -> None: ...
def init(self) -> None: ...
def set_timeout(self) -> None: ...
def wait_for_data(self, timeout: float | None) -> bool: ...
def close(self) -> None: ...
class PollableMethodQueue:
@@ -48,6 +53,7 @@ class ThreadWorker(base.Worker):
poller: DefaultSelector
method_queue: PollableMethodQueue
keepalived_conns: deque[TConn]
pending_conns: deque[TConn]
nr_conns: int
alive: bool
@@ -61,7 +67,9 @@ class ThreadWorker(base.Worker):
def enqueue_req(self, conn: TConn) -> None: ...
def accept(self, listener: socket.socket) -> None: ...
def on_client_socket_readable(self, conn: TConn, client: socket.socket) -> None: ...
def on_pending_socket_readable(self, conn: TConn, client: socket.socket) -> None: ...
def murder_keepalived(self) -> None: ...
def murder_pending(self) -> None: ...
def is_parent_alive(self) -> bool: ...
def wait_for_and_dispatch_events(self, timeout: float | None) -> None: ...
def run(self) -> None: ...