Skip to main content

cli 脚本的简单样板

项目描述

以最少的设置创建命令行界面。

派皮 Python 版本 PyPI 许可证 构建状态 依赖项

clima - 带有模式的命令行界面

目录

简要地

特征

Clima 使用一些现成的功能处理加载和解析命令行参数,包括:

  • 全局配置对象
    • 默认值的快速定义
    • 定义默认值可以作为命令行帮助的描述
    • 带注释的类型处理
  • 带有配置文件的定义
  • 环境变量
    • 加载 .env 文件
  • 用pass存储的秘密
  • post_init 钩子

命令行定义

为您的程序创建命令行界面:

  1. 从 clima 包中导入所有必要的部分
  2. (可选)定义配置,即Schema
  3. 定义命令行命令,即 CLI 类:

示例 ascii

示例:设置配置和准备就绪的命令行界面。

from clima import c

@c
class Cli:
    def say_hi(self):
        print('oh hi - whatever this is..')

命令行使用形式可以很简单:

 my_tool say_hi

漂亮的配置对象

from clima import c

# Defining the settings (configuration object)
class S(Schema):
    place = 'world'
    
@c
class Cli:
    def say_hi(self):
        # using configuration object 'c'
        print(f'oh hi - {c.place}')

同样,命令行使用形式可以很简单:

 my_tool say_hi
 my_tool say_hi --place 'other world'

有关更多示例,请参阅examples文件夹和其他部分。例如,该文件夹包含类似于上述示例的内容。

安装

pip install --user clima

目录

用法

请参阅 中的示例文件examples/script_example.py。这是此类脚本中各个部分的概要(改编自模块示例中的另一个示例)。

首先,导入所需的组件:

from clima import c, Schema

在您的代码中,定义Schema子类:

class Configuration(Schema):
    a: str = 'A'  # a description
    x: int = 1  # x description

这里的“配置”是一个任意名称,没有魔法。继承的Schema类定义了属性(即在这个例子中)ax

注意子类的具体格式Schema

    # attribute[: type] = default value  [# Description for the --help]
    a: str = 'A'  # a description

a是稍后可以在代码中调用的属性c.a。在此示例中,它的类型为“str”,默认值为“A”。方括号中的值[]是可选的。

Clima 在命令行帮助打印输出的定义之后解析注释。换句话说,当使用参数“--help”调用程序时,Clima 会解析所有这些要显示的部分。例如像这样:

./script.py foo -h

现在将产生:

 Usage:       script.py foo [ARGS]
 
 Description: Args:
     --a (str): a description (Default is 'A')
     --x (int): x description (Default is 1)    

本自述文件中显示的示例位于examples/readme_example.py.

Clima 将方法解析为带有各自文档字符串的子命令,当类用装饰器包装时@c,并将在子命令的帮助打印输出中显示这些。

子命令应该定义如下:

@c
class Cli:
    def subcommand_foo(self):
        """This will be shown in --help for subcommand-foo"""
        print('foo')
        print(c.a)
        print(c.x)

    def subcommand_bar(self):
        """This will be shown in --help for subcommand-bar"""
        print('bar')

请注意 - 的双重用法,c首先作为装饰器,然后作为方法内的解析配置:

...
    ...
    print(c.a)
    print(c.x)

作为装饰器,@c将要解析的类定义为子命令。作为一个对象c,它用于访问所有参数。

目录

示例和平台

在 Linux、macOS 和 Windows 上试用并使用。但是,python 中的打包和依赖管理有时很麻烦,而且您的工作量可能会有所不同。

示例目录中的更多示例,其中包含已定义子命令和帮助的打印输出。

测试示例

可以通过克隆 repo 并从 repo 目录根目录运行(在 linux 等上)来试用这些示例:

git clone https://github.com/d3rp/clima.git 
cd clima
PYTHONPATH=$PWD python ./examples/readme_example.py foo -h

运行包装模块的示例:

PYTHONPATH=$PWD python ./examples/module_example/__main__.py -h
PYTHONPATH=$PWD python ./examples/module_example/__main__.py subcommand-foo -h
PYTHONPATH=$PWD python ./examples/module_example/__main__.py subcommand-bar
...

输出应该类似于这个(fire v0.1.3 打印出 Args,fire v0.2.1 没有(虽然它看起来好多了))

$ tester subcommand-foo -- -h

Type:        method
String form: <bound method Cli.subcommand_foo of <__main__.Cli object at 0x000002995AD74BE0>>
File:        C:\Users\foobar\code\py\clima\tester\__main__.py
Line:        18
Docstring:   This will be shown in --help for subcommand-foo
Args:
    --a (str): a description (Default is 'A')
    --x (int): x description (Default is 1)

Usage:       __main__.py subcommand-foo [--X ...]

所有示例脚本都可以通过安装诗歌并运行run_examples.bash 脚本来运行:

