2022-01-22 15:23:07 +08:00
""" 本模块定义插件加载接口。
FrontMatter :
2024-10-22 10:33:48 +08:00
mdx :
format : md
2022-01-22 15:23:07 +08:00
sidebar_position : 1
description : nonebot . plugin . load 模块
"""
2023-06-24 14:47:35 +08:00
2021-11-11 17:33:30 +08:00
import json
2022-08-31 10:07:14 +08:00
from pathlib import Path
2022-08-08 21:07:36 +08:00
from types import ModuleType
2024-04-16 00:33:48 +08:00
from typing import Union , Optional
from collections . abc import Iterable
2021-11-08 01:02:35 +08:00
2022-08-31 10:07:14 +08:00
from nonebot . utils import path_to_module_name
2023-09-27 16:00:26 +08:00
from . model import Plugin
2021-11-08 01:02:35 +08:00
from . manager import PluginManager
2024-04-20 14:47:12 +08:00
from . import _managers , get_plugin , _module_name_to_plugin_id
2021-11-08 01:02:35 +08:00
2023-03-29 11:57:33 +08:00
try : # pragma: py-gte-311
2023-05-18 16:00:10 +08:00
import tomllib # pyright: ignore[reportMissingImports]
2023-03-29 11:57:33 +08:00
except ModuleNotFoundError : # pragma: py-lt-311
2024-10-26 15:36:01 +08:00
import tomli as tomllib # pyright: ignore[reportMissingImports]
2023-02-20 22:25:14 +08:00
2021-11-08 01:02:35 +08:00
2022-08-31 10:07:14 +08:00
def load_plugin ( module_path : Union [ str , Path ] ) - > Optional [ Plugin ] :
2022-01-22 15:23:07 +08:00
""" 加载单个插件,可以是本地插件或是通过 `pip` 安装的插件。
2021-11-08 01:02:35 +08:00
2022-01-12 18:31:12 +08:00
参数 :
2023-06-24 14:47:35 +08:00
module_path : 插件名称 ` path . to . your . plugin `
或插件路径 ` pathlib . Path ( path / to / your / plugin ) `
2021-11-08 01:02:35 +08:00
"""
2022-08-31 10:07:14 +08:00
module_path = (
path_to_module_name ( module_path )
if isinstance ( module_path , Path )
else module_path
)
2021-11-11 17:33:30 +08:00
manager = PluginManager ( [ module_path ] )
_managers . append ( manager )
return manager . load_plugin ( module_path )
2021-11-08 01:02:35 +08:00
2024-04-16 00:33:48 +08:00
def load_plugins ( * plugin_dir : str ) - > set [ Plugin ] :
2022-01-22 15:23:07 +08:00
""" 导入文件夹下多个插件,以 `_` 开头的插件不会被导入!
2021-11-08 01:02:35 +08:00
2022-01-12 18:31:12 +08:00
参数 :
2022-01-22 15:23:07 +08:00
plugin_dir : 文件夹路径
2021-11-08 01:02:35 +08:00
"""
2021-11-11 17:33:30 +08:00
manager = PluginManager ( search_path = plugin_dir )
_managers . append ( manager )
return manager . load_all_plugins ( )
2021-11-22 23:21:26 +08:00
def load_all_plugins (
module_path : Iterable [ str ] , plugin_dir : Iterable [ str ]
2024-04-16 00:33:48 +08:00
) - > set [ Plugin ] :
2022-01-22 15:23:07 +08:00
""" 导入指定列表中的插件以及指定目录下多个插件,以 `_` 开头的插件不会被导入!
2021-11-08 01:02:35 +08:00
2022-01-12 18:31:12 +08:00
参数 :
2022-01-12 19:10:29 +08:00
module_path : 指定插件集合
2022-01-22 15:23:07 +08:00
plugin_dir : 指定文件夹路径集合
2021-11-08 01:02:35 +08:00
"""
2021-11-11 17:33:30 +08:00
manager = PluginManager ( module_path , plugin_dir )
_managers . append ( manager )
return manager . load_all_plugins ( )
2021-11-08 01:02:35 +08:00
2024-04-16 00:33:48 +08:00
def load_from_json ( file_path : str , encoding : str = " utf-8 " ) - > set [ Plugin ] :
2023-06-24 14:47:35 +08:00
""" 导入指定 json 文件中的 `plugins` 以及 `plugin_dirs` 下多个插件。
以 ` _ ` 开头的插件不会被导入 !
2021-11-08 01:02:35 +08:00
2022-01-12 18:31:12 +08:00
参数 :
2022-01-12 19:10:29 +08:00
file_path : 指定 json 文件路径
encoding : 指定 json 文件编码
2022-01-22 15:23:07 +08:00
用法 :
` ` ` json title = plugins . json
{
" plugins " : [ " some_plugin " ] ,
" plugin_dirs " : [ " some_dir " ]
}
` ` `
` ` ` python
nonebot . load_from_json ( " plugins.json " )
` ` `
2022-01-12 19:10:29 +08:00
"""
2023-06-24 14:47:35 +08:00
with open ( file_path , encoding = encoding ) as f :
2021-11-08 01:02:35 +08:00
data = json . load ( f )
2022-08-16 10:03:37 +08:00
if not isinstance ( data , dict ) :
raise TypeError ( " json file must contains a dict! " )
2021-11-08 01:02:35 +08:00
plugins = data . get ( " plugins " )
plugin_dirs = data . get ( " plugin_dirs " )
assert isinstance ( plugins , list ) , " plugins must be a list of plugin name "
2021-11-22 23:21:26 +08:00
assert isinstance ( plugin_dirs , list ) , " plugin_dirs must be a list of directories "
2021-11-08 01:02:35 +08:00
return load_all_plugins ( set ( plugins ) , set ( plugin_dirs ) )
2024-04-16 00:33:48 +08:00
def load_from_toml ( file_path : str , encoding : str = " utf-8 " ) - > set [ Plugin ] :
2023-06-24 14:47:35 +08:00
""" 导入指定 toml 文件 `[tool.nonebot]` 中的
` plugins ` 以及 ` plugin_dirs ` 下多个插件 。
以 ` _ ` 开头的插件不会被导入 !
2021-11-08 01:02:35 +08:00
2022-01-12 18:31:12 +08:00
参数 :
2022-01-12 19:10:29 +08:00
file_path : 指定 toml 文件路径
encoding : 指定 toml 文件编码
2022-01-22 15:23:07 +08:00
用法 :
` ` ` toml title = pyproject . toml
[ tool . nonebot ]
plugins = [ " some_plugin " ]
plugin_dirs = [ " some_dir " ]
` ` `
` ` ` python
nonebot . load_from_toml ( " pyproject.toml " )
` ` `
2022-01-12 19:10:29 +08:00
"""
2023-06-24 14:47:35 +08:00
with open ( file_path , encoding = encoding ) as f :
2023-02-20 22:25:14 +08:00
data = tomllib . loads ( f . read ( ) )
2021-11-08 01:02:35 +08:00
2021-11-15 23:05:05 +08:00
nonebot_data = data . get ( " tool " , { } ) . get ( " nonebot " )
2022-08-16 10:03:37 +08:00
if nonebot_data is None :
raise ValueError ( " Cannot find ' [tool.nonebot] ' in given toml file! " )
if not isinstance ( nonebot_data , dict ) :
raise TypeError ( " ' [tool.nonebot] ' must be a Table! " )
2021-11-08 01:02:35 +08:00
plugins = nonebot_data . get ( " plugins " , [ ] )
plugin_dirs = nonebot_data . get ( " plugin_dirs " , [ ] )
assert isinstance ( plugins , list ) , " plugins must be a list of plugin name "
2021-11-22 23:21:26 +08:00
assert isinstance ( plugin_dirs , list ) , " plugin_dirs must be a list of directories "
2021-11-11 17:33:30 +08:00
return load_all_plugins ( plugins , plugin_dirs )
2021-11-08 01:02:35 +08:00
2022-01-10 22:52:10 +08:00
def load_builtin_plugin ( name : str ) - > Optional [ Plugin ] :
2022-01-22 15:23:07 +08:00
""" 导入 NoneBot 内置插件。
参数 :
name : 插件名称
2021-11-08 01:02:35 +08:00
"""
return load_plugin ( f " nonebot.plugins. { name } " )
2024-04-16 00:33:48 +08:00
def load_builtin_plugins ( * plugins : str ) - > set [ Plugin ] :
2022-01-22 15:23:07 +08:00
""" 导入多个 NoneBot 内置插件。
参数 :
plugins : 插件名称列表
2022-01-10 22:52:10 +08:00
"""
return load_all_plugins ( [ f " nonebot.plugins. { p } " for p in plugins ] , [ ] )
2022-01-26 15:06:53 +08:00
def _find_manager_by_name ( name : str ) - > Optional [ PluginManager ] :
for manager in reversed ( _managers ) :
2024-04-20 14:47:12 +08:00
if (
name in manager . controlled_modules
or name in manager . controlled_modules . values ( )
) :
2022-01-26 15:06:53 +08:00
return manager
2022-08-08 21:07:36 +08:00
def require ( name : str ) - > ModuleType :
2024-04-20 14:47:12 +08:00
""" 声明依赖插件。
2021-11-08 01:02:35 +08:00
2022-01-12 18:31:12 +08:00
参数 :
2024-04-20 14:47:12 +08:00
name : 插件模块名或插件标识符 , 仅在已声明插件的情况下可使用标识符 。
2021-11-08 01:02:35 +08:00
2022-01-12 18:45:35 +08:00
异常 :
2022-01-12 19:10:29 +08:00
RuntimeError : 插件无法加载
2021-11-08 01:02:35 +08:00
"""
2024-04-20 14:47:12 +08:00
if " . " in name :
# name is a module name
plugin = get_plugin ( _module_name_to_plugin_id ( name ) )
else :
# name is a plugin id or simple module name (equals to plugin id)
plugin = get_plugin ( name )
2023-05-22 00:00:50 +08:00
# if plugin not loaded
2024-04-20 14:47:12 +08:00
if plugin is None :
# plugin already declared, module name / plugin id
2022-09-09 11:52:57 +08:00
if manager := _find_manager_by_name ( name ) :
2022-01-26 15:06:53 +08:00
plugin = manager . load_plugin ( name )
2024-04-20 14:47:12 +08:00
2023-05-22 00:00:50 +08:00
# plugin not declared, try to declare and load it
2022-01-26 15:06:53 +08:00
else :
2024-04-20 14:47:12 +08:00
plugin = load_plugin ( name )
if plugin is None :
2022-09-09 11:52:57 +08:00
raise RuntimeError ( f ' Cannot load plugin " { name } " ! ' )
2022-08-08 21:07:36 +08:00
return plugin . module
2023-06-27 16:25:27 +08:00
2024-04-16 00:33:48 +08:00
def inherit_supported_adapters ( * names : str ) - > Optional [ set [ str ] ] :
2023-06-27 16:25:27 +08:00
""" 获取已加载插件的适配器支持状态集合。
如果传入了多个插件名称 , 返回值会自动取交集 。
参数 :
names : 插件名称列表 。
异常 :
RuntimeError : 插件未加载
ValueError : 插件缺少元数据
"""
2024-04-16 00:33:48 +08:00
final_supported : Optional [ set [ str ] ] = None
2023-06-27 16:25:27 +08:00
for name in names :
2024-04-20 14:47:12 +08:00
plugin = get_plugin ( _module_name_to_plugin_id ( name ) )
2023-06-27 16:25:27 +08:00
if plugin is None :
2024-04-20 14:47:12 +08:00
raise RuntimeError (
f ' Plugin " { name } " is not loaded! You should require it first. '
)
2023-06-27 16:25:27 +08:00
meta = plugin . metadata
if meta is None :
raise ValueError ( f ' Plugin " { name } " has no metadata! ' )
2024-04-18 18:50:11 +08:00
if ( raw := meta . supported_adapters ) is None :
2023-06-27 16:25:27 +08:00
continue
2024-04-18 18:50:11 +08:00
support = {
f " nonebot.adapters. { adapter [ 1 : ] } " if adapter . startswith ( " ~ " ) else adapter
for adapter in raw
}
2023-06-27 16:25:27 +08:00
final_supported = (
support if final_supported is None else ( final_supported & support )
)
2024-04-18 18:50:11 +08:00
return final_supported