diff options
| -rw-r--r-- | src/infini/__init__.py | 16 | ||||
| -rw-r--r-- | src/infini/__main__.py | 32 | ||||
| -rw-r--r-- | src/infini/consts/templates.py | 39 | ||||
| -rw-r--r-- | src/infini/event.py | 97 | ||||
| -rw-r--r-- | src/infini/exceptions.py | 28 | ||||
| -rw-r--r-- | src/infini/handler.py | 39 | ||||
| -rw-r--r-- | src/infini/matcher.py | 29 | ||||
| -rw-r--r-- | src/infini/register.py | 136 | ||||
| -rw-r--r-- | src/infini/typing.py | 2 |
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, |
