diff --git a/nonebot/matcher.py b/nonebot/matcher.py index 449be730..1bad8a6e 100644 --- a/nonebot/matcher.py +++ b/nonebot/matcher.py @@ -398,7 +398,9 @@ class Matcher(metaclass=MatcherMeta): return handler_ @classmethod - def handle(cls) -> Callable[[T_Handler], T_Handler]: + def handle( + cls, dependencies: Optional[List[DependsWrapper]] = None + ) -> Callable[[T_Handler], T_Handler]: """ :说明: @@ -406,17 +408,19 @@ class Matcher(metaclass=MatcherMeta): :参数: - * 无 + * ``dependencies: Optional[List[DependsWrapper]]``: 非参数类型依赖列表 """ def _decorator(func: T_Handler) -> T_Handler: - cls.append_handler(func) + cls.append_handler(func, dependencies=dependencies) return func return _decorator @classmethod - def receive(cls) -> Callable[[T_Handler], T_Handler]: + def receive( + cls, dependencies: Optional[List[DependsWrapper]] = None + ) -> Callable[[T_Handler], T_Handler]: """ :说明: @@ -424,7 +428,7 @@ class Matcher(metaclass=MatcherMeta): :参数: - * 无 + * ``dependencies: Optional[List[DependsWrapper]]``: 非参数类型依赖列表 """ async def _receive(state: T_State) -> Union[None, NoReturn]: @@ -434,15 +438,19 @@ class Matcher(metaclass=MatcherMeta): del state["_current_key"] raise RejectedException - def _decorator(func: T_Handler) -> T_Handler: + _dependencies = [DependsWrapper(_receive), *(dependencies or [])] - depend = DependsWrapper(_receive) + def _decorator(func: T_Handler) -> T_Handler: if cls.handlers and cls.handlers[-1].func is func: func_handler = cls.handlers[-1] - func_handler.prepend_dependency(depend) + for depend in reversed(_dependencies): + func_handler.prepend_dependency(depend) else: - cls.append_handler(func, dependencies=[depend] if cls.handlers else []) + cls.append_handler( + func, + dependencies=_dependencies if cls.handlers else dependencies, + ) return func @@ -454,6 +462,7 @@ class Matcher(metaclass=MatcherMeta): key: str, prompt: Optional[Union[str, Message, MessageSegment, MessageTemplate]] = None, args_parser: Optional[T_ArgsParser] = None, + dependencies: Optional[List[DependsWrapper]] = None, ) -> Callable[[T_Handler], T_Handler]: """ :说明: @@ -465,6 +474,7 @@ class Matcher(metaclass=MatcherMeta): * ``key: str``: 参数名 * ``prompt: Optional[Union[str, Message, MessageSegment, MessageFormatter]]``: 在参数不存在时向用户发送的消息 * ``args_parser: Optional[T_ArgsParser]``: 可选参数解析函数,空则使用默认解析函数 + * ``dependencies: Optional[List[DependsWrapper]]``: 非参数类型依赖列表 """ async def _key_getter(bot: Bot, event: Event, state: T_State): @@ -495,17 +505,20 @@ class Matcher(metaclass=MatcherMeta): state[key] = str(event.get_message()) state[f"_{key}_parsed"] = True - def _decorator(func: T_Handler) -> T_Handler: + _dependencies = [ + DependsWrapper(_key_getter), + DependsWrapper(_key_parser), + *(dependencies or []), + ] - get_depend = DependsWrapper(_key_getter) - parser_depend = DependsWrapper(_key_parser) + def _decorator(func: T_Handler) -> T_Handler: 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) + for depend in reversed(_dependencies): + func_handler.prepend_dependency(depend) else: - cls.append_handler(func, dependencies=[get_depend, parser_depend]) + cls.append_handler(func, dependencies=_dependencies) return func diff --git a/website/docs/advanced/README.md b/website/docs/advanced/README.md index 094a5367..2d6fd630 100644 --- a/website/docs/advanced/README.md +++ b/website/docs/advanced/README.md @@ -1,5 +1,4 @@ --- -sidebar_position: 1 id: index slug: /advanced diff --git a/website/docs/advanced/export-and-require.md b/website/docs/advanced/export-and-require.md index 5bb97852..c1e7358e 100644 --- a/website/docs/advanced/export-and-require.md +++ b/website/docs/advanced/export-and-require.md @@ -1,6 +1,4 @@ --- -sidebar_position: 5 - options: menu: weight: 50 diff --git a/website/docs/advanced/handler/dependency-injection.md b/website/docs/advanced/handler/dependency-injection.md new file mode 100644 index 00000000..a19baadb --- /dev/null +++ b/website/docs/advanced/handler/dependency-injection.md @@ -0,0 +1,108 @@ +--- +sidebar_position: 3 + +options: + menu: + weight: 62 + category: advanced +--- + +# 依赖注入 + +受 [`FastApi`](https://fastapi.tiangolo.com/tutorial/dependencies/) 启发,NoneBot 同样编写了一个简易的依赖注入模块,使得开发者可以通过事件处理函数参数的类型标注来自动注入依赖。 + +## 什么是依赖注入? + +~~交给 mix 了~~ + +## 使用依赖注入 + +以下通过一个简单的例子来说明依赖注入的使用方法: + +### 编写依赖函数 + +这里我们编写了一个简单的函数 `depend` 作为依赖函数 + +```python {7-9} +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): + # do something with state + return {**state, "depend": "depend"} + +@test.handle() +async def _(x: dict = Depends(depend)): + print(x) +``` + +它和普通的事件处理函数并无区别,同样可以接受 `bot`, `event` 等参数,你可以把它当作一个普通的事件处理函数但是去除了装饰器(并没有使用 `matcher.handle()` 等来进行装饰),并且它可以返回任何类型的值。 + +在这个例子中,依赖函数接受一个参数: + +- `state: dict`:当前事件处理状态字典。 + +并且返回了一个 `state` 的复制以及一个附加的键值 `depend` 。 + +### Import `Depends` + +```python {2} +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): + # do something with state + return {**state, "depend": "depend"} + +@test.handle() +async def _(x: dict = Depends(depend)): + print(x) +``` + +### 在事件处理函数里声明依赖函数 + +与 FastAPI 类似,你可以在函数中添加一个新的参数,并且使用 `Depends` 来声明它的依赖。 + +```python {12} +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): + # do something with state + return {**state, "depend": "depend"} + +@test.handle() +async def _(x: dict = Depends(depend)): + print(x) +``` + +你需要给 `Depends` 指定一个依赖函数,这个依赖函数的返回值会被作为 `x` 的值。 + +`Depends` 的首个参数即是依赖函数,或者其他 `Callable` 对象,在之后会对更多形式的依赖对象进行介绍。 + +:::tip +参数 `x` 的类型标注并不会影响事件处理函数的运行,类型检查并不会对依赖函数的返回值以及类型标注进行检查。 +::: + +当接收到事件时,NoneBot 会进行以下处理: + +1. 查询缓存,如果缓存中有相应的值,则直接返回。 +2. 准备依赖函数所需要的参数。 +3. 调用依赖函数并获得返回值。 +4. 将返回值存入缓存。 +5. 将返回值作为事件处理函数中的参数值传入。 + +## 依赖缓存 + +## Class 作为依赖 + +## Generator 作为依赖 diff --git a/website/docs/advanced/overloaded-handlers.md b/website/docs/advanced/handler/overload.md similarity index 98% rename from website/docs/advanced/overloaded-handlers.md rename to website/docs/advanced/handler/overload.md index d30211c5..a1df728a 100644 --- a/website/docs/advanced/overloaded-handlers.md +++ b/website/docs/advanced/handler/overload.md @@ -1,9 +1,9 @@ --- -sidebar_position: 6 +sidebar_position: 2 options: menu: - weight: 60 + weight: 61 category: advanced --- @@ -15,7 +15,7 @@ options: 必须要注意的是,该机制利用了 `inspect` 标准库获取到了事件处理函数的 `singnature` (签名) ,进一步获取到参数名称和类型标注。故而,我们在编写 `handler` 时,参数的名称和类型标注必须要符合 `T_Handler` 规定,详情可以参看 **指南** 中的[事件处理](../guide/creating-a-handler)。 -::: tip 提示 +:::tip 提示 如果想了解更多关于 `inspect` 标准库的信息,可以查看[官方文档](https://docs.python.org/zh-cn/3.9/library/inspect.html)。 diff --git a/website/docs/advanced/handler/sync-support.md b/website/docs/advanced/handler/sync-support.md new file mode 100644 index 00000000..54d81b6c --- /dev/null +++ b/website/docs/advanced/handler/sync-support.md @@ -0,0 +1,10 @@ +--- +sidebar_position: 1 + +options: + menu: + weight: 60 + category: advanced +--- + +# 同步支持 diff --git a/website/docs/advanced/permission.md b/website/docs/advanced/permission.md index f9d411ad..c72b4bf2 100644 --- a/website/docs/advanced/permission.md +++ b/website/docs/advanced/permission.md @@ -1,6 +1,4 @@ --- -sidebar_position: 3 - options: menu: weight: 30 diff --git a/website/docs/advanced/publish-plugin.md b/website/docs/advanced/publish-plugin.md index 9396a886..2de8293a 100644 --- a/website/docs/advanced/publish-plugin.md +++ b/website/docs/advanced/publish-plugin.md @@ -1,6 +1,4 @@ --- -sidebar_position: 7 - options: menu: weight: 70 diff --git a/website/docs/advanced/runtime-hook.md b/website/docs/advanced/runtime-hook.md index d6f1f271..cf0dd928 100644 --- a/website/docs/advanced/runtime-hook.md +++ b/website/docs/advanced/runtime-hook.md @@ -1,6 +1,4 @@ --- -sidebar_position: 4 - options: menu: weight: 40 diff --git a/website/docs/advanced/scheduler.md b/website/docs/advanced/scheduler.md index 8caf30b6..dd45a15b 100644 --- a/website/docs/advanced/scheduler.md +++ b/website/docs/advanced/scheduler.md @@ -1,6 +1,4 @@ --- -sidebar_position: 2 - options: menu: weight: 20 diff --git a/website/sidebars.js b/website/sidebars.js index 1ad12483..44577ae6 100644 --- a/website/sidebars.js +++ b/website/sidebars.js @@ -62,7 +62,11 @@ const sidebars = { "advanced/permission", "advanced/runtime-hook", "advanced/export-and-require", - "advanced/overloaded-handlers", + { + type: "category", + label: "高级事件处理", + items: [{ type: "autogenerated", dirName: "advanced/handler" }], + }, ], }, { diff --git a/yarn.lock b/yarn.lock index e86e807d..d49e543a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,24 +2,24 @@ # yarn lockfile v1 -"@algolia/autocomplete-core@1.2.2": - version "1.2.2" - resolved "https://registry.npmmirror.com/@algolia/autocomplete-core/download/@algolia/autocomplete-core-1.2.2.tgz?cache=0&sync_timestamp=1635931001143&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2F%40algolia%2Fautocomplete-core%2Fdownload%2F%40algolia%2Fautocomplete-core-1.2.2.tgz#c121e70c78fd0175c989a219918124ad7758e48b" - integrity sha1-wSHnDHj9AXXJiaIZkYEkrXdY5Is= +"@algolia/autocomplete-core@1.5.0": + version "1.5.0" + resolved "https://registry.npmmirror.com/@algolia/autocomplete-core/download/@algolia/autocomplete-core-1.5.0.tgz?cache=0&sync_timestamp=1635931001143&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2F%40algolia%2Fautocomplete-core%2Fdownload%2F%40algolia%2Fautocomplete-core-1.5.0.tgz#6c91c9de7748e9c103846828a58dfe92bd4d6689" + integrity sha1-bJHJ3ndI6cEDhGgopY3+kr1NZok= dependencies: - "@algolia/autocomplete-shared" "1.2.2" + "@algolia/autocomplete-shared" "1.5.0" -"@algolia/autocomplete-preset-algolia@1.2.2": - version "1.2.2" - resolved "https://registry.npmmirror.com/@algolia/autocomplete-preset-algolia/download/@algolia/autocomplete-preset-algolia-1.2.2.tgz#da734ef9e42a5f64cdad2dfc81c4e9fbf805d976" - integrity sha1-2nNO+eQqX2TNrS38gcTp+/gF2XY= +"@algolia/autocomplete-preset-algolia@1.5.0": + version "1.5.0" + resolved "https://registry.npmmirror.com/@algolia/autocomplete-preset-algolia/download/@algolia/autocomplete-preset-algolia-1.5.0.tgz#61671f09c0c77133d9baf1356719f8378c48437a" + integrity sha1-YWcfCcDHcTPZuvE1Zxn4N4xIQ3o= dependencies: - "@algolia/autocomplete-shared" "1.2.2" + "@algolia/autocomplete-shared" "1.5.0" -"@algolia/autocomplete-shared@1.2.2": - version "1.2.2" - resolved "https://registry.npmmirror.com/@algolia/autocomplete-shared/download/@algolia/autocomplete-shared-1.2.2.tgz#ff25dc308f2a296b2b9b325f1e3b57498eea3e0c" - integrity sha1-/yXcMI8qKWsrmzJfHjtXSY7qPgw= +"@algolia/autocomplete-shared@1.5.0": + version "1.5.0" + resolved "https://registry.npmmirror.com/@algolia/autocomplete-shared/download/@algolia/autocomplete-shared-1.5.0.tgz#09580bc89408a2ab5f29e312120dad68f58019bd" + integrity sha1-CVgLyJQIoqtfKeMSEg2taPWAGb0= "@algolia/cache-browser-local-storage@4.11.0": version "4.11.0" @@ -1146,19 +1146,19 @@ "@babel/helper-validator-identifier" "^7.15.7" to-fast-properties "^2.0.0" -"@docsearch/css@3.0.0-alpha.41": - version "3.0.0-alpha.41" - resolved "https://registry.npmmirror.com/@docsearch/css/download/@docsearch/css-3.0.0-alpha.41.tgz#c5c8e803541bd157ad86e764c2c1e9f1b5a68592" - integrity sha1-xcjoA1Qb0VethudkwsHp8bWmhZI= +"@docsearch/css@3.0.0-alpha.42": + version "3.0.0-alpha.42" + resolved "https://registry.npmmirror.com/@docsearch/css/download/@docsearch/css-3.0.0-alpha.42.tgz#deb6049e999d6ca9451eba4793cb5b6da28c8773" + integrity sha512-AGwI2AXUacYhVOHmYnsXoYDJKO6Ued2W+QO80GERbMLhC7GH5tfvtW5REs/s7jSdcU3vzFoxT8iPDBCh/PkrlQ== "@docsearch/react@^3.0.0-alpha.39": - version "3.0.0-alpha.41" - resolved "https://registry.npmmirror.com/@docsearch/react/download/@docsearch/react-3.0.0-alpha.41.tgz#07e85a664e85f251ce3d13153abce65a4d5997ab" - integrity sha1-B+haZk6F8lHOPRMVOrzmWk1Zl6s= + version "3.0.0-alpha.42" + resolved "https://registry.npmmirror.com/@docsearch/react/download/@docsearch/react-3.0.0-alpha.42.tgz#1d22a2b05779f24d090ff8d7ff2699e4d50dff5c" + integrity sha512-1aOslZJDxwUUcm2QRNmlEePUgL8P5fOAeFdOLDMctHQkV2iTja9/rKVbkP8FZbIUnZxuuCCn8ErLrjD/oXWOag== dependencies: - "@algolia/autocomplete-core" "1.2.2" - "@algolia/autocomplete-preset-algolia" "1.2.2" - "@docsearch/css" "3.0.0-alpha.41" + "@algolia/autocomplete-core" "1.5.0" + "@algolia/autocomplete-preset-algolia" "1.5.0" + "@docsearch/css" "3.0.0-alpha.42" algoliasearch "^4.0.0" "@docusaurus/core@2.0.0-beta.9": @@ -3238,19 +3238,19 @@ dns-txt@^2.0.2: buffer-indexof "^1.0.0" docusaurus-preset-nonepress@canary: - version "0.0.0-113" - resolved "https://registry.npmmirror.com/docusaurus-preset-nonepress/download/docusaurus-preset-nonepress-0.0.0-113.tgz#8ed150a551c02f52cc9dba4c97a7d97c37e20a92" - integrity sha512-2TGTjzGAwuK0WzSokf7M2792yrMIY64pAX6IJfZcuFAe2u+GOTms4b4aaJecXo9Q6DTyvhSaRqppLEo0BSILZw== + version "0.0.0-116" + resolved "https://registry.npmmirror.com/docusaurus-preset-nonepress/download/docusaurus-preset-nonepress-0.0.0-116.tgz#afa053d218666f6a532d76e44bba6e650657e3f0" + integrity sha512-5UmZVhR50qjahlwKl7kLy7PATKjOIxfiXvL16p9kQ0Xd57DkkoOHnueeVdVHRxDSH5Dpgje/ouX+8D/LqsugdA== dependencies: "@docusaurus/core" "2.0.0-beta.9" "@docusaurus/plugin-content-docs" "2.0.0-beta.9" "@docusaurus/plugin-content-pages" "2.0.0-beta.9" - docusaurus-theme-nonepress "0.0.0-113" + docusaurus-theme-nonepress "0.0.0-116" -docusaurus-theme-nonepress@0.0.0-113: - version "0.0.0-113" - resolved "https://registry.npmmirror.com/docusaurus-theme-nonepress/download/docusaurus-theme-nonepress-0.0.0-113.tgz#1f59c962da80e4fae395342273be01391a8c5382" - integrity sha512-qtcchxgpAvi61VklufkaqrpFdd+3xpCbsn256PRhDmbsa9+NINybmuU+OV3u6nlx0VhSpWdBN4uwPozC9Ptsgw== +docusaurus-theme-nonepress@0.0.0-116: + version "0.0.0-116" + resolved "https://registry.npmmirror.com/docusaurus-theme-nonepress/download/docusaurus-theme-nonepress-0.0.0-116.tgz#4e7b8463738c0557118c33ecadc761e9c1ef485f" + integrity sha512-9kv6LoQKe2pfKToKPLYHuT2nwF2cMAFQgRhijQg2lu4YNvXDQ/rHzSbuuBIJp/NRYZfPH3LPwEGEREOe2VXJLQ== dependencies: "@docsearch/react" "^3.0.0-alpha.39" "@docusaurus/core" "2.0.0-beta.9"