Skip to content

Commit

Permalink
feat(plugin): 支持在运行时读取插件类的泛型参数以获取初始化状态和插件配置类 (#98)
Browse files Browse the repository at this point in the history
  • Loading branch information
st1020 authored Oct 28, 2023
1 parent b409101 commit c20e7bb
Show file tree
Hide file tree
Showing 7 changed files with 471 additions and 307 deletions.
4 changes: 1 addition & 3 deletions alicebot/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -499,9 +499,7 @@ async def _handle_event(self, current_event: Optional[Event[Any]] = None) -> Non
Event: current_event,
},
)
if _plugin.name not in self.plugin_state and hasattr(
_plugin, "__init_state__"
):
if _plugin.name not in self.plugin_state:
plugin_state = _plugin.__init_state__()
if plugin_state is not None:
self.plugin_state[_plugin.name] = plugin_state
Expand Down
42 changes: 34 additions & 8 deletions alicebot/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
所有 AliceBot 插件的基类。所有用户编写的插件必须继承自 `Plugin` 类。
"""
import inspect
from abc import ABC, abstractmethod
from enum import Enum
from typing import (
Expand All @@ -11,10 +12,14 @@
Generic,
NoReturn,
Optional,
Tuple,
Type,
cast,
final,
)
from typing_extensions import Annotated, get_args, get_origin

from alicebot.config import ConfigModel
from alicebot.dependencies import Depends
from alicebot.event import Event
from alicebot.exceptions import SkipException, StopException
Expand Down Expand Up @@ -59,17 +64,14 @@ class Plugin(ABC, Generic[EventT, StateT, ConfigT]):

if TYPE_CHECKING:
event: EventT

def __init_state__(self) -> StateT:
"""初始化插件状态。"""
... # pylint: disable=unnecessary-ellipsis

else:
event: EventT = Depends(Event)
event = Depends(Event)

def __init_state__(self) -> Optional[StateT]:
"""初始化插件状态。"""

def __init_subclass__(
cls,
*_args: Any,
config: Optional[Type[ConfigT]] = None,
init_state: Optional[StateT] = None,
**_kwargs: Any,
Expand All @@ -81,9 +83,33 @@ def __init_subclass__(
init_state: 初始状态。
"""
super().__init_subclass__()

orig_bases: Tuple[type, ...] = getattr(cls, "__orig_bases__", ())
for orig_base in orig_bases:
origin_class = get_origin(orig_base)
if inspect.isclass(origin_class) and issubclass(origin_class, Plugin):
try:
_event_t, state_t, config_t = cast(
Tuple[EventT, StateT, ConfigT], get_args(orig_base)
)
except ValueError: # pragma: no cover
continue
if (
config is None
and inspect.isclass(config_t)
and issubclass(config_t, ConfigModel)
):
config = config_t # pyright: ignore
if (
init_state is None
and get_origin(state_t) is Annotated
and hasattr(state_t, "__metadata__")
):
init_state = state_t.__metadata__[0] # pyright: ignore

if not hasattr(cls, "Config") and config is not None:
cls.Config = config
if init_state is not None:
if cls.__init_state__ is Plugin.__init_state__ and init_state is not None:
cls.__init_state__ = lambda _: init_state # type: ignore

@final
Expand Down
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@
"homepage": "https://docs.alicebot.dev/",
"license": "MIT",
"dependencies": {
"vitepress": "1.0.0-rc.20",
"vue": "^3.3.4"
"vitepress": "1.0.0-rc.24",
"vue": "^3.3.7"
},
"devDependencies": {
"@types/node": "^20.8.3",
"@types/node": "^20.8.9",
"conventional-changelog-cli": "^3.0.0",
"markdownlint-cli2": "^0.8.1",
"prettier": "^3.0.3",
"pyright": "^1.1.330",
"pyright": "^1.1.333",
"zhlint": "^0.7.1"
},
"scripts": {
Expand Down
Loading

0 comments on commit c20e7bb

Please sign in to comment.