diff options
| -rw-r--r-- | pyproject.toml | 2 | ||||
| -rw-r--r-- | src/infini/doc.py | 22 | ||||
| -rw-r--r-- | src/infini/generator.py | 2 | ||||
| -rw-r--r-- | src/infini/internal.py | 16 | ||||
| -rw-r--r-- | src/infini/loader.py | 22 | ||||
| -rw-r--r-- | src/infini/register.py | 125 | ||||
| -rw-r--r-- | src/infini/router.py | 33 | ||||
| -rw-r--r-- | src/infini/typing.py | 1 | ||||
| -rw-r--r-- | tests/test_input.py | 2 |
9 files changed, 168 insertions, 57 deletions
diff --git a/pyproject.toml b/pyproject.toml index 90b9718c..eda4752d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "infini" -version = "2.1.3" +version = "2.1.4" description = "Infini 内容输入输出流框架" authors = [ { name = "苏向夜", email = "fu050409@163.com" }, diff --git a/src/infini/doc.py b/src/infini/doc.py new file mode 100644 index 00000000..d5e4aea3 --- /dev/null +++ b/src/infini/doc.py @@ -0,0 +1,22 @@ +from typing import Dict, Optional, TypedDict + + +class Annotation(TypedDict): + usage: Optional[str] + description: Optional[str] + epilog: Optional[str] + + +class Doc: + pre_interceptors: Dict[str, Annotation] + handlers: Dict[str, Annotation] + events: Dict[str, Annotation] + global_variables: Dict[str, Annotation] + interceptors: Dict[str, Annotation] + + def __init__(self) -> None: + self.pre_interceptors = {} + self.handlers = {} + self.events = {} + self.global_variables = {} + self.interceptors = {} diff --git a/src/infini/generator.py b/src/infini/generator.py index 254f0cdd..e2730ca3 100644 --- a/src/infini/generator.py +++ b/src/infini/generator.py @@ -1,5 +1,5 @@ from infini.output import Output -from infini.typing import Type, Dict, Callable, Union, Optional +from infini.typing import Dict, Callable, Union, Optional from infini.exceptions import UnknownEvent, UnknownEventType from infini.injector import Injector from jinja2 import Template diff --git a/src/infini/internal.py b/src/infini/internal.py index e30bda63..67b77b2a 100644 --- a/src/infini/internal.py +++ b/src/infini/internal.py @@ -1,3 +1,4 @@ +from infini.core import Core from infini.loader import Loader from infini.register import Register from infini.typing import List, Optional @@ -25,3 +26,18 @@ def require(name: str, paths: Optional[List] = None) -> Register: caller_frame.f_globals[f"{name}_register"] = register return register + + +def acquire_core() -> Core: + caller_frame = inspect.stack()[1][0] + caller_file: str = caller_frame.f_globals["__file__"] + top_name, *_ = caller_file.split(".") + try: + core = getattr(sys.modules[top_name], "__infini__")["core"] + if not isinstance(core, Core): + raise ValueError("Infini stack returned a mismatch instance.") + return core + except Exception as err: + raise RuntimeError( + "Infini Runtime is not accessible, perhaps infini core is down." + ) from err diff --git a/src/infini/loader.py b/src/infini/loader.py index f02dd910..4c1d38fe 100644 --- a/src/infini/loader.py +++ b/src/infini/loader.py @@ -99,6 +99,8 @@ class Loader: interceptors: List[RouterType] generators: Dict[str, BaseGenerator] + _core: Core + def __init__(self) -> None: self.pre_interceptors = [] self.handlers = [] @@ -125,12 +127,15 @@ class Loader: return register_variables def prepare(self) -> None: + self._core = Core() _install() def load(self, name: str) -> ModuleType: self.prepare() module = importlib.import_module(name) + vars(module)["__infini__"] = {"core": self._core, "loader": self} + registers = self._find_register_variables(module) self.load_from_registers(registers) if not registers: @@ -171,7 +176,7 @@ class Loader: def close(self): _uninstall() - def inject_core(self, core: Core): + def into_core(self) -> Core: pre_interceptor = Interceptor() handler = Handler() generator = TextGenerator() @@ -184,16 +189,13 @@ class Loader: self.inject_generator(generator) self.inject_interceptor(interceptor) self.inject_injector(injector) - core.pre_interceptor = pre_interceptor - core.handler = handler - core.generator = generator - core.interceptor = interceptor - core.injector = injector + self._core.pre_interceptor = pre_interceptor + self._core.handler = handler + self._core.generator = generator + self._core.interceptor = interceptor + self._core.injector = injector - def into_core(self) -> Core: - core = Core() - self.inject_core(core) - return core + return self._core def inject_register(self, register: Register): register.pre_interceptors = self.pre_interceptors diff --git a/src/infini/register.py b/src/infini/register.py index 0d85c3c0..ab6762e7 100644 --- a/src/infini/register.py +++ b/src/infini/register.py @@ -1,3 +1,4 @@ +from infini.doc import Doc from infini.typing import List, Dict, Any, Callable, RouterType, Optional, Union, Type from infini.input import Input from infini.output import Output @@ -14,6 +15,8 @@ class Register: interceptors: List[RouterType] generators: Dict[str, BaseGenerator] + doc: Doc + def __init__(self) -> None: self.pre_interceptors = [] self.handlers = [] @@ -21,89 +24,151 @@ class Register: self.global_variables = {} self.interceptors = [] self.generators = {} - - def pre_interceptor(self, router: Union[Router, str], priority: int = 0): + self.doc = Doc() + + def pre_interceptor( + self, + router: Union[Router, str], + *, + priority: int = 0, + namespace: Optional[str] = None, + usage: Optional[str] = None, + description: Optional[str] = None, + epilog: Optional[str] = None, + ): def decorator(func): @wraps(func) def wrapper(*args, **kwargs) -> Union[Input, Output]: return func(*args, **kwargs) + _router = Contains(router) if isinstance(router, str) else router self.pre_interceptors.append( { "priority": priority, - "router": Contains(router) if isinstance(router, str) else router, + "router": _router, "handler": wrapper, } ) + self.doc.pre_interceptors[ + namespace or _router.namespace or func.__name__ + ] = { + "usage": usage, + "description": description, + "epilog": epilog, + } return wrapper return decorator - def handler(self, router: Union[Router, str], priority: int = 0): + def handler( + self, + router: Union[Router, str], + *, + priority: int = 0, + namespace: Optional[str] = None, + usage: Optional[str] = None, + description: Optional[str] = None, + epilog: Optional[str] = None, + ): + """注册一个业务函数""" + def decorator(func): @wraps(func) def wrapper(*args, **kwargs) -> Output: return func(*args, **kwargs) + _router = Contains(router) if isinstance(router, str) else router self.handlers.append( { "priority": priority, - "router": Contains(router) if isinstance(router, str) else router, + "router": _router, "handler": wrapper, } ) + self.doc.handlers[namespace or _router.namespace or func.__name__] = { + "usage": usage, + "description": description, + "epilog": epilog, + } return wrapper return decorator - def regist_textevent(self, name: str, text: str): - import warnings - - warnings.warn( - "Infini will soon deprecated `regist_textevent`, " - "use `register_textevent` instead." - ) - self.events[name] = text - - def register_textevent(self, name: str, text: str): + def register_textevent( + self, name: str, text: str, *, description: Optional[str] = None + ): self.events[name] = text - - def regist_variable(self, name: str, data: Any): - import warnings - - warnings.warn( - "Infini will soon deprecated `regist_variable`, " - "use `register_variable` instead." - ) + self.doc.events[name] = { + "usage": None, + "description": description, + "epilog": None, + } + + def register_variable( + self, + name: str, + data: Any, + *, + usage: Optional[str] = None, + description: Optional[str] = None, + ): self.global_variables[name] = data - - def register_variable(self, name: str, data: Any): - self.global_variables[name] = data - - def dynamic_variable(self, name: Optional[str] = None): + self.doc.global_variables[name] = { + "usage": usage, + "description": description, + "epilog": None, + } + + def dynamic_variable( + self, + name: Optional[str] = None, + *, + usage: Optional[str] = None, + description: Optional[str] = None, + ): def decorator(func): @wraps(func) def wrapper(*args, **kwargs) -> str: return func(*args, **kwargs) self.global_variables[name or func.__name__] = wrapper + self.doc.global_variables[name or func.__name__] = { + "usage": usage, + "description": description, + "epilog": None, + } return wrapper return decorator - def interceptor(self, router: Union[Router, str], priority: int = 0): + def interceptor( + self, + router: Union[Router, str], + *, + priority: int = 0, + namespace: Optional[str] = None, + usage: Optional[str] = None, + description: Optional[str] = None, + epilog: Optional[str] = None, + ): def decorator(func): @wraps(func) def wrapper(*args, **kwargs) -> Union[Input, Output]: return func(*args, **kwargs) + _router = Contains(router) if isinstance(router, str) else router self.interceptors.append( { "priority": priority, - "router": Contains(router) if isinstance(router, str) else router, + "router": _router, "handler": wrapper, } ) + self.doc.interceptors[namespace or _router.namespace or func.__name__] = { + "usage": usage, + "description": description, + "epilog": epilog, + } return wrapper return decorator diff --git a/src/infini/router.py b/src/infini/router.py index 71da44e2..4e025a15 100644 --- a/src/infini/router.py +++ b/src/infini/router.py @@ -1,14 +1,16 @@ -from infini.typing import Sequence, Literal +from typing import Optional +from infini.typing import Sequence, Literal, Tuple from infini.input import Input class Router: - type: Literal["normal"] = "normal" - signs: set[str] + type: Literal["text"] = "text" + signs: Tuple[str, ...] def __init__(self, sign: str, alias: Sequence[str] = []) -> None: - self.signs = {sign} - self.signs.update(alias) + signs = {sign} + signs.update(alias) + self.signs = tuple(signs) def __eq__(self, __router: "Router") -> bool: return __router.type == self.type and __router.signs == self.signs @@ -17,9 +19,13 @@ class Router: text = plain_text.strip() return any([text == sign for sign in self.signs]) + @property + def namespace(self) -> Optional[str]: + return self.signs[0] if self.signs else None + class Startswith(Router): - type: Literal["startswith"] = "startswith" + name: Literal["startswith"] = "startswith" def match(self, plain_text: str) -> bool: text = plain_text.strip() @@ -27,14 +33,14 @@ class Startswith(Router): class Contains(Router): - type: Literal["contains"] = "contains" + name: Literal["contains"] = "contains" def match(self, plain_text: str) -> bool: return any([sign in plain_text for sign in self.signs]) class Endswith(Router): - type: Literal["endswith"] = "endswith" + name: Literal["endswith"] = "endswith" def match(self, input: Input) -> bool: text = input.get_plain_text().strip() @@ -42,14 +48,13 @@ class Endswith(Router): class Command(Router): - type: Literal["command"] = "command" - prefix: tuple = (".", "/") + name: Literal["command"] = "command" + prefix: tuple = (".", "/", "。", "!", "!") def match(self, input: Input) -> bool: text = input.get_plain_text().strip() - if text: - if text.startswith(self.prefix): - text = text[1:] - return any([text.startswith(sign) for sign in self.signs]) + if text.startswith(self.prefix): + text = text[1:] + return any([text.startswith(sign) for sign in self.signs]) return False diff --git a/src/infini/typing.py b/src/infini/typing.py index 14c219fc..6ba7f7f4 100644 --- a/src/infini/typing.py +++ b/src/infini/typing.py @@ -3,6 +3,7 @@ from typing import ( Dict as Dict, List as List, Any as Any, + Tuple as Tuple, Type as Type, Optional as Optional, Generic as Generic, diff --git a/tests/test_input.py b/tests/test_input.py index 5f44ca1e..88a7ceb5 100644 --- a/tests/test_input.py +++ b/tests/test_input.py @@ -13,4 +13,4 @@ def test_new_input_with_session_id(): def test_new_input_without_session_id(): input = Input("test plain_str", variables={"user_id": "test"}) - assert input.get_session_id() == "session_unknown_test" + assert input.get_session_id() == "test" |
