diff --git a/src/arclet/alconna/_internal/_analyser.py b/src/arclet/alconna/_internal/_analyser.py index 174f4bcd..94d56a9d 100644 --- a/src/arclet/alconna/_internal/_analyser.py +++ b/src/arclet/alconna/_internal/_analyser.py @@ -185,7 +185,7 @@ def process(self, argv: Argv[TDC]) -> Self: sub = argv.context = self.command name, _ = argv.next(sub.separators) if name != sub.name: # 先匹配节点名称 - if argv.fuzzy_match and levenshtein(name, sub.name) >= config.fuzzy_threshold: + if argv.fuzzy_match and levenshtein(name, sub.name) >= argv.fuzzy_threshold: raise FuzzyMatchSuccess(lang.require("fuzzy", "matched").format(source=sub.name, target=name)) raise ParamsUnmatched(lang.require("subcommand", "name_error").format(target=name, source=sub.name)) diff --git a/src/arclet/alconna/_internal/_argv.py b/src/arclet/alconna/_internal/_argv.py index 01891cb2..115d6948 100644 --- a/src/arclet/alconna/_internal/_argv.py +++ b/src/arclet/alconna/_internal/_argv.py @@ -20,6 +20,8 @@ class Argv(Generic[TDC]): namespace: Namespace = field(default=config.default_namespace) fuzzy_match: bool = field(default=False) """当前命令是否模糊匹配""" + fuzzy_threshold: float = field(default=0.6) + """模糊匹配阈值""" preprocessors: dict[type, Callable[..., Any]] = field(default_factory=dict) """命令元素的预处理器""" to_text: Callable[[Any], str | None] = field(default=lambda x: x if isinstance(x, str) else None) diff --git a/src/arclet/alconna/_internal/_handlers.py b/src/arclet/alconna/_internal/_handlers.py index da96a64b..fbfca465 100644 --- a/src/arclet/alconna/_internal/_handlers.py +++ b/src/arclet/alconna/_internal/_handlers.py @@ -154,7 +154,7 @@ def step_keyword(argv: Argv, args: Args, result: dict[str, Any]): if arg.value.base.exec(may_arg).flag == "valid": # type: ignore raise InvalidParam(lang.require("args", "key_missing").format(target=may_arg, key=arg.name)) for name in args.argument.keyword_only: - if levenshtein(_key, name) >= config.fuzzy_threshold: + if levenshtein(_key, name) >= argv.fuzzy_threshold: raise FuzzyMatchSuccess(lang.require("fuzzy", "matched").format(source=name, target=_key)) raise InvalidParam(lang.require("args", "key_not_found").format(name=_key)) arg = args.argument.keyword_only[_key] @@ -261,7 +261,7 @@ def handle_option(argv: Argv, opt: Option) -> tuple[str, OptionResult]: elif name in opt.aliases: error = False if error: - if argv.fuzzy_match and levenshtein(name, opt.name) >= config.fuzzy_threshold: + if argv.fuzzy_match and levenshtein(name, opt.name) >= argv.fuzzy_threshold: raise FuzzyMatchSuccess(lang.require("fuzzy", "matched").format(source=opt.name, target=name)) raise InvalidParam(lang.require("option", "name_error").format(source=opt.name, target=name)) name = opt.dest @@ -477,17 +477,17 @@ def analyse_header(header: Header, argv: Argv) -> HeadResult: if _str: argv.rollback(may_cmd) if argv.fuzzy_match: - _handle_fuzzy(header, head_text) + _handle_fuzzy(header, head_text, argv.fuzzy_threshold) raise InvalidParam(lang.require("header", "error").format(target=head_text), head_text) if _m_str and may_cmd: if argv.fuzzy_match: - _handle_fuzzy(header, f"{head_text} {may_cmd}") + _handle_fuzzy(header, f"{head_text} {may_cmd}", argv.fuzzy_threshold) raise InvalidParam(lang.require("header", "error").format(target=may_cmd), may_cmd) argv.rollback(may_cmd) raise InvalidParam(lang.require("header", "error").format(target=head_text), None) -def _handle_fuzzy(header: Header, source: str): +def _handle_fuzzy(header: Header, source: str, threshold: float): command = header.origin[0] if not header.origin[1]: headers_text = [str(command)] @@ -501,7 +501,7 @@ def _handle_fuzzy(header: Header, source: str): else: headers_text.append(f"{prefix} {command}") for ht in headers_text: - if levenshtein(source, ht) >= config.fuzzy_threshold: + if levenshtein(source, ht) >= threshold: raise FuzzyMatchSuccess(lang.require("fuzzy", "matched").format(target=source, source=ht)) diff --git a/src/arclet/alconna/config.py b/src/arclet/alconna/config.py index a1c99590..ac033321 100644 --- a/src/arclet/alconna/config.py +++ b/src/arclet/alconna/config.py @@ -109,8 +109,6 @@ class _AlconnaConfig: command_max_count: int = 200 """最大命令数量""" - fuzzy_threshold: float = 0.6 - """模糊匹配阈值""" _default_namespace = "Alconna" """默认命名空间名称""" remainders: set[str] = {"--"} diff --git a/src/arclet/alconna/manager.py b/src/arclet/alconna/manager.py index db550169..5e5b0907 100644 --- a/src/arclet/alconna/manager.py +++ b/src/arclet/alconna/manager.py @@ -116,6 +116,7 @@ def register(self, command: Alconna) -> None: self.__argv[command] = __argv_type__.get()( command.namespace_config, # type: ignore fuzzy_match=command.meta.fuzzy_match, # type: ignore + fuzzy_threshold=command.meta.fuzzy_threshold, # type: ignore to_text=command.namespace_config.to_text, # type: ignore converter=command.namespace_config.converter, # type: ignore separators=command.separators, # type: ignore diff --git a/src/arclet/alconna/typing.py b/src/arclet/alconna/typing.py index 20598d98..046001d5 100644 --- a/src/arclet/alconna/typing.py +++ b/src/arclet/alconna/typing.py @@ -79,6 +79,8 @@ class CommandMeta: "命令的作者" fuzzy_match: bool = field(default=False) "命令是否开启模糊匹配" + fuzzy_threshold: float = field(default=0.6) + """模糊匹配阈值""" raise_exception: bool = field(default=False) "命令是否抛出异常" hide: bool = field(default=False)