💥 Remove: 移除 Python 3.9 支持 (#3860)

This commit is contained in:
呵呵です
2026-02-18 00:11:36 +08:00
committed by GitHub
parent f719a6b41b
commit 63cde5da77
56 changed files with 603 additions and 1144 deletions

View File

@@ -1,7 +1,6 @@
from collections.abc import Awaitable, Iterable
from collections.abc import Awaitable, Callable, Iterable
from types import TracebackType
from typing import Any, Callable, Optional, Union, cast
from typing_extensions import TypeAlias
from typing import Any, TypeAlias, cast
import anyio
from anyio.abc import TaskGroup
@@ -11,12 +10,12 @@ from nonebot.utils import is_coroutine_callable, run_sync
SYNC_LIFESPAN_FUNC: TypeAlias = Callable[[], Any]
ASYNC_LIFESPAN_FUNC: TypeAlias = Callable[[], Awaitable[Any]]
LIFESPAN_FUNC: TypeAlias = Union[SYNC_LIFESPAN_FUNC, ASYNC_LIFESPAN_FUNC]
LIFESPAN_FUNC: TypeAlias = SYNC_LIFESPAN_FUNC | ASYNC_LIFESPAN_FUNC
class Lifespan:
def __init__(self) -> None:
self._task_group: Optional[TaskGroup] = None
self._task_group: TaskGroup | None = None
self._startup_funcs: list[LIFESPAN_FUNC] = []
self._ready_funcs: list[LIFESPAN_FUNC] = []
@@ -72,9 +71,9 @@ class Lifespan:
async def shutdown(
self,
*,
exc_type: Optional[type[BaseException]] = None,
exc_val: Optional[BaseException] = None,
exc_tb: Optional[TracebackType] = None,
exc_type: type[BaseException] | None = None,
exc_val: BaseException | None = None,
exc_tb: TracebackType | None = None,
) -> None:
if self._shutdown_funcs:
# reverse shutdown funcs to ensure stack order
@@ -93,8 +92,8 @@ class Lifespan:
async def __aexit__(
self,
exc_type: Optional[type[BaseException]],
exc_val: Optional[BaseException],
exc_tb: Optional[TracebackType],
exc_type: type[BaseException] | None,
exc_val: BaseException | None,
exc_tb: TracebackType | None,
) -> None:
await self.shutdown(exc_type=exc_type, exc_val=exc_val, exc_tb=exc_tb)

View File

@@ -2,8 +2,8 @@ import abc
from collections.abc import AsyncGenerator
from contextlib import AsyncExitStack, asynccontextmanager
from types import TracebackType
from typing import TYPE_CHECKING, Any, ClassVar, Optional, Union
from typing_extensions import Self, TypeAlias
from typing import TYPE_CHECKING, Any, ClassVar, TypeAlias
from typing_extensions import Self
from anyio import CancelScope, create_task_group
from anyio.abc import TaskGroup
@@ -245,9 +245,9 @@ class HTTPClientSession(abc.ABC):
params: QueryTypes = None,
headers: HeaderTypes = None,
cookies: CookieTypes = None,
version: Union[str, HTTPVersion] = HTTPVersion.H11,
version: str | HTTPVersion = HTTPVersion.H11,
timeout: TimeoutTypes = None,
proxy: Optional[str] = None,
proxy: str | None = None,
):
raise NotImplementedError
@@ -283,9 +283,9 @@ class HTTPClientSession(abc.ABC):
async def __aexit__(
self,
exc_type: Optional[type[BaseException]],
exc: Optional[BaseException],
tb: Optional[TracebackType],
exc_type: type[BaseException] | None,
exc: BaseException | None,
tb: TracebackType | None,
) -> None:
await self.close()
@@ -315,9 +315,9 @@ class HTTPClientMixin(ForwardMixin):
params: QueryTypes = None,
headers: HeaderTypes = None,
cookies: CookieTypes = None,
version: Union[str, HTTPVersion] = HTTPVersion.H11,
version: str | HTTPVersion = HTTPVersion.H11,
timeout: TimeoutTypes = None,
proxy: Optional[str] = None,
proxy: str | None = None,
) -> HTTPClientSession:
"""获取一个 HTTP 会话"""
raise NotImplementedError

