Skip to content

Commit 9599079

Browse files
authored
feat: 使用 typing-inspection 来分析类型注解 (#173)
1 parent a791cfb commit 9599079

File tree

9 files changed

+1063
-1041
lines changed

9 files changed

+1063
-1041
lines changed

alicebot/dependencies.py

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,15 @@
1212
asynccontextmanager,
1313
contextmanager,
1414
)
15-
from inspect import get_annotations
16-
from typing import Any, TypeVar, cast
15+
from typing import Any, TypeVar, cast, get_type_hints
1716
from typing_extensions import override
1817

18+
from typing_inspection.introspection import (
19+
UNKNOWN,
20+
AnnotationSource,
21+
inspect_annotation,
22+
)
23+
1924
from alicebot.utils import sync_ctx_manager_wrapper
2025

2126
_T = TypeVar("_T")
@@ -31,11 +36,9 @@
3136
__all__ = ["Depends"]
3237

3338

34-
class InnerDepends:
39+
class _InnerDepends:
3540
"""子依赖的内部实现。
3641
37-
用户无需关注此内部实现。
38-
3942
Attributes:
4043
dependency: 依赖类。如果不指定则根据字段的类型注释自动判断。
4144
use_cache: 是否使用缓存。默认为 `True`。
@@ -69,7 +72,7 @@ def Depends( # noqa: N802 # pylint: disable=invalid-name
6972
Returns:
7073
返回内部子依赖对象。
7174
"""
72-
return InnerDepends(dependency=dependency, use_cache=use_cache) # type: ignore
75+
return _InnerDepends(dependency=dependency, use_cache=use_cache) # type: ignore
7376

7477

