原文链接:Python Click 学习笔记

Click 是 Flask 的团队 pallets 开发的优秀开源项目,它为命令行工具的开发封装了大量方法,使开发者只需要专注于功能实现。恰好我最近在开发的一个小工具需要在命令行环境下操作,就写个学习笔记。

国际惯例,先来一段 “Hello World” 程序(假定已经安装了 Click 包)。

  1. # hello.py
  2. import click
  3. @click.command()
  4. @click.option('--count', default=1, help='Number of greetings.')
  5. @click.option('--name', prompt='Your name',
  6. help='The person to greet.')
  7. def hello(count, name):
  8. """Simple program that greets NAME for a total of COUNT times."""
  9. for x in range(count):
  10. click.echo('Hello %s!' % name)
  11. if __name__ == '__main__':
  12. hello()

执行 python hello.py --count=3,不难猜到控制台的输出结果。除此之外,Click 还悄悄地做了其他的工作,比如帮助选项:

  1. $ python hello.py --help
  2. Usage: hello.py [OPTIONS]
  3. Simple program that greets NAME for a total of COUNT times.
  4. Options:
  5. --count INTEGER Number of greetings.
  6. --name TEXT The person to greet.
  7. --help Show this message and exit.

函数秒变 CLI

从上面的 “Hello World” 演示中可以看出,Click 是通过装饰器来把一个函数方法装饰成命令行接口的,这个装饰器方法就是 @click.command()

  1. import click
  2. @click.command()
  3. def hello():
  4. click.echo('Hello World!')

@click.command() 装饰器把 hello() 方法变成了 Command 对象,当它被调用时,就会执行该实例内的行为。而 --help 参数就是 Command 对象内置的参数。

不同的 Command 实例可以关联到 group 中。group 下绑定的命令就成为了它的子命令,参考下面的代码:

  1. @click.group()
  2. def cli():
  3. pass
  4. @click.command()
  5. def initdb():
  6. click.echo('Initialized the database')
  7. @click.command()
  8. def dropdb():
  9. click.echo('Dropped the database')
  10. cli.add_command(initdb)
  11. cli.add_command(dropdb)

@click.group 装饰器把方法装饰为可以拥有多个子命令的 Group 对象。由 Group.add_command() 方法把 Command 对象关联到 Group 对象。
也可以直接用 @Group.command 装饰方法,会自动把方法关联到该 Group 对象下。

  1. @click.group()
  2. def cli():
  3. pass
  4. @cli.command()
  5. def initdb():
  6. click.echo('Initialized the database')
  7. @cli.command()
  8. def dropdb():
  9. click.echo('Dropped the database')

命令行的参数是不可或缺的,Click 支持对 command 方法添加自定义的参数,由 option() 和 argument() 装饰器实现。

  1. @click.command()
  2. @click.option('--count', default=1, help='number of greetings')
  3. @click.argument('name')
  4. def hello(count, name):
  5. for x in range(count):
  6. click.echo('Hello %s!' % name)

打包跨平台可执行程序

通过 Click 编写了简单的命令行方法后,还需要把 .py 文件转换成可以在控制台里运行的命令行程序。最简单的办法就是在文件末尾加上如下代码:

  1. if __name__ == '__main__':
  2. command()

Click 支持使用 setuptools 来更好的实现命令行程序打包,把源码文件打包成系统中的可执行程序,并且不限平台。一般我们会在源码根目录下创建 setup.py 脚本,先看一段简单的打包代码:

  1. from setuptools import setup
  2. setup(
  3. name='hello',
  4. version='0.1',
  5. py_modules=['hello'],
  6. install_requires=[
  7. 'Click',
  8. ],
  9. entry_points='''
  10. [console_scripts]
  11. hello=hello:cli
  12. ''',
  13. )

留意 entry_points 字段,在 console_scripts 下,每一行都是一个控制台脚本,等号左边的的是脚本的名称,右边的是 Click 命令的导入路径。

详解命令行参数

上面提到了自定义命令行参数的两个装饰器:@click.option() 和 @click.argument(),两者有些许区别,使用场景也有所不同。

总体而言,argument() 装饰器比 option() 功能简单些,后者支持下面的特性:

  • 自动提示缺失的输入;
  • option 参数可以从环境变量中获取,argument 参数则不行;
  • option 参数在 help 输出中有完整的文档,argument 则没有;

而 argument 参数可以接受可变个数的参数值,而 option 参数只能接收固定个数的参数值(默认是 1 个)。

