aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author简律纯 <i@jyunko.cn>2023-12-18 01:39:29 +0800
committerGitHub <noreply@github.com>2023-12-18 01:39:29 +0800
commit91dab95a9a7d96d1449dea28b859291b2748abec (patch)
treef76f697c9de1e47026c697751d4c9076ce2511cf
parent0ed002af9a90cbdaadb2fdf554f656fe7a0aecd2 (diff)
parent29512b9cb2984aa633c49ac7db8fd9556c0d98b9 (diff)
downloadinfini-91dab95a9a7d96d1449dea28b859291b2748abec.tar.gz
infini-91dab95a9a7d96d1449dea28b859291b2748abec.zip
Merge pull request #42 from fu050409/master
🎉 第一阶段目标完成!
-rw-r--r--src/infini/__init__.py16
-rw-r--r--src/infini/__main__.py32
-rw-r--r--src/infini/consts/templates.py39
-rw-r--r--src/infini/event.py97
-rw-r--r--src/infini/exceptions.py28
-rw-r--r--src/infini/handler.py39
-rw-r--r--src/infini/matcher.py29
-rw-r--r--src/infini/register.py136
-rw-r--r--src/infini/typing.py2
9 files changed, 300 insertions, 118 deletions
diff --git a/src/infini/__init__.py b/src/infini/__init__.py
index d572bda8..5a6142c1 100644
--- a/src/infini/__init__.py
+++ b/src/infini/__init__.py
@@ -1,5 +1,13 @@
-from infini.handler import Handler, Result
-from infini.event import MessageEvent
-from infini.matcher import matcher
+from infini.handler import Handler
+from infini.event import MessageEvent, MatcherEvent, WorkflowEvent
+from infini.matcher import Matcher
+from infini.register import Register
-__all__ = ["Handler", "Result", "MessageEvent", "matcher"]
+__all__ = [
+ "Handler",
+ "MessageEvent",
+ "Matcher",
+ "Register",
+ "MatcherEvent",
+ "WorkflowEvent",
+]
diff --git a/src/infini/__main__.py b/src/infini/__main__.py
index 5c4dc2eb..65391989 100644
--- a/src/infini/__main__.py
+++ b/src/infini/__main__.py
@@ -2,6 +2,7 @@ from pathlib import Path
from .utils.cli import parse_args
from .consts import templates
from .logging import logger
+from .register import register
import os
import importlib
@@ -15,7 +16,11 @@ def main():
logger.critical("选项[--gui]尚未被支持!")
sys.exit(1)
- path = Path(args.path).resolve() if args.path else Path(os.getcwd()).resolve()
+ path = (
+ Path(args.path).resolve()
+ if hasattr(args, "path")
+ else Path(os.getcwd()).resolve()
+ )
if args.operate == "new":
if path.exists() and not args.force:
@@ -23,33 +28,36 @@ def main():
sys.exit(1)
path.mkdir(parents=True, exist_ok=True)
- (path / "handler.py").write_text(templates.RULE, encoding="utf-8")
+ (path / "handler.py").write_text(templates.HANDLER, encoding="utf-8")
(path / "event.py").write_text(templates.EVENT, encoding="utf-8")
(path / "tests.py").write_text(templates.TEST, encoding="utf-8")
logger.success("HydroRoll 规则包模板已创建!")
if args.operate == "test":
+ exceptions = []
+
logger.info(f"开始测试规则包: {path.name}...")
- sys.path.append(str(path))
logger.info("初始化规则包中...")
+
try:
- importlib.import_module("event")
- importlib.import_module("handler")
+ register.regist(path)
except Exception as error:
if args.verbose:
logger.exception(error)
- logger.critical(f"初始化规则包时出现异常: {error}")
- return
+ logger.critical(f"初始化规则包时出现异常: {error}")
+ exceptions.append(error)
+
try:
- errors = importlib.import_module("tests").test()
+ errors = importlib.import_module(f"{path.name}.tests").test()
+ exceptions.extend(errors)
except Exception as error:
if args.verbose:
logger.exception(error)
- logger.critical(f"测试规则包时出现异常: {error}")
- return
- sys.path.remove(str(path))
- logger.info(f"测试规则包 {path.name} 出现 {len(errors)} 个异常, 测试完成.")
+ logger.critical(f"测试规则包时出现异常: {error}")
+ exceptions.append(error)
+
+ logger.info(f"测试规则包 {path.name} 出现 {len(exceptions)} 个异常, 测试完成.")
if __name__ == "__main__":
diff --git a/src/infini/consts/templates.py b/src/infini/consts/templates.py
index d25894ae..d780c248 100644
--- a/src/infini/consts/templates.py
+++ b/src/infini/consts/templates.py
@@ -1,37 +1,40 @@
-RULE = """from infini import Handler, Result
+HANDLER = """from infini import Handler, MessageEvent
+from infini.matcher import MatcherEvent
+from .event import MyEvent
-__handlers__ = ["HandlerRule"]
-
-class HandlerRule(Handler):
+class MyHandler(Handler):
\"\"\"自设业务函数\"\"\"
- name = "MyRule" # 规则包名
- priority: int = 0 # 规则包权重
+ name = "example_handler" # 业务函数事件名
+ priority: int = 0 # 业务函数权重
- def process(self, **kwargs) -> Result:
+ def process(self, event: MatcherEvent) -> MessageEvent:
\"\"\"声明规则包检定方式\"\"\"
- return Result("event1", True)
+ plain_text = event.get_plain_text()
+ return MyEvent("rule.example_event", plain_text=plain_text)
+
"""
EVENT = """from infini import MessageEvent
-__events__ = ["MyEvent"]
-
class MyEvent(MessageEvent):
- name = "event1"
- output = "检定成功!"
+ \"\"\"自定义消息事件\"\"\"
+ name = "example_event"
+ output = "捕获到输入: {plain_text}"
"""
TEST = """from infini.matcher import matcher, MatcherEvent
+
def test():
- event = MatcherEvent("MyRule")
+ event = MatcherEvent("rule.example_handler", string="测试")
try:
- matcher.run(event)
+ result = matcher.run(event)
+ assert result == "捕获到输入: 测试"
except Exception as error:
- return [error]
- finally:
- return []
-""" \ No newline at end of file
+ return error
+ return []
+
+"""
diff --git a/src/infini/event.py b/src/infini/event.py
index a83c4042..9c6bd41b 100644
--- a/src/infini/event.py
+++ b/src/infini/event.py
@@ -1,59 +1,92 @@
-from abc import ABCMeta
-from .typing import Dict
-from .exceptions import UnknownMessageEvent
+from abc import ABCMeta, abstractmethod
+from .typing import Dict, Any
-import re
+__all__ = [
+ "InfiniEvent",
+ "MessageEvent",
+ "WorkflowEvent",
+ "MatcherEvent",
+]
-__all__ = ["MessageEvent", "events"]
+class InfiniEvent(metaclass=ABCMeta):
+ """Inifni 事件基类"""
-class Events:
- """事件集合"""
+ name: str
+ kwargs: Dict[str, Any]
+
+ @abstractmethod
+ def __repr__(self) -> str:
+ raise NotImplementedError
+
+ def get_event_name(self) -> str:
+ return self.name
- _events: Dict[str, str] = {}
- def regist(self, name: str, output: str) -> None:
- self._events[name.lower()] = output
+class MessageEvent(InfiniEvent):
+ """Message 事件"""
+
+ name: str
+ output: str
+ kwargs: Dict[str, Any]
+
+ def __init__(self, name: str, **kwargs) -> None:
+ self.name = name
+ self.kwargs = kwargs
- def process(self, name: str, **kwargs) -> str:
- if string := self._events.get(name.lower()):
- return self._format(string, **kwargs)
- raise UnknownMessageEvent(f"事件[{name.lower()}]不存在!")
+ def __str__(self) -> str:
+ return f"<MessageEvent [{self.name}]>"
- def _format(self, string: str, **kwargs):
- pattern = r"{(.*?)}"
- values = re.findall(pattern, string)
- for value in values:
- kwarg = kwargs.get(value)
- value = kwarg if kwarg else ""
- string = re.sub(pattern, value, string)
- return string
+ def __repr__(self) -> str:
+ return self.__str__()
-class MessageEvent(metaclass=ABCMeta):
- """消息事件基类"""
+class WorkflowEvent(InfiniEvent):
+ """Workflow 事件"""
name: str
- output: str
+ kwargs: Dict[str, Any]
+
+ def __init__(self, name: str, **kwargs) -> None:
+ self.name = name
+ self.kwargs = kwargs
+
+ def __repr__(self) -> str:
+ return f"<WorkflowEvent [{self.name}]>"
- def __init_subclass__(cls) -> None:
- events.regist(cls.name, cls.output)
+ def __eq__(self, __value: object) -> bool:
+ if __value is str:
+ return self.name == __value
+ if isinstance(__value, WorkflowEvent):
+ return self.name == __value.name and self.kwargs == __value.kwargs
+ return False
-class MatcherEvent:
+class MatcherEvent(InfiniEvent):
"""Matcher 事件"""
name: str
+ prefix: str
string: str
- kwargs: dict
-
- def __init__(self, event_name: str, string: str | None = None, **kwargs):
+ kwargs: Dict[str, Any]
+
+ def __init__(
+ self,
+ event_name: str,
+ prefix: str | None = None,
+ string: str | None = None,
+ **kwargs,
+ ):
self.name = event_name
+ self.prefix = prefix or ""
self.string = string or ""
self.kwargs = kwargs
def __repr__(self) -> str:
return f"<MatcherEvent [{self.name}]>"
+ def get_prefix(self):
+ return self.prefix
-events = Events()
+ def get_plain_text(self):
+ return self.string
diff --git a/src/infini/exceptions.py b/src/infini/exceptions.py
index 2c181b48..e086fc5d 100644
--- a/src/infini/exceptions.py
+++ b/src/infini/exceptions.py
@@ -2,17 +2,37 @@ class HydroError(Exception):
"""HydroRoll 异常基类"""
-class RuleLoadError(HydroError):
- """规则导入错误"""
+class LoadError(HydroError):
+ """规则包导入错误"""
+
+
+class PackageNotFound(LoadError):
+ """规则包不存在"""
+
+
+class EventLoadError(LoadError, RuntimeError):
+ """事件声明导入失败"""
+
+
+class HandlerLoadError(LoadError, RuntimeError):
+ """业务函数导入失败"""
class EventError(HydroError):
"""事件处理时异常"""
-class UnknownMatcherEvent(EventError):
+class UnknownEvent(EventError):
+ """未知事件"""
+
+
+class UnknownMatcherEvent(UnknownEvent):
"""未知的给入实现"""
-class UnknownMessageEvent(EventError):
+class UnknownMessageEvent(UnknownEvent):
"""未知的给出实现"""
+
+
+class UnsupportedError(EventError, RuntimeError):
+ """方法未被支持"""
diff --git a/src/infini/handler.py b/src/infini/handler.py
index 06d5c5fa..7c0ee73d 100644
--- a/src/infini/handler.py
+++ b/src/infini/handler.py
@@ -1,21 +1,7 @@
from abc import ABCMeta, abstractmethod
-from .event import MatcherEvent
-from .typing import Dict
+from .event import MatcherEvent, InfiniEvent
-__all__ = ["Result", "Handler"]
-
-
-class Result(metaclass=ABCMeta):
- """规则包运行结果基类"""
-
- event: str
- status: bool
- kwargs: dict = {}
-
- def __init__(self, event: str, status: bool, **kwargs) -> None:
- self.event = event
- self.status = status
- self.kwargs = kwargs
+__all__ = ["Handler"]
class Handler:
@@ -24,24 +10,9 @@ class Handler:
name: str
priority: int = 0
- def __init_subclass__(cls) -> None:
- handlers.regist(cls.name, cls())
+ def __init__(self) -> None:
+ pass
@abstractmethod
- def process(self, event: MatcherEvent) -> Result:
+ def process(self, event: MatcherEvent) -> InfiniEvent:
raise NotImplementedError
-
-
-class Handlers:
- """规则包业务集合"""
-
- _handlers: Dict[str, Handler] = {}
-
- def regist(self, name: str, handler: Handler) -> None:
- self._handlers[name.lower()] = handler
-
- def match(self, name: str) -> Handler | None:
- return self._handlers.get(name.lower())
-
-
-handlers = Handlers()
diff --git a/src/infini/matcher.py b/src/infini/matcher.py
index 7fc5b88a..364bc5ea 100644
--- a/src/infini/matcher.py
+++ b/src/infini/matcher.py
@@ -1,30 +1,31 @@
-from .event import MatcherEvent, Events, events
-from .handler import Handlers, Handler, handlers
+from .register import Register, register
+from .event import MatcherEvent
+from .handler import Handler
from .exceptions import UnknownMatcherEvent
class Matcher:
"""事件处理单元"""
- events: Events
- handlers: Handlers
+ register: Register
- def __init__(
- self, _events: Events | None = None, _handlers: Handlers | None = None
- ) -> None:
- self.events = _events if _events else events
- self.handlers = _handlers if _handlers else handlers
+ def __init__(self, _register: Register | None = None) -> None:
+ self.register = _register if _register else register
def match(self, name: str) -> Handler:
- if handler := self.handlers.match(name):
- return handler
+ if handler := self.register.handlers.match(name):
+ if isinstance(handler, Handler):
+ return handler
+ else:
+ return handler()
else:
raise UnknownMatcherEvent(f"未知的规则包: {name}")
def run(self, event: MatcherEvent) -> str:
- result = self.match(event.name).process(event)
- return self.events.process(
- result.event, **result.kwargs if result.kwargs else event.kwargs
+ callback_event = self.match(event.name).process(event)
+ return self.register.events.process(
+ callback_event.name,
+ **callback_event.kwargs if callback_event.kwargs else callback_event.kwargs,
)
diff --git a/src/infini/register.py b/src/infini/register.py
new file mode 100644
index 00000000..64d63cfb
--- /dev/null
+++ b/src/infini/register.py
@@ -0,0 +1,136 @@
+from pathlib import Path
+from .exceptions import UnsupportedError
+from .handler import Handler
+from .event import InfiniEvent, MessageEvent, WorkflowEvent
+from .typing import Dict, Type
+from .exceptions import LoadError, EventLoadError, HandlerLoadError, UnknownEvent
+
+import re
+import sys
+import importlib
+import inspect
+
+
+class Loader:
+ """Infini 事件加载器"""
+
+ name: str
+ meta_path: Path
+ events: dict
+ handlers: dict
+
+ def __init__(self, meta_path: Path | str) -> None:
+ if isinstance(meta_path, str):
+ self.meta_path = Path(meta_path).resolve()
+ else:
+ self.meta_path = meta_path.resolve()
+ self.name = self.meta_path.name
+ self.events = {}
+ self.handlers = {}
+
+ def load(self) -> None:
+ sys.path.append(str(self.meta_path))
+ try:
+ importlib.import_module(f"{self.name}")
+ except Exception as error:
+ raise LoadError(f"规则包[{self.name}]导入失败: {error}") from error
+
+ try:
+ event_module = importlib.import_module(f"{self.name}.event")
+ events = [
+ cls[1]
+ for cls in inspect.getmembers(event_module, inspect.isclass)
+ if issubclass(cls[1], InfiniEvent)
+ and not cls[1].__module__.startswith("infini")
+ ]
+ for event in events:
+ self.events[f"{self.name}.{event.__dict__['name']}"] = event
+ except Exception as error:
+ raise EventLoadError(f"规则包[{self.name}]事件导入失败: {error}") from error
+
+ try:
+ handler_module = importlib.import_module(f"{self.name}.handler")
+ handlers = [
+ cls[1]
+ for cls in inspect.getmembers(handler_module, inspect.isclass)
+ if issubclass(cls[1], Handler)
+ and not cls[1].__module__.startswith("infini")
+ ]
+ for handler in handlers:
+ self.handlers[f"{self.name}.{handler.__dict__['name']}"] = handler
+ except Exception as error:
+ raise HandlerLoadError(f"规则包[{self.name}]业务函数导入失败: {error}") from error
+
+ sys.path.remove(str(self.meta_path))
+
+
+class Events:
+ """事件集合"""
+
+ _events: Dict[str, Type[InfiniEvent]]
+
+ def __init__(self) -> None:
+ self._events = {}
+
+ def regist(self, name: str, event: Type[InfiniEvent]) -> None:
+ self._events[name.lower()] = event
+
+ def update(self, _events: dict) -> None:
+ self._events.update(_events)
+
+ def _process(self, event: Type[InfiniEvent], **kwargs) -> str:
+ if issubclass(event, MessageEvent):
+ return self._format(event.__dict__["output"], **kwargs)
+ elif issubclass(event, WorkflowEvent):
+ raise UnsupportedError
+ else:
+ raise UnsupportedError
+
+ def process(self, name: str, **kwargs) -> str:
+ if event := self._events.get(name.lower()):
+ return self._process(event, **kwargs)
+ raise UnknownEvent(f"事件[{name.lower()}]不存在!")
+
+ def _format(self, string: str, **kwargs):
+ pattern = r"{(.*?)}"
+ values = re.findall(pattern, string)
+ for value in values:
+ kwarg = kwargs.get(value)
+ value = kwarg if kwarg else ""
+ string = re.sub(pattern, value, string)
+ return string
+
+
+class Handlers:
+ """规则包业务集合"""
+
+ _handlers: Dict[str, Handler] = {}
+
+ def regist(self, name: str, handler: Handler) -> None:
+ self._handlers[name.lower()] = handler
+
+ def update(self, _events: dict) -> None:
+ self._handlers.update(_events)
+
+ def match(self, name: str) -> Handler | None:
+ return self._handlers.get(name.lower())
+
+
+class Register:
+ """注册器"""
+
+ events: Events
+ handlers: Handlers
+
+ def __init__(self) -> None:
+ self.events = Events()
+ self.handlers = Handlers()
+
+ def regist(self, meta_path: Path | str | None = None):
+ _loader = Loader(meta_path if meta_path else ".")
+ _loader.load()
+ self.events.update(_loader.events)
+ self.handlers.update(_loader.handlers)
+
+
+register = Register()
diff --git a/src/infini/typing.py b/src/infini/typing.py
index df495707..9af61569 100644
--- a/src/infini/typing.py
+++ b/src/infini/typing.py
@@ -1,5 +1,7 @@
from typing import (
Dict as Dict,
+ Any as Any,
+ Type as Type,
TYPE_CHECKING as TYPE_CHECKING,
TypeVar as TypeVar,
Callable as Callable,