pip install --user poetry
./run_examples.bash

目录

版本印刷

版本打印通过version子命令工作,并为(诗歌)打包脚本提供版本检查功能。因此,随着诗歌版本的增加,Clima 将处理打包工具的当前版本,从而提供一个子命令,例如:

my_tool version

Clima 将version属性解析为c对象,因此如果您想控制它,您可以用 or 覆盖它,post_init否则处理c.version

自动完成

..in IDE (wip)

此外,要在 IDE 中启用自动完成功能,这个 hack 就足够了:

c: Configuration = c

将它放在“全局命名空间”中,例如在定义模板之后。具体示例请参见examples/script_example.py

一切完成后,导入的c变量应该包含配置的所有点点滴滴。它可以在 Cli 类中使用,也可以在代码库周围导入,从而将所有配置封装到一个容器中,并可以快速访问属性 ( c.a, c.x, ...)。

..在 bash

使用参数运行脚本-- --completion应该打印一个自动完成声明以包含在 bash 完成文件中:

my_tool -- --completion

待定:zsh 等完成

发布初始化钩子

post_init根据Schema子类或定义,有两种方法可以定义挂钩Cli

cli.post_init()

在某些情况下,从给定的参数中推断出特定的默认值会很有帮助,例如,在跨平台构建中只允许最少的 CLI 参数。对于那些情况,在类中post_init定义时有一个钩子,例如:post_init()Cli

@c
class Cli:

    @staticmethod
    def post_init(s):
        if s.platform = 'win':
            self.bin_path = 'c:/Users/foo/bar'
        else:
            s.bin_path = '/Users/mac/sth'
        
    def subcommand(self):
        pass

该方法可以访问 cli 参数,但不能将新变量引入模式。

这可以说是 post_inits 的两种变体中更有用的一种。

注意:这两个选项的签名post_init()不同。就目前而言,它是一个@staticmethod

Schema.post_init()

此替代方案提供了可使用 CLI 参数覆盖的类似 post_init 的功能。

class SoAdvanced(Schema):

    platform: str = 'win'  # a description
    bin_path: pathlib.Path = ''  # x description
    
    def post_init(self, *args):
        if self.platform = 'win':
            self.win_specific_field = 'All your files are locked by us..'

注意:这个 post_init() 不能访问 CLI 参数,但是Schemapost_init 可以将新的属性/属性/字段/参数引入配置,而Cli-class post-init 不能。的钩子Schemapost_init()模式对象初始化之后运行,但在命令行对象初始化之前运行。

目录

配置选项

当大多数用例都遵循类似的模式时,在命令行上编写一长串参数是很乏味的。有几个选项可供选择以方便配置的使用。

装饰器/配置将c多个配置选项按优先级顺序链接在一起(数字越小优先级越高):

  1. 命令行参数
  2. 环境变量
  3. .env 文件
  4. 配置文件定义
  5. ~/.password-store如果安装了 gnugpg,则解密密码
  6. 子类继承中的默认值Schema

配置文件和环境变量

配置文件应使用后缀.cfg或命名.conf,例如foo.conf,并具有包含显式“Clima”部分的 ini 类型格式:

# foo.conf
[Clima]
x = 2

键与模式类的声明中的键相同。可以为所有、部分或没有属性定义默认值。这同样适用于环境变量。

# linux example
X=2 tester subcommand-foo

以这种方式定义的配置文件可以位于当前工作目录中,或者 - 如果您Schema定义了一个 cwd字段 - 那里。Clima 将尝试使用它找到的第一个配置文件,因此可能会产生一些警告。

class Conf(Schema):
    cwd = ''

# Running ./script.py --cwd <folder> would automatically load the first *.conf file in <folder>

使用配置定义进行类型转换

定义可以具有类型注释,Schema用于转换给定的参数。例如

class C(Schema):
    p: Path = ''  # Path to something

结果c.p的类型转换为Path

主目录中的配置文件

Schema也可以通过定义magic field在配置类中定义配置文件(一个继承) CFG

例如,假设命令my_tool(打包等)有一个用户配置文件位于~/.my_tool.conf. 现在只需添加CFG = Path.home() / '.my_tool.conf到 Schema 即可处理:

from pathlib import Path

class S(Schema):
    bing = 'bang'
    CFG = Path.home() / '.my_tool.conf'

然后,例如,配置文件将被写为:

#~/.my_tool.conf
[Clima]
bing = diudiu

运行该命令my_tool将在配置文件中生成值,但仍然可以覆盖参数。

my_tool run 
# diudiu

my_tool run --bing bam
# bam

.env 文件

这是由dotenv处理的。简而言之, Schema子类中定义的所有默认值都可以通过以下方式覆盖:

<field> = <value>

或者

export <field> = <value>

密码解包/解密

如果安装了 gnugpg(2),clima 可以利用它即时解密秘密。