7578
async def solve_dependencies(
@@ -99,22 +102,30 @@ async def solve_dependencies(
99102
if isinstance(dependent, type):
100103
# type of dependent is Type[T]
101104
values: dict[str, Any] = {}
102-
ann = get_annotations(dependent)
105+
ann = get_type_hints(dependent)
103106
for name, sub_dependent in inspect.getmembers(
104-
dependent, lambda x: isinstance(x, InnerDepends)
107+
dependent, lambda x: isinstance(x, _InnerDepends)
105108
):
106-
assert isinstance(sub_dependent, InnerDepends)
109+
assert isinstance(sub_dependent, _InnerDepends)
107110
if sub_dependent.dependency is None:
108111
dependent_ann = ann.get(name)
109112
if dependent_ann is None:
110113
raise TypeError("can not solve dependent")
111-
sub_dependent.dependency = dependent_ann
114+
inspected_ann = inspect_annotation(
115+
dependent_ann,
116+
annotation_source=AnnotationSource.CLASS,
117+
)
118+
if inspected_ann.type == UNKNOWN:
119+
raise TypeError("can not solve dependent")
120+
sub_dependent.dependency = inspected_ann.type # type: ignore
121+
assert sub_dependent.dependency is not None
112122
values[name] = await solve_dependencies(
113-
cast("Dependency[_T]", sub_dependent.dependency),
123+
sub_dependent.dependency,
114124
use_cache=sub_dependent.use_cache,
115125
stack=stack,
116126
dependency_cache=dependency_cache,
117127
)
128+
118129
depend_obj = cast(
119130
"_T | AbstractAsyncContextManager[_T] | AbstractContextManager[_T]",
120131
dependent.__new__(dependent), # type: ignore

package.json

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,23 +24,23 @@
2424
},
2525
"dependencies": {
2626
"@iconify-json/mdi": "^1.2.3",
27-
"unocss": "^66.1.3",
27+
"unocss": "^66.3.3",
2828
"vitepress": "^1.6.3",
29-
"vue": "^3.5.16"
29+
"vue": "^3.5.18"
3030
},
3131
"devDependencies": {
32-
"@eslint/js": "^9.28.0",
33-
"@types/node": "^22.15.29",
34-
"@unocss/eslint-config": "^66.1.3",
32+
"@eslint/js": "^9.32.0",
33+
"@types/node": "^22.16.5",
34+
"@unocss/eslint-config": "^66.3.3",
3535
"conventional-changelog-cli": "^5.0.0",
36-
"eslint": "^9.28.0",
37-
"eslint-config-prettier": "^10.1.5",
38-
"eslint-plugin-vue": "^10.1.0",
39-
"globals": "^16.2.0",
36+
"eslint": "^9.32.0",
37+
"eslint-config-prettier": "^10.1.8",
38+
"eslint-plugin-vue": "^10.3.0",
39+
"globals": "^16.3.0",
4040
"markdownlint-cli2": "^0.18.1",
41-
"prettier": "^3.5.3",
42-
"typescript-eslint": "^8.33.0",
43-
"vue-eslint-parser": "^10.1.3"
41+
"prettier": "^3.6.2",
42+
"typescript-eslint": "^8.38.0",
43+
"vue-eslint-parser": "^10.2.0"
4444
},
4545
"pnpm": {
4646
"peerDependencyRules": {

packages/alicebot-adapter-apscheduler/alicebot/adapter/apscheduler/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,6 @@ async def _wrapper(self: PluginT) -> bool:
140140
return _wrapper
141141

142142
cls.rule = _rule_decorator(cls.rule) # type: ignore[method-assign]
143-
return cls # type: ignore[return-value]
143+
return cls
144144

145145
return _decorator

packages/alicebot-adapter-cqhttp/alicebot/adapter/cqhttp/event.py

Lines changed: 5 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
"""CQHTTP 适配器事件。"""
22
# pyright: reportIncompatibleVariableOverride=false
33

4-
from typing import TYPE_CHECKING, Any, Literal, get_args, get_origin
4+
from typing import TYPE_CHECKING, Any, Literal, get_origin
55
from typing_extensions import override
66

77
from pydantic import BaseModel, ConfigDict, Field
88
from pydantic.fields import FieldInfo
9+
from typing_inspection import typing_objects
10+
from typing_inspection.introspection import get_literal_values
911

1012
from alicebot.event import Event
1113
from alicebot.event import MessageEvent as BaseMessageEvent
@@ -61,9 +63,9 @@ def _get_literal_field(field: FieldInfo | None) -> str | None:
6163
if field is None:
6264
return None
6365
annotation = field.annotation
64-
if annotation is None or get_origin(annotation) is not Literal:
66+
if annotation is None or not typing_objects.is_literal(get_origin(annotation)):
6567
return None
66-
literal_values = get_args(annotation)
68+
literal_values = list(get_literal_values(annotation))
6769
if len(literal_values) != 1:
6870
return None
6971
return literal_values[0]
@@ -72,7 +74,6 @@ def _get_literal_field(field: FieldInfo | None) -> str | None:
7274
class CQHTTPEvent(Event["CQHTTPAdapter"]):
7375
"""CQHTTP 事件基类"""
7476

75-
__event__ = ""
7677
type: str | None = Field(alias="post_type")
7778
time: int
7879
self_id: int
@@ -103,7 +104,6 @@ def get_event_type(cls) -> tuple[str | None, str | None, str | None]:
103104
class MessageEvent(CQHTTPEvent, BaseMessageEvent["CQHTTPAdapter"]):
104105
"""消息事件"""
105106

106-
__event__ = "message"
107107
post_type: Literal["message"]
108108
message_type: Literal["private", "group"]
109109
sub_type: str
@@ -144,7 +144,6 @@ async def reply(
144144
class PrivateMessageEvent(MessageEvent):
145145
"""私聊消息"""
146146

147-
__event__ = "message.private"
148147
message_type: Literal["private"]
149148
sub_type: Literal["friend", "group", "other"]
150149

@@ -160,7 +159,6 @@ async def reply(
160159
class GroupMessageEvent(MessageEvent):
161160
"""群消息"""
162161

163-
__event__ = "message.group"
164162
message_type: Literal["group"]
165163
sub_type: Literal["normal", "anonymous", "notice"]
166164
group_id: int
@@ -178,15 +176,13 @@ async def reply(
178176
class NoticeEvent(CQHTTPEvent):
179177
"""通知事件"""
180178

181-
__event__ = "notice"
182179
post_type: Literal["notice"]
183180
notice_type: str
184181

185182

186183
class GroupUploadNoticeEvent(NoticeEvent):
187184
"""群文件上传"""
188185

189-
__event__ = "notice.group_upload"
190186
notice_type: Literal["group_upload"]
191187
user_id: int
192188
group_id: int
@@ -196,7 +192,6 @@ class GroupUploadNoticeEvent(NoticeEvent):
196192
class GroupAdminNoticeEvent(NoticeEvent):
197193
"""群管理员变动"""
198194

199-
__event__ = "notice.group_admin"
200195
notice_type: Literal["group_admin"]
201196
sub_type: Literal["set", "unset"]
202197
user_id: int
@@ -206,7 +201,6 @@ class GroupAdminNoticeEvent(NoticeEvent):
206201
class GroupDecreaseNoticeEvent(NoticeEvent):
207202
"""群成员减少"""
208203

209-
__event__ = "notice.group_decrease"
210204
notice_type: Literal["group_decrease"]
211205
sub_type: Literal["leave", "kick", "kick_me"]
212206
group_id: int
@@ -217,7 +211,6 @@ class GroupDecreaseNoticeEvent(NoticeEvent):
217211
class GroupIncreaseNoticeEvent(NoticeEvent):
218212
"""群成员增加"""
219213

220-
__event__ = "notice.group_increase"
221214
notice_type: Literal["group_increase"]
222215
sub_type: Literal["approve", "invite"]
223216
group_id: int
@@ -228,7 +221,6 @@ class GroupIncreaseNoticeEvent(NoticeEvent):
228221
class GroupBanNoticeEvent(NoticeEvent):
229222
"""群禁言"""
230223

231-
__event__ = "notice.group_ban"
232224
notice_type: Literal["group_ban"]
233225
sub_type: Literal["ban", "lift_ban"]
234226
group_id: int
@@ -240,15 +232,13 @@ class GroupBanNoticeEvent(NoticeEvent):
240232
class FriendAddNoticeEvent(NoticeEvent):
241233
"""好友添加"""
242234

243-
__event__ = "notice.friend_add"
244235
notice_type: Literal["friend_add"]
245236
user_id: int
246237

247238

248239
class GroupRecallNoticeEvent(NoticeEvent):
249240
"""群消息撤回"""
250241

251-
__event__ = "notice.group_recall"
252242
notice_type: Literal["group_recall"]
253243
group_id: int
254244
operator_id: int
@@ -259,7 +249,6 @@ class GroupRecallNoticeEvent(NoticeEvent):
259249
class FriendRecallNoticeEvent(NoticeEvent):
260250
"""好友消息撤回"""
261251

262-
__event__ = "notice.friend_recall"
263252
notice_type: Literal["friend_recall"]
264253
user_id: int
265254
message_id: int
@@ -268,7 +257,6 @@ class FriendRecallNoticeEvent(NoticeEvent):
268257
class NotifyEvent(NoticeEvent):
269258
"""提醒事件"""
270259

271-
__event__ = "notice.notify"
272260
notice_type: Literal["notify"]
273261
sub_type: str
274262
user_id: int
@@ -277,7 +265,6 @@ class NotifyEvent(NoticeEvent):
277265
class PokeNotifyEvent(NotifyEvent):
278266
"""戳一戳"""
279267

280-
__event__ = "notice.notify.poke"
281268
sub_type: Literal["poke"]
282269
target_id: int
283270
group_id: int | None = None
@@ -286,7 +273,6 @@ class PokeNotifyEvent(NotifyEvent):
286273
class GroupLuckyKingNotifyEvent(NotifyEvent):
287274
"""群红包运气王"""
288275

289-
__event__ = "notice.notify.lucky_king"
290276
sub_type: Literal["lucky_king"]
291277
group_id: int
292278
target_id: int
@@ -295,7 +281,6 @@ class GroupLuckyKingNotifyEvent(NotifyEvent):
295281
class GroupHonorNotifyEvent(NotifyEvent):
296282
"""群成员荣誉变更"""
297283

298-
__event__ = "notice.notify.honor"
299284
sub_type: Literal["honor"]
300285
group_id: int
301286
honor_type: Literal["talkative", "performer", "emotion"]
@@ -304,7 +289,6 @@ class GroupHonorNotifyEvent(NotifyEvent):
304289
class RequestEvent(CQHTTPEvent):
305290
"""请求事件"""
306291

307-
__event__ = "request"
308292
post_type: Literal["request"]
309293
request_type: str
310294

@@ -328,7 +312,6 @@ async def refuse(self) -> dict[str, Any]:
328312
class FriendRequestEvent(RequestEvent):
329313
"""加好友请求"""
330314

331-
__event__ = "request.friend"
332315
request_type: Literal["friend"]
333316
user_id: int
334317
comment: str
@@ -356,7 +339,6 @@ async def refuse(self) -> dict[str, Any]:
356339
class GroupRequestEvent(RequestEvent):
357340
"""加群请求 / 邀请"""
358341

359-
__event__ = "request.group"
360342
request_type: Literal["group"]
361343
sub_type: Literal["add", "invite"]
362344
group_id: int
@@ -388,23 +370,20 @@ async def refuse(self, reason: str = "") -> dict[str, Any]:
388370
class MetaEvent(CQHTTPEvent):
389371
"""元事件"""
390372

391-
__event__ = "meta_event"
392373
post_type: Literal["meta_event"]
393374
meta_event_type: str
394375

395376

396377
class LifecycleMetaEvent(MetaEvent):
397378
"""生命周期"""
398379

399-
__event__ = "meta_event.lifecycle"
400380
meta_event_type: Literal["lifecycle"]
401381
sub_type: Literal["enable", "disable", "connect"]
402382

403383

404384
class HeartbeatMetaEvent(MetaEvent):
405385
"""心跳"""
406386

407-
__event__ = "meta_event.heartbeat"
408387
meta_event_type: Literal["heartbeat"]
409388
status: Status
410389
interval: int

packages/alicebot-adapter-onebot/alicebot/adapter/onebot/event.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
"""OneBot 适配器事件。"""
22
# pyright: reportIncompatibleVariableOverride=false
33

4-
from typing import TYPE_CHECKING, Any, Literal, get_args, get_origin
4+
from typing import TYPE_CHECKING, Any, Literal, get_origin
55
from typing_extensions import override
66

77
from pydantic import BaseModel, ConfigDict
88
from pydantic.fields import FieldInfo
9+
from typing_inspection import typing_objects
10+
from typing_inspection.introspection import get_literal_values
911

1012
from alicebot.event import Event
1113
from alicebot.event import MessageEvent as BaseMessageEvent
@@ -52,9 +54,9 @@ def _get_literal_field(field: FieldInfo | None) -> str | None:
5254
if field is None:
5355
return None
5456
annotation = field.annotation
55-
if annotation is None or get_origin(annotation) is not Literal:
57+
if annotation is None or not typing_objects.is_literal(get_origin(annotation)):
5658
return None
57-
literal_values = get_args(annotation)
59+
literal_values = list(get_literal_values(annotation))
5860
if len(literal_values) != 1:
5961
return None
6062
return literal_values[0]

0 commit comments

Comments
 (0)