mirror of
				https://github.com/nonebot/nonebot2.git
				synced 2025-10-30 22:46:40 +00:00 
			
		
		
		
	📝 Docs: 调整跨插件访问文档 (#993)
Co-authored-by: Ju4tCode <42488585+yanyongyu@users.noreply.github.com>
This commit is contained in:
		| @@ -1,5 +1,7 @@ | |||||||
| --- | --- | ||||||
| id: index | id: index | ||||||
|  | sidebar_position: 0 | ||||||
|  | description: 深入了解 NoneBot2 运行机制 | ||||||
| slug: /advanced/ | slug: /advanced/ | ||||||
|  |  | ||||||
| options: | options: | ||||||
|   | |||||||
| @@ -1,3 +1,4 @@ | |||||||
| { | { | ||||||
|   "label": "依赖注入" |   "label": "依赖注入", | ||||||
|  |   "position": 5 | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,10 +1,10 @@ | |||||||
| --- | --- | ||||||
| sidebar_position: 3 | sidebar_position: 2 | ||||||
| description: 重载事件处理函数 | description: 重载事件处理函数 | ||||||
|  |  | ||||||
| options: | options: | ||||||
|   menu: |   menu: | ||||||
|     weight: 62 |     weight: 61 | ||||||
|     category: advanced |     category: advanced | ||||||
| --- | --- | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,122 +0,0 @@ | |||||||
| --- |  | ||||||
| options: |  | ||||||
|   menu: |  | ||||||
|     weight: 50 |  | ||||||
|     category: advanced |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| # 跨插件访问 |  | ||||||
|  |  | ||||||
| 由于 NoneBot2 独特的插件加载机制,直接使用 Python 原生 import 机制来进行插件之间的访问时,很可能会发生奇怪或意料以外的情况。为了避免这种情况发生,您可以使用如下方法来实现跨插件访问: |  | ||||||
|  |  | ||||||
| 1. 将插件间的要使用的公共代码剥离出来,作为公共文件或者文件夹,提供给插件加以调用。 |  | ||||||
| 2. 使用 NoneBot2 提供的 export 和 require 机制,来实现插件间的互相调用。 |  | ||||||
| 3. 在保证插件被加载的情况下,可以采用 import 来访问。 |  | ||||||
|  |  | ||||||
| 第一种方法比较容易理解和实现,这里不再赘述,但需要注意的是,请不要将公共文件或者公共文件夹作为**插件**被 NoneBot2 加载。 |  | ||||||
| 第三种方法需要保证插件被加载,插件加载的方式可以参阅[加载插件](../tutorial/plugin/load-plugin)。 |  | ||||||
|  |  | ||||||
| 下面将介绍第二种方法——export 和 require 机制: |  | ||||||
|  |  | ||||||
| ## 使用 export 和 require |  | ||||||
|  |  | ||||||
| 现在,假定有两个插件 pluginA 和 pluginB,需要在 pluginB 中调用 pluginA 中的一个变量 `varA` 和一个函数 `funcA`。 |  | ||||||
|  |  | ||||||
| 在上面的条件中涉及到了两种操作:一种是在 pluginA 的导出对象操作;而另一种是在 pluginB 的导入对象操作。在 NoneBot2 中,导出对象的操作用 export 机制来实现,导入对象的操作用 require 机制来实现。下面,我们将逐一进行介绍。 |  | ||||||
|  |  | ||||||
| :::warning 警告 |  | ||||||
| 使用这个方法进行跨插件访问时,**需要先加载导出对象的插件,再加载导入对象的插件。** |  | ||||||
| ::: |  | ||||||
|  |  | ||||||
| ### 使用 export |  | ||||||
|  |  | ||||||
| 在 pluginA 中,我们调用 export 机制 导出对象。 |  | ||||||
|  |  | ||||||
| 在 export 机制调用前,我们需要保证导出的对象已经被定义,比如: |  | ||||||
|  |  | ||||||
| ```python |  | ||||||
| varA = "varA" |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def funcA(): |  | ||||||
|     return "funcA" |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| 在确保定义之后,我们可以从 `nonebot.plugin` 导入 `export()` 方法,`export()` 方法会返回一个特殊的字典 `export`: |  | ||||||
|  |  | ||||||
| ```python |  | ||||||
| from nonebot.plugin import export |  | ||||||
|  |  | ||||||
| export=export() |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| 这个字典可以用来装载导出的对象,它的 key 是对象导出后的命名,value 是对象本身,我们可以直接创建新的 key - value 对导出对象: |  | ||||||
|  |  | ||||||
| ```python |  | ||||||
| export.vA = varA |  | ||||||
| export.fA = funcA |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| 除此之外,也支持嵌套导出对象: |  | ||||||
|  |  | ||||||
| ```python |  | ||||||
| export.sub.vA = varA |  | ||||||
| export.sub.fA = funcA |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| 特别地,对于函数对象而言,export 支持用装饰器的方法来导出,因此,我们可以这样定义 `funcA`: |  | ||||||
|  |  | ||||||
| ```python |  | ||||||
| @export.sub |  | ||||||
| def funcA(): |  | ||||||
|     return "funcA" |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| 或者: |  | ||||||
|  |  | ||||||
| ```python |  | ||||||
| @export |  | ||||||
| def funcA(): |  | ||||||
|     return "funcA" |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| 通过装饰器的方法导出函数时,命名固定为函数的命名,也就是说,上面的两个例子等同于: |  | ||||||
|  |  | ||||||
| ```python |  | ||||||
| export.sub.funcA = funcA |  | ||||||
|  |  | ||||||
| export.funcA = funcA |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| 这样,我们就成功导出 `varA` 和 `funcA` 对象了。 |  | ||||||
|  |  | ||||||
| 下面我们将介绍如何在 pluginB 中导入这些对象。 |  | ||||||
|  |  | ||||||
| ### 使用 require |  | ||||||
|  |  | ||||||
| 在 pluginB 中,我们调用 require 机制导入对象。 |  | ||||||
|  |  | ||||||
| :::warning 警告 |  | ||||||
| 在导入来自其他插件的对象时,请确保导出该对象的插件在引用该对象的插件之前加载。如果该插件并未被加载,则会尝试加载,加载失败则会返回 `None`。 |  | ||||||
| ::: |  | ||||||
|  |  | ||||||
| 我们可以从 `nonebot.plugin` 中导入 `require()` 方法: |  | ||||||
|  |  | ||||||
| ```python |  | ||||||
| from nonebot.plugin import require |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| `require()` 方法的参数是插件名,它会返回在指定插件中,用 `export()` 方法创建的字典。 |  | ||||||
|  |  | ||||||
| ```python |  | ||||||
| require_A = require('pluginA') |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| 在之前,这个字典已经存入了 `'vA'` - `varA`、`'fA'` - `funcA` 或 `'funcA'` - `funcA` 这样的 key - value 对。因此在这里我们直接调用属性来获取导入对象: |  | ||||||
|  |  | ||||||
| ```python |  | ||||||
| varA = require_A.vA |  | ||||||
| funcA = require_A.fA or require_A.funcA |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| 这样,我们就在 pluginB 中成功导入了 `varA` 和 `funcA` 对象了。 |  | ||||||
| @@ -1,7 +1,10 @@ | |||||||
| --- | --- | ||||||
|  | sidebar_position: 3 | ||||||
|  | description: 自定义事件响应器的响应权限 | ||||||
|  |  | ||||||
| options: | options: | ||||||
|   menu: |   menu: | ||||||
|     weight: 30 |     weight: 40 | ||||||
|     category: advanced |     category: advanced | ||||||
| --- | --- | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,7 +1,10 @@ | |||||||
| --- | --- | ||||||
|  | sidebar_position: 7 | ||||||
|  | description: 发布插件到 NoneBot2 商店 | ||||||
|  |  | ||||||
| options: | options: | ||||||
|   menu: |   menu: | ||||||
|     weight: 70 |     weight: 80 | ||||||
|     category: advanced |     category: advanced | ||||||
| --- | --- | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,4 +1,7 @@ | |||||||
| --- | --- | ||||||
|  | sidebar_position: 2 | ||||||
|  | description: 自定义事件响应器的响应规则 | ||||||
|  |  | ||||||
| options: | options: | ||||||
|   menu: |   menu: | ||||||
|     weight: 30 |     weight: 30 | ||||||
|   | |||||||
| @@ -1,7 +1,10 @@ | |||||||
| --- | --- | ||||||
|  | sidebar_position: 4 | ||||||
|  | description: 在 NoneBot2 框架中添加 Hook 函数 | ||||||
|  |  | ||||||
| options: | options: | ||||||
|   menu: |   menu: | ||||||
|     weight: 40 |     weight: 50 | ||||||
|     category: advanced |     category: advanced | ||||||
| --- | --- | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,4 +1,7 @@ | |||||||
| --- | --- | ||||||
|  | sidebar_position: 1 | ||||||
|  | description: 在 NoneBot2 中使用定时任务 | ||||||
|  |  | ||||||
| options: | options: | ||||||
|   menu: |   menu: | ||||||
|     weight: 20 |     weight: 20 | ||||||
| @@ -13,7 +16,7 @@ options: | |||||||
|  |  | ||||||
| ## 从 NoneBot v1 迁移 | ## 从 NoneBot v1 迁移 | ||||||
|  |  | ||||||
| `APScheduler` 作为 NoneBot v1 的可选依赖,为众多 bot 提供了方便的定时任务功能。NoneBot2 已将 `APScheduler` 独立为 nonebot_plugin_apscheduler 插件,你可以在[商店](https://v2.nonebot.dev/store.html)中找到它。 | `APScheduler` 作为 NoneBot v1 的可选依赖,为众多 bot 提供了方便的定时任务功能。NoneBot2 已将 `APScheduler` 独立为 nonebot_plugin_apscheduler 插件,你可以在[商店](/store)中找到它。 | ||||||
|  |  | ||||||
| 相比于 NoneBot v1,NoneBot v2 只需要安装插件并修改 `scheduler` 的导入方式即可完成迁移。 | 相比于 NoneBot v1,NoneBot v2 只需要安装插件并修改 `scheduler` 的导入方式即可完成迁移。 | ||||||
|  |  | ||||||
| @@ -40,21 +43,23 @@ poetry add nonebot-plugin-apscheduler | |||||||
| ``` | ``` | ||||||
|  |  | ||||||
| :::tip 提示 | :::tip 提示 | ||||||
| 由于稍后我们将使用 `nonebot.require()` 方法进行导入,所以无需额外的 `nonebot.load_plugin()` | 由于稍后我们将使用 `nonebot.require()` 方法进行声明依赖,所以无需额外的 `nonebot.load_plugin()` | ||||||
| ::: | ::: | ||||||
|  |  | ||||||
| ## 快速上手 | ## 快速上手 | ||||||
|  |  | ||||||
| 1. 在需要设置定时任务的插件中,通过 `nonebot.require` 从 nonebot_plugin_apscheduler 导入 `scheduler` 对象 | 1. 在需要设置定时任务的插件中,通过 `nonebot.require` 声明插件依赖 | ||||||
|  | 2. 从 nonebot_plugin_apscheduler 导入 `scheduler` 对象 | ||||||
| 2. 在该对象的基础上,根据 `APScheduler` 的使用方法进一步配置定时任务 | 3. 在该对象的基础上,根据 `APScheduler` 的使用方法进一步配置定时任务 | ||||||
|  |  | ||||||
| 将上述步骤归纳为最小实现的代码如下: | 将上述步骤归纳为最小实现的代码如下: | ||||||
|  |  | ||||||
| ```python | ```python {3,5} | ||||||
| from nonebot import require | from nonebot import require | ||||||
|  |  | ||||||
| scheduler = require("nonebot_plugin_apscheduler").scheduler | require("nonebot_plugin_apscheduler") | ||||||
|  |  | ||||||
|  | from nonebot_plugin_apscheduler import scheduler | ||||||
|  |  | ||||||
| @scheduler.scheduled_job("cron", hour="*/2", id="xxx", args=[1], kwargs={"arg2": 2}) | @scheduler.scheduled_job("cron", hour="*/2", id="xxx", args=[1], kwargs={"arg2": 2}) | ||||||
| async def run_every_2_hour(arg1, arg2): | async def run_every_2_hour(arg1, arg2): | ||||||
| @@ -69,16 +74,16 @@ scheduler.add_job(run_every_day_from_program_start, "interval", days=1, id="xxx" | |||||||
|  |  | ||||||
| 为了使插件能够实现定时任务,需要先将 `scheduler` 对象导入插件。 | 为了使插件能够实现定时任务,需要先将 `scheduler` 对象导入插件。 | ||||||
|  |  | ||||||
| NoneBot2 提供了 `nonebot.require` 方法来实现导入其他插件的内容,此处我们使用这个方法来导入 `scheduler` 对象。 | NoneBot2 提供了 `nonebot.require` 方法来实现声明插件依赖,确保 `nonebot_plugin_apscheduler` 插件可以在使用之前被导入。声明完成即可直接通过 import 导入 `scheduler` 对象。 | ||||||
|  |  | ||||||
| NoneBot2 使用的 `scheduler` 对象为 `AsyncIOScheduler` 。 | NoneBot2 使用的 `scheduler` 对象为 `AsyncIOScheduler` 。 | ||||||
|  |  | ||||||
| > 使用该方法传入的插件本身也需要有对应实现,关于该方法的更多介绍可以参阅[这里](./export-and-require.md) | ```python {3,5} | ||||||
|  |  | ||||||
| ```python |  | ||||||
| from nonebot import require | from nonebot import require | ||||||
|  |  | ||||||
| scheduler = require("nonebot_plugin_apscheduler").scheduler | require("nonebot_plugin_apscheduler") | ||||||
|  |  | ||||||
|  | from nonebot_plugin_apscheduler import scheduler | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| ### 编写定时任务 | ### 编写定时任务 | ||||||
|   | |||||||
| @@ -5,7 +5,7 @@ slug: /advanced/unittest/ | |||||||
|  |  | ||||||
| options: | options: | ||||||
|   menu: |   menu: | ||||||
|     weight: 80 |     weight: 70 | ||||||
|     category: advanced |     category: advanced | ||||||
| --- | --- | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,3 +1,4 @@ | |||||||
| { | { | ||||||
|   "label": "单元测试" |   "label": "单元测试", | ||||||
|  |   "position": 6 | ||||||
| } | } | ||||||
|   | |||||||
| @@ -101,6 +101,20 @@ nonebot.load_from_toml("plugin_config.toml", encoding="utf-8") | |||||||
| nonebot.load_builtin_plugin("echo") | nonebot.load_builtin_plugin("echo") | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
|  | ## 确保插件加载和跨插件访问 | ||||||
|  |  | ||||||
|  | 倘若 `plugin_a`, `plugin_b` 均需被加载, 且 `plugin_b` 插件需要导入 `plugin_a` 才可运行, 可以在 `plugin-b` 利用 `require` 方法来确保插件加载, 同时可以直接 `import` 导入 `plugin-a` ,进行跨插件访问。 | ||||||
|  |  | ||||||
|  | ```python | ||||||
|  | from nonebot.plugin import require | ||||||
|  | require('plugin_a') | ||||||
|  | import plugin_a | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | :::danger 警告 | ||||||
|  | 不用 `require` 方法也可以进行跨插件访问,但需要保证插件已加载。 | ||||||
|  | ::: | ||||||
|  |  | ||||||
| ## 嵌套插件 | ## 嵌套插件 | ||||||
|  |  | ||||||
| <!-- TODO --> | <!-- TODO --> | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user