Voice et bot modif

This commit is contained in:
pi 2026-06-16 17:09:34 +00:00
parent 189d56026b
commit 7333a22bcd
10774 changed files with 634644 additions and 933308 deletions

View file

@ -9,20 +9,7 @@ import warnings
from abc import ABC, abstractmethod
from collections.abc import Iterable
from itertools import chain
from typing import (
IO,
TYPE_CHECKING,
Any,
Dict,
Final,
List,
Optional,
Set,
TextIO,
Tuple,
Type,
Union,
)
from typing import IO, TYPE_CHECKING, Any, Final, TextIO
from multidict import CIMultiDict
@ -30,13 +17,15 @@ from . import hdrs
from .abc import AbstractStreamWriter
from .helpers import (
_SENTINEL,
DEFAULT_CHUNK_SIZE,
content_disposition_header,
guess_filename,
parse_mimetype,
sentinel,
)
from .http_writer import _safe_header
from .streams import StreamReader
from .typedefs import JSONEncoder, _CIMultiDict
from .typedefs import JSONBytesEncoder, JSONEncoder, _CIMultiDict
__all__ = (
"PAYLOAD_REGISTRY",
@ -51,12 +40,12 @@ __all__ = (
"TextIOPayload",
"StringIOPayload",
"JsonPayload",
"JsonBytesPayload",
"AsyncIterablePayload",
)
TOO_LARGE_BYTES_BODY: Final[int] = 2**20 # 1 MB
READ_SIZE: Final[int] = 2**16 # 64 KB
_CLOSE_FUTURES: Set[asyncio.Future[None]] = set()
_CLOSE_FUTURES: set[asyncio.Future[None]] = set()
class LookupError(Exception):
@ -74,7 +63,7 @@ def get_payload(data: Any, *args: Any, **kwargs: Any) -> "Payload":
def register_payload(
factory: Type["Payload"], type: Any, *, order: Order = Order.normal
factory: type["Payload"], type: Any, *, order: Order = Order.normal
) -> None:
PAYLOAD_REGISTRY.register(factory, type, order=order)
@ -84,13 +73,13 @@ class payload_type:
self.type = type
self.order = order
def __call__(self, factory: Type["Payload"]) -> Type["Payload"]:
def __call__(self, factory: type["Payload"]) -> type["Payload"]:
register_payload(factory, self.type, order=self.order)
return factory
PayloadType = Type["Payload"]
_PayloadRegistryItem = Tuple[PayloadType, Any]
PayloadType = type["Payload"]
_PayloadRegistryItem = tuple[PayloadType, Any]
class PayloadRegistry:
@ -102,16 +91,16 @@ class PayloadRegistry:
__slots__ = ("_first", "_normal", "_last", "_normal_lookup")
def __init__(self) -> None:
self._first: List[_PayloadRegistryItem] = []
self._normal: List[_PayloadRegistryItem] = []
self._last: List[_PayloadRegistryItem] = []
self._normal_lookup: Dict[Any, PayloadType] = {}
self._first: list[_PayloadRegistryItem] = []
self._normal: list[_PayloadRegistryItem] = []
self._last: list[_PayloadRegistryItem] = []
self._normal_lookup: dict[Any, PayloadType] = {}
def get(
self,
data: Any,
*args: Any,
_CHAIN: "Type[chain[_PayloadRegistryItem]]" = chain,
_CHAIN: "type[chain[_PayloadRegistryItem]]" = chain,
**kwargs: Any,
) -> "Payload":
if self._first:
@ -151,19 +140,19 @@ class PayloadRegistry:
class Payload(ABC):
_default_content_type: str = "application/octet-stream"
_size: Optional[int] = None
_size: int | None = None
_consumed: bool = False # Default: payload has not been consumed yet
_autoclose: bool = False # Default: assume resource needs explicit closing
def __init__(
self,
value: Any,
headers: Optional[
Union[_CIMultiDict, Dict[str, str], Iterable[Tuple[str, str]]]
] = None,
content_type: Union[str, None, _SENTINEL] = sentinel,
filename: Optional[str] = None,
encoding: Optional[str] = None,
headers: (
_CIMultiDict | dict[str, str] | Iterable[tuple[str, str]] | None
) = None,
content_type: str | None | _SENTINEL = sentinel,
filename: str | None = None,
encoding: str | None = None,
**kwargs: Any,
) -> None:
self._encoding = encoding
@ -187,7 +176,7 @@ class Payload(ABC):
self._headers.update(headers)
@property
def size(self) -> Optional[int]:
def size(self) -> int | None:
"""Size of the payload in bytes.
Returns the number of bytes that will be transmitted when the payload
@ -197,7 +186,7 @@ class Payload(ABC):
return self._size
@property
def filename(self) -> Optional[str]:
def filename(self) -> str | None:
"""Filename of the payload."""
return self._filename
@ -209,14 +198,15 @@ class Payload(ABC):
@property
def _binary_headers(self) -> bytes:
return (
"".join([k + ": " + v + "\r\n" for k, v in self.headers.items()]).encode(
"utf-8"
)
"".join(
_safe_header(k) + ": " + _safe_header(v) + "\r\n"
for k, v in self.headers.items()
).encode("utf-8")
+ b"\r\n"
)
@property
def encoding(self) -> Optional[str]:
def encoding(self) -> str | None:
"""Payload encoding"""
return self._encoding
@ -283,7 +273,7 @@ class Payload(ABC):
# write_with_length is new in aiohttp 3.12
# it should be overridden by subclasses
async def write_with_length(
self, writer: AbstractStreamWriter, content_length: Optional[int]
self, writer: AbstractStreamWriter, content_length: int | None
) -> None:
"""
Write payload with a specific content length constraint.
@ -353,7 +343,7 @@ class BytesPayload(Payload):
_autoclose = True # No file handle, just bytes in memory
def __init__(
self, value: Union[bytes, bytearray, memoryview], *args: Any, **kwargs: Any
self, value: bytes | bytearray | memoryview, *args: Any, **kwargs: Any
) -> None:
if "content_type" not in kwargs:
kwargs["content_type"] = "application/octet-stream"
@ -407,7 +397,7 @@ class BytesPayload(Payload):
await writer.write(self._value)
async def write_with_length(
self, writer: AbstractStreamWriter, content_length: Optional[int]
self, writer: AbstractStreamWriter, content_length: int | None
) -> None:
"""
Write bytes payload with a specific content length constraint.
@ -432,8 +422,8 @@ class StringPayload(BytesPayload):
self,
value: str,
*args: Any,
encoding: Optional[str] = None,
content_type: Optional[str] = None,
encoding: str | None = None,
content_type: str | None = None,
**kwargs: Any,
) -> None:
@ -466,7 +456,7 @@ class StringIOPayload(StringPayload):
class IOBasePayload(Payload):
_value: io.IOBase
# _consumed = False (inherited) - File can be re-read from the same position
_start_position: Optional[int] = None
_start_position: int | None = None
# _autoclose = False (inherited) - Has file handle that needs explicit closing
def __init__(
@ -496,14 +486,14 @@ class IOBasePayload(Payload):
self._consumed = True
def _read_and_available_len(
self, remaining_content_len: Optional[int]
) -> Tuple[Optional[int], bytes]:
self, remaining_content_len: int | None
) -> tuple[int | None, bytes]:
"""
Read the file-like object and return both its total size and the first chunk.
Args:
remaining_content_len: Optional limit on how many bytes to read in this operation.
If None, READ_SIZE will be used as the default chunk size.
If None, DEFAULT_CHUNK_SIZE will be used as the default chunk size.
Returns:
A tuple containing:
@ -518,16 +508,20 @@ class IOBasePayload(Payload):
self._set_or_restore_start_position()
size = self.size # Call size only once since it does I/O
return size, self._value.read(
min(READ_SIZE, size or READ_SIZE, remaining_content_len or READ_SIZE)
min(
DEFAULT_CHUNK_SIZE,
size or DEFAULT_CHUNK_SIZE,
remaining_content_len or DEFAULT_CHUNK_SIZE,
)
)
def _read(self, remaining_content_len: Optional[int]) -> bytes:
def _read(self, remaining_content_len: int | None) -> bytes:
"""
Read a chunk of data from the file-like object.
Args:
remaining_content_len: Optional maximum number of bytes to read.
If None, READ_SIZE will be used as the default chunk size.
If None, DEFAULT_CHUNK_SIZE will be used as the default chunk size.
Returns:
A chunk of bytes read from the file object, respecting the
@ -537,10 +531,10 @@ class IOBasePayload(Payload):
the initial _read_and_available_len call has been made.
"""
return self._value.read(remaining_content_len or READ_SIZE) # type: ignore[no-any-return]
return self._value.read(remaining_content_len or DEFAULT_CHUNK_SIZE) # type: ignore[no-any-return]
@property
def size(self) -> Optional[int]:
def size(self) -> int | None:
"""
Size of the payload in bytes.
@ -586,7 +580,7 @@ class IOBasePayload(Payload):
await self.write_with_length(writer, None)
async def write_with_length(
self, writer: AbstractStreamWriter, content_length: Optional[int]
self, writer: AbstractStreamWriter, content_length: int | None
) -> None:
"""
Write file-like payload with a specific content length constraint.
@ -640,17 +634,17 @@ class IOBasePayload(Payload):
None,
self._read,
(
min(READ_SIZE, remaining_content_len)
min(DEFAULT_CHUNK_SIZE, remaining_content_len)
if remaining_content_len is not None
else READ_SIZE
else DEFAULT_CHUNK_SIZE
),
)
def _should_stop_writing(
self,
available_len: Optional[int],
available_len: int | None,
total_written_len: int,
remaining_content_len: Optional[int],
remaining_content_len: int | None,
) -> bool:
"""
Determine if we should stop writing data.
@ -736,8 +730,8 @@ class TextIOPayload(IOBasePayload):
self,
value: TextIO,
*args: Any,
encoding: Optional[str] = None,
content_type: Optional[str] = None,
encoding: str | None = None,
content_type: str | None = None,
**kwargs: Any,
) -> None:
@ -761,14 +755,14 @@ class TextIOPayload(IOBasePayload):
)
def _read_and_available_len(
self, remaining_content_len: Optional[int]
) -> Tuple[Optional[int], bytes]:
self, remaining_content_len: int | None
) -> tuple[int | None, bytes]:
"""
Read the text file-like object and return both its total size and the first chunk.
Args:
remaining_content_len: Optional limit on how many bytes to read in this operation.
If None, READ_SIZE will be used as the default chunk size.
If None, DEFAULT_CHUNK_SIZE will be used as the default chunk size.
Returns:
A tuple containing:
@ -787,17 +781,21 @@ class TextIOPayload(IOBasePayload):
self._set_or_restore_start_position()
size = self.size
chunk = self._value.read(
min(READ_SIZE, size or READ_SIZE, remaining_content_len or READ_SIZE)
min(
DEFAULT_CHUNK_SIZE,
size or DEFAULT_CHUNK_SIZE,
remaining_content_len or DEFAULT_CHUNK_SIZE,
)
)
return size, chunk.encode(self._encoding) if self._encoding else chunk.encode()
def _read(self, remaining_content_len: Optional[int]) -> bytes:
def _read(self, remaining_content_len: int | None) -> bytes:
"""
Read a chunk of data from the text file-like object.
Args:
remaining_content_len: Optional maximum number of bytes to read.
If None, READ_SIZE will be used as the default chunk size.
If None, DEFAULT_CHUNK_SIZE will be used as the default chunk size.
Returns:
A chunk of bytes read from the file object and encoded using the payload's
@ -809,7 +807,7 @@ class TextIOPayload(IOBasePayload):
the specified encoding (or UTF-8 if none was provided).
"""
chunk = self._value.read(remaining_content_len or READ_SIZE)
chunk = self._value.read(remaining_content_len or DEFAULT_CHUNK_SIZE)
return chunk.encode(self._encoding) if self._encoding else chunk.encode()
def decode(self, encoding: str = "utf-8", errors: str = "strict") -> str:
@ -869,7 +867,7 @@ class BytesIOPayload(IOBasePayload):
return await self.write_with_length(writer, None)
async def write_with_length(
self, writer: AbstractStreamWriter, content_length: Optional[int]
self, writer: AbstractStreamWriter, content_length: int | None
) -> None:
"""
Write BytesIO payload with a specific content length constraint.
@ -893,7 +891,7 @@ class BytesIOPayload(IOBasePayload):
self._set_or_restore_start_position()
loop_count = 0
remaining_bytes = content_length
while chunk := self._value.read(READ_SIZE):
while chunk := self._value.read(DEFAULT_CHUNK_SIZE):
if loop_count > 0:
# Avoid blocking the event loop
# if they pass a large BytesIO object
@ -956,8 +954,31 @@ class JsonPayload(BytesPayload):
)
class JsonBytesPayload(BytesPayload):
"""JSON payload for encoders that return bytes directly.
Use this when your JSON encoder (like orjson) returns bytes
instead of str, avoiding the encode/decode overhead.
"""
def __init__(
self,
value: Any,
dumps: JSONBytesEncoder,
content_type: str = "application/json",
*args: Any,
**kwargs: Any,
) -> None:
super().__init__(
dumps(value),
content_type=content_type,
*args,
**kwargs,
)
if TYPE_CHECKING:
from typing import AsyncIterable, AsyncIterator
from collections.abc import AsyncIterable, AsyncIterator
_AsyncIterator = AsyncIterator[bytes]
_AsyncIterable = AsyncIterable[bytes]
@ -970,9 +991,9 @@ else:
class AsyncIterablePayload(Payload):
_iter: Optional[_AsyncIterator] = None
_iter: _AsyncIterator | None = None
_value: _AsyncIterable
_cached_chunks: Optional[List[bytes]] = None
_cached_chunks: list[bytes] | None = None
# _consumed stays False to allow reuse with cached content
_autoclose = True # Iterator doesn't need explicit closing
@ -981,7 +1002,7 @@ class AsyncIterablePayload(Payload):
raise TypeError(
"value argument must support "
"collections.abc.AsyncIterable interface, "
"got {!r}".format(type(value))
f"got {type(value)!r}"
)
if "content_type" not in kwargs:
@ -1009,7 +1030,7 @@ class AsyncIterablePayload(Payload):
await self.write_with_length(writer, None)
async def write_with_length(
self, writer: AbstractStreamWriter, content_length: Optional[int]
self, writer: AbstractStreamWriter, content_length: int | None
) -> None:
"""
Write async iterable payload with a specific content length constraint.
@ -1048,10 +1069,7 @@ class AsyncIterablePayload(Payload):
try:
while True:
if sys.version_info >= (3, 10):
chunk = await anext(self._iter)
else:
chunk = await self._iter.__anext__()
chunk = await anext(self._iter)
if remaining_bytes is None:
await writer.write(chunk)
# If we have a content length limit
@ -1089,7 +1107,7 @@ class AsyncIterablePayload(Payload):
return b""
# Read all chunks and cache them
chunks: List[bytes] = []
chunks: list[bytes] = []
async for chunk in self._iter:
chunks.append(chunk)