⚗️ add handler class

This commit is contained in:
yanyongyu 2021-03-18 19:16:18 +08:00
parent 62135dc117
commit 6bd12b7f2c
3 changed files with 208 additions and 92 deletions

View File

@ -7,7 +7,6 @@
import abc import abc
from copy import copy from copy import copy
from typing_extensions import Literal
from functools import reduce, partial from functools import reduce, partial
from dataclasses import dataclass, field from dataclasses import dataclass, field
from typing import Any, Dict, Union, TypeVar, Mapping, Optional, Callable, Iterable, Iterator, Awaitable, TYPE_CHECKING from typing import Any, Dict, Union, TypeVar, Mapping, Optional, Callable, Iterable, Iterator, Awaitable, TYPE_CHECKING

161
nonebot/handler.py Normal file
View File

@ -0,0 +1,161 @@
"""
事件处理函数
===========
该模块实现事件处理函数的封装以实现动态参数等功能
"""
import inspect
from typing_extensions import Literal
from typing import Any, List, Dict, Type, Union, Optional, TYPE_CHECKING
from typing import ForwardRef, _eval_type # type: ignore
from nonebot.log import logger
from nonebot.typing import T_Handler, T_State
if TYPE_CHECKING:
from nonebot.matcher import Matcher
from nonebot.adapters import Bot, Event
class HandlerMeta(type):
...
class Handler(metaclass=HandlerMeta):
"""事件处理函数类"""
def __init__(self, func: T_Handler, module: Optional[str] = None):
self.func: T_Handler = func
"""
:类型: ``T_Handler``
:说明: 事件处理函数
"""
self.module: Optional[str] = module
"""
:类型: ``Optional[str]``
:说明: 事件处理函数所在模块名称
"""
self.signature: inspect.Signature = self.get_signature()
"""
:类型: ``inspect.Signature``
:说明: 事件处理函数签名
"""
async def __call__(self, matcher: "Matcher", bot: "Bot", event: "Event",
state: T_State):
params = {
param.name: param.annotation
for param in self.signature.parameters.values()
}
BotType = ((params["bot"] is not inspect.Parameter.empty) and
inspect.isclass(params["bot"]) and params["bot"])
if BotType and not isinstance(bot, BotType):
logger.debug(
f"Matcher {matcher} bot type {type(bot)} not match annotation {BotType}, ignored"
)
return
EventType = ((params["event"] is not inspect.Parameter.empty) and
inspect.isclass(params["event"]) and params["event"])
if EventType and not isinstance(event, EventType):
logger.debug(
f"Matcher {matcher} event type {type(event)} not match annotation {EventType}, ignored"
)
return
args = {"bot": bot, "event": event, "state": state, "matcher": matcher}
await self.func(
**{k: v for k, v in args.items() if params[k] is not None})
@property
def bot_type(self) -> Type["Bot"]:
return self.signature.parameters["bot"].annotation
@property
def event_type(self) -> Optional[Type["Event"]]:
if "event" not in self.signature:
return None
return self.signature.parameters["event"].annotation
@property
def state_type(self) -> Optional[T_State]:
if "state" not in self.signature:
return None
return self.signature.parameters["state"].annotation
@property
def matcher_type(self) -> Optional[Type["Matcher"]]:
if "matcher" not in self.signature:
return None
return self.signature.parameters["matcher"].annotation
def get_signature(self) -> inspect.Signature:
wrapped_signature = self._get_typed_signature()
signature = self._get_typed_signature(False)
self._check_params(signature)
self._check_bot_param(signature)
self._check_bot_param(wrapped_signature)
signature.parameters["bot"].annotation = wrapped_signature.parameters[
"bot"].annotation
if "event" in wrapped_signature.parameters and "event" in signature.parameters:
signature.parameters[
"event"].annotation = wrapped_signature.parameters[
"event"].annotation
return signature
def update_signature(
self, **kwargs: Union[None, Type["Bot"], Type["Event"], Type["Matcher"],
T_State]
) -> None:
params: List[inspect.Parameter] = []
for param in ["bot", "event", "state", "matcher"]:
sig = self.signature.parameters.get(param, None)
if param in kwargs:
sig = inspect.Parameter(param,
inspect.Parameter.POSITIONAL_OR_KEYWORD,
annotation=kwargs[param])
if sig:
params.append(sig)
self.signature = inspect.Signature(params)
def _get_typed_signature(self,
follow_wrapped: bool = True) -> inspect.Signature:
signature = inspect.signature(self.func, follow_wrapped=follow_wrapped)
globalns = getattr(self, "__globals__", {})
typed_params = [
inspect.Parameter(
name=param.name,
kind=param.kind,
default=param.default,
annotation=param.annotation if follow_wrapped else
self._get_typed_annotation(param, globalns),
) for param in signature.parameters.values()
]
typed_signature = inspect.Signature(typed_params)
return typed_signature
def _get_typed_annotation(self, param: inspect.Parameter,
globalns: Dict[str, Any]) -> Any:
try:
if isinstance(param.annotation, str):
return _eval_type(ForwardRef(param.annotation), globalns,
globalns)
else:
return param.annotation
except Exception:
return param.annotation
def _check_params(self, signature: inspect.Signature):
if not set(signature.parameters.keys()) < {
"bot", "event", "state", "matcher"
}:
raise ValueError(
"Handler param names must in `bot`/`event`/`state`/`matcher`")
def _check_bot_param(self, signature: inspect.Signature):
if not any(
param.name == "bot" for param in signature.parameters.values()):
raise ValueError("Handler missing parameter 'bot'")

