四招美化click命令行应用

·

0 min read

python在实现命令行工具的方面有很多有优势的库,其中Click是其中的佼佼者。

今天不是Click的教学,参考官方文档就可以很快写出一个不错的命令行应用。今天我们来做四件事提高我们命令行应用的B格:

  • 长时间任务的等待效果
  • 命令行帮助的文档的美化
  • 错误命令猜测提示
  • 日志级别的控制

快速实现一个等待效果

在很多命令行工具的交互过程中存在着长时间等待一些任务,如果没有一些友好的提示,经常会被使用者认为 卡死或者死循环等情况。而进度条是一个非常不错的解决方案,progressbarprogressive都是不错的选择, click也有自己的click.progressbar。但是遇到总体进度预先不知道的任务,例如:等待某个任务完成、等待数据加载等这些不知道什么时候才能完成的任务的时候进度条就不那么好用了。这个时候最简单的一个表示方式就是循环动画播放,比如iOS中的“转菊花”。

你可以自己写一个异步的动画播放效果,这里我就不写了。我今天要用非常简单的方法来实现这一效果,我们用 到的是blindspin,一个click-spinner的fork,没什么大变化主要是动画效果不一样。

安装

pip install blindspin
import time

import click
import blindspin


def a_long_time_task():
    """模拟一个需要等待的任务"""
    time.sleep(5)


@click.command()
def cli():
    # 将长时间的任务放在这个with语句中就可以了
    # with中执行的任务全部结束,动画效果就会结束
    with blindspin.spinner():
        a_long_time_task()


if __name__ == "__main__":
    cli()

效果:

asciicast

美化帮助文档

click能够非常方便的生成帮助文档,通过命令行参数--help就能查看到。默认的帮助文档是一色的,现在我们来给它上色吧。

安装

pip install click-help-colors

这里我就用官网的例子,稍稍改一点点,自己体会一下就会用了。

import click
from click_help_colors import HelpColorsGroup, HelpColorsCommand


@click.group(
    cls=HelpColorsGroup,
    help_headers_color='yellow',
    help_options_color='magenta')
def cli():
    """主命令"""
    pass


@cli.command()
@click.option('--count', default=1, help='Some number.')
def command1(count):
    click.echo('command 1')


@cli.command(cls=HelpColorsCommand, help_options_color='blue')
@click.option('--name', help='Some string.')
def command2(name):
    click.echo('command 2')

if __name__ == "__main__":
    cli()

效果

python colour_help.py --help

colour_help_1.png

python colour_help.py command1 --help

colour_help_2.png

python colour_help.py command2 --help

colour_help_3.png

猜测用户希望的命令

当我们子命令比较多的时候,用户可能会记错、拼错或者打错。能提示用户正确的命令那是多么cool的一件事, 然而我们经常用的git就有这样的功能。

dym.png 这里我输错push命令,git提示我们正确的写法。这个功能已经有人帮我们实现了,就是click-didyoumean

安装

pip install click-didyoumean

用法

  1. 为你的命令组指定DYMGroup

     import click
     from click_didyoumean import DYMGroup
    
     @click.group(cls=DYMGroup)
     def cli():
         pass
    
     @cli.command()
     def foo():
         pass
    
     @cli.command()
     def bar():
         pass
    
     @cli.command()
     def barrr():
         pass
    
     if __name__ == "__main__":
         cli()
    
  2. 将需要的子命令放入DYMCommandCollection

     import click
     from click_didyoumean import DYMCommandCollection
    
     @click.group()
     def cli1():
         pass
    
     @cli1.command()
     def foo():
         pass
    
     @cli1.command()
     def bar():
         pass
    
     @click.group()
     def cli2():
         pass
    
     @cli2.command()
     def barrr():
         pass
    
     cli = DYMCommandCollection(sources=[cli1, cli2])
    
     if __name__ == "__main__":
         cli()
    

DYMGroupDYMCommandCollection类有两个配置项,

参数类型默认值描述
max_suggestionsint3最大建议条目数
cutofffloat0.5小于这个可能性分数的将会被忽略

例:

@cli.group(cls=DYMGroup, max_suggestions=2, cutoff=0.7)
def cli():
    pass

... or ...

cli = DYMCommandCollection(sources=[cli1, cli2], max_suggestions=2, cutoff=0.7)

这里有个小插曲,如果你想同时使用click-help-colorsclick-didyoumean,那么你会发现他们都需要对cls参数进行修改。怎么同时让两者都生效呢?这里可以使用python的多重继承功能。

class CLI(DYMGroup, click_help_colors.HelpColorsGroup):
    pass

@click.group(
    cls=CLI, help_headers_color='yellow', help_options_color='blue')
def cli():
    pass

这样我们就能使得我们的命令行应用同时具备着两种功能了👍。

用户指定日志等级

我们经常有这样的情况,希望日志等级可以通过参数的传入来控制。比如,默认情况只输出一下必要信息。加入参数-v可以显示更多详细信息。

通常情况我们可以这样实现:

import logging
logger = logging.getLogger(__name__)

@click.command()
@click.option('--verbose', '-v', default=False, is_flag=True)
def cli(verbose):
    if verbose:
        logger.setLevel(logging.ERROR)
    else:
        logger.setLevel(logging.INFO)

    logger.info("普通输出信息")
    logger.error("详细信息")

然而click-log已经为我们实现了这一功能,使用python最大的好处就是DRY(Don't repeat yourself)

安装

pip install click-log

这里我们仅仅演示基本的做法,更多自定义的内容请参考官方文档

import logging

import click_log

logger = logging.getLogger(__name__)
click_log.basic_config(logger)

@click.command()
@click_log.simple_verbosity_option(logger)
def cli():
    logger.info("Dividing by zero.")

    try:
        1 / 0
    except:
        logger.error("Failed to divide by zero.")

以上代码,中click_log.simple_verbosity_option装饰器为命令增加了一个--verbosity可选参数, 它接受DEBUG,INFO,WARNING,ERROR或者CRITICAL这几个值来控制日志级别。

好了以上就是我们用来美化click命令行应用的四招。