Click 可以设置不同的参数类型,简单类型如 click.STRINGclick.INTclick.FLOATclick.BOOL

命令行的参数名由 “-short_name” 和 “–long_name” 声明,如果参数名既没有以 “-“ 开头,也没有以 “–” 开头,那么这边变量名会成为被装饰方法的内部变量,而非方法参数。

Option 参数

option 最基础的用法就是简单值变量,option 接收一个变量值,下面是一段示例代码:

  1. @click.command()
  2. @click.option('--n', default=1)
  3. def dots(n):
  4. click.echo('.' * n)

如果在命令行后面跟随参数 --n=2 就会输出两个点,如果传参数,默认输出一个点。上面的代码中,参数类型没有显示给出,但解释器会认为是 INT 型,因为默认值 1 是 int 值。
有些时候需要传入多个值,可以理解为一个 list,option 只支持固定长度的参数值,即设置后必须传入,个数由 nargs 确定。

  1. @click.command()
  2. @click.option('--pos', nargs=2, type=float)
  3. def findme(pos):
  4. click.echo('%s / %s' % pos)

findme --pos 2.0 3.0 输出结果就是 2.0 / 3.0

既然可以传入 list,那么 tuple 呢?Click 也是支持的:

  1. @click.command()
  2. @click.option('--item', type=(unicode, int))
  3. def putitem(item):
  4. click.echo('name=%s id=%d' % item)

这样就传入了一个 tuple 变量,putitem --item peter 1338 得到的输出就是 name=peter id=1338
上面没有设置 nargs,因为 nargs 会自动取 tuple 的长度值。因此上面的代码实际上等同于:

  1. @click.command()
  2. @click.option('--item', nargs=2, type=click.Tuple([unicode, int]))
  3. def putitem(item):
  4. click.echo('name=%s id=%d' % item)

option 还支持同一个参数多次使用,类似 git commit -m aa -m bb 中 -m 参数就传入了 2 次。option 通过 multiple 标识位来支持这一特性:

  1. @click.command()
  2. @click.option('--message', '-m', multiple=True)
  3. def commit(message):
  4. click.echo('\n'.join(message))

有时候,命令行参数是固定的几个值,这时就可以用到 Click.choice 类型来限定传参的潜在值:

  1. # choice
  2. @click.command()
  3. @click.option('--hash-type', type=click.Choice(['md5', 'sha1']))
  4. def digest(hash_type):
  5. click.echo(hash_type)

当上面的命令行程序参数 --hash-type 不是 md5 或 sha1,就会输出错误提示,并且在 --help 提示中也会对 choice 选项有显示。

如果希望命令行程序能在我们错误输入或漏掉输入的情况下,友好的提示用户,就需要用到 Click 的 prompt 功能,看代码:

  1. # prompt
  2. @click.command()
  3. @click.option('--name', prompt=True)
  4. def hello(name):
  5. click.echo('Hello %s!' % name)

如果在执行 hello 时没有提供 –name 参数,控制台会提示用户输入该参数。也可以自定义控制台的提示输出,把 prompt 改为自定义内容即可。

对于类似账户密码等参数的输入,就要进行隐藏显示。option 的 hide_input 和 confirmation_promt 标识就是用来控制密码参数的输入:

  1. # password
  2. @click.command()
  3. @click.option('--password', prompt=True, hide_input=True,
  4. confirmation_prompt=True)
  5. def encrypt(password):
  6. click.echo('Encrypting password to %s' % password.encode('rot13'))

Click 把上面的操作进一步封装成装饰器 click.password_option(),因此上面的代码也可以简化成:

  1. # password
  2. @click.command()
  3. @click.password_option()
  4. def encrypt(password):
  5. click.echo('Encrypting password to %s' % password.encode('rot13'))

有的参数会改变命令行程序的执行,比如 node 是进入 Node 控制台,而 node --verion 是输出 node 的版本号。Click 提供 eager 标识对参数名进行标记,拦截既定的命令行执行流程,而是调用一个回调方法,执行后直接退出。下面模拟 click.version_option() 的功能,实现 --version 参数名输出版本号:

  1. # eager
  2. def print_version(ctx, param, value):
  3. if not value or ctx.resilient_parsing:
  4. return
  5. click.echo('Version 1.0')
  6. ctx.exit()
  7. @click.command()
  8. @click.option('--version', is_flag=True, callback=print_version,
  9. expose_value=False, is_eager=True)
  10. def hello():
  11. click.echo('Hello World!')

