一, About Click
如果想在啟動python腳本的時候傳入?yún)?shù)庶灿,我們通常會用sys.argv來獲取參數(shù)列表滴肿,然后判斷一下參數(shù)個數(shù)决乎、類型等是否合法队询,這樣當(dāng)然可以,但如果用click的話可以很簡單優(yōu)雅的實現(xiàn)這些邏輯构诚,并且它還支持更高端用法蚌斩。
本文是讀了官方文檔后做的記錄,方便日后使用時查閱范嘱。
下面是一個官方的例子:
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()
加參數(shù)執(zhí)行結(jié)果:
$ python hello.py --count=3
Your name: John
Hello John!
Hello John!
Hello John!
并且自動生成格式優(yōu)美的幫助提示:
$ 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.
二, 安裝
pip install click
三, 參數(shù)
1送膳,options與arguments
click支持兩種參數(shù)员魏,option和 argument。兩個有一些區(qū)別叠聋。
以下特性只有options支持:
- 未輸入?yún)?shù)自動提示
- 作為標(biāo)記(boolean or otherwise)
- options的值可以從環(huán)境變量中獲取撕阎,而arguments不能。
- options在幫助頁面有詳細文檔碌补,而arguments沒有
另外:arguments可以接收任意數(shù)量的參數(shù)虏束,options只能接收指定數(shù)量的參數(shù),默認(rèn)是1.
2厦章,參數(shù)類型
str / click.STRING
int / click.INT
float / click.FLOAT
bool / click.BOOL
ckick.UUID
click.File
click.Path
click.Choice
click.IntRange
3, 參數(shù)名
傳遞給函數(shù)的參數(shù)優(yōu)先使用長名镇匀,即以--
開頭的名字,如果沒有則使用以-
開頭的名闷袒。
但如果有不含-
的字符串坑律,則直接用作變量名。
如:
('-f', '--foo-bar')
傳遞給函數(shù)的參數(shù)名為foo_bar
, ('-x',)
則為x
囊骤,('-f', '--filename', 'dest')
為dest
四,options
1. 基本
最基本的option是單值的冀值,如果沒有指定類型也物,那么則為string。option的默認(rèn)值用default指定列疗。
@click.command()
@click.option('--n', default=1)
def dots(n):
click.echo('.' * n)
執(zhí)行$ dots --n=2
滑蚯,輸出..
2. 多值選項
當(dāng)參數(shù)的值大于1個是,用參數(shù)nargs指定參數(shù)個數(shù)抵栈,option的參數(shù)個數(shù)是固定的告材。參數(shù)將以tuple的格式傳遞給變量。
@click.command()
@click.option('--pos', nargs=2, type=float)
def findme(pos):
click.echo('%s / %s' % pos)
執(zhí)行findme --pos 2.0 3.0
古劲,輸出2.0 / 3.0
3. 用tuple指定多個值的類型
在上一個列子中兩個參數(shù)的類型是相同的斥赋,但這可能并不是你想要的,有時候需要兩個不同類型的值产艾。那么可以這樣疤剑。
@click.command()
@click.option('--item', type=(unicode, int))
def putitem(item):
click.echo('name=%s id=%d' % item)
當(dāng)type參數(shù)為tuple類型時,nargs為type的長度闷堡。
執(zhí)行putitem --item peter 1338
隘膘,輸出name=peter id=1338
4. 多個相同選項
類似但不同于多值選項,有時候需要多次輸入相同的選項杠览。
@click.command()
@click.option('--message', '-m', multiple=True)
def commit(message):
click.echo('\n'.join(message))
執(zhí)行commit -m foo -m bar
輸入:
foo
bar
5. 計數(shù)
@click.command()
@click.option('-v', '--verbose', count=True)
def log(verbose):
click.echo('Verbosity: %s' % verbose)
執(zhí)行log -vvv
弯菊,輸出Verbosity: 3
感覺這個功能有點雞肋
6. boolean標(biāo)記
布爾標(biāo)記是用來啟用或禁用的選項,你可以用/
分隔符來實現(xiàn)啟用或禁用選項踱阿。(當(dāng)/
在選項名中的時候管钳,click就會認(rèn)為它是個Boolean標(biāo)記)吨悍。
import sys
@click.command()
@click.option('--shout/--no-shout', default=False)
def info(shout):
rv = sys.platform
if shout:
rv = rv.upper() + '!!!!111'
click.echo(rv)
執(zhí)行:
$ info --shout
LINUX2!!!!111
$ info --no-shout
linux2
也可以不用/
,而是用is_flag參數(shù)告知click這是一個boolean標(biāo)記蹋嵌。
@click.command()
@click.option('--shout', is_flag=True)
def info(shout):
rv = sys.platform
if shout:
rv = rv.upper() + '!!!!111'
click.echo(rv)
boolean值默認(rèn)是false.
選項別名(如果某個不想定義育瓜,就寫為空格):
@click.command()
@click.option('--shout/--no-shout', ' /-S', default=False)
def info(shout):
rv = sys.platform
if shout:
rv = rv.upper() + '!!!!111'
click.echo(rv)
執(zhí)行:
$ info --help
Usage: info [OPTIONS]
Options:
--shout / -S, --no-shout
--help
7. Feature Switches
我感覺Feature Switches有點類似于html表單里單選框,也可以用來實現(xiàn)類似Boolean標(biāo)記的功能栽烂。
多個option指定同一個名稱躏仇,并設(shè)置flag_value
。
import sys
@click.command()
@click.option('--upper', 'transformation', flag_value='upper',
default=True)
@click.option('--lower', 'transformation', flag_value='lower')
def info(transformation):
click.echo(getattr(sys.platform, transformation)())
執(zhí)行結(jié)果:
$ info --upper
LINUX2
$ info --lower
linux2
$ info
LINUX2
8. 可選項
@click.command()
@click.option('--hash-type', type=click.Choice(['md5', 'sha1']))
def digest(hash_type):
click.echo(hash_type)
執(zhí)行:
$ digest --hash-type=md5
md5
$ digest --hash-type=foo
Usage: digest [OPTIONS]
Error: Invalid value for "--hash-type": invalid choice: foo. (choose from md5, sha1)
$ digest --help
Usage: digest [OPTIONS]
Options:
--hash-type [md5|sha1]
--help Show this message and exit.
9. 提示
如果option未輸入時腺办,提示焰手。使用prompt參數(shù),prompt=True
則使用默認(rèn)提示怀喉,也可以prompt="使用自定義提示語"
@click.command()
@click.option('--name', prompt='Your name please')
def hello(name):
click.echo('Hello %s!' % name)
執(zhí)行:
$ hello
Your name please: John
Hello John!
10. 密碼
隱藏輸入字符书妻,并兩次輸入確認(rèn)。
@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'))
執(zhí)行:
$ encrypt
Password:
Repeat for confirmation:
Encrypting password to frperg
更簡單的:
@click.command()
@click.password_option()
def encrypt(password):
click.echo('Encrypting password to %s' % password.encode('rot13'))
11. 動態(tài)默認(rèn)值并提示
設(shè)置auto_envvar_prefix和default_map參數(shù)后 可以從環(huán)境變量或配置文件中讀取默認(rèn)值躬拢,但是這更改了提示語機制躲履,使用戶不能交互式的輸入。
這么做可以兩者兼得:
@click.command()
@click.option('--username', prompt=True,
default=lambda: os.environ.get('USER', ''))
def hello(username):
print("Hello,", username)
12. Callbacks and Eager Options
todo
13. YES
有些操作需要提示用戶確認(rèn)后再執(zhí)行聊闯。
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!')
執(zhí)行:
$ dropdb
Are you sure you want to drop the db? [y/N]: n
Aborted!
$ dropdb --yes
Dropped all tables!
同樣的:
@click.command()
@click.confirmation_option(prompt='Are you sure you want to drop the db?')
def dropdb():
click.echo('Dropped all tables!')
14. 從環(huán)境變量取值
兩種方式:
1工猜,調(diào)用時指定auto_envver_prefix
參數(shù),則自動使用以auto_envver_prefix
為前綴菱蔬、option名為后綴下劃線分割的大寫環(huán)境變量篷帅。
@click.command()
@click.option('--username')
def greet(username):
click.echo('Hello %s!' % username)
if __name__ == '__main__':
greet(auto_envvar_prefix='GREETER')
And from the command line:
$ export GREETER_USERNAME=john
$ greet
Hello john!
2,手動指定以envvar參數(shù)指定環(huán)境變量
@click.command()
@click.option('--username', envvar='USERNAME')
def greet(username):
click.echo('Hello %s!' % username)
if __name__ == '__main__':
greet()
And from the command line:
$ export USERNAME=john
$ greet
Hello john!
15. 多個值的環(huán)境變量
option可以接收多個值的參數(shù)拴泌,從環(huán)境變量中獲取多值有點復(fù)雜魏身,Click把它交給type
參數(shù)來解決,但multiple
或nargs
的值大于1的時候蚪腐,Click將調(diào)用 ParamType.split_envvar_value()
來執(zhí)行分割箭昵。除type
為File
和Path
之外的其它類型將全部以空格分割。
@click.command()
@click.option('paths', '--path', envvar='PATHS', multiple=True,
type=click.Path())
def perform(paths):
for path in paths:
click.echo(path)
if __name__ == '__main__':
perform()
And from the command line:
$ export PATHS=./foo/bar:./test
$ perform
./foo/bar
./test
16. 區(qū)間
IntRange
有兩種模式:
1削茁,默認(rèn)模式宙枷,當(dāng)超出范圍后拋出異常
2,clamp=True
時茧跋,越界后取極值慰丛,比如當(dāng)區(qū)間為0-5, 10則為5, -1為0
@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()
And from the command line:
$ repeat --count=1000 --digit=5
55555555555555555555
$ repeat --count=1000 --digit=12
Usage: repeat [OPTIONS]
Error: Invalid value for "--digit": 12 is not in the valid range of 0 to 10.
當(dāng)區(qū)間的一邊設(shè)置為None
時,表示不限制
17. 自定義校驗
如果你需要自定義校驗邏輯瘾杭,可以通過callback
參數(shù)實現(xiàn)诅病。回調(diào)函數(shù)既可以改變值也可以拋出異常。
def validate_rolls(ctx, param, value):
try:
rolls, dice = map(int, value.split('d', 2))
return (dice, rolls)
except ValueError:
raise click.BadParameter('rolls need to be in format NdM')
@click.command()
@click.option('--rolls', callback=validate_rolls, default='1d6')
def roll(rolls):
click.echo('Rolling a %d-sided dice %d time(s)' % rolls)
if __name__ == '__main__':
roll()
And what it looks like:
$ roll --rolls=42
Usage: roll [OPTIONS]
Error: Invalid value for "--rolls": rolls need to be in format NdM
$ roll --rolls=2d12
Rolling a 12-sided dice 2 time(s)
五. Arguments
Arguments只支持Option特性的子集贤笆,Click不會為Arguments參數(shù)生成幫助文檔蝇棉。
1. 基本用法
默認(rèn)類型是string.
@click.command()
@click.argument('filename')
def touch(filename):
click.echo(filename)
And what it looks like:
$ touch foo.txt
foo.txt
2. 變長參數(shù)
用參數(shù)nargs
指定值的格式,-1表示最大芥永,但只能有一個參數(shù)被設(shè)置為-1篡殷,因為它會獲取剩下所有的值。
@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))
And what it looks like:
$ copy foo.txt bar.txt my_folder
move foo.txt to folder my_folder
move bar.txt to folder my_folder
3.文件參數(shù)
Click通過click.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:
break
output.write(chunk)
And what it does:
$ inout - hello.txt
hello
^D
$ inout hello.txt -
hello
-
代表stdin/stdout
4.文件路徑參數(shù)
@click.command()
@click.argument('f', type=click.Path(exists=True))
def touch(f):
click.echo(click.format_filename(f))
And what it does:
$ touch hello.txt
hello.txt
$ touch missing.txt
Usage: touch [OPTIONS] F
Error: Invalid value for "f": Path "missing.txt" does not exist.
5.環(huán)境變量
像option一樣argument也支持獲取從環(huán)境變量中讀取值埋涧,和option不同的是板辽,它只支持明確指定環(huán)境變量名的方式。
@click.command()
@click.argument('src', envvar='SRC', type=click.File('r'))
def echo(src):
click.echo(src.read())
And from the command line:
$ export SRC=hello.txt
$ echo
Hello World!
6. Option-Like Arguments
假如有一個文件-foo.txt
棘催,如果把這個作為argument的值劲弦,click會把它當(dāng)成一個option的值。為了解決這個問題醇坝,像其它POSIX格式的命令行那樣邑跪,click把--
當(dāng)成option和argument的分割符。
@click.command()
@click.argument('files', nargs=-1, type=click.Path())
def touch(files):
for filename in files:
click.echo(filename)
And from the command line:
$ touch -- -foo.txt bar.txt
-foo.txt
bar.txt
六. Commands and Groups
Click最重要的特征之一是任意嵌套的命令行呼猪,這個特性通過command和group(MultiCommand)實現(xiàn)画畅。
1. 回調(diào)
對一個常規(guī)的command來說,只要command運行郑叠,回調(diào)必然執(zhí)行夜赵,除非參數(shù)的回調(diào)函數(shù)打斷了它,比如--help
但對于group和多個command來說乡革,情況不同√福回調(diào)僅在子命令調(diào)用時執(zhí)行沸版。
@click.group()
@click.option('--debug/--no-debug', default=False)
def cli(debug):
click.echo('Debug mode is %s' % ('on' if debug else 'off'))
@cli.command()
def sync():
click.echo('Synching')
Here is what this looks like:
$ tool.py
Usage: tool.py [OPTIONS] COMMAND [ARGS]...
Options:
--debug / --no-debug
--help Show this message and exit.
Commands:
sync
$ tool.py --debug sync
Debug mode is on
Synching
2. 傳遞參數(shù)
Click驗證區(qū)分命令和子命令的參數(shù),這意味著option和argument必須跟在它對應(yīng)的command之后兴蒸,而在其他command之前视粮。可以用--help
來查看說明橙凳。比如一個程序tools.py蕾殴, 有一個名叫sub的子命令。
-
tool.py --help
將返回整個程序的幫助 -
tool.py sub --help
將返回子命令的 幫助 -
tool.py --help sub
和tool.py --help
效果一樣岛啸,因為--help
會中斷子命令繼續(xù)執(zhí)行钓觉。
3. 嵌套處理和上下文
在第一個例子中,debug
參數(shù)并沒有傳遞給子命令坚踩,子命令只能接收它自己的參數(shù)荡灾。如果嵌套命令想相互通信,可以通過Context
.
每當(dāng)一個命令被調(diào)用時,一個上下文對象將產(chǎn)生批幌,并且和上一個上下文對象連接础锐。通常看不到它們荧缘,但確實存在皆警,上下文對象會自動和參數(shù)的值一起傳遞給參數(shù)的回調(diào)函數(shù)。Command
也可以通過使用pass_context()
修飾器手動的請求傳遞上下文對象給它們截粗。
上下文對象也可以攜帶其它你附加的對象信姓。
@click.group()
@click.option('--debug/--no-debug', default=False)
@click.pass_context
def cli(ctx, debug):
ctx.obj['DEBUG'] = debug
print id(ctx.obj)
print id(ctx)
@cli.command()
@click.pass_context
def sync(ctx):
click.echo('Debug is %s' % (ctx.obj['DEBUG'] and 'on' or 'off'))
print id(ctx.obj)
print id(ctx)
print id(ctx.parent)
if __name__ == '__main__':
cli(obj={})
執(zhí)行:
python s.py --debug sync
140673188343888
140673188304592
Debug is on
140673188343888
140673188304656
140673188304592
可以看出上下文的obj
對象是共享的,但是會被重寫桐愉,想訪問上一級上下文财破,可以通過ctx.parent
4. Decorating Commands
pass
5. Group Invocation Without Command
默認(rèn)情況下group和command不會被調(diào)用除非有子命令,事實上會沒有子命令時會調(diào)用--help
. 可以通過傳參invoke_without_command=True
來改變這邏輯从诲。在下面的例子中不管有沒有子命令回調(diào)都將執(zhí)行踪少,上下文對象中包含了是否調(diào)用子命令信息。
@click.group(invoke_without_command=True)
@click.pass_context
def cli(ctx):
if ctx.invoked_subcommand is None:
click.echo('I was invoked without subcommand')
else:
click.echo('I am about to invoke %s' % ctx.invoked_subcommand)
@cli.command()
def sync():
click.echo('The subcommand')
And how it works in practice:
$ tool
I was invoked without subcommand
$ tool sync
I am about to invoke sync
The subcommand
6. Custom Multi Commands
pass
7.Merging Multi Commands
pass
8. Multi Command Pipelines
pass
9. Overriding Defaults
pass
10.Context Defaults
pass
11. Command Return Values
pass