Click库

发布于 2022-05-11  745 次阅读


写在前面

  写了也蛮长时间的功能性脚本了,一直在苦恼有没有一个合适的三方库能解决脚本使用帮助的问题,之前对命令行进行创建的时候用的是getopt库,相应的getopt库是没有设置帮助的写法,所以还是用最为笨拙的办法,用print()方法和函数进行输出。怎么说呢,虽然能达到预期效果但是编写过程中还是比较麻烦的,格式、维护问题等等。其他的办法也有用optparse库的同好,这是Python2中的写法,我个人是以Python3为主的,并且总体用下来也不是很好用。
  近期学习过程中看到了click库,感觉这个库就比较好,简单学习一下,以后可以省点心了。

简介

  Click库是用 Python 写的一个第三方模块,用于快速创建命令行。除此之外,Python内置了一个Argparse的标准库用于创建命令行,但使用起来有些繁琐,Click相比于Argparse,就好比requests相比于urllib

基础用法

  基础的使用方法分为两步:

  • 使用@click.command()装饰一个函数,实质成为命令行的接口,获取命令行输入内容
  • 使用@click.option()等装饰函数,为其添加命令行选项

      基础的用法结构如下:

import click

@click.command()
@click.option('-n','--name', default=default_value, help='name')
def main(param):
    pass

  一个简单的使用示例:

import click

@click.command()
@click.option('--count', default=1, help='Number of greetings.')
@click.option('--name', prompt='Your name', help='The person to greet.')
def main(count, name):
    """Simple program that greets NAME for a total of COUNT times."""
    for x in range(count):
        click.echo('Hello %s!' % name)

if __name__ == '__main__':
    main()

  上面的例子中函数main()有两个参数,countname,参数对应的值因为@click的装饰从而在命令行中获取。

  • @click.command() 使函数 hello 成为命令行接口;
  • @click.option 的第一个参数指定了命令行选项的名称,参数default意为设置默认值;

  同时从这里可以看出使用click.echo()进行输出可以获得更好的兼容性,众所周知print()函数在Python2和Python3中的语法存在差异。
  对于@click.command()可以使用context_settings参数来设置获取帮助的参数名称。

CONTEXT_SETTINGS = dict(help_option_names=['-h', '--help'])

@click.command(context_settings=CONTEXT_SETTINGS)
def main():
    pass

一些用法

click.option

  option最基本的用法就是通过指定命令行选项的名称,从命令行读取参数值,再将其传递给函数。除了设置命令行选项的名称,option可以通过参数default进行默认值的设置,通过参数help设置命令行参数使用说明等。

  • default: 设置命令行参数的默认值
  • help: 参数使用说明
  • type: 参数类型,可以是string,int,float等
  • prompt: 当在命令行中没有输入相应的参数时,会根据prompt提示用户输入
  • nargs: 指定命令行参数接收的值的个数
  • metavar:如何在帮助页面表示值
  • required:要求命令行参数为必填项

参数type

  使用type来指定参数类型


@click.command()
@click.option('--rate', type=float, help='rate')   # 指定rate是float类型
def show(rate):
    click.echo('rate: %s' % rate)

if __name__ == '__main__':
    show()

  使用type指定参数可选值

import click

@click.command()
@click.option('--gender', type=click.Choice(['man', 'woman']))    # 限定值
def choose(gender):
    click.echo('gender: %s' % gender)

if __name__ == '__main__':
    choose()

# 执行情况:
# $ python click_choice.py --gender boy
# Usage: click_choice.py [OPTIONS]
#  
# Error: Invalid value for "--gender": invalid choice: boy. (choose from man, woman)
#  
# $ python click_choice.py --gender man
# gender: man

多值参数

  如果一个参数需要接收多个值。option支持设置固定长度的参数值,通过nargs指定

import click

@click.command()
@click.option('--center', nargs=2, type=float, help='center of the circle')
@click.option('--radius', type=float, help='radius of the circle')
def circle(center, radius):
    click.echo('center: %s, radius: %s' % (center, radius))

if __name__ == '__main__':
    circle()

  option指定了两个参数:centerradius,其中,center表示二维平面上一个圆的圆心坐标,接收两个值,以元组的形式将值传递给函数,而radius表示圆的半径。

关于输入密码

  在输入密码的时候,有时希望能隐藏显示。option提供了两个参数来设置密码的输入:hide_inputconfirmation_promt,其中,hide_input用于隐藏输入,confirmation_promt用于重复输入。

import click

@click.command()
@click.option('--password', prompt=True, hide_input=True, confirmation_prompt=True)
def input_password(password):
    click.echo('password: %s' % password)

if __name__ == '__main__':
    input_password()

  对于例子中的写法有些复杂,click库提供了一种更为简单的写法,使用@click.password_option()以达到目的

import click

@click.command()
@click.password_option()
def input_password(password):
    click.echo('password: %s' % password)

if __name__ == '__main__':
    input_password()

