[gunicorn] Update to 25.1.0 (#15425)

This commit is contained in:
Semyon Moroz
2026-02-14 12:12:50 +00:00
committed by GitHub
parent da3de3ac09
commit 6e7ee0eb95
13 changed files with 325 additions and 16 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
version = "25.0.3"
version = "25.1.0"
upstream_repository = "https://github.com/benoitc/gunicorn"
requires = ["types-gevent"]
+28
View File
@@ -1283,3 +1283,31 @@ class DirtyWorkerExit(Setting):
desc: ClassVar[str]
def dirty_worker_exit(arbiter: Arbiter, worker: Worker) -> None: ... # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues]
class ControlSocket(Setting):
name: ClassVar[str]
section: ClassVar[str]
cli: ClassVar[list[str]]
meta: ClassVar[str]
validator: ClassVar[_StringValidatorType]
default: ClassVar[str]
desc: ClassVar[str]
class ControlSocketMode(Setting):
name: ClassVar[str]
section: ClassVar[str]
cli: ClassVar[list[str]]
meta: ClassVar[str]
validator: ClassVar[_IntValidatorType]
type: ClassVar[Callable[[Any, str], int]]
default: ClassVar[int]
desc: ClassVar[str]
class ControlSocketDisable(Setting):
name: ClassVar[str]
section: ClassVar[str]
cli: ClassVar[list[str]]
validator: ClassVar[_BoolValidatorType]
action: ClassVar[str]
default: ClassVar[bool]
desc: ClassVar[str]
+5
View File
@@ -0,0 +1,5 @@
from gunicorn.ctl.client import ControlClient as ControlClient
from gunicorn.ctl.protocol import ControlProtocol as ControlProtocol
from gunicorn.ctl.server import ControlSocketServer as ControlSocketServer
__all__ = ["ControlSocketServer", "ControlClient", "ControlProtocol"]
+13
View File
@@ -0,0 +1,13 @@
from _typeshed import Incomplete
def format_workers(data: dict[str, Incomplete]) -> str: ...
def format_dirty(data: dict[str, Incomplete]) -> str: ...
def format_stats(data: dict[str, Incomplete]) -> str: ...
def format_listeners(data: dict[str, Incomplete]) -> str: ...
def format_config(data: dict[str, Incomplete]) -> str: ...
def format_help(data: dict[str, Incomplete]) -> str: ...
def format_all(data: dict[str, Incomplete]) -> str: ...
def format_response(command: str, data: dict[str, Incomplete]) -> str: ...
def run_command(socket_path: str, command: str, json_output: bool = False) -> int: ...
def run_interactive(socket_path: str, json_output: bool = False) -> int: ...
def main() -> int: ...
+16
View File
@@ -0,0 +1,16 @@
from _typeshed import Incomplete, Unused
from typing_extensions import Self
class ControlClientError(Exception): ...
class ControlClient:
socket_path: str
timeout: float
def __init__(self, socket_path: str, timeout: float = 30.0) -> None: ...
def connect(self) -> None: ...
def close(self) -> None: ...
def send_command(self, command: str, args: list[str] | None = None) -> dict[Incomplete, Incomplete]: ...
def __enter__(self) -> Self: ...
def __exit__(self, *args: Unused) -> None: ...
def parse_command(line: str) -> tuple[str, list[str]]: ...
+23
View File
@@ -0,0 +1,23 @@
from _typeshed import Incomplete
from gunicorn.arbiter import Arbiter
class CommandHandlers:
arbiter: Arbiter
def __init__(self, arbiter: Arbiter) -> None: ...
# TODO: Use TypedDict for next return types
def show_workers(self) -> dict[str, Incomplete]: ...
def show_dirty(self) -> dict[str, Incomplete]: ...
def show_config(self) -> dict[str, Incomplete]: ...
def show_stats(self) -> dict[str, Incomplete]: ...
def show_listeners(self) -> dict[str, Incomplete]: ...
def worker_add(self, count: int = 1) -> dict[str, Incomplete]: ...
def worker_remove(self, count: int = 1) -> dict[str, Incomplete]: ...
def worker_kill(self, pid: int) -> dict[str, Incomplete]: ...
def dirty_add(self, count: int = 1) -> dict[str, Incomplete]: ...
def dirty_remove(self, count: int = 1) -> dict[str, Incomplete]: ...
def reload(self) -> dict[str, Incomplete]: ...
def reopen(self) -> dict[str, Incomplete]: ...
def shutdown(self, mode: str = "graceful") -> dict[str, Incomplete]: ...
def show_all(self) -> dict[str, Incomplete]: ...
def help(self) -> dict[str, Incomplete]: ...
+26
View File
@@ -0,0 +1,26 @@
from _typeshed import Incomplete
from asyncio import StreamReader, StreamWriter
from socket import socket
from typing import ClassVar
class ProtocolError(Exception): ...
class ControlProtocol:
MAX_MESSAGE_SIZE: ClassVar[int]
@staticmethod
def encode_message(data: dict[Incomplete, Incomplete]) -> bytes: ...
@staticmethod
def decode_message(data: bytes) -> dict[Incomplete, Incomplete]: ...
@staticmethod
def read_message(sock: socket) -> dict[Incomplete, Incomplete]: ...
@staticmethod
def write_message(sock: socket, data: dict[Incomplete, Incomplete]) -> None: ...
@staticmethod
async def read_message_async(reader: StreamReader) -> dict[Incomplete, Incomplete]: ...
@staticmethod
async def write_message_async(writer: StreamWriter, data: dict[Incomplete, Incomplete]) -> None: ...
# TODO: Use TypedDict for next return types
def make_request(request_id: int, command: str, args: list[str] | None = None) -> dict[str, Incomplete]: ...
def make_response(request_id: int, data: dict[Incomplete, Incomplete] | None = None) -> dict[str, Incomplete]: ...
def make_error_response(request_id: int, error: str) -> dict[str, Incomplete]: ...
+11
View File
@@ -0,0 +1,11 @@
from gunicorn.arbiter import Arbiter
from gunicorn.ctl.handlers import CommandHandlers
class ControlSocketServer:
arbiter: Arbiter
socket_path: str
socket_mode: int
handlers: CommandHandlers
def __init__(self, arbiter: Arbiter, socket_path: str, socket_mode: int = 0o600) -> None: ...
def start(self) -> None: ...
def stop(self) -> None: ...
@@ -1,3 +1,4 @@
from . import stash as stash
from .app import DirtyApp as DirtyApp
from .arbiter import DirtyArbiter as DirtyArbiter
from .client import (
@@ -17,6 +18,13 @@ from .errors import (
DirtyTimeoutError as DirtyTimeoutError,
DirtyWorkerError as DirtyWorkerError,
)
from .stash import (
StashClient as StashClient,
StashError as StashError,
StashKeyNotFoundError as StashKeyNotFoundError,
StashTable as StashTable,
StashTableNotFoundError as StashTableNotFoundError,
)
__all__ = [
"DirtyError",
@@ -32,6 +40,12 @@ __all__ = [
"get_dirty_client_async",
"close_dirty_client",
"close_dirty_client_async",
"stash",
"StashClient",
"StashTable",
"StashError",
"StashTableNotFoundError",
"StashKeyNotFoundError",
"DirtyArbiter",
"set_dirty_socket_path",
]
+6 -1
View File
@@ -25,17 +25,22 @@ class DirtyArbiter:
worker_consumers: dict[int, asyncio.Task[None]]
worker_age: int
alive: bool
num_workers: int
app_specs: dict[str, dict[Incomplete, Incomplete]]
app_worker_map: dict[str, set[Incomplete]]
worker_app_map: dict[int, list[Incomplete]]
stash_tables: dict[str, dict[Incomplete, 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 handle_status_request(self, message: dict[str, Incomplete], client_writer: StreamWriter) -> None: ...
async def handle_manage_request(self, message: dict[str, Incomplete], client_writer: StreamWriter) -> None: ...
async def handle_stash_request(self, message: dict[str, Incomplete], client_writer: StreamWriter) -> None: ...
async def manage_workers(self) -> None: ...
def spawn_worker(self) -> int | None: ...
def spawn_worker(self, force_all_apps: bool = False) -> int | None: ...
def kill_worker(self, pid: int, sig: int) -> None: ...
async def murder_workers(self) -> None: ...
def reap_workers(self) -> None: ...
+86 -14
View File
@@ -1,10 +1,45 @@
import asyncio
import socket
from _typeshed import Incomplete
from typing import ClassVar
from typing import ClassVar, Final
class DirtyProtocol:
HEADER_FORMAT: ClassVar[str]
MAGIC: Final = b"GD"
VERSION: Final = 0x01
MSG_TYPE_REQUEST: Final = 0x01
MSG_TYPE_RESPONSE: Final = 0x02
MSG_TYPE_ERROR: Final = 0x03
MSG_TYPE_CHUNK: Final = 0x04
MSG_TYPE_END: Final = 0x05
MSG_TYPE_STASH: Final = 0x10
MSG_TYPE_STATUS: Final = 0x11
MSG_TYPE_MANAGE: Final = 0x12
MSG_TYPE_REQUEST_STR: Final = "request"
MSG_TYPE_RESPONSE_STR: Final = "response"
MSG_TYPE_ERROR_STR: Final = "error"
MSG_TYPE_CHUNK_STR: Final = "chunk"
MSG_TYPE_END_STR: Final = "end"
MSG_TYPE_STASH_STR: Final = "stash"
MSG_TYPE_STATUS_STR: Final = "status"
MSG_TYPE_MANAGE_STR: Final = "manage"
MSG_TYPE_TO_STR: Final[dict[int, str]]
MSG_TYPE_FROM_STR: Final[dict[str, int]]
STASH_OP_PUT: Final = 1
STASH_OP_GET: Final = 2
STASH_OP_DELETE: Final = 3
STASH_OP_KEYS: Final = 4
STASH_OP_CLEAR: Final = 5
STASH_OP_INFO: Final = 6
STASH_OP_ENSURE: Final = 7
STASH_OP_DELETE_TABLE: Final = 8
STASH_OP_TABLES: Final = 9
STASH_OP_EXISTS: Final = 10
MANAGE_OP_ADD: Final = 1
MANAGE_OP_REMOVE: Final = 2
HEADER_FORMAT: Final = ">2sBBIQ"
HEADER_SIZE: Final[int]
MAX_MESSAGE_SIZE: Final = 67108864
class BinaryProtocol:
HEADER_SIZE: ClassVar[int]
MAX_MESSAGE_SIZE: ClassVar[int]
MSG_TYPE_REQUEST: ClassVar[str]
@@ -12,29 +47,66 @@ class DirtyProtocol:
MSG_TYPE_ERROR: ClassVar[str]
MSG_TYPE_CHUNK: ClassVar[str]
MSG_TYPE_END: ClassVar[str]
MSG_TYPE_STASH: ClassVar[str]
MSG_TYPE_STATUS: ClassVar[str]
MSG_TYPE_MANAGE: ClassVar[str]
@staticmethod
def encode(message: dict[Incomplete, Incomplete]) -> bytes: ...
def encode_header(msg_type: int, request_id: int, payload_length: int) -> bytes: ...
@staticmethod
def decode(data: bytes) -> dict[Incomplete, Incomplete]: ...
def decode_header(data: bytes) -> tuple[int, int, int]: ...
@staticmethod
async def read_message_async(reader: asyncio.StreamReader) -> dict[Incomplete, Incomplete]: ...
def encode_request(
request_id: int,
app_path: str,
action: str,
args: tuple[Incomplete, ...] | None = None,
kwargs: dict[str, Incomplete] | None = None,
) -> bytes: ...
@staticmethod
async def write_message_async(writer: asyncio.StreamWriter, message: dict[Incomplete, Incomplete]) -> None: ...
def encode_response(request_id: int, result) -> bytes: ...
@staticmethod
def read_message(sock: socket.socket) -> dict[Incomplete, Incomplete]: ...
def encode_error(request_id: int, error: BaseException | dict[str, Incomplete]) -> bytes: ...
@staticmethod
def write_message(sock: socket.socket, message: dict[Incomplete, Incomplete]) -> None: ...
def encode_chunk(request_id: int, data) -> bytes: ...
@staticmethod
def encode_end(request_id: int) -> bytes: ...
@staticmethod
def encode_status(request_id: int) -> bytes: ...
@staticmethod
def encode_manage(request_id: int, op: int, count: int = 1) -> bytes: ...
@staticmethod
def encode_stash(request_id: int, op: int, table: str, key=None, value=None, pattern=None) -> bytes: ...
@staticmethod
def decode_message(data: bytes) -> tuple[str, int, Incomplete]: ...
@staticmethod
async def read_message_async(reader: asyncio.StreamReader) -> dict[str, Incomplete]: ...
@staticmethod
async def write_message_async(writer: asyncio.StreamWriter, message: dict[str, Incomplete]) -> None: ...
@staticmethod
def _recv_exactly(sock: socket.socket, n: int) -> bytes: ...
@staticmethod
def read_message(sock: socket.socket) -> dict[str, Incomplete]: ...
@staticmethod
def write_message(sock: socket.socket, message: dict[str, Incomplete]) -> None: ...
@staticmethod
def _encode_from_dict(message: dict[str, Incomplete]) -> bytes: ...
DirtyProtocol = BinaryProtocol
# TODO: Use TypedDict for results
def make_request(
request_id: str,
request_id: int | 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]: ...
def make_response(request_id: int | str, result) -> dict[str, Incomplete]: ...
def make_error_response(request_id: int | str, error) -> dict[str, Incomplete]: ...
def make_chunk_message(request_id: int | str, data) -> dict[str, Incomplete]: ...
def make_end_message(request_id: int | str) -> dict[str, Incomplete]: ...
def make_stash_message(
request_id: int | str, op: int, table: str, key=None, value=None, pattern=None
) -> dict[str, Incomplete]: ...
def make_manage_message(request_id: int | str, op: int, count: int = 1) -> dict[str, Incomplete]: ...
+71
View File
@@ -0,0 +1,71 @@
from _typeshed import Incomplete
from collections.abc import Iterator
from types import TracebackType
from typing_extensions import Self
from .errors import DirtyError
class StashError(DirtyError): ...
class StashTableNotFoundError(StashError):
table_name: str
def __init__(self, table_name: str) -> None: ...
class StashKeyNotFoundError(StashError):
table_name: str
key: str
def __init__(self, table_name: str, key: str) -> None: ...
class StashClient:
socket_path: str
timeout: float
def __init__(self, socket_path: str, timeout: float = 30.0) -> None: ...
def put(self, table: str, key: str, value) -> None: ...
def get(self, table: str, key: str, default=None): ...
def delete(self, table: str, key: str) -> bool: ...
def keys(self, table: str, pattern: str | None = None) -> list[str]: ...
def clear(self, table: str) -> None: ...
def info(self, table: str) -> dict[str, Incomplete]: ...
def ensure(self, table: str) -> None: ...
def exists(self, table: str, key=None) -> bool: ...
def delete_table(self, table: str) -> None: ...
def tables(self) -> list[str]: ...
def table(self, name: str) -> StashTable: ...
def close(self) -> None: ...
def __enter__(self) -> Self: ...
def __exit__(
self, exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None
) -> None: ...
class StashTable:
def __init__(self, client: StashClient, name: str) -> None: ...
@property
def name(self) -> str: ...
def __getitem__(self, key: str): ...
def __setitem__(self, key: str, value) -> None: ...
def __delitem__(self, key: str) -> None: ...
def __contains__(self, key: str) -> bool: ...
def __iter__(self) -> Iterator[str]: ...
def __len__(self) -> int: ...
def get(self, key: str, default=None): ...
def keys(self, pattern: str | None = None) -> list[str]: ...
def clear(self) -> None: ...
def items(self) -> Iterator[tuple[Incomplete, Incomplete]]: ...
def values(self) -> Iterator[Incomplete]: ...
def set_stash_socket_path(path: str) -> None: ...
def get_stash_socket_path() -> str: ...
def put(table: str, key: str, value) -> None: ...
def get(table: str, key: str, default=None): ...
def delete(table: str, key: str) -> bool: ...
def keys(table: str, pattern: str | None = None) -> list[str]: ...
def clear(table: str) -> None: ...
def info(table: str) -> dict[str, Incomplete]: ...
def ensure(table: str) -> None: ...
def exists(table: str, key: str | None = None) -> bool: ...
def delete_table(table: str) -> None: ...
def tables() -> list[str]: ...
def table(name: str) -> StashTable: ...
+25
View File
@@ -0,0 +1,25 @@
from _typeshed import Incomplete
from typing import Final
TYPE_NONE: Final = 0x00
TYPE_BOOL: Final = 0x01
TYPE_INT64: Final = 0x05
TYPE_FLOAT64: Final = 0x06
TYPE_BYTES: Final = 0x10
TYPE_STRING: Final = 0x11
TYPE_LIST: Final = 0x20
TYPE_DICT: Final = 0x21
MAX_STRING_SIZE: Final = 67108864
MAX_BYTES_SIZE: Final = 67108864
MAX_LIST_SIZE: Final = 1048576
MAX_DICT_SIZE: Final = 1048576
class TLVEncoder:
@staticmethod
def encode(
value: bool | float | str | bytes | list[Incomplete] | tuple[Incomplete, ...] | dict[object, Incomplete] | None,
) -> bytes: ... # dict key passed to `str()` function
@staticmethod
def decode(data: bytes, offset: int = 0) -> tuple[Incomplete, int]: ...
@staticmethod
def decode_full(data: bytes): ...