View File

@ -2,9 +2,10 @@
事件响应器 事件响应器
========== ==========
该模块实现事件响应器的创建与运行并提供一些快捷方法来帮助用户更好的与机器人进行 对话 该模块实现事件响应器的创建与运行并提供一些快捷方法来帮助用户更好的与机器人进行对话
""" """
import sys
import inspect import inspect
from functools import wraps from functools import wraps
from datetime import datetime from datetime import datetime
@ -14,6 +15,7 @@ from typing import Type, List, Dict, Union, Mapping, Iterable, Callable, Optiona
from nonebot.rule import Rule from nonebot.rule import Rule
from nonebot.log import logger from nonebot.log import logger
from nonebot.handler import Handler
from nonebot.permission import Permission, USER from nonebot.permission import Permission, USER
from nonebot.typing import T_State, T_StateFactory, T_Handler, T_ArgsParser, T_TypeUpdater, T_PermissionUpdater from nonebot.typing import T_State, T_StateFactory, T_Handler, T_ArgsParser, T_TypeUpdater, T_PermissionUpdater
from nonebot.exception import PausedException, RejectedException, FinishedException, StopPropagation from nonebot.exception import PausedException, RejectedException, FinishedException, StopPropagation
@ -31,11 +33,21 @@ current_event: ContextVar = ContextVar("current_event")
class MatcherMeta(type): class MatcherMeta(type):
if TYPE_CHECKING:
module: Optional[str]
type: str
rule: Rule
permission: Permission
handlers: List[T_Handler]
priority: int
block: bool
temp: bool
expire_time: Optional[datetime]
def __repr__(self) -> str: def __repr__(self) -> str:
return (f"<Matcher from {self.module or 'unknow'}, " # type: ignore return (f"<Matcher from {self.module or 'unknow'}, "
f"type={self.type}, priority={self.priority}, " # type: ignore f"type={self.type}, priority={self.priority}, "
f"temp={self.temp}>") # type: ignore f"temp={self.temp}>")
def __str__(self) -> str: def __str__(self) -> str:
return repr(self) return repr(self)
@ -64,9 +76,9 @@ class Matcher(metaclass=MatcherMeta):
:类型: ``Permission`` :类型: ``Permission``
:说明: 事件响应器触发权限 :说明: 事件响应器触发权限
""" """
handlers: List[T_Handler] = [] handlers: List[Handler] = []
""" """
:类型: ``List[T_Handler]`` :类型: ``List[Handler]``
:说明: 事件响应器拥有的事件处理函数列表 :说明: 事件响应器拥有的事件处理函数列表
""" """
priority: int = 1 priority: int = 1
@ -134,7 +146,7 @@ class Matcher(metaclass=MatcherMeta):
type_: str = "", type_: str = "",
rule: Optional[Rule] = None, rule: Optional[Rule] = None,
permission: Optional[Permission] = None, permission: Optional[Permission] = None,
handlers: Optional[List[T_Handler]] = None, handlers: Optional[List[Union[T_Handler, Handler]]] = None,
temp: bool = False, temp: bool = False,
priority: int = 1, priority: int = 1,
block: bool = False, block: bool = False,
@ -178,7 +190,9 @@ class Matcher(metaclass=MatcherMeta):
"permission": "permission":
permission or Permission(), permission or Permission(),
"handlers": [ "handlers": [
cls.process_handler(handler) for handler in handlers handler
if isinstance(handler, Handler) else Handler(handler)
for handler in handlers
] if handlers else [], ] if handlers else [],
"temp": "temp":
temp, temp,
@ -284,27 +298,13 @@ class Matcher(metaclass=MatcherMeta):
cls._default_permission_updater = func cls._default_permission_updater = func
return func return func
@staticmethod
def process_handler(handler: T_Handler) -> T_Handler:
signature = inspect.signature(handler, follow_wrapped=False)
bot = signature.parameters.get("bot")
event = signature.parameters.get("event")
state = signature.parameters.get("state")
matcher = signature.parameters.get("matcher")
if not bot:
raise ValueError("Handler missing parameter 'bot'")
handler.__params__ = {
"bot": bot.annotation,
"event": event.annotation if event else None,
"state": T_State if state else None,
"matcher": matcher.annotation if matcher else None
}
return handler
@classmethod @classmethod
def append_handler(cls, handler: T_Handler) -> None: def append_handler(cls,
# Process handler first handler: T_Handler,
cls.handlers.append(cls.process_handler(handler)) module: Optional[str] = None) -> Handler:
handler_ = Handler(handler, module)
cls.handlers.append(handler_)
return handler_
@classmethod @classmethod
def handle(cls) -> Callable[[T_Handler], T_Handler]: def handle(cls) -> Callable[[T_Handler], T_Handler]:
@ -319,6 +319,9 @@ class Matcher(metaclass=MatcherMeta):
""" """
def _decorator(func: T_Handler) -> T_Handler: def _decorator(func: T_Handler) -> T_Handler:
print(
sys._getframe(1).f_code.co_filename,
sys._getframe(1).f_code.co_name)
cls.append_handler(func) cls.append_handler(func)
return func return func
@ -339,23 +342,17 @@ class Matcher(metaclass=MatcherMeta):
async def _receive(bot: "Bot", event: "Event") -> NoReturn: async def _receive(bot: "Bot", event: "Event") -> NoReturn:
raise PausedException raise PausedException
cls.process_handler(_receive)
if cls.handlers: if cls.handlers:
# 已有前置handlers则接受一条新的消息否则视为接收初始消息 # 已有前置handlers则接受一条新的消息否则视为接收初始消息
cls.append_handler(_receive) receive_handler = cls.append_handler(_receive)
else:
receive_handler = None
def _decorator(func: T_Handler) -> T_Handler: def _decorator(func: T_Handler) -> T_Handler:
cls.process_handler(func)
if not cls.handlers or cls.handlers[-1] is not func: if not cls.handlers or cls.handlers[-1] is not func:
cls.append_handler(func) func_handler = cls.append_handler(func)
receive_handler.update_signature(bot=func_handler.bot_type,
_receive.__params__.update({ event=func_handler.event_type)
"bot":
func.__params__["bot"],
"event":
func.__params__["event"] or _receive.__params__["event"]
})
return func return func
@ -416,42 +413,27 @@ class Matcher(metaclass=MatcherMeta):
else: else:
state[state["_current_key"]] = str(event.get_message()) state[state["_current_key"]] = str(event.get_message())
cls.append_handler(_key_getter) getter_handler = cls.append_handler(_key_getter)
cls.append_handler(_key_parser) parser_handler = cls.append_handler(_key_parser)
def _decorator(func: T_Handler) -> T_Handler: def _decorator(func: T_Handler) -> T_Handler:
if not hasattr(cls.handlers[-1], "__wrapped__"): if not hasattr(cls.handlers[-1], "__wrapped__"):
cls.process_handler(func)
parser = cls.handlers.pop() parser = cls.handlers.pop()
@wraps(func) @wraps(func)
async def wrapper(bot: "Bot", event: "Event", state: T_State, async def wrapper(bot: "Bot", event: "Event", state: T_State,
matcher: Matcher): matcher: Matcher):
await matcher.run_handler(parser, bot, event, state) await parser(matcher, bot, event, state)
await matcher.run_handler(func, bot, event, state) await func_handler(matcher, bot, event, state)
if "_current_key" in state: if "_current_key" in state:
del state["_current_key"] del state["_current_key"]
cls.append_handler(wrapper) func_handler = cls.append_handler(wrapper)
wrapper.__params__.update({ getter_handler.update_signature(bot=func_handler.bot_type,
"bot": event=func_handler.event_type)
func.__params__["bot"], parser_handler.update_signature(bot=func_handler.bot_type,
"event": event=func_handler.event_type)
func.__params__["event"] or wrapper.__params__["event"]
})
_key_getter.__params__.update({
"bot":
func.__params__["bot"],
"event":
func.__params__["event"] or wrapper.__params__["event"]
})
_key_parser.__params__.update({
"bot":
func.__params__["bot"],
"event":
func.__params__["event"] or wrapper.__params__["event"]
})
return func return func
@ -545,32 +527,6 @@ class Matcher(metaclass=MatcherMeta):
""" """
self.block = True self.block = True
async def run_handler(self, handler: T_Handler, bot: "Bot", event: "Event",
state: T_State):
if not hasattr(handler, "__params__"):
self.process_handler(handler)
params = getattr(handler, "__params__")
BotType = ((params["bot"] is not inspect.Parameter.empty) and
inspect.isclass(params["bot"]) and params["bot"])
if BotType and not isinstance(bot, BotType):
logger.debug(
f"Matcher {self} bot type {type(bot)} not match annotation {BotType}, ignored"
)
return
EventType = ((params["event"] is not inspect.Parameter.empty) and
inspect.isclass(params["event"]) and params["event"])
if EventType and not isinstance(event, EventType):
logger.debug(
f"Matcher {self} event type {type(event)} not match annotation {EventType}, ignored"
)
return
args = {"bot": bot, "event": event, "state": state, "matcher": self}
await handler(
**{k: v for k, v in args.items() if params[k] is not None})
# 运行handlers # 运行handlers
async def run(self, bot: "Bot", event: "Event", state: T_State): async def run(self, bot: "Bot", event: "Event", state: T_State):
b_t = current_bot.set(bot) b_t = current_bot.set(bot)
@ -583,7 +539,7 @@ class Matcher(metaclass=MatcherMeta):
for _ in range(len(self.handlers)): for _ in range(len(self.handlers)):
handler = self.handlers.pop(0) handler = self.handlers.pop(0)
await self.run_handler(handler, bot, event, state_) await handler(self, bot, event, state_)
except RejectedException: except RejectedException:
self.handlers.insert(0, handler) # type: ignore self.handlers.insert(0, handler) # type: ignore