Python3 Click模块的使用方法详解
Click是Flask的团队pallets开发的优秀开源项目,它为命令行工具的开发封装了大量方法,使开发者只需要专注于功能实现。恰好我最近在开发的一个小工具需要在命令行环境下操作,就写个学习笔记。
国际惯例,先来一段“HelloWorld”程序(假定已经安装了Click包)。
#hello.py importclick @click.command() @click.option('--count',default=1,help='Numberofgreetings.') @click.option('--name',prompt='Yourname', help='Thepersontogreet.') defhello(count,name): """SimpleprogramthatgreetsNAMEforatotalofCOUNTtimes.""" forxinrange(count): click.echo('Hello%s!'%name) if__name__=='__main__': hello()
执行pythonhello.py--count=3,不难猜到控制台的输出结果。除此之外,Click还悄悄地做了其他的工作,比如帮助选项:
$pythonhello.py--help Usage:hello.py[OPTIONS] SimpleprogramthatgreetsNAMEforatotalofCOUNTtimes. Options: --countINTEGERNumberofgreetings. --nameTEXTThepersontogreet. --helpShowthismessageandexit.
函数秒变CLI
从上面的“HelloWorld”演示中可以看出,Click是通过装饰器来把一个函数方法装饰成命令行接口的,这个装饰器方法就是`@click.command()`。
importclick @click.command() defhello(): click.echo('HelloWorld!')
`@click.command()装饰器把hello()方法变成了Command对象,当它被调用时,就会执行该实例内的行为。而–help参数就是Command`对象内置的参数。
不同的Command实例可以关联到group中。group下绑定的命令就成为了它的子命令,参考下面的代码:
@click.group() defcli(): pass @click.command() definitdb(): click.echo('Initializedthedatabase') @click.command() defdropdb(): click.echo('Droppedthedatabase') cli.add_command(initdb) cli.add_command(dropdb)
`@click.group装饰器把方法装饰为可以拥有多个子命令的Group对象。由Group.add_command()方法把Command对象关联到Group对象。也可以直接用@Group.command装饰方法,会自动把方法关联到该Group`对象下。
@click.group() defcli(): pass @cli.command() definitdb(): click.echo('Initializedthedatabase') @cli.command() defdropdb(): click.echo('Droppedthedatabase')
命令行的参数是不可或缺的,Click支持对command方法添加自定义的参数,由option()和argument()装饰器实现。
@click.command() @click.option('--count',default=1,help='numberofgreetings') @click.argument('name') defhello(count,name): forxinrange(count): click.echo('Hello%s!'%name)
打包跨平台可执行程序
通过Click编写了简单的命令行方法后,还需要把.py文件转换成可以在控制台里运行的命令行程序。最简单的办法就是在文件末尾加上如下代码:
if__name__=='__main__': command()
Click支持使用setuptools来更好的实现命令行程序打包,把源码文件打包成系统中的可执行程序,并且不限平台。一般我们会在源码根目录下创建setup.py脚本,先看一段简单的打包代码:
fromsetuptoolsimportsetup setup( 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接收一个变量值,下面是一段示例代码:
@click.command() @click.option('--n',default=1) defdots(n): click.echo('.'*n)
如果在命令行后面跟随参数--n=2就会输出两个点,如果传参数,默认输出一个点。上面的代码中,参数类型没有显示给出,但解释器会认为是INT型,因为默认值1是int值。
有些时候需要传入多个值,可以理解为一个list,option只支持固定长度的参数值,即设置后必须传入,个数由nargs确定。
@click.command() @click.option('--pos',nargs=2,type=float) deffindme(pos): click.echo('%s/%s'%pos) findme--pos2.03.0输出结果就是2.0/3.0
既然可以传入list,那么tuple呢?Click也是支持的:
@click.command() @click.option('--item',type=(unicode,int)) defputitem(item): click.echo('name=%sid=%d'%item)
这样就传入了一个tuple变量,putitem--itempeter1338得到的输出就是name=peterid=1338
上面没有设置nargs,因为nargs会自动取tuple的长度值。因此上面的代码实际上等同于:
@click.command() @click.option('--item',nargs=2,type=click.Tuple([unicode,int])) defputitem(item): click.echo('name=%sid=%d'%item)
option还支持同一个参数多次使用,类似gitcommit-maa-mbb中-m参数就传入了2次。option通过multiple标识位来支持这一特性:
@click.command() @click.option('--message','-m',multiple=True) defcommit(message): click.echo('\n'.join(message))
有时候,命令行参数是固定的几个值,这时就可以用到Click.choice类型来限定传参的潜在值:
#choice @click.command() @click.option('--hash-type',type=click.Choice(['md5','sha1'])) defdigest(hash_type): click.echo(hash_type)
当上面的命令行程序参数--hash-type不是md5或sha1,就会输出错误提示,并且在--help提示中也会对choice选项有显示。
如果希望命令行程序能在我们错误输入或漏掉输入的情况下,友好的提示用户,就需要用到Click的prompt功能,看代码:
#prompt @click.command() @click.option('--name',prompt=True) defhello(name): click.echo('Hello%s!'%name)
如果在执行hello时没有提供–name参数,控制台会提示用户输入该参数。也可以自定义控制台的提示输出,把prompt改为自定义内容即可。
对于类似账户密码等参数的输入,就要进行隐藏显示。option的hide_input和confirmation_promt标识就是用来控制密码参数的输入:
#password @click.command() @click.option('--password',prompt=True,hide_input=True, confirmation_prompt=True) defencrypt(password): click.echo('Encryptingpasswordto%s'%password.encode('rot13'))
Click把上面的操作进一步封装成装饰器click.password_option(),因此上面的代码也可以简化成:
#password @click.command() @click.password_option() defencrypt(password): click.echo('Encryptingpasswordto%s'%password.encode('rot13'))
有的参数会改变命令行程序的执行,比如node是进入Node控制台,而node--verion是输出node的版本号。Click提供eager标识对参数名进行标记,拦截既定的命令行执行流程,而是调用一个回调方法,执行后直接退出。下面模拟click.version_option()的功能,实现--version参数名输出版本号:
#eager defprint_version(ctx,param,value): ifnotvalueorctx.resilient_parsing: return click.echo('Version1.0') ctx.exit() @click.command() @click.option('--version',is_flag=True,callback=print_version, expose_value=False,is_eager=True) defhello(): click.echo('HelloWorld!')
对于类似删除数据库表这样的危险操作,Click支持弹出确认提示,--yes标识位置为True时会让用户再次确认:
#yesparameters defabort_if_false(ctx,param,value): ifnotvalue: ctx.abort() @click.command() @click.option('--yes',is_flag=True,callback=abort_if_false, expose_value=False, prompt='Areyousureyouwanttodropthedb?') defdropdb(): click.echo('Droppedalltables!')
测试运行下:
$dropdb Areyousureyouwanttodropthedb?[y/N]:n Aborted! $dropdb--yes Droppedalltables!
同样的,Click对次进行了封装,click.confirmation_option()装饰器实现了上述功能:
@click.command() @click.confirmation_option(prompt='Areyousureyouwanttodropthedb?') defdropdb(): click.echo('Droppedalltables!')
前面只讲了默认的参数前缀--和-,Click允许开发者自定义参数前缀(虽然严重不推荐)。
#otherprefix @click.command() @click.option('+w/-w') defchmod(w): click.echo('writable=%s'%w) if__name__=='__main__': chmod()
如果想要用/作为前缀,而且要像上面一样采用布尔标识,会产生冲突,因为布尔标识也是用/,这种情况下可以用;代替布尔标识的/:
@click.command() @click.option('/debug;/no-debug') deflog(debug): click.echo('debug=%s'%debug) if__name__=='__main__': log()
既然支持Choice,不难联想到Range,先看代码:
#range @click.command() @click.option('--count',type=click.IntRange(0,20,clamp=True)) @click.option('--digit',type=click.IntRange(0,10)) defrepeat(count,digit): click.echo(str(digit)*count) if__name__=='__main__': repeat()
Argument参数
Argument的作用类似Option,但没有Option那么全面的功能。
和Option一样,Argument最基础的应用就是传递一个简单变量值:
@click.command() @click.argument('filename') deftouch(filename): click.echo(filename)
命令行后跟的参数值被赋值给参数名filename。
另一个用的比较广泛的是可变参数,也是由nargs来确定参数个数,变量值会以tuple的形式传入函数:
@click.command() @click.argument('src',nargs=-1) @click.argument('dst',nargs=1) defcopy(src,dst): forfninsrc: click.echo('move%stofolder%s'%(fn,dst))
运行程序:
$copyfoo.txtbar.txtmy_folder movefoo.txttofoldermy_folder movebar.txttofoldermy_folder
Click支持通过文件名参数对文件进行操作,click.File()装饰器就是处理这种操作的,尤其是在类Unix系统下,它支持以-符号作为标准输入/输出。
#File @click.command() @click.argument('input',type=click.File('rb')) @click.argument('output',type=click.File('wb')) definout(input,output): whileTrue: chunk=input.read(1024) ifnotchunk: break output.write(chunk)
运行程序,先将文本写进文件,再读取
$inout-hello.txt hello ^D $inouthello.txt- hello
如果参数值只是想做为文件名而已呢,很简单,将type指定为click.Path():
@click.command() @click.argument('f',type=click.Path(exists=True)) deftouch(f): click.echo(click.format_filename(f)) $touchhello.txt hello.txt $touchmissing.txt Usage:touch[OPTIONS]F Error:Invalidvaluefor"f":Path"missing.txt"doesnotexist.
更多关于Python3Click模块的使用方法请查看下面的相关链接
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。