Introdução
Como desenvolvedores Python, frequentemente precisamos criar scripts de linha de comando (CLI). Em projetos de ciência de dados, por exemplo, pode ser necessário executar scripts para treinar modelos ou calcular métricas de desempenho. Scripts de CLI bem projetados aumentam a produtividade, especialmente em ambientes com múltiplos desenvolvedores.
Para alcançar isso, recomenda-se seguir estas quatro diretrizes:
- Forneça valores padrão para parâmetros sempre que possível.
- Trate todos os casos de erro, como argumentos faltantes ou tipos incorretos.
- Documente todos os parâmetros e opções claramente.
- Exiba barras de progresso para tarefas demoradas.
Exemplo Prático: Cifra de César
Vamos aplicar essas regras em um script que usa a cifra de César para criptografar e descriptografar mensagens. O objetivo é permitir que o usuário selecione o modo (criptografar ou descriptografar) e uma chave via argumentos de linha de comando.
Primeiro, defina a função de criptografia:
def aplicar_cifra(texto, chave):
saida = ''
for caractere in texto:
if caractere.isalpha():
valor_ascii = ord(caractere)
valor_ascii += chave
if caractere.isupper():
if valor_ascii > ord('Z'):
valor_ascii -= 26
elif valor_ascii < ord('A'):
valor_ascii += 26
else:
if valor_ascii > ord('z'):
valor_ascii -= 26
elif valor_ascii < ord('a'):
valor_ascii += 26
caractere = chr(valor_ascii)
saida += caractere
return saida
Abordagem com sys.argv
Uma maneira inicial de obter argumentos é usando sys.argv, que retorna uma lista de argumentos da linha de comando. No entanto, isso requer análise manual, tornando o código extenso e propenso a erros.
Exemplo de script usando sys.argv:
import sys
from cifra_cesar import aplicar_cifra
def executar():
chave = 1
erro = False
args = sys.argv[1:]
for i, arg in enumerate(args):
if arg in ['--chave', '-c'] and i + 1 < len(args):
chave = int(args[i + 1])
del args[i:i+2]
break
modo_descriptografar = False
for i, arg in enumerate(args):
if arg in ['--descriptografar', '-d']:
modo_descriptografar = True
del args[i]
break
if not args or any(a.startswith('-') for a in args):
erro = True
if erro:
print(f'Uso: python {sys.argv[0]} [--chave <chave>] [--descriptografar] <texto>')
else:
texto = ' '.join(args)
if modo_descriptografar:
chave = -chave
print(aplicar_cifra(texto, chave))
if __name__ == '__main__':
executar()</texto></chave>
Esta abordagem atende parcialmente às diretrizes, mas o código é complexo e difícil de manter.
Utilizando argparse
A biblioteca padrão argparse simplifica a análise de argumentos, oferecendo melhor documentação e tratamento de erros.
Exemplo com argparse:
import argparse
from cifra_cesar import aplicar_cifra
def executar():
parser = argparse.ArgumentParser(description='Cifra de César via CLI.')
grupo = parser.add_mutually_exclusive_group()
grupo.add_argument('-e', '--criptografar', action='store_true', help='Modo criptografia')
grupo.add_argument('-d', '--descriptografar', action='store_true', help='Modo descriptografia')
parser.add_argument('texto', nargs='*', help='Texto a processar')
parser.add_argument('-c', '--chave', type=int, default=1, help='Chave numérica')
args = parser.parse_args()
texto_str = ' '.join(args.texto)
chave = args.chave
if args.descriptografar:
chave = -chave
resultado = aplicar_cifra(texto_str, chave)
print(resultado)
if __name__ == '__main__':
executar()
Este código é mais limpo, mas a definição de argumentos ainda pode ser verbosa.
Adotando a Biblioteca click
A biblioteca click oferece uma API mais declarativa e elegante para CLI. Ela usa decoradores para definir argumentos e opções diretamente na função.
Exemplo com click:
import click
from cifra_cesar import aplicar_cifra
@click.command()
@click.argument('texto', nargs=-1)
@click.option('--descriptografar/--criptografar', '-d/-e', default=False, help='Seleciona modo')
@click.option('--chave', '-c', default=1, help='Chave para cifra')
def executar(texto, descriptografar, chave):
texto_completo = ' '.join(texto)
if descriptografar:
chave = -chave
click.echo(aplicar_cifra(texto_completo, chave))
if __name__ == '__main__':
executar()
Aqui, os argumentos são definidos como decoradores, tornando o código mais conciso. O nargs=-1 permite múltiplos argumentos posicionais.
Recursos Avançados com click
Para aprimorar a segurança e usabilidade, podemos adicionar suporte a arquivos e entradas ocultas.
import click
from cifra_cesar import aplicar_cifra
@click.command()
@click.option('--arquivo-entrada', type=click.File('r'), help='Arquivo com texto de entrada')
@click.option('--arquivo-saida', type=click.File('w'), help='Arquivo para salvar resultado')
@click.option('--descriptografar/--criptografar', '-d/-e', default=False, help='Modo de operação')
@click.option('--chave', '-c', default=1, help='Chave numérica')
def executar(arquivo_entrada, arquivo_saida, descriptografar, chave):
if arquivo_entrada:
texto = arquivo_entrada.read()
else:
texto = click.prompt('Digite o texto', hide_input=not descriptografar)
if descriptografar:
chave = -chave
resultado = aplicar_cifra(texto, chave)
if arquivo_saida:
arquivo_saida.write(resultado)
else:
click.echo(resultado)
if __name__ == '__main__':
executar()
Este script inclui opções para ler de arquivos, escrever para arquiovs e entrada oculta em modo de criptografia. A biblioteca click lida automaticamente com erros de arquivo, como arquivos inexistentes.