Образец кода и задействованные исторические статьи были обновлены доРепозиторий HelloGitHub-Team
Введение
В последних двух статьях мы представилиclick
«Параметры» и «опции» в этой статье будут продолжать пониматьclick
, ориентируясь на его «команды» и «группы».
本系列文章默认使用 Python 3 作为解释器进行讲解。
若你仍在使用 Python 2,请注意两者之间语法和库的使用差异哦~
2. Команды и группы
Click
Очень важной особенностью является концепция произвольно вложенных инструментов командной строки, черезCommandа такжеGroup(в реальностиMultiCommand)реализовать.
Так называемая группа команд представляет собой совокупность нескольких команд (или подкоманд), которую также называют многокомандной.
2.1 Обратный вызов
Для обычной команды обратный вызов происходит при выполнении команды. Если бы в реализации этой программы были только команды, коллбэк срабатывал бы всегда, как и все примеры, которые мы приводили в предыдущей статье. Но, как--help
Такие опции предотвращают вход в обратный вызов.
Ситуация немного отличается для групп и нескольких подкоманд. Обратные вызовы обычно возникают при выполнении подкоманд:
@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() # @cli, not @click!
def sync():
click.echo('Syncing')
Эффект от исполнения следующий:
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
Syncing
В приведенном выше примере мы поместили функциюcli
Определяемая как группа, функцияsync
Определяется как подкоманда в этой группе. когда мы звонимtool.py --debug sync
команда, она будет запущена по очередиcli
а такжеsync
Логика обработки (то есть обратный вызов команды).
2.2 Вложенная обработка и контексты
Как видно из приведенного выше примера, группа командcli
Полученные аргументы и подкомандыsync
независимы друг от друга. Но иногда мы хотим получить параметры группы команд в подкоманде, которую можно использоватьContextреализовать.
Всякий раз, когда вызывается команда,click
Новый контекст создается и связывается с родительским контекстом. Обычно мы не видим контекстной информации. но мы можем пройтиpass_contextдекоратор, чтобы явно позволитьclick
Передайте контекст, эта переменная передается в качестве первого параметра.
@click.group()
@click.option('--debug/--no-debug', default=False)
@click.pass_context
def cli(ctx, debug):
# 确保 ctx.obj 存在并且是个 dict。 (以防 `cli()` 指定 obj 为其他类型
ctx.ensure_object(dict)
ctx.obj['DEBUG'] = debug
@cli.command()
@click.pass_context
def sync(ctx):
click.echo('Debug is %s' % (ctx.obj['DEBUG'] and 'on' or 'off'))
if __name__ == '__main__':
cli(obj={})
В приведенном выше примере:
- через группу команд
cli
и подкомандыsync
Укажите декораторclick.pass_context
, первый параметр обеих функций равенctx
контекст - в командной группе
cli
, для контекстаobj
Присвоение переменной (словарю) - в подкоманде
sync
прошедшийctx.obj['DEBUG']
Получить параметры предыдущего шага - Передача параметров из группы команд в подкоманды осуществляется таким образом
2.3 Вызов групп команд без команд
По умолчанию группы команд вызываются при вызове подкоманд. А иногда вы можете захотеть вызвать группу команд напрямую, указавclick.group
изinvoke_without_command=True
реализовать:
@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')
Команды вызова:
$ tool
I was invoked without subcommand
$ tool sync
I am about to invoke sync
The subcommand
В приведенном выше примере поctx.invoked_subcommand
Чтобы определить, вызвана ли она подкомандой, распечатайте журнал для двух случаев.
2.4 Пользовательская группа команд/несколько команд
Помимо использованияclick.groupВ дополнение к определению групп команд вы также можете настраивать группы команд (также называемые несколькими командами), чтобы вы могли отложенно загружать подкоманды, что может быть полезно.
Пользовательская мультикоманда должна быть реализованаlist_commands
а такжеget_command
метод:
import click
import os
plugin_folder = os.path.join(os.path.dirname(__file__), 'commands')
class MyCLI(click.MultiCommand):
def list_commands(self, ctx):
rv = [] # 命令名称列表
for filename in os.listdir(plugin_folder):
if filename.endswith('.py'):
rv.append(filename[:-3])
rv.sort()
return rv
def get_command(self, ctx, name):
ns = {}
fn = os.path.join(plugin_folder, name + '.py') # 命令对应的 Python 文件
with open(fn) as f:
code = compile(f.read(), fn, 'exec')
eval(code, ns, ns)
return ns['cli']
cli = MyCLI(help='This tool\'s subcommands are loaded from a '
'plugin folder dynamically.')
# 等价方式是通过 click.command 装饰器,指定 cls=MyCLI
# @click.command(cls=MyCLI)
# def cli():
# pass
if __name__ == '__main__':
cli()
2.5 Объединение групп команд/несколько команд
Когда есть несколько групп команд с некоторыми командами в каждой группе команд, и вы хотите объединить все команды в один набор,click.CommandCollection
Это пригодится:
@click.group()
def cli1():
pass
@cli1.command()
def cmd1():
"""Command on cli1"""
@click.group()
def cli2():
pass
@cli2.command()
def cmd2():
"""Command on cli2"""
cli = click.CommandCollection(sources=[cli1, cli2])
if __name__ == '__main__':
cli()
Команды вызова:
$ cli --help
Usage: cli [OPTIONS] COMMAND [ARGS]...
Options:
--help Show this message and exit.
Commands:
cmd1 Command on cli1
cmd2 Command on cli2
Как видно из приведенного выше примера,cmd1
а такжеcmd2
принадлежитcli1
а такжеcli2
,пройти черезclick.CommandCollection
Эти подкоманды можно комбинировать, чтобы обеспечить их возможности в одной и той же командной программе.
Советы: Если одна и та же подкоманда определена в нескольких группах команд, выберите подкоманду в первой группе команд.
2.6 Связанная группа команд/несколько команд
Иногда одноуровневая подкоманда может не соответствовать вашим потребностям, и вам может даже понадобиться несколько уровней подкоманд. Как правило,setuptools
В пакете поддерживаются многоуровневые/связанные подкоманды:setup.py sdist bdist_wheel upload
. После клика 3.0 реализация связанных групп команд стала очень простой, простоclick.group
указано вchain=True
:
@click.group(chain=True)
def cli():
pass
@cli.command('sdist')
def sdist():
click.echo('sdist called')
@cli.command('bdist_wheel')
def bdist_wheel():
click.echo('bdist_wheel called')
Команда вызова:
$ setup.py sdist bdist_wheel
sdist called
bdist_wheel called
2.7 Группа команд/мультикомандный конвейер
Обычный сценарий в связанных группах команд заключается в реализации конвейера, чтобы после обработки предыдущей команды результат мог быть передан следующей команде для обработки.
Смысл реализации конвейера групп команд состоит в том, чтобы каждая команда возвращала функцию обработчика, а затем писалась общая диспетчерская функция конвейера (и запускаласьMultiCommand.resultcallback()
Украсить):
@click.group(chain=True, invoke_without_command=True)
@click.option('-i', '--input', type=click.File('r'))
def cli(input):
pass
@cli.resultcallback()
def process_pipeline(processors, input):
iterator = (x.rstrip('\r\n') for x in input)
for processor in processors:
iterator = processor(iterator)
for item in iterator:
click.echo(item)
@cli.command('uppercase')
def make_uppercase():
def processor(iterator):
for line in iterator:
yield line.upper()
return processor
@cli.command('lowercase')
def make_lowercase():
def processor(iterator):
for line in iterator:
yield line.lower()
return processor
@cli.command('strip')
def make_strip():
def processor(iterator):
for line in iterator:
yield line.strip()
return processor
В приведенном выше примере:
- будет
cli
Определяется как связанная группа команд, и укажите invoke_without_command=True, что означает, что группа команд может запускаться без передачи подкоманд. - Определены три функции обработки команд, соответствующие
uppercase
,lowercase
а такжеstrip
Заказ - Функция расписания в трубопроводе
process_pipeline
, войтиinput
Станьте генератором, а затем вызовите функцию обработки (на самом деле введите несколько команд, есть несколько функций обработки) для обработки
2.8 Переопределение значений по умолчанию
По умолчанию дефолтное значение параметра берется из параметра через декораторdefault
определение. Мы также можем пройтиContext.default_map
словарь контекста, чтобы переопределить значение по умолчанию:
@click.group()
def cli():
pass
@cli.command()
@click.option('--port', default=8000)
def runserver(port):
click.echo('Serving on http://127.0.0.1:%d/' % port)
if __name__ == '__main__':
cli(default_map={
'runserver': {
'port': 5000
}
})
В приведенном выше примере поcli
указано вdefault_map
Переменная может переопределить параметр команды (первичный ключ) (вторичный ключ) по умолчанию (значение вторичного ключа).
мы также можемclick.group
указано вcontext_settings
для достижения той же цели:
CONTEXT_SETTINGS = dict(
default_map={'runserver': {'port': 5000}}
)
@click.group(context_settings=CONTEXT_SETTINGS)
def cli():
pass
@cli.command()
@click.option('--port', default=8000)
def runserver(port):
click.echo('Serving on http://127.0.0.1:%d/' % port)
if __name__ == '__main__':
cli()
Команда вызова:
$ cli runserver
Serving on http://127.0.0.1:5000/
3. Резюме
В этой статье сначала рассматриваются вызов обратного вызова и контекст команд, а затем вводятся дополнительные сведения о настройке, слиянии, связывании, конвейере и других функциях групп команд.click
власти. И чем более продвинутые способности в командной группе(как возвращаемое значение команды), вы можете обратиться к официальной документации для получения дополнительной информации.
Мы представляемclick
Параметры, опции и команды смогли полностью реализовать все функции программы командной строки. а такжеclick
Это также дает нам много вишенки на торте, таких как утилиты, автозаполнение параметров и т. д., которые мы подробно опишем в следующем разделе.
«Объяснение серии проектов с открытым исходным кодом»——Пусть больше не боятся люди, интересующиеся проектами с открытым исходным кодом, и пусть инициаторы проектов с открытым исходным кодом больше не остаются в одиночестве. Следите за нашими статьями, и вы откроете для себя радость программирования, насколько легко им пользоваться, и узнаете, как легко участвовать в проектах с открытым исходным кодом. Добро пожаловать, чтобы оставить сообщение, чтобы связаться с нами, присоединиться к нам, позволить большему количеству людей влюбиться в открытый исходный код и внести свой вклад в открытый исходный код ~