diff options
| author | 2023-12-15 10:15:59 +0800 | |
|---|---|---|
| committer | 2023-12-15 10:15:59 +0800 | |
| commit | 5cf3a4a5ca8adc6db3e95a1cd01502b5167bb88f (patch) | |
| tree | 7ded1b558cc5c7ca0b7ac5973f447ea8c590c581 | |
| parent | 4dafb0f0a81255193f2a44df5d203239325e2236 (diff) | |
| parent | f7f775b4c529b1d65e55a12ee6b4e7c6c636f004 (diff) | |
| download | infini-5cf3a4a5ca8adc6db3e95a1cd01502b5167bb88f.tar.gz infini-5cf3a4a5ca8adc6db3e95a1cd01502b5167bb88f.zip | |
Merge pull request #40 from HydroRoll-Team/sourcery/pull-39
🎉 阶段性完成 (Sourcery refactored)
| -rw-r--r-- | README.md | 35 | ||||
| -rw-r--r-- | codehere.bat | 2 | ||||
| -rw-r--r-- | mkdocs.yml | 2 | ||||
| -rw-r--r-- | pyproject.toml | 6 | ||||
| -rw-r--r-- | src/infini/__main__.py | 56 | ||||
| -rw-r--r-- | src/infini/consts/templates.py | 74 | ||||
| -rw-r--r-- | src/infini/event.py | 30 | ||||
| -rw-r--r-- | src/infini/exceptions.py | 12 | ||||
| -rw-r--r-- | src/infini/handler.py | 47 | ||||
| -rw-r--r-- | src/infini/logging.py | 15 | ||||
| -rw-r--r-- | src/infini/matcher.py | 31 | ||||
| -rw-r--r-- | src/infini/utils/cli.py | 23 | ||||
| -rw-r--r-- | src/infini/utils/gui.py | 2 |
13 files changed, 243 insertions, 92 deletions
@@ -41,25 +41,16 @@ 2. 创建规则包实例 - 创建`cli.py`并写入以下内容: - - ```python - import infini - - client = infini.Cli() - client.parse_args() - ``` - - 打开终端并执行: + 确保你的`infini`正确安装后,打开终端并执行: ``` shell - python cli.py --new --path MyRule + python -m infini new MyRule ``` - 你可以在生成的 `MyRule\rule.py` 创建一个或者多个 `rule` 实例并继承 `Rule` 基类, 通过编写合适的相关方法与类注册规则包实现规则的自定义。 + 你可以在生成的 `MyRule\rule.py` 创建一个或者多个继承 `Handler` 基类的实例, 通过编写合适的相关方法与类注册规则包实现规则的自定义。 ``` python - from infini import Rule, Result, Dice + from infini import Handler, Result class MyRule(Rule): """自设规则包""" @@ -70,14 +61,24 @@ def __init__(self) -> None: """初始化你的规则包""" - def check(self, dice: Dice) -> Result: - """声明规则包检定方式""" + def process(self, **kwargs) -> Result: + """声明规则包运行方式""" return Result("event1", True) ``` - `check`函数应当返回一个`Result`对象,它应当包含一个消息事件名(例如示例中的`event1`),该消息事件名应当在 `MyRule\event.py` 中被注册。消息事件的动态内容通过`{name}`的方式声明并通过`name="内容"`的方式实现。 + `process`函数应当返回一个`Result`对象,它应当包含一个消息事件名(例如示例中的`event1`),该消息事件名应当在 `MyRule\event.py` 中被注册。消息事件的动态内容通过`{name}`的方式声明并通过`name="内容"`的方式实现。 + +3. 创建你的测试文件 -3. 合理修改你的 `config.toml` 配置文件,完成注册! + 在 `MyRule\tests.py` 中的 `test` 函数中给出测试函数,并返回一个 `list`,应当包含所有异常内容。 + +4. 测试你的规则包 + + 执行指令: + + ```bash + python -m infini test MyRule + ``` ### 🎍Sites diff --git a/codehere.bat b/codehere.bat new file mode 100644 index 00000000..976c1ef2 --- /dev/null +++ b/codehere.bat @@ -0,0 +1,2 @@ +@echo off +code .
\ No newline at end of file @@ -1,4 +1,4 @@ -site_name: infini 核心文档 +site_name: Infini 核心文档 repo_url: https://github.com/HydroRoll-Team/infini repo_name: HydroRoll-Team/infini edit_uri: edit/master/docs/ diff --git a/pyproject.toml b/pyproject.toml index a7f20ffd..22d90c98 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,10 +1,10 @@ [project] name = "infini" version = "1.0.4" -description = "The `Core` of `HydroRoll`, the `Loader` of your rules packages." -authors = [{ name = "简律纯", email = "i@jyunko.cn" }] +description = "Infini 核心标准输入输出模块" +authors = [{ name = "简律纯", email = "i@jyunko.cn" }, { name = "苏向夜", email = "fu050409@163.com" }] dependencies = ["reportlab[pycairo]>=4.0.5", "pydantic>=2.4.2", "loguru>=0.7.2"] -requires-python = ">=3.8" +requires-python = ">=3.9" readme = "README.md" license = { text = "MIT" } diff --git a/src/infini/__main__.py b/src/infini/__main__.py new file mode 100644 index 00000000..5c4dc2eb --- /dev/null +++ b/src/infini/__main__.py @@ -0,0 +1,56 @@ +from pathlib import Path +from .utils.cli import parse_args +from .consts import templates +from .logging import logger + +import os +import importlib +import sys + + +def main(): + args = parse_args() + + if args.gui: + logger.critical("选项[--gui]尚未被支持!") + sys.exit(1) + + path = Path(args.path).resolve() if args.path else Path(os.getcwd()).resolve() + + if args.operate == "new": + if path.exists() and not args.force: + logger.error("指定的文件夹已经存在!") + sys.exit(1) + + path.mkdir(parents=True, exist_ok=True) + (path / "handler.py").write_text(templates.RULE, 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": + logger.info(f"开始测试规则包: {path.name}...") + sys.path.append(str(path)) + logger.info("初始化规则包中...") + try: + importlib.import_module("event") + importlib.import_module("handler") + except Exception as error: + if args.verbose: + logger.exception(error) + logger.critical(f"初始化规则包时出现异常: {error}") + return + try: + errors = importlib.import_module("tests").test() + 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)} 个异常, 测试完成.") + + +if __name__ == "__main__": + main() diff --git a/src/infini/consts/templates.py b/src/infini/consts/templates.py index e2edc2d6..d25894ae 100644 --- a/src/infini/consts/templates.py +++ b/src/infini/consts/templates.py @@ -1,69 +1,37 @@ -RULE = """from infini import Rule, Result, Dice +RULE = """from infini import Handler, Result +__handlers__ = ["HandlerRule"] -class MyRule(Rule): - \"\"\"自设规则包\"\"\" - name = "MyRule" - priority: int = 0 +class HandlerRule(Handler): + \"\"\"自设业务函数\"\"\" - def __init__(self) -> None: - \"\"\"初始化你的规则包\"\"\" + name = "MyRule" # 规则包名 + priority: int = 0 # 规则包权重 - def check(self, dice: Dice) -> Result: + def process(self, **kwargs) -> Result: \"\"\"声明规则包检定方式\"\"\" - return Result("myevent.event1", True) + return Result("event1", True) """ -EVENT = """from infini import Event +EVENT = """from infini import MessageEvent __events__ = ["MyEvent"] -class MyEvent(Event): +class MyEvent(MessageEvent): name = "event1" output = "检定成功!" """ -DICE = """from infini import Dice - -import random -import re - - -class BaseDice(Dice): - \"\"\"多面骰\"\"\" - - def __init__(self, roll_string: str = "") -> None: - self.roll_string = roll_string - self.parse() - - def parse(self) -> "Dice": - self.dices = [] - split = re.split(r"[dD]", self.roll_string) - - if split[0]: - self.a = int(split[0]) - else: - self.a = 1 - - if split[1]: - self.b = int(split[1]) - else: - self.b = 100 - - self.db = f"{self.a}D{self.b}" - self.dices += [f"D{self.b}"] * self.a - return self - - def roll(self) -> int: - self.results = [] - - for _ in range(self.a): - result = random.randint(1, self.b) - - self.results.append(result) - - self.outcome = sum(self.results) - return self.outcome -""" +TEST = """from infini.matcher import matcher, MatcherEvent + +def test(): + event = MatcherEvent("MyRule") + try: + matcher.run(event) + except Exception as error: + return [error] + finally: + return [] +"""
\ No newline at end of file diff --git a/src/infini/event.py b/src/infini/event.py index 9ce34bd9..0363ac96 100644 --- a/src/infini/event.py +++ b/src/infini/event.py @@ -1,9 +1,10 @@ +from abc import ABCMeta from .typing import Dict -from .logging import logger +from .exceptions import UnknownMessageEvent import re -__all__ = ["Event", "events"] +__all__ = ["MessageEvent", "events"] class Events: @@ -17,8 +18,7 @@ class Events: def process(self, name: str, **kwargs) -> str: if string := self._events.get(name.lower()): return self._format(string, **kwargs) - logger.warning(f"事件[{name.lower()}]不存在,将返回空字符串!") - return "" + raise UnknownMessageEvent(f"事件[{name.lower()}]不存在!") def _format(self, string: str, **kwargs): pattern = r"{(.*?)}" @@ -30,8 +30,8 @@ class Events: return string -class Event: - """事件基类""" +class MessageEvent(metaclass=ABCMeta): + """消息事件基类""" name: str output: str @@ -40,4 +40,20 @@ class Event: events.regist(cls.name, cls.output) -events = Events() +class MatcherEvent: + """Matcher 事件""" + + name: str + string: str + kwargs: dict + + def __init__(self, event_name: str, string: str | None = None, **kwargs): + self.name = event_name + self.string = string or "" + self.kwargs = kwargs + + def __repr__(self) -> str: + return f"<MatcherEvent [{self.name}]>" + + +events = Events()
\ No newline at end of file diff --git a/src/infini/exceptions.py b/src/infini/exceptions.py index 62c88fa1..e7c87b36 100644 --- a/src/infini/exceptions.py +++ b/src/infini/exceptions.py @@ -4,3 +4,15 @@ class HydroError(Exception): class RuleLoadError(HydroError): """规则导入错误""" + + +class EventError(HydroError): + """事件处理时异常""" + + +class UnknownMatcherEvent(EventError): + """未知的给入实现""" + + +class UnknownMessageEvent(EventError): + """未知的给出实现"""
\ No newline at end of file diff --git a/src/infini/handler.py b/src/infini/handler.py new file mode 100644 index 00000000..06d5c5fa --- /dev/null +++ b/src/infini/handler.py @@ -0,0 +1,47 @@ +from abc import ABCMeta, abstractmethod +from .event import MatcherEvent +from .typing import Dict + +__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 + + +class Handler: + """规则包业务基类""" + + name: str + priority: int = 0 + + def __init_subclass__(cls) -> None: + handlers.regist(cls.name, cls()) + + @abstractmethod + def process(self, event: MatcherEvent) -> Result: + 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/logging.py b/src/infini/logging.py index a1458ef4..3c3d2b02 100644 --- a/src/infini/logging.py +++ b/src/infini/logging.py @@ -1,6 +1,6 @@ -"""infini 日志。 +"""Infini 日志。 -infini 使用 [loguru](https://github.com/Delgan/loguru) 来记录日志信息。 +Infini 使用 [loguru](https://github.com/Delgan/loguru) 来记录日志信息。 自定义 logger 请参考 [loguru](https://github.com/Delgan/loguru) 文档。 """ from datetime import datetime @@ -12,7 +12,7 @@ from .settings import DEBUG __all__ = ["logger", "error_or_exception"] logger = multilogger( - name="HydroRoll", payload="Core", level="DEBUG" if DEBUG else "INFO" + name="Infini", payload="Core", level="DEBUG" if DEBUG else "INFO" ) current_path = Path(__file__).resolve().parent LOG_PATH = current_path / "logs" @@ -26,15 +26,8 @@ logger.add( def error_or_exception(message: str, exception: Exception, verbose: bool = True): - # 弃用的方法 - # logger.remove() - # logger.add( - # sys.stderr, - # format="<magenta>{time:YYYY-MM-DD HH:mm:ss.SSS}</magenta> <level>[{level}]</level> > <cyan>{name}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan> - <level>{message}</level>", - # ) - if verbose: logger.exception(exception) logger.critical(message) else: - logger.critical(f"{message} {exception!r}") + logger.critical(f"{message} {exception!r}")
\ No newline at end of file diff --git a/src/infini/matcher.py b/src/infini/matcher.py new file mode 100644 index 00000000..7fc5b88a --- /dev/null +++ b/src/infini/matcher.py @@ -0,0 +1,31 @@ +from .event import MatcherEvent, Events, events +from .handler import Handlers, Handler, handlers +from .exceptions import UnknownMatcherEvent + + +class Matcher: + """事件处理单元""" + + events: Events + handlers: Handlers + + 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 match(self, name: str) -> Handler: + if handler := self.handlers.match(name): + 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 + ) + + +matcher = Matcher() diff --git a/src/infini/utils/cli.py b/src/infini/utils/cli.py new file mode 100644 index 00000000..6a4f9128 --- /dev/null +++ b/src/infini/utils/cli.py @@ -0,0 +1,23 @@ +import argparse +import sys + + +def parse_args(argv: list[str] | None = None) -> argparse.Namespace: + parser = argparse.ArgumentParser(prog="Infini CLI", description="Infini 命令行工具") + + parser.add_argument("--gui", action="store_true", help="启用 GUI 模式") + + subparsers = parser.add_subparsers(title="功能件", dest="operate") + + # 子命令 `new` + new_parser = subparsers.add_parser("new", help="创建一个 Infini 规则包模板") + new_parser.add_argument("path", help="目标位置") + new_parser.add_argument("-v", "--verbose", action="store_true", help="异常追踪") + new_parser.add_argument("-f", "--force", action="store_true", help="强制创建") + + # 子命令 `test` + test_parser = subparsers.add_parser("test", help="测试 Infini 规则包") + test_parser.add_argument("path", help="目标位置") + test_parser.add_argument("-v", "--verbose", action="store_true", help="异常追踪") + + return parser.parse_args(argv if argv else sys.argv[1:]) diff --git a/src/infini/utils/gui.py b/src/infini/utils/gui.py new file mode 100644 index 00000000..d7bc24ae --- /dev/null +++ b/src/infini/utils/gui.py @@ -0,0 +1,2 @@ +"""Infini 核心图形界面""" +# TODO |
