From 698623229086a9a54d17ea49553c33fccc9c00c6 Mon Sep 17 00:00:00 2001 From: yanyongyu Date: Mon, 16 Nov 2020 11:25:42 +0800 Subject: [PATCH 1/7] :art: change temp matcher process #50 --- nonebot/exception.py | 6 --- nonebot/message.py | 104 ++++++++++++++++++++++++------------------- nonebot/typing.py | 6 +-- 3 files changed, 62 insertions(+), 54 deletions(-) diff --git a/nonebot/exception.py b/nonebot/exception.py index 4d862e41..5e27acc4 100644 --- a/nonebot/exception.py +++ b/nonebot/exception.py @@ -9,12 +9,6 @@ from nonebot.typing import List, Type, Optional -class _ExceptionContainer(Exception): - - def __init__(self, exceptions: List[Type[Exception]]) -> None: - self.exceptions = exceptions - - class IgnoredException(Exception): """ :说明: diff --git a/nonebot/message.py b/nonebot/message.py index 49cff307..9b3aab47 100644 --- a/nonebot/message.py +++ b/nonebot/message.py @@ -9,10 +9,9 @@ from datetime import datetime from nonebot.log import logger from nonebot.rule import TrieRule from nonebot.utils import escape_tag -from nonebot.matcher import matchers -from nonebot.exception import IgnoredException, ExpiredException -from nonebot.exception import StopPropagation, _ExceptionContainer -from nonebot.typing import Set, Type, Union, NoReturn, Bot, Event, Matcher +from nonebot.matcher import matchers, Matcher +from nonebot.typing import Set, Type, Union, Iterable, NoReturn, Bot, Event +from nonebot.exception import IgnoredException, ExpiredException, StopPropagation from nonebot.typing import EventPreProcessor, RunPreProcessor, EventPostProcessor, RunPostProcessor _event_preprocessors: Set[EventPreProcessor] = set() @@ -41,20 +40,46 @@ def run_postprocessor(func: RunPostProcessor) -> RunPostProcessor: return func +async def _check_matcher(priority: int, bot: Bot, event: Event, + state: dict) -> Iterable[Type[Matcher]]: + current_matchers = matchers[priority].copy() + + async def _check(Matcher: Type[Matcher], bot: Bot, event: Event, + state: dict) -> Optional[Type[Matcher]]: + try: + if await Matcher.check_perm( + bot, event) and await Matcher.check_rule(bot, event, state): + return Matcher + except Exception as e: + logger.opt(colors=True, exception=e).error( + f"Rule check failed for {Matcher}." + ) + return None + + async def _check_expire(Matcher: Type[Matcher]) -> Optional[Type[Matcher]]: + if Matcher.temp or (Matcher.expire_time and + datetime.now() > Matcher.expire_time): + return Matcher + return None + + checking_tasks = [ + _check(Matcher, bot, event, state) for Matcher in current_matchers + ] + checking_expire_tasks = [ + _check_expire(Matcher) for Matcher in current_matchers + ] + results = await asyncio.gather(*checking_tasks, return_exceptions=True) + expired = await asyncio.gather(*checking_expire_tasks) + for expired_matcher in filter(lambda x: issubclass(Matcher), expired): + try: + matchers[priority].remove(expired_matcher) + except Exception: + pass + return filter(lambda x: issubclass(Matcher), results) + + async def _run_matcher(Matcher: Type[Matcher], bot: Bot, event: Event, state: dict) -> Union[None, NoReturn]: - if Matcher.expire_time and datetime.now() > Matcher.expire_time: - raise _ExceptionContainer([ExpiredException]) - - try: - if not await Matcher.check_perm( - bot, event) or not await Matcher.check_rule(bot, event, state): - return - except Exception as e: - logger.opt(colors=True, exception=e).error( - f"Rule check failed for {Matcher}.") - return - logger.info(f"Event will be handled by {Matcher}") matcher = Matcher() @@ -74,7 +99,7 @@ async def _run_matcher(Matcher: Type[Matcher], bot: Bot, event: Event, "Running cancelled!") return - exceptions = [] + exception = None try: logger.debug(f"Running matcher {matcher}") @@ -83,15 +108,10 @@ async def _run_matcher(Matcher: Type[Matcher], bot: Bot, event: Event, logger.opt(colors=True, exception=e).error( f"Running matcher {matcher} failed." ) - exceptions.append(e) - - if Matcher.temp: - exceptions.append(ExpiredException) - if Matcher.block: - exceptions.append(StopPropagation) + exception = e coros = list( - map(lambda x: x(matcher, exceptions, bot, event, state), + map(lambda x: x(matcher, exception, bot, event, state), _run_postprocessors)) if coros: try: @@ -101,8 +121,8 @@ async def _run_matcher(Matcher: Type[Matcher], bot: Bot, event: Event, "Error when running RunPostProcessors" ) - if exceptions: - raise _ExceptionContainer(exceptions) + if matcher.block: + raise StopPropagation async def handle_event(bot: Bot, event: Event): @@ -153,29 +173,23 @@ async def handle_event(bot: Bot, event: Event): if break_flag: break - pending_tasks = [ - _run_matcher(matcher, bot, event, state.copy()) - for matcher in matchers[priority] - ] - if show_log: logger.debug(f"Checking for matchers in priority {priority}...") + + run_matchers = _check_matcher(priority, bot, event, state) + + pending_tasks = [ + _run_matcher(matcher, bot, event, state.copy()) + for matcher in run_matchers + ] + results = await asyncio.gather(*pending_tasks, return_exceptions=True) - i = 0 - for index, result in enumerate(results): - if isinstance(result, _ExceptionContainer): - e_list = result.exceptions - if StopPropagation in e_list: - if not break_flag: - break_flag = True - logger.debug("Stop event propagation") - if ExpiredException in e_list: - logger.debug( - f"Matcher {matchers[priority][index - i]} will be removed." - ) - del matchers[priority][index - i] - i += 1 + for result in results: + if result is StopPropagation: + if not break_flag: + break_flag = True + logger.debug("Stop event propagation") coros = list(map(lambda x: x(bot, event, state), _event_postprocessors)) if coros: diff --git a/nonebot/typing.py b/nonebot/typing.py index da7923bd..09109b37 100644 --- a/nonebot/typing.py +++ b/nonebot/typing.py @@ -119,14 +119,14 @@ RunPreProcessor = Callable[["Matcher", Bot, Event, dict], 事件响应器运行前预处理函数 RunPreProcessor 类型 """ -RunPostProcessor = Callable[["Matcher", List[Any], Bot, Event, dict], +RunPostProcessor = Callable[["Matcher", Optional[Exception], Bot, Event, dict], Union[Awaitable[None], Awaitable[NoReturn]]] """ -:类型: ``Callable[[Matcher, List[Any], Bot, Event, dict], Union[Awaitable[None], Awaitable[NoReturn]]]`` +:类型: ``Callable[[Matcher, Optional[Exception], Bot, Event, dict], Union[Awaitable[None], Awaitable[NoReturn]]]`` :说明: - 事件响应器运行前预处理函数 RunPostProcessor 类型,第二个参数包含运行时产生的错误以及 ``ExpiredException``, ``StopPropagation`` (如果存在) + 事件响应器运行前预处理函数 RunPostProcessor 类型,第二个参数为运行时产生的错误(如果存在) """ Matcher = TypeVar("Matcher", bound="MatcherClass") From 829f08534073bc7f74aed9ea0c587bbd3384e699 Mon Sep 17 00:00:00 2001 From: yanyongyu Date: Mon, 16 Nov 2020 12:35:04 +0800 Subject: [PATCH 2/7] :bug: fix coding error --- nonebot/message.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/nonebot/message.py b/nonebot/message.py index 9b3aab47..942ea3e8 100644 --- a/nonebot/message.py +++ b/nonebot/message.py @@ -10,8 +10,8 @@ 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, Union, Iterable, NoReturn, Bot, Event -from nonebot.exception import IgnoredException, ExpiredException, StopPropagation +from nonebot.typing import Set, Type, Union, Optional, Iterable, NoReturn, Bot, Event +from nonebot.exception import IgnoredException, StopPropagation from nonebot.typing import EventPreProcessor, RunPreProcessor, EventPostProcessor, RunPostProcessor _event_preprocessors: Set[EventPreProcessor] = set() @@ -70,12 +70,12 @@ async def _check_matcher(priority: int, bot: Bot, event: Event, ] results = await asyncio.gather(*checking_tasks, return_exceptions=True) expired = await asyncio.gather(*checking_expire_tasks) - for expired_matcher in filter(lambda x: issubclass(Matcher), expired): + for expired_matcher in filter(lambda x: x and x in results, expired): try: matchers[priority].remove(expired_matcher) except Exception: pass - return filter(lambda x: issubclass(Matcher), results) + return filter(lambda x: x, results) async def _run_matcher(Matcher: Type[Matcher], bot: Bot, event: Event, @@ -176,7 +176,7 @@ async def handle_event(bot: Bot, event: Event): if show_log: logger.debug(f"Checking for matchers in priority {priority}...") - run_matchers = _check_matcher(priority, bot, event, state) + run_matchers = await _check_matcher(priority, bot, event, state) pending_tasks = [ _run_matcher(matcher, bot, event, state.copy()) From b3e6d1c8030ac6f8216b2eaf7286d5f92b4ef24e Mon Sep 17 00:00:00 2001 From: yanyongyu Date: Mon, 16 Nov 2020 15:06:37 +0800 Subject: [PATCH 3/7] :bulb: add message docstring --- nonebot/message.py | 58 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/nonebot/message.py b/nonebot/message.py index 942ea3e8..c000d39a 100644 --- a/nonebot/message.py +++ b/nonebot/message.py @@ -1,6 +1,8 @@ """ 事件处理 ======== + +NoneBot 内部处理并按优先级分发事件给所有事件响应器,提供了多个插槽以进行事件的预处理等。 """ import asyncio @@ -21,21 +23,64 @@ _run_postprocessors: Set[RunPostProcessor] = set() def event_preprocessor(func: EventPreProcessor) -> EventPreProcessor: + """ + :说明: + 事件预处理。装饰一个函数,使它在每次接收到事件并分发给各响应器之前执行。 + :参数: + 事件预处理函数接收三个参数。 + + * ``bot: Bot``: Bot 对象 + * ``event: Event``: Event 对象 + * ``state: dict``: 当前 State + """ _event_preprocessors.add(func) return func def event_postprocessor(func: EventPostProcessor) -> EventPostProcessor: + """ + :说明: + 事件后处理。装饰一个函数,使它在每次接收到事件并分发给各响应器之后执行。 + :参数: + 事件后处理函数接收三个参数。 + + * ``bot: Bot``: Bot 对象 + * ``event: Event``: Event 对象 + * ``state: dict``: 当前事件运行前 State + """ _event_postprocessors.add(func) return func def run_preprocessor(func: RunPreProcessor) -> RunPreProcessor: + """ + :说明: + 运行预处理。装饰一个函数,使它在每次事件响应器运行前执行。 + :参数: + 运行预处理函数接收四个参数。 + + * ``matcher: Matcher``: 当前要运行的事件响应器 + * ``bot: Bot``: Bot 对象 + * ``event: Event``: Event 对象 + * ``state: dict``: 当前 State + """ _run_preprocessors.add(func) return func def run_postprocessor(func: RunPostProcessor) -> RunPostProcessor: + """ + :说明: + 运行后处理。装饰一个函数,使它在每次事件响应器运行后执行。 + :参数: + 运行后处理函数接收五个参数。 + + * ``matcher: Matcher``: 运行完毕的事件响应器 + * ``exception: Optional[Exception]``: 事件响应器运行错误(如果存在) + * ``bot: Bot``: Bot 对象 + * ``event: Event``: Event 对象 + * ``state: dict``: 当前 State + """ _run_postprocessors.add(func) return func @@ -126,6 +171,19 @@ async def _run_matcher(Matcher: Type[Matcher], bot: Bot, event: Event, async def handle_event(bot: Bot, event: Event): + """ + :说明: + 处理一个事件。调用该函数以实现分发事件。 + :参数: + * ``bot: Bot``: Bot 对象 + * ``event: Event``: Event 对象 + :示例: + + ..code-block:: python + + import asyncio + asyncio.create_task(handle_event(bot, event)) + """ show_log = True log_msg = f"{bot.type.upper()} | {event.self_id} [{event.name}]: " if event.type == "message": From 1c31453ba0e4fe2a46842d6929f29f72416e9417 Mon Sep 17 00:00:00 2001 From: yanyongyu Date: Mon, 16 Nov 2020 19:39:51 +0800 Subject: [PATCH 4/7] :fire: remove unused exception --- nonebot/exception.py | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/nonebot/exception.py b/nonebot/exception.py index 5e27acc4..f16cbcd0 100644 --- a/nonebot/exception.py +++ b/nonebot/exception.py @@ -73,19 +73,6 @@ class FinishedException(Exception): pass -class ExpiredException(Exception): - """ - :说明: - - 指示 NoneBot 当前 ``Matcher`` 已失效。 - - :用法: - - 当 ``Matcher`` 运行前检查时抛出。 - """ - pass - - class StopPropagation(Exception): """ :说明: From 36c0d995afbd5f14c0062bae1f95c99f6f47e6a5 Mon Sep 17 00:00:00 2001 From: nonebot Date: Mon, 16 Nov 2020 11:42:47 +0000 Subject: [PATCH 5/7] :memo: update api docs --- docs/api/exception.md | 17 ------ docs/api/message.md | 135 ++++++++++++++++++++++++++++++++++++++++++ docs/api/typing.md | 4 +- 3 files changed, 137 insertions(+), 19 deletions(-) diff --git a/docs/api/exception.md b/docs/api/exception.md index 5c89a5f8..0a9876a8 100644 --- a/docs/api/exception.md +++ b/docs/api/exception.md @@ -83,23 +83,6 @@ sidebarDepth: 0 -## _exception_ `ExpiredException` - -基类:`Exception` - - -* **说明** - - 指示 NoneBot 当前 `Matcher` 已失效。 - - - -* **用法** - - 当 `Matcher` 运行前检查时抛出。 - - - ## _exception_ `StopPropagation` 基类:`Exception` diff --git a/docs/api/message.md b/docs/api/message.md index 5fe65f56..4dc025e3 100644 --- a/docs/api/message.md +++ b/docs/api/message.md @@ -6,3 +6,138 @@ sidebarDepth: 0 # NoneBot.message 模块 ## 事件处理 + +NoneBot 内部处理并按优先级分发事件给所有事件响应器,提供了多个插槽以进行事件的预处理等。 + + +## `event_preprocessor(func)` + + +* **说明** + + 事件预处理。装饰一个函数,使它在每次接收到事件并分发给各响应器之前执行。 + + + +* **参数** + + 事件预处理函数接收三个参数。 + + + * `bot: Bot`: Bot 对象 + + + * `event: Event`: Event 对象 + + + * `state: dict`: 当前 State + + + +## `event_postprocessor(func)` + + +* **说明** + + 事件后处理。装饰一个函数,使它在每次接收到事件并分发给各响应器之后执行。 + + + +* **参数** + + 事件后处理函数接收三个参数。 + + + * `bot: Bot`: Bot 对象 + + + * `event: Event`: Event 对象 + + + * `state: dict`: 当前事件运行前 State + + + +## `run_preprocessor(func)` + + +* **说明** + + 运行预处理。装饰一个函数,使它在每次事件响应器运行前执行。 + + + +* **参数** + + 运行预处理函数接收四个参数。 + + + * `matcher: Matcher`: 当前要运行的事件响应器 + + + * `bot: Bot`: Bot 对象 + + + * `event: Event`: Event 对象 + + + * `state: dict`: 当前 State + + + +## `run_postprocessor(func)` + + +* **说明** + + 运行后处理。装饰一个函数,使它在每次事件响应器运行后执行。 + + + +* **参数** + + 运行后处理函数接收五个参数。 + + + * `matcher: Matcher`: 运行完毕的事件响应器 + + + * `exception: Optional[Exception]`: 事件响应器运行错误(如果存在) + + + * `bot: Bot`: Bot 对象 + + + * `event: Event`: Event 对象 + + + * `state: dict`: 当前 State + + + +## _async_ `handle_event(bot, event)` + + +* **说明** + + 处理一个事件。调用该函数以实现分发事件。 + + + +* **参数** + + + * `bot: Bot`: Bot 对象 + + + * `event: Event`: Event 对象 + + + +* **示例** + + +..code-block:: python + +> import asyncio +> asyncio.create_task(handle_event(bot, event)) diff --git a/docs/api/typing.md b/docs/api/typing.md index cdf9f224..8f8a2223 100644 --- a/docs/api/typing.md +++ b/docs/api/typing.md @@ -163,13 +163,13 @@ sidebarDepth: 0 * **类型** - `Callable[[Matcher, List[Any], Bot, Event, dict], Union[Awaitable[None], Awaitable[NoReturn]]]` + `Callable[[Matcher, Optional[Exception], Bot, Event, dict], Union[Awaitable[None], Awaitable[NoReturn]]]` * **说明** - 事件响应器运行前预处理函数 RunPostProcessor 类型,第二个参数包含运行时产生的错误以及 `ExpiredException`, `StopPropagation` (如果存在) + 事件响应器运行前预处理函数 RunPostProcessor 类型,第二个参数为运行时产生的错误(如果存在) From 872df975f5b5972d8f02a21a0dea9b758f3b538a Mon Sep 17 00:00:00 2001 From: yanyongyu Date: Mon, 16 Nov 2020 19:57:49 +0800 Subject: [PATCH 6/7] :pencil2: fix rst typo --- nonebot/message.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nonebot/message.py b/nonebot/message.py index c000d39a..af0849a5 100644 --- a/nonebot/message.py +++ b/nonebot/message.py @@ -178,8 +178,8 @@ async def handle_event(bot: Bot, event: Event): * ``bot: Bot``: Bot 对象 * ``event: Event``: Event 对象 :示例: - - ..code-block:: python + + .. code-block:: python import asyncio asyncio.create_task(handle_event(bot, event)) From 8524c32b93164ff209a30401e456b53c832b9f7d Mon Sep 17 00:00:00 2001 From: nonebot Date: Mon, 16 Nov 2020 11:59:05 +0000 Subject: [PATCH 7/7] :memo: update api docs --- docs/api/message.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/api/message.md b/docs/api/message.md index 4dc025e3..c9d7c158 100644 --- a/docs/api/message.md +++ b/docs/api/message.md @@ -137,7 +137,7 @@ NoneBot 内部处理并按优先级分发事件给所有事件响应器,提供 * **示例** -..code-block:: python - -> import asyncio -> asyncio.create_task(handle_event(bot, event)) +```python +import asyncio +asyncio.create_task(handle_event(bot, event)) +```