Skip to content

Commit

Permalink
📝 Docs: 更新最佳实践部分的 Alconna 章节 (#2303)
Browse files Browse the repository at this point in the history
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
  • Loading branch information
RF-Tar-Railt and pre-commit-ci[bot] authored Aug 26, 2023
1 parent 41498bd commit dc4ac6d
Show file tree
Hide file tree
Showing 5 changed files with 440 additions and 76 deletions.
134 changes: 123 additions & 11 deletions website/docs/best-practice/alconna/alconna.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,15 @@ alc = Alconna(".rd{roll:int}")
assert alc.parse(".rd123").header["roll"] == 123
```

Bracket Header 类似 python 里的 f-string 写法,通过 "{}" 声明匹配类型

"{}" 中的内容为 "name:type or pat":

- "{}", "{:}": 占位符,等价于 "(.+)"
- "{foo}": 等价于 "(?P&lt;foo&gt;.+)"
- "{:\d+}": 等价于 "(\d+)"
- "{foo:int}": 等价于 "(?P&lt;foo&gt;\d+)",其中 "int" 部分若能转为 `BasePattern` 则读取里面的表达式

### 组件

我们可以看到主要的两大组件:`Option``Subcommand`
Expand All @@ -74,6 +83,27 @@ assert alc.parse(".rd123").header["roll"] == 123

`Subcommand` 则可以传入自己的 **Option****Subcommand**

```python
from arclet.alconna import Alconna, Option, Subcommand

alc = Alconna(
"command_name",
Option("opt1"),
Option("--opt2"),
Subcommand(
"sub1",
Option("sub1_opt1"),
Option("-SO2"),
Subcommand(
"sub1_sub1"
)
),
Subcommand(
"sub2"
)
)
```

他们拥有如下共同参数:

- `help_text`: 传入该组件的帮助信息
Expand Down Expand Up @@ -237,8 +267,8 @@ assert alc.parse("test123 BARabc").matched

```python
>>> from arclet.alconna import Alconna, Option, Args, append
>>> alc = Alconna("gcc", Option("--flag|-F", Args["content", str], action=append))
>>> alc.parse("gcc -Fabc -Fdef -Fxyz").query("flag.content")
>>> alc = Alconna("gcc", Option("--flag|-F", Args["content", str], action=append, compact=True))
>>> alc.parse("gcc -Fabc -Fdef -Fxyz").query[list[str]]("flag.content")
['abc', 'def', 'xyz']
```

Expand All @@ -247,7 +277,7 @@ assert alc.parse("test123 BARabc").matched
```python
>>> from arclet.alconna import Alconna, Option, Args, count
>>> alc = Alconna("pp", Option("--verbose|-v", action=count, default=0))
>>> alc.parse("pp -vvv").query("verbose.value")
>>> alc.parse("pp -vvv").query[int]("verbose.value")
3
```

Expand Down Expand Up @@ -291,13 +321,13 @@ with namespace(config.default_namespace.name) as np:

半自动补全为用户提供了推荐后续输入的功能。

补全默认通过 `--comp``-cp` 触发:(命名空间配置可修改名称)
补全默认通过 `--comp``-cp` `?` 触发:(命名空间配置可修改名称)

```python
from arclet.alconna import Alconna, Args, Option

alc = Alconna("test", Args["abc", int]) + Option("foo") + Option("bar")
alc.parse("test --comp")
alc.parse("test ?")

'''
output
Expand All @@ -323,40 +353,48 @@ output
>>> from arclet.alconna import Alconna, Args
>>> alc = Alconna("setu", Args["count", int])
>>> alc.shortcut("涩图(\d+)张", {"args": ["{0}"]})
'Alconna::setu 的快截指令: "涩图(\\d+)张" 添加成功'
'Alconna::setu 的快捷指令: "涩图(\\d+)张" 添加成功'
>>> alc.parse("涩图3张").query("count")
3
```

`shortcut` 的第一个参数为快捷指令名称,第二个参数为 `ShortcutArgs`,作为快捷指令的配置

```python
class ShortcutArgs(TypedDict, Generic[TDC]):
class ShortcutArgs(TypedDict):
"""快捷指令参数"""

command: NotRequired[TDC]
command: NotRequired[DataCollection[Any]]
"""快捷指令的命令"""
args: NotRequired[list[Any]]
"""快捷指令的附带参数"""
fuzzy: NotRequired[bool]
"""是否允许命令后随参数"""
prefix: NotRequired[bool]
"""是否调用时保留指令前缀"""
```

`fuzzy` 为 False 时,传入 `"涩图1张 abc"` 之类的快捷指令将视为解析失败

快捷指令允许三类特殊的 placeholder:

- `{%X}`: 只用于 `command`, `setu {%0}`表示此处填入快截指令后随的第 X 个参数。
- `{%X}`: 如 `setu {%0}`表示此处填入快捷指令后随的第 X 个参数。

例如,若快捷指令为 `涩图`, 配置为 `{"command": "setu {%0}"}`, 则指令 `涩图 1` 相当于 `setu 1`

- `{*}`: 只用于 `command`, 表示此处填入所有后随参数,并且可以通过 `{*X}` 的方式指定组合参数之间的分隔符。
- `{X}`: 用于 `command``args`表示此处填入可能的正则匹配的组:
- `{*}`: 表示此处填入所有后随参数,并且可以通过 `{*X}` 的方式指定组合参数之间的分隔符。
- `{X}`: 表示此处填入可能的正则匹配的组:
-`command` 中存在匹配组 `(xxx)`,则 `{X}` 表示第 X 个匹配组的内容
-`command` 中存储匹配组 `(?P<xxx>...)`, 则 `{X}` 表示名字为 X 的匹配结果

除此之外, 通过内置选项 `--shortcut` 可以动态操作快捷指令。

例如:

- `cmd --shortcut <key> <cmd>` 来增加一个快捷指令
- `cmd --shortcut list` 来列出当前指令的所有快捷指令
- `cmd --shortcut delete key` 来删除一个快捷指令

### 使用模糊匹配

模糊匹配通过在 Alconna 中设置其 CommandMeta 开启。
Expand All @@ -370,3 +408,77 @@ alc = Alconna("test_fuzzy", meta=CommandMeta(fuzzy_match=True))
alc.parse("test_fuzy")
# output: test_fuzy is not matched. Do you mean "test_fuzzy"?
```

## 解析结果

`Alconna.parse` 会返回由 **Arparma** 承载的解析结果。

`Arpamar` 会有如下参数:

- 调试类

- matched: 是否匹配成功
- error_data: 解析失败时剩余的数据
- error_info: 解析失败时的异常内容
- origin: 原始命令,可以类型标注

- 分析类
- header_match: 命令头部的解析结果,包括原始头部、解析后头部、解析结果与可能的正则匹配组
- main_args: 命令的主参数的解析结果
- options: 命令所有选项的解析结果
- subcommands: 命令所有子命令的解析结果
- other_args: 除主参数外的其他解析结果
- all_matched_args: 所有 Args 的解析结果

`Arparma` 同时提供了便捷的查询方法 `query[type]()`,会根据传入的 `path` 查找参数并返回

`path` 支持如下:

- `main_args`, `options`, ...: 返回对应的属性
- `args`: 返回 all_matched_args
- `main_args.xxx`, `options.xxx`, ...: 返回字典中 `xxx`键对应的值
- `args.xxx`: 返回 all_matched_args 中 `xxx`键对应的值
- `options.foo`, `foo`: 返回选项 `foo` 的解析结果 (OptionResult)
- `options.foo.value`, `foo.value`: 返回选项 `foo` 的解析值
- `options.foo.args`, `foo.args`: 返回选项 `foo` 的解析参数字典
- `options.foo.args.bar`, `foo.bar`: 返回选项 `foo` 的参数字典中 `bar` 键对应的值
...

同样, `Arparma["foo.bar"]` 的表现与 `query()` 一致

## Duplication

**Duplication** 用来提供更好的自动补全,类似于 **ArgParse****Namespace**,经测试表现良好(好耶)。

普通情况下使用,需要利用到 **ArgsStub****OptionStub****SubcommandStub** 三个部分,

以pip为例,其对应的 Duplication 应如下构造:

```python
from arclet.alconna import OptionResult, Duplication, SubcommandStub

class MyDup(Duplication):
verbose: OptionResult
install: SubcommandStub # 选项与子命令对应的stub的变量名必须与其名字相同
```

并在解析时传入 Duplication:

```python
result = alc.parse("pip -v install ...", duplication=MyDup)
>>> type(result)
<class MyDup>
```

**Duplication** 也可以如 **Namespace** 一样直接标明参数名称和类型:

```python
from typing import Optional
from arclet.alconna import Duplication


class MyDup(Duplication):
package: str
file: Optional[str] = None
url: Optional[str] = None
```
7 changes: 7 additions & 0 deletions website/docs/best-practice/alconna/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,10 @@ description: 配置项
- **默认值**: `False`

是否全局使用原始消息 (即未经过 to_me 等处理的), 该选项会影响到 Alconna 的匹配行为。

## alconna_use_param

- **类型**: `bool`
- **默认值**: `True`

是否使用特制的 Param 提供更好的依赖注入,该选项不会对使用依赖注入函数形式造成影响
Loading

0 comments on commit dc4ac6d

Please sign in to comment.