Python click: создание CLI-приложений

12 октября 2026
Автор

Антон Ларичев

Что такое click и зачем он нужен

click — библиотека для создания интерфейсов командной строки (CLI) на Python. Она позволяет превратить обычную функцию в полноценную консольную команду с аргументами, опциями, подсказками и автодополнением — без написания парсеров вручную.

По сравнению со стандартным модулем argparse, click использует декораторы вместо конфигурации объектов, что делает код декларативным и читаемым. Flask, Poetry, AWS CLI и многие другие популярные инструменты построены именно на click.

Курс по теме

Основы Python — курс

40 000+ студентов · рейтинг 4.8 · гарантия возврата 30 дней

Установка

pip install click

Проверить установку:

python -c "import click; print(click.__version__)"

Первая команда

Минимальный пример CLI-команды:

import click

@click.command()
def hello():
    click.echo("Привет, мир!")

if __name__ == "__main__":
    hello()

Запуск:

python app.py
# Привет, мир!

python app.py --help
# Usage: app.py [OPTIONS]
# Options:
#   --help  Show this message and exit.

Декоратор @click.command() регистрирует функцию как CLI-команду. click.echo() используется вместо print() — он корректно обрабатывает кодировки и перенаправление вывода.

Опции (Options)

Опции — именованные параметры, которые передаются через --name value.

import click

@click.command()
@click.option("--name", default="мир", help="Имя для приветствия")
@click.option("--count", default=1, type=int, help="Количество повторений")
def greet(name, count):
    for _ in range(count):
        click.echo(f"Привет, {name}!")

if __name__ == "__main__":
    greet()
python app.py --name Антон --count 3
# Привет, Антон!
# Привет, Антон!
# Привет, Антон!

Обязательные опции

По умолчанию опции необязательны. Чтобы сделать опцию обязательной, используйте required=True:

@click.command()
@click.option("--email", required=True, help="Email пользователя")
def register(email):
    click.echo(f"Регистрация: {email}")

Флаги (boolean options)

Флаги — опции без значения, которые включают или выключают режим:

@click.command()
@click.option("--verbose", "-v", is_flag=True, help="Подробный вывод")
@click.option("--dry-run", is_flag=True, help="Симуляция без применения")
def deploy(verbose, dry_run):
    if verbose:
        click.echo("Режим подробного вывода включён")
    if dry_run:
        click.echo("[DRY RUN] Деплой пропущен")
    else:
        click.echo("Деплой выполнен")
python app.py --verbose --dry-run

Короткие псевдонимы

Можно задать несколько имён для одной опции:

@click.option("--output", "-o", help="Путь для сохранения результата")

Аргументы (Arguments)

Аргументы — позиционные параметры без имени, передаются напрямую:

import click

@click.command()
@click.argument("source")
@click.argument("destination")
def copy_file(source, destination):
    click.echo(f"Копирование {source} -> {destination}")

if __name__ == "__main__":
    copy_file()
python app.py file.txt backup/file.txt
# Копирование file.txt -> backup/file.txt

Отличие аргументов от опций: аргументы обязательны по умолчанию и не имеют -- префикса. Используйте аргументы для основных входных данных, опции — для настроек.

Множественные аргументы

@click.command()
@click.argument("files", nargs=-1)
def process(files):
    for f in files:
        click.echo(f"Обработка: {f}")
python app.py a.txt b.txt c.txt

Типы данных

click автоматически конвертирует и валидирует типы:

import click

@click.command()
@click.option("--port", type=int, default=8080)
@click.option("--ratio", type=float, default=0.5)
@click.option("--mode", type=click.Choice(["dev", "prod", "test"]))
@click.option("--config", type=click.Path(exists=True))
@click.option("--output", type=click.File("w"), default="-")
def server(port, ratio, mode, config, output):
    click.echo(f"Порт: {port}, режим: {mode}", file=output)
  • click.Choice — значение должно быть из списка
  • click.Path — путь к файлу или директории с валидацией
  • click.File — открывает файл автоматически ("-" означает stdin/stdout)
  • click.IntRange, click.FloatRange — числа в диапазоне
@click.option("--workers", type=click.IntRange(1, 32), default=4)

Группы команд

Группы позволяют создавать CLI с подкомандами, как у git или docker:

import click

@click.group()
def cli():
    pass

@cli.command()
@click.argument("name")
def create(name):
    click.echo(f"Создание проекта: {name}")

@cli.command()
@click.argument("name")
def delete(name):
    click.secho(f"Удаление проекта: {name}", fg="red")

@cli.command(name="list")
def list_projects():
    click.echo("Список проектов:")
    for p in ["alpha", "beta", "gamma"]:
        click.echo(f"  - {p}")

if __name__ == "__main__":
    cli()
python app.py --help
# Usage: app.py [OPTIONS] COMMAND [ARGS]...
# Commands:
#   create
#   delete
#   list

python app.py create my-project
python app.py list

Вложенные группы

@click.group()
def db():
    pass

@db.command()
def migrate():
    click.echo("Миграция БД")

@db.command()
def rollback():
    click.echo("Откат БД")

cli.add_command(db)
python app.py db migrate
python app.py db rollback

Контекст и передача данных между командами

Для передачи общего состояния между группой и подкомандами используется контекст:

import click

@click.group()
@click.option("--debug", is_flag=True)
@click.pass_context
def cli(ctx, debug):
    ctx.ensure_object(dict)
    ctx.obj["debug"] = debug

@cli.command()
@click.pass_context
def run(ctx):
    if ctx.obj["debug"]:
        click.echo("[DEBUG] Запуск в режиме отладки")
    click.echo("Приложение запущено")

if __name__ == "__main__":
    cli()
python app.py --debug run
# [DEBUG] Запуск в режиме отладки
# Приложение запущено

@click.pass_context передаёт объект контекста первым аргументом. ctx.obj — словарь для произвольных данных.

Интерактивный ввод

Промпты

@click.command()
@click.option("--name", prompt="Введите имя", help="Ваше имя")
@click.option("--password", prompt=True, hide_input=True, confirmation_prompt=True)
def signup(name, password):
    click.echo(f"Пользователь {name} зарегистрирован")

Если опция не передана через CLI, click запросит её интерактивно. hide_input=True скрывает ввод для паролей. confirmation_prompt=True просит ввести значение дважды.

Подтверждение действий

@click.command()
@click.argument("name")
def remove(name):
    click.confirm(f'Удалить "{name}"?', abort=True)
    click.echo(f"{name} удалён")
python app.py remove project-alpha
# Удалить "project-alpha"? [y/N]: n
# Aborted!

abort=True прерывает выполнение при отказе.

Функция click.prompt()

@click.command()
def setup():
    host = click.prompt("Хост БД", default="localhost")
    port = click.prompt("Порт", default=5432, type=int)
    click.echo(f"Подключение к {host}:{port}")

Цветной вывод и стилизация

@click.command()
def status():
    click.secho("OK", fg="green", bold=True)
    click.secho("ПРЕДУПРЕЖДЕНИЕ", fg="yellow")
    click.secho("ОШИБКА", fg="red", err=True)

    # Стилизация части строки
    click.echo("Статус: " + click.style("активен", fg="green"))

Параметры click.secho и click.style:

  • fg — цвет текста: black, red, green, yellow, blue, magenta, cyan, white
  • bg — цвет фона
  • bold, dim, underline, blink, reverse — форматирование
  • err=True — вывод в stderr

Прогресс-бар

import click
import time

@click.command()
@click.argument("count", type=int, default=10)
def process(count):
    items = range(count)
    with click.progressbar(items, label="Обработка") as bar:
        for item in bar:
            time.sleep(0.1)
    click.secho("Готово!", fg="green")
python app.py 20
# Обработка  [####################################]  100%
# Готово!

Пагинация длинного вывода

@click.command()
def show_logs():
    logs = "\n".join([f"Строка {i}" for i in range(1, 201)])
    click.echo_via_pager(logs)

Вывод откроется в пейджере (less/more), что удобно для длинных отчётов.

Практический пример: файловый менеджер

Соберём полноценный CLI-инструмент для работы с файлами:

import click
import os
import shutil
from pathlib import Path

@click.group()
@click.version_option(version="1.0.0")
def cli():
    """Файловый менеджер — утилита для работы с файлами."""
    pass

@cli.command()
@click.argument("directory", default=".", type=click.Path(exists=True))
@click.option("--ext", help="Фильтр по расширению (например: .py)")
@click.option("--sort", type=click.Choice(["name", "size", "date"]), default="name")
def ls(directory, ext, sort):
    """Список файлов в директории."""
    path = Path(directory)
    files = list(path.iterdir())

    if ext:
        files = [f for f in files if f.suffix == ext]

    if sort == "size":
        files.sort(key=lambda f: f.stat().st_size if f.is_file() else 0)
    elif sort == "date":
        files.sort(key=lambda f: f.stat().st_mtime)
    else:
        files.sort(key=lambda f: f.name)

    for f in files:
        if f.is_dir():
            click.secho(f"[DIR]  {f.name}", fg="blue")
        else:
            size = f.stat().st_size
            click.echo(f"[FILE] {f.name} ({size} байт)")

@cli.command()
@click.argument("source", type=click.Path(exists=True))
@click.argument("destination")
@click.option("--overwrite", is_flag=True, help="Перезаписать существующий файл")
def cp(source, destination, overwrite):
    """Копирование файла."""
    dest_path = Path(destination)
    if dest_path.exists() and not overwrite:
        click.confirm(f'"{destination}" уже существует. Перезаписать?', abort=True)
    shutil.copy2(source, destination)
    click.secho(f"Скопировано: {source} -> {destination}", fg="green")

@cli.command()
@click.argument("path", type=click.Path(exists=True))
@click.option("--force", "-f", is_flag=True, help="Без подтверждения")
def rm(path, force):
    """Удаление файла или директории."""
    if not force:
        click.confirm(f'Удалить "{path}"?', abort=True)
    p = Path(path)
    if p.is_dir():
        shutil.rmtree(path)
    else:
        p.unlink()
    click.secho(f"Удалено: {path}", fg="red")

@cli.command()
@click.argument("path")
def mkdir(path):
    """Создание директории."""
    Path(path).mkdir(parents=True, exist_ok=True)
    click.secho(f"Директория создана: {path}", fg="green")

if __name__ == "__main__":
    cli()

Пример использования:

python fm.py --help
python fm.py ls --sort size --ext .py
python fm.py cp src.txt dst.txt --overwrite
python fm.py mkdir projects/new-app
python fm.py rm old-file.txt --force

Тестирование CLI-команд

click предоставляет CliRunner для юнит-тестов без запуска процессов:

from click.testing import CliRunner
from app import greet

def test_greet_default():
    runner = CliRunner()
    result = runner.invoke(greet)
    assert result.exit_code == 0
    assert "Привет, мир!" in result.output

def test_greet_with_name():
    runner = CliRunner()
    result = runner.invoke(greet, ["--name", "Антон", "--count", "2"])
    assert result.exit_code == 0
    assert result.output.count("Привет, Антон!") == 2

def test_greet_invalid_count():
    runner = CliRunner()
    result = runner.invoke(greet, ["--count", "abc"])
    assert result.exit_code != 0

Установка как пакета

Чтобы команда запускалась глобально без python app.py, настройте pyproject.toml:

[project.scripts]
fm = "fm:cli"

Или setup.py:

setup(
    entry_points={
        "console_scripts": [
            "fm=fm:cli",
        ],
    },
)

После pip install -e . команда fm доступна в системе:

fm ls --sort size
fm mkdir new-dir

Итоги

click покрывает весь типичный спектр задач CLI-разработки:

  • Простые команды через @click.command()
  • Опции (@click.option) и аргументы (@click.argument) с типизацией
  • Группы подкоманд через @click.group()
  • Интерактивные промпты и подтверждения
  • Цветной вывод, прогресс-бары, пагинация
  • Встроенный --help и --version
  • Тестирование через CliRunner

Для углублённого изучения Python и написания профессионального кода — курс на PurpleSchool: https://purpleschool.ru/course/python?utm_source=knowledgebase&utm_medium=text&utm_campaign=click-cli-applications

Стрелочка влевоPython itertools — работа с итераторамиПарсинг сайтов с BeautifulSoup в PythonСтрелочка вправо

Постройте личный план изучения Python до уровня Middle — бесплатно!

Python — часть карты развития Backend

  • step100+ шагов развития
  • lessons30 бесплатных лекций
  • lessons300 бонусных рублей на счет

Все гайды по Python

