Criando uma CLI profissional com argparse
1. Introdução ao argparse: por que escolhê-lo?
Ao construir uma aplicação de linha de comando em Python, o desenvolvedor se depara com várias opções: Click, Typer, Fire e o nativo argparse. Embora Click e Typer ofereçam sintaxe mais declarativa e recursos como decoradores, o argparse é a escolha ideal quando você precisa de uma solução sem dependências externas, total controle sobre o parsing e compatibilidade garantida com qualquer ambiente Python.
O argparse faz parte da biblioteca padrão desde Python 2.7/3.2, o que significa que sua CLI funcionará em qualquer instalação Python sem necessidade de pip install. Ele é particularmente recomendado para ferramentas de sistema, scripts de automação e projetos que serão distribuídos como pacotes.
Estrutura básica
import argparse
parser = argparse.ArgumentParser(description='Uma CLI de exemplo')
parser.add_argument('nome', help='Seu nome')
args = parser.parse_args()
print(f'Olá, {args.nome}!')
Salve como saudacao.py e execute:
$ python saudacao.py Maria
Olá, Maria!
Já temos uma CLI funcional com ajuda automática:
$ python saudacao.py --help
usage: saudacao.py [-h] nome
Uma CLI de exemplo
positional arguments:
nome Seu nome
options:
-h, --help show this help message and exit
2. Definindo argumentos posicionais e opcionais
No argparse, argumentos posicionais são obrigatórios por padrão e seguem a ordem em que são declarados. Argumentos opcionais usam flags como -v ou --verbose e podem ter valores padrão.
import argparse
parser = argparse.ArgumentParser(description='Ferramenta de processamento')
parser.add_argument('arquivo', help='Arquivo de entrada')
parser.add_argument('--saida', '-o', default='resultado.txt',
help='Arquivo de saída (padrão: resultado.txt)')
parser.add_argument('--verbose', '-v', action='store_true',
help='Modo verboso')
args = parser.parse_args()
print(f'Processando {args.arquivo}...')
if args.verbose:
print(f'Saída definida para: {args.saida}')
Boas práticas de nomenclatura
- Use
metavarpara exibir nomes descritivos na ajuda:
parser.add_argument('--limite', '-l', type=int, metavar='NUM',
help='Número máximo de itens (padrão: %(default)s)')
3. Tipos, validação e conversão automática
O argparse converte automaticamente strings para os tipos especificados:
parser.add_argument('--idade', type=int, help='Sua idade')
parser.add_argument('--peso', type=float, help='Seu peso em kg')
parser.add_argument('--arquivo', type=open, help='Arquivo para leitura')
Validação personalizada
def validar_email(valor):
if '@' not in valor:
raise argparse.ArgumentTypeError(f'Email inválido: {valor}')
return valor
parser.add_argument('--email', type=validar_email, required=True,
help='Endereço de email válido')
Restringindo valores com choices e múltiplos argumentos com nargs
parser.add_argument('--modo', choices=['rápido', 'completo', 'teste'],
default='rápido', help='Modo de execução')
parser.add_argument('--arquivos', nargs='+', help='Lista de arquivos')
Uso:
$ python script.py --modo completo --arquivos a.txt b.txt c.txt
4. Subcomandos: organizando funcionalidades complexas
Para CLIs com múltiplas operações (como git commit, git push), usamos subparsers:
import argparse
def cmd_add(args):
print(f'Adicionando tarefa: {args.tarefa}')
def cmd_list(args):
print('Listando tarefas...')
def cmd_done(args):
print(f'Marcando tarefa {args.id} como concluída')
parser = argparse.ArgumentParser(description='Gerenciador de tarefas')
parser.add_argument('--verbose', '-v', action='store_true',
help='Modo verboso')
subparsers = parser.add_subparsers(dest='comando', title='Comandos',
description='Comandos disponíveis')
# Subcomando "add"
parser_add = subparsers.add_parser('add', help='Adicionar nova tarefa')
parser_add.add_argument('tarefa', help='Descrição da tarefa')
parser_add.set_defaults(func=cmd_add)
# Subcomando "list"
parser_list = subparsers.add_parser('list', help='Listar tarefas')
parser_list.set_defaults(func=cmd_list)
# Subcomando "done"
parser_done = subparsers.add_parser('done', help='Concluir tarefa')
parser_done.add_argument('id', type=int, help='ID da tarefa')
parser_done.set_defaults(func=cmd_done)
args = parser.parse_args()
if hasattr(args, 'func'):
args.func(args)
else:
parser.print_help()
Uso:
$ python todo.py add "Estudar argparse"
$ python todo.py list
$ python todo.py done 1
Os subcomandos herdam automaticamente o argumento --verbose definido no parser principal.
5. Melhorando a experiência do usuário
Personalização da ajuda
from argparse import RawDescriptionHelpFormatter
parser = argparse.ArgumentParser(
description='Ferramenta de backup automático',
epilog='Exemplo: backup.py --origem /home --destino /mnt/backup',
formatter_class=RawDescriptionHelpFormatter
)
Integrando com Rich para cores
from rich.console import Console
from rich.table import Table
console = Console()
def exibir_tabela(args):
tabela = Table(title="Configuração")
tabela.add_column("Parâmetro", style="cyan")
tabela.add_column("Valor", style="green")
tabela.add_row("Origem", args.origem)
tabela.add_row("Destino", args.destino)
console.print(tabela)
Tratamento de erros amigável
class CLIErrorParser(argparse.ArgumentParser):
def error(self, message):
self.print_usage()
print(f'\033[91mErro: {message}\033[0m')
self.exit(2)
parser = CLIErrorParser(description='CLI com erros coloridos')
6. Padrões avançados
Grupos mutuamente exclusivos
grupo = parser.add_mutually_exclusive_group(required=True)
grupo.add_argument('--compacto', action='store_true', help='Formato compacto')
grupo.add_argument('--detalhado', action='store_true', help='Formato detalhado')
Ações customizadas
class AçãoContador(argparse.Action):
def __call__(self, parser, namespace, values, option_string=None):
setattr(namespace, self.dest, values + 1)
parser.add_argument('--debug', action=AçãoContador, nargs=0,
help='Nível de debug (use múltiplas vezes)')
Argumentos com contagem
parser.add_argument('-v', '--verbose', action='count', default=0,
help='Aumentar verbosidade')
Uso: -v = nível 1, -vv = nível 2, -vvv = nível 3.
7. Testando e depurando sua CLI
Simulação de argumentos
# Em vez de parse_args() sem argumentos, use uma lista
args = parser.parse_args(['--verbose', '--modo', 'rápido', 'arquivo.txt'])
assert args.verbose == True
assert args.modo == 'rápido'
Testes com pytest
import pytest
def test_parser_modo_padrao():
parser = criar_parser()
args = parser.parse_args(['arquivo.txt'])
assert args.modo == 'rápido'
def test_parser_modo_completo():
parser = criar_parser()
args = parser.parse_args(['--modo', 'completo', 'arquivo.txt'])
assert args.modo == 'completo'
def test_parser_modo_invalido():
parser = criar_parser()
with pytest.raises(SystemExit):
parser.parse_args(['--modo', 'invalido', 'arquivo.txt'])
Logging com verbose
import logging
logging.basicConfig(level=logging.WARNING)
if args.verbose >= 1:
logging.getLogger().setLevel(logging.INFO)
if args.verbose >= 2:
logging.getLogger().setLevel(logging.DEBUG)
logging.debug('Iniciando processamento...')
logging.info(f'Arquivo: {args.arquivo}')
8. Empacotamento e distribuição profissional
Configuração com pyproject.toml
[build-system]
requires = ["setuptools>=61.0"]
build-backend = "setuptools.backends._legacy:_Backend"
[project]
name = "minha-cli"
version = "1.0.0"
description = "Uma CLI profissional"
[project.scripts]
minha-cli = "minha_cli.main:main"
Alternativa com setup.py
from setuptools import setup, find_packages
setup(
name='minha-cli',
version='1.0.0',
packages=find_packages(),
entry_points={
'console_scripts': [
'minha-cli=minha_cli.main:main',
],
},
)
Após instalar com pip install -e ., o comando minha-cli estará disponível globalmente.
Checklist final para produção
- [ ] Documentação completa com
--helpdescritivo - [ ] Tratamento de edge cases (arquivos inexistentes, permissões)
- [ ] Compatibilidade Python 3.8+
- [ ] Testes unitários para todos os subcomandos
- [ ] Mensagens de erro claras e acionáveis
- [ ] Suporte a variáveis de ambiente para configuração
- [ ] Logging configurável via
--verbose
O argparse oferece um equilíbrio perfeito entre simplicidade e poder. Embora bibliotecas como Click e Typer sejam excelentes para projetos maiores, o argparse é a escolha certa quando você precisa de uma CLI robusta, sem dependências e com controle total. Comece pequeno, adicione subcomandos conforme necessário e mantenha a experiência do usuário sempre em mente.
Referências
- Documentação oficial do argparse — Referência completa com todos os métodos, parâmetros e exemplos da biblioteca padrão.
- Python Argparse Cookbook — Tutorial prático do Real Python cobrindo desde o básico até padrões avançados com exemplos reais.
- Argparse vs Click vs Typer: Comparação — Análise detalhada das diferenças entre as principais bibliotecas para CLI em Python.
- Testando CLIs com pytest — Guia oficial do pytest com exemplos de como testar parsers de argumentos e simular entradas de linha de comando.
- Empacotamento com pyproject.toml — Guia oficial do Python Packaging Authority sobre como configurar entry_points e distribuir sua CLI.
- Rich: Formatação de terminal — Biblioteca para adicionar cores, tabelas e formatação profissional à sua CLI.
- Python Logging Cookbook — Receitas práticas para integrar logging configurável com argumentos --verbose em CLIs.