mirror of
https://github.com/nonebot/nonebot2.git
synced 2024-11-24 00:55:07 +08:00
commit
602185a34e
2
.github/workflows/build_docs.yml
vendored
2
.github/workflows/build_docs.yml
vendored
@ -25,8 +25,6 @@ jobs:
|
||||
with:
|
||||
path: ~/.cache/pypoetry/virtualenvs
|
||||
key: ${{ runner.os }}-poetry-${{ hashFiles('poetry.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-poetry-
|
||||
|
||||
- name: Set up dependencies
|
||||
run: |
|
||||
|
43
docs/api/dependencies.md
Normal file
43
docs/api/dependencies.md
Normal file
@ -0,0 +1,43 @@
|
||||
---
|
||||
contentSidebar: true
|
||||
sidebarDepth: 0
|
||||
---
|
||||
|
||||
# NoneBot.handler 模块
|
||||
|
||||
## 依赖注入处理模块
|
||||
|
||||
该模块实现了依赖注入的定义与处理。
|
||||
|
||||
|
||||
## `Depends(dependency=None, *, use_cache=True)`
|
||||
|
||||
|
||||
* **说明**
|
||||
|
||||
参数依赖注入装饰器
|
||||
|
||||
|
||||
|
||||
* **参数**
|
||||
|
||||
|
||||
* `dependency: Optional[Callable[..., Any]] = None`: 依赖函数。默认为参数的类型注释。
|
||||
|
||||
|
||||
* `use_cache: bool = True`: 是否使用缓存。默认为 `True`。
|
||||
|
||||
|
||||
```python
|
||||
def depend_func() -> Any:
|
||||
return ...
|
||||
|
||||
def depend_gen_func():
|
||||
try:
|
||||
yield ...
|
||||
finally:
|
||||
...
|
||||
|
||||
async def handler(param_name: Any = Depends(depend_func), gen: Any = Depends(depend_gen_func)):
|
||||
...
|
||||
```
|
@ -22,24 +22,6 @@ sidebarDepth: 0
|
||||
|
||||
|
||||
|
||||
## _exception_ `IgnoredException`
|
||||
|
||||
基类:`nonebot.exception.NoneBotException`
|
||||
|
||||
|
||||
* **说明**
|
||||
|
||||
指示 NoneBot 应该忽略该事件。可由 PreProcessor 抛出。
|
||||
|
||||
|
||||
|
||||
* **参数**
|
||||
|
||||
|
||||
* `reason`: 忽略事件的原因
|
||||
|
||||
|
||||
|
||||
## _exception_ `ParserExit`
|
||||
|
||||
基类:`nonebot.exception.NoneBotException`
|
||||
@ -61,57 +43,21 @@ sidebarDepth: 0
|
||||
|
||||
|
||||
|
||||
## _exception_ `PausedException`
|
||||
## _exception_ `IgnoredException`
|
||||
|
||||
基类:`nonebot.exception.NoneBotException`
|
||||
|
||||
|
||||
* **说明**
|
||||
|
||||
指示 NoneBot 结束当前 `Handler` 并等待下一条消息后继续下一个 `Handler`。
|
||||
可用于用户输入新信息。
|
||||
指示 NoneBot 应该忽略该事件。可由 PreProcessor 抛出。
|
||||
|
||||
|
||||
|
||||
* **用法**
|
||||
* **参数**
|
||||
|
||||
可以在 `Handler` 中通过 `Matcher.pause()` 抛出。
|
||||
|
||||
|
||||
|
||||
## _exception_ `RejectedException`
|
||||
|
||||
基类:`nonebot.exception.NoneBotException`
|
||||
|
||||
|
||||
* **说明**
|
||||
|
||||
指示 NoneBot 结束当前 `Handler` 并等待下一条消息后重新运行当前 `Handler`。
|
||||
可用于用户重新输入。
|
||||
|
||||
|
||||
|
||||
* **用法**
|
||||
|
||||
可以在 `Handler` 中通过 `Matcher.reject()` 抛出。
|
||||
|
||||
|
||||
|
||||
## _exception_ `FinishedException`
|
||||
|
||||
基类:`nonebot.exception.NoneBotException`
|
||||
|
||||
|
||||
* **说明**
|
||||
|
||||
指示 NoneBot 结束当前 `Handler` 且后续 `Handler` 不再被运行。
|
||||
可用于结束用户会话。
|
||||
|
||||
|
||||
|
||||
* **用法**
|
||||
|
||||
可以在 `Handler` 中通过 `Matcher.finish()` 抛出。
|
||||
|
||||
* `reason`: 忽略事件的原因
|
||||
|
||||
|
||||
|
||||
@ -132,6 +78,88 @@ sidebarDepth: 0
|
||||
|
||||
|
||||
|
||||
## _exception_ `MatcherException`
|
||||
|
||||
基类:`nonebot.exception.NoneBotException`
|
||||
|
||||
|
||||
* **说明**
|
||||
|
||||
所有 Matcher 发生的异常基类。
|
||||
|
||||
|
||||
|
||||
## _exception_ `SkippedException`
|
||||
|
||||
基类:`nonebot.exception.MatcherException`
|
||||
|
||||
|
||||
* **说明**
|
||||
|
||||
指示 NoneBot 立即结束当前 `Handler` 的处理,继续处理下一个 `Handler`。
|
||||
|
||||
|
||||
|
||||
* **用法**
|
||||
|
||||
可以在 `Handler` 中通过 `Matcher.skip()` 抛出。
|
||||
|
||||
|
||||
|
||||
## _exception_ `PausedException`
|
||||
|
||||
基类:`nonebot.exception.MatcherException`
|
||||
|
||||
|
||||
* **说明**
|
||||
|
||||
指示 NoneBot 结束当前 `Handler` 并等待下一条消息后继续下一个 `Handler`。
|
||||
可用于用户输入新信息。
|
||||
|
||||
|
||||
|
||||
* **用法**
|
||||
|
||||
可以在 `Handler` 中通过 `Matcher.pause()` 抛出。
|
||||
|
||||
|
||||
|
||||
## _exception_ `RejectedException`
|
||||
|
||||
基类:`nonebot.exception.MatcherException`
|
||||
|
||||
|
||||
* **说明**
|
||||
|
||||
指示 NoneBot 结束当前 `Handler` 并等待下一条消息后重新运行当前 `Handler`。
|
||||
可用于用户重新输入。
|
||||
|
||||
|
||||
|
||||
* **用法**
|
||||
|
||||
可以在 `Handler` 中通过 `Matcher.reject()` 抛出。
|
||||
|
||||
|
||||
|
||||
## _exception_ `FinishedException`
|
||||
|
||||
基类:`nonebot.exception.MatcherException`
|
||||
|
||||
|
||||
* **说明**
|
||||
|
||||
指示 NoneBot 结束当前 `Handler` 且后续 `Handler` 不再被运行。
|
||||
可用于结束用户会话。
|
||||
|
||||
|
||||
|
||||
* **用法**
|
||||
|
||||
可以在 `Handler` 中通过 `Matcher.finish()` 抛出。
|
||||
|
||||
|
||||
|
||||
## _exception_ `AdapterException`
|
||||
|
||||
基类:`nonebot.exception.NoneBotException`
|
||||
@ -152,7 +180,7 @@ sidebarDepth: 0
|
||||
|
||||
## _exception_ `NoLogException`
|
||||
|
||||
基类:`Exception`
|
||||
基类:`nonebot.exception.AdapterException`
|
||||
|
||||
|
||||
* **说明**
|
||||
|
@ -14,12 +14,32 @@ sidebarDepth: 0
|
||||
|
||||
基类:`object`
|
||||
|
||||
事件处理函数类
|
||||
事件处理器类。支持依赖注入。
|
||||
|
||||
|
||||
### `__init__(func)`
|
||||
### `__init__(func, *, name=None, dependencies=None, allow_types=None)`
|
||||
|
||||
|
||||
* **说明**
|
||||
|
||||
装饰一个函数为事件处理器。
|
||||
|
||||
|
||||
|
||||
* **参数**
|
||||
|
||||
|
||||
* `func: Callable[..., Any]`: 事件处理函数。
|
||||
|
||||
|
||||
* `name: Optional[str]`: 事件处理器名称。默认为函数名。
|
||||
|
||||
|
||||
* `dependencies: Optional[List[DependsWrapper]]`: 额外的非参数依赖注入。
|
||||
|
||||
|
||||
* `allow_types: Optional[List[Type[Param]]]`: 允许的参数类型。
|
||||
|
||||
装饰事件处理函数以便根据动态参数运行
|
||||
|
||||
|
||||
### `func`
|
||||
@ -27,7 +47,7 @@ sidebarDepth: 0
|
||||
|
||||
* **类型**
|
||||
|
||||
`T_Handler`
|
||||
`Callable[..., Any]`
|
||||
|
||||
|
||||
|
||||
@ -37,75 +57,45 @@ sidebarDepth: 0
|
||||
|
||||
|
||||
|
||||
### `signature`
|
||||
### `name`
|
||||
|
||||
|
||||
* **类型**
|
||||
|
||||
`inspect.Signature`
|
||||
`str`
|
||||
|
||||
|
||||
|
||||
* **说明**
|
||||
|
||||
事件处理函数签名
|
||||
事件处理函数名
|
||||
|
||||
|
||||
|
||||
### _property_ `bot_type`
|
||||
### `allow_types`
|
||||
|
||||
|
||||
* **类型**
|
||||
|
||||
`Union[Type["Bot"], inspect.Parameter.empty]`
|
||||
`List[Type[Param]]`
|
||||
|
||||
|
||||
|
||||
* **说明**
|
||||
|
||||
事件处理函数接受的 Bot 对象类型
|
||||
事件处理器允许的参数类型
|
||||
|
||||
|
||||
|
||||
### _property_ `event_type`
|
||||
### `dependencies`
|
||||
|
||||
|
||||
* **类型**
|
||||
|
||||
`Optional[Union[Type[Event], inspect.Parameter.empty]]`
|
||||
`List[DependsWrapper]`
|
||||
|
||||
|
||||
|
||||
* **说明**
|
||||
|
||||
事件处理函数接受的 event 类型 / 不需要 event 参数
|
||||
|
||||
|
||||
|
||||
### _property_ `state_type`
|
||||
|
||||
|
||||
* **类型**
|
||||
|
||||
`Optional[Union[T_State, inspect.Parameter.empty]]`
|
||||
|
||||
|
||||
|
||||
* **说明**
|
||||
|
||||
事件处理函数是否接受 state 参数
|
||||
|
||||
|
||||
|
||||
### _property_ `matcher_type`
|
||||
|
||||
|
||||
* **类型**
|
||||
|
||||
`Optional[Union[Type["Matcher"], inspect.Parameter.empty]]`
|
||||
|
||||
|
||||
|
||||
* **说明**
|
||||
|
||||
事件处理函数是否接受 matcher 参数
|
||||
事件处理器的额外依赖
|
||||
|
@ -348,7 +348,7 @@ sidebarDepth: 0
|
||||
|
||||
|
||||
|
||||
### _async classmethod_ `check_perm(bot, event)`
|
||||
### _async classmethod_ `check_perm(bot, event, stack=None, dependency_cache=None)`
|
||||
|
||||
|
||||
* **说明**
|
||||
@ -374,7 +374,7 @@ sidebarDepth: 0
|
||||
|
||||
|
||||
|
||||
### _async classmethod_ `check_rule(bot, event, state)`
|
||||
### _async classmethod_ `check_rule(bot, event, state, stack=None, dependency_cache=None)`
|
||||
|
||||
|
||||
* **说明**
|
||||
|
@ -19,21 +19,6 @@ NoneBot 内部处理并按优先级分发事件给所有事件响应器,提供
|
||||
|
||||
|
||||
|
||||
* **参数**
|
||||
|
||||
事件预处理函数接收三个参数。
|
||||
|
||||
|
||||
* `bot: Bot`: Bot 对象
|
||||
|
||||
|
||||
* `event: Event`: Event 对象
|
||||
|
||||
|
||||
* `state: T_State`: 当前 State
|
||||
|
||||
|
||||
|
||||
## `event_postprocessor(func)`
|
||||
|
||||
|
||||
@ -43,21 +28,6 @@ NoneBot 内部处理并按优先级分发事件给所有事件响应器,提供
|
||||
|
||||
|
||||
|
||||
* **参数**
|
||||
|
||||
事件后处理函数接收三个参数。
|
||||
|
||||
|
||||
* `bot: Bot`: Bot 对象
|
||||
|
||||
|
||||
* `event: Event`: Event 对象
|
||||
|
||||
|
||||
* `state: T_State`: 当前事件运行前 State
|
||||
|
||||
|
||||
|
||||
## `run_preprocessor(func)`
|
||||
|
||||
|
||||
@ -67,24 +37,6 @@ NoneBot 内部处理并按优先级分发事件给所有事件响应器,提供
|
||||
|
||||
|
||||
|
||||
* **参数**
|
||||
|
||||
运行预处理函数接收四个参数。
|
||||
|
||||
|
||||
* `matcher: Matcher`: 当前要运行的事件响应器
|
||||
|
||||
|
||||
* `bot: Bot`: Bot 对象
|
||||
|
||||
|
||||
* `event: Event`: Event 对象
|
||||
|
||||
|
||||
* `state: T_State`: 当前 State
|
||||
|
||||
|
||||
|
||||
## `run_postprocessor(func)`
|
||||
|
||||
|
||||
@ -94,27 +46,6 @@ NoneBot 内部处理并按优先级分发事件给所有事件响应器,提供
|
||||
|
||||
|
||||
|
||||
* **参数**
|
||||
|
||||
运行后处理函数接收五个参数。
|
||||
|
||||
|
||||
* `matcher: Matcher`: 运行完毕的事件响应器
|
||||
|
||||
|
||||
* `exception: Optional[Exception]`: 事件响应器运行错误(如果存在)
|
||||
|
||||
|
||||
* `bot: Bot`: Bot 对象
|
||||
|
||||
|
||||
* `event: Event`: Event 对象
|
||||
|
||||
|
||||
* `state: T_State`: 当前 State
|
||||
|
||||
|
||||
|
||||
## _async_ `handle_event(bot, event)`
|
||||
|
||||
|
||||
|
@ -7,7 +7,7 @@ sidebarDepth: 0
|
||||
|
||||
## 权限
|
||||
|
||||
每个 `Matcher` 拥有一个 `Permission` ,其中是 **异步** `PermissionChecker` 的集合,只要有一个 `PermissionChecker` 检查结果为 `True` 时就会继续运行。
|
||||
每个 `Matcher` 拥有一个 `Permission` ,其中是 `PermissionChecker` 的集合,只要有一个 `PermissionChecker` 检查结果为 `True` 时就会继续运行。
|
||||
|
||||
:::tip 提示
|
||||
`PermissionChecker` 既可以是 async function 也可以是 sync function
|
||||
@ -42,7 +42,7 @@ Permission(async_function, run_sync(sync_function))
|
||||
* **参数**
|
||||
|
||||
|
||||
* `*checkers: Callable[[Bot, Event], Awaitable[bool]]`: **异步** PermissionChecker
|
||||
* `*checkers: Union[T_PermissionChecker, Handler]`: PermissionChecker
|
||||
|
||||
|
||||
|
||||
@ -58,11 +58,11 @@ Permission(async_function, run_sync(sync_function))
|
||||
* **类型**
|
||||
|
||||
|
||||
* `Set[Callable[[Bot, Event], Awaitable[bool]]]`
|
||||
* `Set[Handler]`
|
||||
|
||||
|
||||
|
||||
### _async_ `__call__(bot, event)`
|
||||
### _async_ `__call__(bot, event, stack=None, dependency_cache=None)`
|
||||
|
||||
|
||||
* **说明**
|
||||
@ -80,6 +80,12 @@ Permission(async_function, run_sync(sync_function))
|
||||
* `event: Event`: Event 对象
|
||||
|
||||
|
||||
* `stack: Optional[AsyncExitStack]`: 异步上下文栈
|
||||
|
||||
|
||||
* `dependency_cache: Optional[Dict[Callable[..., Any], Any]]`: 依赖缓存
|
||||
|
||||
|
||||
|
||||
* **返回**
|
||||
|
||||
|
@ -7,10 +7,10 @@ sidebarDepth: 0
|
||||
|
||||
## 规则
|
||||
|
||||
每个事件响应器 `Matcher` 拥有一个匹配规则 `Rule` ,其中是 **异步** `RuleChecker` 的集合,只有当所有 `RuleChecker` 检查结果为 `True` 时继续运行。
|
||||
每个事件响应器 `Matcher` 拥有一个匹配规则 `Rule` ,其中是 `RuleChecker` 的集合,只有当所有 `RuleChecker` 检查结果为 `True` 时继续运行。
|
||||
|
||||
:::tip 提示
|
||||
`RuleChecker` 既可以是 async function 也可以是 sync function,但在最终会被 `nonebot.utils.run_sync` 转换为 async function
|
||||
`RuleChecker` 既可以是 async function 也可以是 sync function
|
||||
:::
|
||||
|
||||
|
||||
@ -42,7 +42,7 @@ Rule(async_function, run_sync(sync_function))
|
||||
* **参数**
|
||||
|
||||
|
||||
* `*checkers: Callable[[Bot, Event, T_State], Awaitable[bool]]`: **异步** RuleChecker
|
||||
* `*checkers: Union[T_RuleChecker, Handler]`: RuleChecker
|
||||
|
||||
|
||||
|
||||
@ -58,11 +58,11 @@ Rule(async_function, run_sync(sync_function))
|
||||
* **类型**
|
||||
|
||||
|
||||
* `Set[Callable[[Bot, Event, T_State], Awaitable[bool]]]`
|
||||
* `Set[Handler]`
|
||||
|
||||
|
||||
|
||||
### _async_ `__call__(bot, event, state)`
|
||||
### _async_ `__call__(bot, event, state, stack=None, dependency_cache=None)`
|
||||
|
||||
|
||||
* **说明**
|
||||
@ -83,6 +83,12 @@ Rule(async_function, run_sync(sync_function))
|
||||
* `state: T_State`: 当前 State
|
||||
|
||||
|
||||
* `stack: Optional[AsyncExitStack]`: 异步上下文栈
|
||||
|
||||
|
||||
* `dependency_cache: Optional[Dict[Callable[..., Any], Any]]`: 依赖缓存
|
||||
|
||||
|
||||
|
||||
* **返回**
|
||||
|
||||
|
@ -115,7 +115,20 @@ sidebarDepth: 0
|
||||
|
||||
* **类型**
|
||||
|
||||
`Callable[[Bot, Event, T_State], Awaitable[None]]`
|
||||
`Callable[..., Union[None, Awaitable[None]]]`
|
||||
|
||||
|
||||
|
||||
* **依赖参数**
|
||||
|
||||
|
||||
* `BotParam`: Bot 对象
|
||||
|
||||
|
||||
* `EventParam`: Event 对象
|
||||
|
||||
|
||||
* `StateParam`: State 对象
|
||||
|
||||
|
||||
|
||||
@ -131,7 +144,20 @@ sidebarDepth: 0
|
||||
|
||||
* **类型**
|
||||
|
||||
`Callable[[Bot, Event, T_State], Awaitable[None]]`
|
||||
`Callable[..., Union[None, Awaitable[None]]]`
|
||||
|
||||
|
||||
|
||||
* **依赖参数**
|
||||
|
||||
|
||||
* `BotParam`: Bot 对象
|
||||
|
||||
|
||||
* `EventParam`: Event 对象
|
||||
|
||||
|
||||
* `StateParam`: State 对象
|
||||
|
||||
|
||||
|
||||
@ -147,7 +173,23 @@ sidebarDepth: 0
|
||||
|
||||
* **类型**
|
||||
|
||||
`Callable[[Matcher, Bot, Event, T_State], Awaitable[None]]`
|
||||
`Callable[..., Union[None, Awaitable[None]]]`
|
||||
|
||||
|
||||
|
||||
* **依赖参数**
|
||||
|
||||
|
||||
* `BotParam`: Bot 对象
|
||||
|
||||
|
||||
* `EventParam`: Event 对象
|
||||
|
||||
|
||||
* `StateParam`: State 对象
|
||||
|
||||
|
||||
* `MatcherParam`: Matcher 对象
|
||||
|
||||
|
||||
|
||||
@ -163,7 +205,26 @@ sidebarDepth: 0
|
||||
|
||||
* **类型**
|
||||
|
||||
`Callable[[Matcher, Optional[Exception], Bot, Event, T_State], Awaitable[None]]`
|
||||
`Callable[..., Union[None, Awaitable[None]]]`
|
||||
|
||||
|
||||
|
||||
* **依赖参数**
|
||||
|
||||
|
||||
* `BotParam`: Bot 对象
|
||||
|
||||
|
||||
* `EventParam`: Event 对象
|
||||
|
||||
|
||||
* `StateParam`: State 对象
|
||||
|
||||
|
||||
* `MatcherParam`: Matcher 对象
|
||||
|
||||
|
||||
* `ExceptionParam`: 异常对象(可能为 None)
|
||||
|
||||
|
||||
|
||||
@ -179,7 +240,20 @@ sidebarDepth: 0
|
||||
|
||||
* **类型**
|
||||
|
||||
`Callable[[Bot, Event, T_State], Union[bool, Awaitable[bool]]]`
|
||||
`Callable[..., Union[bool, Awaitable[bool]]]`
|
||||
|
||||
|
||||
|
||||
* **依赖参数**
|
||||
|
||||
|
||||
* `BotParam`: Bot 对象
|
||||
|
||||
|
||||
* `EventParam`: Event 对象
|
||||
|
||||
|
||||
* `StateParam`: State 对象
|
||||
|
||||
|
||||
|
||||
@ -195,7 +269,17 @@ sidebarDepth: 0
|
||||
|
||||
* **类型**
|
||||
|
||||
`Callable[[Bot, Event], Union[bool, Awaitable[bool]]]`
|
||||
`Callable[..., Union[bool, Awaitable[bool]]]`
|
||||
|
||||
|
||||
|
||||
* **依赖参数**
|
||||
|
||||
|
||||
* `BotParam`: Bot 对象
|
||||
|
||||
|
||||
* `EventParam`: Event 对象
|
||||
|
||||
|
||||
|
||||
@ -211,23 +295,29 @@ sidebarDepth: 0
|
||||
|
||||
* **类型**
|
||||
|
||||
|
||||
* `Callable[[Bot, Event, T_State], Union[Awaitable[None], Awaitable[NoReturn]]]`
|
||||
|
||||
|
||||
* `Callable[[Bot, Event], Union[Awaitable[None], Awaitable[NoReturn]]]`
|
||||
|
||||
|
||||
* `Callable[[Bot, T_State], Union[Awaitable[None], Awaitable[NoReturn]]]`
|
||||
|
||||
|
||||
* `Callable[[Bot], Union[Awaitable[None], Awaitable[NoReturn]]]`
|
||||
`Callable[..., Any]`
|
||||
|
||||
|
||||
|
||||
* **说明**
|
||||
|
||||
Handler 即事件的处理函数。
|
||||
Handler 处理函数。
|
||||
|
||||
|
||||
|
||||
|
||||
## `T_DependencyCache`
|
||||
|
||||
|
||||
* **类型**
|
||||
|
||||
`Dict[T_Handler, Any]`
|
||||
|
||||
|
||||
|
||||
* **说明**
|
||||
|
||||
依赖缓存, 用于存储依赖函数的返回值
|
||||
|
||||
|
||||
|
||||
|
@ -41,14 +41,14 @@ sidebarDepth: 0
|
||||
* **参数**
|
||||
|
||||
|
||||
* `func: Callable[..., Any]`: 被装饰的同步函数
|
||||
* `func: Callable[P, R]`: 被装饰的同步函数
|
||||
|
||||
|
||||
|
||||
* **返回**
|
||||
|
||||
|
||||
* `Callable[..., Awaitable[Any]]`
|
||||
* `Callable[P, Awaitable[R]]`
|
||||
|
||||
|
||||
|
||||
|
12
docs_build/dependencies.rst
Normal file
12
docs_build/dependencies.rst
Normal file
@ -0,0 +1,12 @@
|
||||
\-\-\-
|
||||
contentSidebar: true
|
||||
sidebarDepth: 0
|
||||
\-\-\-
|
||||
|
||||
NoneBot.handler 模块
|
||||
====================
|
||||
|
||||
.. automodule:: nonebot.dependencies
|
||||
:members:
|
||||
:private-members:
|
||||
:show-inheritance:
|
@ -278,6 +278,7 @@ def run(host: Optional[str] = None,
|
||||
get_driver().run(host, port, *args, **kwargs)
|
||||
|
||||
|
||||
import nonebot.params as params
|
||||
from nonebot.plugin import export as export
|
||||
from nonebot.plugin import require as require
|
||||
from nonebot.plugin import on_regex as on_regex
|
||||
|
@ -67,8 +67,7 @@ class CustomEnvSettings(EnvSettingsSource):
|
||||
env_val = settings.__config__.json_loads(env_val)
|
||||
except ValueError as e:
|
||||
raise SettingsError(
|
||||
f'error parsing JSON for "{env_name}"' # type: ignore
|
||||
) from e
|
||||
f'error parsing JSON for "{env_name}"') from e
|
||||
d[field.alias] = env_val
|
||||
|
||||
if env_file_vars:
|
||||
|
232
nonebot/dependencies/__init__.py
Normal file
232
nonebot/dependencies/__init__.py
Normal file
@ -0,0 +1,232 @@
|
||||
"""
|
||||
依赖注入处理模块
|
||||
===============
|
||||
|
||||
该模块实现了依赖注入的定义与处理。
|
||||
"""
|
||||
|
||||
import inspect
|
||||
from itertools import chain
|
||||
from typing import Any, Dict, List, Type, Tuple, Callable, Optional, cast
|
||||
from contextlib import AsyncExitStack, contextmanager, asynccontextmanager
|
||||
|
||||
from pydantic import BaseConfig
|
||||
from pydantic.schema import get_annotation_from_field_info
|
||||
from pydantic.fields import Required, Undefined, ModelField
|
||||
|
||||
from nonebot.log import logger
|
||||
from .models import Param as Param
|
||||
from .utils import get_typed_signature
|
||||
from .models import Dependent as Dependent
|
||||
from nonebot.exception import SkippedException
|
||||
from .models import DependsWrapper as DependsWrapper
|
||||
from nonebot.typing import T_Handler, T_DependencyCache
|
||||
from nonebot.utils import (CacheLock, run_sync, is_gen_callable,
|
||||
run_sync_ctx_manager, is_async_gen_callable,
|
||||
is_coroutine_callable)
|
||||
|
||||
cache_lock = CacheLock()
|
||||
|
||||
|
||||
class CustomConfig(BaseConfig):
|
||||
arbitrary_types_allowed = True
|
||||
|
||||
|
||||
def get_param_sub_dependent(
|
||||
*,
|
||||
param: inspect.Parameter,
|
||||
allow_types: Optional[List[Type[Param]]] = None) -> Dependent:
|
||||
depends: DependsWrapper = param.default
|
||||
if depends.dependency:
|
||||
dependency = depends.dependency
|
||||
else:
|
||||
dependency = param.annotation
|
||||
return get_sub_dependant(depends=depends,
|
||||
dependency=dependency,
|
||||
name=param.name,
|
||||
allow_types=allow_types)
|
||||
|
||||
|
||||
def get_parameterless_sub_dependant(
|
||||
*,
|
||||
depends: DependsWrapper,
|
||||
allow_types: Optional[List[Type[Param]]] = None) -> Dependent:
|
||||
assert callable(
|
||||
depends.dependency
|
||||
), "A parameter-less dependency must have a callable dependency"
|
||||
return get_sub_dependant(depends=depends,
|
||||
dependency=depends.dependency,
|
||||
allow_types=allow_types)
|
||||
|
||||
|
||||
def get_sub_dependant(
|
||||
*,
|
||||
depends: DependsWrapper,
|
||||
dependency: T_Handler,
|
||||
name: Optional[str] = None,
|
||||
allow_types: Optional[List[Type[Param]]] = None) -> Dependent:
|
||||
sub_dependant = get_dependent(func=dependency,
|
||||
name=name,
|
||||
use_cache=depends.use_cache,
|
||||
allow_types=allow_types)
|
||||
return sub_dependant
|
||||
|
||||
|
||||
def get_dependent(*,
|
||||
func: T_Handler,
|
||||
name: Optional[str] = None,
|
||||
use_cache: bool = True,
|
||||
allow_types: Optional[List[Type[Param]]] = None) -> Dependent:
|
||||
signature = get_typed_signature(func)
|
||||
params = signature.parameters
|
||||
dependent = Dependent(func=func,
|
||||
name=name,
|
||||
allow_types=allow_types,
|
||||
use_cache=use_cache)
|
||||
for param_name, param in params.items():
|
||||
if isinstance(param.default, DependsWrapper):
|
||||
sub_dependent = get_param_sub_dependent(param=param,
|
||||
allow_types=allow_types)
|
||||
dependent.dependencies.append(sub_dependent)
|
||||
continue
|
||||
|
||||
default_value = Required
|
||||
if param.default != param.empty:
|
||||
default_value = param.default
|
||||
|
||||
if isinstance(default_value, Param):
|
||||
field_info = default_value
|
||||
default_value = field_info.default
|
||||
else:
|
||||
for allow_type in dependent.allow_types:
|
||||
if allow_type._check(param_name, param):
|
||||
field_info = allow_type(default_value)
|
||||
break
|
||||
else:
|
||||
raise ValueError(
|
||||
f"Unknown parameter {param_name} for function {func} with type {param.annotation}"
|
||||
)
|
||||
|
||||
annotation: Any = Any
|
||||
required = default_value == Required
|
||||
if param.annotation != param.empty:
|
||||
annotation = param.annotation
|
||||
annotation = get_annotation_from_field_info(annotation, field_info,
|
||||
param_name)
|
||||
dependent.params.append(
|
||||
ModelField(name=param_name,
|
||||
type_=annotation,
|
||||
class_validators=None,
|
||||
model_config=CustomConfig,
|
||||
default=None if required else default_value,
|
||||
required=required,
|
||||
field_info=field_info))
|
||||
|
||||
return dependent
|
||||
|
||||
|
||||
async def solve_dependencies(
|
||||
*,
|
||||
_dependent: Dependent,
|
||||
_stack: Optional[AsyncExitStack] = None,
|
||||
_sub_dependents: Optional[List[Dependent]] = None,
|
||||
_dependency_cache: Optional[T_DependencyCache] = None,
|
||||
**params: Any) -> Tuple[Dict[str, Any], T_DependencyCache]:
|
||||
values: Dict[str, Any] = {}
|
||||
dependency_cache = {} if _dependency_cache is None else _dependency_cache
|
||||
|
||||
# solve sub dependencies
|
||||
sub_dependent: Dependent
|
||||
for sub_dependent in chain(_sub_dependents or tuple(),
|
||||
_dependent.dependencies):
|
||||
sub_dependent.func = cast(Callable[..., Any], sub_dependent.func)
|
||||
sub_dependent.cache_key = cast(Callable[..., Any],
|
||||
sub_dependent.cache_key)
|
||||
func = sub_dependent.func
|
||||
|
||||
# solve sub dependency with current cache
|
||||
solved_result = await solve_dependencies(
|
||||
_dependent=sub_dependent,
|
||||
_dependency_cache=dependency_cache,
|
||||
**params)
|
||||
sub_values, sub_dependency_cache = solved_result
|
||||
# update cache?
|
||||
# dependency_cache.update(sub_dependency_cache)
|
||||
|
||||
# run dependency function
|
||||
async with cache_lock:
|
||||
if sub_dependent.use_cache and sub_dependent.cache_key in dependency_cache:
|
||||
solved = dependency_cache[sub_dependent.cache_key]
|
||||
elif is_gen_callable(func) or is_async_gen_callable(func):
|
||||
assert isinstance(
|
||||
_stack, AsyncExitStack
|
||||
), "Generator dependency should be called in context"
|
||||
if is_gen_callable(func):
|
||||
cm = run_sync_ctx_manager(
|
||||
contextmanager(func)(**sub_values))
|
||||
else:
|
||||
cm = asynccontextmanager(func)(**sub_values)
|
||||
solved = await _stack.enter_async_context(cm)
|
||||
elif is_coroutine_callable(func):
|
||||
solved = await func(**sub_values)
|
||||
else:
|
||||
solved = await run_sync(func)(**sub_values)
|
||||
|
||||
# parameter dependency
|
||||
if sub_dependent.name is not None:
|
||||
values[sub_dependent.name] = solved
|
||||
# save current dependency to cache
|
||||
if sub_dependent.cache_key not in dependency_cache:
|
||||
dependency_cache[sub_dependent.cache_key] = solved
|
||||
|
||||
# usual dependency
|
||||
for field in _dependent.params:
|
||||
field_info = field.field_info
|
||||
assert isinstance(field_info,
|
||||
Param), "Params must be subclasses of Param"
|
||||
value = field_info._solve(**params)
|
||||
if value == Undefined:
|
||||
value = field.get_default()
|
||||
_, errs_ = field.validate(value,
|
||||
values,
|
||||
loc=(str(field_info), field.alias))
|
||||
if errs_:
|
||||
logger.debug(
|
||||
f"{field_info} "
|
||||
f"type {type(value)} not match depends {_dependent.func} "
|
||||
f"annotation {field._type_display()}, ignored")
|
||||
raise SkippedException
|
||||
else:
|
||||
values[field.name] = value
|
||||
|
||||
return values, dependency_cache
|
||||
|
||||
|
||||
def Depends(dependency: Optional[T_Handler] = None,
|
||||
*,
|
||||
use_cache: bool = True) -> Any:
|
||||
"""
|
||||
:说明:
|
||||
|
||||
参数依赖注入装饰器
|
||||
|
||||
:参数:
|
||||
|
||||
* ``dependency: Optional[Callable[..., Any]] = None``: 依赖函数。默认为参数的类型注释。
|
||||
* ``use_cache: bool = True``: 是否使用缓存。默认为 ``True``。
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def depend_func() -> Any:
|
||||
return ...
|
||||
|
||||
def depend_gen_func():
|
||||
try:
|
||||
yield ...
|
||||
finally:
|
||||
...
|
||||
|
||||
async def handler(param_name: Any = Depends(depend_func), gen: Any = Depends(depend_gen_func)):
|
||||
...
|
||||
"""
|
||||
return DependsWrapper(dependency=dependency, use_cache=use_cache)
|
54
nonebot/dependencies/models.py
Normal file
54
nonebot/dependencies/models.py
Normal file
@ -0,0 +1,54 @@
|
||||
import abc
|
||||
import inspect
|
||||
from typing import Any, List, Type, Optional
|
||||
|
||||
from pydantic.fields import FieldInfo, ModelField
|
||||
|
||||
from nonebot.utils import get_name
|
||||
from nonebot.typing import T_Handler
|
||||
|
||||
|
||||
class Param(abc.ABC, FieldInfo):
|
||||
|
||||
@classmethod
|
||||
@abc.abstractmethod
|
||||
def _check(cls, name: str, param: inspect.Parameter) -> bool:
|
||||
raise NotImplementedError
|
||||
|
||||
@abc.abstractmethod
|
||||
def _solve(self, **kwargs: Any) -> Any:
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class DependsWrapper:
|
||||
|
||||
def __init__(self,
|
||||
dependency: Optional[T_Handler] = None,
|
||||
*,
|
||||
use_cache: bool = True) -> None:
|
||||
self.dependency = dependency
|
||||
self.use_cache = use_cache
|
||||
|
||||
def __repr__(self) -> str:
|
||||
dep = get_name(self.dependency)
|
||||
cache = "" if self.use_cache else ", use_cache=False"
|
||||
return f"{self.__class__.__name__}({dep}{cache})"
|
||||
|
||||
|
||||
class Dependent:
|
||||
|
||||
def __init__(self,
|
||||
*,
|
||||
func: Optional[T_Handler] = None,
|
||||
name: Optional[str] = None,
|
||||
params: Optional[List[ModelField]] = None,
|
||||
allow_types: Optional[List[Type[Param]]] = None,
|
||||
dependencies: Optional[List["Dependent"]] = None,
|
||||
use_cache: bool = True) -> None:
|
||||
self.func = func
|
||||
self.name = name
|
||||
self.params = params or []
|
||||
self.allow_types = allow_types or []
|
||||
self.dependencies = dependencies or []
|
||||
self.use_cache = use_cache
|
||||
self.cache_key = self.func
|
37
nonebot/dependencies/utils.py
Normal file
37
nonebot/dependencies/utils.py
Normal file
@ -0,0 +1,37 @@
|
||||
import inspect
|
||||
from typing import Any, Dict
|
||||
|
||||
from loguru import logger
|
||||
from pydantic.typing import ForwardRef, evaluate_forwardref
|
||||
|
||||
from nonebot.typing import T_Handler
|
||||
|
||||
|
||||
def get_typed_signature(func: T_Handler) -> inspect.Signature:
|
||||
signature = inspect.signature(func)
|
||||
globalns = getattr(func, "__globals__", {})
|
||||
typed_params = [
|
||||
inspect.Parameter(
|
||||
name=param.name,
|
||||
kind=param.kind,
|
||||
default=param.default,
|
||||
annotation=get_typed_annotation(param, globalns),
|
||||
) for param in signature.parameters.values()
|
||||
]
|
||||
typed_signature = inspect.Signature(typed_params)
|
||||
return typed_signature
|
||||
|
||||
|
||||
def get_typed_annotation(param: inspect.Parameter, globalns: Dict[str,
|
||||
Any]) -> Any:
|
||||
annotation = param.annotation
|
||||
if isinstance(annotation, str):
|
||||
annotation = ForwardRef(annotation)
|
||||
try:
|
||||
annotation = evaluate_forwardref(annotation, globalns, globalns)
|
||||
except Exception as e:
|
||||
logger.opt(colors=True, exception=e).warning(
|
||||
f"Unknown ForwardRef[\"{param.annotation}\"] for parameter {param.name}"
|
||||
)
|
||||
return inspect.Parameter.empty
|
||||
return annotation
|
@ -248,6 +248,8 @@ class Driver(ForwardDriver):
|
||||
await asyncio.sleep(3)
|
||||
continue
|
||||
|
||||
setup_ = cast(HTTPPollingSetup, setup_)
|
||||
|
||||
if not bot:
|
||||
request = await _build_request(setup_)
|
||||
if not request:
|
||||
@ -264,7 +266,6 @@ class Driver(ForwardDriver):
|
||||
bot.request = request
|
||||
|
||||
request = cast(HTTPRequest, request)
|
||||
setup_ = cast(HTTPPollingSetup, setup_)
|
||||
|
||||
headers = request.headers
|
||||
timeout = aiohttp.ClientTimeout(30)
|
||||
|
@ -409,6 +409,8 @@ class Driver(ReverseDriver, ForwardDriver):
|
||||
await asyncio.sleep(3)
|
||||
continue
|
||||
|
||||
setup_ = cast(HTTPPollingSetup, setup_)
|
||||
|
||||
if not bot:
|
||||
request = await _build_request(setup_)
|
||||
if not request:
|
||||
@ -423,7 +425,6 @@ class Driver(ReverseDriver, ForwardDriver):
|
||||
continue
|
||||
bot.request = request
|
||||
|
||||
setup_ = cast(HTTPPollingSetup, setup_)
|
||||
request = cast(HTTPRequest, request)
|
||||
headers = request.headers
|
||||
|
||||
|
@ -6,6 +6,8 @@
|
||||
这些异常并非所有需要用户处理,在 NoneBot 内部运行时被捕获,并进行对应操作。
|
||||
"""
|
||||
|
||||
from typing import Optional
|
||||
|
||||
|
||||
class NoneBotException(Exception):
|
||||
"""
|
||||
@ -13,9 +15,33 @@ class NoneBotException(Exception):
|
||||
|
||||
所有 NoneBot 发生的异常基类。
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
# Rule Exception
|
||||
class ParserExit(NoneBotException):
|
||||
"""
|
||||
:说明:
|
||||
|
||||
``shell command`` 处理消息失败时返回的异常
|
||||
|
||||
:参数:
|
||||
|
||||
* ``status``
|
||||
* ``message``
|
||||
"""
|
||||
|
||||
def __init__(self, status: int = 0, message: Optional[str] = None):
|
||||
self.status = status
|
||||
self.message = message
|
||||
|
||||
def __repr__(self):
|
||||
return f"<ParserExit status={self.status} message={self.message}>"
|
||||
|
||||
def __str__(self):
|
||||
return self.__repr__()
|
||||
|
||||
|
||||
# Processor Exception
|
||||
class IgnoredException(NoneBotException):
|
||||
"""
|
||||
:说明:
|
||||
@ -37,71 +63,6 @@ class IgnoredException(NoneBotException):
|
||||
return self.__repr__()
|
||||
|
||||
|
||||
class ParserExit(NoneBotException):
|
||||
"""
|
||||
:说明:
|
||||
|
||||
``shell command`` 处理消息失败时返回的异常
|
||||
|
||||
:参数:
|
||||
|
||||
* ``status``
|
||||
* ``message``
|
||||
"""
|
||||
|
||||
def __init__(self, status=0, message=None):
|
||||
self.status = status
|
||||
self.message = message
|
||||
|
||||
def __repr__(self):
|
||||
return f"<ParserExit status={self.status} message={self.message}>"
|
||||
|
||||
def __str__(self):
|
||||
return self.__repr__()
|
||||
|
||||
|
||||
class PausedException(NoneBotException):
|
||||
"""
|
||||
:说明:
|
||||
|
||||
指示 NoneBot 结束当前 ``Handler`` 并等待下一条消息后继续下一个 ``Handler``。
|
||||
可用于用户输入新信息。
|
||||
|
||||
:用法:
|
||||
|
||||
可以在 ``Handler`` 中通过 ``Matcher.pause()`` 抛出。
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class RejectedException(NoneBotException):
|
||||
"""
|
||||
:说明:
|
||||
|
||||
指示 NoneBot 结束当前 ``Handler`` 并等待下一条消息后重新运行当前 ``Handler``。
|
||||
可用于用户重新输入。
|
||||
|
||||
:用法:
|
||||
|
||||
可以在 ``Handler`` 中通过 ``Matcher.reject()`` 抛出。
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class FinishedException(NoneBotException):
|
||||
"""
|
||||
:说明:
|
||||
|
||||
指示 NoneBot 结束当前 ``Handler`` 且后续 ``Handler`` 不再被运行。
|
||||
可用于结束用户会话。
|
||||
|
||||
:用法:
|
||||
|
||||
可以在 ``Handler`` 中通过 ``Matcher.finish()`` 抛出。
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class StopPropagation(NoneBotException):
|
||||
"""
|
||||
:说明:
|
||||
@ -112,9 +73,69 @@ class StopPropagation(NoneBotException):
|
||||
|
||||
在 ``Matcher.block == True`` 时抛出。
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
# Matcher Exceptions
|
||||
class MatcherException(NoneBotException):
|
||||
"""
|
||||
:说明:
|
||||
|
||||
所有 Matcher 发生的异常基类。
|
||||
"""
|
||||
|
||||
|
||||
class SkippedException(MatcherException):
|
||||
"""
|
||||
:说明:
|
||||
|
||||
指示 NoneBot 立即结束当前 ``Handler`` 的处理,继续处理下一个 ``Handler``。
|
||||
|
||||
:用法:
|
||||
|
||||
可以在 ``Handler`` 中通过 ``Matcher.skip()`` 抛出。
|
||||
"""
|
||||
|
||||
|
||||
class PausedException(MatcherException):
|
||||
"""
|
||||
:说明:
|
||||
|
||||
指示 NoneBot 结束当前 ``Handler`` 并等待下一条消息后继续下一个 ``Handler``。
|
||||
可用于用户输入新信息。
|
||||
|
||||
:用法:
|
||||
|
||||
可以在 ``Handler`` 中通过 ``Matcher.pause()`` 抛出。
|
||||
"""
|
||||
|
||||
|
||||
class RejectedException(MatcherException):
|
||||
"""
|
||||
:说明:
|
||||
|
||||
指示 NoneBot 结束当前 ``Handler`` 并等待下一条消息后重新运行当前 ``Handler``。
|
||||
可用于用户重新输入。
|
||||
|
||||
:用法:
|
||||
|
||||
可以在 ``Handler`` 中通过 ``Matcher.reject()`` 抛出。
|
||||
"""
|
||||
|
||||
|
||||
class FinishedException(MatcherException):
|
||||
"""
|
||||
:说明:
|
||||
|
||||
指示 NoneBot 结束当前 ``Handler`` 且后续 ``Handler`` 不再被运行。
|
||||
可用于结束用户会话。
|
||||
|
||||
:用法:
|
||||
|
||||
可以在 ``Handler`` 中通过 ``Matcher.finish()`` 抛出。
|
||||
"""
|
||||
|
||||
|
||||
# Adapter Exceptions
|
||||
class AdapterException(NoneBotException):
|
||||
"""
|
||||
:说明:
|
||||
@ -130,7 +151,7 @@ class AdapterException(NoneBotException):
|
||||
self.adapter_name = adapter_name
|
||||
|
||||
|
||||
class NoLogException(Exception):
|
||||
class NoLogException(AdapterException):
|
||||
"""
|
||||
:说明:
|
||||
|
||||
|
@ -5,172 +5,114 @@
|
||||
该模块实现事件处理函数的封装,以实现动态参数等功能。
|
||||
"""
|
||||
|
||||
import inspect
|
||||
from typing import _eval_type # type: ignore
|
||||
from typing import (TYPE_CHECKING, Any, Dict, List, Type, Union, Optional,
|
||||
ForwardRef)
|
||||
import asyncio
|
||||
from contextlib import AsyncExitStack
|
||||
from typing import Any, Dict, List, Type, Callable, Optional
|
||||
|
||||
from nonebot.log import logger
|
||||
from nonebot.typing import T_State, T_Handler
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from nonebot.matcher import Matcher
|
||||
from nonebot.adapters import Bot, Event
|
||||
from nonebot.utils import get_name, run_sync
|
||||
from nonebot.dependencies import (Param, Dependent, DependsWrapper,
|
||||
get_dependent, solve_dependencies,
|
||||
get_parameterless_sub_dependant)
|
||||
|
||||
|
||||
class Handler:
|
||||
"""事件处理函数类"""
|
||||
"""事件处理器类。支持依赖注入。"""
|
||||
|
||||
def __init__(self, func: T_Handler):
|
||||
"""装饰事件处理函数以便根据动态参数运行"""
|
||||
self.func: T_Handler = func
|
||||
def __init__(self,
|
||||
func: Callable[..., Any],
|
||||
*,
|
||||
name: Optional[str] = None,
|
||||
dependencies: Optional[List[DependsWrapper]] = None,
|
||||
allow_types: Optional[List[Type[Param]]] = None):
|
||||
"""
|
||||
:类型: ``T_Handler``
|
||||
:说明:
|
||||
|
||||
装饰一个函数为事件处理器。
|
||||
|
||||
:参数:
|
||||
|
||||
* ``func: Callable[..., Any]``: 事件处理函数。
|
||||
* ``name: Optional[str]``: 事件处理器名称。默认为函数名。
|
||||
* ``dependencies: Optional[List[DependsWrapper]]``: 额外的非参数依赖注入。
|
||||
* ``allow_types: Optional[List[Type[Param]]]``: 允许的参数类型。
|
||||
"""
|
||||
self.func = func
|
||||
"""
|
||||
:类型: ``Callable[..., Any]``
|
||||
:说明: 事件处理函数
|
||||
"""
|
||||
self.signature: inspect.Signature = self.get_signature()
|
||||
self.name = get_name(func) if name is None else name
|
||||
"""
|
||||
:类型: ``inspect.Signature``
|
||||
:说明: 事件处理函数签名
|
||||
:类型: ``str``
|
||||
:说明: 事件处理函数名
|
||||
"""
|
||||
self.allow_types = allow_types or []
|
||||
"""
|
||||
:类型: ``List[Type[Param]]``
|
||||
:说明: 事件处理器允许的参数类型
|
||||
"""
|
||||
|
||||
self.dependencies = dependencies or []
|
||||
"""
|
||||
:类型: ``List[DependsWrapper]``
|
||||
:说明: 事件处理器的额外依赖
|
||||
"""
|
||||
self.sub_dependents: Dict[Callable[..., Any], Dependent] = {}
|
||||
if dependencies:
|
||||
for depends in dependencies:
|
||||
self.cache_dependent(depends)
|
||||
self.dependent = get_dependent(func=func, allow_types=self.allow_types)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return (f"<Handler {self.func.__name__}(bot: {self.bot_type}, "
|
||||
f"event: {self.event_type}, state: {self.state_type}, "
|
||||
f"matcher: {self.matcher_type})>")
|
||||
return (
|
||||
f"<Handler {self.name}({', '.join(map(str, self.dependent.params))})>"
|
||||
)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return repr(self)
|
||||
|
||||
async def __call__(self, matcher: "Matcher", bot: "Bot", event: "Event",
|
||||
state: T_State):
|
||||
BotType = ((self.bot_type is not inspect.Parameter.empty) and
|
||||
inspect.isclass(self.bot_type) and self.bot_type)
|
||||
if BotType and not isinstance(bot, BotType):
|
||||
logger.debug(
|
||||
f"Matcher {matcher} bot type {type(bot)} not match annotation {BotType}, ignored"
|
||||
)
|
||||
return
|
||||
async def __call__(self,
|
||||
*,
|
||||
_stack: Optional[AsyncExitStack] = None,
|
||||
_dependency_cache: Optional[Dict[Callable[..., Any],
|
||||
Any]] = None,
|
||||
**params) -> Any:
|
||||
values, _ = await solve_dependencies(
|
||||
_dependent=self.dependent,
|
||||
_stack=_stack,
|
||||
_sub_dependents=[
|
||||
self.sub_dependents[dependency.dependency] # type: ignore
|
||||
for dependency in self.dependencies
|
||||
],
|
||||
_dependency_cache=_dependency_cache,
|
||||
**params)
|
||||
|
||||
EventType = ((self.event_type is not inspect.Parameter.empty) and
|
||||
inspect.isclass(self.event_type) and self.event_type)
|
||||
if EventType and not isinstance(event, EventType):
|
||||
logger.debug(
|
||||
f"Matcher {matcher} event type {type(event)} not match annotation {EventType}, ignored"
|
||||
)
|
||||
return
|
||||
if asyncio.iscoroutinefunction(self.func):
|
||||
return await self.func(**values)
|
||||
else:
|
||||
return await run_sync(self.func)(**values)
|
||||
|
||||
args = {"bot": bot, "event": event, "state": state, "matcher": matcher}
|
||||
await self.func(
|
||||
**{
|
||||
k: v
|
||||
for k, v in args.items()
|
||||
if self.signature.parameters.get(k, None) is not None
|
||||
})
|
||||
def cache_dependent(self, dependency: DependsWrapper):
|
||||
if not dependency.dependency:
|
||||
raise ValueError(f"{dependency} has no dependency")
|
||||
if dependency.dependency in self.sub_dependents:
|
||||
raise ValueError(f"{dependency} is already in dependencies")
|
||||
sub_dependant = get_parameterless_sub_dependant(
|
||||
depends=dependency, allow_types=self.allow_types)
|
||||
self.sub_dependents[dependency.dependency] = sub_dependant
|
||||
|
||||
@property
|
||||
def bot_type(self) -> Union[Type["Bot"], inspect.Parameter.empty]:
|
||||
"""
|
||||
:类型: ``Union[Type["Bot"], inspect.Parameter.empty]``
|
||||
:说明: 事件处理函数接受的 Bot 对象类型"""
|
||||
return self.signature.parameters["bot"].annotation
|
||||
def prepend_dependency(self, dependency: DependsWrapper):
|
||||
self.cache_dependent(dependency)
|
||||
self.dependencies.insert(0, dependency)
|
||||
|
||||
@property
|
||||
def event_type(
|
||||
self) -> Optional[Union[Type["Event"], inspect.Parameter.empty]]:
|
||||
"""
|
||||
:类型: ``Optional[Union[Type[Event], inspect.Parameter.empty]]``
|
||||
:说明: 事件处理函数接受的 event 类型 / 不需要 event 参数
|
||||
"""
|
||||
if "event" not in self.signature.parameters:
|
||||
return None
|
||||
return self.signature.parameters["event"].annotation
|
||||
def append_dependency(self, dependency: DependsWrapper):
|
||||
self.cache_dependent(dependency)
|
||||
self.dependencies.append(dependency)
|
||||
|
||||
@property
|
||||
def state_type(self) -> Optional[Union[T_State, inspect.Parameter.empty]]:
|
||||
"""
|
||||
:类型: ``Optional[Union[T_State, inspect.Parameter.empty]]``
|
||||
:说明: 事件处理函数是否接受 state 参数
|
||||
"""
|
||||
if "state" not in self.signature.parameters:
|
||||
return None
|
||||
return self.signature.parameters["state"].annotation
|
||||
|
||||
@property
|
||||
def matcher_type(
|
||||
self) -> Optional[Union[Type["Matcher"], inspect.Parameter.empty]]:
|
||||
"""
|
||||
:类型: ``Optional[Union[Type["Matcher"], inspect.Parameter.empty]]``
|
||||
:说明: 事件处理函数是否接受 matcher 参数
|
||||
"""
|
||||
if "matcher" not in self.signature.parameters:
|
||||
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"].replace(
|
||||
annotation=wrapped_signature.parameters["bot"].annotation)
|
||||
if "event" in wrapped_signature.parameters and "event" in signature.parameters:
|
||||
signature.parameters["event"].replace(
|
||||
annotation=wrapped_signature.parameters["event"].annotation)
|
||||
return signature
|
||||
|
||||
def update_signature(
|
||||
self, **kwargs: Union[None, Type["Bot"], Type["Event"], Type["Matcher"],
|
||||
T_State, inspect.Parameter.empty]
|
||||
) -> 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.func, "__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'")
|
||||
def remove_dependency(self, dependency: DependsWrapper):
|
||||
if not dependency.dependency:
|
||||
raise ValueError(f"{dependency} has no dependency")
|
||||
if dependency.dependency in self.sub_dependents:
|
||||
del self.sub_dependents[dependency.dependency]
|
||||
if dependency in self.dependencies:
|
||||
self.dependencies.remove(dependency)
|
||||
|
@ -5,35 +5,39 @@
|
||||
该模块实现事件响应器的创建与运行,并提供一些快捷方法来帮助用户更好的与机器人进行对话 。
|
||||
"""
|
||||
|
||||
from functools import wraps
|
||||
from types import ModuleType
|
||||
from datetime import datetime
|
||||
from contextvars import ContextVar
|
||||
from collections import defaultdict
|
||||
from contextlib import AsyncExitStack
|
||||
from typing import (TYPE_CHECKING, Any, Dict, List, Type, Union, Callable,
|
||||
NoReturn, Optional)
|
||||
|
||||
from nonebot import params
|
||||
from nonebot.rule import Rule
|
||||
from nonebot.log import logger
|
||||
from nonebot.handler import Handler
|
||||
from nonebot.adapters import MessageTemplate
|
||||
from nonebot.dependencies import DependsWrapper
|
||||
from nonebot.permission import USER, Permission
|
||||
from nonebot.adapters import (Bot, Event, Message, MessageSegment,
|
||||
MessageTemplate)
|
||||
from nonebot.exception import (PausedException, StopPropagation,
|
||||
FinishedException, RejectedException)
|
||||
SkippedException, FinishedException,
|
||||
RejectedException)
|
||||
from nonebot.typing import (T_State, T_Handler, T_ArgsParser, T_TypeUpdater,
|
||||
T_StateFactory, T_PermissionUpdater)
|
||||
T_StateFactory, T_DependencyCache,
|
||||
T_PermissionUpdater)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from nonebot.plugin import Plugin
|
||||
from nonebot.adapters import Bot, Event, Message, MessageSegment
|
||||
|
||||
matchers: Dict[int, List[Type["Matcher"]]] = defaultdict(list)
|
||||
"""
|
||||
:类型: ``Dict[int, List[Type[Matcher]]]``
|
||||
:说明: 用于存储当前所有的事件响应器
|
||||
"""
|
||||
current_bot: ContextVar["Bot"] = ContextVar("current_bot")
|
||||
current_event: ContextVar["Event"] = ContextVar("current_event")
|
||||
current_bot: ContextVar[Bot] = ContextVar("current_bot")
|
||||
current_event: ContextVar[Event] = ContextVar("current_event")
|
||||
current_state: ContextVar[T_State] = ContextVar("current_state")
|
||||
|
||||
|
||||
@ -152,6 +156,11 @@ class Matcher(metaclass=MatcherMeta):
|
||||
:说明: 事件响应器权限更新函数
|
||||
"""
|
||||
|
||||
HANDLER_PARAM_TYPES = [
|
||||
params.BotParam, params.EventParam, params.StateParam,
|
||||
params.MatcherParam, params.DefaultParam
|
||||
]
|
||||
|
||||
def __init__(self):
|
||||
"""实例化 Matcher 以便运行"""
|
||||
self.handlers = self.handlers.copy()
|
||||
@ -228,8 +237,8 @@ class Matcher(metaclass=MatcherMeta):
|
||||
"permission":
|
||||
permission or Permission(),
|
||||
"handlers": [
|
||||
handler
|
||||
if isinstance(handler, Handler) else Handler(handler)
|
||||
handler if isinstance(handler, Handler) else Handler(
|
||||
handler, allow_types=cls.HANDLER_PARAM_TYPES)
|
||||
for handler in handlers
|
||||
] if handlers else [],
|
||||
"temp":
|
||||
@ -258,7 +267,13 @@ 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,
|
||||
stack: Optional[AsyncExitStack] = None,
|
||||
dependency_cache: Optional[Dict[Callable[..., Any],
|
||||
Any]] = None) -> bool:
|
||||
"""
|
||||
:说明:
|
||||
|
||||
@ -275,11 +290,17 @@ class Matcher(metaclass=MatcherMeta):
|
||||
"""
|
||||
event_type = event.get_type()
|
||||
return (event_type == (cls.type or event_type) and
|
||||
await cls.permission(bot, event))
|
||||
await cls.permission(bot, event, stack, dependency_cache))
|
||||
|
||||
@classmethod
|
||||
async def check_rule(cls, bot: "Bot", event: "Event",
|
||||
state: T_State) -> bool:
|
||||
async def check_rule(
|
||||
cls,
|
||||
bot: Bot,
|
||||
event: Event,
|
||||
state: T_State,
|
||||
stack: Optional[AsyncExitStack] = None,
|
||||
dependency_cache: Optional[Dict[Callable[..., Any],
|
||||
Any]] = None) -> bool:
|
||||
"""
|
||||
:说明:
|
||||
|
||||
@ -297,7 +318,7 @@ class Matcher(metaclass=MatcherMeta):
|
||||
"""
|
||||
event_type = event.get_type()
|
||||
return (event_type == (cls.type or event_type) and
|
||||
await cls.rule(bot, event, state))
|
||||
await cls.rule(bot, event, state, stack, dependency_cache))
|
||||
|
||||
@classmethod
|
||||
def args_parser(cls, func: T_ArgsParser) -> T_ArgsParser:
|
||||
@ -343,8 +364,13 @@ class Matcher(metaclass=MatcherMeta):
|
||||
return func
|
||||
|
||||
@classmethod
|
||||
def append_handler(cls, handler: T_Handler) -> Handler:
|
||||
handler_ = Handler(handler)
|
||||
def append_handler(
|
||||
cls,
|
||||
handler: T_Handler,
|
||||
dependencies: Optional[List[DependsWrapper]] = None) -> Handler:
|
||||
handler_ = Handler(handler,
|
||||
dependencies=dependencies,
|
||||
allow_types=cls.HANDLER_PARAM_TYPES)
|
||||
cls.handlers.append(handler_)
|
||||
return handler_
|
||||
|
||||
@ -378,22 +404,22 @@ class Matcher(metaclass=MatcherMeta):
|
||||
* 无
|
||||
"""
|
||||
|
||||
async def _receive(bot: "Bot", event: "Event") -> NoReturn:
|
||||
raise PausedException
|
||||
|
||||
if cls.handlers:
|
||||
# 已有前置handlers则接受一条新的消息,否则视为接收初始消息
|
||||
receive_handler = cls.append_handler(_receive)
|
||||
else:
|
||||
receive_handler = None
|
||||
async def _receive(state: T_State) -> Union[None, NoReturn]:
|
||||
if state.get(_receive):
|
||||
return
|
||||
state[_receive] = True
|
||||
raise RejectedException
|
||||
|
||||
def _decorator(func: T_Handler) -> T_Handler:
|
||||
if not cls.handlers or cls.handlers[-1] is not func:
|
||||
func_handler = cls.append_handler(func)
|
||||
if receive_handler:
|
||||
receive_handler.update_signature(
|
||||
bot=func_handler.bot_type,
|
||||
event=func_handler.event_type)
|
||||
|
||||
depend = DependsWrapper(_receive)
|
||||
|
||||
if cls.handlers and cls.handlers[-1].func is func:
|
||||
func_handler = cls.handlers[-1]
|
||||
func_handler.prepend_dependency(depend)
|
||||
else:
|
||||
cls.append_handler(
|
||||
func, dependencies=[depend] if cls.handlers else [])
|
||||
|
||||
return func
|
||||
|
||||
@ -403,7 +429,7 @@ class Matcher(metaclass=MatcherMeta):
|
||||
def got(
|
||||
cls,
|
||||
key: str,
|
||||
prompt: Optional[Union[str, "Message", "MessageSegment",
|
||||
prompt: Optional[Union[str, Message, MessageSegment,
|
||||
MessageTemplate]] = None,
|
||||
args_parser: Optional[T_ArgsParser] = None
|
||||
) -> Callable[[T_Handler], T_Handler]:
|
||||
@ -419,8 +445,12 @@ class Matcher(metaclass=MatcherMeta):
|
||||
* ``args_parser: Optional[T_ArgsParser]``: 可选参数解析函数,空则使用默认解析函数
|
||||
"""
|
||||
|
||||
async def _key_getter(bot: "Bot", event: "Event", state: T_State):
|
||||
async def _key_getter(bot: Bot, event: Event, state: T_State):
|
||||
if state.get(f"_{key}_prompted"):
|
||||
return
|
||||
|
||||
state["_current_key"] = key
|
||||
state[f"_{key}_prompted"] = True
|
||||
if key not in state:
|
||||
if prompt is not None:
|
||||
if isinstance(prompt, MessageTemplate):
|
||||
@ -428,52 +458,40 @@ class Matcher(metaclass=MatcherMeta):
|
||||
else:
|
||||
_prompt = prompt
|
||||
await bot.send(event=event, message=_prompt)
|
||||
raise PausedException
|
||||
raise RejectedException
|
||||
else:
|
||||
state["_skip_key"] = True
|
||||
state[f"_{key}_parsed"] = True
|
||||
|
||||
async def _key_parser(bot: "Bot", event: "Event", state: T_State):
|
||||
if key in state and state.get("_skip_key"):
|
||||
del state["_skip_key"]
|
||||
async def _key_parser(bot: Bot, event: Event, state: T_State):
|
||||
if key in state and state.get(f"_{key}_parsed"):
|
||||
return
|
||||
|
||||
parser = args_parser or cls._default_parser
|
||||
if parser:
|
||||
# parser = cast(T_ArgsParser["Bot", "Event"], parser)
|
||||
await parser(bot, event, state)
|
||||
else:
|
||||
state[state["_current_key"]] = str(event.get_message())
|
||||
|
||||
getter_handler = cls.append_handler(_key_getter)
|
||||
parser_handler = cls.append_handler(_key_parser)
|
||||
state[key] = str(event.get_message())
|
||||
state[f"_{key}_parsed"] = True
|
||||
|
||||
def _decorator(func: T_Handler) -> T_Handler:
|
||||
if not hasattr(cls.handlers[-1].func, "__wrapped__"):
|
||||
parser = cls.handlers.pop()
|
||||
func_handler = Handler(func)
|
||||
|
||||
@wraps(func)
|
||||
async def wrapper(bot: "Bot", event: "Event", state: T_State,
|
||||
matcher: Matcher):
|
||||
await parser(matcher, bot, event, state)
|
||||
await func_handler(matcher, bot, event, state)
|
||||
if "_current_key" in state:
|
||||
del state["_current_key"]
|
||||
get_depend = DependsWrapper(_key_getter)
|
||||
parser_depend = DependsWrapper(_key_parser)
|
||||
|
||||
wrapper_handler = cls.append_handler(wrapper)
|
||||
|
||||
getter_handler.update_signature(
|
||||
bot=wrapper_handler.bot_type,
|
||||
event=wrapper_handler.event_type)
|
||||
parser_handler.update_signature(
|
||||
bot=wrapper_handler.bot_type,
|
||||
event=wrapper_handler.event_type)
|
||||
if cls.handlers and cls.handlers[-1].func is func:
|
||||
func_handler = cls.handlers[-1]
|
||||
func_handler.prepend_dependency(parser_depend)
|
||||
func_handler.prepend_dependency(get_depend)
|
||||
else:
|
||||
cls.append_handler(func,
|
||||
dependencies=[get_depend, parser_depend])
|
||||
|
||||
return func
|
||||
|
||||
return _decorator
|
||||
|
||||
@classmethod
|
||||
async def send(cls, message: Union[str, "Message", "MessageSegment",
|
||||
async def send(cls, message: Union[str, Message, MessageSegment,
|
||||
MessageTemplate], **kwargs) -> Any:
|
||||
"""
|
||||
:说明:
|
||||
@ -496,7 +514,7 @@ class Matcher(metaclass=MatcherMeta):
|
||||
|
||||
@classmethod
|
||||
async def finish(cls,
|
||||
message: Optional[Union[str, "Message", "MessageSegment",
|
||||
message: Optional[Union[str, Message, MessageSegment,
|
||||
MessageTemplate]] = None,
|
||||
**kwargs) -> NoReturn:
|
||||
"""
|
||||
@ -522,7 +540,7 @@ class Matcher(metaclass=MatcherMeta):
|
||||
|
||||
@classmethod
|
||||
async def pause(cls,
|
||||
prompt: Optional[Union[str, "Message", "MessageSegment",
|
||||
prompt: Optional[Union[str, Message, MessageSegment,
|
||||
MessageTemplate]] = None,
|
||||
**kwargs) -> NoReturn:
|
||||
"""
|
||||
@ -548,8 +566,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:
|
||||
"""
|
||||
:说明:
|
||||
@ -564,6 +582,8 @@ class Matcher(metaclass=MatcherMeta):
|
||||
bot = current_bot.get()
|
||||
event = current_event.get()
|
||||
state = current_state.get()
|
||||
if "_current_key" in state and f"_{state['_current_key']}_parsed" in state:
|
||||
del state[f"_{state['_current_key']}_parsed"]
|
||||
if isinstance(prompt, MessageTemplate):
|
||||
_prompt = prompt.format(**state)
|
||||
else:
|
||||
@ -581,7 +601,12 @@ class Matcher(metaclass=MatcherMeta):
|
||||
self.block = True
|
||||
|
||||
# 运行handlers
|
||||
async def run(self, bot: "Bot", event: "Event", state: T_State):
|
||||
async def run(self,
|
||||
bot: Bot,
|
||||
event: Event,
|
||||
state: T_State,
|
||||
stack: Optional[AsyncExitStack] = None,
|
||||
dependency_cache: Optional[T_DependencyCache] = None):
|
||||
b_t = current_bot.set(bot)
|
||||
e_t = current_event.set(event)
|
||||
s_t = current_state.set(self.state)
|
||||
@ -594,7 +619,15 @@ class Matcher(metaclass=MatcherMeta):
|
||||
while self.handlers:
|
||||
handler = self.handlers.pop(0)
|
||||
logger.debug(f"Running handler {handler}")
|
||||
await handler(self, bot, event, self.state)
|
||||
try:
|
||||
await handler(matcher=self,
|
||||
bot=bot,
|
||||
event=event,
|
||||
state=self.state,
|
||||
_stack=stack,
|
||||
_dependency_cache=dependency_cache)
|
||||
except SkippedException:
|
||||
pass
|
||||
|
||||
except RejectedException:
|
||||
self.handlers.insert(0, handler) # type: ignore
|
||||
@ -610,11 +643,8 @@ class Matcher(metaclass=MatcherMeta):
|
||||
|
||||
updater = self.__class__._default_permission_updater
|
||||
if updater:
|
||||
permission = await updater(
|
||||
bot,
|
||||
event,
|
||||
self.state, # type: ignore
|
||||
self.permission)
|
||||
permission = await updater(bot, event, self.state,
|
||||
self.permission)
|
||||
else:
|
||||
permission = USER(event.get_session_id(), perm=self.permission)
|
||||
|
||||
@ -647,11 +677,8 @@ class Matcher(metaclass=MatcherMeta):
|
||||
|
||||
updater = self.__class__._default_permission_updater
|
||||
if updater:
|
||||
permission = await updater(
|
||||
bot,
|
||||
event,
|
||||
self.state, # type: ignore
|
||||
self.permission)
|
||||
permission = await updater(bot, event, self.state,
|
||||
self.permission)
|
||||
else:
|
||||
permission = USER(event.get_session_id(), perm=self.permission)
|
||||
|
||||
|
@ -7,23 +7,39 @@ NoneBot 内部处理并按优先级分发事件给所有事件响应器,提供
|
||||
|
||||
import asyncio
|
||||
from datetime import datetime
|
||||
from typing import TYPE_CHECKING, Set, Type, Optional
|
||||
from contextlib import AsyncExitStack
|
||||
from typing import TYPE_CHECKING, Any, Set, Dict, Type, Optional
|
||||
|
||||
from nonebot import params
|
||||
from nonebot.log import logger
|
||||
from nonebot.rule import TrieRule
|
||||
from nonebot.handler import Handler
|
||||
from nonebot.utils import escape_tag
|
||||
from nonebot.matcher import Matcher, matchers
|
||||
from nonebot.exception import NoLogException, StopPropagation, IgnoredException
|
||||
from nonebot.typing import (T_State, T_RunPreProcessor, T_RunPostProcessor,
|
||||
T_EventPreProcessor, T_EventPostProcessor)
|
||||
from nonebot.typing import (T_State, T_DependencyCache, T_RunPreProcessor,
|
||||
T_RunPostProcessor, T_EventPreProcessor,
|
||||
T_EventPostProcessor)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from nonebot.adapters import Bot, Event
|
||||
|
||||
_event_preprocessors: Set[T_EventPreProcessor] = set()
|
||||
_event_postprocessors: Set[T_EventPostProcessor] = set()
|
||||
_run_preprocessors: Set[T_RunPreProcessor] = set()
|
||||
_run_postprocessors: Set[T_RunPostProcessor] = set()
|
||||
_event_preprocessors: Set[Handler] = set()
|
||||
_event_postprocessors: Set[Handler] = set()
|
||||
_run_preprocessors: Set[Handler] = set()
|
||||
_run_postprocessors: Set[Handler] = set()
|
||||
|
||||
EVENT_PCS_PARAMS = [
|
||||
params.BotParam, params.EventParam, params.StateParam, params.DefaultParam
|
||||
]
|
||||
RUN_PREPCS_PARAMS = [
|
||||
params.MatcherParam, params.BotParam, params.EventParam, params.StateParam,
|
||||
params.DefaultParam
|
||||
]
|
||||
RUN_POSTPCS_PARAMS = [
|
||||
params.MatcherParam, params.ExceptionParam, params.BotParam,
|
||||
params.EventParam, params.StateParam, params.DefaultParam
|
||||
]
|
||||
|
||||
|
||||
def event_preprocessor(func: T_EventPreProcessor) -> T_EventPreProcessor:
|
||||
@ -31,16 +47,8 @@ def event_preprocessor(func: T_EventPreProcessor) -> T_EventPreProcessor:
|
||||
:说明:
|
||||
|
||||
事件预处理。装饰一个函数,使它在每次接收到事件并分发给各响应器之前执行。
|
||||
|
||||
:参数:
|
||||
|
||||
事件预处理函数接收三个参数。
|
||||
|
||||
* ``bot: Bot``: Bot 对象
|
||||
* ``event: Event``: Event 对象
|
||||
* ``state: T_State``: 当前 State
|
||||
"""
|
||||
_event_preprocessors.add(func)
|
||||
_event_preprocessors.add(Handler(func, allow_types=EVENT_PCS_PARAMS))
|
||||
return func
|
||||
|
||||
|
||||
@ -49,16 +57,8 @@ def event_postprocessor(func: T_EventPostProcessor) -> T_EventPostProcessor:
|
||||
:说明:
|
||||
|
||||
事件后处理。装饰一个函数,使它在每次接收到事件并分发给各响应器之后执行。
|
||||
|
||||
:参数:
|
||||
|
||||
事件后处理函数接收三个参数。
|
||||
|
||||
* ``bot: Bot``: Bot 对象
|
||||
* ``event: Event``: Event 对象
|
||||
* ``state: T_State``: 当前事件运行前 State
|
||||
"""
|
||||
_event_postprocessors.add(func)
|
||||
_event_postprocessors.add(Handler(func, allow_types=EVENT_PCS_PARAMS))
|
||||
return func
|
||||
|
||||
|
||||
@ -67,17 +67,8 @@ def run_preprocessor(func: T_RunPreProcessor) -> T_RunPreProcessor:
|
||||
:说明:
|
||||
|
||||
运行预处理。装饰一个函数,使它在每次事件响应器运行前执行。
|
||||
|
||||
:参数:
|
||||
|
||||
运行预处理函数接收四个参数。
|
||||
|
||||
* ``matcher: Matcher``: 当前要运行的事件响应器
|
||||
* ``bot: Bot``: Bot 对象
|
||||
* ``event: Event``: Event 对象
|
||||
* ``state: T_State``: 当前 State
|
||||
"""
|
||||
_run_preprocessors.add(func)
|
||||
_run_preprocessors.add(Handler(func, allow_types=RUN_PREPCS_PARAMS))
|
||||
return func
|
||||
|
||||
|
||||
@ -86,23 +77,19 @@ def run_postprocessor(func: T_RunPostProcessor) -> T_RunPostProcessor:
|
||||
:说明:
|
||||
|
||||
运行后处理。装饰一个函数,使它在每次事件响应器运行后执行。
|
||||
|
||||
:参数:
|
||||
|
||||
运行后处理函数接收五个参数。
|
||||
|
||||
* ``matcher: Matcher``: 运行完毕的事件响应器
|
||||
* ``exception: Optional[Exception]``: 事件响应器运行错误(如果存在)
|
||||
* ``bot: Bot``: Bot 对象
|
||||
* ``event: Event``: Event 对象
|
||||
* ``state: T_State``: 当前 State
|
||||
"""
|
||||
_run_postprocessors.add(func)
|
||||
_run_postprocessors.add(Handler(func, allow_types=RUN_POSTPCS_PARAMS))
|
||||
return func
|
||||
|
||||
|
||||
async def _check_matcher(priority: int, Matcher: Type[Matcher], bot: "Bot",
|
||||
event: "Event", state: T_State) -> None:
|
||||
async def _check_matcher(
|
||||
priority: int,
|
||||
Matcher: Type[Matcher],
|
||||
bot: "Bot",
|
||||
event: "Event",
|
||||
state: T_State,
|
||||
stack: Optional[AsyncExitStack] = None,
|
||||
dependency_cache: Optional[T_DependencyCache] = None) -> None:
|
||||
if Matcher.expire_time and datetime.now() > Matcher.expire_time:
|
||||
try:
|
||||
matchers[priority].remove(Matcher)
|
||||
@ -112,7 +99,9 @@ async def _check_matcher(priority: int, Matcher: Type[Matcher], bot: "Bot",
|
||||
|
||||
try:
|
||||
if not await Matcher.check_perm(
|
||||
bot, event) or not await Matcher.check_rule(bot, event, state):
|
||||
bot, event, stack,
|
||||
dependency_cache) or not await Matcher.check_rule(
|
||||
bot, event, state, stack, dependency_cache):
|
||||
return
|
||||
except Exception as e:
|
||||
logger.opt(colors=True, exception=e).error(
|
||||
@ -125,17 +114,29 @@ async def _check_matcher(priority: int, Matcher: Type[Matcher], bot: "Bot",
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
await _run_matcher(Matcher, bot, event, state)
|
||||
await _run_matcher(Matcher, bot, event, state, stack, dependency_cache)
|
||||
|
||||
|
||||
async def _run_matcher(Matcher: Type[Matcher], bot: "Bot", event: "Event",
|
||||
state: T_State) -> None:
|
||||
async def _run_matcher(
|
||||
Matcher: Type[Matcher],
|
||||
bot: "Bot",
|
||||
event: "Event",
|
||||
state: T_State,
|
||||
stack: Optional[AsyncExitStack] = None,
|
||||
dependency_cache: Optional[T_DependencyCache] = None) -> None:
|
||||
logger.info(f"Event will be handled by {Matcher}")
|
||||
|
||||
matcher = Matcher()
|
||||
|
||||
coros = list(
|
||||
map(lambda x: x(matcher, bot, event, state), _run_preprocessors))
|
||||
map(
|
||||
lambda x: x(matcher=matcher,
|
||||
bot=bot,
|
||||
event=event,
|
||||
state=state,
|
||||
_stack=stack,
|
||||
_dependency_cache=dependency_cache),
|
||||
_run_preprocessors))
|
||||
if coros:
|
||||
try:
|
||||
await asyncio.gather(*coros)
|
||||
@ -153,7 +154,7 @@ async def _run_matcher(Matcher: Type[Matcher], bot: "Bot", event: "Event",
|
||||
|
||||
try:
|
||||
logger.debug(f"Running matcher {matcher}")
|
||||
await matcher.run(bot, event, state)
|
||||
await matcher.run(bot, event, state, stack, dependency_cache)
|
||||
except Exception as e:
|
||||
logger.opt(colors=True, exception=e).error(
|
||||
f"<r><bg #f8bbd0>Running matcher {matcher} failed.</bg #f8bbd0></r>"
|
||||
@ -161,7 +162,14 @@ async def _run_matcher(Matcher: Type[Matcher], bot: "Bot", event: "Event",
|
||||
exception = e
|
||||
|
||||
coros = list(
|
||||
map(lambda x: x(matcher, exception, bot, event, state),
|
||||
map(
|
||||
lambda x: x(matcher=matcher,
|
||||
exception=exception,
|
||||
bot=bot,
|
||||
event=event,
|
||||
state=state,
|
||||
_stack=stack,
|
||||
_dependency_cache=dependency_cache),
|
||||
_run_postprocessors))
|
||||
if coros:
|
||||
try:
|
||||
@ -203,59 +211,79 @@ async def handle_event(bot: "Bot", event: "Event") -> None:
|
||||
if show_log:
|
||||
logger.opt(colors=True).success(log_msg)
|
||||
|
||||
state = {}
|
||||
coros = list(map(lambda x: x(bot, event, state), _event_preprocessors))
|
||||
if coros:
|
||||
try:
|
||||
if show_log:
|
||||
logger.debug("Running PreProcessors...")
|
||||
await asyncio.gather(*coros)
|
||||
except IgnoredException as e:
|
||||
logger.opt(colors=True).info(
|
||||
f"Event {escape_tag(event.get_event_name())} is <b>ignored</b>")
|
||||
return
|
||||
except Exception as e:
|
||||
logger.opt(colors=True, exception=e).error(
|
||||
"<r><bg #f8bbd0>Error when running EventPreProcessors. "
|
||||
"Event ignored!</bg #f8bbd0></r>")
|
||||
return
|
||||
state: Dict[Any, Any] = {}
|
||||
dependency_cache: T_DependencyCache = {}
|
||||
|
||||
# Trie Match
|
||||
_, _ = TrieRule.get_value(bot, event, state)
|
||||
|
||||
break_flag = False
|
||||
for priority in sorted(matchers.keys()):
|
||||
if break_flag:
|
||||
break
|
||||
|
||||
if show_log:
|
||||
logger.debug(f"Checking for matchers in priority {priority}...")
|
||||
|
||||
pending_tasks = [
|
||||
_check_matcher(priority, matcher, bot, event, state.copy())
|
||||
for matcher in matchers[priority]
|
||||
]
|
||||
|
||||
results = await asyncio.gather(*pending_tasks, return_exceptions=True)
|
||||
|
||||
for result in results:
|
||||
if not isinstance(result, Exception):
|
||||
continue
|
||||
if isinstance(result, StopPropagation):
|
||||
break_flag = True
|
||||
logger.debug("Stop event propagation")
|
||||
else:
|
||||
logger.opt(colors=True, exception=result).error(
|
||||
"<r><bg #f8bbd0>Error when checking Matcher.</bg #f8bbd0></r>"
|
||||
async with AsyncExitStack() as stack:
|
||||
coros = list(
|
||||
map(
|
||||
lambda x: x(bot=bot,
|
||||
event=event,
|
||||
state=state,
|
||||
_stack=stack,
|
||||
_dependency_cache=dependency_cache),
|
||||
_event_preprocessors))
|
||||
if coros:
|
||||
try:
|
||||
if show_log:
|
||||
logger.debug("Running PreProcessors...")
|
||||
await asyncio.gather(*coros)
|
||||
except IgnoredException as e:
|
||||
logger.opt(colors=True).info(
|
||||
f"Event {escape_tag(event.get_event_name())} is <b>ignored</b>"
|
||||
)
|
||||
return
|
||||
except Exception as e:
|
||||
logger.opt(colors=True, exception=e).error(
|
||||
"<r><bg #f8bbd0>Error when running EventPreProcessors. "
|
||||
"Event ignored!</bg #f8bbd0></r>")
|
||||
return
|
||||
|
||||
# Trie Match
|
||||
_, _ = TrieRule.get_value(bot, event, state)
|
||||
|
||||
break_flag = False
|
||||
for priority in sorted(matchers.keys()):
|
||||
if break_flag:
|
||||
break
|
||||
|
||||
coros = list(map(lambda x: x(bot, event, state), _event_postprocessors))
|
||||
if coros:
|
||||
try:
|
||||
if show_log:
|
||||
logger.debug("Running PostProcessors...")
|
||||
await asyncio.gather(*coros)
|
||||
except Exception as e:
|
||||
logger.opt(colors=True, exception=e).error(
|
||||
"<r><bg #f8bbd0>Error when running EventPostProcessors</bg #f8bbd0></r>"
|
||||
)
|
||||
logger.debug(f"Checking for matchers in priority {priority}...")
|
||||
|
||||
pending_tasks = [
|
||||
_check_matcher(priority, matcher, bot, event, state.copy(),
|
||||
stack, dependency_cache)
|
||||
for matcher in matchers[priority]
|
||||
]
|
||||
|
||||
results = await asyncio.gather(*pending_tasks,
|
||||
return_exceptions=True)
|
||||
|
||||
for result in results:
|
||||
if not isinstance(result, Exception):
|
||||
continue
|
||||
if isinstance(result, StopPropagation):
|
||||
break_flag = True
|
||||
logger.debug("Stop event propagation")
|
||||
else:
|
||||
logger.opt(colors=True, exception=result).error(
|
||||
"<r><bg #f8bbd0>Error when checking Matcher.</bg #f8bbd0></r>"
|
||||
)
|
||||
|
||||
coros = list(
|
||||
map(
|
||||
lambda x: x(bot=bot,
|
||||
event=event,
|
||||
state=state,
|
||||
_stack=stack,
|
||||
_dependency_cache=dependency_cache),
|
||||
_event_postprocessors))
|
||||
if coros:
|
||||
try:
|
||||
if show_log:
|
||||
logger.debug("Running PostProcessors...")
|
||||
await asyncio.gather(*coros)
|
||||
except Exception as e:
|
||||
logger.opt(colors=True, exception=e).error(
|
||||
"<r><bg #f8bbd0>Error when running EventPostProcessors</bg #f8bbd0></r>"
|
||||
)
|
||||
|
84
nonebot/params.py
Normal file
84
nonebot/params.py
Normal file
@ -0,0 +1,84 @@
|
||||
import inspect
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
from pydantic.fields import Undefined
|
||||
|
||||
from nonebot.typing import T_State
|
||||
from nonebot.dependencies import Param
|
||||
from nonebot.adapters import Bot, Event
|
||||
from nonebot.utils import generic_check_issubclass
|
||||
|
||||
|
||||
class BotParam(Param):
|
||||
|
||||
@classmethod
|
||||
def _check(cls, name: str, param: inspect.Parameter) -> bool:
|
||||
return generic_check_issubclass(
|
||||
param.annotation, Bot) or (param.annotation == param.empty and
|
||||
name == "bot")
|
||||
|
||||
def _solve(self, bot: Bot, **kwargs: Any) -> Any:
|
||||
return bot
|
||||
|
||||
|
||||
class EventParam(Param):
|
||||
|
||||
@classmethod
|
||||
def _check(cls, name: str, param: inspect.Parameter) -> bool:
|
||||
return generic_check_issubclass(
|
||||
param.annotation, Event) or (param.annotation == param.empty and
|
||||
name == "event")
|
||||
|
||||
def _solve(self, event: Event, **kwargs: Any) -> Any:
|
||||
return event
|
||||
|
||||
|
||||
class StateParam(Param):
|
||||
|
||||
@classmethod
|
||||
def _check(cls, name: str, param: inspect.Parameter) -> bool:
|
||||
return generic_check_issubclass(
|
||||
param.annotation, Dict) or (param.annotation == param.empty and
|
||||
name == "state")
|
||||
|
||||
def _solve(self, state: T_State, **kwargs: Any) -> Any:
|
||||
return state
|
||||
|
||||
|
||||
class MatcherParam(Param):
|
||||
|
||||
@classmethod
|
||||
def _check(cls, name: str, param: inspect.Parameter) -> bool:
|
||||
return generic_check_issubclass(
|
||||
param.annotation, Matcher) or (param.annotation == param.empty and
|
||||
name == "matcher")
|
||||
|
||||
def _solve(self, matcher: Optional["Matcher"] = None, **kwargs: Any) -> Any:
|
||||
return matcher
|
||||
|
||||
|
||||
class ExceptionParam(Param):
|
||||
|
||||
@classmethod
|
||||
def _check(cls, name: str, param: inspect.Parameter) -> bool:
|
||||
return generic_check_issubclass(
|
||||
param.annotation, Exception) or (param.annotation == param.empty and
|
||||
name == "exception")
|
||||
|
||||
def _solve(self,
|
||||
exception: Optional[Exception] = None,
|
||||
**kwargs: Any) -> Any:
|
||||
return exception
|
||||
|
||||
|
||||
class DefaultParam(Param):
|
||||
|
||||
@classmethod
|
||||
def _check(cls, name: str, param: inspect.Parameter) -> bool:
|
||||
return param.default != param.empty
|
||||
|
||||
def _solve(self, **kwargs: Any) -> Any:
|
||||
return Undefined
|
||||
|
||||
|
||||
from nonebot.matcher import Matcher
|
@ -2,7 +2,7 @@ r"""
|
||||
权限
|
||||
====
|
||||
|
||||
每个 ``Matcher`` 拥有一个 ``Permission`` ,其中是 **异步** ``PermissionChecker`` 的集合,只要有一个 ``PermissionChecker`` 检查结果为 ``True`` 时就会继续运行。
|
||||
每个 ``Matcher`` 拥有一个 ``Permission`` ,其中是 ``PermissionChecker`` 的集合,只要有一个 ``PermissionChecker`` 检查结果为 ``True`` 时就会继续运行。
|
||||
|
||||
\:\:\:tip 提示
|
||||
``PermissionChecker`` 既可以是 async function 也可以是 sync function
|
||||
@ -10,14 +10,14 @@ r"""
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
from typing import TYPE_CHECKING, Union, Callable, NoReturn, Optional, Awaitable
|
||||
from contextlib import AsyncExitStack
|
||||
from typing import Any, Dict, List, Type, Union, Callable, NoReturn, Optional
|
||||
|
||||
from nonebot.utils import run_sync
|
||||
from nonebot import params
|
||||
from nonebot.handler import Handler
|
||||
from nonebot.adapters import Bot, Event
|
||||
from nonebot.typing import T_PermissionChecker
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from nonebot.adapters import Bot, Event
|
||||
|
||||
|
||||
class Permission:
|
||||
"""
|
||||
@ -36,15 +36,21 @@ class Permission:
|
||||
"""
|
||||
__slots__ = ("checkers",)
|
||||
|
||||
def __init__(
|
||||
self, *checkers: Callable[["Bot", "Event"],
|
||||
Awaitable[bool]]) -> None:
|
||||
HANDLER_PARAM_TYPES = [
|
||||
params.BotParam, params.EventParam, params.DefaultParam
|
||||
]
|
||||
|
||||
def __init__(self, *checkers: Union[T_PermissionChecker, Handler]) -> None:
|
||||
"""
|
||||
:参数:
|
||||
|
||||
* ``*checkers: Callable[[Bot, Event], Awaitable[bool]]``: **异步** PermissionChecker
|
||||
* ``*checkers: Union[T_PermissionChecker, Handler]``: PermissionChecker
|
||||
"""
|
||||
self.checkers = set(checkers)
|
||||
|
||||
self.checkers = set(
|
||||
checker if isinstance(checker, Handler) else Handler(
|
||||
checker, allow_types=self.HANDLER_PARAM_TYPES)
|
||||
for checker in checkers)
|
||||
"""
|
||||
:说明:
|
||||
|
||||
@ -52,10 +58,16 @@ class Permission:
|
||||
|
||||
:类型:
|
||||
|
||||
* ``Set[Callable[[Bot, Event], Awaitable[bool]]]``
|
||||
* ``Set[Handler]``
|
||||
"""
|
||||
|
||||
async def __call__(self, bot: "Bot", event: "Event") -> bool:
|
||||
async def __call__(
|
||||
self,
|
||||
bot: Bot,
|
||||
event: Event,
|
||||
stack: Optional[AsyncExitStack] = None,
|
||||
dependency_cache: Optional[Dict[Callable[..., Any],
|
||||
Any]] = None) -> bool:
|
||||
"""
|
||||
:说明:
|
||||
|
||||
@ -65,6 +77,8 @@ class Permission:
|
||||
|
||||
* ``bot: Bot``: Bot 对象
|
||||
* ``event: Event``: Event 对象
|
||||
* ``stack: Optional[AsyncExitStack]``: 异步上下文栈
|
||||
* ``dependency_cache: Optional[Dict[Callable[..., Any], Any]]``: 依赖缓存
|
||||
|
||||
:返回:
|
||||
|
||||
@ -73,7 +87,11 @@ class Permission:
|
||||
if not self.checkers:
|
||||
return True
|
||||
results = await asyncio.gather(
|
||||
*map(lambda c: c(bot, event), self.checkers))
|
||||
*(checker(bot=bot,
|
||||
event=event,
|
||||
_stack=stack,
|
||||
_dependency_cache=dependency_cache)
|
||||
for checker in self.checkers))
|
||||
return any(results)
|
||||
|
||||
def __and__(self, other) -> NoReturn:
|
||||
@ -82,31 +100,27 @@ class Permission:
|
||||
def __or__(
|
||||
self, other: Optional[Union["Permission",
|
||||
T_PermissionChecker]]) -> "Permission":
|
||||
checkers = self.checkers.copy()
|
||||
if other is None:
|
||||
return self
|
||||
elif isinstance(other, Permission):
|
||||
checkers |= other.checkers
|
||||
elif asyncio.iscoroutinefunction(other):
|
||||
checkers.add(other) # type: ignore
|
||||
return Permission(*self.checkers, *other.checkers)
|
||||
else:
|
||||
checkers.add(run_sync(other))
|
||||
return Permission(*checkers)
|
||||
return Permission(*self.checkers, other)
|
||||
|
||||
|
||||
async def _message(bot: "Bot", event: "Event") -> bool:
|
||||
async def _message(event: Event) -> bool:
|
||||
return event.get_type() == "message"
|
||||
|
||||
|
||||
async def _notice(bot: "Bot", event: "Event") -> bool:
|
||||
async def _notice(event: Event) -> bool:
|
||||
return event.get_type() == "notice"
|
||||
|
||||
|
||||
async def _request(bot: "Bot", event: "Event") -> bool:
|
||||
async def _request(event: Event) -> bool:
|
||||
return event.get_type() == "request"
|
||||
|
||||
|
||||
async def _metaevent(bot: "Bot", event: "Event") -> bool:
|
||||
async def _metaevent(event: Event) -> bool:
|
||||
return event.get_type() == "meta_event"
|
||||
|
||||
|
||||
@ -140,14 +154,14 @@ def USER(*user: str, perm: Optional[Permission] = None):
|
||||
* ``perm: Optional[Permission]``: 需要同时满足的权限
|
||||
"""
|
||||
|
||||
async def _user(bot: "Bot", event: "Event") -> bool:
|
||||
async def _user(bot: Bot, event: Event) -> bool:
|
||||
return bool(event.get_session_id() in user and
|
||||
(perm is None or await perm(bot, event)))
|
||||
|
||||
return Permission(_user)
|
||||
|
||||
|
||||
async def _superuser(bot: "Bot", event: "Event") -> bool:
|
||||
async def _superuser(bot: Bot, event: Event) -> bool:
|
||||
return (event.get_type() == "message" and
|
||||
event.get_user_id() in bot.config.superusers)
|
||||
|
||||
|
@ -2,19 +2,16 @@ import re
|
||||
import sys
|
||||
import inspect
|
||||
from types import ModuleType
|
||||
from typing import (TYPE_CHECKING, Any, Set, Dict, List, Type, Tuple, Union,
|
||||
Optional)
|
||||
from typing import Any, Set, Dict, List, Type, Tuple, Union, Optional
|
||||
|
||||
from nonebot.adapters import Event
|
||||
from nonebot.handler import Handler
|
||||
from nonebot.matcher import Matcher
|
||||
from .manager import _current_plugin
|
||||
from nonebot.permission import Permission
|
||||
from nonebot.typing import T_State, T_Handler, T_RuleChecker, T_StateFactory
|
||||
from nonebot.rule import (Rule, ArgumentParser, regex, command, keyword,
|
||||
endswith, startswith, shell_command)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from nonebot.adapters import Bot, Event
|
||||
from nonebot.rule import (PREFIX_KEY, RAW_CMD_KEY, Rule, ArgumentParser, regex,
|
||||
command, keyword, endswith, startswith, shell_command)
|
||||
|
||||
|
||||
def _store_matcher(matcher: Type[Matcher]) -> None:
|
||||
@ -376,16 +373,16 @@ def on_command(cmd: Union[str, Tuple[str, ...]],
|
||||
- ``Type[Matcher]``
|
||||
"""
|
||||
|
||||
async def _strip_cmd(bot: "Bot", event: "Event", state: T_State):
|
||||
async def _strip_cmd(event: Event, state: T_State):
|
||||
message = event.get_message()
|
||||
if len(message) < 1:
|
||||
return
|
||||
segment = message.pop(0)
|
||||
segment_text = str(segment).lstrip()
|
||||
if not segment_text.startswith(state["_prefix"]["raw_command"]):
|
||||
if not segment_text.startswith(state[PREFIX_KEY][RAW_CMD_KEY]):
|
||||
return
|
||||
new_message = message.__class__(
|
||||
segment_text[len(state["_prefix"]["raw_command"]):].lstrip())
|
||||
segment_text[len(state[PREFIX_KEY][RAW_CMD_KEY]):].lstrip())
|
||||
for new_segment in reversed(new_message):
|
||||
message.insert(0, new_segment)
|
||||
|
||||
@ -433,12 +430,11 @@ def on_shell_command(cmd: Union[str, Tuple[str, ...]],
|
||||
- ``Type[Matcher]``
|
||||
"""
|
||||
|
||||
async def _strip_cmd(bot: "Bot", event: "Event", state: T_State):
|
||||
async def _strip_cmd(event: Event, state: T_State):
|
||||
message = event.get_message()
|
||||
segment = message.pop(0)
|
||||
new_message = message.__class__(
|
||||
str(segment)
|
||||
[len(state["_prefix"]["raw_command"]):].strip()) # type: ignore
|
||||
str(segment)[len(state[PREFIX_KEY][RAW_CMD_KEY]):].strip())
|
||||
for new_segment in reversed(new_message):
|
||||
message.insert(0, new_segment)
|
||||
|
||||
|
@ -3,14 +3,14 @@ from functools import reduce
|
||||
from nonebot.rule import to_me
|
||||
from nonebot.plugin import on_command
|
||||
from nonebot.permission import SUPERUSER
|
||||
from nonebot.adapters.cqhttp import (Bot, Message, MessageEvent, MessageSegment,
|
||||
from nonebot.adapters.cqhttp import (Message, MessageEvent, MessageSegment,
|
||||
unescape)
|
||||
|
||||
say = on_command("say", to_me(), permission=SUPERUSER)
|
||||
|
||||
|
||||
@say.handle()
|
||||
async def say_unescape(bot: Bot, event: MessageEvent):
|
||||
async def say_unescape(event: MessageEvent):
|
||||
|
||||
def _unescape(message: Message, segment: MessageSegment):
|
||||
if segment.is_text():
|
||||
@ -18,12 +18,12 @@ async def say_unescape(bot: Bot, event: MessageEvent):
|
||||
return message.append(segment)
|
||||
|
||||
message = reduce(_unescape, event.get_message(), Message()) # type: ignore
|
||||
await bot.send(message=message, event=event)
|
||||
await say.send(message=message)
|
||||
|
||||
|
||||
echo = on_command("echo", to_me())
|
||||
|
||||
|
||||
@echo.handle()
|
||||
async def echo_escape(bot: Bot, event: MessageEvent):
|
||||
await bot.send(message=event.get_message(), event=event)
|
||||
async def echo_escape(event: MessageEvent):
|
||||
await echo.send(message=event.get_message())
|
||||
|
@ -1,8 +1,6 @@
|
||||
from typing import Dict, Optional
|
||||
from typing import Dict
|
||||
|
||||
from nonebot.typing import T_State
|
||||
from nonebot.matcher import Matcher
|
||||
from nonebot.adapters import Bot, Event
|
||||
from nonebot.adapters import Event
|
||||
from nonebot.message import (IgnoredException, run_preprocessor,
|
||||
run_postprocessor)
|
||||
|
||||
@ -10,7 +8,7 @@ _running_matcher: Dict[str, int] = {}
|
||||
|
||||
|
||||
@run_preprocessor
|
||||
async def preprocess(matcher: Matcher, bot: Bot, event: Event, state: T_State):
|
||||
async def preprocess(event: Event):
|
||||
try:
|
||||
session_id = event.get_session_id()
|
||||
except Exception:
|
||||
@ -24,8 +22,7 @@ async def preprocess(matcher: Matcher, bot: Bot, event: Event, state: T_State):
|
||||
|
||||
|
||||
@run_postprocessor
|
||||
async def postprocess(matcher: Matcher, exception: Optional[Exception],
|
||||
bot: Bot, event: Event, state: T_State):
|
||||
async def postprocess(event: Event):
|
||||
try:
|
||||
session_id = event.get_session_id()
|
||||
except Exception:
|
||||
|
@ -1 +0,0 @@
|
||||
|
183
nonebot/rule.py
183
nonebot/rule.py
@ -2,10 +2,10 @@ r"""
|
||||
规则
|
||||
====
|
||||
|
||||
每个事件响应器 ``Matcher`` 拥有一个匹配规则 ``Rule`` ,其中是 **异步** ``RuleChecker`` 的集合,只有当所有 ``RuleChecker`` 检查结果为 ``True`` 时继续运行。
|
||||
每个事件响应器 ``Matcher`` 拥有一个匹配规则 ``Rule`` ,其中是 ``RuleChecker`` 的集合,只有当所有 ``RuleChecker`` 检查结果为 ``True`` 时继续运行。
|
||||
|
||||
\:\:\:tip 提示
|
||||
``RuleChecker`` 既可以是 async function 也可以是 sync function,但在最终会被 ``nonebot.utils.run_sync`` 转换为 async function
|
||||
``RuleChecker`` 既可以是 async function 也可以是 sync function
|
||||
\:\:\:
|
||||
"""
|
||||
|
||||
@ -14,20 +14,36 @@ import shlex
|
||||
import asyncio
|
||||
from itertools import product
|
||||
from argparse import Namespace
|
||||
from contextlib import AsyncExitStack
|
||||
from typing_extensions import TypedDict
|
||||
from argparse import ArgumentParser as ArgParser
|
||||
from typing import (TYPE_CHECKING, Any, Dict, Tuple, Union, Callable, NoReturn,
|
||||
Optional, Sequence, Awaitable)
|
||||
from typing import (Any, Dict, List, Type, Tuple, Union, Callable, NoReturn,
|
||||
Optional, Sequence)
|
||||
|
||||
from pygtrie import CharTrie
|
||||
|
||||
from nonebot import get_driver
|
||||
from nonebot.log import logger
|
||||
from nonebot.utils import run_sync
|
||||
from nonebot.handler import Handler
|
||||
from nonebot import params, get_driver
|
||||
from nonebot.exception import ParserExit
|
||||
from nonebot.typing import T_State, T_RuleChecker
|
||||
from nonebot.adapters import Bot, Event, MessageSegment
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from nonebot.adapters import Bot, Event
|
||||
PREFIX_KEY = "_prefix"
|
||||
SUFFIX_KEY = "_suffix"
|
||||
CMD_KEY = "command"
|
||||
RAW_CMD_KEY = "raw_command"
|
||||
CMD_RESULT = TypedDict("CMD_RESULT", {
|
||||
"command": Optional[Tuple[str, ...]],
|
||||
"raw_command": Optional[str]
|
||||
})
|
||||
|
||||
SHELL_ARGS = "_args"
|
||||
SHELL_ARGV = "_argv"
|
||||
|
||||
REGEX_MATCHED = "_matched"
|
||||
REGEX_GROUP = "_matched_groups"
|
||||
REGEX_DICT = "_matched_dict"
|
||||
|
||||
|
||||
class Rule:
|
||||
@ -47,16 +63,22 @@ class Rule:
|
||||
"""
|
||||
__slots__ = ("checkers",)
|
||||
|
||||
def __init__(
|
||||
self, *checkers: Callable[["Bot", "Event", T_State],
|
||||
Awaitable[bool]]) -> None:
|
||||
HANDLER_PARAM_TYPES = [
|
||||
params.BotParam, params.EventParam, params.StateParam,
|
||||
params.DefaultParam
|
||||
]
|
||||
|
||||
def __init__(self, *checkers: Union[T_RuleChecker, Handler]) -> None:
|
||||
"""
|
||||
:参数:
|
||||
|
||||
* ``*checkers: Callable[[Bot, Event, T_State], Awaitable[bool]]``: **异步** RuleChecker
|
||||
* ``*checkers: Union[T_RuleChecker, Handler]``: RuleChecker
|
||||
|
||||
"""
|
||||
self.checkers = set(checkers)
|
||||
self.checkers = set(
|
||||
checker if isinstance(checker, Handler) else Handler(
|
||||
checker, allow_types=self.HANDLER_PARAM_TYPES)
|
||||
for checker in checkers)
|
||||
"""
|
||||
:说明:
|
||||
|
||||
@ -64,11 +86,17 @@ class Rule:
|
||||
|
||||
:类型:
|
||||
|
||||
* ``Set[Callable[[Bot, Event, T_State], Awaitable[bool]]]``
|
||||
* ``Set[Handler]``
|
||||
"""
|
||||
|
||||
async def __call__(self, bot: "Bot", event: "Event",
|
||||
state: T_State) -> bool:
|
||||
async def __call__(
|
||||
self,
|
||||
bot: Bot,
|
||||
event: Event,
|
||||
state: T_State,
|
||||
stack: Optional[AsyncExitStack] = None,
|
||||
dependency_cache: Optional[Dict[Callable[..., Any],
|
||||
Any]] = None) -> bool:
|
||||
"""
|
||||
:说明:
|
||||
|
||||
@ -79,26 +107,31 @@ class Rule:
|
||||
* ``bot: Bot``: Bot 对象
|
||||
* ``event: Event``: Event 对象
|
||||
* ``state: T_State``: 当前 State
|
||||
* ``stack: Optional[AsyncExitStack]``: 异步上下文栈
|
||||
* ``dependency_cache: Optional[Dict[Callable[..., Any], Any]]``: 依赖缓存
|
||||
|
||||
:返回:
|
||||
|
||||
- ``bool``
|
||||
"""
|
||||
if not self.checkers:
|
||||
return True
|
||||
results = await asyncio.gather(
|
||||
*map(lambda c: c(bot, event, state), self.checkers))
|
||||
*(checker(bot=bot,
|
||||
event=event,
|
||||
state=state,
|
||||
_stack=stack,
|
||||
_dependency_cache=dependency_cache)
|
||||
for checker in self.checkers))
|
||||
return all(results)
|
||||
|
||||
def __and__(self, other: Optional[Union["Rule", T_RuleChecker]]) -> "Rule":
|
||||
checkers = self.checkers.copy()
|
||||
if other is None:
|
||||
return self
|
||||
elif isinstance(other, Rule):
|
||||
checkers |= other.checkers
|
||||
elif asyncio.iscoroutinefunction(other):
|
||||
checkers.add(other) # type: ignore
|
||||
return Rule(*self.checkers, *other.checkers)
|
||||
else:
|
||||
checkers.add(run_sync(other))
|
||||
return Rule(*checkers)
|
||||
return Rule(*self.checkers, other)
|
||||
|
||||
def __or__(self, other) -> NoReturn:
|
||||
raise RuntimeError("Or operation between rules is not allowed.")
|
||||
@ -123,58 +156,28 @@ class TrieRule:
|
||||
cls.suffix[suffix[::-1]] = value
|
||||
|
||||
@classmethod
|
||||
def get_value(cls, bot: "Bot", event: "Event",
|
||||
state: T_State) -> Tuple[Dict[str, Any], Dict[str, Any]]:
|
||||
def get_value(cls, bot: Bot, event: Event,
|
||||
state: T_State) -> Tuple[CMD_RESULT, CMD_RESULT]:
|
||||
prefix = CMD_RESULT(command=None, raw_command=None)
|
||||
suffix = CMD_RESULT(command=None, raw_command=None)
|
||||
state[PREFIX_KEY] = prefix
|
||||
state[SUFFIX_KEY] = suffix
|
||||
if event.get_type() != "message":
|
||||
state["_prefix"] = {"raw_command": None, "command": None}
|
||||
state["_suffix"] = {"raw_command": None, "command": None}
|
||||
return {
|
||||
"raw_command": None,
|
||||
"command": None
|
||||
}, {
|
||||
"raw_command": None,
|
||||
"command": None
|
||||
}
|
||||
return prefix, suffix
|
||||
|
||||
prefix = None
|
||||
suffix = None
|
||||
message = event.get_message()
|
||||
message_seg = message[0]
|
||||
message_seg: MessageSegment = message[0]
|
||||
if message_seg.is_text():
|
||||
prefix = cls.prefix.longest_prefix(str(message_seg).lstrip())
|
||||
message_seg_r = message[-1]
|
||||
pf = cls.prefix.longest_prefix(str(message_seg).lstrip())
|
||||
prefix[RAW_CMD_KEY] = pf.key
|
||||
prefix[CMD_KEY] = pf.value
|
||||
message_seg_r: MessageSegment = message[-1]
|
||||
if message_seg_r.is_text():
|
||||
suffix = cls.suffix.longest_prefix(
|
||||
str(message_seg_r).rstrip()[::-1])
|
||||
sf = cls.suffix.longest_prefix(str(message_seg_r).rstrip()[::-1])
|
||||
suffix[RAW_CMD_KEY] = sf.key
|
||||
suffix[CMD_KEY] = sf.value
|
||||
|
||||
state["_prefix"] = {
|
||||
"raw_command": prefix.key,
|
||||
"command": prefix.value
|
||||
} if prefix else {
|
||||
"raw_command": None,
|
||||
"command": None
|
||||
}
|
||||
state["_suffix"] = {
|
||||
"raw_command": suffix.key,
|
||||
"command": suffix.value
|
||||
} if suffix else {
|
||||
"raw_command": None,
|
||||
"command": None
|
||||
}
|
||||
|
||||
return ({
|
||||
"raw_command": prefix.key,
|
||||
"command": prefix.value
|
||||
} if prefix else {
|
||||
"raw_command": None,
|
||||
"command": None
|
||||
}, {
|
||||
"raw_command": suffix.key,
|
||||
"command": suffix.value
|
||||
} if suffix else {
|
||||
"raw_command": None,
|
||||
"command": None
|
||||
})
|
||||
return prefix, suffix
|
||||
|
||||
|
||||
def startswith(msg: Union[str, Tuple[str, ...]],
|
||||
@ -195,7 +198,7 @@ def startswith(msg: Union[str, Tuple[str, ...]],
|
||||
f"^(?:{'|'.join(re.escape(prefix) for prefix in msg)})",
|
||||
re.IGNORECASE if ignorecase else 0)
|
||||
|
||||
async def _startswith(bot: "Bot", event: "Event", state: T_State) -> bool:
|
||||
async def _startswith(bot: Bot, event: Event, state: T_State) -> bool:
|
||||
if event.get_type() != "message":
|
||||
return False
|
||||
text = event.get_plaintext()
|
||||
@ -222,7 +225,7 @@ def endswith(msg: Union[str, Tuple[str, ...]],
|
||||
f"(?:{'|'.join(re.escape(prefix) for prefix in msg)})$",
|
||||
re.IGNORECASE if ignorecase else 0)
|
||||
|
||||
async def _endswith(bot: "Bot", event: "Event", state: T_State) -> bool:
|
||||
async def _endswith(bot: Bot, event: Event, state: T_State) -> bool:
|
||||
if event.get_type() != "message":
|
||||
return False
|
||||
text = event.get_plaintext()
|
||||
@ -242,7 +245,7 @@ def keyword(*keywords: str) -> Rule:
|
||||
* ``*keywords: str``: 关键词
|
||||
"""
|
||||
|
||||
async def _keyword(bot: "Bot", event: "Event", state: T_State) -> bool:
|
||||
async def _keyword(event: Event) -> bool:
|
||||
if event.get_type() != "message":
|
||||
return False
|
||||
text = event.get_plaintext()
|
||||
@ -290,8 +293,8 @@ 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: T_State) -> bool:
|
||||
return state["_prefix"]["command"] in commands
|
||||
async def _command(state: T_State) -> bool:
|
||||
return state[PREFIX_KEY][CMD_KEY] in commands
|
||||
|
||||
return Rule(_command)
|
||||
|
||||
@ -310,7 +313,7 @@ class ArgumentParser(ArgParser):
|
||||
old_message += message
|
||||
setattr(self, "message", old_message)
|
||||
|
||||
def exit(self, status=0, message=None):
|
||||
def exit(self, status: int = 0, message: Optional[str] = None):
|
||||
raise ParserExit(status=status,
|
||||
message=message or getattr(self, "message", None))
|
||||
|
||||
@ -376,19 +379,18 @@ def shell_command(*cmds: Union[str, Tuple[str, ...]],
|
||||
for start, sep in product(command_start, command_sep):
|
||||
TrieRule.add_prefix(f"{start}{sep.join(command)}", command)
|
||||
|
||||
async def _shell_command(bot: "Bot", event: "Event",
|
||||
state: T_State) -> bool:
|
||||
if state["_prefix"]["command"] in commands:
|
||||
async def _shell_command(event: Event, state: T_State) -> bool:
|
||||
if state[PREFIX_KEY][CMD_KEY] in commands:
|
||||
message = str(event.get_message())
|
||||
strip_message = message[len(state["_prefix"]["raw_command"]
|
||||
strip_message = message[len(state[PREFIX_KEY][RAW_CMD_KEY]
|
||||
):].lstrip()
|
||||
state["argv"] = shlex.split(strip_message)
|
||||
state[SHELL_ARGV] = shlex.split(strip_message)
|
||||
if parser:
|
||||
try:
|
||||
args = parser.parse_args(state["argv"])
|
||||
state["args"] = args
|
||||
args = parser.parse_args(state[SHELL_ARGV])
|
||||
state[SHELL_ARGS] = args
|
||||
except ParserExit as e:
|
||||
state["args"] = e
|
||||
state[SHELL_ARGS] = e
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
@ -417,14 +419,14 @@ def regex(regex: str, flags: Union[int, re.RegexFlag] = 0) -> Rule:
|
||||
|
||||
pattern = re.compile(regex, flags)
|
||||
|
||||
async def _regex(bot: "Bot", event: "Event", state: T_State) -> bool:
|
||||
async def _regex(event: Event, state: T_State) -> bool:
|
||||
if event.get_type() != "message":
|
||||
return False
|
||||
matched = pattern.search(str(event.get_message()))
|
||||
if matched:
|
||||
state["_matched"] = matched.group()
|
||||
state["_matched_groups"] = matched.groups()
|
||||
state["_matched_dict"] = matched.groupdict()
|
||||
state[REGEX_MATCHED] = matched.group()
|
||||
state[REGEX_GROUP] = matched.groups()
|
||||
state[REGEX_DICT] = matched.groupdict()
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
@ -432,6 +434,10 @@ def regex(regex: str, flags: Union[int, re.RegexFlag] = 0) -> Rule:
|
||||
return Rule(_regex)
|
||||
|
||||
|
||||
async def _to_me(event: Event) -> bool:
|
||||
return event.is_tome()
|
||||
|
||||
|
||||
def to_me() -> Rule:
|
||||
"""
|
||||
:说明:
|
||||
@ -443,7 +449,4 @@ def to_me() -> Rule:
|
||||
* 无
|
||||
"""
|
||||
|
||||
async def _to_me(bot: "Bot", event: "Event", state: T_State) -> bool:
|
||||
return event.is_tome()
|
||||
|
||||
return Rule(_to_me)
|
||||
|
@ -17,16 +17,15 @@
|
||||
.. _typing:
|
||||
https://docs.python.org/3/library/typing.html
|
||||
"""
|
||||
from collections.abc import Callable as BaseCallable
|
||||
|
||||
from typing import (TYPE_CHECKING, Any, Dict, Union, TypeVar, Callable,
|
||||
NoReturn, Optional, Awaitable)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from nonebot.matcher import Matcher
|
||||
from nonebot.adapters import Bot, Event
|
||||
from nonebot.permission import Permission
|
||||
|
||||
T_Wrapped = TypeVar("T_Wrapped", bound=BaseCallable)
|
||||
T_Wrapped = TypeVar("T_Wrapped", bound=Callable)
|
||||
|
||||
|
||||
def overrides(InterfaceClass: object):
|
||||
@ -90,77 +89,109 @@ T_CalledAPIHook = Callable[
|
||||
``bot.call_api`` 后执行的函数,参数分别为 bot, exception, api, data, result
|
||||
"""
|
||||
|
||||
T_EventPreProcessor = Callable[["Bot", "Event", T_State], Awaitable[None]]
|
||||
T_EventPreProcessor = Callable[..., Union[None, Awaitable[None]]]
|
||||
"""
|
||||
:类型: ``Callable[[Bot, Event, T_State], Awaitable[None]]``
|
||||
:类型: ``Callable[..., Union[None, Awaitable[None]]]``
|
||||
|
||||
:依赖参数:
|
||||
|
||||
* ``BotParam``: Bot 对象
|
||||
* ``EventParam``: Event 对象
|
||||
* ``StateParam``: State 对象
|
||||
|
||||
:说明:
|
||||
|
||||
事件预处理函数 EventPreProcessor 类型
|
||||
"""
|
||||
T_EventPostProcessor = Callable[["Bot", "Event", T_State], Awaitable[None]]
|
||||
T_EventPostProcessor = Callable[..., Union[None, Awaitable[None]]]
|
||||
"""
|
||||
:类型: ``Callable[[Bot, Event, T_State], Awaitable[None]]``
|
||||
:类型: ``Callable[..., Union[None, Awaitable[None]]]``
|
||||
|
||||
:依赖参数:
|
||||
|
||||
* ``BotParam``: Bot 对象
|
||||
* ``EventParam``: Event 对象
|
||||
* ``StateParam``: State 对象
|
||||
|
||||
:说明:
|
||||
|
||||
事件预处理函数 EventPostProcessor 类型
|
||||
"""
|
||||
T_RunPreProcessor = Callable[["Matcher", "Bot", "Event", T_State],
|
||||
Awaitable[None]]
|
||||
T_RunPreProcessor = Callable[..., Union[None, Awaitable[None]]]
|
||||
"""
|
||||
:类型: ``Callable[[Matcher, Bot, Event, T_State], Awaitable[None]]``
|
||||
:类型: ``Callable[..., Union[None, Awaitable[None]]]``
|
||||
|
||||
:依赖参数:
|
||||
|
||||
* ``BotParam``: Bot 对象
|
||||
* ``EventParam``: Event 对象
|
||||
* ``StateParam``: State 对象
|
||||
* ``MatcherParam``: Matcher 对象
|
||||
|
||||
:说明:
|
||||
|
||||
事件响应器运行前预处理函数 RunPreProcessor 类型
|
||||
"""
|
||||
T_RunPostProcessor = Callable[
|
||||
["Matcher", Optional[Exception], "Bot", "Event", T_State], Awaitable[None]]
|
||||
T_RunPostProcessor = Callable[..., Union[None, Awaitable[None]]]
|
||||
"""
|
||||
:类型: ``Callable[[Matcher, Optional[Exception], Bot, Event, T_State], Awaitable[None]]``
|
||||
:类型: ``Callable[..., Union[None, Awaitable[None]]]``
|
||||
|
||||
:依赖参数:
|
||||
|
||||
* ``BotParam``: Bot 对象
|
||||
* ``EventParam``: Event 对象
|
||||
* ``StateParam``: State 对象
|
||||
* ``MatcherParam``: Matcher 对象
|
||||
* ``ExceptionParam``: 异常对象(可能为 None)
|
||||
|
||||
:说明:
|
||||
|
||||
事件响应器运行前预处理函数 RunPostProcessor 类型,第二个参数为运行时产生的错误(如果存在)
|
||||
"""
|
||||
|
||||
T_RuleChecker = Callable[["Bot", "Event", T_State], Union[bool,
|
||||
Awaitable[bool]]]
|
||||
T_RuleChecker = Callable[..., Union[bool, Awaitable[bool]]]
|
||||
"""
|
||||
:类型: ``Callable[[Bot, Event, T_State], Union[bool, Awaitable[bool]]]``
|
||||
:类型: ``Callable[..., Union[bool, Awaitable[bool]]]``
|
||||
|
||||
:依赖参数:
|
||||
|
||||
* ``BotParam``: Bot 对象
|
||||
* ``EventParam``: Event 对象
|
||||
* ``StateParam``: State 对象
|
||||
|
||||
:说明:
|
||||
|
||||
RuleChecker 即判断是否响应事件的处理函数。
|
||||
"""
|
||||
T_PermissionChecker = Callable[["Bot", "Event"], Union[bool, Awaitable[bool]]]
|
||||
T_PermissionChecker = Callable[..., Union[bool, Awaitable[bool]]]
|
||||
"""
|
||||
:类型: ``Callable[[Bot, Event], Union[bool, Awaitable[bool]]]``
|
||||
:类型: ``Callable[..., Union[bool, Awaitable[bool]]]``
|
||||
|
||||
:依赖参数:
|
||||
|
||||
* ``BotParam``: Bot 对象
|
||||
* ``EventParam``: Event 对象
|
||||
|
||||
:说明:
|
||||
|
||||
RuleChecker 即判断是否响应消息的处理函数。
|
||||
"""
|
||||
|
||||
T_Handler = Union[Callable[[Any, Any, Any, Any], Union[Awaitable[None],
|
||||
Awaitable[NoReturn]]],
|
||||
Callable[[Any, Any, Any], Union[Awaitable[None],
|
||||
Awaitable[NoReturn]]],
|
||||
Callable[[Any, Any], Union[Awaitable[None],
|
||||
Awaitable[NoReturn]]],
|
||||
Callable[[Any], Union[Awaitable[None], Awaitable[NoReturn]]]]
|
||||
T_Handler = Callable[..., Any]
|
||||
"""
|
||||
:类型:
|
||||
|
||||
* ``Callable[[Bot, Event, T_State], Union[Awaitable[None], Awaitable[NoReturn]]]``
|
||||
* ``Callable[[Bot, Event], Union[Awaitable[None], Awaitable[NoReturn]]]``
|
||||
* ``Callable[[Bot, T_State], Union[Awaitable[None], Awaitable[NoReturn]]]``
|
||||
* ``Callable[[Bot], Union[Awaitable[None], Awaitable[NoReturn]]]``
|
||||
:类型: ``Callable[..., Any]``
|
||||
|
||||
:说明:
|
||||
|
||||
Handler 即事件的处理函数。
|
||||
Handler 处理函数。
|
||||
"""
|
||||
T_DependencyCache = Dict[T_Handler, Any]
|
||||
"""
|
||||
:类型: ``Dict[T_Handler, Any]``
|
||||
|
||||
:说明:
|
||||
|
||||
依赖缓存, 用于存储依赖函数的返回值
|
||||
"""
|
||||
T_ArgsParser = Callable[["Bot", "Event", T_State], Union[Awaitable[None],
|
||||
Awaitable[NoReturn]]]
|
||||
|
153
nonebot/utils.py
153
nonebot/utils.py
@ -1,13 +1,23 @@
|
||||
import re
|
||||
import json
|
||||
import asyncio
|
||||
import inspect
|
||||
import collections
|
||||
import dataclasses
|
||||
from functools import wraps, partial
|
||||
from typing import Any, Callable, Optional, Awaitable
|
||||
from contextlib import asynccontextmanager
|
||||
from typing_extensions import GenericAlias # type: ignore
|
||||
from typing_extensions import ParamSpec, get_args, get_origin
|
||||
from typing import (Any, Type, Deque, Tuple, Union, TypeVar, Callable, Optional,
|
||||
Awaitable, AsyncGenerator, ContextManager)
|
||||
|
||||
from nonebot.log import logger
|
||||
from nonebot.typing import overrides
|
||||
|
||||
P = ParamSpec("P")
|
||||
R = TypeVar("R")
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
def escape_tag(s: str) -> str:
|
||||
"""
|
||||
@ -26,7 +36,48 @@ def escape_tag(s: str) -> str:
|
||||
return re.sub(r"</?((?:[fb]g\s)?[^<>\s]*)>", r"\\\g<0>", s)
|
||||
|
||||
|
||||
def run_sync(func: Callable[..., Any]) -> Callable[..., Awaitable[Any]]:
|
||||
def generic_check_issubclass(
|
||||
cls: Any, class_or_tuple: Union[Type[Any], Tuple[Type[Any],
|
||||
...]]) -> bool:
|
||||
try:
|
||||
return issubclass(cls, class_or_tuple)
|
||||
except TypeError:
|
||||
if get_origin(cls) is Union:
|
||||
for type_ in get_args(cls):
|
||||
if type_ is not type(None) and not generic_check_issubclass(
|
||||
type_, class_or_tuple):
|
||||
return False
|
||||
return True
|
||||
elif isinstance(cls, GenericAlias):
|
||||
origin = get_origin(cls)
|
||||
return bool(origin and issubclass(origin, class_or_tuple))
|
||||
raise
|
||||
|
||||
|
||||
def is_coroutine_callable(func: Callable[..., Any]) -> bool:
|
||||
if inspect.isroutine(func):
|
||||
return inspect.iscoroutinefunction(func)
|
||||
if inspect.isclass(func):
|
||||
return False
|
||||
func_ = getattr(func, "__call__", None)
|
||||
return inspect.iscoroutinefunction(func_)
|
||||
|
||||
|
||||
def is_gen_callable(func: Callable[..., Any]) -> bool:
|
||||
if inspect.isgeneratorfunction(func):
|
||||
return True
|
||||
func_ = getattr(func, "__call__", None)
|
||||
return inspect.isgeneratorfunction(func_)
|
||||
|
||||
|
||||
def is_async_gen_callable(func: Callable[..., Any]) -> bool:
|
||||
if inspect.isasyncgenfunction(func):
|
||||
return True
|
||||
func_ = getattr(func, "__call__", None)
|
||||
return inspect.isasyncgenfunction(func_)
|
||||
|
||||
|
||||
def run_sync(func: Callable[P, R]) -> Callable[P, Awaitable[R]]:
|
||||
"""
|
||||
:说明:
|
||||
|
||||
@ -34,15 +85,15 @@ def run_sync(func: Callable[..., Any]) -> Callable[..., Awaitable[Any]]:
|
||||
|
||||
:参数:
|
||||
|
||||
* ``func: Callable[..., Any]``: 被装饰的同步函数
|
||||
* ``func: Callable[P, R]``: 被装饰的同步函数
|
||||
|
||||
:返回:
|
||||
|
||||
- ``Callable[..., Awaitable[Any]]``
|
||||
- ``Callable[P, Awaitable[R]]``
|
||||
"""
|
||||
|
||||
@wraps(func)
|
||||
async def _wrapper(*args: Any, **kwargs: Any) -> Any:
|
||||
async def _wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
|
||||
loop = asyncio.get_running_loop()
|
||||
pfunc = partial(func, *args, **kwargs)
|
||||
result = await loop.run_in_executor(None, pfunc)
|
||||
@ -51,6 +102,98 @@ def run_sync(func: Callable[..., Any]) -> Callable[..., Awaitable[Any]]:
|
||||
return _wrapper
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
async def run_sync_ctx_manager(
|
||||
cm: ContextManager[T],) -> AsyncGenerator[T, None]:
|
||||
try:
|
||||
yield await run_sync(cm.__enter__)()
|
||||
except Exception as e:
|
||||
ok = await run_sync(cm.__exit__)(type(e), e, None)
|
||||
if not ok:
|
||||
raise e
|
||||
else:
|
||||
await run_sync(cm.__exit__)(None, None, None)
|
||||
|
||||
|
||||
def get_name(obj: Any) -> str:
|
||||
if inspect.isfunction(obj) or inspect.isclass(obj):
|
||||
return obj.__name__
|
||||
return obj.__class__.__name__
|
||||
|
||||
|
||||
class CacheLock:
|
||||
|
||||
def __init__(self):
|
||||
self._waiters: Optional[Deque[asyncio.Future]] = None
|
||||
self._locked = False
|
||||
|
||||
def __repr__(self):
|
||||
extra = "locked" if self._locked else "unlocked"
|
||||
if self._waiters:
|
||||
extra = f"{extra}, waiters: {len(self._waiters)}"
|
||||
return f"<{self.__class__.__name__} [{extra}]>"
|
||||
|
||||
async def __aenter__(self):
|
||||
await self.acquire()
|
||||
return None
|
||||
|
||||
async def __aexit__(self, exc_type, exc, tb):
|
||||
self.release()
|
||||
|
||||
def locked(self):
|
||||
return self._locked
|
||||
|
||||
async def acquire(self):
|
||||
if (not self._locked and (self._waiters is None or
|
||||
all(w.cancelled() for w in self._waiters))):
|
||||
self._locked = True
|
||||
return True
|
||||
|
||||
if self._waiters is None:
|
||||
self._waiters = collections.deque()
|
||||
|
||||
loop = asyncio.get_running_loop()
|
||||
future = loop.create_future()
|
||||
self._waiters.append(future)
|
||||
|
||||
# Finally block should be called before the CancelledError
|
||||
# handling as we don't want CancelledError to call
|
||||
# _wake_up_first() and attempt to wake up itself.
|
||||
try:
|
||||
try:
|
||||
await future
|
||||
finally:
|
||||
self._waiters.remove(future)
|
||||
except asyncio.CancelledError:
|
||||
if not self._locked:
|
||||
self._wake_up_first()
|
||||
raise
|
||||
|
||||
self._locked = True
|
||||
return True
|
||||
|
||||
def release(self):
|
||||
if self._locked:
|
||||
self._locked = False
|
||||
self._wake_up_first()
|
||||
else:
|
||||
raise RuntimeError("Lock is not acquired.")
|
||||
|
||||
def _wake_up_first(self):
|
||||
if not self._waiters:
|
||||
return
|
||||
try:
|
||||
future = next(iter(self._waiters))
|
||||
except StopIteration:
|
||||
return
|
||||
|
||||
# .done() necessarily means that a waiter will wake up later on and
|
||||
# either take the lock, or, if it was cancelled and lock wasn't
|
||||
# taken already, will hit this again and wake up a new waiter.
|
||||
if not future.done():
|
||||
future.set_result(True)
|
||||
|
||||
|
||||
class DataclassEncoder(json.JSONEncoder):
|
||||
"""
|
||||
:说明:
|
||||
|
@ -5,13 +5,12 @@ from typing import TYPE_CHECKING, List, Type, Optional
|
||||
from pydantic import BaseModel
|
||||
from pygtrie import StringTrie
|
||||
|
||||
from .message import Message
|
||||
from nonebot.typing import overrides
|
||||
from nonebot.utils import escape_tag
|
||||
from nonebot.exception import NoLogException
|
||||
from .exception import NoLogException
|
||||
from nonebot.adapters import Event as BaseEvent
|
||||
|
||||
from .message import Message
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .bot import Bot
|
||||
|
||||
|
@ -1,9 +1,10 @@
|
||||
from typing import Optional
|
||||
|
||||
from nonebot.exception import (AdapterException, ActionFailed as
|
||||
BaseActionFailed, NetworkError as
|
||||
BaseNetworkError, ApiNotAvailable as
|
||||
BaseApiNotAvailable)
|
||||
from nonebot.exception import AdapterException
|
||||
from nonebot.exception import ActionFailed as BaseActionFailed
|
||||
from nonebot.exception import NetworkError as BaseNetworkError
|
||||
from nonebot.exception import NoLogException as BaseNoLogException
|
||||
from nonebot.exception import ApiNotAvailable as BaseApiNotAvailable
|
||||
|
||||
|
||||
class CQHTTPAdapterException(AdapterException):
|
||||
@ -12,6 +13,10 @@ class CQHTTPAdapterException(AdapterException):
|
||||
super().__init__("cqhttp")
|
||||
|
||||
|
||||
class NoLogException(BaseNoLogException, CQHTTPAdapterException):
|
||||
pass
|
||||
|
||||
|
||||
class ActionFailed(BaseActionFailed, CQHTTPAdapterException):
|
||||
"""
|
||||
:说明:
|
||||
|
@ -1,26 +1,21 @@
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from nonebot.adapters import Event
|
||||
from nonebot.permission import Permission
|
||||
|
||||
from .event import PrivateMessageEvent, GroupMessageEvent
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from nonebot.adapters import Bot, Event
|
||||
from .event import GroupMessageEvent, PrivateMessageEvent
|
||||
|
||||
|
||||
async def _private(bot: "Bot", event: "Event") -> bool:
|
||||
async def _private(event: Event) -> bool:
|
||||
return isinstance(event, PrivateMessageEvent)
|
||||
|
||||
|
||||
async def _private_friend(bot: "Bot", event: "Event") -> bool:
|
||||
async def _private_friend(event: Event) -> bool:
|
||||
return isinstance(event, PrivateMessageEvent) and event.sub_type == "friend"
|
||||
|
||||
|
||||
async def _private_group(bot: "Bot", event: "Event") -> bool:
|
||||
async def _private_group(event: Event) -> bool:
|
||||
return isinstance(event, PrivateMessageEvent) and event.sub_type == "group"
|
||||
|
||||
|
||||
async def _private_other(bot: "Bot", event: "Event") -> bool:
|
||||
async def _private_other(event: Event) -> bool:
|
||||
return isinstance(event, PrivateMessageEvent) and event.sub_type == "other"
|
||||
|
||||
|
||||
@ -42,20 +37,20 @@ PRIVATE_OTHER = Permission(_private_other)
|
||||
"""
|
||||
|
||||
|
||||
async def _group(bot: "Bot", event: "Event") -> bool:
|
||||
async def _group(event: Event) -> bool:
|
||||
return isinstance(event, GroupMessageEvent)
|
||||
|
||||
|
||||
async def _group_member(bot: "Bot", event: "Event") -> bool:
|
||||
async def _group_member(event: Event) -> bool:
|
||||
return isinstance(event,
|
||||
GroupMessageEvent) and event.sender.role == "member"
|
||||
|
||||
|
||||
async def _group_admin(bot: "Bot", event: "Event") -> bool:
|
||||
async def _group_admin(event: Event) -> bool:
|
||||
return isinstance(event, GroupMessageEvent) and event.sender.role == "admin"
|
||||
|
||||
|
||||
async def _group_owner(bot: "Bot", event: "Event") -> bool:
|
||||
async def _group_owner(event: Event) -> bool:
|
||||
return isinstance(event, GroupMessageEvent) and event.sender.role == "owner"
|
||||
|
||||
|
||||
|
@ -4,11 +4,13 @@ sidebar: auto
|
||||
|
||||
# 更新日志
|
||||
|
||||
## v2.0.0a17
|
||||
## v2.0.0b1
|
||||
|
||||
- 新增 `MessageTemplate` 对于 `str` 普通模板的支持
|
||||
- 移除插件加载的 `NameSpace` 模式
|
||||
- 修改 toml 加载插件时的键名为 `tool.nonebot` 以符合规范
|
||||
- 新增 Handler 依赖注入支持
|
||||
- 统一 `Processor`, `Rule`, `Permission` 使用 `Handler`
|
||||
|
||||
## v2.0.0a16
|
||||
|
||||
|
257
poetry.lock
generated
257
poetry.lock
generated
@ -33,7 +33,7 @@ python-versions = ">=3.6,<4.0"
|
||||
|
||||
[[package]]
|
||||
name = "aiohttp"
|
||||
version = "3.8.0"
|
||||
version = "3.8.1"
|
||||
description = "Async http client/server framework (asyncio)"
|
||||
category = "main"
|
||||
optional = true
|
||||
@ -297,7 +297,7 @@ python-versions = ">=3.5"
|
||||
|
||||
[[package]]
|
||||
name = "httpcore"
|
||||
version = "0.13.7"
|
||||
version = "0.14.2"
|
||||
description = "A minimal low-level HTTP client."
|
||||
category = "main"
|
||||
optional = false
|
||||
@ -305,6 +305,7 @@ python-versions = ">=3.6"
|
||||
|
||||
[package.dependencies]
|
||||
anyio = ">=3.0.0,<4.0.0"
|
||||
certifi = "*"
|
||||
h11 = ">=0.11,<0.13"
|
||||
sniffio = ">=1.0.0,<2.0.0"
|
||||
|
||||
@ -324,7 +325,7 @@ test = ["Cython (==0.29.22)"]
|
||||
|
||||
[[package]]
|
||||
name = "httpx"
|
||||
version = "0.20.0"
|
||||
version = "0.21.1"
|
||||
description = "The next generation HTTP client."
|
||||
category = "main"
|
||||
optional = false
|
||||
@ -334,7 +335,7 @@ python-versions = ">=3.6"
|
||||
certifi = "*"
|
||||
charset-normalizer = "*"
|
||||
h2 = {version = ">=3,<5", optional = true, markers = "extra == \"http2\""}
|
||||
httpcore = ">=0.13.3,<0.14.0"
|
||||
httpcore = ">=0.14.0,<0.15.0"
|
||||
rfc3986 = {version = ">=1.3,<2", extras = ["idna2008"]}
|
||||
sniffio = "*"
|
||||
|
||||
@ -771,7 +772,7 @@ python-versions = ">=3.5"
|
||||
|
||||
[[package]]
|
||||
name = "snowballstemmer"
|
||||
version = "2.1.0"
|
||||
version = "2.2.0"
|
||||
description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms."
|
||||
category = "dev"
|
||||
optional = false
|
||||
@ -828,7 +829,7 @@ yapf = "*"
|
||||
type = "git"
|
||||
url = "https://github.com/nonebot/sphinx-markdown-builder.git"
|
||||
reference = "master"
|
||||
resolved_reference = "7a8c8a66dfe42436b4584d1d13f5d0127fc83301"
|
||||
resolved_reference = "2204923f5938a8f7354c6a69ed58079edd180a43"
|
||||
|
||||
[[package]]
|
||||
name = "sphinxcontrib-applehelp"
|
||||
@ -934,11 +935,11 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
|
||||
[[package]]
|
||||
name = "typing-extensions"
|
||||
version = "3.10.0.2"
|
||||
description = "Backported and Experimental Type Hints for Python 3.5+"
|
||||
version = "4.0.0"
|
||||
description = "Backported and Experimental Type Hints for Python 3.6+"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[[package]]
|
||||
name = "unify"
|
||||
@ -1019,7 +1020,7 @@ python-versions = ">=3.5"
|
||||
|
||||
[[package]]
|
||||
name = "websockets"
|
||||
version = "10.0"
|
||||
version = "10.1"
|
||||
description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)"
|
||||
category = "main"
|
||||
optional = false
|
||||
@ -1099,7 +1100,7 @@ quart = ["Quart"]
|
||||
[metadata]
|
||||
lock-version = "1.1"
|
||||
python-versions = "^3.7.3"
|
||||
content-hash = "51f4f0ce5ced234a65cae790c4f57486e42d7120972657a3f51e733cb4e7c639"
|
||||
content-hash = "537c91f98fd6598dbce8c2942530f18dee0858a896b6f393a684252a77dc76c6"
|
||||
|
||||
[metadata.files]
|
||||
aiocache = [
|
||||
@ -1115,78 +1116,78 @@ aiofiles = [
|
||||
{file = "aiofiles-0.7.0.tar.gz", hash = "sha256:a1c4fc9b2ff81568c83e21392a82f344ea9d23da906e4f6a52662764545e19d4"},
|
||||
]
|
||||
aiohttp = [
|
||||
{file = "aiohttp-3.8.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:48f218a5257b6bc16bcf26a91d97ecea0c7d29c811a90d965f3dd97c20f016d6"},
|
||||
{file = "aiohttp-3.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a2fee4d656a7cc9ab47771b2a9e8fad8a9a33331c1b59c3057ecf0ac858f5bfe"},
|
||||
{file = "aiohttp-3.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:688a1eb8c1a5f7e795c7cb67e0fe600194e6723ba35f138dfae0db20c0cb8f94"},
|
||||
{file = "aiohttp-3.8.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ba09bb3dcb0b7ec936a485db2b64be44fe14cdce0a5eac56f50e55da3627385"},
|
||||
{file = "aiohttp-3.8.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d7715daf84f10bcebc083ad137e3eced3e1c8e7fa1f096ade9a8d02b08f0d91c"},
|
||||
{file = "aiohttp-3.8.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5e3f81fbbc170418e22918a9585fd7281bbc11d027064d62aa4b507552c92671"},
|
||||
{file = "aiohttp-3.8.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1fa9f50aa1f114249b7963c98e20dc35c51be64096a85bc92433185f331de9cc"},
|
||||
{file = "aiohttp-3.8.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8a50150419b741ee048b53146c39c47053f060cb9d98e78be08fdbe942eaa3c4"},
|
||||
{file = "aiohttp-3.8.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:a84c335337b676d832c1e2bc47c3a97531b46b82de9f959dafb315cbcbe0dfcd"},
|
||||
{file = "aiohttp-3.8.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:88d4917c30fcd7f6404fb1dc713fa21de59d3063dcc048f4a8a1a90e6bbbd739"},
|
||||
{file = "aiohttp-3.8.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:b76669b7c058b8020b11008283c3b8e9c61bfd978807c45862956119b77ece45"},
|
||||
{file = "aiohttp-3.8.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:84fe1732648c1bc303a70faa67cbc2f7f2e810c8a5bca94f6db7818e722e4c0a"},
|
||||
{file = "aiohttp-3.8.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:730b7c2b7382194d9985ffdc32ab317e893bca21e0665cb1186bdfbb4089d990"},
|
||||
{file = "aiohttp-3.8.0-cp310-cp310-win32.whl", hash = "sha256:0a96473a1f61d7920a9099bc8e729dc8282539d25f79c12573ee0fdb9c8b66a8"},
|
||||
{file = "aiohttp-3.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:764c7c6aa1f78bd77bd9674fc07d1ec44654da1818d0eef9fb48aa8371a3c847"},
|
||||
{file = "aiohttp-3.8.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:9951c2696c4357703001e1fe6edc6ae8e97553ac630492ea1bf64b429cb712a3"},
|
||||
{file = "aiohttp-3.8.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0af379221975054162959e00daf21159ff69a712fc42ed0052caddbd70d52ff4"},
|
||||
{file = "aiohttp-3.8.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9689af0f0a89e5032426c143fa3683b0451f06c83bf3b1e27902bd33acfae769"},
|
||||
{file = "aiohttp-3.8.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe4a327da0c6b6e59f2e474ae79d6ee7745ac3279fd15f200044602fa31e3d79"},
|
||||
{file = "aiohttp-3.8.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:ecb314e59bedb77188017f26e6b684b1f6d0465e724c3122a726359fa62ca1ba"},
|
||||
{file = "aiohttp-3.8.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a5399a44a529083951b55521cf4ecbf6ad79fd54b9df57dbf01699ffa0549fc9"},
|
||||
{file = "aiohttp-3.8.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:09754a0d5eaab66c37591f2f8fac8f9781a5f61d51aa852a3261c4805ca6b984"},
|
||||
{file = "aiohttp-3.8.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:adf0cb251b1b842c9dee5cfcdf880ba0aae32e841b8d0e6b6feeaef002a267c5"},
|
||||
{file = "aiohttp-3.8.0-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:a4759e85a191de58e0ea468ab6fd9c03941986eee436e0518d7a9291fab122c8"},
|
||||
{file = "aiohttp-3.8.0-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:28369fe331a59d80393ec82df3d43307c7461bfaf9217999e33e2acc7984ff7c"},
|
||||
{file = "aiohttp-3.8.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:2f44d1b1c740a9e2275160d77c73a11f61e8a916191c572876baa7b282bcc934"},
|
||||
{file = "aiohttp-3.8.0-cp36-cp36m-win32.whl", hash = "sha256:e27cde1e8d17b09730801ce97b6e0c444ba2a1f06348b169fd931b51d3402f0d"},
|
||||
{file = "aiohttp-3.8.0-cp36-cp36m-win_amd64.whl", hash = "sha256:15a660d06092b7c92ed17c1dbe6c1eab0a02963992d60e3e8b9d5fa7fa81f01e"},
|
||||
{file = "aiohttp-3.8.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:257f4fad1714d26d562572095c8c5cd271d5a333252795cb7a002dca41fdbad7"},
|
||||
{file = "aiohttp-3.8.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a6074a3b2fa2d0c9bf0963f8dfc85e1e54a26114cc8594126bc52d3fa061c40e"},
|
||||
{file = "aiohttp-3.8.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7a315ceb813208ef32bdd6ec3a85cbe3cb3be9bbda5fd030c234592fa9116993"},
|
||||
{file = "aiohttp-3.8.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9a52b141ff3b923a9166595de6e3768a027546e75052ffba267d95b54267f4ab"},
|
||||
{file = "aiohttp-3.8.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6a038cb1e6e55b26bb5520ccffab7f539b3786f5553af2ee47eb2ec5cbd7084e"},
|
||||
{file = "aiohttp-3.8.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98b1ea2763b33559dd9ec621d67fc17b583484cb90735bfb0ec3614c17b210e4"},
|
||||
{file = "aiohttp-3.8.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:9e8723c3256641e141cd18f6ce478d54a004138b9f1a36e41083b36d9ecc5fc5"},
|
||||
{file = "aiohttp-3.8.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:14a6f026eca80dfa3d52e86be89feb5cd878f6f4a6adb34457e2c689fd85229b"},
|
||||
{file = "aiohttp-3.8.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:c62d4791a8212c885b97a63ef5f3974b2cd41930f0cd224ada9c6ee6654f8150"},
|
||||
{file = "aiohttp-3.8.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:90a97c2ed2830e7974cbe45f0838de0aefc1c123313f7c402e21c29ec063fbb4"},
|
||||
{file = "aiohttp-3.8.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:dcc4d5dd5fba3affaf4fd08f00ef156407573de8c63338787614ccc64f96b321"},
|
||||
{file = "aiohttp-3.8.0-cp37-cp37m-win32.whl", hash = "sha256:de42f513ed7a997bc821bddab356b72e55e8396b1b7ba1bf39926d538a76a90f"},
|
||||
{file = "aiohttp-3.8.0-cp37-cp37m-win_amd64.whl", hash = "sha256:7d76e8a83396e06abe3df569b25bd3fc88bf78b7baa2c8e4cf4aaf5983af66a3"},
|
||||
{file = "aiohttp-3.8.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5d79174d96446a02664e2bffc95e7b6fa93b9e6d8314536c5840dff130d0878b"},
|
||||
{file = "aiohttp-3.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4a6551057a846bf72c7a04f73de3fcaca269c0bd85afe475ceb59d261c6a938c"},
|
||||
{file = "aiohttp-3.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:871d4fdc56288caa58b1094c20f2364215f7400411f76783ea19ad13be7c8e19"},
|
||||
{file = "aiohttp-3.8.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ba08a71caa42eef64357257878fb17f3fba3fba6e81a51d170e32321569e079"},
|
||||
{file = "aiohttp-3.8.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:51f90dabd9933b1621260b32c2f0d05d36923c7a5a909eb823e429dba0fd2f3e"},
|
||||
{file = "aiohttp-3.8.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f348ebd20554e8bc26e8ef3ed8a134110c0f4bf015b3b4da6a4ddf34e0515b19"},
|
||||
{file = "aiohttp-3.8.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:d5f8c04574efa814a24510122810e3a3c77c0552f9f6ff65c9862f1f046be2c3"},
|
||||
{file = "aiohttp-3.8.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5ecffdc748d3b40dd3618ede0170e4f5e1d3c9647cfb410d235d19e62cb54ee0"},
|
||||
{file = "aiohttp-3.8.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:577cc2c7b807b174814dac2d02e673728f2e46c7f90ceda3a70ea4bb6d90b769"},
|
||||
{file = "aiohttp-3.8.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6b79f6c31e68b6dafc0317ec453c83c86dd8db1f8f0c6f28e97186563fca87a0"},
|
||||
{file = "aiohttp-3.8.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:2bdd655732e38b40f8a8344d330cfae3c727fb257585df923316aabbd489ccb8"},
|
||||
{file = "aiohttp-3.8.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:63fa57a0708573d3c059f7b5527617bd0c291e4559298473df238d502e4ab98c"},
|
||||
{file = "aiohttp-3.8.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d3f90ee275b1d7c942e65b5c44c8fb52d55502a0b9a679837d71be2bd8927661"},
|
||||
{file = "aiohttp-3.8.0-cp38-cp38-win32.whl", hash = "sha256:fa818609357dde5c4a94a64c097c6404ad996b1d38ca977a72834b682830a722"},
|
||||
{file = "aiohttp-3.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:097ecf52f6b9859b025c1e36401f8aa4573552e887d1b91b4b999d68d0b5a3b3"},
|
||||
{file = "aiohttp-3.8.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:be03a7483ad9ea60388f930160bb3728467dd0af538aa5edc60962ee700a0bdc"},
|
||||
{file = "aiohttp-3.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:78d51e35ed163783d721b6f2ce8ce3f82fccfe471e8e50a10fba13a766d31f5a"},
|
||||
{file = "aiohttp-3.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:bda75d73e7400e81077b0910c9a60bf9771f715420d7e35fa7739ae95555f195"},
|
||||
{file = "aiohttp-3.8.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:707adc30ea6918fba725c3cb3fe782d271ba352b22d7ae54a7f9f2e8a8488c41"},
|
||||
{file = "aiohttp-3.8.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f58aa995b905ab82fe228acd38538e7dc1509e01508dcf307dad5046399130f"},
|
||||
{file = "aiohttp-3.8.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c996eb91bfbdab1e01e2c02e7ff678c51e2b28e3a04e26e41691991cc55795"},
|
||||
{file = "aiohttp-3.8.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:d6a1a66bb8bac9bc2892c2674ea363486bfb748b86504966a390345a11b1680e"},
|
||||
{file = "aiohttp-3.8.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:dafc01a32b4a1d7d3ef8bfd3699406bb44f7b2e0d3eb8906d574846e1019b12f"},
|
||||
{file = "aiohttp-3.8.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:949a605ef3907254b122f845baa0920407080cdb1f73aa64f8d47df4a7f4c4f9"},
|
||||
{file = "aiohttp-3.8.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:0d7b056fd3972d353cb4bc305c03f9381583766b7f8c7f1c44478dba69099e33"},
|
||||
{file = "aiohttp-3.8.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:6f1d39a744101bf4043fa0926b3ead616607578192d0a169974fb5265ab1e9d2"},
|
||||
{file = "aiohttp-3.8.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:67ca7032dfac8d001023fadafc812d9f48bf8a8c3bb15412d9cdcf92267593f4"},
|
||||
{file = "aiohttp-3.8.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:cb751ef712570d3bda9a73fd765ff3e1aba943ec5d52a54a0c2e89c7eef9da1e"},
|
||||
{file = "aiohttp-3.8.0-cp39-cp39-win32.whl", hash = "sha256:6d3e027fe291b77f6be9630114a0200b2c52004ef20b94dc50ca59849cd623b3"},
|
||||
{file = "aiohttp-3.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:3c5e9981e449d54308c6824f172ec8ab63eb9c5f922920970249efee83f7e919"},
|
||||
{file = "aiohttp-3.8.0.tar.gz", hash = "sha256:d3b19d8d183bcfd68b25beebab8dc3308282fe2ca3d6ea3cb4cd101b3c279f8d"},
|
||||
{file = "aiohttp-3.8.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1ed0b6477896559f17b9eaeb6d38e07f7f9ffe40b9f0f9627ae8b9926ae260a8"},
|
||||
{file = "aiohttp-3.8.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7dadf3c307b31e0e61689cbf9e06be7a867c563d5a63ce9dca578f956609abf8"},
|
||||
{file = "aiohttp-3.8.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a79004bb58748f31ae1cbe9fa891054baaa46fb106c2dc7af9f8e3304dc30316"},
|
||||
{file = "aiohttp-3.8.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:12de6add4038df8f72fac606dff775791a60f113a725c960f2bab01d8b8e6b15"},
|
||||
{file = "aiohttp-3.8.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6f0d5f33feb5f69ddd57a4a4bd3d56c719a141080b445cbf18f238973c5c9923"},
|
||||
{file = "aiohttp-3.8.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eaba923151d9deea315be1f3e2b31cc39a6d1d2f682f942905951f4e40200922"},
|
||||
{file = "aiohttp-3.8.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:099ebd2c37ac74cce10a3527d2b49af80243e2a4fa39e7bce41617fbc35fa3c1"},
|
||||
{file = "aiohttp-3.8.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2e5d962cf7e1d426aa0e528a7e198658cdc8aa4fe87f781d039ad75dcd52c516"},
|
||||
{file = "aiohttp-3.8.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:fa0ffcace9b3aa34d205d8130f7873fcfefcb6a4dd3dd705b0dab69af6712642"},
|
||||
{file = "aiohttp-3.8.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:61bfc23df345d8c9716d03717c2ed5e27374e0fe6f659ea64edcd27b4b044cf7"},
|
||||
{file = "aiohttp-3.8.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:31560d268ff62143e92423ef183680b9829b1b482c011713ae941997921eebc8"},
|
||||
{file = "aiohttp-3.8.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:01d7bdb774a9acc838e6b8f1d114f45303841b89b95984cbb7d80ea41172a9e3"},
|
||||
{file = "aiohttp-3.8.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:97ef77eb6b044134c0b3a96e16abcb05ecce892965a2124c566af0fd60f717e2"},
|
||||
{file = "aiohttp-3.8.1-cp310-cp310-win32.whl", hash = "sha256:c2aef4703f1f2ddc6df17519885dbfa3514929149d3ff900b73f45998f2532fa"},
|
||||
{file = "aiohttp-3.8.1-cp310-cp310-win_amd64.whl", hash = "sha256:713ac174a629d39b7c6a3aa757b337599798da4c1157114a314e4e391cd28e32"},
|
||||
{file = "aiohttp-3.8.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:473d93d4450880fe278696549f2e7aed8cd23708c3c1997981464475f32137db"},
|
||||
{file = "aiohttp-3.8.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99b5eeae8e019e7aad8af8bb314fb908dd2e028b3cdaad87ec05095394cce632"},
|
||||
{file = "aiohttp-3.8.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3af642b43ce56c24d063325dd2cf20ee012d2b9ba4c3c008755a301aaea720ad"},
|
||||
{file = "aiohttp-3.8.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c3630c3ef435c0a7c549ba170a0633a56e92629aeed0e707fec832dee313fb7a"},
|
||||
{file = "aiohttp-3.8.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:4a4a4e30bf1edcad13fb0804300557aedd07a92cabc74382fdd0ba6ca2661091"},
|
||||
{file = "aiohttp-3.8.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6f8b01295e26c68b3a1b90efb7a89029110d3a4139270b24fda961893216c440"},
|
||||
{file = "aiohttp-3.8.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:a25fa703a527158aaf10dafd956f7d42ac6d30ec80e9a70846253dd13e2f067b"},
|
||||
{file = "aiohttp-3.8.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:5bfde62d1d2641a1f5173b8c8c2d96ceb4854f54a44c23102e2ccc7e02f003ec"},
|
||||
{file = "aiohttp-3.8.1-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:51467000f3647d519272392f484126aa716f747859794ac9924a7aafa86cd411"},
|
||||
{file = "aiohttp-3.8.1-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:03a6d5349c9ee8f79ab3ff3694d6ce1cfc3ced1c9d36200cb8f08ba06bd3b782"},
|
||||
{file = "aiohttp-3.8.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:102e487eeb82afac440581e5d7f8f44560b36cf0bdd11abc51a46c1cd88914d4"},
|
||||
{file = "aiohttp-3.8.1-cp36-cp36m-win32.whl", hash = "sha256:4aed991a28ea3ce320dc8ce655875e1e00a11bdd29fe9444dd4f88c30d558602"},
|
||||
{file = "aiohttp-3.8.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b0e20cddbd676ab8a64c774fefa0ad787cc506afd844de95da56060348021e96"},
|
||||
{file = "aiohttp-3.8.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:37951ad2f4a6df6506750a23f7cbabad24c73c65f23f72e95897bb2cecbae676"},
|
||||
{file = "aiohttp-3.8.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c23b1ad869653bc818e972b7a3a79852d0e494e9ab7e1a701a3decc49c20d51"},
|
||||
{file = "aiohttp-3.8.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:15b09b06dae900777833fe7fc4b4aa426556ce95847a3e8d7548e2d19e34edb8"},
|
||||
{file = "aiohttp-3.8.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:477c3ea0ba410b2b56b7efb072c36fa91b1e6fc331761798fa3f28bb224830dd"},
|
||||
{file = "aiohttp-3.8.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2f2f69dca064926e79997f45b2f34e202b320fd3782f17a91941f7eb85502ee2"},
|
||||
{file = "aiohttp-3.8.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ef9612483cb35171d51d9173647eed5d0069eaa2ee812793a75373447d487aa4"},
|
||||
{file = "aiohttp-3.8.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:6d69f36d445c45cda7b3b26afef2fc34ef5ac0cdc75584a87ef307ee3c8c6d00"},
|
||||
{file = "aiohttp-3.8.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:55c3d1072704d27401c92339144d199d9de7b52627f724a949fc7d5fc56d8b93"},
|
||||
{file = "aiohttp-3.8.1-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:b9d00268fcb9f66fbcc7cd9fe423741d90c75ee029a1d15c09b22d23253c0a44"},
|
||||
{file = "aiohttp-3.8.1-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:07b05cd3305e8a73112103c834e91cd27ce5b4bd07850c4b4dbd1877d3f45be7"},
|
||||
{file = "aiohttp-3.8.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c34dc4958b232ef6188c4318cb7b2c2d80521c9a56c52449f8f93ab7bc2a8a1c"},
|
||||
{file = "aiohttp-3.8.1-cp37-cp37m-win32.whl", hash = "sha256:d2f9b69293c33aaa53d923032fe227feac867f81682f002ce33ffae978f0a9a9"},
|
||||
{file = "aiohttp-3.8.1-cp37-cp37m-win_amd64.whl", hash = "sha256:6ae828d3a003f03ae31915c31fa684b9890ea44c9c989056fea96e3d12a9fa17"},
|
||||
{file = "aiohttp-3.8.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0c7ebbbde809ff4e970824b2b6cb7e4222be6b95a296e46c03cf050878fc1785"},
|
||||
{file = "aiohttp-3.8.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8b7ef7cbd4fec9a1e811a5de813311ed4f7ac7d93e0fda233c9b3e1428f7dd7b"},
|
||||
{file = "aiohttp-3.8.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c3d6a4d0619e09dcd61021debf7059955c2004fa29f48788a3dfaf9c9901a7cd"},
|
||||
{file = "aiohttp-3.8.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:718626a174e7e467f0558954f94af117b7d4695d48eb980146016afa4b580b2e"},
|
||||
{file = "aiohttp-3.8.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:589c72667a5febd36f1315aa6e5f56dd4aa4862df295cb51c769d16142ddd7cd"},
|
||||
{file = "aiohttp-3.8.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2ed076098b171573161eb146afcb9129b5ff63308960aeca4b676d9d3c35e700"},
|
||||
{file = "aiohttp-3.8.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:086f92daf51a032d062ec5f58af5ca6a44d082c35299c96376a41cbb33034675"},
|
||||
{file = "aiohttp-3.8.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:11691cf4dc5b94236ccc609b70fec991234e7ef8d4c02dd0c9668d1e486f5abf"},
|
||||
{file = "aiohttp-3.8.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:31d1e1c0dbf19ebccbfd62eff461518dcb1e307b195e93bba60c965a4dcf1ba0"},
|
||||
{file = "aiohttp-3.8.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:11a67c0d562e07067c4e86bffc1553f2cf5b664d6111c894671b2b8712f3aba5"},
|
||||
{file = "aiohttp-3.8.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:bb01ba6b0d3f6c68b89fce7305080145d4877ad3acaed424bae4d4ee75faa950"},
|
||||
{file = "aiohttp-3.8.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:44db35a9e15d6fe5c40d74952e803b1d96e964f683b5a78c3cc64eb177878155"},
|
||||
{file = "aiohttp-3.8.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:844a9b460871ee0a0b0b68a64890dae9c415e513db0f4a7e3cab41a0f2fedf33"},
|
||||
{file = "aiohttp-3.8.1-cp38-cp38-win32.whl", hash = "sha256:7d08744e9bae2ca9c382581f7dce1273fe3c9bae94ff572c3626e8da5b193c6a"},
|
||||
{file = "aiohttp-3.8.1-cp38-cp38-win_amd64.whl", hash = "sha256:04d48b8ce6ab3cf2097b1855e1505181bdd05586ca275f2505514a6e274e8e75"},
|
||||
{file = "aiohttp-3.8.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f5315a2eb0239185af1bddb1abf472d877fede3cc8d143c6cddad37678293237"},
|
||||
{file = "aiohttp-3.8.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a996d01ca39b8dfe77440f3cd600825d05841088fd6bc0144cc6c2ec14cc5f74"},
|
||||
{file = "aiohttp-3.8.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:13487abd2f761d4be7c8ff9080de2671e53fff69711d46de703c310c4c9317ca"},
|
||||
{file = "aiohttp-3.8.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea302f34477fda3f85560a06d9ebdc7fa41e82420e892fc50b577e35fc6a50b2"},
|
||||
{file = "aiohttp-3.8.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a2f635ce61a89c5732537a7896b6319a8fcfa23ba09bec36e1b1ac0ab31270d2"},
|
||||
{file = "aiohttp-3.8.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e999f2d0e12eea01caeecb17b653f3713d758f6dcc770417cf29ef08d3931421"},
|
||||
{file = "aiohttp-3.8.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0770e2806a30e744b4e21c9d73b7bee18a1cfa3c47991ee2e5a65b887c49d5cf"},
|
||||
{file = "aiohttp-3.8.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d15367ce87c8e9e09b0f989bfd72dc641bcd04ba091c68cd305312d00962addd"},
|
||||
{file = "aiohttp-3.8.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:6c7cefb4b0640703eb1069835c02486669312bf2f12b48a748e0a7756d0de33d"},
|
||||
{file = "aiohttp-3.8.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:71927042ed6365a09a98a6377501af5c9f0a4d38083652bcd2281a06a5976724"},
|
||||
{file = "aiohttp-3.8.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:28d490af82bc6b7ce53ff31337a18a10498303fe66f701ab65ef27e143c3b0ef"},
|
||||
{file = "aiohttp-3.8.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:b6613280ccedf24354406caf785db748bebbddcf31408b20c0b48cb86af76866"},
|
||||
{file = "aiohttp-3.8.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:81e3d8c34c623ca4e36c46524a3530e99c0bc95ed068fd6e9b55cb721d408fb2"},
|
||||
{file = "aiohttp-3.8.1-cp39-cp39-win32.whl", hash = "sha256:7187a76598bdb895af0adbd2fb7474d7f6025d170bc0a1130242da817ce9e7d1"},
|
||||
{file = "aiohttp-3.8.1-cp39-cp39-win_amd64.whl", hash = "sha256:1c182cb873bc91b411e184dab7a2b664d4fea2743df0e4d57402f7f3fa644bac"},
|
||||
{file = "aiohttp-3.8.1.tar.gz", hash = "sha256:fc5471e1a54de15ef71c1bc6ebe80d4dc681ea600e68bfd1cbce40427f0b7578"},
|
||||
]
|
||||
aiosignal = [
|
||||
{file = "aiosignal-1.2.0-py3-none-any.whl", hash = "sha256:26e62109036cd181df6e6ad646f91f0dcfd05fe16d0cb924138ff2ab75d64e3a"},
|
||||
@ -1470,8 +1471,8 @@ html2text = [
|
||||
{file = "html2text-2020.1.16.tar.gz", hash = "sha256:e296318e16b059ddb97f7a8a1d6a5c1d7af4544049a01e261731d2d5cc277bbb"},
|
||||
]
|
||||
httpcore = [
|
||||
{file = "httpcore-0.13.7-py3-none-any.whl", hash = "sha256:369aa481b014cf046f7067fddd67d00560f2f00426e79569d99cb11245134af0"},
|
||||
{file = "httpcore-0.13.7.tar.gz", hash = "sha256:036f960468759e633574d7c121afba48af6419615d36ab8ede979f1ad6276fa3"},
|
||||
{file = "httpcore-0.14.2-py3-none-any.whl", hash = "sha256:47d7c8f755719d4a57be0b6e022897e9e963bf9ce4b15b9cc006a38a1cfa2932"},
|
||||
{file = "httpcore-0.14.2.tar.gz", hash = "sha256:ff8f8b9434ec4823f95a30596fbe78039913e706d3e598b0b8955b1e1828e093"},
|
||||
]
|
||||
httptools = [
|
||||
{file = "httptools-0.2.0-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:79dbc21f3612a78b28384e989b21872e2e3cf3968532601544696e4ed0007ce5"},
|
||||
@ -1491,8 +1492,8 @@ httptools = [
|
||||
{file = "httptools-0.2.0.tar.gz", hash = "sha256:94505026be56652d7a530ab03d89474dc6021019d6b8682281977163b3471ea0"},
|
||||
]
|
||||
httpx = [
|
||||
{file = "httpx-0.20.0-py3-none-any.whl", hash = "sha256:33af5aad9bdc82ef1fc89219c1e36f5693bf9cd0ebe330884df563445682c0f8"},
|
||||
{file = "httpx-0.20.0.tar.gz", hash = "sha256:09606d630f070d07f9ff28104fbcea429ea0014c1e89ac90b4d8de8286c40e7b"},
|
||||
{file = "httpx-0.21.1-py3-none-any.whl", hash = "sha256:208e5ef2ad4d105213463cfd541898ed9d11851b346473539a8425e644bb7c66"},
|
||||
{file = "httpx-0.21.1.tar.gz", hash = "sha256:02af20df486b78892a614a7ccd4e4e86a5409ec4981ab0e422c579a887acad83"},
|
||||
]
|
||||
hypercorn = [
|
||||
{file = "Hypercorn-0.12.0-py3-none-any.whl", hash = "sha256:485a03dc171549dd802c5a2d4cce2d46daf077fbc06c7db90e0862ebc1bd07c9"},
|
||||
@ -1867,8 +1868,8 @@ sniffio = [
|
||||
{file = "sniffio-1.2.0.tar.gz", hash = "sha256:c4666eecec1d3f50960c6bdf61ab7bc350648da6c126e3cf6898d8cd4ddcd3de"},
|
||||
]
|
||||
snowballstemmer = [
|
||||
{file = "snowballstemmer-2.1.0-py2.py3-none-any.whl", hash = "sha256:b51b447bea85f9968c13b650126a888aabd4cb4463fca868ec596826325dedc2"},
|
||||
{file = "snowballstemmer-2.1.0.tar.gz", hash = "sha256:e997baa4f2e9139951b6f4c631bad912dfd3c792467e2f03d7239464af90e914"},
|
||||
{file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"},
|
||||
{file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"},
|
||||
]
|
||||
sphinx = [
|
||||
{file = "Sphinx-4.3.0-py3-none-any.whl", hash = "sha256:7e2b30da5f39170efcd95c6270f07669d623c276521fee27ad6c380f49d2bf5b"},
|
||||
@ -1912,9 +1913,8 @@ tomlkit = [
|
||||
{file = "tomlkit-0.7.2.tar.gz", hash = "sha256:d7a454f319a7e9bd2e249f239168729327e4dd2d27b17dc68be264ad1ce36754"},
|
||||
]
|
||||
typing-extensions = [
|
||||
{file = "typing_extensions-3.10.0.2-py2-none-any.whl", hash = "sha256:d8226d10bc02a29bcc81df19a26e56a9647f8b0a6d4a83924139f4a8b01f17b7"},
|
||||
{file = "typing_extensions-3.10.0.2-py3-none-any.whl", hash = "sha256:f1d25edafde516b146ecd0613dabcc61409817af4766fbbcfb8d1ad4ec441a34"},
|
||||
{file = "typing_extensions-3.10.0.2.tar.gz", hash = "sha256:49f75d16ff11f1cd258e1b988ccff82a3ca5570217d7ad8c5f48205dd99a677e"},
|
||||
{file = "typing_extensions-4.0.0-py3-none-any.whl", hash = "sha256:829704698b22e13ec9eaf959122315eabb370b0884400e9818334d8b677023d9"},
|
||||
{file = "typing_extensions-4.0.0.tar.gz", hash = "sha256:2cdf80e4e04866a9b3689a51869016d36db0814d84b8d8a568d22781d45d27ed"},
|
||||
]
|
||||
unify = [
|
||||
{file = "unify-0.5.tar.gz", hash = "sha256:8ddce812b2457212b7598fe574c9e6eb3ad69710f445391338270c7f8a71723c"},
|
||||
@ -1953,31 +1953,54 @@ watchgod = [
|
||||
{file = "watchgod-0.7.tar.gz", hash = "sha256:48140d62b0ebe9dd9cf8381337f06351e1f2e70b2203fa9c6eff4e572ca84f29"},
|
||||
]
|
||||
websockets = [
|
||||
{file = "websockets-10.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cd8c6f2ec24aedace251017bc7a414525171d4e6578f914acab9349362def4da"},
|
||||
{file = "websockets-10.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:1f6b814cff6aadc4288297cb3a248614829c6e4ff5556593c44a115e9dd49939"},
|
||||
{file = "websockets-10.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:01db0ecd1a0ca6702d02a5ed40413e18b7d22f94afb3bbe0d323bac86c42c1c8"},
|
||||
{file = "websockets-10.0-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:82b17524b1ce6ae7f7dd93e4d18e9b9474071e28b65dbf1dfe9b5767778db379"},
|
||||
{file = "websockets-10.0-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:8bbf8660c3f833ddc8b1afab90213f2e672a9ddac6eecb3cde968e6b2807c1c7"},
|
||||
{file = "websockets-10.0-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:b8176deb6be540a46695960a765a77c28ac8b2e3ef2ec95d50a4f5df901edb1c"},
|
||||
{file = "websockets-10.0-cp37-cp37m-win32.whl", hash = "sha256:706e200fc7f03bed99ad0574cd1ea8b0951477dd18cc978ccb190683c69dba76"},
|
||||
{file = "websockets-10.0-cp37-cp37m-win_amd64.whl", hash = "sha256:5b2600e01c7ca6f840c42c747ffbe0254f319594ed108db847eb3d75f4aacb80"},
|
||||
{file = "websockets-10.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:085bb8a6e780d30eaa1ba48ac7f3a6707f925edea787cfb761ce5a39e77ac09b"},
|
||||
{file = "websockets-10.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:9a4d889162bd48588e80950e07fa5e039eee9deb76a58092e8c3ece96d7ef537"},
|
||||
{file = "websockets-10.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:b4ade7569b6fd17912452f9c3757d96f8e4044016b6d22b3b8391e641ca50456"},
|
||||
{file = "websockets-10.0-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:2a43072e434c041a99f2e1eb9b692df0232a38c37c61d00e9f24db79474329e4"},
|
||||
{file = "websockets-10.0-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:7f79f02c7f9a8320aff7d3321cd1c7e3a7dbc15d922ac996cca827301ee75238"},
|
||||
{file = "websockets-10.0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:1ac35426fe3e7d3d0fac3d63c8965c76ed67a8fd713937be072bf0ce22808539"},
|
||||
{file = "websockets-10.0-cp38-cp38-win32.whl", hash = "sha256:ff59c6bdb87b31f7e2d596f09353d5a38c8c8ff571b0e2238e8ee2d55ad68465"},
|
||||
{file = "websockets-10.0-cp38-cp38-win_amd64.whl", hash = "sha256:d67646ddd17a86117ae21c27005d83c1895c0cef5d7be548b7549646372f868a"},
|
||||
{file = "websockets-10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:82bd921885231f4a30d9bc550552495b3fc36b1235add6d374e7c65c3babd805"},
|
||||
{file = "websockets-10.0-cp39-cp39-manylinux1_i686.whl", hash = "sha256:7d2e12e4f901f1bc062dfdf91831712c4106ed18a9a4cdb65e2e5f502124ca37"},
|
||||
{file = "websockets-10.0-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:71358c7816e2762f3e4af3adf0040f268e219f5a38cb3487a9d0fc2e554fef6a"},
|
||||
{file = "websockets-10.0-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:fe83b3ec9ef34063d86dfe1029160a85f24a5a94271036e5714a57acfdd089a1"},
|
||||
{file = "websockets-10.0-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:eb282127e9c136f860c6068a4fba5756eb25e755baffb5940b6f1eae071928b2"},
|
||||
{file = "websockets-10.0-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:62160772314920397f9d219147f958b33fa27a12c662d4455c9ccbba9a07e474"},
|
||||
{file = "websockets-10.0-cp39-cp39-win32.whl", hash = "sha256:e42a1f1e03437b017af341e9bbfdc09252cd48ef32a8c3c3ead769eab3b17368"},
|
||||
{file = "websockets-10.0-cp39-cp39-win_amd64.whl", hash = "sha256:c5880442f5fc268f1ef6d37b2c152c114deccca73f48e3a8c48004d2f16f4567"},
|
||||
{file = "websockets-10.0.tar.gz", hash = "sha256:c4fc9a1d242317892590abe5b61a9127f1a61740477bfb121743f290b8054002"},
|
||||
{file = "websockets-10.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:38db6e2163b021642d0a43200ee2dec8f4980bdbda96db54fde72b283b54cbfc"},
|
||||
{file = "websockets-10.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e1b60fd297adb9fc78375778a5220da7f07bf54d2a33ac781319650413fc6a60"},
|
||||
{file = "websockets-10.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3477146d1f87ead8df0f27e8960249f5248dceb7c2741e8bbec9aa5338d0c053"},
|
||||
{file = "websockets-10.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb01ea7b5f52e7125bdc3c5807aeaa2d08a0553979cf2d96a8b7803ea33e15e7"},
|
||||
{file = "websockets-10.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9fd62c6dc83d5d35fb6a84ff82ec69df8f4657fff05f9cd6c7d9bec0dd57f0f6"},
|
||||
{file = "websockets-10.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3bbf080f3892ba1dc8838786ec02899516a9d227abe14a80ef6fd17d4fb57127"},
|
||||
{file = "websockets-10.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5560558b0dace8312c46aa8915da977db02738ac8ecffbc61acfbfe103e10155"},
|
||||
{file = "websockets-10.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:667c41351a6d8a34b53857ceb8343a45c85d438ee4fd835c279591db8aeb85be"},
|
||||
{file = "websockets-10.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:468f0031fdbf4d643f89403a66383247eb82803430b14fa27ce2d44d2662ca37"},
|
||||
{file = "websockets-10.1-cp310-cp310-win32.whl", hash = "sha256:d0d81b46a5c87d443e40ce2272436da8e6092aa91f5fbeb60d1be9f11eff5b4c"},
|
||||
{file = "websockets-10.1-cp310-cp310-win_amd64.whl", hash = "sha256:b68b6caecb9a0c6db537aa79750d1b592a841e4f1a380c6196091e65b2ad35f9"},
|
||||
{file = "websockets-10.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a249139abc62ef333e9e85064c27fefb113b16ffc5686cefc315bdaef3eefbc8"},
|
||||
{file = "websockets-10.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8877861e3dee38c8d302eee0d5dbefa6663de3b46dc6a888f70cd7e82562d1f7"},
|
||||
{file = "websockets-10.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:e3872ae57acd4306ecf937d36177854e218e999af410a05c17168cd99676c512"},
|
||||
{file = "websockets-10.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b66e6d514f12c28d7a2d80bb2a48ef223342e99c449782d9831b0d29a9e88a17"},
|
||||
{file = "websockets-10.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:9f304a22ece735a3da8a51309bc2c010e23961a8f675fae46fdf62541ed62123"},
|
||||
{file = "websockets-10.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:189ed478395967d6a98bb293abf04e8815349e17456a0a15511f1088b6cb26e4"},
|
||||
{file = "websockets-10.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:08a42856158307e231b199671c4fce52df5786dd3d703f36b5d8ac76b206c485"},
|
||||
{file = "websockets-10.1-cp37-cp37m-win32.whl", hash = "sha256:3ef6f73854cded34e78390dbdf40dfdcf0b89b55c0e282468ef92646fce8d13a"},
|
||||
{file = "websockets-10.1-cp37-cp37m-win_amd64.whl", hash = "sha256:89e985d40d407545d5f5e2e58e1fdf19a22bd2d8cd54d20a882e29f97e930a0a"},
|
||||
{file = "websockets-10.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:002071169d2e44ce8eb9e5ebac9fbce142ba4b5146eef1cfb16b177a27662657"},
|
||||
{file = "websockets-10.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cfae282c2aa7f0c4be45df65c248481f3509f8c40ca8b15ed96c35668ae0ff69"},
|
||||
{file = "websockets-10.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:97b4b68a2ddaf5c4707ae79c110bfd874c5be3c6ac49261160fb243fa45d8bbb"},
|
||||
{file = "websockets-10.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7c9407719f42cb77049975410490c58a705da6af541adb64716573e550e5c9db"},
|
||||
{file = "websockets-10.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1d858fb31e5ac992a2cdf17e874c95f8a5b1e917e1fb6b45ad85da30734b223f"},
|
||||
{file = "websockets-10.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7bdd3d26315db0a9cf8a0af30ca95e0aa342eda9c1377b722e71ccd86bc5d1dd"},
|
||||
{file = "websockets-10.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e259be0863770cb91b1a6ccf6907f1ac2f07eff0b7f01c249ed751865a70cb0d"},
|
||||
{file = "websockets-10.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6b014875fae19577a392372075e937ebfebf53fd57f613df07b35ab210f31534"},
|
||||
{file = "websockets-10.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:98de71f86bdb29430fd7ba9997f47a6b10866800e3ea577598a786a785701bb0"},
|
||||
{file = "websockets-10.1-cp38-cp38-win32.whl", hash = "sha256:3a02ab91d84d9056a9ee833c254895421a6333d7ae7fff94b5c68e4fa8095519"},
|
||||
{file = "websockets-10.1-cp38-cp38-win_amd64.whl", hash = "sha256:7d6673b2753f9c5377868a53445d0c321ef41ff3c8e3b6d57868e72054bfce5f"},
|
||||
{file = "websockets-10.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ddab2dc69ee5ae27c74dbfe9d7bb6fee260826c136dca257faa1a41d1db61a89"},
|
||||
{file = "websockets-10.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:14e9cf68a08d1a5d42109549201aefba473b1d925d233ae19035c876dd845da9"},
|
||||
{file = "websockets-10.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e4819c6fb4f336fd5388372cb556b1f3a165f3f68e66913d1a2fc1de55dc6f58"},
|
||||
{file = "websockets-10.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:05e7f098c76b0a4743716590bb8f9706de19f1ef5148d61d0cf76495ec3edb9c"},
|
||||
{file = "websockets-10.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5bb6256de5a4fb1d42b3747b4e2268706c92965d75d0425be97186615bf2f24f"},
|
||||
{file = "websockets-10.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:888a5fa2a677e0c2b944f9826c756475980f1b276b6302e606f5c4ff5635be9e"},
|
||||
{file = "websockets-10.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:6fdec1a0b3e5630c58e3d8704d2011c678929fce90b40908c97dfc47de8dca72"},
|
||||
{file = "websockets-10.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:531d8eb013a9bc6b3ad101588182aa9b6dd994b190c56df07f0d84a02b85d530"},
|
||||
{file = "websockets-10.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0d93b7cadc761347d98da12ec1930b5c71b2096f1ceed213973e3cda23fead9c"},
|
||||
{file = "websockets-10.1-cp39-cp39-win32.whl", hash = "sha256:d9b245db5a7e64c95816e27d72830e51411c4609c05673d1ae81eb5d23b0be54"},
|
||||
{file = "websockets-10.1-cp39-cp39-win_amd64.whl", hash = "sha256:882c0b8bdff3bf1bd7f024ce17c6b8006042ec4cceba95cf15df57e57efa471c"},
|
||||
{file = "websockets-10.1-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:10edd9d7d3581cfb9ff544ac09fc98cab7ee8f26778a5a8b2d5fd4b0684c5ba5"},
|
||||
{file = "websockets-10.1-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:baa83174390c0ff4fc1304fbe24393843ac7a08fdd59295759c4b439e06b1536"},
|
||||
{file = "websockets-10.1-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:483edee5abed738a0b6a908025be47f33634c2ad8e737edd03ffa895bd600909"},
|
||||
{file = "websockets-10.1-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:816ae7dac2c6522cfa620947ead0ca95ac654916eebf515c94d7c28de5601a6e"},
|
||||
{file = "websockets-10.1-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:1dafe98698ece09b8ccba81b910643ff37198e43521d977be76caf37709cf62b"},
|
||||
{file = "websockets-10.1.tar.gz", hash = "sha256:181d2b25de5a437b36aefedaf006ecb6fa3aa1328ec0236cdde15f32f9d3ff6d"},
|
||||
]
|
||||
werkzeug = [
|
||||
{file = "Werkzeug-2.0.2-py3-none-any.whl", hash = "sha256:63d3dc1cf60e7b7e35e97fa9861f7397283b75d765afcaefd993d6046899de8f"},
|
||||
|
@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "nonebot2"
|
||||
version = "2.0.0-alpha.16"
|
||||
version = "2.0.0-beta.1"
|
||||
description = "An asynchronous python bot framework."
|
||||
authors = ["yanyongyu <yyy@nonebot.dev>"]
|
||||
license = "MIT"
|
||||
@ -28,6 +28,7 @@ pygtrie = "^2.4.1"
|
||||
tomlkit = "^0.7.0"
|
||||
fastapi = "^0.70.0"
|
||||
websockets = ">=9.1"
|
||||
typing-extensions = ">=3.10.0,<5.0.0"
|
||||
Quart = { version = "^0.15.0", optional = true }
|
||||
httpx = { version = ">=0.20.0, <1.0.0", extras = ["http2"] }
|
||||
pydantic = { version = "~1.8.0", extras = ["dotenv"] }
|
||||
|
28
tests/test_plugins/test_depends.py
Normal file
28
tests/test_plugins/test_depends.py
Normal file
@ -0,0 +1,28 @@
|
||||
from nonebot.log import logger
|
||||
from nonebot.dependencies import Depends
|
||||
from nonebot import on_command, on_message
|
||||
|
||||
test = on_command("123")
|
||||
|
||||
|
||||
def depend(state: dict):
|
||||
print("==== depends running =====")
|
||||
return state
|
||||
|
||||
|
||||
@test.got("a", prompt="a")
|
||||
@test.got("b", prompt="b")
|
||||
@test.receive()
|
||||
@test.got("c", prompt="c")
|
||||
async def _(x: dict = Depends(depend)):
|
||||
logger.info(f"=======, {x}")
|
||||
|
||||
|
||||
test_cache1 = on_message()
|
||||
test_cache2 = on_message()
|
||||
|
||||
|
||||
@test_cache1.handle()
|
||||
@test_cache2.handle()
|
||||
async def _(x: dict = Depends(depend)):
|
||||
logger.info(f"======= test, {x}")
|
@ -1,15 +1,15 @@
|
||||
from nonebot.adapters import Event
|
||||
from nonebot.typing import T_State
|
||||
from nonebot.matcher import Matcher
|
||||
from nonebot.adapters import Bot, Event
|
||||
from nonebot.message import event_preprocessor, run_preprocessor
|
||||
from nonebot.message import run_preprocessor, event_preprocessor
|
||||
|
||||
|
||||
@event_preprocessor
|
||||
async def handle(bot: Bot, event: Event, state: T_State):
|
||||
async def handle(event: Event, state: T_State):
|
||||
state["preprocessed"] = True
|
||||
print(type(event), event)
|
||||
|
||||
|
||||
@run_preprocessor
|
||||
async def run(matcher: Matcher, bot: Bot, event: Event, state: T_State):
|
||||
async def run(matcher: Matcher):
|
||||
print(matcher)
|
||||
|
Loading…
Reference in New Issue
Block a user