对于类似删除数据库表这样的危险操作,Click 支持弹出确认提示,--yes 标识位置为 True 时会让用户再次确认:

  1. # yes parameters
  2. def abort_if_false(ctx, param, value):
  3. if not value:
  4. ctx.abort()
  5. @click.command()
  6. @click.option('--yes', is_flag=True, callback=abort_if_false,
  7. expose_value=False,
  8. prompt='Are you sure you want to drop the db?')
  9. def dropdb():
  10. click.echo('Dropped all tables!')

测试运行下:

  1. $ dropdb
  2. Are you sure you want to drop the db? [y/N]: n
  3. Aborted!
  4. $ dropdb --yes
  5. Dropped all tables!

同样的,Click 对次进行了封装,click.confirmation_option() 装饰器实现了上述功能:

  1. @click.command()
  2. @click.confirmation_option(prompt='Are you sure you want to drop the db?')
  3. def dropdb():
  4. click.echo('Dropped all tables!')

前面只讲了默认的参数前缀 -- 和 -,Click 允许开发者自定义参数前缀(虽然严重不推荐)。

  1. # other prefix
  2. @click.command()
  3. @click.option('+w/-w')
  4. def chmod(w):
  5. click.echo('writable=%s' % w)
  6. if __name__ == '__main__':
  7. chmod()

如果想要用 / 作为前缀,而且要像上面一样采用布尔标识,会产生冲突,因为布尔标识也是用 /,这种情况下可以用 ; 代替布尔标识的 /

  1. @click.command()
  2. @click.option('/debug;/no-debug')
  3. def log(debug):
  4. click.echo('debug=%s' % debug)
  5. if __name__ == '__main__':
  6. log()

既然支持 Choice,不难联想到 Range,先看代码:

  1. # range
  2. @click.command()
  3. @click.option('--count', type=click.IntRange(0, 20, clamp=True))
  4. @click.option('--digit', type=click.IntRange(0, 10))
  5. def repeat(count, digit):
  6. click.echo(str(digit) * count)
  7. if __name__ == '__main__':
  8. repeat()

Argument 参数

Argument 的作用类似 Option,但没有 Option 那么全面的功能。

和 Option 一样,Argument 最基础的应用就是传递一个简单变量值:

  1. @click.command()
  2. @click.argument('filename')
  3. def touch(filename):
  4. click.echo(filename)

命令行后跟的参数值被赋值给参数名 filename

另一个用的比较广泛的是可变参数,也是由 nargs 来确定参数个数,变量值会以 tuple 的形式传入函数:

  1. @click.command()
  2. @click.argument('src', nargs=-1)
  3. @click.argument('dst', nargs=1)
  4. def copy(src, dst):
  5. for fn in src:
  6. click.echo('move %s to folder %s' % (fn, dst))

运行程序:

  1. $ copy foo.txt bar.txt my_folder
  2. move foo.txt to folder my_folder
  3. move bar.txt to folder my_folder

Click 支持通过文件名参数对文件进行操作,click.File() 装饰器就是处理这种操作的,尤其是在类 Unix 系统下,它支持以 - 符号作为标准输入/输出。

  1. # File
  2. @click.command()
  3. @click.argument('input', type=click.File('rb'))
  4. @click.argument('output', type=click.File('wb'))
  5. def inout(input, output):
  6. while True:
  7. chunk = input.read(1024)
  8. if not chunk:
  9. break
  10. output.write(chunk)

运行程序,先将文本写进文件,再读取

如果参数值只是想做为文件名而已呢,很简单,将 type 指定为 click.Path()

  1. @click.command()
  2. @click.argument('f', type=click.Path(exists=True))
  3. def touch(f):
  4. click.echo(click.format_filename(f))

命令组

  1. $ touch hello.txt
  2. hello.txt
  3.  
  4. $ touch missing.txt
  5. Usage: touch [OPTIONS] F
  6.  
  7. Error: Invalid value for "f": Path "missing.txt" does not exist.

仿 Hexo 示例程序

  1. # hexo.py

