Knihovna click
slouží k vytváření rozhraní pro příkazovou řádku
(angl. command line interface, CLI).
Primárně to je zpracování argumentů, ale click umí zjednodušit i výstup.
Click je dobré používat s knihovnou colorama
, která se stará o obarvování
textu na příkazové řádce ve Windows (a na Unixu nedělá nic).
Nainstalujte si tedy obě:
$ python -m pip install click colorama
Nástroje na zpracování argumentů z CLI jsou i přímo ve standardní knihovně,
a dokonce jich není málo: os.environ, argparse, optparse, getopt.
S knihovnou click
je ale práce mnohem příjemnější a výsledky většinou
lépe odpovídají zavedeným konvencím.
Cena za jednoduchost a konzistenci je, že některé styly návrhu CLI click nepodporuje. Máte-li existující rozhraní, které chcete jen převést do Pythonu, click nejspíš nebude nejlepší volba.
Takto jednoduše se dá vytvořit aplikace s přepínači:
import click
@click.command()
@click.option('--count', default=1, metavar='COUNT',
help='Number of greetings.')
@click.option('--name', prompt='Your name', metavar='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(f'Hello {name}!')
if __name__ == '__main__':
hello()
Vyzkoušejte si ji! Máte-li ji uloženou jako hello.py
, zkuste:
$ python hello.py
$ python hello.py --help
$ python hello.py --name Pythonista
$ python hello.py --count 5
Funkce s dekorátorem @click.command
je příkaz – když ji zavoláte,
click zpracuje argumenty příkazové řádky a zavolá původní funkci
s příslušnými pythonními hodnotami.
Proto se dává do bloku if __name__ == '__main__':
, který se spustí, jen
když se pythonní soubor spoustí „přímo“.
Když je soubor importován, tenhle blok se neprovede.
Dekorátory @click.option
a @click.argument
pak přidávají přepínače
a argumenty.
Přepínače (angl. options), přidávané pomocí option
, jsou nepovinné
parametry, kterými se nějak obměňuje chování programu.
Pokud uživatel nějaký přepínač nezadá, použije se hodnota zadaná jako default
(nebo None
, když default
chybí).
Podle default
se řídí i typ argumentu, není-li dán explicitně.
Takže count
v příkladu výše je celé číslo.
Jména přepínačů začínají, podle Unixové konvence, pomlčkami: jednou pomlčkou pro jednopísmenné zkratky, dvěma pomlčkami pro vícepísmenná jména. Jeden přepínač může mít i víc jmen.
Speciální případ jsou booleovské přepínače, které mají jedno jméno
pro True
a jiné pro False
. Lze samozřejmě také vytvořit bezhodnotový
přepínač pomocí is_flag
.
import click
@click.command()
@click.option('-n', '--name', default='world',
help='Name of the person to greet')
@click.option('-c/-C', '--color/--no-color',
help='Make the output colorful')
@click.option('-v', '--verbose', is_flag=True,
help='More verbose output')
def hello(name, color, verbose):
if color:
name = click.style(name, fg='blue')
click.echo(f'Hello {name}!')
if verbose:
click.echo('Nice to meet you.')
if __name__ == '__main__':
hello()
$ python hello.py
Hello world!
$ python hello.py --name Guido
Hello Guido!
$ python hello.py --name Jane -v
Hello Jane!
Nice to meet you.
$ python hello.py -n 'Mr. Git'
Hello Mr. Git!
$ python hello.py --help
Usage: hello.py [OPTIONS]
Options:
-n, --name TEXT Name of the person to greet
-c, --color / -C, --no-color Make the output colorful
--help Show this message and exit.
Přepínač --help
přidává click sám.
Kromě přepínačů podporuje click i argumenty. Přepínače musí uživatel na řádce pojmenovat; argumenty se zadávají beze jména, ale záleží u nich na pořadí. Používají se ve dvou případech: pro povinné parametry a pro parametry, kterých může být zadán libovolný počet. Na všechno ostatní radši použijte přepínače.
Například příkaz cd
potřebuje jeden argument: jméno adresáře,
do kterého má přepnout.
Jeho rozhraní by v clicku vypadalo takto:
@click.command()
@click.argument('directory')
def cd(directory):
"""Change the current directory"""
click.echo(f'Changing to directory {directory}')
Proměnný počet argumentů se zadává pomocí nargs=-1
(0 nebo víc argumentů)
nebo nargs=-1, required=True
(1 nebo víc).
Například příkaz mv
bere N souborů a adresář, kam je přesune.
Takové rozhraní by v clicku vypadalo následovně:
@click.command()
@click.argument('source', nargs=-1, required=True)
@click.argument('destination')
def mv(source, destination):
"""Move any number of files to one destination"""
for filename in source:
click.echo(f'Moving {filename} to {destination}')
Má-li uživatel zadat jméno souboru, nepoužívejte řetězce, ale speciální typ
click.File()
.
Click za vás soubor automaticky otevře a zavře.
Kromě toho podporuje unixovskou konvenci, že -
znamená standardní
vstup/výstup.
Argument pro File
je mód, ve kterém se soubor otevírá, podobně jako pro
funkci open
:
'r'
pro čtení, 'w'
pro zápis.
@click.command()
@click.argument('files', nargs=-1, type=click.File('r'))
def cat(files):
"""Print out the contents of the given files"""
for file in files:
print(file.read(), end='')
Existuje i varianta click.Path()
,
která soubor neotvírá. Pomocí ní jde např. zadat jméno adresáře. Click takto
poskytuje i jiné další typy.
Vstupy získané z přepínačů i argumentů lze ověřit pomocí
vlastních podmínek a podle toho naprogramovat chování včetně
chybových hlášek. Click však opět nabízí pohodlnější způsob,
a to pomocí callback
.
V rámci callback funkce můžete ověřit libovolně hodnotu a/nebo
ji vhodně transformovat. Pokud hodnota neodpovídá požadavkům,
můžete použít vyjímku click.UsageError
nebo click.BadParameter
(vztahuje-li se přímo ke konkrétnímu parametru). Click se pak
sám postará o případné ukončení programu s odpovídající chybovou
hláškou a kódem.
def validate_username(ctx, param, value):
if 2 <= len(value) <= 8 and re.match('^[a-zA-Z]+[0-9]*$', value):
return value.lower()
else:
raise click.BadParameter('not valid CTU username')
@click.command()
@click.option('-u', '--username', callback=validate_username)
def email(username):
click.echo(f'{username}@fit.cvut.cz')
if __name__ == '__main__':
email()
Click má dobrou podporu pro podpříkazy známé z verzovacích systémů jako git:
příkaz git
sám o sobě nedělá nic, jen sdružuje podpříkazy jako git add
a git commit
.
Umí-li váš program více akcí, souhrnný příkaz označte @click.group()
a jednotlivé podpříkazy pak přidávejte pomocí command()
:
@click.group()
def git2():
pass
@git2.command()
def commit():
message = click.edit('Made some changes')
click.echo(f'Making commit with message: {message}')
@git2.command()
@click.argument('files', nargs=-1)
def add(files):
for file in files:
click.echo(f'Adding {file}')
Tahle lekce není popis všeho, co click umí – je to jen ochutnávka, abyste věděli, co od téhle knihovny očekávat.
Click má velice dobrou dokumentaci, ve které najdete detaily i všechny ostatní možnosti.