Комментарии 9
Мне не удалось найти хорошее руководство по argparse для начинающих, поэтому я и решил написать такое руководство сам.
документация к модулю является отличнейшим руководством, со всеми необходимыми примерами.
Занимаюсь ETL и анализом данных, разработчик. Сначала активно использовали argparse, потом мигрировали на Flask-Script. Сейчас комбинируем его с click.
Преимущества миграции на Flask-Script были:
+ меньше кода при создании команд;
+ возможность создавать вложенные команды, это позволяло разбивать их на подклассы. Удобно, когда команд становится более десятка;
+ нормальная документация;
— пришлось написать небольшую обвязку, чтобы все заработало. Кому-то это может показаться трудной задачей;
— не является стандартной библиотекой;
Я бы порекомендовал приглядеться к click. Это отличная замена argparse.
По опыту использования:
+ очень прост и удобен;
+ минимум кода;
+ куча классных встроенных возможностей: диалоги, группировки и др.
+ хорошая документация;
— не является стандартной библиотекой;
Из тяжеловесных инструментов для создания CLI-приложения порекомендовал бы взглянуть на Cement Framework. Сам не пользовался, но отзывы от знакомых были положительные.
import click
@click.command()
@click.option(
"--count",
default=1,
type=click.IntRange(min=1, max=100),
help="Number of files to generate.",
)
@click.option(
"--output",
type=click.Path(exists=True, file_okay=False, writable=True),
help="Output directory script will write files to.",
)
def file_generator(count, output):
for i in range(count):
with open(f"{i}.txt", "w") as f:
f.write(f"Dummy data {i}")
if __name__ == "__main__":
file_generator()
Вот пользуюсь и не могу нарадоваться, от скольких же рутинных проверок этот инструмент меня избавил.
P. S. Да, во время генерации самих файлов проверки все еще нужны, но это уже другая история. :)
P. P. S. Форматировался код при помощи black
Можете подсказать?
Судя по документации, можно сделать либо с ограниченным количеством аргументов (в данном случае — 3 аргумента; определит из длины кортежа type
):
import click
@click.command()
@click.option('--userdata', type=(str, str, int), help='User data represented as "FirstName LastName Age"')
def greet(userdata):
print('Hello, user {} {}. You are {} y.o.'.format(*userdata))
if __name__ == '__main__':
greet()
Что можно использовать как:
python app.py --userdata Vasiliy Petrenko 42
Если нужно передавать несколько одинаковых параметров — можно делать так:
import click
@click.command()
@click.option('--name', type=str, help='A name to greet', multiple=True)
def greet(name):
for n in name:
print(f'Hello, {n}')
if __name__ == '__main__':
greet()
И использовать как:
python app.py --name Vasya --name Petya
Указывать неограниченное количество аргументов для Option (например, python app.py --names Vasya Petya Tolya
) — нельзя.
А без костылей способа, чтобы было как в argparse и красиво, не находил.
1) В дополнение к вариантам выше:
import click
@click.command()
@click.argument('numbers', nargs=-1)
@click.option('--word')
def fancy_command(numbers, word):
result = sum(int(s) for s in numbers)
print('Numbers sum: {}. Word: {}'.format(result, word))
if __name__ == '__main__':
fancy_command()
Получим:
$ python script.py 1 2 3 4 5 --word Hello
Numbers sum: 15. Word: Hello
2) Ну и костыльный способ:
import click
@click.command()
@click.option('--numbers', nargs=0)
@click.argument('numbers', nargs=-1)
def fancy_command(numbers):
result = sum(int(s) for s in numbers)
print('Numbers sum: {}'.format(result))
if __name__ == '__main__':
fancy_command()
Получим:
$ python script.py --numbers 1 2 3
Numbers sum: 6
import os
import argparse
def validate_path(path: str) -> str:
if not os.path.isabs(path):
raise argparse.ArgumentTypeError(f'Absolute path required, got "{path}"')
return os.path.normpath(path)
parser.add_argument('--path', metavar='PATH', type=validate_path)
2) Если хотите чтобы параметры по умолчанию отображались в помощи, можно использовать такой ArgumentParser
import argparse
import typing
class RawTextArgumentDefaultsHelpFormatter(argparse.RawTextHelpFormatter):
'Adds default arguments to parser help output'
def _get_help_string(self, action: argparse.Action) -> str:
help_str = action.help or ''
if 'default: ' in help_str or action.default is argparse.SUPPRESS or action.default is None or action.default is False:
return help_str
if not action.option_strings and action.nargs not in [argparse.OPTIONAL, argparse.ZERO_OR_MORE]:
return help_str
return f'{help_str} (default: {action.default})'
class ArgumentParser(argparse.ArgumentParser):
'`argparse.ArgumentParser` that shows default values in help message.'
def __init__(self, **kwargs: typing.Any) -> None:
kwargs['formatter_class'] = RawTextArgumentDefaultsHelpFormatter
super(ArgumentParser, self).__init__(**kwargs)
Питон для самых маленьких?
Документация и читается и пробуется за 30 минут. Зачем нужны такие "гайды"-обрезки — не понятно. К тому же и они уже были:
https://m.habr.com/ru/post/144416/
https://jenyay.net/Programming/Argparse
https://rtfm.co.ua/python-modul-argparse-opcii-komandnoj-stroki-v-primerax/
Впрочем что я требую от ruvds? У них же никогда не было годных статей...
Изучаем Python: модуль argparse