Python Click 学习笔记(转)的更多相关文章

  1. 【原】Learning Spark (Python版) 学习笔记(三)----工作原理、调优与Spark SQL

    周末的任务是更新Learning Spark系列第三篇,以为自己写不完了,但为了改正拖延症,还是得完成给自己定的任务啊 = =.这三章主要讲Spark的运行过程(本地+集群),性能调优以及Spark ...

  2. 0003.5-20180422-自动化第四章-python基础学习笔记--脚本

    0003.5-20180422-自动化第四章-python基础学习笔记--脚本 1-shopping """ v = [ {"name": " ...

  3. Python Flask学习笔记之模板

    Python Flask学习笔记之模板 Jinja2模板引擎 默认情况下,Flask在程序文件夹中的templates子文件夹中寻找模板.Flask提供的render_template函数把Jinja ...

  4. Python Flask学习笔记之Hello World

    Python Flask学习笔记之Hello World 安装virtualenv,配置Flask开发环境 virtualenv 虚拟环境是Python解释器的一个私有副本,在这个环境中可以安装私有包 ...

  5. 获取字段唯一值工具- -ArcPy和Python案例学习笔记

    获取字段唯一值工具- -ArcPy和Python案例学习笔记   目的:获取某一字段的唯一值,可以作为工具使用,也可以作为函数调用 联系方式:谢老师,135-4855-4328,xiexiaokui# ...

  6. Python高级学习笔记

    Python高级学习笔记,此笔记中包含Linux操作系统.Html+CSS+JS.网络协议等. 所有思维导图为本人亲手所画,请勿用于商用. 大哥们,求点赞哦. 第一天笔记:链接 第二天笔记:链接 第三 ...

  7. Python入门学习笔记4:他人的博客及他人的学习思路

    看其他人的学习笔记,可以保证自己不走弯路.并且一举两得,即学知识又学方法! 廖雪峰:https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958 ...

  8. Python 基础学习笔记(超详细版)

    1.变量 python中变量很简单,不需要指定数据类型,直接使用等号定义就好.python变量里面存的是内存地址,也就是这个值存在内存里面的哪个地方,如果再把这个变量赋值给另一个变量,新的变量通过之前 ...

  9. Python人工智能学习笔记

    Python教程 Python 教程 Python 简介 Python 环境搭建 Python 中文编码 Python 基础语法 Python 变量类型 Python 运算符 Python 条件语句 ...

随机推荐

  1. 抓包工具 Charles 使用心得

    前言 虽然实习工作还没有着落,但学习还是要继续的嘛,今天就来学习使用下 Mac 下截取网络封包的工具:Charles. 我想,如果你是个善于利用搜索引擎的人,那么在 Google 中输入「Charle ...

  2. MySQL数据库操作常用命令

    MySQL数据库操作常用命令DOS连接数据库1.安装MySQL配置好环境2.运行cmd命令net start mysql3.找到mysql文件根目录输入命令mysql -h localhost -u ...

  3. Hibernate composite key

    有两种方法来map composite key. 第一种用@IdClass第二种用@Embeddable,参考链接: http://stackoverflow.com/questions/358503 ...

  4. javaWeb学习总结(2)- http协议

    一.http简介 1.基本介绍: (1)客户端连上web服务器后,若想获得web服务器中的某个web资源,需遵守一定的通讯格式,HTTP协议用于定义客户端与web服务器通迅的格式. (2)WEB浏览器 ...

  5. struts2.1.6教程十、类型转换

    建立struts2conversion项目,并搭建好struts2的基本开发环境 1.基于Action的直接属性转换 建立t.jsp页面,内容如下: <s:form action="p ...

  6. 用sftp上传文件至linux服务器

    1.项目环境 框架:springmvc    项目管理工具:maven 2.必须使用的jar com.jcraft jsch 0.1.27 test 3.新建一个FileUpDown工具类,在类中添加 ...

  7. TCP/UDP客户端

    Python 网络编程----模块socekt 在渗透测试的过程中,经常会遇到需要创建一个TCP客户端来连接服务器.发送垃圾数据.进行模糊测试活进行其他任务的情况. 简单的TCP客户端代码: #!/u ...

  8. PHP中的函数声明与使用

                                               函数 1.  函数名是标识符之一,只能有字母数字下划线,开头不能是数字:      函数名的命名,必须符合&quo ...

  9. js中 substr(), substring(), slice()的区别

    一.作用 三者都是基于原字符串创建新字符串的方法. 接收一到两个参数,第一个参数截取字符串的开始位置(字符下标,从0开始),第二个参数因方法不同而不同,后面不同点会说到. 另外,三个方法都不会修改原字 ...

  10. Spring学习笔记——01 控制反转

    想一下之前学的Java,如果某个类需要引用某个对象,则需要手动new一个出来.这样带来的一个问题就是,若被引用的类发生改动或被删除,则引用它的所有类都会报错.因为两个类耦合在一起了.解决的办法就是不由 ...