View File

@@ -1,4 +1,4 @@
from typing import TYPE_CHECKING, TypeVar, Union, overload
from typing import TYPE_CHECKING, TypeVar, overload
from .abstract import Driver, Mixin
@@ -21,7 +21,7 @@ def combine_driver(
def combine_driver(
driver: type[D], *mixins: type[Mixin]
) -> Union[type[D], type["CombinedDriver"]]:
) -> type[D] | type["CombinedDriver"]:
"""将一个驱动器和多个混入类合并。"""
# check first
if not issubclass(driver, Driver):

View File

@@ -1,48 +1,51 @@
import abc
from collections.abc import Awaitable, Iterator, Mapping, MutableMapping
from collections.abc import Awaitable, Callable, Iterator, Mapping, MutableMapping
from dataclasses import dataclass
from enum import Enum
from http.cookiejar import Cookie, CookieJar
from typing import IO, Any, Callable, Optional, Union
from typing_extensions import TypeAlias
from typing import IO, Any, TypeAlias
import urllib.request
from multidict import CIMultiDict
from yarl import URL as URL
RawURL: TypeAlias = tuple[bytes, bytes, Optional[int], bytes]
SimpleQuery: TypeAlias = Union[str, int, float]
QueryVariable: TypeAlias = Union[SimpleQuery, list[SimpleQuery]]
QueryTypes: TypeAlias = Union[
None, str, Mapping[str, QueryVariable], list[tuple[str, SimpleQuery]]
]
@dataclass
class Timeout:
"""Request 超时配置。"""
HeaderTypes: TypeAlias = Union[
None,
CIMultiDict[str],
dict[str, str],
list[tuple[str, str]],
]
total: float | None = None
connect: float | None = None
read: float | None = None
CookieTypes: TypeAlias = Union[
None, "Cookies", CookieJar, dict[str, str], list[tuple[str, str]]
]
ContentTypes: TypeAlias = Union[str, bytes, None]
DataTypes: TypeAlias = Union[dict, None]
FileContent: TypeAlias = Union[IO[bytes], bytes]
FileType: TypeAlias = tuple[Optional[str], FileContent, Optional[str]]
FileTypes: TypeAlias = Union[
# file (or bytes)
FileContent,
# (filename, file (or bytes))
tuple[Optional[str], FileContent],
# (filename, file (or bytes), content_type)
FileType,
]
FilesTypes: TypeAlias = Union[dict[str, FileTypes], list[tuple[str, FileTypes]], None]
TimeoutTypes: TypeAlias = Union[float, "Timeout", None]
RawURL: TypeAlias = tuple[bytes, bytes, int | None, bytes]
SimpleQuery: TypeAlias = str | int | float
QueryVariable: TypeAlias = SimpleQuery | list[SimpleQuery]
QueryTypes: TypeAlias = (
None | str | Mapping[str, QueryVariable] | list[tuple[str, SimpleQuery]]
)
HeaderTypes: TypeAlias = (
None | CIMultiDict[str] | dict[str, str] | list[tuple[str, str]]
)
CookieTypes: TypeAlias = (
"None | Cookies | CookieJar | dict[str, str] | list[tuple[str, str]]"
)
ContentTypes: TypeAlias = str | bytes | None
DataTypes: TypeAlias = dict | None
FileContent: TypeAlias = IO[bytes] | bytes
FileType: TypeAlias = tuple[str | None, FileContent, str | None]
FileTypes: TypeAlias = (
FileContent # file (or bytes)
| tuple[str | None, FileContent] # (filename, file (or bytes))
| FileType # (filename, file (or bytes), content_type)
)
FilesTypes: TypeAlias = dict[str, FileTypes] | list[tuple[str, FileTypes]] | None
TimeoutTypes: TypeAlias = float | Timeout | None
class HTTPVersion(Enum):
@@ -51,20 +54,11 @@ class HTTPVersion(Enum):
H2 = "2"
@dataclass
class Timeout:
"""Request 超时配置。"""
total: Optional[float] = None
connect: Optional[float] = None
read: Optional[float] = None
class Request:
def __init__(
self,
method: Union[str, bytes],
url: Union["URL", str, RawURL],
method: str | bytes,
url: "URL | str | RawURL",
*,
params: QueryTypes = None,
headers: HeaderTypes = None,
@@ -73,9 +67,9 @@ class Request:
data: DataTypes = None,
json: Any = None,
files: FilesTypes = None,
version: Union[str, HTTPVersion] = HTTPVersion.H11,
version: str | HTTPVersion = HTTPVersion.H11,
timeout: TimeoutTypes = None,
proxy: Optional[str] = None,
proxy: str | None = None,
):
# method
self.method: str = (
@@ -88,7 +82,7 @@ class Request:
# timeout
self.timeout: TimeoutTypes = timeout
# proxy
self.proxy: Optional[str] = proxy
self.proxy: str | None = proxy
# url
if isinstance(url, tuple):
@@ -117,7 +111,7 @@ class Request:
self.content: ContentTypes = content
self.data: DataTypes = data
self.json: Any = json
self.files: Optional[list[tuple[str, FileType]]] = None
self.files: list[tuple[str, FileType]] | None = None
if files:
self.files = []
files_ = files.items() if isinstance(files, dict) else files
@@ -140,7 +134,7 @@ class Response:
*,
headers: HeaderTypes = None,
content: ContentTypes = None,
request: Optional[Request] = None,
request: Request | None = None,
):
# status code
self.status_code: int = status_code
@@ -153,7 +147,7 @@ class Response:
self.content: ContentTypes = content
# request
self.request: Optional[Request] = request
self.request: Request | None = request
def __repr__(self) -> str:
return f"{self.__class__.__name__}(status_code={self.status_code!r})"
@@ -183,7 +177,7 @@ class WebSocket(abc.ABC):
raise NotImplementedError
@abc.abstractmethod
async def receive(self) -> Union[str, bytes]:
async def receive(self) -> str | bytes:
"""接收一条 WebSocket text/bytes 信息"""
raise NotImplementedError
@@ -197,7 +191,7 @@ class WebSocket(abc.ABC):
"""接收一条 WebSocket binary 信息"""
raise NotImplementedError
async def send(self, data: Union[str, bytes]) -> None:
async def send(self, data: str | bytes) -> None:
"""发送一条 WebSocket text/bytes 信息"""
if isinstance(data, str):
await self.send_text(data)
@@ -258,11 +252,11 @@ class Cookies(MutableMapping):
def get( # pyright: ignore[reportIncompatibleMethodOverride]
self,
name: str,
default: Optional[str] = None,
domain: Optional[str] = None,
path: Optional[str] = None,
) -> Optional[str]:
value: Optional[str] = None
default: str | None = None,
domain: str | None = None,
path: str | None = None,
) -> str | None:
value: str | None = None
for cookie in self.jar:
if (
cookie.name == name
@@ -277,7 +271,7 @@ class Cookies(MutableMapping):
return default if value is None else value
def delete(
self, name: str, domain: Optional[str] = None, path: Optional[str] = None
self, name: str, domain: str | None = None, path: str | None = None
) -> None:
if domain is not None and path is not None:
return self.jar.clear(domain, path, name)
@@ -293,7 +287,7 @@ class Cookies(MutableMapping):
for cookie in remove:
self.jar.clear(cookie.domain, cookie.path, cookie.name)
def clear(self, domain: Optional[str] = None, path: Optional[str] = None) -> None:
def clear(self, domain: str | None = None, path: str | None = None) -> None:
self.jar.clear(domain, path)
def update( # pyright: ignore[reportIncompatibleMethodOverride]