Voice et bot modif
This commit is contained in:
parent
189d56026b
commit
7333a22bcd
10774 changed files with 634644 additions and 933308 deletions
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue