Criando CLI com Python — 2025 | Python Brasil

Aprenda a criar ferramentas de linha de comando com Python usando argparse e Click. Exemplos praticos com subcomandos, opcoes e formatacao.

5 min de leitura Equipe Python Brasil

Ferramentas de linha de comando sao parte essencial do dia a dia de qualquer desenvolvedor. Python oferece otimas opcoes para criar CLIs profissionais, desde o argparse da biblioteca padrao ate o Click, uma biblioteca externa muito popular. Neste artigo, a gente vai explorar as duas abordagens com exemplos praticos.

Por Que Criar CLIs com Python?

CLIs sao ideais para automacao, scripts de deploy, ferramentas de desenvolvimento e processamento de dados. Python e perfeito para isso porque tem excelente suporte a manipulacao de strings, acesso ao sistema operacional e uma vasta colecao de bibliotecas.

Usando argparse (Biblioteca Padrao)

O argparse ja vem incluso no Python e e a forma mais basica de criar CLIs:

#!/usr/bin/env python3
"""Ferramenta CLI para gerenciar tarefas."""

import argparse
import json
from pathlib import Path
from datetime import datetime


ARQUIVO_TAREFAS = Path.home() / ".tarefas.json"


def carregar_tarefas():
    """Carrega tarefas do arquivo JSON."""
    if ARQUIVO_TAREFAS.exists():
        return json.loads(ARQUIVO_TAREFAS.read_text())
    return []


def salvar_tarefas(tarefas):
    """Salva tarefas no arquivo JSON."""
    ARQUIVO_TAREFAS.write_text(json.dumps(tarefas, indent=2, ensure_ascii=False))


def adicionar(args):
    """Adiciona uma nova tarefa."""
    tarefas = carregar_tarefas()
    tarefa = {
        "id": len(tarefas) + 1,
        "titulo": args.titulo,
        "prioridade": args.prioridade,
        "concluida": False,
        "criada_em": datetime.now().isoformat()
    }
    tarefas.append(tarefa)
    salvar_tarefas(tarefas)
    print(f"Tarefa #{tarefa['id']} adicionada: {args.titulo}")


def listar(args):
    """Lista todas as tarefas."""
    tarefas = carregar_tarefas()
    if not tarefas:
        print("Nenhuma tarefa encontrada.")
        return

    for t in tarefas:
        status = "concluida" if t["concluida"] else "pendente"
        print(f"  [{t['id']}] {t['titulo']} ({t['prioridade']}) - {status}")


def concluir(args):
    """Marca uma tarefa como concluida."""
    tarefas = carregar_tarefas()
    for t in tarefas:
        if t["id"] == args.id:
            t["concluida"] = True
            salvar_tarefas(tarefas)
            print(f"Tarefa #{args.id} concluida!")
            return
    print(f"Tarefa #{args.id} nao encontrada.")


def main():
    parser = argparse.ArgumentParser(
        description="Gerenciador de tarefas via terminal"
    )
    subparsers = parser.add_subparsers(dest="comando", help="Comandos disponiveis")

    # Subcomando: adicionar
    parser_add = subparsers.add_parser("adicionar", help="Adiciona uma tarefa")
    parser_add.add_argument("titulo", help="Titulo da tarefa")
    parser_add.add_argument(
        "-p", "--prioridade",
        choices=["baixa", "media", "alta"],
        default="media",
        help="Prioridade da tarefa (padrao: media)"
    )
    parser_add.set_defaults(func=adicionar)

    # Subcomando: listar
    parser_list = subparsers.add_parser("listar", help="Lista todas as tarefas")
    parser_list.set_defaults(func=listar)

    # Subcomando: concluir
    parser_done = subparsers.add_parser("concluir", help="Conclui uma tarefa")
    parser_done.add_argument("id", type=int, help="ID da tarefa")
    parser_done.set_defaults(func=concluir)

    args = parser.parse_args()
    if hasattr(args, "func"):
        args.func(args)
    else:
        parser.print_help()


if __name__ == "__main__":
    main()

Usando no terminal:

python tarefas.py adicionar "Estudar Python" -p alta
python tarefas.py listar
python tarefas.py concluir 1

Usando Click (Biblioteca Externa)

O Click simplifica muito a criacao de CLIs com decoradores intuitivos:

pip install click

Vamos recriar o gerenciador de tarefas com Click:

#!/usr/bin/env python3
"""Gerenciador de tarefas com Click."""

import click
import json
from pathlib import Path
from datetime import datetime


ARQUIVO = Path.home() / ".tarefas_click.json"


def carregar():
    if ARQUIVO.exists():
        return json.loads(ARQUIVO.read_text())
    return []


def salvar(tarefas):
    ARQUIVO.write_text(json.dumps(tarefas, indent=2, ensure_ascii=False))


@click.group()
@click.version_option(version="1.0.0")
def cli():
    """Gerenciador de tarefas via terminal."""
    pass


