Python3学习(五十五):Click模块的使用
Click 是 Flask 的团队 pallets 开发的优秀开源项目,它为命令行工具的开发封装了大量方法,使开发者只需要专注于功能实现。恰好我最近在开发的一个小工具需要在命令行环境下操作,就写个学习笔记。
国际惯例,先来一段 “Hello World” 程序(假定已经安装了 Click 包)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
# hello.py 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 hello(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__':hello() |
执行 python hello.py --count=3
,不难猜到控制台的输出结果。除此之外,Click 还悄悄地做了其他的工作,比如帮助选项:
1 2 3 4 5 6 7 8 9 |
$ python hello.py --help Usage: hello.py [OPTIONS]Simple program that greets NAME for a total of COUNT times.Options:--count INTEGER Number of greetings.--name TEXT The person to greet.--help Show this message and exit. |
函数秒变 CLI
从上面的 “Hello World” 演示中可以看出,Click 是通过装饰器来把一个函数方法装饰成命令行接口的,这个装饰器方法就是 `@click.command()`。
1 2 3 4 5 |
import click@click.command() def hello():click.echo('Hello World!') |
`@click.command()装饰器把
hello()方法变成了
Command对象,当它被调用时,就会执行该实例内的行为。而
–help参数就是
Command` 对象内置的参数。
不同的 Command
实例可以关联到 group
中。group
下绑定的命令就成为了它的子命令,参考下面的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
@click.group() def cli():pass@click.command() def initdb():click.echo('Initialized the database')@click.command() def dropdb():click.echo('Dropped the database')cli.add_command(initdb) cli.add_command(dropdb) |
`@click.group装饰器把方法装饰为可以拥有多个子命令的
Group对象。由
Group.add_command()方法把
Command对象关联到
Group对象。 也可以直接用
@Group.command装饰方法,会自动把方法关联到该
Group` 对象下。
1 2 3 4 5 6 7 8 9 10 11 |
@click.group() def cli():pass@cli.command() def initdb():click.echo('Initialized the database')@cli.command() def dropdb():click.echo('Dropped the database') |
命令行的参数是不可或缺的,Click 支持对 command
方法添加自定义的参数,由 option()
和 argument()
装饰器实现。
1 2 3 4 5 6 |
@click.command() @click.option('--count', default=1, help='number of greetings') @click.argument('name') def hello(count, name):for x in range(count):click.echo('Hello %s!' % name) |
打包跨平台可执行程序
通过 Click 编写了简单的命令行方法后,还需要把 .py
文件转换成可以在控制台里运行的命令行程序。最简单的办法就是在文件末尾加上如下代码:
1 2 |
if __name__ == '__main__':command() |
Click 支持使用 setuptools
来更好的实现命令行程序打包,把源码文件打包成系统中的可执行程序,并且不限平台。一般我们会在源码根目录下创建 setup.py
脚本,先看一段简单的打包代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
from setuptools import setupsetup(name='hello',version='0.1',py_modules=['hello'],install_requires=['Click',],entry_points='''[console_scripts]hello=hello:cli''', ) |
留意 entry_points
字段,在 console_scripts
下,每一行都是一个控制台脚本,等号左边的的是脚本的名称,右边的是 Click 命令的导入路径。
详解命令行参数
上面提到了自定义命令行参数的两个装饰器:`@click.option()和
@click.argument()`,两者有些许区别,使用场景也有所不同。
总体而言,argument()
装饰器比 option()
功能简单些,后者支持下面的特性:
- 自动提示缺失的输入;
- option 参数可以从环境变量中获取,argument 参数则不行;
- option 参数在 help 输出中有完整的文档,argument 则没有;
而 argument 参数可以接受可变个数的参数值,而 option 参数只能接收固定个数的参数值(默认是 1 个)。
Click 可以设置不同的参数类型,简单类型如 click.STRING
,click.INT
,click.FLOAT
,click.BOOL
。
命令行的参数名由 “-short_name” 和 “–long_name” 声明,如果参数名既没有以 “-“ 开头,也没有以 “–” 开头,那么这边变量名会成为被装饰方法的内部变量,而非方法参数。
Option 参数
option 最基础的用法就是简单值变量,option 接收一个变量值,下面是一段示例代码:
1 2 3 4 |
@click.command() @click.option('--n', default=1) def dots(n):click.echo('.' * n) |
如果在命令行后面跟随参数 --n=2
就会输出两个点,如果传参数,默认输出一个点。上面的代码中,参数类型没有显示给出,但解释器会认为是 INT
型,因为默认值 1 是 int 值。
有些时候需要传入多个值,可以理解为一个 list,option 只支持固定长度的参数值,即设置后必须传入,个数由 nargs
确定。
1 2 3 4 |
@click.command() @click.option('--pos', nargs=2, type=float) def findme(pos):click.echo('%s / %s' % pos) |
findme --pos 2.0 3.0
输出结果就是 2.0 / 3.0
既然可以传入 list,那么 tuple 呢?Click 也是支持的:
1 2 3 4 |
@click.command() @click.option('--item', type=(unicode, int)) def putitem(item):click.echo('name=%s id=%d' % item) |
这样就传入了一个 tuple 变量,putitem --item peter 1338
得到的输出就是 name=peter id=1338
上面没有设置 nargs,因为 nargs 会自动取 tuple 的长度值。因此上面的代码实际上等同于:
1 2 3 4 |
@click.command() @click.option('--item', nargs=2, type=click.Tuple([unicode, int])) def putitem(item):click.echo('name=%s id=%d' % item) |
option 还支持同一个参数多次使用,类似 git commit -m aa -m bb
中 -m
参数就传入了 2 次。option 通过 multiple
标识位来支持这一特性:
1 2 3 4 |
@click.command() @click.option('--message', '-m', multiple=True) def commit(message):click.echo('\n'.join(message)) |
有时候,命令行参数是固定的几个值,这时就可以用到 Click.choice
类型来限定传参的潜在值:
1 2 3 4 5 |
# choice @click.command() @click.option('--hash-type', type=click.Choice(['md5', 'sha1'])) def digest(hash_type):click.echo(hash_type) |
当上面的命令行程序参数 --hash-type
不是 md5 或 sha1,就会输出错误提示,并且在 --help
提示中也会对 choice 选项有显示。
如果希望命令行程序能在我们错误输入或漏掉输入的情况下,友好的提示用户,就需要用到 Click 的 prompt 功能,看代码:
1 2 3 4 5 |
# prompt @click.command() @click.option('--name', prompt=True) def hello(name):click.echo('Hello %s!' % name) |
如果在执行 hello 时没有提供 –name 参数,控制台会提示用户输入该参数。也可以自定义控制台的提示输出,把 prompt
改为自定义内容即可。
对于类似账户密码等参数的输入,就要进行隐藏显示。option
的 hide_input
和 confirmation_promt
标识就是用来控制密码参数的输入:
1 2 3 4 5 6 |
# password @click.command() @click.option('--password', prompt=True, hide_input=True,confirmation_prompt=True) def encrypt(password):click.echo('Encrypting password to %s' % password.encode('rot13')) |
Click 把上面的操作进一步封装成装饰器 click.password_option()
,因此上面的代码也可以简化成:
1 2 3 4 5 |
# password @click.command() @click.password_option() def encrypt(password):click.echo('Encrypting password to %s' % password.encode('rot13')) |
有的参数会改变命令行程序的执行,比如 node
是进入 Node 控制台,而 node --verion
是输出 node 的版本号。Click 提供 eager 标识对参数名进行标记,拦截既定的命令行执行流程,而是调用一个回调方法,执行后直接退出。下面模拟 click.version_option()
的功能,实现 --version
参数名输出版本号:
1 2 3 4 5 6 7 8 9 10 11 12 |
# eager def print_version(ctx, param, value):if not value or ctx.resilient_parsing:returnclick.echo('Version 1.0')ctx.exit()@click.command() @click.option('--version', is_flag=True, callback=print_version,expose_value=False, is_eager=True) def hello():click.echo('Hello World!') |
对于类似删除数据库表这样的危险操作,Click 支持弹出确认提示,--yes
标识位置为 True 时会让用户再次确认:
1 2 3 4 5 6 7 8 9 10 11 |
# yes parameters def abort_if_false(ctx, param, value):if not value:ctx.abort()@click.command() @click.option('--yes', is_flag=True, callback=abort_if_false,expose_value=False,prompt='Are you sure you want to drop the db?') def dropdb():click.echo('Dropped all tables!') |
测试运行下:
1 2 3 4 5 |
$ dropdb Are you sure you want to drop the db? [y/N]: n Aborted! $ dropdb --yes Dropped all tables! |
同样的,Click 对次进行了封装,click.confirmation_option()
装饰器实现了上述功能:
1 2 3 4 |
@click.command() @click.confirmation_option(prompt='Are you sure you want to drop the db?') def dropdb():click.echo('Dropped all tables!') |
前面只讲了默认的参数前缀 --
和 -
,Click 允许开发者自定义参数前缀(虽然严重不推荐)。
1 2 3 4 5 6 7 8 |
# other prefix @click.command() @click.option('+w/-w') def chmod(w):click.echo('writable=%s' % w)if __name__ == '__main__':chmod() |
如果想要用 /
作为前缀,而且要像上面一样采用布尔标识,会产生冲突,因为布尔标识也是用 /
,这种情况下可以用 ;
代替布尔标识的 /
:
1 2 3 4 5 6 7 |
@click.command() @click.option('/debug;/no-debug') def log(debug):click.echo('debug=%s' % debug)if __name__ == '__main__':log() |
既然支持 Choice,不难联想到 Range,先看代码:
1 2 3 4 5 6 7 8 9 |
# range @click.command() @click.option('--count', type=click.IntRange(0, 20, clamp=True)) @click.option('--digit', type=click.IntRange(0, 10)) def repeat(count, digit):click.echo(str(digit) * count)if __name__ == '__main__':repeat() |
Argument 参数
Argument 的作用类似 Option,但没有 Option 那么全面的功能。
和 Option 一样,Argument 最基础的应用就是传递一个简单变量值:
1 2 3 4 |
@click.command() @click.argument('filename') def touch(filename):click.echo(filename) |
命令行后跟的参数值被赋值给参数名 filename
。
另一个用的比较广泛的是可变参数,也是由 nargs
来确定参数个数,变量值会以 tuple 的形式传入函数:
1 2 3 4 5 6 |
@click.command() @click.argument('src', nargs=-1) @click.argument('dst', nargs=1) def copy(src, dst):for fn in src:click.echo('move %s to folder %s' % (fn, dst)) |
运行程序:
1 2 3 |
$ copy foo.txt bar.txt my_folder move foo.txt to folder my_folder move bar.txt to folder my_folder |
Click 支持通过文件名参数对文件进行操作,click.File()
装饰器就是处理这种操作的,尤其是在类 Unix 系统下,它支持以 -
符号作为标准输入/输出。
1 2 3 4 5 6 7 8 9 10 |
# File @click.command() @click.argument('input', type=click.File('rb')) @click.argument('output', type=click.File('wb')) def inout(input, output):while True:chunk = input.read(1024)if not chunk:breakoutput.write(chunk) |
运行程序,先将文本写进文件,再读取
1 2 3 4 5 |
$ inout - hello.txt hello ^D $ inout hello.txt - hello |
如果参数值只是想做为文件名而已呢,很简单,将 type
指定为 click.Path()
:
1 2 3 4 |
@click.command() @click.argument('f', type=click.Path(exists=True)) def touch(f):click.echo(click.format_filename(f)) |
1 2 3 4 5 6 7 |
$ touch hello.txt hello.txt$ touch missing.txt Usage: touch [OPTIONS] FError: Invalid value for "f": Path "missing.txt" does not exist. |
Python3学习(五十五):Click模块的使用相关推荐
- python复制指定字符串_python3.4学习笔记(十五) 字符串操作(string替换、删除、截取、复制、连接、比较、查找、包含、大小写转换、分割等)...
python3.4学习笔记(十五) 字符串操作(string替换.删除.截取.复制.连接.比较.查找.包含.大小写转换.分割等) python print 不换行(在后面加上,end=''),prin ...
- python学习[第十五篇] 文件系统
python学习[第十五篇] 文件系统 对文件系统访问大多数都通过os模块实现. os 模块文件/目录访问函数 文件处理 mkfifo() 创建命名通道只用于linux remove(path)/un ...
- Polyworks脚本开发学习笔记(十五)-用Python连接Polyworks的COM组件
Polyworks脚本开发学习笔记(十五)-用Python连接Polyworks的COM组件 用Polyworks脚本开发,没有高级语言的支持,功能难免单一,一些比较复杂的交互实现不了,界面和报告也很 ...
- python数据挖掘学习】十五.Matplotlib调用imshow()函数绘制热图
python数据挖掘学习]十五.Matplotlib调用imshow()函数绘制热图 #2018-03-28 14:47:19 March Wednesday the 13 week, the 087 ...
- 花书+吴恩达深度学习(十五)序列模型之循环神经网络 RNN
目录 0. 前言 1. RNN 计算图 2. RNN 前向传播 3. RNN 反向传播 4. 导师驱动过程(teacher forcing) 5. 不同序列长度的 RNN 如果这篇文章对你有一点小小的 ...
- JavaScript学习(五十五)—组合继承
JavaScript学习(五十五)-组合继承 组合继承:就是借用构造方法继承和原型链继承的组合形式
- JavaScript学习(十五)—内部样式与外部样式的修改与设置
JavaScript学习(十五)-内部样式与外部样式的修改与设置 (一).行内样式 获取方式:元素节点.style.CSS属性名称: 注意:如果CSS属性中包含"-",那么需要采用 ...
- BT源代码学习心得(十五):客户端源代码分析(下载过程中的块选取策略)
BT源代码学习心得(十五):客户端源代码分析(下载过程中的块选取策略) 发信人: wolfenstein (NeverSayNever), 个人文集 标 题: BT源代码学习心得(十五):客户端源代 ...
- windows内核开发学习笔记十五:IRP结构
windows内核开发学习笔记十五:IRP结构 IRP(I/O Request Package)在windows内核中,有一种系统组件--IRP,即输入输出请求包.当上层应用程序需要访问底层输入输 ...
- java学习记录十五:集合二Collections、Set、Map
java学习记录十五:集合二 一.Collections工具类 一.解释 二.常用方法 1.打乱集合顺序 2.按照默认规则排序 3.按指定规则排序 4.批量添加元素 二.可变参数 一.解释 二.写法 ...
最新文章
- python txt文件读写(追加、覆盖)
- django项目转pyc_问题网址.pycdjango生成的文件,如何修复?
- React 回忆录(三)使用 React 渲染界面
- python list查找元素_使用python list 查找所有匹配元素的位置实例
- 帮助子女养成良好的读书习惯 胜过1000亿财富
- 高性能 HTML5 地铁样式的应用程序中的内容
- 绕过 WAF:绕过一些 WAF 设备的 Burp 插件
- vue使用Echart跟随窗口大小改变而重新绘制时出现读取窗口大小不及时的问题
- 7-5 sdut-验证“哥德巴赫猜想” (10 分)(优化素数判断)
- Asciidoc语法
- grafana zabbix 模板_【Grafana教程】安装Grafana并配置Zabbix数据源
- HighCharts:为plotLines基准线添加label标签不显示
- (转)遇到重复的Maven项目初始配置,就要创建自己的Maven Archetype
- python和pycharm版本要对应吗_pycharm的版本问题
- 人机工程学座椅设计_人体工程学_座椅设计说明
- KDD 2016 | node2vec:Scalable Feature Learning for Networks
- 教程:如何使用Java以编程方式打印PDF文件?
- centos7创建asm磁盘_centos7下安装oracle rac使用udev绑定磁盘方法
- Pr-快速上手-基本操作-教程
- CANoe测试:CAPL Test Module的2种创建方式