Skip to content

Commit 0be92a8

Browse files
committed
🔥 merge handle_option
1 parent 580c328 commit 0be92a8

File tree

3 files changed

+47
-72
lines changed

3 files changed

+47
-72
lines changed

src/arclet/alconna/ingedia/_argv.py

+8-9
Original file line numberDiff line numberDiff line change
@@ -16,36 +16,35 @@
1616
@dataclass(repr=True)
1717
class Argv(Generic[TDC]):
1818
"""命令行参数"""
19-
19+
# basic
2020
conf: InitVar[Config]
2121
namespace: Namespace = field(default=global_config.default_namespace)
2222
"""命名空间"""
2323
separators: str = field(default=" ")
2424
"""命令分隔符"""
25-
25+
# input
2626
preprocessors: dict[type, Callable[..., Any]] = field(default_factory=dict)
2727
"""命令元素的预处理器"""
2828
checker: Callable[[Any], bool] | None = field(default=None)
2929
"""检查传入命令"""
30-
31-
fuzzy_match: bool = field(init=False)
32-
"""当前命令是否模糊匹配"""
33-
fuzzy_threshold: float = field(init=False)
34-
"""模糊匹配阈值"""
3530
to_text: Callable[[Any], str | None] = field(default=lambda x: x if isinstance(x, str) else None)
3631
"""将命令元素转换为文本, 或者返回None以跳过该元素"""
3732
converter: Callable[[str | list], TDC] = field(default=lambda x: x)
3833
"""将字符串或列表转为目标命令类型"""
34+
# control
35+
fuzzy_match: bool = field(init=False)
36+
"""当前命令是否模糊匹配"""
37+
fuzzy_threshold: float = field(init=False)
38+
"""模糊匹配阈值"""
3939
filter_crlf: bool = field(init=False)
4040
"""是否过滤掉换行符"""
4141
message_cache: bool = field(init=False)
4242
"""是否缓存消息"""
4343
context_style: Literal["bracket", "parentheses"] | None = field(init=False)
4444
"命令上下文插值的风格,None 为关闭,bracket 为 {...},parentheses 为 $(...)"
45-
45+
# data
4646
current_index: int = field(init=False)
4747
"""当前数据的索引"""
48-
4948
ndata: int = field(init=False)
5049
"""原始数据的长度"""
5150
bak_data: list[str | Any] = field(init=False)

src/arclet/alconna/ingedia/_handlers.py

+39-63
Original file line numberDiff line numberDiff line change
@@ -268,14 +268,15 @@ def analyse_args(analyser: Analyser, argv: Argv, args: _Args) -> dict[str, Any]:
268268
return result
269269

270270

