Skip to content

Commit 0d8b816

Browse files
authored
✨ Feature: 支持 PEP 695 类型别名 (#3621)
1 parent 56f52f2 commit 0d8b816

16 files changed

Lines changed: 232 additions & 9 deletions

File tree

nonebot/dependencies/utils.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,14 @@
77
"""
88

99
import inspect
10-
from typing import Any, Callable, ForwardRef
10+
from typing import Any, Callable, ForwardRef, cast
11+
from typing_extensions import TypeAliasType
1112

1213
from loguru import logger
1314

1415
from nonebot.compat import ModelField
1516
from nonebot.exception import TypeMisMatch
16-
from nonebot.typing import evaluate_forwardref
17+
from nonebot.typing import evaluate_forwardref, is_type_alias_type
1718

1819

1920
def get_typed_signature(call: Callable[..., Any]) -> inspect.Signature:
@@ -46,6 +47,9 @@ def get_typed_annotation(param: inspect.Parameter, globalns: dict[str, Any]) ->
4647
f'Unknown ForwardRef["{param.annotation}"] for parameter {param.name}'
4748
)
4849
return inspect.Parameter.empty
50+
if is_type_alias_type(annotation):
51+
# Python 3.12+ supports PEP 695 TypeAliasType
52+
annotation = cast(TypeAliasType, annotation).__value__
4953
return annotation
5054

5155

nonebot/typing.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,17 @@ def is_none_type(type_: type[t.Any]) -> bool:
9999
return type_ in NONE_TYPES
100100

101101

102+
if sys.version_info < (3, 12):
103+
104+
def is_type_alias_type(type_: type[t.Any]) -> bool:
105+
"""判断是否是 TypeAliasType 类型"""
106+
return isinstance(type_, t_ext.TypeAliasType)
107+
else:
108+
109+
def is_type_alias_type(type_: type[t.Any]) -> bool:
110+
return isinstance(type_, (t.TypeAliasType, t_ext.TypeAliasType))
111+
112+
102113
def evaluate_forwardref(
103114
ref: t.ForwardRef, globalns: dict[str, t.Any], localns: dict[str, t.Any]
104115
) -> t.Any:

nonebot/utils.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,13 +89,15 @@ def generic_check_issubclass(
8989
9090
特别的:
9191
92+
- 如果 cls 是 `typing.TypeVar` 类型,
93+
则会检查其 `__bound__` 或 `__constraints__`
94+
是否是 class_or_tuple 中一个类型的子类或 None。
9295
- 如果 cls 是 `typing.Union` 或 `types.UnionType` 类型,
9396
则会检查其中的所有类型是否是 class_or_tuple 中一个类型的子类或 None。
9497
- 如果 cls 是 `typing.Literal` 类型,
9598
则会检查其中的所有值是否是 class_or_tuple 中一个类型的实例。
96-
- 如果 cls 是 `typing.TypeVar` 类型,
97-
则会检查其 `__bound__` 或 `__constraints__`
98-
是否是 class_or_tuple 中一个类型的子类或 None。
99+
- 如果 cls 是 `typing.List`、`typing.Dict` 等泛型类型,
100+
则会检查其原始类型是否是 class_or_tuple 中一个类型的子类。
99101
"""
100102
# if the target is a TypeVar, we check it first
101103
if isinstance(cls, TypeVar):

pyproject.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ dependencies = [
2121
"pygtrie >=2.4.1, <3.0.0",
2222
"exceptiongroup >=1.2.2, <2.0.0",
2323
"python-dotenv >=0.21.0, <2.0.0",
24-
"typing-extensions >=4.4.0, <5.0.0",
24+
"typing-extensions >=4.6.0, <5.0.0",
2525
"tomli >=2.0.1, <3.0.0; python_version < '3.11'",
2626
"pydantic >=1.10.0, <3.0.0, !=2.5.0, !=2.5.1, !=2.10.0, !=2.10.1",
2727
]
@@ -129,6 +129,9 @@ pythonVersion = "3.9"
129129
pythonPlatform = "All"
130130
defineConstant = { PYDANTIC_V2 = true }
131131
executionEnvironments = [
132+
{ root = "./tests/python_3_12", pythonVersion = "3.12", extraPaths = [
133+
"./",
134+
] },
132135
{ root = "./tests", extraPaths = [
133136
"./",
134137
] },

tests/conftest.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from functools import wraps
33
import os
44
from pathlib import Path
5+
import sys
56
import threading
67
from typing import TYPE_CHECKING, Callable, TypeVar
78
from typing_extensions import ParamSpec
@@ -67,7 +68,14 @@ def _wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
6768
@run_once
6869
def load_plugin(anyio_backend, nonebug_init: None) -> set["Plugin"]:
6970
# preload global plugins
70-
return nonebot.load_plugins(str(Path(__file__).parent / "plugins"))
71+
plugins: set["Plugin"] = set()
72+
plugins |= nonebot.load_plugins(str(Path(__file__).parent / "plugins"))
73+
if sys.version_info >= (3, 12):
74+
# preload python 3.12 plugins
75+
plugins |= nonebot.load_plugins(
76+
str(Path(__file__).parent / "python_3_12" / "plugins")
77+
)
78+
return plugins
7179

7280

7381
@pytest.fixture(scope="session", autouse=True)
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
from pathlib import Path
2+
3+
from nonebot import load_plugins
4+
5+
_sub_plugins = set()
6+
7+
_sub_plugins |= load_plugins(str(Path(__file__).parent))
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
from typing import Annotated
2+
3+
from nonebot.adapters import Message
4+
from nonebot.params import Arg
5+
6+
type AliasedArg = Annotated[Message, Arg()]
7+
8+
9+
async def aliased_arg(key: AliasedArg) -> Message:
10+
return key
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
from nonebot.adapters import Bot
2+
3+
type AliasedBot = Bot
4+
5+
6+
async def get_aliased_bot(b: AliasedBot) -> Bot:
7+
return b
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
from typing import Annotated
2+
3+
from nonebot import on_message
4+
from nonebot.params import Depends
5+
6+
test_depends = on_message()
7+
8+
runned = []
9+
10+
11+
def dependency():
12+
runned.append(1)
13+
return 1
14+
15+
16+
type AliasedDepends = Annotated[int, Depends(dependency)]
17+
18+
19+
@test_depends.handle()
20+
async def aliased_depends(x: AliasedDepends):
21+
return x
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
from nonebot.adapters import Event
2+
3+
type AliasedEvent = Event
4+
5+
6+
async def aliased_event(e: AliasedEvent) -> Event:
7+
return e

0 commit comments

Comments
 (0)