Aplicações CLI
LidoNesta aula, você aprenderá a criar aplicações de linha de comando (CLI) em Python, utilizando as bibliotecas argparse e click. Exploraremos a estrutura de um projeto CLI, boas práticas de desenvolvimento e como publicar sua ferramenta, com exemplos práticos e exercícios.
Bem-vindo à aula 87! Nesta aula, vamos explorar a criação de aplicações de linha de comando (CLI) em Python. CLIs são ferramentas essenciais para automatizar tarefas, interagir com sistemas e fornecer interfaces poderosas para usuários avançados. Você aprenderá a usar a biblioteca padrão argparse e a biblioteca de terceiros click, além de entender a estrutura ideal de um projeto CLI e as melhores práticas para tornar seu código robusto e utilizável.
Vamos começar com uma visão geral do que é uma CLI e por que Python é uma excelente escolha para desenvolvê-las. Em seguida, mergulharemos em argparse, mostrando como criar argumentos, opções e subcomandos. Depois, apresentaremos click, que oferece uma abordagem mais elegante e produtiva. Finalmente, discutiremos a estrutura de um projeto e boas práticas, incluindo testes, documentação e empacotamento.
argparse
O módulo argparse é a ferramenta padrão do Python para criar interfaces de linha de comando. Ele permite definir argumentos posicionais, opções (flags) e subcomandos, além de gerar automaticamente mensagens de ajuda e validação de entrada. É uma escolha sólida para ferramentas que precisam de uma interface robusta sem dependências externas.
Vamos criar um exemplo simples: uma calculadora de linha de comando que soma ou multiplica dois números. Primeiro, importamos argparse, criamos um parser e adicionamos argumentos. O método parse_args() lê os argumentos da linha de comando e os converte em atributos do objeto retornado.
import argparse
parser = argparse.ArgumentParser(description='Calculadora simples.')
parser.add_argument('num1', type=float, help='primeiro número')
parser.add_argument('num2', type=float, help='segundo número')
parser.add_argument('-o', '--operation', choices=['add', 'mul'], default='add',
help='operação: add (soma) ou mul (multiplicação)')
args = parser.parse_args()
if args.operation == 'add':
result = args.num1 + args.num2
else:
result = args.num1 * args.num2
print(f'Resultado: {result}')
Execute o script com python calc.py 3 4 -o mul e veja o resultado. O argparse também oferece suporte a subcomandos, úteis para ferramentas com múltiplas operações, como um gerenciador de tarefas com comandos add, list, done. Para isso, usamos add_subparsers().
import argparse
parser = argparse.ArgumentParser(description='Gerenciador de tarefas')
subparsers = parser.add_subparsers(dest='command', help='comando disponível')
# Subcomando add
parser_add = subparsers.add_parser('add', help='adiciona uma tarefa')
parser_add.add_argument('task', help='descrição da tarefa')
# Subcomando list
parser_list = subparsers.add_parser('list', help='lista todas as tarefas')
args = parser.parse_args()
if args.command == 'add':
print(f'Tarefa adicionada: {args.task}')
elif args.command == 'list':
print('Lista de tarefas: ...')
else:
parser.print_help()
O argparse é poderoso e flexível, mas pode se tornar verboso para CLIs complexas. Para projetos maiores, bibliotecas como click oferecem uma sintaxe mais concisa e decoradores que aceleram o desenvolvimento.
click (visão geral)
O click é uma biblioteca de terceiros que simplifica a criação de CLIs. Ele usa decoradores para definir comandos e opções, reduzindo a quantidade de código boilerplate. Além disso, integra-se bem com o ecossistema Python, como o Flask, e oferece recursos avançados como prompts interativos, cores e paginação.
Para instalar, use pip install click. O exemplo da calculadora fica assim:
import click
@click.command()
@click.argument('num1', type=float)
@click.argument('num2', type=float)
@click.option('-o', '--operation', type=click.Choice(['add', 'mul']), default='add',
help='Operação: add ou mul')
def calc(num1, num2, operation):
"""Calculadora simples."""
if operation == 'add':
result = num1 + num2
else:
result = num1 * num2
click.echo(f'Resultado: {result}')
if __name__ == '__main__':
calc()
Observe como os argumentos e opções são definidos como decoradores e a função recebe os valores como parâmetros. O click também suporta grupos de comandos, permitindo criar CLIs com subcomandos de forma intuitiva:
import click
@click.group()
def cli():
"""Gerenciador de tarefas."""
pass
@cli.command()
@click.argument('task')
def add(task):
"""Adiciona uma tarefa."""
click.echo(f'Tarefa adicionada: {task}')
@cli.command()
def list():
"""Lista todas as tarefas."""
click.echo('Lista de tarefas: ...')
if __name__ == '__main__':
cli()
O click é amplamente adotado na comunidade e é a base de ferramentas como o Flask CLI. Sua curva de aprendizado é suave e a documentação é excelente.
Estrutura
Uma boa estrutura de projeto é fundamental para a manutenibilidade de uma CLI. Vamos considerar a organização de um projeto Python típico:
meu_projeto/
├── meu_projeto/
│ ├── __init__.py
│ ├── cli.py
│ └── core.py
├── tests/
│ └── test_cli.py
├── pyproject.toml
└── README.md
O diretório meu_projeto contém o código-fonte, com cli.py responsável pela interface de linha de comando e core.py com a lógica de negócio. Essa separação permite testar a lógica independentemente da interface. O arquivo pyproject.toml define os metadados e dependências, e README.md documenta o uso.
Para executar a CLI durante o desenvolvimento, você pode usar python -m meu_projeto.cli ou criar um entry point em pyproject.toml que instale o comando globalmente. Por exemplo, com setuptools:
[project]
name = "meu-projeto"
version = "0.1.0"
[project.scripts]
meu-cli = "meu_projeto.cli:main"
Assim, após pip install ., o comando meu-cli estará disponível no terminal. Essa estrutura é escalável e segue convenções da comunidade.
Boas práticas
Ao desenvolver CLIs, siga estas boas práticas para garantir qualidade e usabilidade:
- Use nomes claros e consistentes para comandos, opções e argumentos.
- Forneça mensagens de ajuda úteis descrevendo cada parâmetro.
- Valide entradas e trate erros graciosamente, usando códigos de saída adequados.
- Escreva testes para a lógica de negócio e, se possível, para a interface (usando
CliRunnerdo click). - Mantenha a lógica de negócio separada da interface para facilitar a reutilização.
- Documente o uso em um README ou página de ajuda.
- Siga o princípio da menor surpresa: o comportamento deve ser intuitivo.
Além disso, considere usar logging em vez de print para mensagens de depuração, e ofereça a opção --verbose para aumentar a verbosidade. Essas práticas tornam sua ferramenta profissional e confiável.
Referências
- Documentação oficial do argparse
- Documentação oficial do Click
- Exemplos de argparse
- Quickstart do Click
- Empacotamento de projetos Python
- Documentação do pytest
Exercícios
- Crie uma CLI usando
argparseque aceite um nome e uma idade, e imprima uma mensagem de boas-vindas personalizada. Valide que a idade é um número inteiro positivo. - Reescreva o exercício 1 usando
click, com opções em vez de argumentos posicionais. - Implemente uma CLI com subcomandos
addelistpara um gerenciador de tarefas simples (pode usar uma lista em memória). Useargparse. - Usando
click, crie um grupo de comandos comaddelistpara o mesmo gerenciador de tarefas. - Estruture um projeto CLI conforme as boas práticas: crie um diretório com módulo principal e um arquivo de teste básico usando
pytestpara testar a lógica de negócio (por exemplo, uma função que valida dados).
import argparse
parser = argparse.ArgumentParser(description='Mensagem de boas-vindas.')
parser.add_argument('nome', help='nome do usuário')
parser.add_argument('idade', type=int, help='idade do usuário')
args = parser.parse_args()
if args.idade < 0:
parser.error('A idade deve ser um número positivo.')
print(f'Olá, {args.nome}! Você tem {args.idade} anos.')
import click
@click.command()
@click.option('--nome', prompt='Seu nome', help='Nome do usuário')
@click.option('--idade', type=int, prompt='Sua idade', help='Idade do usuário')
def saudacao(nome, idade):
"""Mensagem de boas-vindas."""
if idade < 0:
click.echo('Idade inválida!', err=True)
raise SystemExit(1)
click.echo(f'Olá, {nome}! Você tem {idade} anos.')
if __name__ == '__main__':
saudacao()
import argparse
tarefas = []
parser = argparse.ArgumentParser(description='Gerenciador de tarefas')
subparsers = parser.add_subparsers(dest='comando', required=True)
parser_add = subparsers.add_parser('add', help='adiciona tarefa')
parser_add.add_argument('tarefa', help='descrição da tarefa')
parser_list = subparsers.add_parser('list', help='lista tarefas')
args = parser.parse_args()
if args.comando == 'add':
tarefas.append(args.tarefa)
print(f'Tarefa adicionada: {args.tarefa}')
elif args.comando == 'list':
for i, tarefa in enumerate(tarefas, 1):
print(f'{i}. {tarefa}')
import click
tarefas = []
@click.group()
def cli():
"""Gerenciador de tarefas."""
pass
@cli.command()
@click.argument('tarefa')
def add(tarefa):
"""Adiciona uma tarefa."""
tarefas.append(tarefa)
click.echo(f'Tarefa adicionada: {tarefa}')
@cli.command()
def list():
"""Lista todas as tarefas."""
for i, tarefa in enumerate(tarefas, 1):
click.echo(f'{i}. {tarefa}')
if __name__ == '__main__':
cli()
Estrutura do projeto:
meu_projeto/
├── meu_projeto/
│ ├── __init__.py
│ ├── core.py
│ └── cli.py
├── tests/
│ └── test_core.py
└── pyproject.toml
Em core.py:
def validar_idade(idade):
if idade < 0:
raise ValueError('Idade deve ser positiva')
return True
Em tests/test_core.py:
import pytest
from meu_projeto.core import validar_idade
def test_validar_idade_positiva():
assert validar_idade(25) is True
def test_validar_idade_negativa():
with pytest.raises(ValueError):
validar_idade(-1)
Observações finais
Nesta aula, você aprendeu a criar CLIs com Python usando argparse e click. A escolha entre eles depende do contexto: argparse é ideal para scripts simples e sem dependências, enquanto click oferece mais produtividade e recursos para projetos complexos. Lembre-se de sempre estruturar seu projeto de forma organizada e seguir boas práticas para garantir que sua ferramenta seja fácil de manter e usar. Pratique com os exercícios e explore mais a documentação para se aprofundar.