diff --git a/exam3.py b/exam3.py index 21d4dbcd..da3cac0f 100644 --- a/exam3.py +++ b/exam3.py @@ -1,15 +1,31 @@ -from typing import Any, Callable, TypeVar -from typing_extensions import ParamSpec, Self +from __future__ import annotations +from typing import Any, Callable, TypeVar, overload, Literal +from typing_extensions import ParamSpec, Concatenate, Self + T = TypeVar("T") R = TypeVar("R") P = ParamSpec("P") -def deco(fn: Callable[P, T]) -> Callable[[Callable[[Any, T], R]], Callable[P, R]]: - def wrapper(func: Callable[[Any, T], R]) -> Callable[P, R]: +@overload +def deco(fn: Callable[P, T]) -> Callable[[Callable[[T], R]], Callable[P, R]]: ... + + +@overload +def deco( + fn: Callable[P, T], is_method: Literal[True] +) -> Callable[[Callable[[Any, T], R]], Callable[Concatenate[Any, P], R]]: ... + + +def deco( # type: ignore + fn: Callable[P, T], is_method: bool = False +) -> Callable[[Callable[[T], R] | Callable[[Any, T], R]], Callable[P, R] | Callable[Concatenate[Any, P], R]]: + def wrapper(func: Callable[[T], R] | Callable[[Any, T], R]) -> Callable[P, R] | Callable[Concatenate[Any, P], R]: def inner(*args: P.args, **kwargs: P.kwargs): - return func(args[0], fn(*args[1:], **kwargs)) # type: ignore + if is_method: + return func(args[0], fn(*args[1:], **kwargs)) # type: ignore + return func(fn(*args, **kwargs)) # type: ignore return inner @@ -25,7 +41,7 @@ class B: def foo(self, num: int): ... - @deco(A) + @deco(A, is_method=True) def add(self, args: A) -> Self: print(args.num) return self diff --git a/src/arclet/alconna/args.py b/src/arclet/alconna/args.py index ede59d8a..51683763 100644 --- a/src/arclet/alconna/args.py +++ b/src/arclet/alconna/args.py @@ -122,7 +122,7 @@ def __init__( default.default = None if default.default is Empty else default.default # type: ignore if _value == NONE: raise InvalidArgs(lang.require("args", "value_error").format(target=name)) - self.value = _value + self.value = _value # type: ignore self.field = default self.notice = notice self.separators = (seps,) if isinstance(seps, str) else tuple(seps) @@ -137,7 +137,7 @@ def __init__( self.optional = ArgFlag.OPTIONAL in self.flag self.hidden = ArgFlag.HIDDEN in self.flag if ArgFlag.ANTI in self.flag and self.value not in (ANY, AllParam): - self.value = AntiPattern(self.value) + self.value = AntiPattern(self.value) # type: ignore def __repr__(self): n, v = f"'{self.name}'", str(self.value) @@ -381,7 +381,7 @@ def __getitem__(self, item: Union[Arg, tuple[Arg, ...], str, tuple[Any, ...]]) - Returns: Self | Arg: 参数集合自身或需要的参数单元 """ - if isinstance(item, str) and (res := next(filter(lambda x: x.name == item, self.argument), None)): + if res := next((x for x in self.argument if x.name == item), None): return res data: tuple[Arg, ...] | tuple[Any, ...] = item if isinstance(item, tuple) else (item,) if isinstance(data[0], Arg): diff --git a/src/arclet/alconna/core.py b/src/arclet/alconna/core.py index 8370dece..72130f92 100644 --- a/src/arclet/alconna/core.py +++ b/src/arclet/alconna/core.py @@ -16,13 +16,11 @@ from .arparma import Arparma, ArparmaBehavior, requirement_handler from .base import Completion, Help, Option, Shortcut, Subcommand from .config import Namespace, config -from .duplication import Duplication from .exceptions import ExecuteFailed, NullMessage from .formatter import TextFormatter from .manager import ShortcutArgs, command_manager from .typing import TDC, CommandMeta, DataCollection, InnerShortcutArgs, ShortcutRegWrapper, TPrefixes -T_Duplication = TypeVar("T_Duplication", bound=Duplication) T = TypeVar("T") TDC1 = TypeVar("TDC1", bound=DataCollection[Any]) @@ -63,7 +61,8 @@ class ArparmaExecutor(Generic[T]): target: Callable[..., T] binding: Callable[..., list[Arparma]] = field(default=lambda: [], repr=False) - __call__ = lambda self, *args, **kwargs: self.target(*args, **kwargs) + def __call__(self, *args, **kwargs): + return self.target(*args, **kwargs) @property def result(self) -> T: @@ -171,7 +170,6 @@ def __init__( command_manager.register(self) self._executors: dict[ArparmaExecutor, Any] = {} self.union: "WeakSet[Alconna]" = WeakSet() - self._union = False @property def namespace_config(self) -> Namespace: @@ -276,24 +274,6 @@ def shortcut(self, key: str, *, delete: Literal[True]) -> str: ... def shortcut(self, key: str, args: ShortcutArgs | None = None, delete: bool = False, **kwargs): - """操作快捷命令 - - Args: - key (str): 快捷命令名 - args (ShortcutArgs | None, optional): 快捷命令参数, 不传入时则尝试使用最近一次使用的命令 - delete (bool, optional): 是否删除快捷命令, 默认为 `False` - command (str, optional): 快捷命令指向的命令 - arguments (list[Any] | None, optional): 快捷命令参数, 默认为 `None` - fuzzy (bool, optional): 是否允许命令后随参数, 默认为 `True` - prefix (bool, optional): 是否调用时保留指令前缀, 默认为 `False` - wrapper (ShortcutRegWrapper, optional): 快捷指令的正则匹配结果的额外处理函数, 默认为 `None` - - Returns: - str: 操作结果 - - Raises: - ValueError: 快捷命令操作失败时抛出 - """ try: if delete: return command_manager.delete_shortcut(self, key) @@ -337,41 +317,28 @@ def add(self, opt: Option | Subcommand) -> Self: command_manager.register(self) return self - @init_spec(Option, True) + @init_spec(Option, is_method=True) def option(self, opt: Option) -> Self: """添加选项""" return self.add(opt) - @init_spec(Subcommand, True) + @init_spec(Subcommand, is_method=True) def subcommand(self, sub: Subcommand) -> Self: """添加子命令""" return self.add(sub) def _parse(self, message: TDC, ctx: dict[str, Any] | None = None) -> Arparma[TDC]: - if self._union: - for ana, argv in command_manager.unpack(self.union): - if (res := ana.process(argv.enter(ctx).build(message))).matched: - return res analyser = command_manager.require(self) argv = command_manager.resolve(self) argv.enter(ctx).build(message) return analyser.process(argv) - @overload def parse(self, message: TDC, ctx: dict[str, Any] | None = None) -> Arparma[TDC]: - ... - - @overload - def parse(self, message, ctx: dict[str, Any] | None = None, *, duplication: type[T_Duplication]) -> T_Duplication: - ... - - def parse(self, message: TDC, ctx: dict[str, Any] | None = None, *, duplication: type[T_Duplication] | None = None) -> Arparma[TDC] | T_Duplication: """命令分析功能, 传入字符串或消息链, 返回一个特定的数据集合类 Args: message (TDC): 命令消息 ctx (dict[str, Any], optional): 上下文信息 - duplication (type[T_Duplication], optional): 指定的`副本`类型 Returns: Arparma[TDC] | T_Duplication: 若`duplication`参数为`None`则返回`Arparma`对象, 否则返回`duplication`类型的对象 Raises: @@ -388,7 +355,7 @@ def parse(self, message: TDC, ctx: dict[str, Any] | None = None, *, duplication: if self._executors: for ext in self._executors: self._executors[ext] = arp.call(ext.target) - return duplication(arp) if duplication else arp + return arp def bind(self, active: bool = True): """绑定命令执行器 @@ -433,7 +400,14 @@ def __add__(self, other) -> Self: def __or__(self, other: Alconna) -> Self: self.union.add(other) - self._union = True + + def _parse(message: TDC, ctx: dict[str, Any] | None = None) -> Arparma[TDC]: + for ana, argv in command_manager.unpack(self.union): + if (res := ana.process(argv.enter(ctx).build(message))).matched: + return res + return command_manager.require(self).process(command_manager.resolve(self).enter(ctx).build(message)) + + self._parse = _parse return self def _calc_hash(self): @@ -448,10 +422,6 @@ def __call__(self, *args): return self.parse(argv) # type: ignore return self.parse([head, *argv]) # type: ignore - @property - def headers(self): - return self.prefixes - @property def header_display(self): ana = command_manager.require(self) diff --git a/tests/components_test.py b/tests/components_test.py index c5d3b085..3377f048 100644 --- a/tests/components_test.py +++ b/tests/components_test.py @@ -42,7 +42,7 @@ class Demo1(Duplication): ) res = com4.parse("comp4 123 --bar abc sub --sub1 xyz") assert res.matched is True - duplication = com4.parse("comp4 123 --bar abc sub --sub1 xyz", duplication=Demo) + duplication = Demo(com4.parse("comp4 123 --bar abc sub --sub1 xyz")) assert isinstance(duplication, Demo) assert duplication.testArgs.available is True assert duplication.testArgs.foo == 123 @@ -50,7 +50,7 @@ class Demo1(Duplication): assert duplication.bar.args.bar == "abc" assert duplication.sub.available is True assert duplication.sub.option("sub1").args.first == "xyz" - duplication1 = com4.parse("comp4 123 --bar abc sub --sub1 xyz", duplication=Demo1) + duplication1 = Demo1(com4.parse("comp4 123 --bar abc sub --sub1 xyz")) assert isinstance(duplication1, Demo1) assert isinstance(duplication1.foo, int) assert isinstance(duplication1.bar, str)