参数prompt

  参数prompt其实来自于click.prompt()函数,在需要用户输入时,就可以使用prompt()函数。而在上述的例子中,在命令行中没有输入设置了prompt的参数时,会根据prompt提示用户输入。prompt=True表示接受用户输入。默认情况下,它接受任何Unicode字符串,同时也可以要求任何其他类型。

value = click.prompt('Please enter a valid integer', type=int)

  如果提供默认值,则会自动确定类型。

value = click.prompt('Please enter a number', default=42.0)

  在此基础上延伸出来另外一个专职负责输入"确认"的函数click.confirm(),询问用户是否想要继续某一个动作,默认情况下,它以布尔值的形式返回提示的结果。

if click.confirm('Do you want to continue?'):
    click.echo('Well done!')

  如果程序没有返回True,也可以自动中止程序的执行

click.confirm('Do you want to continue?', abort=True)

改变命令行程序的执行

  有些参数会改变命令行程序的执行,比如在终端输入python是进入Python控制台,而输入python --version是打印Python版本。Click 提供eager标识对参数名进行标识,如果输入该参数,则会拦截既定的命令行执行流程,跳转去执行一个回调函数。

import click

def print_version(ctx, param, value):
    if not value or ctx.resilient_parsing:
        return
    click.echo('Version 1.0')
    ctx.exit()

@click.command()
@click.option('--version', is_flag=True, callback=print_version,
              expose_value=False, is_eager=True)
@click.option('--name', default='Ethan', help='name')
def hello(name):
    click.echo('Hello %s!' % name)

if __name__ == '__main__':
    hello()

# 执行情况
# $ python click_eager.py
# Hello Ethan!
#  
# $ python click_eager.py --version                   # 拦截既定的命令行执行流程
# Version 1.0
#  
# $ python click_eager.py --name Michael
# Hello Michael!
#  
# $ python click_eager.py --version --name Ethan      # 忽略 name 选项
# Version 1.0
  • is_eager=True:表明该命令行选项优先级高于其他选项;
  • expose_value=False:表示如果没有输入该命令行选项,会执行既定的命令行流程;
  • callback:指定了输入该命令行选项时,要跳转执行的函数;
  • is_flag=True:表明该参数的值只有True和False。

click.argument

  @click.argument可以用来添加固定参数。它的使用和option类似,但支持的功能比option少。

import click

@click.command()
@click.argument('coordinates')
def show(coordinates):
    click.echo('coordinates: %s' % coordinates)

if __name__ == '__main__':
    show()

# 运行情况
# $ python click_argument.py                     # 错误,缺少参数 coordinates
# Usage: click_argument.py [OPTIONS] COORDINATES
#  
# Error: Missing argument "coordinates".
#  
# $ python click_argument.py --help              # argument 指定的参数在 help 中没有显示
# Usage: click_argument.py [OPTIONS] COORDINATES
#  
# Options:
#   --help  Show this message and exit.
#  
# $ python click_argument.py --coordinates 10    # 错误用法,这是 option 参数的用法
# Error: no such option: --coordinates
#  
# $ python click_argument.py 10                  # 正确,直接输入值即可
# coordinates: 10

  多个argument:

import click

@click.command()
@click.argument('x')
@click.argument('y')
@click.argument('z')
def show(x, y, z):
    click.echo('x: %s, y: %s, z:%s' % (x, y, z))

if __name__ == '__main__':
    show()

# 运行情况
# $ python click_argument.py 10 20 30
# x: 10, y: 20, z:30
#  
# $ python click_argument.py 10
# Usage: click_argument.py [OPTIONS] X Y Z
#  
# Error: Missing argument "y".
#  
# $ python click_argument.py 10 20
# Usage: click_argument.py [OPTIONS] X Y Z
#  
# Error: Missing argument "z".
#  
# $ python click_argument.py 10 20 30 40
# Usage: click_argument.py [OPTIONS] X Y Z
#  
# Error: Got unexpected extra argument (40)

  不定参数:

import click

@click.command()
@click.argument('src', nargs=-1)
@click.argument('dst', nargs=1)
def move(src, dst):
    click.echo('move %s to %s' % (src, dst))

if __name__ == '__main__':
    move()

# 运行情况
# $ python click_argument.py file1 trash    # src=('file1',)  dst='trash'
# move (u'file1',) to trash
# $ python click_argument.py file1 file2 file3 trash   # src=('file1', 'file2', 'file3')  dst='trash'
# move (u'file1', u'file2', u'file3') to trash

  其中,nargs=-1表明参数src接收不定量的参数值,参数值会以tuple的形式传入函数。如果nargs大于等于1,表示接收nargs个参数值,

彩色输出

  如果需要使用彩色输出则需要colorama库,同时输出函数要是用click.secho()而并非click.echo()

import click

@click.command()
@click.option('--name', help='The person to greet.')
def hello(name):
    click.secho('Hello %s!' % name, fg='red', underline=True)
    click.secho('Hello %s!' % name, fg='yellow', bg='black')

if __name__ == '__main__':
    hello()
  • fg 表示前景颜色(即字体颜色),可选值有:BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE 等;
  • bg 表示背景颜色,可选值有:BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE 等;
  • underline 表示下划线,可选的样式还有:dim=True,bold=True 等;