From 629eed08b6d7568e66d289b9b864f5104c4e26de Mon Sep 17 00:00:00 2001 From: yanyongyu Date: Sun, 6 Dec 2020 02:30:19 +0800 Subject: [PATCH] :art: update typing support --- docs/guide/basic-configuration.md | 6 ++- nonebot/__init__.py | 13 +++--- nonebot/adapters/__init__.py | 18 +++++---- nonebot/adapters/cqhttp/bot.py | 13 +++--- nonebot/adapters/cqhttp/bot.pyi | 3 +- nonebot/adapters/cqhttp/event.py | 4 +- nonebot/adapters/cqhttp/exception.py | 3 +- nonebot/adapters/cqhttp/message.py | 3 +- nonebot/adapters/cqhttp/utils.py | 3 +- nonebot/adapters/ding/bot.py | 9 +++-- nonebot/adapters/ding/event.py | 3 +- nonebot/adapters/ding/exception.py | 3 +- nonebot/adapters/ding/message.py | 4 +- nonebot/adapters/ding/model.py | 3 +- nonebot/config.py | 5 +-- nonebot/drivers/__init__.py | 13 +++--- nonebot/drivers/fastapi.py | 18 ++------- nonebot/matcher.py | 53 +++++++++++++----------- nonebot/message.py | 29 ++++++++------ nonebot/permission.py | 41 ++++++++++--------- nonebot/plugin.py | 60 ++++++++++++++-------------- nonebot/plugin.pyi | 57 ++++++++++++++------------ nonebot/plugins/base.py | 7 ++-- nonebot/rule.py | 32 ++++++++------- nonebot/typing.py | 46 ++++++++++----------- nonebot/utils.py | 3 +- 26 files changed, 247 insertions(+), 205 deletions(-) diff --git a/docs/guide/basic-configuration.md b/docs/guide/basic-configuration.md index e4c4449f..73e88382 100644 --- a/docs/guide/basic-configuration.md +++ b/docs/guide/basic-configuration.md @@ -46,6 +46,10 @@ CUSTOM_CONFIG2= # 留空则从系统环境变量读取,如不存在则为空 详细的配置项参考 [Config Reference](../api/config.md) 。 +## 系统环境变量 + +如果在系统环境变量中定义了配置,则一样会被读取。 + ## bot.py 文件 配置项也可以在 NoneBot 初始化时传入。此处可以传入任意合法 Python 变量。当然也可以在初始化完成后修改或新增。 @@ -65,4 +69,4 @@ config.custom_config4 = "new config after init" ## 优先级 -`bot.py init` > `env file` > `system env` +`bot.py init` > `system env` > `env file` diff --git a/nonebot/__init__.py b/nonebot/__init__.py index 5aafc1ef..8c26145e 100644 --- a/nonebot/__init__.py +++ b/nonebot/__init__.py @@ -26,7 +26,13 @@ import importlib import pkg_resources -from nonebot.typing import Bot, Dict, Type, Union, Driver, Optional +from typing import Dict, Type, Optional + +from nonebot.utils import escape_tag +from nonebot.config import Env, Config +from nonebot.adapters import BaseBot as Bot +from nonebot.log import logger, default_filter +from nonebot.drivers import BaseDriver as Driver _dist: pkg_resources.Distribution = pkg_resources.get_distribution("nonebot2") __version__ = _dist.version @@ -136,11 +142,6 @@ def get_bots() -> Dict[str, Bot]: return driver.bots -from nonebot.utils import escape_tag -from nonebot.config import Env, Config -from nonebot.log import logger, default_filter - - def init(*, _env_file: Optional[str] = None, **kwargs): """ :说明: diff --git a/nonebot/adapters/__init__.py b/nonebot/adapters/__init__.py index 5f5d849a..00fad277 100644 --- a/nonebot/adapters/__init__.py +++ b/nonebot/adapters/__init__.py @@ -8,12 +8,14 @@ import abc from functools import reduce, partial from dataclasses import dataclass, field +from typing import Any, Dict, Union, TypeVar, Optional, Callable, Iterable, Awaitable, Generic, TYPE_CHECKING from pydantic import BaseModel from nonebot.config import Config -from nonebot.typing import Driver, Message, WebSocket -from nonebot.typing import Any, Dict, Union, Optional, Callable, Iterable, Awaitable, TypeVar, Generic + +if TYPE_CHECKING: + from nonebot.drivers import BaseDriver as Driver, BaseWebSocket as WebSocket class BaseBot(abc.ABC): @@ -23,12 +25,12 @@ class BaseBot(abc.ABC): @abc.abstractmethod def __init__(self, - driver: Driver, + driver: "Driver", connection_type: str, config: Config, self_id: str, *, - websocket: Optional[WebSocket] = None): + websocket: Optional["WebSocket"] = None): """ :参数: @@ -60,7 +62,7 @@ class BaseBot(abc.ABC): @classmethod @abc.abstractmethod - async def check_permission(cls, driver: Driver, connection_type: str, + async def check_permission(cls, driver: "Driver", connection_type: str, headers: dict, body: Optional[dict]) -> str: """ :说明: @@ -252,7 +254,7 @@ class BaseEvent(abc.ABC, Generic[T]): @property @abc.abstractmethod - def message(self) -> Optional[Message]: + def message(self) -> Optional["BaseMessage"]: """消息内容""" raise NotImplementedError @@ -373,7 +375,9 @@ class BaseMessage(list, abc.ABC): @staticmethod @abc.abstractmethod - def _construct(msg: Union[str, dict, list]) -> Iterable[BaseMessageSegment]: + def _construct( + msg: Union[str, dict, list, + BaseModel]) -> Iterable[BaseMessageSegment]: raise NotImplementedError def __add__( diff --git a/nonebot/adapters/cqhttp/bot.py b/nonebot/adapters/cqhttp/bot.py index 7b61c1e1..5ab62586 100644 --- a/nonebot/adapters/cqhttp/bot.py +++ b/nonebot/adapters/cqhttp/bot.py @@ -3,22 +3,25 @@ import sys import hmac import json import asyncio +from typing import Any, Dict, Union, Optional, TYPE_CHECKING import httpx from nonebot.log import logger from nonebot.config import Config +from nonebot.typing import overrides from nonebot.adapters import BaseBot from nonebot.message import handle_event from nonebot.exception import RequestDenied -from nonebot.typing import Any, Dict, Union, Optional -from nonebot.typing import overrides, Driver, WebSocket from .event import Event from .message import Message, MessageSegment from .exception import NetworkError, ApiNotAvailable, ActionFailed from .utils import log +if TYPE_CHECKING: + from nonebot.drivers import BaseDriver as Driver, BaseWebSocket as WebSocket + def get_auth_bearer(access_token: Optional[str] = None) -> Optional[str]: if not access_token: @@ -212,12 +215,12 @@ class Bot(BaseBot): """ def __init__(self, - driver: Driver, + driver: "Driver", connection_type: str, config: Config, self_id: str, *, - websocket: Optional[WebSocket] = None): + websocket: Optional["WebSocket"] = None): super().__init__(driver, connection_type, @@ -235,7 +238,7 @@ class Bot(BaseBot): @classmethod @overrides(BaseBot) - async def check_permission(cls, driver: Driver, connection_type: str, + async def check_permission(cls, driver: "Driver", connection_type: str, headers: dict, body: Optional[dict]) -> str: """ :说明: diff --git a/nonebot/adapters/cqhttp/bot.pyi b/nonebot/adapters/cqhttp/bot.pyi index cf487c20..07463175 100644 --- a/nonebot/adapters/cqhttp/bot.pyi +++ b/nonebot/adapters/cqhttp/bot.pyi @@ -1,8 +1,9 @@ import asyncio +from typing import Any, Dict, List, Union, Optional from nonebot.config import Config from nonebot.adapters import BaseBot -from nonebot.typing import Any, Dict, List, Union, Driver, Optional, WebSocket +from nonebot.drivers import BaseDriver as Driver, BaseWebSocket as WebSocket from .event import Event from .message import Message, MessageSegment diff --git a/nonebot/adapters/cqhttp/event.py b/nonebot/adapters/cqhttp/event.py index 5bc959c9..e813d24b 100644 --- a/nonebot/adapters/cqhttp/event.py +++ b/nonebot/adapters/cqhttp/event.py @@ -1,5 +1,7 @@ +from typing import Optional + +from nonebot.typing import overrides from nonebot.adapters import BaseEvent -from nonebot.typing import Optional, overrides from .message import Message diff --git a/nonebot/adapters/cqhttp/exception.py b/nonebot/adapters/cqhttp/exception.py index 2bcc73f4..d91bcc64 100644 --- a/nonebot/adapters/cqhttp/exception.py +++ b/nonebot/adapters/cqhttp/exception.py @@ -1,4 +1,5 @@ -from nonebot.typing import Optional +from typing import Optional + from nonebot.exception import (AdapterException, ActionFailed as BaseActionFailed, NetworkError as BaseNetworkError, ApiNotAvailable as diff --git a/nonebot/adapters/cqhttp/message.py b/nonebot/adapters/cqhttp/message.py index 47d21bc8..acc6c6cf 100644 --- a/nonebot/adapters/cqhttp/message.py +++ b/nonebot/adapters/cqhttp/message.py @@ -1,6 +1,7 @@ import re +from typing import Any, Dict, Union, Tuple, Iterable, Optional -from nonebot.typing import Any, Dict, Union, Tuple, Iterable, Optional, overrides +from nonebot.typing import overrides from nonebot.adapters import BaseMessage, BaseMessageSegment from .utils import log, escape, unescape, _b2s diff --git a/nonebot/adapters/cqhttp/utils.py b/nonebot/adapters/cqhttp/utils.py index ecfee872..747b964f 100644 --- a/nonebot/adapters/cqhttp/utils.py +++ b/nonebot/adapters/cqhttp/utils.py @@ -1,4 +1,5 @@ -from nonebot.typing import Optional +from typing import Optional + from nonebot.utils import logger_wrapper log = logger_wrapper("CQHTTP") diff --git a/nonebot/adapters/ding/bot.py b/nonebot/adapters/ding/bot.py index 840685c2..c8c9ad20 100644 --- a/nonebot/adapters/ding/bot.py +++ b/nonebot/adapters/ding/bot.py @@ -1,6 +1,7 @@ import hmac import base64 from datetime import datetime +from typing import Any, Union, Optional, TYPE_CHECKING import httpx from nonebot.log import logger @@ -8,7 +9,6 @@ from nonebot.config import Config from nonebot.adapters import BaseBot from nonebot.message import handle_event from nonebot.exception import RequestDenied -from nonebot.typing import Any, Union, Driver, Optional from .utils import log from .event import Event @@ -16,13 +16,16 @@ from .model import MessageModel from .message import Message, MessageSegment from .exception import NetworkError, ApiNotAvailable, ActionFailed, SessionExpired +if TYPE_CHECKING: + from nonebot.drivers import BaseDriver as Driver + class Bot(BaseBot): """ 钉钉 协议 Bot 适配。继承属性参考 `BaseBot <./#class-basebot>`_ 。 """ - def __init__(self, driver: Driver, connection_type: str, config: Config, + def __init__(self, driver: "Driver", connection_type: str, config: Config, self_id: str, **kwargs): super().__init__(driver, connection_type, config, self_id, **kwargs) @@ -35,7 +38,7 @@ class Bot(BaseBot): return "ding" @classmethod - async def check_permission(cls, driver: Driver, connection_type: str, + async def check_permission(cls, driver: "Driver", connection_type: str, headers: dict, body: Optional[dict]) -> str: """ :说明: diff --git a/nonebot/adapters/ding/event.py b/nonebot/adapters/ding/event.py index 876ad493..ccd1ab47 100644 --- a/nonebot/adapters/ding/event.py +++ b/nonebot/adapters/ding/event.py @@ -1,5 +1,6 @@ +from typing import Union, Optional + from nonebot.adapters import BaseEvent -from nonebot.typing import Union, Optional from .message import Message from .model import MessageModel, ConversationType, TextMessage diff --git a/nonebot/adapters/ding/exception.py b/nonebot/adapters/ding/exception.py index b1d74d14..37276eaa 100644 --- a/nonebot/adapters/ding/exception.py +++ b/nonebot/adapters/ding/exception.py @@ -1,4 +1,5 @@ -from nonebot.typing import Optional +from typing import Optional + from nonebot.exception import (AdapterException, ActionFailed as BaseActionFailed, ApiNotAvailable as BaseApiNotAvailable, NetworkError as diff --git a/nonebot/adapters/ding/message.py b/nonebot/adapters/ding/message.py index d8e88314..c8b39488 100644 --- a/nonebot/adapters/ding/message.py +++ b/nonebot/adapters/ding/message.py @@ -1,5 +1,7 @@ -from nonebot.typing import Any, Dict, Union, Iterable +from typing import Any, Dict, Union, Iterable + from nonebot.adapters import BaseMessage, BaseMessageSegment + from .utils import log from .model import TextMessage diff --git a/nonebot/adapters/ding/model.py b/nonebot/adapters/ding/model.py index d317ea5b..8f0cbe1c 100644 --- a/nonebot/adapters/ding/model.py +++ b/nonebot/adapters/ding/model.py @@ -1,5 +1,6 @@ -from typing import List, Optional from enum import Enum +from typing import List, Optional + from pydantic import BaseModel diff --git a/nonebot/config.py b/nonebot/config.py index 756ef945..e1ac2b71 100644 --- a/nonebot/config.py +++ b/nonebot/config.py @@ -18,12 +18,11 @@ import os from pathlib import Path from datetime import timedelta from ipaddress import IPv4Address +from typing import Any, Set, Dict, Union, Mapping, Optional from pydantic import BaseSettings, IPvAnyAddress from pydantic.env_settings import SettingsError, env_file_sentinel, read_env_file -from nonebot.typing import Any, Set, Dict, Union, Mapping, Optional - class BaseConfig(BaseSettings): @@ -56,7 +55,7 @@ class BaseConfig(BaseSettings): for field in self.__fields__.values(): env_val: Optional[str] = None - for env_name in field.field_info.extra['env_names']: + for env_name in field.field_info.extra["env_names"]: env_val = env_vars.get(env_name) if env_name in env_file_vars: del env_file_vars[env_name] diff --git a/nonebot/drivers/__init__.py b/nonebot/drivers/__init__.py index 8227690f..af4f960e 100644 --- a/nonebot/drivers/__init__.py +++ b/nonebot/drivers/__init__.py @@ -6,10 +6,13 @@ """ import abc +from typing import Dict, Type, Optional, Callable, TYPE_CHECKING from nonebot.log import logger from nonebot.config import Env, Config -from nonebot.typing import Bot, Dict, Type, Union, Optional, Callable + +if TYPE_CHECKING: + from nonebot.adapters import BaseBot as Bot class BaseDriver(abc.ABC): @@ -17,7 +20,7 @@ class BaseDriver(abc.ABC): Driver 基类。将后端框架封装,以满足适配器使用。 """ - _adapters: Dict[str, Type[Bot]] = {} + _adapters: Dict[str, Type["Bot"]] = {} """ :类型: ``Dict[str, Type[Bot]]`` :说明: 已注册的适配器列表 @@ -41,14 +44,14 @@ class BaseDriver(abc.ABC): :类型: ``Config`` :说明: 配置对象 """ - self._clients: Dict[str, Bot] = {} + self._clients: Dict[str, "Bot"] = {} """ :类型: ``Dict[str, Bot]`` :说明: 已连接的 Bot """ @classmethod - def register_adapter(cls, name: str, adapter: Type[Bot]): + def register_adapter(cls, name: str, adapter: Type["Bot"]): """ :说明: @@ -88,7 +91,7 @@ class BaseDriver(abc.ABC): raise NotImplementedError @property - def bots(self) -> Dict[str, Bot]: + def bots(self) -> Dict[str, "Bot"]: """ :类型: ``Dict[str, Bot]`` :说明: 获取当前所有已连接的 Bot diff --git a/nonebot/drivers/fastapi.py b/nonebot/drivers/fastapi.py index 841b82e5..073f3748 100644 --- a/nonebot/drivers/fastapi.py +++ b/nonebot/drivers/fastapi.py @@ -8,34 +8,22 @@ FastAPI 驱动适配 https://fastapi.tiangolo.com/ """ -import hmac import json import asyncio import logging +from typing import Optional, Callable import uvicorn from fastapi.responses import Response -from fastapi import Body, status, Header, Request, FastAPI, Depends, HTTPException +from fastapi import Body, status, Request, FastAPI, HTTPException from starlette.websockets import WebSocketDisconnect, WebSocket as FastAPIWebSocket from nonebot.log import logger +from nonebot.typing import overrides from nonebot.config import Env, Config from nonebot.utils import DataclassEncoder from nonebot.exception import RequestDenied from nonebot.drivers import BaseDriver, BaseWebSocket -from nonebot.typing import Optional, Callable, overrides - - -def get_auth_bearer(access_token: Optional[str] = Header( - None, alias="Authorization")): - if not access_token: - return None - scheme, _, param = access_token.partition(" ") - if scheme.lower() not in ["bearer", "token"]: - raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, - detail="Not authenticated", - headers={"WWW-Authenticate": "Bearer"}) - return param class Driver(BaseDriver): diff --git a/nonebot/matcher.py b/nonebot/matcher.py index 1c2eabe6..e91a4e26 100644 --- a/nonebot/matcher.py +++ b/nonebot/matcher.py @@ -5,20 +5,25 @@ 该模块实现事件响应器的创建与运行,并提供一些快捷方法来帮助用户更好的与机器人进行 对话 。 """ -from nonebot.log import logger import typing import inspect from functools import wraps from datetime import datetime from contextvars import ContextVar from collections import defaultdict +from typing import Type, List, Dict, Union, Callable, Optional, NoReturn, TYPE_CHECKING from nonebot.rule import Rule +from nonebot.log import logger from nonebot.permission import Permission, USER -from nonebot.typing import Type, List, Dict, Union, Callable, Optional, NoReturn -from nonebot.typing import Bot, Event, Handler, Message, ArgsParser, MessageSegment +from nonebot.typing import State, Handler, ArgsParser from nonebot.exception import PausedException, RejectedException, FinishedException +if TYPE_CHECKING: + from nonebot.adapters import (BaseBot as Bot, BaseEvent as Event, + BaseMessage as Message, BaseMessageSegment as + MessageSegment) + matchers: Dict[int, List[Type["Matcher"]]] = defaultdict(list) """ :类型: ``Dict[int, List[Type[Matcher]]]`` @@ -88,9 +93,9 @@ class Matcher(metaclass=MatcherMeta): :说明: 事件响应器过期时间点 """ - _default_state: dict = {} + _default_state: State = {} """ - :类型: ``dict`` + :类型: ``State`` :说明: 事件响应器默认状态 """ @@ -123,7 +128,7 @@ class Matcher(metaclass=MatcherMeta): block: bool = False, *, module: Optional[str] = None, - default_state: Optional[dict] = None, + default_state: Optional[State] = None, expire_time: Optional[datetime] = None) -> Type["Matcher"]: """ :说明: @@ -140,7 +145,7 @@ class Matcher(metaclass=MatcherMeta): * ``priority: int``: 响应优先级 * ``block: bool``: 是否阻止事件向更低优先级的响应器传播 * ``module: Optional[str]``: 事件响应器所在模块名称 - * ``default_state: Optional[dict]``: 默认状态 ``state`` + * ``default_state: Optional[State]``: 默认状态 ``state`` * ``expire_time: Optional[datetime]``: 事件响应器最终有效时间点,过时即被删除 :返回: @@ -167,7 +172,7 @@ class Matcher(metaclass=MatcherMeta): return NewMatcher @classmethod - async def check_perm(cls, bot: Bot, event: Event) -> bool: + async def check_perm(cls, bot: "Bot", event: "Event") -> bool: """ :说明: @@ -185,7 +190,7 @@ class Matcher(metaclass=MatcherMeta): return await cls.permission(bot, event) @classmethod - async def check_rule(cls, bot: Bot, event: Event, state: dict) -> bool: + async def check_rule(cls, bot: "Bot", event: "Event", state: State) -> bool: """ :说明: @@ -195,7 +200,7 @@ class Matcher(metaclass=MatcherMeta): * ``bot: Bot``: Bot 对象 * ``event: Event``: 上报事件 - * ``state: dict``: 当前状态 + * ``state: State``: 当前状态 :返回: @@ -248,7 +253,8 @@ class Matcher(metaclass=MatcherMeta): * 无 """ - async def _receive(bot: Bot, event: Event, state: dict) -> NoReturn: + async def _receive(bot: "Bot", event: "Event", + state: State) -> NoReturn: raise PausedException if cls.handlers: @@ -267,7 +273,7 @@ class Matcher(metaclass=MatcherMeta): def got( cls, key: str, - prompt: Optional[Union[str, Message, MessageSegment]] = None, + prompt: Optional[Union[str, "Message", "MessageSegment"]] = None, args_parser: Optional[ArgsParser] = None ) -> Callable[[Handler], Handler]: """ @@ -282,7 +288,7 @@ class Matcher(metaclass=MatcherMeta): * ``args_parser: Optional[ArgsParser]``: 可选参数解析函数,空则使用默认解析函数 """ - async def _key_getter(bot: Bot, event: Event, state: dict): + async def _key_getter(bot: "Bot", event: "Event", state: State): state["_current_key"] = key if key not in state: if prompt: @@ -292,7 +298,7 @@ class Matcher(metaclass=MatcherMeta): else: state["_skip_key"] = True - async def _key_parser(bot: Bot, event: Event, state: dict): + async def _key_parser(bot: "Bot", event: "Event", state: State): if key in state and state.get("_skip_key"): del state["_skip_key"] return @@ -310,7 +316,7 @@ class Matcher(metaclass=MatcherMeta): parser = cls.handlers.pop() @wraps(func) - async def wrapper(bot: Bot, event: Event, state: dict): + async def wrapper(bot: "Bot", event: "Event", state: State): await parser(bot, event, state) await func(bot, event, state) if "_current_key" in state: @@ -323,7 +329,8 @@ class Matcher(metaclass=MatcherMeta): return _decorator @classmethod - async def send(cls, message: Union[str, Message, MessageSegment], **kwargs): + async def send(cls, message: Union[str, "Message", "MessageSegment"], + **kwargs): """ :说明: @@ -340,8 +347,8 @@ class Matcher(metaclass=MatcherMeta): @classmethod async def finish(cls, - message: Optional[Union[str, Message, - MessageSegment]] = None, + message: Optional[Union[str, "Message", + "MessageSegment"]] = None, **kwargs) -> NoReturn: """ :说明: @@ -361,8 +368,8 @@ class Matcher(metaclass=MatcherMeta): @classmethod async def pause(cls, - prompt: Optional[Union[str, Message, - MessageSegment]] = None, + prompt: Optional[Union[str, "Message", + "MessageSegment"]] = None, **kwargs) -> NoReturn: """ :说明: @@ -382,8 +389,8 @@ class Matcher(metaclass=MatcherMeta): @classmethod async def reject(cls, - prompt: Optional[Union[str, Message, - MessageSegment]] = None, + prompt: Optional[Union[str, "Message", + "MessageSegment"]] = None, **kwargs) -> NoReturn: """ :说明: @@ -402,7 +409,7 @@ class Matcher(metaclass=MatcherMeta): raise RejectedException # 运行handlers - async def run(self, bot: Bot, event: Event, state: dict): + async def run(self, bot: "Bot", event: "Event", state: State): b_t = current_bot.set(bot) e_t = current_event.set(event) try: diff --git a/nonebot/message.py b/nonebot/message.py index d13731f8..89e04835 100644 --- a/nonebot/message.py +++ b/nonebot/message.py @@ -7,14 +7,17 @@ NoneBot 内部处理并按优先级分发事件给所有事件响应器,提供 import asyncio from datetime import datetime +from typing import Set, Type, Optional, Iterable, TYPE_CHECKING from nonebot.log import logger from nonebot.rule import TrieRule from nonebot.utils import escape_tag from nonebot.matcher import matchers, Matcher -from nonebot.typing import Set, Type, Optional, Iterable, Bot, Event from nonebot.exception import IgnoredException, StopPropagation -from nonebot.typing import EventPreProcessor, RunPreProcessor, EventPostProcessor, RunPostProcessor +from nonebot.typing import State, EventPreProcessor, RunPreProcessor, EventPostProcessor, RunPostProcessor + +if TYPE_CHECKING: + from nonebot.adapters import BaseBot as Bot, BaseEvent as Event _event_preprocessors: Set[EventPreProcessor] = set() _event_postprocessors: Set[EventPostProcessor] = set() @@ -34,7 +37,7 @@ def event_preprocessor(func: EventPreProcessor) -> EventPreProcessor: * ``bot: Bot``: Bot 对象 * ``event: Event``: Event 对象 - * ``state: dict``: 当前 State + * ``state: State``: 当前 State """ _event_preprocessors.add(func) return func @@ -52,7 +55,7 @@ def event_postprocessor(func: EventPostProcessor) -> EventPostProcessor: * ``bot: Bot``: Bot 对象 * ``event: Event``: Event 对象 - * ``state: dict``: 当前事件运行前 State + * ``state: State``: 当前事件运行前 State """ _event_postprocessors.add(func) return func @@ -71,7 +74,7 @@ def run_preprocessor(func: RunPreProcessor) -> RunPreProcessor: * ``matcher: Matcher``: 当前要运行的事件响应器 * ``bot: Bot``: Bot 对象 * ``event: Event``: Event 对象 - * ``state: dict``: 当前 State + * ``state: State``: 当前 State """ _run_preprocessors.add(func) return func @@ -91,18 +94,18 @@ def run_postprocessor(func: RunPostProcessor) -> RunPostProcessor: * ``exception: Optional[Exception]``: 事件响应器运行错误(如果存在) * ``bot: Bot``: Bot 对象 * ``event: Event``: Event 对象 - * ``state: dict``: 当前 State + * ``state: State``: 当前 State """ _run_postprocessors.add(func) return func -async def _check_matcher(priority: int, bot: Bot, event: Event, - state: dict) -> Iterable[Type[Matcher]]: +async def _check_matcher(priority: int, bot: "Bot", event: "Event", + state: State) -> Iterable[Type[Matcher]]: current_matchers = matchers[priority].copy() - async def _check(Matcher: Type[Matcher], bot: Bot, event: Event, - state: dict) -> Optional[Type[Matcher]]: + async def _check(Matcher: Type[Matcher], bot: "Bot", event: "Event", + state: State) -> Optional[Type[Matcher]]: try: if (not Matcher.expire_time or datetime.now() <= Matcher.expire_time ) and await Matcher.check_perm( @@ -136,8 +139,8 @@ async def _check_matcher(priority: int, bot: Bot, event: Event, return filter(lambda x: x, results) -async def _run_matcher(Matcher: Type[Matcher], bot: Bot, event: Event, - state: dict) -> None: +async def _run_matcher(Matcher: Type[Matcher], bot: "Bot", event: "Event", + state: State) -> None: logger.info(f"Event will be handled by {Matcher}") matcher = Matcher() @@ -186,7 +189,7 @@ async def _run_matcher(Matcher: Type[Matcher], bot: Bot, event: Event, return -async def handle_event(bot: Bot, event: Event): +async def handle_event(bot: "Bot", event: "Event"): """ :说明: diff --git a/nonebot/permission.py b/nonebot/permission.py index f4c793b0..62d5f62f 100644 --- a/nonebot/permission.py +++ b/nonebot/permission.py @@ -10,16 +10,21 @@ """ import asyncio +from typing import Union, Optional, Callable, NoReturn, Awaitable, TYPE_CHECKING from nonebot.utils import run_sync -from nonebot.typing import Bot, Event, Union, NoReturn, Optional, Callable, Awaitable, PermissionChecker +from nonebot.typing import PermissionChecker + +if TYPE_CHECKING: + from nonebot.adapters import BaseBot as Bot, BaseEvent as Event class Permission: __slots__ = ("checkers",) - def __init__(self, *checkers: Callable[[Bot, Event], - Awaitable[bool]]) -> None: + def __init__( + self, *checkers: Callable[["Bot", "Event"], + Awaitable[bool]]) -> None: """ :参数: @@ -36,7 +41,7 @@ class Permission: * ``Set[Callable[[Bot, Event], Awaitable[bool]]]`` """ - async def __call__(self, bot: Bot, event: Event) -> bool: + async def __call__(self, bot: "Bot", event: "Event") -> bool: """ :说明: @@ -75,19 +80,19 @@ class Permission: return Permission(*checkers) -async def _message(bot: Bot, event: Event) -> bool: +async def _message(bot: "Bot", event: "Event") -> bool: return event.type == "message" -async def _notice(bot: Bot, event: Event) -> bool: +async def _notice(bot: "Bot", event: "Event") -> bool: return event.type == "notice" -async def _request(bot: Bot, event: Event) -> bool: +async def _request(bot: "Bot", event: "Event") -> bool: return event.type == "request" -async def _metaevent(bot: Bot, event: Event) -> bool: +async def _metaevent(bot: "Bot", event: "Event") -> bool: return event.type == "meta_event" @@ -121,28 +126,28 @@ def USER(*user: int, perm: Permission = Permission()): * ``perm: Permission``: 需要同时满足的权限 """ - async def _user(bot: Bot, event: Event) -> bool: + async def _user(bot: "Bot", event: "Event") -> bool: return event.type == "message" and event.user_id in user and await perm( bot, event) return Permission(_user) -async def _private(bot: Bot, event: Event) -> bool: +async def _private(bot: "Bot", event: "Event") -> bool: return event.type == "message" and event.detail_type == "private" -async def _private_friend(bot: Bot, event: Event) -> bool: +async def _private_friend(bot: "Bot", event: "Event") -> bool: return (event.type == "message" and event.detail_type == "private" and event.sub_type == "friend") -async def _private_group(bot: Bot, event: Event) -> bool: +async def _private_group(bot: "Bot", event: "Event") -> bool: return (event.type == "message" and event.detail_type == "private" and event.sub_type == "group") -async def _private_other(bot: Bot, event: Event) -> bool: +async def _private_other(bot: "Bot", event: "Event") -> bool: return (event.type == "message" and event.detail_type == "private" and event.sub_type == "other") @@ -165,21 +170,21 @@ PRIVATE_OTHER = Permission(_private_other) """ -async def _group(bot: Bot, event: Event) -> bool: +async def _group(bot: "Bot", event: "Event") -> bool: return event.type == "message" and event.detail_type == "group" -async def _group_member(bot: Bot, event: Event) -> bool: +async def _group_member(bot: "Bot", event: "Event") -> bool: return (event.type == "message" and event.detail_type == "group" and event.sender.get("role") == "member") -async def _group_admin(bot: Bot, event: Event) -> bool: +async def _group_admin(bot: "Bot", event: "Event") -> bool: return (event.type == "message" and event.detail_type == "group" and event.sender.get("role") == "admin") -async def _group_owner(bot: Bot, event: Event) -> bool: +async def _group_owner(bot: "Bot", event: "Event") -> bool: return (event.type == "message" and event.detail_type == "group" and event.sender.get("role") == "owner") @@ -206,7 +211,7 @@ GROUP_OWNER = Permission(_group_owner) """ -async def _superuser(bot: Bot, event: Event) -> bool: +async def _superuser(bot: "Bot", event: "Event") -> bool: return event.type == "message" and event.user_id in bot.config.superusers diff --git a/nonebot/plugin.py b/nonebot/plugin.py index 57410566..527ff533 100644 --- a/nonebot/plugin.py +++ b/nonebot/plugin.py @@ -9,17 +9,17 @@ import re import sys import pkgutil import importlib -from datetime import datetime +from types import ModuleType from dataclasses import dataclass from importlib._bootstrap import _load from contextvars import Context, ContextVar, copy_context +from typing import Any, Set, List, Dict, Type, Tuple, Union, Optional from nonebot.log import logger from nonebot.matcher import Matcher from nonebot.permission import Permission -from nonebot.typing import Handler, RuleChecker +from nonebot.typing import State, Handler, RuleChecker from nonebot.rule import Rule, startswith, endswith, keyword, command, regex -from nonebot.typing import Any, Set, List, Dict, Type, Tuple, Union, Optional, ArgsParser, ModuleType plugins: Dict[str, "Plugin"] = {} """ @@ -108,7 +108,7 @@ def on(type: str = "", temp: bool = False, priority: int = 1, block: bool = False, - state: Optional[dict] = None) -> Type[Matcher]: + state: Optional[State] = None) -> Type[Matcher]: """ :说明: @@ -123,7 +123,7 @@ def on(type: str = "", * ``temp: bool``: 是否为临时事件响应器(仅执行一次) * ``priority: int``: 事件响应器优先级 * ``block: bool``: 是否阻止事件向更低优先级传递 - * ``state: Optional[dict]``: 默认的 state + * ``state: Optional[State]``: 默认的 state :返回: @@ -147,7 +147,7 @@ def on_metaevent(rule: Optional[Union[Rule, RuleChecker]] = None, temp: bool = False, priority: int = 1, block: bool = False, - state: Optional[dict] = None) -> Type[Matcher]: + state: Optional[State] = None) -> Type[Matcher]: """ :说明: @@ -160,7 +160,7 @@ def on_metaevent(rule: Optional[Union[Rule, RuleChecker]] = None, * ``temp: bool``: 是否为临时事件响应器(仅执行一次) * ``priority: int``: 事件响应器优先级 * ``block: bool``: 是否阻止事件向更低优先级传递 - * ``state: Optional[dict]``: 默认的 state + * ``state: Optional[State]``: 默认的 state :返回: @@ -185,7 +185,7 @@ def on_message(rule: Optional[Union[Rule, RuleChecker]] = None, temp: bool = False, priority: int = 1, block: bool = True, - state: Optional[dict] = None) -> Type[Matcher]: + state: Optional[State] = None) -> Type[Matcher]: """ :说明: @@ -199,7 +199,7 @@ def on_message(rule: Optional[Union[Rule, RuleChecker]] = None, * ``temp: bool``: 是否为临时事件响应器(仅执行一次) * ``priority: int``: 事件响应器优先级 * ``block: bool``: 是否阻止事件向更低优先级传递 - * ``state: Optional[dict]``: 默认的 state + * ``state: Optional[State]``: 默认的 state :返回: @@ -223,7 +223,7 @@ def on_notice(rule: Optional[Union[Rule, RuleChecker]] = None, temp: bool = False, priority: int = 1, block: bool = False, - state: Optional[dict] = None) -> Type[Matcher]: + state: Optional[State] = None) -> Type[Matcher]: """ :说明: @@ -236,7 +236,7 @@ def on_notice(rule: Optional[Union[Rule, RuleChecker]] = None, * ``temp: bool``: 是否为临时事件响应器(仅执行一次) * ``priority: int``: 事件响应器优先级 * ``block: bool``: 是否阻止事件向更低优先级传递 - * ``state: Optional[dict]``: 默认的 state + * ``state: Optional[State]``: 默认的 state :返回: @@ -260,7 +260,7 @@ def on_request(rule: Optional[Union[Rule, RuleChecker]] = None, temp: bool = False, priority: int = 1, block: bool = False, - state: Optional[dict] = None) -> Type[Matcher]: + state: Optional[State] = None) -> Type[Matcher]: """ :说明: @@ -273,7 +273,7 @@ def on_request(rule: Optional[Union[Rule, RuleChecker]] = None, * ``temp: bool``: 是否为临时事件响应器(仅执行一次) * ``priority: int``: 事件响应器优先级 * ``block: bool``: 是否阻止事件向更低优先级传递 - * ``state: Optional[dict]``: 默认的 state + * ``state: Optional[State]``: 默认的 state :返回: @@ -308,7 +308,7 @@ def on_startswith(msg: str, * ``temp: bool``: 是否为临时事件响应器(仅执行一次) * ``priority: int``: 事件响应器优先级 * ``block: bool``: 是否阻止事件向更低优先级传递 - * ``state: Optional[dict]``: 默认的 state + * ``state: Optional[State]``: 默认的 state :返回: @@ -334,7 +334,7 @@ def on_endswith(msg: str, * ``temp: bool``: 是否为临时事件响应器(仅执行一次) * ``priority: int``: 事件响应器优先级 * ``block: bool``: 是否阻止事件向更低优先级传递 - * ``state: Optional[dict]``: 默认的 state + * ``state: Optional[State]``: 默认的 state :返回: @@ -360,7 +360,7 @@ def on_keyword(keywords: Set[str], * ``temp: bool``: 是否为临时事件响应器(仅执行一次) * ``priority: int``: 事件响应器优先级 * ``block: bool``: 是否阻止事件向更低优先级传递 - * ``state: Optional[dict]``: 默认的 state + * ``state: Optional[State]``: 默认的 state :返回: @@ -390,14 +390,14 @@ def on_command(cmd: Union[str, Tuple[str, ...]], * ``temp: bool``: 是否为临时事件响应器(仅执行一次) * ``priority: int``: 事件响应器优先级 * ``block: bool``: 是否阻止事件向更低优先级传递 - * ``state: Optional[dict]``: 默认的 state + * ``state: Optional[State]``: 默认的 state :返回: - ``Type[Matcher]`` """ - async def _strip_cmd(bot, event, state: dict): + async def _strip_cmd(bot, event, state: State): message = event.message event.message = message.__class__( str(message)[len(state["_prefix"]["raw_command"]):].strip()) @@ -430,7 +430,7 @@ def on_regex(pattern: str, * ``temp: bool``: 是否为临时事件响应器(仅执行一次) * ``priority: int``: 事件响应器优先级 * ``block: bool``: 是否阻止事件向更低优先级传递 - * ``state: Optional[dict]``: 默认的 state + * ``state: Optional[State]``: 默认的 state :返回: @@ -521,7 +521,7 @@ class MatcherGroup: * ``temp: bool``: 是否为临时事件响应器(仅执行一次) * ``priority: int``: 事件响应器优先级 * ``block: bool``: 是否阻止事件向更低优先级传递 - * ``state: Optional[dict]``: 默认的 state + * ``state: Optional[State]``: 默认的 state :返回: @@ -547,7 +547,7 @@ class MatcherGroup: * ``temp: bool``: 是否为临时事件响应器(仅执行一次) * ``priority: int``: 事件响应器优先级 * ``block: bool``: 是否阻止事件向更低优先级传递 - * ``state: Optional[dict]``: 默认的 state + * ``state: Optional[State]``: 默认的 state :返回: @@ -575,7 +575,7 @@ class MatcherGroup: * ``temp: bool``: 是否为临时事件响应器(仅执行一次) * ``priority: int``: 事件响应器优先级 * ``block: bool``: 是否阻止事件向更低优先级传递 - * ``state: Optional[dict]``: 默认的 state + * ``state: Optional[State]``: 默认的 state :返回: @@ -602,7 +602,7 @@ class MatcherGroup: * ``temp: bool``: 是否为临时事件响应器(仅执行一次) * ``priority: int``: 事件响应器优先级 * ``block: bool``: 是否阻止事件向更低优先级传递 - * ``state: Optional[dict]``: 默认的 state + * ``state: Optional[State]``: 默认的 state :返回: @@ -629,7 +629,7 @@ class MatcherGroup: * ``temp: bool``: 是否为临时事件响应器(仅执行一次) * ``priority: int``: 事件响应器优先级 * ``block: bool``: 是否阻止事件向更低优先级传递 - * ``state: Optional[dict]``: 默认的 state + * ``state: Optional[State]``: 默认的 state :返回: @@ -661,7 +661,7 @@ class MatcherGroup: * ``temp: bool``: 是否为临时事件响应器(仅执行一次) * ``priority: int``: 事件响应器优先级 * ``block: bool``: 是否阻止事件向更低优先级传递 - * ``state: Optional[dict]``: 默认的 state + * ``state: Optional[State]``: 默认的 state :返回: @@ -687,7 +687,7 @@ class MatcherGroup: * ``temp: bool``: 是否为临时事件响应器(仅执行一次) * ``priority: int``: 事件响应器优先级 * ``block: bool``: 是否阻止事件向更低优先级传递 - * ``state: Optional[dict]``: 默认的 state + * ``state: Optional[State]``: 默认的 state :返回: @@ -713,7 +713,7 @@ class MatcherGroup: * ``temp: bool``: 是否为临时事件响应器(仅执行一次) * ``priority: int``: 事件响应器优先级 * ``block: bool``: 是否阻止事件向更低优先级传递 - * ``state: Optional[dict]``: 默认的 state + * ``state: Optional[State]``: 默认的 state :返回: @@ -743,14 +743,14 @@ class MatcherGroup: * ``temp: bool``: 是否为临时事件响应器(仅执行一次) * ``priority: int``: 事件响应器优先级 * ``block: bool``: 是否阻止事件向更低优先级传递 - * ``state: Optional[dict]``: 默认的 state + * ``state: Optional[State]``: 默认的 state :返回: - ``Type[Matcher]`` """ - async def _strip_cmd(bot, event, state: dict): + async def _strip_cmd(bot, event, state: State): message = event.message event.message = message.__class__( str(message)[len(state["_prefix"]["raw_command"]):].strip()) @@ -785,7 +785,7 @@ class MatcherGroup: * ``temp: bool``: 是否为临时事件响应器(仅执行一次) * ``priority: int``: 事件响应器优先级 * ``block: bool``: 是否阻止事件向更低优先级传递 - * ``state: Optional[dict]``: 默认的 state + * ``state: Optional[State]``: 默认的 state :返回: diff --git a/nonebot/plugin.pyi b/nonebot/plugin.pyi index 607c1d27..a4617f80 100644 --- a/nonebot/plugin.pyi +++ b/nonebot/plugin.pyi @@ -1,8 +1,12 @@ import re +from types import ModuleType from contextvars import ContextVar +from typing import Any, Set, List, Dict, Type, Tuple, Union, Optional -from nonebot.typing import Rule, Matcher, Handler, Permission, RuleChecker -from nonebot.typing import Set, List, Dict, Type, Tuple, Union, Optional, ModuleType +from nonebot.rule import Rule +from nonebot.matcher import Matcher +from nonebot.permission import Permission +from nonebot.typing import State, Handler, RuleChecker plugins: Dict[str, "Plugin"] = ... @@ -37,7 +41,7 @@ def on(type: str = ..., temp: bool = ..., priority: int = ..., block: bool = ..., - state: Optional[dict] = ...) -> Type[Matcher]: + state: Optional[State] = ...) -> Type[Matcher]: ... @@ -47,7 +51,7 @@ def on_metaevent(rule: Optional[Union[Rule, RuleChecker]] = ..., temp: bool = ..., priority: int = ..., block: bool = ..., - state: Optional[dict] = ...) -> Type[Matcher]: + state: Optional[State] = ...) -> Type[Matcher]: ... @@ -58,7 +62,7 @@ def on_message(rule: Optional[Union[Rule, RuleChecker]] = ..., temp: bool = ..., priority: int = ..., block: bool = ..., - state: Optional[dict] = ...) -> Type[Matcher]: + state: Optional[State] = ...) -> Type[Matcher]: ... @@ -68,7 +72,7 @@ def on_notice(rule: Optional[Union[Rule, RuleChecker]] = ..., temp: bool = ..., priority: int = ..., block: bool = ..., - state: Optional[dict] = ...) -> Type[Matcher]: + state: Optional[State] = ...) -> Type[Matcher]: ... @@ -78,7 +82,7 @@ def on_request(rule: Optional[Union[Rule, RuleChecker]] = ..., temp: bool = ..., priority: int = ..., block: bool = ..., - state: Optional[dict] = ...) -> Type[Matcher]: + state: Optional[State] = ...) -> Type[Matcher]: ... @@ -90,7 +94,7 @@ def on_startswith(msg: str, temp: bool = ..., priority: int = ..., block: bool = ..., - state: Optional[dict] = ...) -> Type[Matcher]: + state: Optional[State] = ...) -> Type[Matcher]: ... @@ -102,7 +106,7 @@ def on_endswith(msg: str, temp: bool = ..., priority: int = ..., block: bool = ..., - state: Optional[dict] = ...) -> Type[Matcher]: + state: Optional[State] = ...) -> Type[Matcher]: ... @@ -114,7 +118,7 @@ def on_keyword(keywords: Set[str], temp: bool = ..., priority: int = ..., block: bool = ..., - state: Optional[dict] = ...) -> Type[Matcher]: + state: Optional[State] = ...) -> Type[Matcher]: ... @@ -127,7 +131,7 @@ def on_command(cmd: Union[str, Tuple[str, ...]], temp: bool = ..., priority: int = ..., block: bool = ..., - state: Optional[dict] = ...) -> Type[Matcher]: + state: Optional[State] = ...) -> Type[Matcher]: ... @@ -140,7 +144,7 @@ def on_regex(pattern: str, temp: bool = ..., priority: int = ..., block: bool = ..., - state: Optional[dict] = ...) -> Type[Matcher]: + state: Optional[State] = ...) -> Type[Matcher]: ... @@ -183,8 +187,9 @@ class CommandGroup: temp: bool = ..., priority: int = ..., block: bool = ..., - state: Optional[dict] = ...): - ... + state: Optional[State] = ...): + self.basecmd: Tuple[str, ...] = ... + self.base_kwargs: Dict[str, Any] = ... def command(self, cmd: Union[str, Tuple[str, ...]], @@ -196,7 +201,7 @@ class CommandGroup: temp: bool = ..., priority: int = ..., block: bool = ..., - state: Optional[dict] = ...) -> Type[Matcher]: + state: Optional[State] = ...) -> Type[Matcher]: ... @@ -211,7 +216,7 @@ class MatcherGroup: temp: bool = ..., priority: int = ..., block: bool = ..., - state: Optional[dict] = ...): + state: Optional[State] = ...): ... def on(self, @@ -223,7 +228,7 @@ class MatcherGroup: temp: bool = ..., priority: int = ..., block: bool = ..., - state: Optional[dict] = ...) -> Type[Matcher]: + state: Optional[State] = ...) -> Type[Matcher]: ... def on_metaevent(self, @@ -233,7 +238,7 @@ class MatcherGroup: temp: bool = False, priority: int = 1, block: bool = False, - state: Optional[dict] = None) -> Type[Matcher]: + state: Optional[State] = None) -> Type[Matcher]: ... def on_message(self, @@ -244,7 +249,7 @@ class MatcherGroup: temp: bool = False, priority: int = 1, block: bool = True, - state: Optional[dict] = None) -> Type[Matcher]: + state: Optional[State] = None) -> Type[Matcher]: ... def on_notice(self, @@ -254,7 +259,7 @@ class MatcherGroup: temp: bool = False, priority: int = 1, block: bool = False, - state: Optional[dict] = None) -> Type[Matcher]: + state: Optional[State] = None) -> Type[Matcher]: ... def on_request(self, @@ -264,7 +269,7 @@ class MatcherGroup: temp: bool = False, priority: int = 1, block: bool = False, - state: Optional[dict] = None) -> Type[Matcher]: + state: Optional[State] = None) -> Type[Matcher]: ... def on_startswith(self, @@ -276,7 +281,7 @@ class MatcherGroup: temp: bool = ..., priority: int = ..., block: bool = ..., - state: Optional[dict] = ...) -> Type[Matcher]: + state: Optional[State] = ...) -> Type[Matcher]: ... def on_endswith(self, @@ -288,7 +293,7 @@ class MatcherGroup: temp: bool = ..., priority: int = ..., block: bool = ..., - state: Optional[dict] = ...) -> Type[Matcher]: + state: Optional[State] = ...) -> Type[Matcher]: ... def on_keyword(self, @@ -300,7 +305,7 @@ class MatcherGroup: temp: bool = ..., priority: int = ..., block: bool = ..., - state: Optional[dict] = ...) -> Type[Matcher]: + state: Optional[State] = ...) -> Type[Matcher]: ... def on_command(self, @@ -313,7 +318,7 @@ class MatcherGroup: temp: bool = ..., priority: int = ..., block: bool = ..., - state: Optional[dict] = ...) -> Type[Matcher]: + state: Optional[State] = ...) -> Type[Matcher]: ... def on_regex(self, @@ -326,5 +331,5 @@ class MatcherGroup: temp: bool = ..., priority: int = ..., block: bool = ..., - state: Optional[dict] = ...) -> Type[Matcher]: + state: Optional[State] = ...) -> Type[Matcher]: ... diff --git a/nonebot/plugins/base.py b/nonebot/plugins/base.py index c937f5c0..bb6c4164 100644 --- a/nonebot/plugins/base.py +++ b/nonebot/plugins/base.py @@ -1,15 +1,16 @@ from functools import reduce from nonebot.rule import to_me +from nonebot.typing import State from nonebot.plugin import on_command from nonebot.permission import SUPERUSER -from nonebot.typing import Bot, Event, MessageSegment +from nonebot.adapters import BaseBot as Bot, BaseEvent as Event, BaseMessageSegment as MessageSegment say = on_command("say", to_me(), permission=SUPERUSER) @say.handle() -async def say_unescape(bot: Bot, event: Event, state: dict): +async def say_unescape(bot: Bot, event: Event, state: State): Message = event.message.__class__ def _unescape(message: Message, segment: MessageSegment): @@ -25,7 +26,7 @@ echo = on_command("echo", to_me()) @echo.handle() -async def echo_escape(bot: Bot, event: Event, state: dict): +async def echo_escape(bot: Bot, event: Event, state: State): # Message = event.message.__class__ # MessageSegment = event.message[0].__class__ diff --git a/nonebot/rule.py b/nonebot/rule.py index c58d5874..47ed8fc7 100644 --- a/nonebot/rule.py +++ b/nonebot/rule.py @@ -12,13 +12,17 @@ import re import asyncio from itertools import product +from typing import Any, Dict, Union, Tuple, Optional, Callable, NoReturn, Awaitable, TYPE_CHECKING from pygtrie import CharTrie from nonebot import get_driver from nonebot.log import logger from nonebot.utils import run_sync -from nonebot.typing import Bot, Any, Dict, Event, Union, Tuple, NoReturn, Optional, Callable, Awaitable, RuleChecker +from nonebot.typing import State, RuleChecker + +if TYPE_CHECKING: + from nonebot.adapters import BaseBot as Bot, BaseEvent as Event class Rule: @@ -39,12 +43,12 @@ class Rule: __slots__ = ("checkers",) def __init__( - self, *checkers: Callable[[Bot, Event, dict], + self, *checkers: Callable[["Bot", "Event", State], Awaitable[bool]]) -> None: """ :参数: - * ``*checkers: Callable[[Bot, Event, dict], Awaitable[bool]]``: **异步** RuleChecker + * ``*checkers: Callable[[Bot, Event, State], Awaitable[bool]]``: **异步** RuleChecker """ self.checkers = set(checkers) @@ -55,10 +59,10 @@ class Rule: :类型: - * ``Set[Callable[[Bot, Event, dict], Awaitable[bool]]]`` + * ``Set[Callable[[Bot, Event, State], Awaitable[bool]]]`` """ - async def __call__(self, bot: Bot, event: Event, state: dict) -> bool: + async def __call__(self, bot: "Bot", event: "Event", state: State) -> bool: """ :说明: @@ -68,7 +72,7 @@ class Rule: * ``bot: Bot``: Bot 对象 * ``event: Event``: Event 对象 - * ``state: dict``: 当前 State + * ``state: State``: 当前 State :返回: @@ -113,8 +117,8 @@ class TrieRule: cls.suffix[suffix[::-1]] = value @classmethod - def get_value(cls, bot: Bot, event: Event, - state: dict) -> Tuple[Dict[str, Any], Dict[str, Any]]: + def get_value(cls, bot: "Bot", event: "Event", + state: State) -> Tuple[Dict[str, Any], Dict[str, Any]]: if event.type != "message": state["_prefix"] = {"raw_command": None, "command": None} state["_suffix"] = {"raw_command": None, "command": None} @@ -176,7 +180,7 @@ def startswith(msg: str) -> Rule: * ``msg: str``: 消息开头字符串 """ - async def _startswith(bot: Bot, event: Event, state: dict) -> bool: + async def _startswith(bot: "Bot", event: "Event", state: State) -> bool: return event.plain_text.startswith(msg) return Rule(_startswith) @@ -193,7 +197,7 @@ def endswith(msg: str) -> Rule: * ``msg: str``: 消息结尾字符串 """ - async def _endswith(bot: Bot, event: Event, state: dict) -> bool: + async def _endswith(bot: "Bot", event: "Event", state: State) -> bool: return event.plain_text.endswith(msg) return Rule(_endswith) @@ -210,7 +214,7 @@ def keyword(*keywords: str) -> Rule: * ``*keywords: str``: 关键词 """ - async def _keyword(bot: Bot, event: Event, state: dict) -> bool: + async def _keyword(bot: "Bot", event: "Event", state: State) -> bool: return bool(event.plain_text and any(keyword in event.plain_text for keyword in keywords)) @@ -256,7 +260,7 @@ def command(*cmds: Union[str, Tuple[str, ...]]) -> Rule: for start, sep in product(command_start, command_sep): TrieRule.add_prefix(f"{start}{sep.join(command)}", command) - async def _command(bot: Bot, event: Event, state: dict) -> bool: + async def _command(bot: "Bot", event: "Event", state: State) -> bool: return state["_prefix"]["command"] in commands return Rule(_command) @@ -282,7 +286,7 @@ def regex(regex: str, flags: Union[int, re.RegexFlag] = 0) -> Rule: pattern = re.compile(regex, flags) - async def _regex(bot: Bot, event: Event, state: dict) -> bool: + async def _regex(bot: "Bot", event: "Event", state: State) -> bool: matched = pattern.search(str(event.message)) if matched: state["_matched"] = matched.group() @@ -305,7 +309,7 @@ def to_me() -> Rule: * 无 """ - async def _to_me(bot: Bot, event: Event, state: dict) -> bool: + async def _to_me(bot: "Bot", event: "Event", state: State) -> bool: return bool(event.to_me) return Rule(_to_me) diff --git a/nonebot/typing.py b/nonebot/typing.py index cdc6ee38..8ed2a4a5 100644 --- a/nonebot/typing.py +++ b/nonebot/typing.py @@ -29,7 +29,7 @@ if TYPE_CHECKING: from nonebot.drivers import BaseDriver, BaseWebSocket from nonebot.permission import Permission as PermissionClass from nonebot.adapters import BaseBot, BaseEvent, BaseMessage, BaseMessageSegment - from nonebot.matcher import Matcher as MatcherClass, MatcherGroup as MatcherGroupClass + from nonebot.matcher import Matcher as MatcherClass def overrides(InterfaceClass: object): @@ -92,34 +92,42 @@ MessageSegment = TypeVar("MessageSegment", bound="BaseMessageSegment") 所有 MessageSegment 的基类。 """ -EventPreProcessor = Callable[[Bot, Event, dict], Awaitable[None]] +State = Dict[Any, Any] """ -:类型: ``Callable[[Bot, Event, dict], Awaitable[None]]`` +:类型: ``Dict[Any, Any]`` + +:说明: + + 事件处理状态 State 类型 +""" +EventPreProcessor = Callable[[Bot, Event, State], Awaitable[None]] +""" +:类型: ``Callable[[Bot, Event, State], Awaitable[None]]`` :说明: 事件预处理函数 EventPreProcessor 类型 """ -EventPostProcessor = Callable[[Bot, Event, dict], Awaitable[None]] +EventPostProcessor = Callable[[Bot, Event, State], Awaitable[None]] """ -:类型: ``Callable[[Bot, Event, dict], Awaitable[None]]`` +:类型: ``Callable[[Bot, Event, State], Awaitable[None]]`` :说明: 事件预处理函数 EventPostProcessor 类型 """ -RunPreProcessor = Callable[["Matcher", Bot, Event, dict], Awaitable[None]] +RunPreProcessor = Callable[["Matcher", Bot, Event, State], Awaitable[None]] """ -:类型: ``Callable[[Matcher, Bot, Event, dict], Awaitable[None]]`` +:类型: ``Callable[[Matcher, Bot, Event, State], Awaitable[None]]`` :说明: 事件响应器运行前预处理函数 RunPreProcessor 类型 """ -RunPostProcessor = Callable[["Matcher", Optional[Exception], Bot, Event, dict], +RunPostProcessor = Callable[["Matcher", Optional[Exception], Bot, Event, State], Awaitable[None]] """ -:类型: ``Callable[[Matcher, Optional[Exception], Bot, Event, dict], Awaitable[None]]`` +:类型: ``Callable[[Matcher, Optional[Exception], Bot, Event, State], Awaitable[None]]`` :说明: @@ -134,14 +142,6 @@ Matcher = TypeVar("Matcher", bound="MatcherClass") Matcher 即响应事件的处理类。通过 Rule 判断是否响应事件,运行 Handler。 """ -MatcherGroup = TypeVar("MatcherGroup", bound="MatcherGroupClass") -""" -:类型: ``MatcherGroup`` - -:说明: - - MatcherGroup 为 Matcher 的集合。可以共享 Handler。 -""" Rule = TypeVar("Rule", bound="RuleClass") """ :类型: ``Rule`` @@ -150,9 +150,9 @@ Rule = TypeVar("Rule", bound="RuleClass") Rule 即判断是否响应事件的处理类。内部存储 RuleChecker ,返回全为 True 则响应事件。 """ -RuleChecker = Callable[[Bot, Event, dict], Union[bool, Awaitable[bool]]] +RuleChecker = Callable[[Bot, Event, State], Union[bool, Awaitable[bool]]] """ -:类型: ``Callable[[Bot, Event, dict], Union[bool, Awaitable[bool]]]`` +:类型: ``Callable[[Bot, Event, State], Union[bool, Awaitable[bool]]]`` :说明: @@ -174,17 +174,17 @@ PermissionChecker = Callable[[Bot, Event], Union[bool, Awaitable[bool]]] RuleChecker 即判断是否响应消息的处理函数。 """ -Handler = Callable[[Bot, Event, dict], Awaitable[None]] +Handler = Callable[[Bot, Event, State], Awaitable[None]] """ -:类型: ``Callable[[Bot, Event, dict], Awaitable[None]]`` +:类型: ``Callable[[Bot, Event, State], Awaitable[None]]`` :说明: Handler 即事件的处理函数。 """ -ArgsParser = Callable[[Bot, Event, dict], Awaitable[None]] +ArgsParser = Callable[[Bot, Event, State], Awaitable[None]] """ -:类型: ``Callable[[Bot, Event, dict], Awaitable[None]]`` +:类型: ``Callable[[Bot, Event, State], Awaitable[None]]`` :说明: diff --git a/nonebot/utils.py b/nonebot/utils.py index 7ef93769..8253d3c7 100644 --- a/nonebot/utils.py +++ b/nonebot/utils.py @@ -3,9 +3,10 @@ import json import asyncio import dataclasses from functools import wraps, partial +from typing import Any, Optional, Callable, Awaitable from nonebot.log import logger -from nonebot.typing import Any, Optional, Callable, Awaitable, overrides +from nonebot.typing import overrides def escape_tag(s: str) -> str: