💡 add docstrings

This commit is contained in:
yanyongyu 2022-01-22 15:23:07 +08:00
parent f9674da6ea
commit c4e204001e
22 changed files with 342 additions and 191 deletions

View File

@ -1,6 +1,10 @@
"""本模块定义了协议适配基类,各协议请继承以下基类。 """本模块定义了协议适配基类,各协议请继承以下基类。
使用 {ref}`nonebot.drivers.Driver.register_adapter` 注册适配器 使用 {ref}`nonebot.drivers.Driver.register_adapter` 注册适配器
FrontMatter:
sidebar_position: 0
description: nonebot.adapters 模块
""" """
from typing import Iterable from typing import Iterable

View File

@ -1,3 +1,8 @@
"""
FrontMatter:
sidebar_position: 1
description: nonebot.adapters._adapter 模块
"""
import abc import abc
from contextlib import asynccontextmanager from contextlib import asynccontextmanager
from typing import Any, Dict, AsyncGenerator from typing import Any, Dict, AsyncGenerator

View File

@ -1,3 +1,8 @@
"""
FrontMatter:
sidebar_position: 2
description: nonebot.adapters._bot 模块
"""
import abc import abc
import asyncio import asyncio
from functools import partial from functools import partial

View File

@ -1,3 +1,8 @@
"""
FrontMatter:
sidebar_position: 3
description: nonebot.adapters._event 模块
"""
import abc import abc
from pydantic import BaseModel from pydantic import BaseModel

View File

@ -1,3 +1,8 @@
"""
FrontMatter:
sidebar_position: 4
description: nonebot.adapters._message 模块
"""
import abc import abc
from copy import deepcopy from copy import deepcopy
from dataclasses import field, asdict, dataclass from dataclasses import field, asdict, dataclass

View File

@ -1,3 +1,8 @@
"""
FrontMatter:
sidebar_position: 5
description: nonebot.adapters._template 模块
"""
import inspect import inspect
import functools import functools
from string import Formatter from string import Formatter

View File

@ -1,4 +1,9 @@
"""本模块模块实现了依赖注入的定义与处理。""" """本模块模块实现了依赖注入的定义与处理。
FrontMatter:
sidebar_position: 0
description: nonebot.dependencies 模块
"""
import abc import abc
import inspect import inspect

View File

@ -1,3 +1,8 @@
"""
FrontMatter:
sidebar_position: 1
description: nonebot.dependencies.utils 模块
"""
import inspect import inspect
from typing import Any, Dict, Callable from typing import Any, Dict, Callable

View File

