Alconna
该教程是对整个 Alconna 特性的大致归纳,以帮助读者熟悉 Alconna 的使用与开发。
有关更多详细内容,请参阅 文档
Alconna
隶属于 ArcletProject
,是一个简单、灵活、高效的命令参数解析器, 并且不局限于解析命令式字符串。
示例的 Alconna
使用:
from arclet.alconna import Alconna, Args, Option, Subcommand, count
alc = Alconna(
"pip",
Subcommand(
"install",
Args["package", str],
Option("-r|--requirement", Args["file", str]),
Option("-i|--index-url", Args["url", str]),
),
Option("-v|--version", action=count),
)
print(alc.parse("pip install nonebot2 -i https://mirrors.aliyun.com/pypi/simple/").all_matched_args)
# {'package': 'nonebot2', 'url': 'https://mirrors.aliyun.com/pypi/simple/'}
其特点有:
- 高效
- 直观的命令组件创建方式,例如选项别名,默认值,解析操作等
- 强大的类型解析与类型转换功能,具体在
Args
的使用上 - 自定义的帮助信息格式,帮助信息由内置选项
help
触发,用户可以选择自定义的 TextFormatter 获得不一样的输出格式 - 多语言支持,目前支持中文与英文
- 易用的快捷命令创建与使用,可由内置选项
shortcut
触发,或使用Alconna.shortcut()
方法 - 可创建命令补全会话, 以实现多轮连续的补全提示
- 可通过
Namespace
,CommandMeta
配置命令的行为与属性,例如通过Namespace
自定义内置选项的触发字段,或通过CommandMeta
启用模糊匹配 - 可嵌套的多级子命令
Duplication
能像argparse.Namespace
一样获取指定的解析结果并获得类型支持- 正则匹配支持
- ...
实时演示 (js):
安装
for Python
- pdm
- poetry
- pip
pdm add arclet-alconna
poetry add arclet-alconna
pip install --upgrade arclet-alconna
for Nodejs
npm install @arcletjs/alconna
组件
Alconna
拥有两大组件:Option 与 Subcommand。
Option
可以传入一组 alias
,如 Option("--foo|-F|--FOO|-f")
或 Option("--foo", alias=["-F"]
那么 -f, -F 与 --FOO 将等同于 --foo
Subcommand
则可以传入自己的 Option 与 Subcommand:
Subcommand("sub", Option("sub_opt"), Subcommand("sub_sub"), Args)
Subcommand("sub", Args, [Option("sub_opt"), Subcommand("sub_sub")])
他们拥有如下共同参数:
help_text
: 传入该组件的帮助信息dest
: 被指定为解析完成时标注匹配结果的标识符,不传入时默认为选项或子命令的名称 (name)requires
: 一段指定顺序的字符串列表,作为唯一的前置序列与命令嵌套替换对于命令
test foo bar baz qux <a:int>
来讲,因为foo bar baz
仅需要判断是否相等, 所以可以这么编写:Alconna("test", Option("qux", Args.a[int], requires=["foo", "bar", "baz"]))
提示requires 也可以在 name 中传入:
Option("foo bar baz qux")
default
: 默认值,在该组件未被解析时使用使用该值替换。特别的,使用
OptionResult
或SubcomanndResult
可以设置包括参数字典在内的默认值:from arclet.alconna import Option, OptionResult
opt1 = Option("--foo", default=False)
opt2 = Option("--foo", default=OptionResult(value=False, args={"bar": 1}))
选项操作
Option
可以特别设置传入一类 Action
,作为解析操作
Action
分为三类:
store
: 无 Args 时, 仅存储一个值, 默认为 Ellipsis; 有 Args 时, 后续的解析结果会覆盖之前的值append
: 无 Args 时, 将多个值存为列表, 默认为 Ellipsis; 有 Args 时, 每个解析结果会追加到列表中当存在默认值并且不为列表时, 会自动将默认值变成列表, 以保证追加的正确性
count
: 无 Args 时, 计数器加一; 有 Args 时, 表现与 STORE 相同当存在默认值并且不为数字时, 会自动将默认值变成 1, 以保证计数器的正确性。
Alconna
提供了预制的几类 action
:
store
,store_value
,store_true
,store_false
append
,append_value
count
紧凑命令
Alconna
, Option
与 Subcommand
可以设置 compact=True
使得解析命令时允许名称与后随参数之间没有分隔:
from arclet.alconna import Alconna, Option, CommandMeta, Args
alc = Alconna("test", Args["foo", int], Option("BAR", Args["baz", str], compact=True), meta=CommandMeta(compact=True))
assert alc.parse("test123 BARabc").matched
这使得我们可以实现如下命令:
>>> 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")
['abc', 'def', 'xyz']
当 Option
的 action
为 count
时,其自动支持 compact
特性:
>>> 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")
3
帮助信息
每个 Alconna 命令 都可以通过 --help
或 -h
触发命令的帮助信息,并且可以通过继承
arclet.alconna.components.output.TextFormatter
来个性化信息样式,如:
from arclet.alconna import Alconna, Args
from arclet.alconna.tools import MarkdownTextFormatter
alc = Alconna("test", Args["count#这是一个注释", int], formatter_type=MarkdownTextFormatter)
alc.parse("test --help")
'''
## Unknown
指令:
**test <count:int>**
### 注释:
```
count: 这是一个注释
```
'''
除此之外,你可以通过 command_manager
获取当前程序下的所有命令:
from arclet.alconna import command_manager
...
print(command_manager.all_command_help())
'''
# 当前可用的命令有:
- foo : Unknown
- bar : Unknown
- baz : Unknown
- qux : Unknown
# 输入'命令名 --help' 查看特定命令的语法
'''
Alconna 可以设置 meta.hide
参数以不被 command_manager 打印出来。
from arclet.alconna import Alconna, CommandMeta, command_manager
foo = Alconna("foo", meta=CommandMeta(hide=True))
...
print(command_manager.all_command_help())
'''
# 当前可用的命令有:
- bar : Unknown
- baz : Unknown
- qux : Unknown
# 输入'命令名 --help' 查看特定命令的语法
'''
配置
arclet.alconna.Namespace
表示某一命名空间下的默认配置:
from arclet.alconna import config, namespace, Namespace
from arclet.alconna.tools import ShellTextFormatter
np = Namespace("foo", prefixes=["/"]) # 创建 Namespace 对象,并进行初始配置
with namespace("bar") as np1:
np1.prefixes = ["!"] # 以上下文管理器方式配置命名空间,此时配置会自动注入上下文内创建的命令
np1.formatter_type = ShellTextFormatter # 设置此命名空间下的命令的 formatter 默认为 ShellTextFormatter
np1.builtin_option_name["help"] = {"帮助", "-h"} # 设置此命名空间下的命令的帮助选项名称
config.namespaces["foo"] = np # 将命名空间挂载到 config 上
同时也提供了默认命名空间配置与修改方法:
from arclet.alconna import config, namespace, Namespace
config.default_namespace.prefixes = [...] # 直接修改默认配置
np = Namespace("xxx", prefixes=[...])
config.default_namespace = np # 更换默认的命名空间
with namespace(config.default_namespace.name) as np:
np.prefixes = [...]
半自动补全
半自动补全为用户提供了推荐后续输入的功能。
补全默认通过 --comp
或 -cp
触发:(命名空间配置可修改名称)
from arclet.alconna import Alconna, Args, Option
alc = Alconna("test", Args["abc", int]) + Option("foo") + Option("bar")
alc.parse("test --comp")
'''
output
以下是建议的输入:
* <abc: int>
* --help
* -h
* -sct
* --shortcut
* foo
* bar
'''
补全会话
补全会话基于半自动补全,提供了交互操作补全的接口
补全会话通过创建 CompSession
并进入上下文触发:
from arclet.alconna import Alconna, Args, Option, CompSession
alc = Alconna("test", Args["abc", int]) + Option("foo") + Option("bar")
with CompSession(alc) as comp:
alc.parse("test")
if comp.available:
print("current completion:", comp.current())
print("next completion:", comp.tab())
with comp:
res = comp.enter(["1"])
assert res.matched
快捷指令
快捷指令顾名思义,可以为基础指令创建便捷的触发方式
一般情况下你可以通过 Alconna.shortcut
进行快捷指令操作 (创建,删除);
>>> from arclet.alconna import Alconna, Args
>>> alc = Alconna("setu", Args["count", int])
>>> alc.shortcut("涩图(\d+)张", {"args": ["{0}"]})
'Alconna::setu 的快截指令: "涩图(\\d+)张" 添加成功'
>>> alc.parse("涩图3张").query("count")
3
shortcut
的第一个参数为快捷指令名称,第二个参数为 ShortcutArgs
,作为快捷指令的配置
class ShortcutArgs(TypedDict, Generic[TDC]):
"""快捷指令参数"""
command: NotRequired[TDC]
"""快捷指令的命令"""
args: NotRequired[list[Any]]
"""快捷指令的附带参数"""
fuzzy: NotRequired[bool]
"""是否允许命令后随参数"""
当 fuzzy
为 False 时,传入 "涩图1张 abc"
之类的快捷指令将视为解析失败
快捷指令允许三类特殊的 placeholder:
{%X}
: 只用于command
, 如setu {%0}
,表示此处填入快截指令后随的第 X 个参数。例如,若快捷指令为
涩图
, 配置为{"command": "setu {%0}"}
, 则指令涩图 1
相当于setu 1
{*}
: 只用于command
, 表示此处填入所有后随参数,并且可以通过{*X}
的方式指定组合参数之间的分隔符。{X}
: 用于command
与args
, 表示此处填入可能的正则匹配的组:- 若
command
中存在匹配组(xxx)
,则{X}
表示第 X 个匹配组的内容 - 若
command
中存储匹配组(?P<xxx>...)
, 则{X}
表示名字为 X 的匹配结果
- 若
除此之外, 通过内置选项 --shortcut
可以动态操作快捷指令。
命令参数
Args 在 Alconna 中有非常重要的地位,甚至称得上是核心,比 Alconna 重要十倍甚至九倍。
其通常以 Args[name1, var1, default1][name2, var2][Arg(name3, var3), Arg(name4, var4, default4)][...]
的方式构造一个 Args。
其中,name 一定是字符串,而 var 一般为参数的类型,default 为具体的值或者 arclet.alconna.args.Field。
name
name
的作用是用以标记解析出来的参数并存放于 Arparma 中,以方便用户调用。
其有三种为 Args 注解的标识符,为 ?
、/
与 !
。标识符与 name 之间建议以 ;
分隔:
!
标识符表示该处传入的参数应不是规定的类型,或不在指定的值中。?
标识符表示该参数为可选参数,会在无参数匹配时跳过。/
标识符表示该参数的类型注解需要隐藏。
另外,对于参数的注释也可以标记在 name
中,其与 name 或者标识符 以 #
分割:
foo#这是注释;?
或 foo?#这是注释
var
var 负责命令参数的类型检查与类型转化
var 可以是以下几类:
- 存在于
nepattern.all_patterns
中的类型/字符串,用以替换为预制好的 BasePattern - 字符串
- 若字符串以
"re:"
打头,表示将其转为正则解析表达式,并且返回类型为匹配字符串 - 若字符串以
"rep:"
打头,表示将其转为特殊的RegexPattern
,并且返回类型为re.Match
- 其他字符串将作为直接的比较对象
- 若字符串以
- 列表,其中可存放 BasePattern、类型或者任意参数值,如字符串或者数字
Union
、Optional
、Literal
等会尝试转换为List[Type]
Dict[type1,type2]
、List[type]
、Set[type]
- 一般的类型,其会尝试比较传入参数的类型是否与其相关
- AnyOne、AllParam,作为泛匹配的标识符
- AnyString, 会将传入的任意参数转为字符串
- 预制好的字典, 表示传入值依据该字典的键决定匹配结果
Annotated[type, Callable[..., bool], ...]
,表示为某一类型添加校验器Callable[[P], T]
,表示会将传入的参数 P 经过该调用对象并将返回值 T 作为匹配结果- ...
内置的类型检查包括 int
、str
、float
、bool
、'url'
、'ip'
、'email'
、list
、dict
、tuple
、set
、Any
、bytes
、hex
、datetime
等。
若 Arg
只传入了 key
,则 var
自动选择 key
的值作为比较对象
另外,Alconna
提供了两类特殊的类型用以实现限制功能:
- MultiVar:将该参数标记为需要获取可变数量或指定数量的数据,通过填入
flag: int | Literal['+', '*']
实现 - KeyWordVar:将该参数标记为需要同时写入参数名才认定为合法参数,默认形式为
key=arg
,可指定分隔符
当 MultiVar 与 KeyWordVar 一起使用时, 该参数表示为需要接收多个 key=arg
形式的数据, 类似 **kwargs
Arg
Arg 是 Args 的最小单位:
Args[Arg(k1, v1), Arg(k2, v2), ...]
Arg 初始化时可以传入:
- name: 参数名
- value: 参数类型
- field: 参数域
- seps: 参数分隔符
- notice: 参数注释
- flags: 参数标识符
Arg 可以通过传入 seps
指定该参数如何与后续数据区分,也可通过 Args 的 separators
参数统一设置
解析结果
Alconna.parse
会返回由 Arparma 承载的解析结果。
Arpamar
会有如下参数:
调试类
- matched: 是否匹配成功
- error_data: 解析失败时剩余的数据
- error_info: 解析失败时的异常内容
- origin: 原始命令,可以类型标注
分析类
header_match: 命令头部的解析结果,包括原始头部、解析后头部、解析结果与可能的正则匹配组
main_args: 命令的主参数的解析结果
options: 命令所有选项的解析结果
subcommands: 命令所有子命令的解析结果
other_args: 除主参数外的其他解析结果
all_matched_args: 所有 Args 的解析结果
Arparma
同时提供了便捷的查询方法 query()
,会根据传入的 path
查找参数并返回
path
支持如下:
main_args
,options
, ...: 返回对应的属性args
: 返回 all_matched_argsmain_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 应如下构造:
from arclet.alconna import OptionResult, Duplication, SubcommandStub
class MyDup(Duplication):
verbose: OptionResult
install: SubcommandStub # 选项与子命令对应的stub的变量名必须与其名字相同
并在解析时传入 Duplication:
result = alc.parse("pip -v install ...", duplication=MyDup)
>>> type(result)
<class MyDup>
Duplication 也可以如 Namespace 一样直接标明参数名称和类型:
from typing import Optional
from arclet.alconna import Duplication
class MyDup(Duplication):
package: str
file: Optional[str] = None
url: Optional[str] = None
使用模糊匹配
模糊匹配通过在 Alconna 中设置其 CommandMeta 开启。
模糊匹配会应用在任意需要进行名称判断的地方,如命令名称,选项名称和参数名称(如指定需要传入参数名称)。
from arclet.alconna import Alconna, CommandMeta
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 的 i18n 支持使用了 Tarina.lang
其语言文件配置位于 arclet.alconna.i18n
下
您可以通过 tarina.lang.select
切换语言配置,也可以通过 tarina.lang.set
对单一文本进行修改。
参考链接
教程; 👉点击
QQ 交流群: 🔗链接
友链: 📚文档