跳转到主要内容

开发命令行界面的最佳方式

项目描述

Coleo

Coleo是一个以最小努力在Python中创建命令行界面的方法。

  • 在使用的位置声明选项。
  • 轻松扩展到具有数十个子命令和选项的扩展CLIs。

基本用法

首先,按照以下方式定义一个命令行界面

from coleo import Option, auto_cli, default

@auto_cli
def main():
    # The greeting
    greeting: Option = default("Hello")

    # The name to greet
    name: Option = default("you")

    return f"{greeting}, {name}!"

然后您可以在命令行上像这样运行它

$ python hello.py
Hello, you!
$ python hello.py --name Luke
Hello, Luke!
$ python hello.py --name Luke --greeting "Happy birthday"
Happy birthday, Luke!
$ python hello.py -h
usage: hello.py [-h] [--greeting VALUE] [--name VALUE]

optional arguments:
  -h, --help        show this help message and exit
  --greeting VALUE  The greeting
  --name VALUE      The name to greet
  • 任何使用Option注解的变量将成为一个选项。
  • 您可以使用default(value)提供默认值,尽管如果参数是必需的,则不需要这样做。
  • 如果变量上方有注释,则它将用作选项的文档。

选项类型

默认情况下,所有参数都被解释为字符串,但您可以为参数指定不同的类型

@auto_cli
def main():
    # This argument will be converted to an int
    x: Option & int
    # This argument will be converted to a float
    y: Option & float
    return x + y

布尔标志

如果类型为bool,则选项将不接受任何参数,例如

@auto_cli
def main():
    flag: Option & bool = default(False)
    return "yes!" if flag else "no!"

像这样使用它

$ python script.py --flag
yes!
$ python script.py
no!

您也可以否定标志,这意味着您想要提供一个选项,该选项将在变量中存储False而不是True。例如

@auto_cli
def main():
    # [negate]
    flag: Option & bool = default(True)
    return "yes!" if flag else "no!"

默认情况下,上面的代码将创建一个名为--no-的标志

$ python script.py
yes!
$ python script.py --no-flag
no!

如果您想使选项为--xyz-n,请写入[negate: --xyz -n]。这将覆盖默认的--no-flag选项。

请注意,使用[negate]将删除--flag,因为我们假设它默认为True,因此不需要此选项。

如果您愿意,您可以使用同时设置标志为True和False的选项,使用[false-options]。您可以使用[false-options-doc](如果未提供,Coleo将使用合理的默认值)来记录这些选项

@auto_cli
def main():
    # Set the flag to True
    # [options: -y]
    # [false-options: -n]
    # [false-options-doc: Set the flag to False]
    flag: Option & bool = default(None)
    return flag
$ python script.py
None
$ python script.py -y
True
$ python script.py -n
False

文件

使用 coleo.FileType(或 argparse.FileType,两者相同)来打开文件进行读取或写入

@auto_cli
def main():
    grocery_list: Option & coleo.FileType("r")
    with grocery_list as f:
        for food in f.readlines():
            print(f"Gotta buy some {food}")

配置

您可以使用 coleo.configcoleo.ConfigFile 来操作配置文件

@auto_cli
def main():
    # ConfigFile lets you read or write a configuration file
    book: Option & ConfigFile
    contents = book.read()
    contents["xyz"] = "abc"
    book.write(contents)

    # config will read the file for you or parse the argument as JSON
    magazine: Option & config
    print(magazine)

使用方式如下

$ python librarian.py --book alice.json --magazine vogue.json
$ python librarian.py --book history.yaml --magazine gamez.toml
$ python librarian.py --book physics.json --magazine '{"a": 1, "b": 2}'
# etc

支持的扩展名有 jsonyamltoml(后两个需要安装 pyyamltoml 包)。

其他

任何函数都可以用作参数的“类型”。例如,如果您想在命令行中提供列表和字典,可以简单地使用 json.loads(尽管通常使用 coleo.config 更好,因为它还可以以各种格式读取文件)

@auto_cli
def main():
    obj: Option & json.loads
    return type(obj).__name__
$ python json.py --obj 1
int
$ python json.py --obj '"hello"'
str
$ python json.py --obj '{"a": 1, "b": 2}'
dict

如果您非常冲动且不关心安全性,甚至可以使用 eval

@auto_cli
def main():
    obj: Option & eval
    return type(obj).__name__
$ python eval.py --obj "1 + 2"
int
$ python eval.py --obj "lambda x: x + 1"
function

自定义

使用形式为 # [<instruction>: <args ...>] 的注释可以自定义选项解析器

@auto_cli
def main():
    # This argument can be given as either --greeting or -g
    # [alias: -g]
    greeting: Option = default("Hello")

    # This argument is positional
    # [positional]
    name: Option = default("you")

    # This argument can only be given as -n
    # [options: -n]
    ntimes: Option & int = default(1)

    for i in range(ntimes):
        print(f"{greeting}, {name}!")

用法如下

$ python hello.py Alice -g Greetings -n 2
Greetings, Alice!
Greetings, Alice!

