click--命令行神器

一, 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ù)來解決,但multiplenargs的值大于1的時候蚪腐,Click將調(diào)用 ParamType.split_envvar_value()來執(zhí)行分割箭昵。除typeFilePath之外的其它類型將全部以空格分割。

@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 subtool.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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末拣度,一起剝皮案震驚了整個濱河市搪桂,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌描扯,老刑警劉巖定页,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異绽诚,居然都是意外死亡典徊,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進店門恩够,熙熙樓的掌柜王于貴愁眉苦臉地迎上來卒落,“玉大人,你說我怎么就攤上這事蜂桶±鼙希” “怎么了?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵扑媚,是天一觀的道長腰湾。 經(jīng)常有香客問我,道長疆股,這世上最難降的妖魔是什么费坊? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮押桃,結(jié)果婚禮上葵萎,老公的妹妹穿的比我還像新娘导犹。我一直安慰自己,他們只是感情好羡忘,可當(dāng)我...
    茶點故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布谎痢。 她就那樣靜靜地躺著,像睡著了一般卷雕。 火紅的嫁衣襯著肌膚如雪节猿。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天漫雕,我揣著相機與錄音滨嘱,去河邊找鬼。 笑死浸间,一個胖子當(dāng)著我的面吹牛太雨,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播魁蒜,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼囊扳,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了兜看?” 一聲冷哼從身側(cè)響起锥咸,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎细移,沒想到半個月后搏予,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡弧轧,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年雪侥,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片精绎。...
    茶點故事閱讀 39,795評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡校镐,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出捺典,到底是詐尸還是另有隱情,我是刑警寧澤从祝,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布襟己,位于F島的核電站,受9級特大地震影響牍陌,放射性物質(zhì)發(fā)生泄漏擎浴。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一毒涧、第九天 我趴在偏房一處隱蔽的房頂上張望贮预。 院中可真熱鬧,春花似錦、人聲如沸仿吞。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽唤冈。三九已至峡迷,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間你虹,已是汗流浹背绘搞。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留傅物,地道東北人夯辖。 一個月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像董饰,于是被迫代替她去往敵國和親蒿褂。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,724評論 2 354

推薦閱讀更多精彩內(nèi)容