注意:目前这最方便使用没有密码的 gpg-keys。Gpg 处理可能在某些平台或配置上失败的密码提示。

\n注意 2:解密时会去除前导和尾随空格(包括换行符)。

pass(非必需)可用于将密码作为 gpg 加密文件存储在主目录下。Clima 使用默认路径 ~/.password-store 和在其中找到的文件。然后它将参数与存储的密码匹配,例如:

 tree -A ~/.password-store                                                                                                                                                                                                                                                                             ✔ | 41s | anaconda3 
 /Users/me/.password-store
 ├── work
 │   ├── ci
 │   │   ├── sign_id.gpg
 │   │   ├── sign_pw.gpg
 ... ... ...

以及相应的Schema定义:

 class Conf(Schema):
     sign_id: str = ''  # signing id for the CI
     sign_pw: str = ''  # signing pw for the CI

将接受这些参数作为 cli 参数,或者如果省略,将遍历.password-store并解密找到的值sign_id.gpg并将找到sign_pw.gpg的值放置在配置对象c中。

目录

通过 Fire 的附加功能

有关不错的附加功能,请参阅Python Fire's Flags 文档,例如:

# e.g. tester.py is our cli program
tester.py subcommand-foo -- --trace
tester.py -- --interactive
tester.py -- --completion

截断错误打印

这个功能作为一个自以为是的选项上升,我承认,它应该是用户可以绕过的东西。尽管我已经专业使用python几年了,但我仍然对它的错误打印不满意。在引发异常时,Clima 会截断错误列表并尝试提供更易读的“第一个”故障点版本。整个回溯被写入日志文件exception_traceback.log,以检查截断的输出是否提供了足够的信息。

注意:运行示例时,exception_traceback.log文件将写入examples目录中

Error (full trace in exception.log):

traceback_example.py:7   ::  lumberjack()            :  self.bright_side_of_death()  =>
traceback_example.py:12  ::  bright_side_of_death()  :  return tuple()[0]            =>  IndexError

IndexError: tuple index out of range

为初学者运行脚本的方法

这里有一些关于如何使用 Clima 包装脚本的建议。

将可执行脚本链接到 ~/.local/bin

假设这些行是写在一个名为script.py. 终端中的命令行用法将是例如:

python script.py foo
python script.py foo --a 42

将此行添加到script.py

#!/usr/bin/env python

并更改其执行权限(mac、linux):

chmod +x script.py

允许更短的执行方式:

./script.py foo

现在可以将其链接为临时命令,例如:

ln -s $PWD/script.py ~/.local/bin/my_command

打包一个模块(pip 就绪)

对于 pip 可安装包,可以将其打包为可运行命令- 在公共或私人 pypi 等中发布 - 然后接近首先显示的便利因素。

pip install my_tool
my_command foo -h

用诗歌发表是相当直接的。首先在 pypi.org 中创建一个帐户,然后:

cd <project directory>
poetry build
poetry publish

您可以使用version来提升版本:

poetry version patch

从源代码构建/安装

这个回购是基于诗歌

git clone https://github.com/d3rp/clima.git 
cd clima
poetry install --no-dev

--no-dev用于在没有开发工具的情况下安装运行环境。

目录

详细描述和背景

您可以将子命令编写为封装“业务逻辑”的类。Clima 将命令行参数封装为一个容器,当您在一个简单的模式类中声明它们时映射其属性。

换句话说,您可以使用它来将脚本包装为命令行命令,而无需求助于 Bash 或在 python 中维护参数解析。它消除了重复注释以--help记住参数是什么以及它们做了​​什么的需要。使用一些装饰器魔法可以提供 CLI 程序的典型用户体验(例如,参数解析和验证、--help、子命令……)。

Clima 背后的前提是一个简单的脚本通常在整个用户代码中使用脚本范围的全局配置,或者换句话说,在代码的不同部分访问的程序的上下文。Clima 使用代码中的默认参数和一些进一步的补充选项填充该上下文或配置。然后可以通过全局c变量或容器访问这些变量,这些变量或容器可以在代码库中以最少的额外工作被扔掉。几乎不需要额外的努力,Clima 可以在 IDE 中提供自动完成功能(作为属性)。c.当在“模式”中输入提供这些字段作为属性后自动完成启动时,此功能有助于扩展您的代码。

为什么要使用另一个 cli 框架?

Clima 不会像下面列出的那样尝试将其作为功能完整的 CLI 框架。它是一个包,可帮助样板文件为您的工作流程获得快速但可重用的工具。

获得全功能 CLI 体验的其他选项:

依赖项

  • dotenv

  • gnugpg - 这是通过。如果未安装,则该功能未使用。

  • fire -来自 google 的python-fire执行 cli 包装/分叉并包含在 repo 中 - 我想要版本 0.1.x 格式化并通过我自己的一些 hack 帮助输出

目录