以下是一些可用的自定义项

  • [alias: ...] 定义一个或多个选项,它们是主选项的别名。选项之间用空格、逗号或分号分隔。
  • [options: ...] 定义一个或多个选项,用于覆盖默认选项。选项之间用空格、逗号或分号分隔。
  • [positional] 定义一个位置参数。
    • [positional: n]:n 个位置参数(返回一个列表)。
    • [positional: ?]:一个可选的位置参数
    • [positional: *]:零个或多个位置参数
    • [positional: +]:一个或多个位置参数
  • [remainder] 代表未被参数解析器匹配的所有参数
  • [nargs: n] 声明该选项接受 n 个参数
    • [nargs: ?]:一个可选参数
    • [nargs: *]:零个或多个参数
    • [nargs: +]:一个或多个参数
    • [nargs: **][nargs: --]:所有剩余参数,包括 --args
  • [action: <action>] 自定义要执行的操作
    • [action: append] 允许您多次使用选项,将结果累积在列表中(例如,python app.py -a 1 -a 2 -a 3,将 [1, 2, 3] 放入 a
  • [metavar: varname] 更改帮助字符串中选项后面的变量名,例如 --opt METAVAR
  • [group: groupname] 将选项放入一个命名组。同一组中的选项将在帮助中一起出现。
  • 仅对 bool 选项有效
    • [negate: ...] 改变选项,使其在给定时将变量设置为 False 而不是 True。可以提供选项的空间/逗号别名,否则标志将命名为 --no-<optname>
    • [false-options: ] 提供一个选项列表,这些选项将标志设置为 False。
    • [false-options-doc: ] 为使用上述语句给出的选项提供文档。

子命令

您可以通过使用 auto_cli 装饰器来创建一个具有子命令层次结构的界面

@auto_cli
class main:
    class calc:
        def add():
            x: Option & int
            y: Option & int
            return x + y

        def mul():
            x: Option & int
            y: Option & int
            return x * y

        def pow():
            base: Option & int
            exponent: Option & int
            return base ** exponent

    def greet():
        greeting: Option = default("Hello")
        name: Option = default("you")
        return f"{greeting}, {name}!"

该类仅包含结构,永远不会被实例化,因此不要将这些函数的参数列表中添加 self

然后您可以使用它如下

$ python multi.py greet --name Alice --greeting Hi
Hi, Alice!
$ python multi.py calc add --x=3 --y=8
11

共享参数

您可以在子命令之间共享行为和参数,或将复杂功能拆分为多个部分。例如,您的应用程序中的多个子命令可能需要 API 密钥,该密钥可以是命令行中提供的,也可以从文件中读取。这就是您如何在所有子命令之间共享此行为的方式

from coleo import Option, auto_cli, config, default, tooled

@tooled
def apikey():
    # The API key to use
    key: Option = default(None)
    if key is None:
        # If no key parameter is given on the command line, try to read it from
        # some standard location.
        key = config("~/.config/myapp/config.json")["key"]
    return key

@auto_cli
class main:
    def search():
        interface = Application(apikey())
        query: Option
        return interface.search(query)

    def install():
        interface = Application(apikey())
        package: Option
        return interface.install(package)

如果一个函数被 @tooled 装饰,并从一个主函数(或另一个 tooled 函数)中被调用,Coleo 将在该函数中搜索参数。因此,任何调用 apikey() 的子命令都将获得一个 --key 选项。

除了这个,您还可以通过在多个函数中定义具有相同类型的相同参数来“共享”参数。Coleo会将它们都设置为相同的值。

例如,在上面的示例中,您可以很容易地让用户指定包含密钥的文件的路径,只需替换

key = config("~/.config/myapp/config.json")["key"]

# ==>

config_path: Option = default("~/.config/myapp/config.json")
key = config(config_path)["key"]

当然,您也可以在任何需要读取一些配置值的函数中声明config_path参数。

run_cli

from coleo import Option, auto_cli

@auto_cli
def main():
    x: Option
    return x

等同于

from coleo import Option, run_cli, tooled

@tooled
def main():
    x: Option
    return x

result = run_cli(main)
if result is not None:
    print(result)

非CLI使用

您可以使用setvars而不使用auto_cli来设置参数

from coleo import Option, setvars, tooled

@tooled
def greet():
    greeting: Option = default("Hello")
    name: Option = default("you")
    return f"{greeting} {name}!"

with setvars(greeting="Hi", name="Bob"):
    assert greet() == "Hi bob!"

注意

  • 使用setvars时,您必须用@tooled装饰函数(这是auto_cli为您做的事情)。
  • setvars完全绕过了选项解析,类型注解将不会用于包装这些值。换句话说,如果变量被注解为Option & int,您提供“1”的值,它将保持为字符串。

与Ptera一起使用

Coleo基于Ptera,Ptera的所有功能都在@tooled标记的函数上实际可用。例如,使用上面的示例

# Set the variables in the greet function -- it's a bit like making an object
hibob = greet.new(greeting="Hi", name="Bob")
assert hibob() == "Hi Bob!"

# Same as above but this would also change greeting/name in any other function
# that is called by greet, and so on recursively (a bit like dynamic scoping)
hibob = greet.tweaking({"greeting": "Hi", "name": "Bob"})
assert hibob() == "Hi Bob!"

# More complex behavior
from ptera import overlay
with overlay.tweaking({
    "greet(greeting='Bonjour') > name": "Toto"
}):
    assert greet() == "Hello you!"
    assert greet.new(greeting="Hi")() == "Hi you!"
    assert greet.new(greeting="Bonjour")() == "Bonjour toto!"

阅读Ptera的文档以获取更多信息。请注意,Ptera不仅限于带有Option标签的变量,它可以操作工具函数中的任何变量。

项目详情


下载文件

下载您平台上的文件。如果您不确定选择哪一个,请了解有关安装包的更多信息。

源分布

coleo-0.3.4.tar.gz (60.2 kB 查看哈希)

上传时间

构建分布

coleo-0.3.4-py3-none-any.whl (12.2 kB 查看哈希)

上传时间 Python 3

支持者

AWS AWS 云计算和安全赞助商 Datadog Datadog 监控 Fastly Fastly CDN Google Google 下载分析 Microsoft Microsoft PSF 赞助商 Pingdom Pingdom 监控 Sentry Sentry 错误记录 StatusPage StatusPage 状态页面