@cli.command()
@click.argument("titulo")
@click.option(
    "-p", "--prioridade",
    type=click.Choice(["baixa", "media", "alta"]),
    default="media",
    help="Prioridade da tarefa."
)
def adicionar(titulo, prioridade):
    """Adiciona uma nova tarefa."""
    tarefas = carregar()
    tarefa = {
        "id": len(tarefas) + 1,
        "titulo": titulo,
        "prioridade": prioridade,
        "concluida": False,
        "criada_em": datetime.now().isoformat()
    }
    tarefas.append(tarefa)
    salvar(tarefas)
    click.echo(click.style(f"Tarefa #{tarefa['id']} adicionada!", fg="green"))


@cli.command()
@click.option("--todas", is_flag=True, help="Mostra tarefas concluidas tambem.")
def listar(todas):
    """Lista tarefas pendentes."""
    tarefas = carregar()
    if not tarefas:
        click.echo("Nenhuma tarefa encontrada.")
        return

    for t in tarefas:
        if not todas and t["concluida"]:
            continue
        cor = "green" if t["concluida"] else "yellow"
        status = "OK" if t["concluida"] else "pendente"
        click.echo(
            f"  [{t['id']}] "
            + click.style(f"{t['titulo']}", fg=cor)
            + f" ({t['prioridade']}) - {status}"
        )


@cli.command()
@click.argument("tarefa_id", type=int)
def concluir(tarefa_id):
    """Marca uma tarefa como concluida."""
    tarefas = carregar()
    for t in tarefas:
        if t["id"] == tarefa_id:
            t["concluida"] = True
            salvar(tarefas)
            click.echo(click.style(f"Tarefa #{tarefa_id} concluida!", fg="green"))
            return
    click.echo(click.style(f"Tarefa #{tarefa_id} nao encontrada.", fg="red"))


@cli.command()
@click.argument("tarefa_id", type=int)
@click.confirmation_option(prompt="Tem certeza que deseja remover?")
def remover(tarefa_id):
    """Remove uma tarefa permanentemente."""
    tarefas = carregar()
    tarefas = [t for t in tarefas if t["id"] != tarefa_id]
    salvar(tarefas)
    click.echo(f"Tarefa #{tarefa_id} removida.")


if __name__ == "__main__":
    cli()

Click vs argparse: Comparacao

A principal diferenca entre as duas abordagens esta na ergonomia. O argparse exige construcao manual dos parsers e subparsers, enquanto o Click usa decoradores declarativos. Para CLIs simples, argparse resolve bem. Para projetos maiores, Click oferece vantagens como grupos de comandos aninhados, validacao automatica e coloracao no terminal.

Outro ponto forte do Click e a integracao com setuptools para distribuicao:

# setup.py ou pyproject.toml
# entry_points = {"console_scripts": ["tarefas=tarefas:cli"]}

Isso permite instalar a CLI com pip install . e usa-la diretamente no terminal.

Adicionando Barras de Progresso

Ambas as bibliotecas suportam indicadores de progresso. Com Click:

import click
import time

@click.command()
@click.argument("arquivos", nargs=-1)
def processar(arquivos):
    """Processa uma lista de arquivos."""
    with click.progressbar(arquivos, label="Processando") as barra:
        for arquivo in barra:
            time.sleep(0.5)  # Simula processamento
    click.echo("Concluido!")

Formatando Saida com Tabelas

Para saidas mais profissionais, use a biblioteca tabulate:

from tabulate import tabulate

def mostrar_tabela(tarefas):
    """Mostra tarefas em formato de tabela."""
    dados = [
        [t["id"], t["titulo"], t["prioridade"],
         "Sim" if t["concluida"] else "Nao"]
        for t in tarefas
    ]
    headers = ["ID", "Titulo", "Prioridade", "Concluida"]
    print(tabulate(dados, headers=headers, tablefmt="grid"))

# +------+------------------+-------------+------------+
# |   ID | Titulo           | Prioridade  | Concluida  |
# +======+==================+=============+============+
# |    1 | Estudar Python   | alta        | Sim        |
# +------+------------------+-------------+------------+

Empacotando Sua CLI

Para distribuir sua ferramenta, configure o pyproject.toml:

[project]
name = "tarefas-cli"
version = "1.0.0"
description = "Gerenciador de tarefas via terminal"
requires-python = ">=3.9"
dependencies = ["click>=8.0"]

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

Depois basta instalar em modo de desenvolvimento com pip install -e . e o comando tarefas estara disponivel globalmente.

Boas Praticas para CLIs

Ao criar ferramentas de linha de comando, siga estas recomendacoes:

  • Sempre inclua --help com descricoes claras para todos os comandos e opcoes
  • Use codigos de saida adequados: 0 para sucesso, 1 para erro
  • Adicione --version para facilitar debug
  • Formate a saida com cores e tabelas para melhor legibilidade
  • Trate erros de forma amigavel com mensagens claras
  • Adicione confirmacao para operacoes destrutivas
  • Escreva testes para os comandos usando CliRunner do Click

Conclusao

Python e uma escolha excelente para criar ferramentas de linha de comando. O argparse atende bem para scripts simples, enquanto o Click brilha em projetos maiores com multiplos subcomandos e opcoes. Independente da escolha, o importante e criar CLIs com boa documentacao, tratamento de erros e uma experiencia agradavel para o usuario. Comece com os exemplos deste guia e evolua conforme a complexidade do seu projeto.

E

Equipe Python Brasil

Contribuidor do Python Brasil — Aprenda Python em Português