# Python Click(翻譯)
###### tags: `翻譯` `Python`
[TOC]
## 安裝
```shell
pip install click
```
如果想測試Click的話也可以利用`virtualenv`來隔離環境
## 說明
:::danger
官方文件:
* [click](https://click.palletsprojects.com/en/7.x/)
:::
Click是一個可以利用很少的程式碼來建置Command Line介面的Package,它是Command Line介面的創造工具,有高度可配置性,但是又有著開箱即用的合理預設值。
它的宗旨是讓編寫Command Line工具的過程是快又有趣,並且防止任何因為無法實現預期CLI API而導致的挫折。
Click有三個重點:
* 任意嵌入命令(Command)
* 自動生成`help`
* 支援執行中延遲加載(lazy loading)子命令(subcommands)
它看起來像什麼?下面是一個簡單的Click程式範例,新增一個Python文件,命名為'hello.py',如下:
```python
# file name: 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):
for x in range(count):
click.echo(f'hello {name}')
if __name__ == '__main__':
hello()
```
接著開啟Command Line測試,如下:
* 測試參數--help
```shell
D:\python\spyder_ticket>python hello.py --help
Usage: hello.py [OPTIONS]
Options:
--count INTEGER Number of greetings.
--name TEXT The person to greet.
--help Show this message and exit.
```
* 測試只輸入`--count`
```shell
D:\python\spyder_ticket>python hello.py --count=1
Your name:
```
這時候系統會停留在Your name處等你輸入下一個參數,這是來自於上面`prompt`的設置,輸入姓名之後就正常的執行後續的程式碼。
```shell
D:\python\spyder_ticket>python hello.py --count=1
Your name: shaoe
hello shaoe
D:\python\spyder_ticket>python hello.py --count=3
Your name: shaoe
hello shaoe
hello shaoe
hello shaoe
```
* 測試兩個參數一併輸入
```shell
D:\python\spyder_ticket>python hello.py --count=3 --name=shaoe
hello shaoe
hello shaoe
hello shaoe
```
## [Quickstart](https://click.palletsprojects.com/en/7.x/quickstart/)
### [Screencast and Examples](https://click.palletsprojects.com/en/7.x/quickstart/#screencast-and-examples)
官方文件上有一個[基礎說明影片](https://www.youtube.com/watch?v=kNke39OZ2k0),裡面快速演示著Click的API操作與建立簡單的應用程式以及如何利用子Subcommand來建立Command。
GitHub上也有豐富的範例文檔:
* inout: [File input and output](https://github.com/pallets/click/tree/master/examples/inout)
* naval: [Port of docopt naval example](https://github.com/pallets/click/tree/master/examples/naval)
* aliases: [Command alias example](https://github.com/pallets/click/tree/master/examples/aliases)
* repo: [Git-/Mercurial-like command line interface](https://github.com/pallets/click/tree/master/examples/repo)
* complex: [Complex example with plugin loading](https://github.com/pallets/click/tree/master/examples/complex)
* validation: [Custom parameter validation example](https://github.com/pallets/click/tree/master/examples/validation)
* colors: [Colorama ANSI color support](https://github.com/pallets/click/tree/master/examples/colors)
* termui: [Terminal UI functions demo](https://github.com/pallets/click/tree/master/examples/termui)
* imagepipe: [Multi command chaining demo](https://github.com/pallets/click/tree/master/examples/imagepipe)
### [Basic Concepts - Creating a Command](https://click.palletsprojects.com/en/7.x/quickstart/#basic-concepts-creating-a-command)
Click利用裝飾器來宣告命令,在內部有一個高階用法是不應用裝飾器的接口,但並不建議使用。
函數透過裝飾器`@click.command()`變更為Click Command Line,最簡單的方式,使用裝飾器裝飾一個函數會使它變為一個可調用的腳本,下面新增一個Python文件,命名為`hello.py`:
```python
import click
@click.command()
def hello():
click.echo('Hello World!')
if __name__ == '__main__':
hello()
```
上面範例發生的事情是,裝飾器將函數`hello`轉換為Command,現在你可以調用它了。
```shell
>>> python hello.py
Hello World!
>>> python hello.py --help
Usage: hello.py [OPTIONS]
Options:
--help Show this message and exit.
```
### [Echoing](https://click.palletsprojects.com/en/7.x/quickstart/#echoing)
為什麼這個範例使用`echo()`而不是使用正規的`print()`函數?這問題的答案是,Cilck試著以相同的方式支援Python2與Python3,即使環境配置錯誤的時候也非常強大。Click想要在最基本的級別上有作用。, 即使一切都完全破壞了,這意味著`echo()`函數實現了一些錯誤偵測以防止終端機配置錯誤而造成`UnicodeError`。
額外的好處是,`Click 2.0`之後,`echo()`也支援了ANSI色彩。如果輸出流是文件並且支援colorama,它將自動地隔離ANSI程式碼,ANSI色彩也適用於Windows。注意到,Python2中`echo()`函數並不會從bytearrays中解析顏色代碼,更多的訊息請參閱[ANSI Color文件](https://click.palletsprojects.com/en/7.x/utils/#ansi-colors)
如果你不需要`echo()`,也可以使用`print()`
### [Nesting Commands](https://click.palletsprojects.com/en/7.x/quickstart/#nesting-commands)
命令可以附加到其它的群組類型命令,這允許任意崁套腳本,下面範例是一個腳本,它實現了兩個管理資料庫的命令,如下:
```python
@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()`裝飾器像`@click.command`般的運作,但建立`Group`物件可以利用`Group.add_command(command)`來附加子命令。
對於簡單的應用也可以直接利用`Group.command`來附加命令,如下面範例所示:
```python
@click.group()
def cli():
pass
@cli.command()
def initdb():
click.echo('Initialized the database')
@cli.command()
def dropdb():
click.echo('Dropped the database')
You would then invoke the Group in your setuptools entry points or other invocations:
if __name__ == '__main__':
cli()
```
### [Adding Parameters](https://click.palletsprojects.com/en/7.x/quickstart/#adding-parameters)
要增加參數,可以使用`option()`與`argument()`裝飾器,如下:
```python
@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)
```
利用`option`設置參數`--count`,它的預設值為1,說明文字(help)為`number of greetings`
利用`argument`設置參數`name`,利用`argument`設置的參數並不會在help中顯示。
它看起來像什麼:
```shell
>>> python hello.py --help
Usage: hello.py [OPTIONS] NAME
Options:
--count INTEGER number of greetings
--help Show this message and exit.
```
### [Switching to Setuptools](https://click.palletsprojects.com/en/7.x/quickstart/#switching-to-setuptools)
目前為止所寫的程式碼在最後的區塊中都有著`if __name__ == '__main__'`,這是傳統單一Python文件的作法,使用Click你可以繼續這麼做,但有更好的方法,透過setuptools,這麼做有兩個主要原因:
1. setuptools自動為Windows生成可執行包裝器,因此你的Command Line也可以在Windows上執行。
2. setuptools腳本在Unix上使用virtualenv而不需要啟動它。這是非常實用的概念,允許你將你的腳本綁定到virtualenv中
Click非常適合使用它,實際上其他文檔將假設你正通過setuptools編寫應用程式,我強烈建議在閱讀其它文章之前先去查閱[Setuptools Integration](https://click.palletsprojects.com/en/7.x/setuptools/#setuptools-integration)章節,因為範例將假設你會使用setuptools
## [Options](https://click.palletsprojects.com/en/7.x/options/)
可以通過裝飾器`@option`來增加`option`,由於`option`可以有各種不同的版本,因此有大量參數可用來配置行為。Click中的`option`與[位置參數](https://click.palletsprojects.com/en/7.x/arguments/#arguments)(positional arguments)是不同的
### Name Your Options
命名規則可以在[Parameter Names](https://click.palletsprojects.com/en/7.x/parameters/#parameter-names)找到,簡單的說,你可以通過最長的`dash-prefixed`參數隱式地引用該`option`,如下:
```python
@click.command()
@click.option('-s', '--string-to-echo')
def echo(string_to_echo):
click.echo(string_to_echo)
```
或是明確的給一個`non-dash-prefixed`參數,如下:
```python
@click.command()
@click.option('-s', '--string-to-echo', 'string')
def echo(string):
click.echo(string)
```
### Basic Value Options
最基礎的`option`是價值選項。這些`option`接受一個參數,即一個值。如果未提供型別,則使用預設值的型別。如果未提供預設值,則假定型別為STRING(文字)。
除非明確指定名稱,否則參數的名稱就是定義的第一個`long option`<sub>(兩個`--`)</sub>,不然就使用第一個`short option`<sub>(一個`-`)</sub>
預設情況下,`option`並非必要的,但如果要將`option`設置為必要項目,可以在裝飾器中加入參數`required=True`
```python
@click.command()
@click.option('--n', default=1)
def dots(n):
click.echo('.' * n)
```
`--n`內的`n`即為參數名稱,因此後續函數可直接取用`n`,其預設值為1,因此型別會因預設值而定義為`int`
```shell
$ dots --n=2
..
```
測試該命令得到兩個『.』的回應,但注意到我們並沒有確實的定義`n`為`int`
```python
# How to make an option required
@click.command()
@click.option('--n', required=True, type=int)
def dots(n):
click.echo('.' * n)
```
利用`required=True`將`--n`設置為必須項目,並且型別宣告為`int`
```python
# How to use a Python reserved word such as `from` as a parameter
@click.command()
@click.option('--from', '-f', 'from_')
@click.option('--to', '-t')
def reserved_param_name(from_, to):
click.echo('from %s to %s' % (from_, to))
```
使用Python保留字`from`做為選項,後續定義其參數名稱為`from_`
如果要在Command help上顯示`option`的預設值,可以設置`show_default=True`,如下:
```python
@click.command()
@click.option('--n', default=1, show_default=True)
def dots(n):
click.echo('.' * n)
```
```shell
$ dots --help
Usage: dots [OPTIONS]
Options:
--n INTEGER [default: 1]
--help Show this message and exit.
```
### Multi Value Options
有時候,你的`option`帶有一個以上的參數。對於`option`,僅支持固定數量的參數。這可以利用`nargs`參數進行設置。然後將值存儲為`tuple`,如下範例:
```python
@click.command()
@click.option('--pos', nargs=2, type=float)
def findme(pos):
click.echo('%s / %s' % pos)
```
設置`nrgs=2`代表該`option`接收兩個參數值,並且`pos`的格式會變為`tuple`,但參數的型別為`float`
```shell
$ findme --pos 2.0 3.0
2.0 / 3.0
```
### Counting
在非常少見的情況下,使用重複`options`來計算整數是很有趣的。這可以用於詳細標記,例如:
設置`count=True`
```python
@click.command()
@click.option('-v', '--verbose', count=True)
def log(verbose):
click.echo('Verbosity: %s' % verbose)
```
執行如下:
```shell
$ log -vvv
Verbosity: 3
```
統計你輸入了三個v
### Boolean Flags
Boolean flags option可以用來啟用或禁用項目。這可以用`/`來定義啟用與禁用的`option`(如果`/`存在`option`字串中,Click會知道這是一個Boolean flag,並且會隱式的傳遞`is_flag=True`),Click總是希望你提供啟用或是禁用的flag,因此你可以在稍後更改預設值。
範例如下:
```python
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)
```
預設項目為`False`,執行如下:
```shell
$ info --shout
LINUX!!!!111
$ info --no-shout
linux
```
`/`的左邊為啟用(enable),右邊為禁用(disable)
如果你真的不希望利用開關來開開關關的,你可以只定義一個`option`並手動通知點擊某個東西是一個flag,如下:
```python
import sys
@click.command()
@click.option('--shout', is_flag=True)
def info(shout):
rv = sys.platform
if shout:
rv = rv.upper() + '!!!!111'
click.echo(rv)
```
只設置一個`--shout`,因此在你有給這個`option`的時候才會當它是`True`,如下:
```shell
$ info --shout
LINUX!!!!111
```
如果你只想替第二個選項定義別名,你將需要在別名前面留空格,範例如下:
```python
import sys
@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)
```
上面設置` /-S`,注意前面留空格,當執行指令的時候加上`-S`即代表`--no-shout`
```shell
$ info --help
Usage: info [OPTIONS]
Options:
--shout / -S, --no-shout
--help Show this message and exit.
```
### Feature Switches
除了boolean flags之外,還有功能開關。這是通過將多個`options`設置相同參數名稱並定義`flag`值來實現的。請注意,通過提供參數`flag_value`,Click將隱式的設置`is_flag=True`。
對於要設置`flag`為預設值的部份應該要將其項目設置為`default=True`,如下範例:
```python
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)())
```
`--upper`設置了`default=True`代表該選項為預設,執行結果如下:
```shell
$ info --upper
LINUX
$ info --lower
linux
$ info
LINUX
```
### Choice Options
部份時候,你希望參數是一個選擇清單。這種情況下可以使用`choice`類型。它可以被實例化為有效值清單,如下範例:
```python
@click.command()
@click.option('--hash-type', type=click.Choice(['md5', 'sha1']))
def digest(hash_type):
click.echo(hash_type)
```
執行如下:
```shell
$ digest --hash-type=md5
md5
```
執行指令,設置`option`為md5
```shell
$ digest --hash-type=foo
Usage: digest [OPTIONS]
Try "digest --help" for help.
Error: Invalid value for "--hash-type": invalid choice: foo. (choose from md5, sha1)
```
執行指令,故意不使用有效清單內值,造成異常
```shell
$ digest --help
Usage: digest [OPTIONS]
Options:
--hash-type [md5|sha1]
--help Show this message and exit.
```
:::warning
只能使用`list`或是`tuple`做項目物件,其它的可迭代物件可能會造成令人驚訝的結果
:::
### Prompting
部份情況下,你想要可以從命令行提供參數,但假如沒有提供的話就需要求要使用者輪入。這可以透過定義Click`prompt`來實現,如下範例:
```python
@click.command()
@click.option('--name', prompt=True)
def hello(name):
click.echo('Hello %s!' % name)
```
它看起來像什麼:
```shell
$ hello --name=John
Hello John!
$ hello
Name: John
Hello John!
```
如果你對預設的提示字串不滿意的話,你可以另外設置,如下:
```python
@click.command()
@click.option('--name', prompt='Your name please')
def hello(name):
click.echo('Hello %s!' % name)
```
它看來起像什麼:
```shell
$ hello
Your name please: John
Hello John!
```
### Password Prompts
Click還支援隱藏提示並要求確認,這對輸入密碼非常有效:
```python
@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'))
```
執行如下:
```shell
$ encrypt
Password:
Repeat for confirmation:
```
當輸入一次密碼之後會再一次要求輸入確認。
因為這種參數組合很常見,也可以利用裝飾器`password_option()`來替代:
```python
@click.command()
@click.password_option()
def encrypt(password):
click.echo('Encrypting password to %s' % password.encode('rot13'))
```
### Dynamic Defaults for Prompts
上下文中的`auto_envvar_prefix`與`default_map`兩個`options`允許應用程式從環境變數或設定檔案中讀取`option values`。然而,這會覆寫提示機制,因此使用者無法在交互式界面中改變`option value`。
假如你要讓使用者可以設置預設值,但依然會提示,如果沒有在`Command line`中指定`option`,可以透過提供`callable`作為預設值,如下範例,從環境變數取得預設值:
```python
@click.command()
@click.option('--username', prompt=True,
default=lambda: os.environ.get('USER', ''))
def hello(username):
print("Hello,", username)
```
要描述預設值,可以在`show_default`中設置:
```python
@click.command()
@click.option('--username', prompt=True,
default=lambda: os.environ.get('USER', ''),
show_default='current user')
def hello(username):
print("Hello,", username)
```
```shell
$ hello --help
Usage: hello [OPTIONS]
Options:
--username TEXT [default: (current user)]
--help Show this message and exit.
```
### Callbacks and Eager Options
有時候你希望參數可以完全的改變執行流程。舉例來說,當你希望有一個`--version`參數在列印出版本之後就退出應用程式就是這種情況。
注意:Click內`click.version_option()`提供了可重用`--version`參數的實際實現。這邊的程式碼只是如何實現這種`flag`的範例。
這種情況下,你需要兩個概念:`eager parameter, callback`。`eager parameter`指在其它參數之前處理的參數,`callback`指處理參數之後執行的函數。`eagerness `是必要的,以便先前所需的參數不會產生錯誤消息。舉例來說,如果`--version`不是`eager parameter`並且參數`--foo`是需要先被定義的,你將需要去指定它讓`--version`可以工作,更多資訊可見[Callback Evaluation Order](https://click.palletsprojects.com/en/7.x/advanced/#callback-evaluation-order)
`callback`是調用了兩個參數的函數,當前的[上下文](https://click.palletsprojects.com/en/7.x/api/#click.Context)以及其值。上下文提供了有用的功能,如退出應用程式,並允許訪問其它已經處理的參數。
這是一個`--version`標記的範例:
```python
def print_version(ctx, param, value):
if not value or ctx.resilient_parsing:
return
click.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!')
```
`expose_value`預防將無意義的`version`參數傳遞給`callback function`,如果沒有指定,一個`boolean`會傳遞給`hello`。假如Click想要解析命令行而沒有任何可能改變執行流程的破壞性行為,則`resil_parsing`標誌將應用於上下文。這個範例中因為我們會退出程序,因此我們什麼都不做。
看起來像這樣:
```shell
$ hello
Hello World!
$ hello --version
Version 1.0
```
### Yes Parameters
對於危險的操作,能夠要求使用者確認是非常有幫助的。這可以透過加入`--yes`標記來完成並且要求確認,如果使用者沒有提供回饋,可以在`callback`中讓它失敗,如下範例:
```python
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!')
```
它在`Command Line`中看起來像這樣:
```shell
$ dropdb
Are you sure you want to drop the db? [y/N]: n
Aborted!
$ dropdb --yes
Dropped all tables!
```
因為這種參數組合是很常見的,因此可以使用裝飾器`confirmation_option`來替代:
```python
@click.command()
@click.confirmation_option(prompt='Are you sure you want to drop the db?')
def dropdb():
click.echo('Dropped all tables!')
```
### Values from Environment Variables
Click一個非常實用的功能,除了常規的參數之外還可以接受從環境變數來的參數。這可以讓工具更簡單的自動化。舉例來說,你也許想要利用`--config`參數來傳遞一個配置文件,也支援導出`TOOL_CONFIG = hello.cfg`以獲得更好的開發體驗。
在Click有兩種方式可以支援這種作法。一種是自動化建立僅支援`option`的環境變數。要開啟這個功能,參數`auto_envvar_prefix`需要被傳遞給調用的腳本。然後將每個命令和參數添加為大寫下劃線分隔變數。如果你有一個子命令命名為`foo`帶有一個`option bar`,它的前綴是`MY_TOOL`,那這變數為`MY_TOOL_FOO_BAR`。
範例如下:
```python
@click.command()
@click.option('--username')
def greet(username):
click.echo('Hello %s!' % username)
if __name__ == '__main__':
greet(auto_envvar_prefix='GREETER')
```
調用該Click的腳本傳遞了`auto_envvar_prefix='GREETE'`,它的`option`為`--username`,調用範例如下:
```shell
$ export GREETER_USERNAME=john
$ greet
Hello john!
```
定義了環境變數`GREETER_USERNAME=john`,接著使用該腳本之後取得環境變數
當`auto_envvar_prefix`與命令群組一起使用的時候,該命令名稱需要被包含在環境變數內,判於前綴與參數名稱,例如`PREFIX_COMMAND_VARIABLE`,範例如下:
```python
@click.group()
@click.option('--debug/--no-debug')
def cli(debug):
click.echo('Debug mode is %s' % ('on' if debug else 'off'))
@cli.command()
@click.option('--username')
def greet(username):
click.echo('Hello %s!' % username)
if __name__ == '__main__':
cli(auto_envvar_prefix='GREETER')
```
```shell
$ export GREETER_DEBUG=false
$ export GREETER_GREET_USERNAME=John
$ cli greet
Debug mode is off
Hello John!
```
第二個`option`是通過在選項上定義環境變量的名稱來手動從特定環境變量中提取值
範例如下:
```python
@click.command()
@click.option('--username', envvar='USERNAME')
def greet(username):
click.echo('Hello %s!' % username)
if __name__ == '__main__':
greet()
```
利用`envvar`指定環境變數
```shell
$ export USERNAME=john
$ greet
Hello john!
```
在這種情況下,它也可以是選擇第一個環境變量的不同環境變量的列表。
### Multiple Values from Environment Values
由於`option`可以接受多個值,因此從環境變數中取得這些值會稍微複雜一點。Click解決這問題的方式是將它保留到類型以自定義行為。對於值不是1的`multiple`與`nargs`,Click將會調用`ParamType.split_envvar_value()`來執行拆分。
預設是以空格做為分解索引,這規則的例外是檔案與路徑格式,它們都依據操作系統的路徑分解規則來進行拆解。在Unix系統上,如Linux, OS X會使用`:`,而Windows上則為`;`,範例如下:
```python
@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()
```
取得環境變數`PATHS`,設置`multiple=True`以及類型`type=click.Path()`
```shell
$ export PATHS=./foo/bar:./test
$ perform
./foo/bar
./test
```
執行該文件之後確實的拆解掉PATHS內的路徑
### Other Prefix Characters
Click可以處理除`-`之外的替代前綴字符。例如,如果要處理`/`作為參數或類似的東西,這將非常有用。注意,非常強烈的不建議這麼,因為Click希望開發人員可以貼近`POSIX`語意。然後在某些情況下這將非常有用:
```python
@click.command()
@click.option('+w/-w')
def chmod(w):
click.echo('writable=%s' % w)
if __name__ == '__main__':
chmod()
```
執行如下:
```shell
$ chmod +w
writable=True
$ chmod -w
writable=False
```
如果你使用`/`做為前綴字符並且希望使用boolean標記,你需要以`;`代替`/`來隔離。
```python
@click.command()
@click.option('/debug;/no-debug')
def log(debug):
click.echo('debug=%s' % debug)
if __name__ == '__main__':
log()
```
### Range Options
特別值得一提的是`IntRange`,它跟`INT`非常類似,但是限制數值在指定區間內(包含兩個邊界值),它有兩種模式:
* 預設為`non-cliamping`模式,意味著當數值超過區間的時候會拋出異常
* `cliamping`模式,意味著當數值超過邊界的時候會以邊界值為數值
* 區間為0-5,當數值為10的時候視為5,當值為-1的時候視為0
範例如下:
```python
@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()
```
執行如下:
```shell
$ repeat --count=1000 --digit=5
55555555555555555555
$ repeat --count=1000 --digit=12
Usage: repeat [OPTIONS]
Try "repeat --help" for help.
Error: Invalid value for "--digit": 12 is not in the valid range of 0 to 10.
```
如果你在區間設置的任一邊界設置為None,代表無限制。
### Callbacks for Validation
如果你要實現自定義驗證邏輯,你可以在參數callback中實作。如果驗證不起作用,這些callback既可以修改數值也可以拋出異常。
在Click1.0你只可以拋出`UsageError`異常,但在Click2.0中你還可以拋出`BadParameter`異常,這有額外的優點,它將自動格式化錯誤消息以包含參數名稱。
範例如下:
```python
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()
```
執行如下:
```shell
$ 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
`Arguments`的工作模型與`options`類似,但是有位置性的。由於它的語法特性問題,僅支持`options`部份的功能。Click也不會嘗試為你記錄參數,並希望你手動記錄它們以避免難看的幫助頁面。
### Basic Arguments
最基本的`option`是一個值簡單字符串參數。如果沒有提供類型,則使用預設值的類型,如果未提供預設值,則假定類型為`STRING`。
範例如下:
```python
@click.command()
@click.argument('filename')
def touch(filename):
click.echo(filename)
```
它看起來像這樣:
```shell
$ touch foo.txt
foo.txt
```
### Variadic Arguments
第二種常見的版本為可變參數,接受特定(或無限制)數量的參數。這可以利用參數`nargs`控制。如果設置為`-1`則代表無限制。
值以`tuple`格式傳遞。注意到,只能有一個`argument`設置`nargs=-1`,它將佔據所有的參數。範例如下:
```python
@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))
```
執行如下:
```shell
$ copy foo.txt bar.txt my_folder
move foo.txt to folder my_folder
move bar.txt to folder my_folder
```
注意