271-
def handle_option(analyser: Analyser, argv: Argv, opt: Option, name_validated: bool) -> tuple[Any, dict[str, Any]]:
271+
def analyse_option(analyser: Analyser, opt: Option, argv: Argv, path: tuple[str, ...], name_validated: bool):
272272
"""
273-
处理 `Option` 部分
273+
分析 `Option` 部分
274274
275275
Args:
276-
analyser (Analyser): 当前解析器
277-
argv (Argv): 命令行参数
276+
analyser (SubAnalyser): 当前解析器
278277
opt (Option): 目标 `Option`
278+
argv (Argv): 命令行参数
279+
path (tuple[str, ...]): 路径
279280
name_validated (bool): 是否已经验证过名称
280281
"""
281282
_cnt = 0
@@ -303,53 +304,36 @@ def handle_option(analyser: Analyser, argv: Argv, opt: Option, name_validated: b
303304
if levenshtein(name, al) >= argv.fuzzy_threshold:
304305
raise FuzzyMatchSuccess(lang.require("fuzzy", "matched").format(source=al, target=name))
305306
raise InvalidParam(lang.require("option", "name_error").format(source=opt.dest, target=name), opt)
306-
if opt.nargs:
307-
return ..., analyse_args(analyser, argv, opt.args)
308-
return _cnt or opt.action.value, {}
309-
310-
311-
def analyse_option(analyser: Analyser, opt: Option, argv: Argv, path: tuple[str, ...], name_validated: bool):
312-
"""
313-
分析 `Option` 部分
314-
315-
Args:
316-
analyser (SubAnalyser): 当前解析器
317-
opt (Option): 目标 `Option`
318-
argv (Argv): 命令行参数
319-
path (tuple[str, ...]): 路径
320-
name_validated (bool): 是否已经验证过名称
321-
"""
322-
value, args = handle_option(analyser, argv, opt, name_validated)
307+
args = analyse_args(analyser, argv, opt.args) if opt.nargs else {}
323308
if path not in analyser.value_result:
324-
if opt.action.type == 1:
325-
if args:
326-
analyser.value_result[path] = value
327-
analyser.args_result[path] = {key: [value] for key, value in args.items()}
328-
else:
329-
analyser.value_result[path] = value[:]
330-
else:
331-
analyser.value_result[path] = value
332-
analyser.args_result[path] = args
333-
return
309+
analyser.args_result[path] = args
310+
if opt.action.type == 1 and args:
311+
analyser.args_result[path] = {key: [value] for key, value in args.items()}
312+
analyser.value_result[path] = _cnt or opt.action.value
313+
return True
334314
if opt.action.type == 0: # cover the old value
335-
analyser.value_result[path] = value
315+
analyser.value_result[path] = _cnt or opt.action.value
336316
analyser.args_result[path] = args
337-
elif opt.action.type == 2:
338-
if not opt.nargs:
339-
analyser.value_result[path] += value
340-
else:
341-
analyser.value_result[path] = value
342-
elif not opt.nargs: # opt.action.type == 1
343-
analyser.value_result[path].extend(value)
317+
return True
318+
if opt.action.type == 2:
319+
analyser.value_result[path] += _cnt or opt.action.value
320+
return True
321+
if not opt.nargs: # opt.action.type == 1
322+
source = analyser.value_result[path][:]
323+
source.extend(opt.action.value)
324+
analyser.value_result[path] = source
344325
else:
345326
for key, value in args.items():
346327
if key in analyser.args_result[path]:
347328
analyser.args_result[path][key].append(args[key])
348329
else:
349330
analyser.args_result[path][key] = [value]
331+
return True
350332

351333

352334
def analyse_subcommand(analyser: Analyser, sub: Subcommand, argv: Argv, path: tuple[str, ...], name_validated: bool = True):
335+
if path in analyser.value_result:
336+
return False
353337
if not name_validated:
354338
name, _ = argv.next(sub.separators)
355339
if name not in sub.aliases:
@@ -365,7 +349,7 @@ def analyse_subcommand(analyser: Analyser, sub: Subcommand, argv: Argv, path: tu
365349
while analyse_param(analyser, sub, argv, path, sub.separators) and argv.current_index != argv.ndata:
366350
pass
367351
if path not in analyser.args_optional or path in analyser.args_result:
368-
return
352+
return True
369353
if not analyser.args_optional[path]:
370354
raise ArgumentMissing(
371355
sub.args.data[0].field.get_missing_tips(
@@ -374,7 +358,7 @@ def analyse_subcommand(analyser: Analyser, sub: Subcommand, argv: Argv, path: tu
374358
sub
375359
)
376360
analyser.args_result[path] = analyse_args(analyser, argv, sub.args)
377-
return
361+
return True
378362

379363

380364
def analyse_compact_params(analyser: Analyser, argv: Argv, prefixes: tuple[str, ...]):
@@ -426,25 +410,18 @@ def analyse_param(analyser: Analyser, current: Subcommand, argv: Argv, prefixes:
426410
# analyser.compile_params 有命中,说明在当前子命令内有对应的选项/子命令
427411
if _str and _text and (_param := current._lookup_map.get(_text)):
428412
path = prefixes + (_param.dest,)
429-
if _param.__class__ is Subcommand:
430-
# 禁止子命令重复解析
431-
if path not in analyser.value_result:
432-
try:
433-
analyse_subcommand(analyser, _param, argv, path, True) # type: ignore
434-
except (FuzzyMatchSuccess, PauseTriggered):
435-
raise
436-
except AnalyseException as e:
437-
if not analyser._error:
438-
analyser._error = e
439-
return True
440-
else:
441-
try:
442-
analyse_option(analyser, _param, argv, path, True) # type: ignore
443-
except (FuzzyMatchSuccess, PauseTriggered):
444-
raise
445-
except AnalyseException as e:
446-
if not analyser._error:
447-
analyser._error = e
413+
apply = True
414+
try:
415+
if _param.__class__ is Subcommand:
416+
apply = analyse_subcommand(analyser, _param, argv, path, True) # type: ignore
417+
else:
418+
apply = analyse_option(analyser, _param, argv, path, True) # type: ignore
419+
except (FuzzyMatchSuccess, PauseTriggered):
420+
raise
421+
except AnalyseException as e:
422+
if not analyser._error:
423+
analyser._error = e
424+
if apply:
448425
return True
449426
# 如果没有命中,则说明当前参数可能存在自定义分隔符,或者属于子命令的主参数,那么需要重新解析
450427
argv.rollback(_text)
@@ -473,18 +450,17 @@ def analyse_param(analyser: Analyser, current: Subcommand, argv: Argv, prefixes:
473450

474451

475452
def analyse_header(header: "Header", argv: Argv):
476-
content = header.content
477453
head_text, _str = argv.next()
478454
if _str:
479-
if head_text in content:
455+
if head_text in header.content:
480456
return HeadResult(head_text, head_text, True)
481457
if header.compact and (mat := header.compact_pattern.match(head_text)):
482458
argv.rollback(head_text[len(mat[0]):], replace=True)
483459
return HeadResult(mat[0], mat[0], True)
484460
may_cmd, _m_str = argv.next()
485461
if _m_str:
486462
cmd = f"{head_text}{argv.separators[0]}{may_cmd}"
487-
if cmd in content:
463+
if cmd in header.content:
488464
return HeadResult(cmd, cmd, True)
489465
if header.compact and (mat := header.compact_pattern.match(cmd)):
490466
argv.rollback(cmd[len(mat[0]):], replace=True)

src/arclet/alconna/ingedia/_util.py

Whitespace-only changes.

0 commit comments

Comments
 (0)