@ -1,7 +1,10 @@
""" """本模块定义了驱动适配器基类。
## 后端驱动适配基类
各驱动请继承以下基类 各驱动请继承以下基类
FrontMatter:
sidebar_position: 0
description: nonebot.drivers 模块
""" """
import abc import abc
@ -35,57 +38,38 @@ if TYPE_CHECKING:
class Driver(abc.ABC): class Driver(abc.ABC):
""" """Driver 基类。
Driver 基类
参数:
env: 包含环境信息的 Env 对象
config: 包含配置信息的 Config 对象
""" """
_adapters: Dict[str, "Adapter"] = {} _adapters: Dict[str, "Adapter"] = {}
""" """已注册的适配器列表"""
已注册的适配器列表
"""
_bot_connection_hook: Set[T_BotConnectionHook] = set() _bot_connection_hook: Set[T_BotConnectionHook] = set()
""" """Bot 连接建立时执行的函数"""
Bot 连接建立时执行的函数
"""
_bot_disconnection_hook: Set[T_BotDisconnectionHook] = set() _bot_disconnection_hook: Set[T_BotDisconnectionHook] = set()
""" """Bot 连接断开时执行的函数"""
Bot 连接断开时执行的函数
"""
def __init__(self, env: Env, config: Config): def __init__(self, env: Env, config: Config):
"""
参数:
env: 包含环境信息的 Env 对象
config: 包含配置信息的 Config 对象
"""
self.env: str = env.environment self.env: str = env.environment
""" """环境名称"""
环境名称
"""
self.config: Config = config self.config: Config = config
""" """全局配置对象"""
配置对象
"""
self._clients: Dict[str, "Bot"] = {} self._clients: Dict[str, "Bot"] = {}
"""
已连接的 Bot
"""
@property @property
def bots(self) -> Dict[str, "Bot"]: def bots(self) -> Dict[str, "Bot"]:
""" """获取当前所有已连接的 Bot"""
获取当前所有已连接的 Bot
"""
return self._clients return self._clients
def register_adapter(self, adapter: Type["Adapter"], **kwargs) -> None: def register_adapter(self, adapter: Type["Adapter"], **kwargs) -> None:
""" """注册一个协议适配器
注册一个协议适配器
参数: 参数:
name: 适配器名称用于在连接时进行识别 adapter: 适配器类
adapter: 适配器 Class kwargs: 其他传递给适配器的参数
**kwargs: 其他传递给适配器的参数
""" """
name = adapter.get_name() name = adapter.get_name()
if name in self._adapters: if name in self._adapters:
@ -121,36 +105,36 @@ class Driver(abc.ABC):
@abc.abstractmethod @abc.abstractmethod
def on_startup(self, func: Callable) -> Callable: def on_startup(self, func: Callable) -> Callable:
"""注册一个在驱动启动时运行的函数""" """注册一个在驱动器启动时执行的函数"""
raise NotImplementedError raise NotImplementedError
@abc.abstractmethod @abc.abstractmethod
def on_shutdown(self, func: Callable) -> Callable: def on_shutdown(self, func: Callable) -> Callable:
"""注册一个在驱动停止时运行的函数""" """注册一个在驱动器停止时执行的函数"""
raise NotImplementedError raise NotImplementedError
def on_bot_connect(self, func: T_BotConnectionHook) -> T_BotConnectionHook: def on_bot_connect(self, func: T_BotConnectionHook) -> T_BotConnectionHook:
""" """装饰一个函数使他在 bot 连接成功时执行。
装饰一个函数使他在 bot 通过 WebSocket 连接成功时执行
参数: 插槽函数参数:
bot: 当前连接上的 Bot 对象
- bot: 当前连接上的 Bot 对象
""" """
self._bot_connection_hook.add(func) self._bot_connection_hook.add(func)
return func return func
def on_bot_disconnect(self, func: T_BotDisconnectionHook) -> T_BotDisconnectionHook: def on_bot_disconnect(self, func: T_BotDisconnectionHook) -> T_BotDisconnectionHook:
""" """装饰一个函数使他在 bot 连接断开时执行。
装饰一个函数使他在 bot 通过 WebSocket 连接断开时执行
参数: 插槽函数参数:
bot: 当前连接上的 Bot 对象
- bot: 当前连接上的 Bot 对象
""" """
self._bot_disconnection_hook.add(func) self._bot_disconnection_hook.add(func)
return func return func
def _bot_connect(self, bot: "Bot") -> None: def _bot_connect(self, bot: "Bot") -> None:
""" WebSocket 连接成功后,调用该函数来注册 bot 对象""" """连接成功后,调用该函数来注册 bot 对象"""
if bot.self_id in self._clients: if bot.self_id in self._clients:
raise RuntimeError(f"Duplicate bot connection with id {bot.self_id}") raise RuntimeError(f"Duplicate bot connection with id {bot.self_id}")
self._clients[bot.self_id] = bot self._clients[bot.self_id] = bot
@ -169,7 +153,7 @@ class Driver(abc.ABC):
asyncio.create_task(_run_hook(bot)) asyncio.create_task(_run_hook(bot))
def _bot_disconnect(self, bot: "Bot") -> None: def _bot_disconnect(self, bot: "Bot") -> None:
""" WebSocket 连接断开后,调用该函数来注销 bot 对象""" """连接断开后,调用该函数来注销 bot 对象"""
if bot.self_id in self._clients: if bot.self_id in self._clients:
del self._clients[bot.self_id] del self._clients[bot.self_id]
@ -188,32 +172,33 @@ class Driver(abc.ABC):
class ForwardMixin(abc.ABC): class ForwardMixin(abc.ABC):
"""客户端混入基类。"""
@property @property
@abc.abstractmethod @abc.abstractmethod
def type(self) -> str: def type(self) -> str:
"""客户端驱动类型名称"""
raise NotImplementedError raise NotImplementedError
@abc.abstractmethod @abc.abstractmethod
async def request(self, setup: Request) -> Response: async def request(self, setup: Request) -> Response:
"""发送一个 HTTP 请求"""
raise NotImplementedError raise NotImplementedError
@abc.abstractmethod @abc.abstractmethod
@asynccontextmanager @asynccontextmanager
async def websocket(self, setup: Request) -> AsyncGenerator[WebSocket, None]: async def websocket(self, setup: Request) -> AsyncGenerator[WebSocket, None]:
"""发起一个 WebSocket 连接"""
raise NotImplementedError raise NotImplementedError
yield # used for static type checking's generator detection yield # used for static type checking's generator detection
class ForwardDriver(Driver, ForwardMixin): class ForwardDriver(Driver, ForwardMixin):
""" """客户端基类。将客户端框架封装,以满足适配器使用。"""
Forward Driver 基类将客户端框架封装以满足适配器使用
"""
class ReverseDriver(Driver): class ReverseDriver(Driver):
""" """服务端基类。将后端框架封装,以满足适配器使用。"""
Reverse Driver 基类将后端框架封装以满足适配器使用
"""
@property @property
@abc.abstractmethod @abc.abstractmethod
@ -229,20 +214,26 @@ class ReverseDriver(Driver):
@abc.abstractmethod @abc.abstractmethod
def setup_http_server(self, setup: "HTTPServerSetup") -> None: def setup_http_server(self, setup: "HTTPServerSetup") -> None:
"""设置一个 HTTP 服务器路由配置"""
raise NotImplementedError raise NotImplementedError
@abc.abstractmethod @abc.abstractmethod
def setup_websocket_server(self, setup: "WebSocketServerSetup") -> None: def setup_websocket_server(self, setup: "WebSocketServerSetup") -> None:
"""设置一个 WebSocket 服务器路由配置"""
raise NotImplementedError raise NotImplementedError
def combine_driver(driver: Type[Driver], *mixins: Type[ForwardMixin]) -> Type[Driver]: def combine_driver(driver: Type[Driver], *mixins: Type[ForwardMixin]) -> Type[Driver]:
"""将一个驱动器和多个混入类合并。"""
# check first # check first
assert issubclass(driver, Driver), "`driver` must be subclass of Driver" assert issubclass(driver, Driver), "`driver` must be subclass of Driver"
assert all( assert all(
map(lambda m: issubclass(m, ForwardMixin), mixins) map(lambda m: issubclass(m, ForwardMixin), mixins)
), "`mixins` must be subclass of ForwardMixin" ), "`mixins` must be subclass of ForwardMixin"
if not mixins:
return driver
class CombinedDriver(*mixins, driver, ForwardDriver): # type: ignore class CombinedDriver(*mixins, driver, ForwardDriver): # type: ignore
@property @property
def type(self) -> str: def type(self) -> str:
@ -257,6 +248,8 @@ def combine_driver(driver: Type[Driver], *mixins: Type[ForwardMixin]) -> Type[Dr
@dataclass @dataclass
class HTTPServerSetup: class HTTPServerSetup:
"""HTTP 服务器路由配置。"""
path: URL # path should not be absolute, check it by URL.is_absolute() == False path: URL # path should not be absolute, check it by URL.is_absolute() == False
method: str method: str
name: str name: str
@ -265,6 +258,8 @@ class HTTPServerSetup:
@dataclass @dataclass
class WebSocketServerSetup: class WebSocketServerSetup:
"""WebSocket 服务器路由配置。"""
path: URL # path should not be absolute, check it by URL.is_absolute() == False path: URL # path should not be absolute, check it by URL.is_absolute() == False
name: str name: str
handle_func: Callable[[WebSocket], Awaitable[Any]] handle_func: Callable[[WebSocket], Awaitable[Any]]

View File

@ -1,10 +1,21 @@
""" """[AIOHTTP](https://aiohttp.readthedocs.io/en/stable/) 驱动适配器。
## AIOHTTP 驱动适配
```bash
nb driver install aiohttp
# 或者
pip install nonebot2[aiohttp]
```
:::tip 提示
本驱动仅支持客户端连接 本驱动仅支持客户端连接
:::
FrontMatter:
sidebar_position: 2
description: nonebot.drivers.aiohttp 模块
""" """
from typing import AsyncGenerator from typing import Type, AsyncGenerator
from contextlib import asynccontextmanager from contextlib import asynccontextmanager
from nonebot.typing import overrides from nonebot.typing import overrides
@ -12,7 +23,12 @@ from nonebot.drivers import Request, Response
from nonebot.exception import WebSocketClosed from nonebot.exception import WebSocketClosed
from nonebot.drivers._block_driver import BlockDriver from nonebot.drivers._block_driver import BlockDriver
from nonebot.drivers import WebSocket as BaseWebSocket from nonebot.drivers import WebSocket as BaseWebSocket
from nonebot.drivers import HTTPVersion, ForwardMixin, combine_driver from nonebot.drivers import (
HTTPVersion,
ForwardMixin,
ForwardDriver,
combine_driver,
)
try: try:
import aiohttp import aiohttp
@ -23,6 +39,8 @@ except ImportError:
class Mixin(ForwardMixin): class Mixin(ForwardMixin):
"""AIOHTTP Mixin"""
@property @property
@overrides(ForwardMixin) @overrides(ForwardMixin)
def type(self) -> str: def type(self) -> str:
@ -84,6 +102,8 @@ class Mixin(ForwardMixin):
class WebSocket(BaseWebSocket): class WebSocket(BaseWebSocket):
"""AIOHTTP Websocket Wrapper"""
def __init__( def __init__(
self, self,
*, *,
@ -138,4 +158,5 @@ class WebSocket(BaseWebSocket):
await self.websocket.send_bytes(data) await self.websocket.send_bytes(data)
Driver = combine_driver(BlockDriver, Mixin) Driver: Type[ForwardDriver] = combine_driver(BlockDriver, Mixin) # type: ignore
"""AIOHTTP Driver"""

View File

@ -1,9 +1,12 @@
""" """[FastAPI](https://fastapi.tiangolo.com/) 驱动适配
## FastAPI 驱动适配
本驱动同时支持服务端以及客户端连接 :::tip 提示
本驱动仅支持服务端连接
:::
后端使用方法请参考: [`FastAPI 文档`](https://fastapi.tiangolo.com/) FrontMatter:
sidebar_position: 1
description: nonebot.drivers.fastapi 模块
""" """
import logging import logging
@ -39,53 +42,33 @@ def catch_closed(func):
class Config(BaseSettings): class Config(BaseSettings):
""" """FastAPI 驱动框架设置,详情参考 FastAPI 文档"""
FastAPI 驱动框架设置详情参考 FastAPI 文档
"""
fastapi_openapi_url: Optional[str] = None fastapi_openapi_url: Optional[str] = None
""" """`openapi.json` 地址,默认为 `None` 即关闭"""
`openapi.json` 地址默认为 `None` 即关闭
"""
fastapi_docs_url: Optional[str] = None fastapi_docs_url: Optional[str] = None
""" """`swagger` 地址,默认为 `None` 即关闭"""
`swagger` 地址默认为 `None` 即关闭
"""
fastapi_redoc_url: Optional[str] = None fastapi_redoc_url: Optional[str] = None
""" """`redoc` 地址,默认为 `None` 即关闭"""
`redoc` 地址默认为 `None` 即关闭
"""
fastapi_include_adapter_schema: bool = True fastapi_include_adapter_schema: bool = True
""" """是否包含适配器路由的 schema默认为 `True`"""
是否包含适配器路由的 schema默认为 `True`
"""
fastapi_reload: bool = False fastapi_reload: bool = False
""" """开启/关闭冷重载"""
开启/关闭冷重载
"""
fastapi_reload_dirs: Optional[List[str]] = None fastapi_reload_dirs: Optional[List[str]] = None
""" """重载监控文件夹列表,默认为 uvicorn 默认值"""
重载监控文件夹列表默认为 uvicorn 默认值
"""
fastapi_reload_delay: Optional[float] = None fastapi_reload_delay: Optional[float] = None
""" """重载延迟,默认为 uvicorn 默认值"""
重载延迟默认为 uvicorn 默认值
"""
fastapi_reload_includes: Optional[List[str]] = None fastapi_reload_includes: Optional[List[str]] = None
""" """要监听的文件列表,支持 glob pattern默认为 uvicorn 默认值"""
要监听的文件列表支持 glob pattern默认为 uvicorn 默认值
"""
fastapi_reload_excludes: Optional[List[str]] = None fastapi_reload_excludes: Optional[List[str]] = None
""" """不要监听的文件列表,支持 glob pattern默认为 uvicorn 默认值"""
不要监听的文件列表支持 glob pattern默认为 uvicorn 默认值
"""
class Config: class Config:
extra = "ignore" extra = "ignore"
class Driver(ReverseDriver): class Driver(ReverseDriver):
"""FastAPI 驱动框架。包含反向 Server 功能。""" """FastAPI 驱动框架。"""
def __init__(self, env: Env, config: NoneBotConfig): def __init__(self, env: Env, config: NoneBotConfig):
super(Driver, self).__init__(env, config) super(Driver, self).__init__(env, config)
@ -254,6 +237,8 @@ class Driver(ReverseDriver):
class FastAPIWebSocket(BaseWebSocket): class FastAPIWebSocket(BaseWebSocket):
"""FastAPI WebSocket Wrapper"""
@overrides(BaseWebSocket) @overrides(BaseWebSocket)
def __init__(self, *, request: BaseRequest, websocket: WebSocket): def __init__(self, *, request: BaseRequest, websocket: WebSocket):
super().__init__(request=request) super().__init__(request=request)
@ -294,3 +279,6 @@ class FastAPIWebSocket(BaseWebSocket):
@overrides(BaseWebSocket) @overrides(BaseWebSocket)
async def send_bytes(self, data: bytes) -> None: async def send_bytes(self, data: bytes) -> None:
await self.websocket.send({"type": "websocket.send", "bytes": data}) await self.websocket.send({"type": "websocket.send", "bytes": data})
__autodoc__ = {"catch_closed": False}

View File

@ -1,4 +1,20 @@
from typing import AsyncGenerator """[HTTPX](https://www.python-httpx.org/) 驱动适配
```bash
nb driver install httpx
# 或者
pip install nonebot2[httpx]
```
:::tip 提示
本驱动仅支持客户端 HTTP 连接
:::
FrontMatter:
sidebar_position: 3
description: nonebot.drivers.httpx 模块
"""
from typing import Type, AsyncGenerator
from contextlib import asynccontextmanager from contextlib import asynccontextmanager
from nonebot.typing import overrides from nonebot.typing import overrides
@ -9,6 +25,7 @@ from nonebot.drivers import (
WebSocket, WebSocket,
HTTPVersion, HTTPVersion,
ForwardMixin, ForwardMixin,
ForwardDriver,
combine_driver, combine_driver,
) )
@ -21,6 +38,8 @@ except ImportError:
class Mixin(ForwardMixin): class Mixin(ForwardMixin):
"""HTTPX Mixin"""
@property @property
@overrides(ForwardMixin) @overrides(ForwardMixin)
def type(self) -> str: def type(self) -> str:
@ -57,4 +76,5 @@ class Mixin(ForwardMixin):
yield ws yield ws
Driver = combine_driver(BlockDriver, Mixin) Driver: Type[ForwardDriver] = combine_driver(BlockDriver, Mixin) # type: ignore
"""HTTPX Driver"""

View File

@ -1,7 +1,18 @@
""" """[Quart](https://pgjones.gitlab.io/quart/index.html) 驱动适配
## Quart 驱动适配
后端使用方法请参考: [`Quart 文档`](https://pgjones.gitlab.io/quart/index.html) ```bash
nb driver install quart
# 或者
pip install nonebot2[quart]
```
:::tip 提示
本驱动仅支持服务端连接
:::
FrontMatter:
sidebar_position: 5
description: nonebot.drivers.quart 模块
""" """
import asyncio import asyncio
@ -47,39 +58,25 @@ def catch_closed(func):
class Config(BaseSettings): class Config(BaseSettings):
""" """Quart 驱动框架设置"""
Quart 驱动框架设置
"""
quart_reload: bool = False quart_reload: bool = False
""" """开启/关闭冷重载"""
开启/关闭冷重载
"""
quart_reload_dirs: Optional[List[str]] = None quart_reload_dirs: Optional[List[str]] = None
""" """重载监控文件夹列表,默认为 uvicorn 默认值"""
重载监控文件夹列表默认为 uvicorn 默认值
"""
quart_reload_delay: Optional[float] = None quart_reload_delay: Optional[float] = None
""" """重载延迟,默认为 uvicorn 默认值"""
重载延迟默认为 uvicorn 默认值
"""
quart_reload_includes: Optional[List[str]] = None quart_reload_includes: Optional[List[str]] = None
""" """要监听的文件列表,支持 glob pattern默认为 uvicorn 默认值"""
要监听的文件列表支持 glob pattern默认为 uvicorn 默认值
"""
quart_reload_excludes: Optional[List[str]] = None quart_reload_excludes: Optional[List[str]] = None
""" """不要监听的文件列表,支持 glob pattern默认为 uvicorn 默认值"""
不要监听的文件列表支持 glob pattern默认为 uvicorn 默认值
"""
class Config: class Config:
extra = "ignore" extra = "ignore"
class Driver(ReverseDriver): class Driver(ReverseDriver):
""" """Quart 驱动框架"""
Quart 驱动框架
"""
def __init__(self, env: Env, config: NoneBotConfig): def __init__(self, env: Env, config: NoneBotConfig):
super().__init__(env, config) super().__init__(env, config)
@ -239,6 +236,8 @@ class Driver(ReverseDriver):
class WebSocket(BaseWebSocket): class WebSocket(BaseWebSocket):
"""Quart WebSocket Wrapper"""
def __init__(self, *, request: BaseRequest, websocket: QuartWebSocket): def __init__(self, *, request: BaseRequest, websocket: QuartWebSocket):
super().__init__(request=request) super().__init__(request=request)
self.websocket = websocket self.websocket = websocket
@ -280,3 +279,6 @@ class WebSocket(BaseWebSocket):
@overrides(BaseWebSocket) @overrides(BaseWebSocket)
async def send_bytes(self, data: bytes): async def send_bytes(self, data: bytes):
await self.websocket.send(data) await self.websocket.send(data)
__autodoc__ = {"catch_closed": False}

View File

@ -1,6 +1,22 @@
"""[websockets](https://websockets.readthedocs.io/) 驱动适配
```bash
nb driver install websockets
# 或者
pip install nonebot2[websockets]
```
:::tip 提示
本驱动仅支持客户端 WebSocket 连接
:::
FrontMatter:
sidebar_position: 4
description: nonebot.drivers.websockets 模块
"""
import logging import logging
from functools import wraps from functools import wraps
from typing import AsyncGenerator from typing import Type, AsyncGenerator
from contextlib import asynccontextmanager from contextlib import asynccontextmanager
from nonebot.typing import overrides from nonebot.typing import overrides
@ -9,7 +25,7 @@ from nonebot.drivers import Request, Response
from nonebot.exception import WebSocketClosed from nonebot.exception import WebSocketClosed
from nonebot.drivers._block_driver import BlockDriver from nonebot.drivers._block_driver import BlockDriver
from nonebot.drivers import WebSocket as BaseWebSocket from nonebot.drivers import WebSocket as BaseWebSocket
from nonebot.drivers import ForwardMixin, combine_driver from nonebot.drivers import ForwardMixin, ForwardDriver, combine_driver
try: try:
from websockets.exceptions import ConnectionClosed from websockets.exceptions import ConnectionClosed
@ -38,6 +54,8 @@ def catch_closed(func):
class Mixin(ForwardMixin): class Mixin(ForwardMixin):
"""Websockets Mixin"""
@property @property
@overrides(ForwardMixin) @overrides(ForwardMixin)
def type(self) -> str: def type(self) -> str:
@ -60,6 +78,8 @@ class Mixin(ForwardMixin):
class WebSocket(BaseWebSocket): class WebSocket(BaseWebSocket):
"""Websockets WebSocket Wrapper"""
@overrides(BaseWebSocket) @overrides(BaseWebSocket)
def __init__(self, *, request: Request, websocket: WebSocketClientProtocol): def __init__(self, *, request: Request, websocket: WebSocketClientProtocol):
super().__init__(request=request) super().__init__(request=request)
@ -103,4 +123,5 @@ class WebSocket(BaseWebSocket):
await self.websocket.send(data) await self.websocket.send(data)
Driver = combine_driver(BlockDriver, Mixin) Driver: Type[ForwardDriver] = combine_driver(BlockDriver, Mixin) # type: ignore
"""Websockets Driver"""

View File

@ -1,7 +1,37 @@
""" """本模块为 NoneBot 插件开发提供便携的定义函数。
## 插件
NoneBot 插件开发提供便携的定义函数 ## 快捷导入
为方便使用本模块从子模块导入了部分内容以下内容可以直接通过本模块导入:
- `on` => {ref}``on` <nonebot.plugin.on.on>`
- `on_metaevent` => {ref}``on_metaevent` <nonebot.plugin.on.on_metaevent>`
- `on_message` => {ref}``on_message` <nonebot.plugin.on.on_message>`
- `on_notice` => {ref}``on_notice` <nonebot.plugin.on.on_notice>`
- `on_request` => {ref}``on_request` <nonebot.plugin.on.on_request>`
- `on_startswith` => {ref}``on_startswith` <nonebot.plugin.on.on_startswith>`
- `on_endswith` => {ref}``on_endswith` <nonebot.plugin.on.on_endswith>`
- `on_keyword` => {ref}``on_keyword` <nonebot.plugin.on.on_keyword>`
- `on_command` => {ref}``on_command` <nonebot.plugin.on.on_command>`
- `on_shell_command` => {ref}``on_shell_command` <nonebot.plugin.on.on_shell_command>`
- `on_regex` => {ref}``on_regex` <nonebot.plugin.on.on_regex>`
- `CommandGroup` => {ref}``CommandGroup` <nonebot.plugin.on.CommandGroup>`
- `Matchergroup` => {ref}``MatcherGroup` <nonebot.plugin.on.MatcherGroup>`
- `load_plugin` => {ref}``load_plugin` <nonebot.plugin.load.load_plugin>`
- `load_plugins` => {ref}``load_plugins` <nonebot.plugin.load.load_plugins>`
- `load_all_plugins` => {ref}``load_all_plugins` <nonebot.plugin.load.load_all_plugins>`
- `load_from_json` => {ref}``load_from_json` <nonebot.plugin.load.load_from_json>`
- `load_from_toml` => {ref}``load_from_toml` <nonebot.plugin.load.load_from_toml>`
- `load_builtin_plugin` => {ref}``load_builtin_plugin` <nonebot.plugin.load.load_builtin_plugin>`
- `load_builtin_plugins` => {ref}``load_builtin_plugins` <nonebot.plugin.load.load_builtin_plugins>`
- `get_plugin` => {ref}``get_plugin` <nonebot.plugin.plugin.get_plugin>`
- `get_loaded_plugins` => {ref}``get_loaded_plugins` <nonebot.plugin.plugin.get_loaded_plugins>`
- `export` => {ref}``export` <nonebot.plugin.export.export>`
- `require` => {ref}``require` <nonebot.plugin.load.require>`
FrontMatter:
sidebar_position: 0
description: nonebot.plugin 模块
""" """
from typing import List, Optional from typing import List, Optional

View File

@ -1,9 +1,17 @@
"""本模块定义了插件导出的内容对象。
在新版插件系统中推荐优先使用直接 import 所需要的插件内容
FrontMatter:
sidebar_position: 4
description: nonebot.plugin.export 模块
"""
from . import _current_plugin from . import _current_plugin
class Export(dict): class Export(dict):
""" """插件导出内容以使得其他插件可以获得。
插件导出内容以使得其他插件可以获得
用法: 用法:
```python ```python
@ -42,9 +50,7 @@ class Export(dict):
def export() -> Export: def export() -> Export:
""" """获取当前插件的导出内容对象"""
获取插件的导出内容对象
"""
plugin = _current_plugin.get() plugin = _current_plugin.get()
if not plugin: if not plugin:
raise RuntimeError("Export outside of the plugin!") raise RuntimeError("Export outside of the plugin!")

View File

@ -1,3 +1,9 @@
"""本模块定义插件加载接口。
FrontMatter:
sidebar_position: 1
description: nonebot.plugin.load 模块
"""
import json import json
import warnings import warnings
from typing import Set, Iterable, Optional from typing import Set, Iterable, Optional
@ -11,8 +17,7 @@ from .plugin import Plugin, get_plugin
def load_plugin(module_path: str) -> Optional[Plugin]: def load_plugin(module_path: str) -> Optional[Plugin]:
""" """加载单个插件,可以是本地插件或是通过 `pip` 安装的插件。
使用 `PluginManager` 加载单个插件可以是本地插件或是通过 `pip` 安装的插件
参数: 参数:
module_path: 插件名称 `path.to.your.plugin` module_path: 插件名称 `path.to.your.plugin`
@ -24,11 +29,10 @@ def load_plugin(module_path: str) -> Optional[Plugin]:
def load_plugins(*plugin_dir: str) -> Set[Plugin]: def load_plugins(*plugin_dir: str) -> Set[Plugin]:
""" """导入文件夹下多个插件,以 `_` 开头的插件不会被导入!
导入目录下多个插件 `_` 开头的插件不会被导入
参数: 参数:
plugin_dir: 插件路径 plugin_dir: 文件夹路径
""" """
manager = PluginManager(search_path=plugin_dir) manager = PluginManager(search_path=plugin_dir)
_managers.append(manager) _managers.append(manager)
@ -38,12 +42,11 @@ def load_plugins(*plugin_dir: str) -> Set[Plugin]:
def load_all_plugins( def load_all_plugins(
module_path: Iterable[str], plugin_dir: Iterable[str] module_path: Iterable[str], plugin_dir: Iterable[str]
) -> Set[Plugin]: ) -> Set[Plugin]:
""" """导入指定列表中的插件以及指定目录下多个插件,以 `_` 开头的插件不会被导入!
导入指定列表中的插件以及指定目录下多个插件 `_` 开头的插件不会被导入
参数: 参数:
module_path: 指定插件集合 module_path: 指定插件集合
plugin_dir: 指定插件路径集合 plugin_dir: 指定文件夹路径集合
""" """
manager = PluginManager(module_path, plugin_dir) manager = PluginManager(module_path, plugin_dir)
_managers.append(manager) _managers.append(manager)
@ -51,12 +54,23 @@ def load_all_plugins(
def load_from_json(file_path: str, encoding: str = "utf-8") -> Set[Plugin]: def load_from_json(file_path: str, encoding: str = "utf-8") -> Set[Plugin]:
""" """导入指定 json 文件中的 `plugins` 以及 `plugin_dirs` 下多个插件,以 `_` 开头的插件不会被导入!
导入指定 json 文件中的 `plugins` 以及 `plugin_dirs` 下多个插件 `_` 开头的插件不会被导入
参数: 参数:
file_path: 指定 json 文件路径 file_path: 指定 json 文件路径
encoding: 指定 json 文件编码 encoding: 指定 json 文件编码
用法:
```json title=plugins.json
{
"plugins": ["some_plugin"],
"plugin_dirs": ["some_dir"]
}
```
```python
nonebot.load_from_json("plugins.json")
```
""" """
with open(file_path, "r", encoding=encoding) as f: with open(file_path, "r", encoding=encoding) as f:
data = json.load(f) data = json.load(f)
@ -68,13 +82,22 @@ def load_from_json(file_path: str, encoding: str = "utf-8") -> Set[Plugin]:
def load_from_toml(file_path: str, encoding: str = "utf-8") -> Set[Plugin]: def load_from_toml(file_path: str, encoding: str = "utf-8") -> Set[Plugin]:
""" """导入指定 toml 文件 `[tool.nonebot]` 中的 `plugins` 以及 `plugin_dirs` 下多个插件,以 `_` 开头的插件不会被导入!
导入指定 toml 文件 `[tool.nonebot]` 中的 `plugins` 以及 `plugin_dirs` 下多个插件
`_` 开头的插件不会被导入
参数: 参数:
file_path: 指定 toml 文件路径 file_path: 指定 toml 文件路径
encoding: 指定 toml 文件编码 encoding: 指定 toml 文件编码
用法:
```toml title=pyproject.toml
[tool.nonebot]
plugins = ["some_plugin"]
plugin_dirs = ["some_dir"]
```
```python
nonebot.load_from_toml("pyproject.toml")
```
""" """
with open(file_path, "r", encoding=encoding) as f: with open(file_path, "r", encoding=encoding) as f:
data = tomlkit.parse(f.read()) # type: ignore data = tomlkit.parse(f.read()) # type: ignore
@ -97,25 +120,30 @@ def load_from_toml(file_path: str, encoding: str = "utf-8") -> Set[Plugin]:
def load_builtin_plugin(name: str) -> Optional[Plugin]: def load_builtin_plugin(name: str) -> Optional[Plugin]:
""" """导入 NoneBot 内置插件。
导入 NoneBot 内置插件
参数:
name: 插件名称
""" """
return load_plugin(f"nonebot.plugins.{name}") return load_plugin(f"nonebot.plugins.{name}")
def load_builtin_plugins(*plugins) -> Set[Plugin]: def load_builtin_plugins(*plugins) -> Set[Plugin]:
""" """导入多个 NoneBot 内置插件。
导入多个 NoneBot 内置插件
参数:
plugins: 插件名称列表
""" """
return load_all_plugins([f"nonebot.plugins.{p}" for p in plugins], []) return load_all_plugins([f"nonebot.plugins.{p}" for p in plugins], [])
def require(name: str) -> Export: def require(name: str) -> Export:
""" """获取一个插件的导出内容。
获取一个插件的导出内容
如果为 `load_plugins` 文件夹导入的插件则为文件()
参数: 参数:
name: 插件名`load_plugin` 参数一致如果为 `load_plugins` 导入的插件则为文件() name: 插件名{ref}`nonebot.plugin.plugin.Plugin.name`
异常: 异常:
RuntimeError: 插件无法加载 RuntimeError: 插件无法加载

View File

@ -1,3 +1,11 @@
"""本模块实现插件加载流程。
参考: [import hooks](https://docs.python.org/3/reference/import.html#import-hooks), [PEP302](https://www.python.org/dev/peps/pep-0302/)
FrontMatter:
sidebar_position: 5
description: nonebot.plugin.manager 模块
"""
import sys import sys
import pkgutil import pkgutil
import importlib import importlib

View File

@ -1,3 +1,9 @@
"""本模块定义事件响应器便携定义函数。
FrontMatter:
sidebar_position: 2
description: nonebot.plugin.on 模块
"""
import re import re
import sys import sys
import inspect import inspect

View File

@ -1,3 +1,9 @@
"""本模块定义插件对象。
FrontMatter:
sidebar_position: 3
description: nonebot.plugin.plugin 模块
"""
from types import ModuleType from types import ModuleType
from dataclasses import field, dataclass from dataclasses import field, dataclass
from typing import TYPE_CHECKING, Set, Dict, Type, Optional from typing import TYPE_CHECKING, Set, Dict, Type, Optional
@ -10,9 +16,7 @@ if TYPE_CHECKING:
from .manager import PluginManager from .manager import PluginManager
plugins: Dict[str, "Plugin"] = {} plugins: Dict[str, "Plugin"] = {}
""" """已加载的插件"""
已加载的插件
"""
@dataclass(eq=False) @dataclass(eq=False)
@ -20,53 +24,36 @@ class Plugin(object):
"""存储插件信息""" """存储插件信息"""
name: str name: str
""" """插件名称,使用 文件/文件夹 名称作为插件名"""
插件名称使用 文件/文件夹 名称作为插件名
"""
module: ModuleType module: ModuleType
""" """插件模块对象"""
插件模块对象
"""
module_name: str module_name: str
""" """点分割模块路径"""
点分割模块路径
"""
manager: "PluginManager" manager: "PluginManager"
""" """导入该插件的插件管理器"""
导入该插件的插件管理器
"""
export: Export = field(default_factory=Export) export: Export = field(default_factory=Export)
""" """插件内定义的导出内容"""
插件内定义的导出内容
"""
matcher: Set[Type[Matcher]] = field(default_factory=set) matcher: Set[Type[Matcher]] = field(default_factory=set)
""" """插件内定义的 `Matcher`"""
插件内定义的 `Matcher`
"""
parent_plugin: Optional["Plugin"] = None parent_plugin: Optional["Plugin"] = None
""" """父插件"""
父插件
"""
sub_plugins: Set["Plugin"] = field(default_factory=set) sub_plugins: Set["Plugin"] = field(default_factory=set)
""" """子插件集合"""
子插件集合
"""
def get_plugin(name: str) -> Optional[Plugin]: def get_plugin(name: str) -> Optional[Plugin]:
""" """获取已经导入的某个插件。
获取当前导入的某个插件
如果为 `load_plugins` 文件夹导入的插件则为文件()
参数: 参数:
name: 插件名`load_plugin` 参数一致如果为 `load_plugins` 导入的插件则为文件() name: 插件名{ref}`nonebot.plugin.plugin.Plugin.name`
""" """
return plugins.get(name) return plugins.get(name)
def get_loaded_plugins() -> Set[Plugin]: def get_loaded_plugins() -> Set[Plugin]:
""" """获取当前已导入的所有插件。"""
获取当前已导入的所有插件
"""
return set(plugins.values()) return set(plugins.values())

8
poetry.lock generated
View File

@ -721,7 +721,7 @@ python-versions = "*"
[[package]] [[package]]
name = "pyparsing" name = "pyparsing"
version = "3.0.6" version = "3.0.7"
description = "Python parsing module" description = "Python parsing module"
category = "dev" category = "dev"
optional = false optional = false
@ -1109,7 +1109,7 @@ websockets = ["websockets"]
[metadata] [metadata]
lock-version = "1.1" lock-version = "1.1"
python-versions = "^3.7.3" python-versions = "^3.7.3"
content-hash = "0e101f22381597b968f6c51419c1aa284243183f7298ac7837f125510e5a7a53" content-hash = "b18b98c20de891dfd80f15ac896f13ed529264dd4913ed1ca9cccff3fee379ba"
[metadata.files] [metadata.files]
aiodns = [ aiodns = [
@ -1848,8 +1848,8 @@ pygtrie = [
{file = "pygtrie-2.4.2.tar.gz", hash = "sha256:43205559d28863358dbbf25045029f58e2ab357317a59b11f11ade278ac64692"}, {file = "pygtrie-2.4.2.tar.gz", hash = "sha256:43205559d28863358dbbf25045029f58e2ab357317a59b11f11ade278ac64692"},
] ]
pyparsing = [ pyparsing = [
{file = "pyparsing-3.0.6-py3-none-any.whl", hash = "sha256:04ff808a5b90911829c55c4e26f75fa5ca8a2f5f36aa3a51f68e27033341d3e4"}, {file = "pyparsing-3.0.7-py3-none-any.whl", hash = "sha256:a6c06a88f252e6c322f65faf8f418b16213b51bdfaece0524c1c1bc30c63c484"},
{file = "pyparsing-3.0.6.tar.gz", hash = "sha256:d9bdec0013ef1eb5a84ab39a3b3868911598afa494f5faa038647101504e2b81"}, {file = "pyparsing-3.0.7.tar.gz", hash = "sha256:18ee9022775d270c55187733956460083db60b37d0d0fb357445f3094eed3eea"},
] ]
pytest = [ pytest = [
{file = "pytest-6.2.5-py3-none-any.whl", hash = "sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134"}, {file = "pytest-6.2.5-py3-none-any.whl", hash = "sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134"},

View File

@ -30,7 +30,7 @@ tomlkit = "^0.7.0"
fastapi = "^0.70.0" fastapi = "^0.70.0"
typing-extensions = ">=3.10.0,<5.0.0" typing-extensions = ">=3.10.0,<5.0.0"
Quart = { version = "^0.16.0", optional = true } Quart = { version = "^0.16.0", optional = true }
websockets = { version=">=9.1", optional = true } websockets = { version="^10.0", optional = true }
pydantic = { version = "~1.9.0", extras = ["dotenv"] } pydantic = { version = "~1.9.0", extras = ["dotenv"] }
uvicorn = { version = "^0.15.0", extras = ["standard"] } uvicorn = { version = "^0.15.0", extras = ["standard"] }
aiohttp = { version = "^3.7.4", extras = ["speedups"], optional = true } aiohttp = { version = "^3.7.4", extras = ["speedups"], optional = true }