Как отправлять запросы с помощью requests в PythonПочему Python выводит значение без команды printКак работает команда print в PythonВозможности Python для автоматизации задачРабота с JSON в Python на примерахPython get — методы получения данныхКак находить и исправлять ошибки в PythonСтруктура и оформление кода PythonОсновы Django с PythonРабота с данными через API и внешние сервисыПолезные приёмы в Python для повседневной работыИспользование locals в Python для отладкиИнтеграция PHP и PythonКак выполнять HTTPS-запросы в PythonFastAPI Python — быстрый старт: создание REST API с нуляКак работать с API в Python
Ввод целого числа в PythonВедение логов в PythonУдаление данных в Python с помощью removeОбработка исключений с помощью try/except в PythonФункция super() в Python — как вызвать метод родителяСоздание собственных контекстных менеджеров в PythonРабота с символами программирования PythonРабота с переменной X в PythonРабота с классами в PythonКак скачать Python на компьютерПростая программа на Python для начинающихОсновы Python для тех, кто начинаетЧто нового в Python 3Поддерживается ли Python 2 и стоит ли его использоватьКоманда python print - полное руководство по выводу данныхPython 1 — с чего начиналась история языкаПравила именования переменных в PythonПользовательские исключения в PythonОсновы Python coreОписание объектов PythonНаследование классов в Python — основы и примерыМножественное наследование в Python — примеры и MROКонтекстный менеджер with в Python — как работает и зачем нуженКомментарии в Python — однострочные, многострочные и docstringКакой Python выбрать для установкиКак вывести целое число с помощью print в PythonКак установить Python на Windows macOS и LinuxКак пользоваться консолью PythonКак получить последний элемент в PythonКак найти значение в PythonКак настроить PythonКак использовать print для строк в PythonКак работает интерпретатор PythonИнструкция по работе с PythonБлок finally в обработке исключений PythonЦелые числа в PythonАбстрактные классы в Python — ABC и abstractmethod
uv — современный менеджер пакетов PythonPython subprocess: запуск внешних команд и процессовSQLModel ORM для FastAPIPython rich: красивый вывод в терминалеВеб-скрапинг с Scrapy в PythonPydantic: валидация данных в PythonPoetry: управление зависимостями в PythonPython pathlib — современная работа с файловыми путямиPython itertools — работа с итераторамиPython click: создание CLI-приложенийПарсинг сайтов с BeautifulSoup в PythonPython argparse: разбор аргументов командной строкиAlembic для миграций базы данных с SQLAlchemy
Загрузка данных PythonУправление проектами на GitHub с PythonСоздание веб-приложений на Flask PythonСоздание бота на PythonСоздание интерфейсов Python QTСоздание игр с PygameСоздание GUI в PythonКак работать со словарями в PythonРабота с библиотеками через Python PackagingРабота со временем в Python при помощи модуля timePython name — особенности переменнойМатематические операции в Python с модулем mathPython listing — что это и как использоватьPytest — тестирование на Python: полное руководствоОбработка изображений с OpenCV PythonNumPy в Python — основы и применение в задачахМашинное обучение с PythonИспользование Anaconda с PythonМодуль contextlib в Python — утилиты для контекстных менеджеровБиблиотеки Python и их применение в проектах
Возврат значений из функции в PythonВложенные функции в PythonСоздание собственных декораторов в PythonРабота с функцией map в PythonЦикл while в Python и примеры использованияОсновные операторы в Python с примерамиУсловные выражения if else в Python для начинающихКак выполняется вызов функций call в PythonПродвинутые генераторы в Python — send, throw, close и корутиныПозиционные и именованные аргументы в PythonОбработка чисел, введённых через input в PythonОбъявление переменных и управление областью видимости в PythonПередача аргументов по ссылке и по значению в PythonПередача аргументов через args и kwargs в PythonОсновные методы Python и примеры их использованияОператор match/case в Python 3.10+ — основы структурного сопоставленияПаттерны match/case в Python — деструктуризация, guard и вложенные шаблоныПрактические примеры match/case в Python — реальные сценарии примененияЛокальные и глобальные переменные в PythonЧасто используемые команды PythonКлючевые слова global и nonlocal в PythonКак создавать функции в PythonКак работает сборщик мусора в PythonКак работает область видимости переменных в PythonКак работает функция callable в PythonКак работает функция any и all в PythonКак проверить тип переменной в PythonКак передать функцию как аргумент в PythonКак использовать функцию isinstance в PythonКак использовать функцию filter в PythonКак использовать функцию filter в PythonКак использовать функцию eval безопасно в PythonКак использовать декораторы в PythonИзменяемые и неизменяемые типы данных в PythonГенераторы и yield в Python — как создавать и использоватьГенераторные выражения в Python — синтаксис и примерыФункции в Python и способы их вызоваФункции как объекты в PythonЧто такое замыкания в PythonЧто делает функция reduce в PythonЧто делает функция id в PythonАргументы по умолчанию в PythonАнонимные функции и lambda в PythonАлгоритмы на Python — примеры и объяснение
Запись данных в PythonУстановка pip в PythonУправление зависимостями requirement в PythonУправление библиотеками с помощью Python PackagingУдаление пробелов с помощью strip в PythonСтруктурирование кода в PythonСоздание исполняемого файла Python в exeРазбор traceback в модуле PythonРазбор site-packages в PythonРазбор Program Files в PythonРабота с Unicode кодировками в PythonРабота с системными функциями Python sysРабота с папкой AppData в PythonРабота с модулем logging в PythonРабота с каталогами в PythonРабота с CSV в PythonВиртуальная среда venv в Python — создание и настройкаКак создать простое приложение на PythonИспользование pip в Python для установки пакетовМодули в Python и организация кода в проектеРабота с файлами в Python пошаговоИмпорт модулей в Python и правила подключенияЧто делает компилятор Python и как он работаетПолучение строки из модуля PythonПодключение файлов в Python с includeПеременные среды в PythonСборка проекта с помощью packaging в PythonНастройка Python сервераИспользование Python на UbuntuИспользование консоли PythonИспользование кодировок в PythonИнициализация пакетов PythonИмпорт модулей PythonИмпорт имен в PythonСреда IDLE Python и базовые возможностиЧтение и запись TXT в PythonЧтение файлов в Python с помощью open file
Удаление элементов из списка PythonТипы данных в Python — обзор и рекомендацииОсновные операции со строками в PythonМетоды str в Python и обработка текстаСписки в Python и их ключевые методыСоздание списков данных в PythonРабота со строками и символами в PythonРабота со столбцами в PythonРабота со списком значений в PythonРабота с таблицами в Python с помощью DataFrameРабота с RFR в PythonРабота с пробелами в PythonРабота с массивами в PythonРабота с кортежами tuple PythonРабота с координатами X и Y в PythonРабота с ключами в PythonРабота с элементами данных PythonРабота с двоичными числами PythonРабота с данными в PythonРабота с данными NumPy PythonРабота с битами в PythonРабота с большими числами в PythonЧто такое значение в Python и как его определитьРабота с байтами в PythonМножества в Python и операции с нимиПроверка на четность в PythonИспользование range в Python для цикловПроверка числа в PythonПреобразование типов в PythonПреобразование списка в строку PythonПреобразование числа в строку в PythonПостроение графиков в PythonОпределение индекса элемента в PythonОкругление чисел в PythonОбъединение списков в Python с помощью zipМножества в PythonМассивы в Python и отличие от списковМассив чисел в PythonКортежи данных в PythonКак вычислить сумму чисел в PythonКак найти следующее число в PythonКак получить остаток от деления в PythonИспользование Unicode в PythonТип int в Python и его особенностиИндекс списка в PythonФункции для работы со строками в PythonЭлементы Python и способы доступа к нимДоступ к элементам массива в PythonДеление чисел в PythonРабота с данными в Python на практикеКак работать с числами в Python
Телеграм-бот на Python: от команды до деплояRedis в Python с asyncioMotor: асинхронная работа с MongoDB в PythonPython multiprocessing: параллельные процессыPython httpx: современный HTTP-клиентFastAPI middleware и обработка запросовFastAPI WebSockets в реальном времениFastAPI с JWT аутентификациейconcurrent.futures: параллельные задачи в PythonФоновые задачи с Celery в PythonАсинхронная работа с PostgreSQL в Python через asyncpgPython asyncio.TaskGroupМодуль asyncio в Python — основы асинхронного программированияСинтаксис async/await в Python — как писать асинхронный кодБиблиотека aiohttp в Python — асинхронные HTTP-запросы
Открыть базу знаний

Лучшие курсы по теме

Иконка молнииНовый
изображение курса

Основы Python

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.8
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Nest.js с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.6
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Docker и Ansible

Антон Ларичев
AI-тренажерыAI-тренажеры
Гарантия
Бонусы
иконка звёздочки рейтинга4.7
3 999 ₽ 6 990 ₽
Подробнее

Отправить комментарий