Trilha de aprendizado · Nível 15 · Tutorial 3

Criar comandos e opções com argparse

Expor os casos de uso da aplicação por uma interface de terminal com argumentos, opções, subcomandos e ajuda gerada pelo argparse.

  • Nível: Intermediário
  • Duração: 20 min
  • 8 passos
Criar comandos e opções com argparse

O que você vai percorrer

  1. Entender o papel do parser Veja como o argparse declara as entradas da CLI e as transforma em valores para a aplicação. 2 min
  2. Declarar posicionais, opções e sinalizadores Declare entradas obrigatórias e opcionais para uma busca no catálogo e observe os atributos produzidos pelo parser. 2 min
  3. Converter e restringir valores Converta opções recebidas no terminal, limite alternativas aceitas e defina padrões coerentes para a consulta ao catálogo. 3 min
  4. Tornar a interface consultável com ajuda Documente a interface diretamente no parser e use a ajuda automática para entender as entradas aceitas. 2 min
  5. Organizar operações em subcomandos Agrupe busca e listagem em uma única interface, exigindo que o usuário escolha explicitamente a operação desejada. 3 min
  6. Encaminhar os argumentos ao caso de uso Conecte a interface criada com argparse às funções da aplicação, mantendo cada responsabilidade separada. 3 min
  7. Interpretar argumentos sem depender do terminal Exercite o parser com listas explícitas e preserve a diferença entre os argumentos do processo e uma lista vazia. 3 min
  8. Validar a CLI completa Reúna subcomandos, opções, ajuda e listas explícitas em um script local e confira evidências observáveis da interface. 4 min

O que você vai aprender

  • Declarar argumentos posicionais e opções com conversões e restrições adequadas.
  • Organizar operações diferentes em subcomandos.
  • Encaminhar os argumentos interpretados ao caso de uso correspondente.
  • Permitir que a interpretação dos argumentos seja exercitada com uma lista fornecida explicitamente.

Antes de começar

  • Separar domínio, persistência e interface
  • Controlar a execução de módulos com __name__
  • Tratar valores opcionais e uniões de tipos

Passo 1 de 8

Entender o papel do parser

Veja como o argparse declara as entradas da CLI e as transforma em valores para a aplicação.

A CLI declara o que aceita

O parser na fronteira da aplicação

Uma interface de linha de comando (CLI) recebe palavras digitadas no terminal. O argparse é uma biblioteca padrão do Python que permite declarar quais entradas a aplicação aceita e interpretá-las.

Pense no ArgumentParser como a descrição da porta de entrada da CLI. Ele não contém as regras de busca nem decide quais itens pertencem ao catálogo: essas responsabilidades continuam nos casos de uso. O parser recebe argumentos, verifica-os conforme a interface declarada e entrega valores interpretados à aplicação.

Do terminal ao caso de uso

A CLI atua como adaptador entre o terminal e a aplicação.

Diagrama mostrando argumentos digitados no terminal passando por um parser e sendo transformados em valores interpretados antes de chegar ao caso de uso de catálogo.

O parser fica na fronteira: declara e interpreta entradas; o caso de uso trabalha com a operação da aplicação.

Um catálogo executado como script

Exemplo inicial

Crie um arquivo chamado catalogo.py no seu computador. Este exemplo é autocontido: os dados ficam em memória e a função buscar_catalogo representa um caso de uso simples.

Por enquanto, o parser é criado e parse_args() lê os argumentos usados ao executar o script. Nas próximas etapas, você declarará quais argumentos a interface aceita.

catalogo.py

python
import argparse

CATALOGO = [
    "Python para análise de dados",
    "Arquitetura de aplicações Python",
    "Algoritmos em prática",
]


def buscar_catalogo(termo: str) -> list[str]:
    termo_normalizado = termo.casefold()
    return [
        titulo
        for titulo in CATALOGO
        if termo_normalizado in titulo.casefold()
    ]


def listar_catalogo() -> list[str]:
    return CATALOGO.copy()


parser = argparse.ArgumentParser(
    description="Consulta um catálogo em memória."
)
argumentos = parser.parse_args()

print("Parser criado. Em seguida, a CLI receberá entradas declaradas.")

Exemplo

Execute localmente

No diretório que contém o arquivo, execute:

python catalogo.py

A saída será:

Parser criado. Em seguida, a CLI receberá entradas declaradas.

Não é necessário instalar um comando, criar pacote nem preparar arquivos adicionais. Neste momento, ainda não há entradas declaradas para a aplicação.

Criar não é interpretar

Duas ações distintas

argparse.ArgumentParser(...) cria o objeto que representa a interface. Já parser.parse_args() lê os argumentos da execução atual e tenta interpretá-los conforme essa interface.

Quando adicionarmos entradas declaradas, parse_args() produzirá um objeto com os valores resultantes. Depois, a CLI poderá usar esses valores para chamar a busca ou a listagem. O parser não deve receber a responsabilidade de implementar essas operações.

Associe cada parte à responsabilidade

Relacione os elementos da CLI às suas responsabilidades neste exemplo.

Toque em um item e depois no par correspondente.

Passo 2 de 8

Declarar posicionais, opções e sinalizadores

Declare entradas obrigatórias e opcionais para uma busca no catálogo e observe os atributos produzidos pelo parser.

Três formas de entrada

O que cada declaração aceita

Depois de criar o ArgumentParser, use add_argument() para declarar as entradas da sua CLI.

  • Um posicional é informado pela posição e, nesta declaração básica, é obrigatório.
  • Uma opção nomeada começa com - ou -- e recebe um valor.
  • Um sinalizador indica presença ou ausência: não recebe valor próprio.

Para uma busca no catálogo, declare o termo buscado, um limite opcional e a exibição detalhada.

Declarações do parser

python
import argparse

parser = argparse.ArgumentParser(
    description="Consulta um catálogo em memória."
)

parser.add_argument("termo", help="Texto a procurar no catálogo")
parser.add_argument(
    "-l", "--limite",
    help="Quantidade máxima de itens exibidos"
)
parser.add_argument(
    "-d", "--detalhado",
    action="store_true",
    help="Exibe informações detalhadas"
)

args = parser.parse_args()

Anatomia de uma chamada

Nesta chamada, cada parte é consumida pela declaração correspondente. As aspas fazem com que Python para iniciantes chegue ao programa como um único argumento, mesmo contendo espaços.

Diagrama de uma linha de terminal dividida em nome do script, termo entre aspas, opção limite com valor e sinalizador detalhado, todos apontando para seus campos no Namespace.

O termo é posicional; --limite recebe um valor; --detalhado apenas é ativado.

Do terminal ao Namespace

Um atributo por destino

parse_args() devolve um objeto Namespace. Cada argumento declarado produz um atributo acessível por ponto.

Os dois nomes de -l e --limite são apenas aliases da mesma opção: ambos alimentam args.limite, não dois valores diferentes.

Sem conversão declarada, o valor recebido por uma opção continua sendo texto. Quando --limite não aparece, seu valor é None. Já store_true gera False quando o sinalizador está ausente e True quando está presente.

Exemplo

Dois resultados possíveis

Chamada:

python catalogo.py "Python para iniciantes" --limite 5 --detalhado

Resultado conceitual:

Namespace(termo="Python para iniciantes", limite="5", detalhado=True)

Chamada:

python catalogo.py algoritmos

Resultado conceitual:

Namespace(termo="algoritmos", limite=None, detalhado=False)

Você pode ler esses valores com args.termo, args.limite e args.detalhado.

Dica

Aspas pertencem ao shell

No terminal, use aspas para proteger um termo com espaços. O parser recebe o conteúdo como um único valor; as aspas não fazem parte de args.termo.

Pratique a declaração

Complete o sinalizador

Complete a declaração para que --detalhado resulte em True quando estiver presente:

parser.add_argument("-d", "--detalhado", action="_____")

Confira a leitura dos argumentos

Preveja o Namespace

Com as declarações apresentadas, qual Namespace corresponde a esta chamada?

python catalogo.py "ciência de dados" -l 3

Passo 3 de 8

Converter e restringir valores

Converta opções recebidas no terminal, limite alternativas aceitas e defina padrões coerentes para a consulta ao catálogo.

Converter, limitar e completar

Três papéis diferentes

Argumentos chegam pela linha de comando como texto. Na declaração, você pode:

  • usar type=int para converter um valor;
  • usar choices para aceitar apenas alternativas previstas;
  • usar default para definir o valor quando a opção não for informada.

Esses recursos atuam antes de chamar o caso de uso. Assim, o atributo do Namespace já vem no formato esperado pela interface.

O caminho de cada opção

A mesma chamada pode acionar conversão, restrição e preenchimento por padrão.

Diagrama mostrando --limite 3 sendo convertido de texto para inteiro 3, --ordenar nome sendo aceito entre alternativas, e uma opção ausente recebendo o valor padrão nome.

Conversão, escolhas e padrão são regras declaradas no parser.

Declaração da interface

Opções tipadas e restritas

Adicione estas declarações ao parser criado no step anterior.

python
import argparse

parser = argparse.ArgumentParser(description="Consulta um catálogo em memória")
parser.add_argument("termo", help="texto a procurar no catálogo")
parser.add_argument(
    "-l",
    "--limite",
    type=int,
    default=5,
    help="quantidade máxima de itens (padrão: 5)",
)
parser.add_argument(
    "-o",
    "--ordenar",
    choices=["nome", "preco"],
    default="nome",
    help="critério de ordenação: nome ou preco (padrão: nome)",
)

args = parser.parse_args()
print(args)

Exemplo

Valores após a interpretação

Execute no seu terminal:

python catalogo.py "café" --limite 3 --ordenar preco

O resultado terá valores equivalentes a:

Namespace(termo='café', limite=3, ordenar='preco')

Embora 3 tenha sido digitado como texto, args.limite é um int. Já ordenar continua sendo uma string, mas necessariamente uma das alternativas declaradas.

Dica

Padrões também precisam ser válidos

O padrão de limite deve ser um inteiro, como 5; o de ordenar deve pertencer a choices, como "nome". Ao executar python catalogo.py "café", o Namespace usará limite=5 e ordenar='nome'.

O que o parser recusa — e o que ainda cabe ao domínio

Atenção

Inteiro não significa limite válido para o negócio

type=int aceita valores como 0 e -2, pois ambos podem ser convertidos para inteiro. Se a regra do catálogo exigir limite positivo, essa é uma regra de negócio a validar no caso de uso; ela não é garantida apenas por type=int.

Exemplo

Falhas observáveis do parser

Estas chamadas são recusadas antes da operação:

python catalogo.py café --limite muitos
→ o parser informa que muitos não é um inteiro válido.

python catalogo.py café --ordenar data
→ o parser informa que data não é uma escolha válida e apresenta as alternativas permitidas.

Leia a mensagem exibida pelo seu ambiente para conferir a opção e o valor que causaram a recusa.

Preveja o Namespace

Chamada sem opções

Com a declaração apresentada, qual valor é produzido por parser.parse_args(["chá"])?

Qual é a causa da recusa?

Por que parser.parse_args(["chá", "--limite", "2", "--ordenar", "data"]) é recusado?

Passo 4 de 8

Tornar a interface consultável com ajuda

Documente a interface diretamente no parser e use a ajuda automática para entender as entradas aceitas.

Descrição e ajuda de cada entrada

A ajuda faz parte da interface

Uma CLI não deve exigir que a pessoa usuária memorize suas entradas. No argparse, use description para explicar a finalidade do programa e help para explicar cada argumento.

O parser cria automaticamente -h e --help. Ao executar uma dessas opções, o programa mostra a ajuda e não precisa receber o termo posicional obrigatório.

Parser documentado

Adicione estes textos ao parser do catálogo.

python
import argparse

parser = argparse.ArgumentParser(
    description="Consulta títulos em um catálogo em memória."
)
parser.add_argument(
    "termo",
    help="termo obrigatório usado na busca; use aspas se houver espaços"
)
parser.add_argument(
    "-l", "--limite",
    type=int,
    default=10,
    help="quantidade máxima de resultados (padrão: 10)"
)
parser.add_argument(
    "--ordem",
    choices=["titulo", "ano"],
    default="titulo",
    help="critério de ordenação: titulo ou ano (padrão: titulo)"
)
parser.add_argument(
    "-d", "--detalhado",
    action="store_true",
    help="exibe também o ano de cada título"
)

args = parser.parse_args()
print(args)

Como ler a saída

A linha `usage` é um mapa da chamada

No terminal, salve o código como catalogo.py e execute:

python catalogo.py --help

A linha de uso mostra a forma aceita pela chamada:

  • termo, sem colchetes, é obrigatório;
  • itens entre colchetes, como [-l LIMITE], são opcionais;
  • LIMITE indica que -l ou --limite recebe um valor;
  • {-titulo,ano} não é a forma exibida: as alternativas de choices aparecem agrupadas como {titulo,ano};
  • -l LIMITE, --limite LIMITE mostra dois aliases para a mesma opção, não duas opções independentes.

Estrutura típica da ajuda

Compare a ajuda local com esta anatomia. Os textos das descrições e argumentos vêm do seu código.

Diagrama de uma tela de terminal com a ajuda de uma CLI anotada: linha de uso no topo, descrição abaixo, seção de argumentos posicionais e seção de opções; há destaques para termo obrigatório, opção opcional que recebe valor, choices entre chaves, aliases curto e longo e sinalizador.

A estrutura é gerada pelo argparse; description e help fornecem a explicação voltada à pessoa usuária.

Dica

Rótulos podem variar

Os textos que você passa em description e help são da sua aplicação e devem estar claros em português. Já rótulos internos gerados pelo argparse, como os títulos de seções, podem aparecer em outro idioma conforme o ambiente Python. Foque no significado e na estrutura da ajuda.

Padrões e alternativas precisam estar claros

Documente o que acontece quando a opção é omitida

default define o valor usado pelo programa, mas não conte com ele para explicar sozinho esse comportamento na tela de ajuda. Inclua o padrão no próprio texto de help, como em padrão: 10 e padrão: titulo.

Assim, a pessoa usuária descobre tanto as alternativas aceitas por --ordem quanto o critério usado se ela não informar essa opção.

Leia a sua ajuda

Execute python catalogo.py --help. Com suas palavras, explique: qual entrada é obrigatória, qual opção recebe um número, quais valores --ordem aceita e quais padrões foram documentados.

Escreva pelo menos 80 caracteres (0/80).

Passo 5 de 8

Organizar operações em subcomandos

Agrupe busca e listagem em uma única interface, exigindo que o usuário escolha explicitamente a operação desejada.

Uma CLI com operações explícitas

Do parser principal aos subcomandos

Quando a aplicação oferece operações diferentes, o parser principal pode reunir subparsers. Cada subparser declara apenas as entradas da operação a que pertence.

No catálogo, buscar precisa de um termo; listar não. Por isso, o termo posicional deixa de pertencer ao parser principal e passa para buscar.

Escopo das entradas

A árvore mostra quais argumentos estão disponíveis em cada caminho da interface.

Diagrama em árvore com um parser principal no topo, ramificando para os subcomandos buscar e listar. Buscar contém termo, limite e detalhado; listar contém ordem e limite.

O nome do subcomando vem antes das opções específicas dele.

Dica

Ordem da chamada

As opções específicas vêm depois do subcomando: python catalogo.py buscar "Python" --limite 3. Assim, o parser sabe qual conjunto de argumentos deve interpretar.

Declare os subparsers

Seleção obrigatória

add_subparsers() cria o agrupamento de operações. Com dest="comando", o Namespace registra o nome escolhido. Com required=True, uma chamada sem subcomando é recusada pelo parser antes de qualquer operação ser executada.

Estrutura da interface

Este trecho constrói a interface; ele ainda não decide qual operação executar.

python
import argparse

parser = argparse.ArgumentParser(
    description="Consulta um catálogo em memória."
)

subparsers = parser.add_subparsers(
    dest="comando",
    required=True,
    title="operações"
)

buscar_parser = subparsers.add_parser(
    "buscar",
    help="busca itens por termo",
    description="Busca itens cujo título contém o termo informado."
)
buscar_parser.add_argument("termo", help="termo da busca")
buscar_parser.add_argument(
    "-l", "--limite",
    type=int,
    default=10,
    help="quantidade máxima de resultados (padrão: 10)"
)
buscar_parser.add_argument(
    "-d", "--detalhado",
    action="store_true",
    help="exibe detalhes de cada item"
)

listar_parser = subparsers.add_parser(
    "listar",
    help="lista todos os itens",
    description="Lista os itens do catálogo em uma ordem escolhida."
)
listar_parser.add_argument(
    "--ordem",
    choices=["titulo", "ano"],
    default="titulo",
    help="critério de ordenação (padrão: titulo)"
)
listar_parser.add_argument(
    "-l", "--limite",
    type=int,
    default=10,
    help="quantidade máxima de resultados (padrão: 10)"
)

args = parser.parse_args()
print(args)

Exemplo

Namespaces possíveis

python catalogo.py buscar "Python avançado" -l 3 -d
→ Namespace(comando='buscar', termo='Python avançado', limite=3, detalhado=True)

python catalogo.py listar --ordem ano
→ Namespace(comando='listar', ordem='ano', limite=10)

Cada Namespace contém comando e os atributos declarados pelo subcomando escolhido. Portanto, não acesse args.termo no caminho de listar.

Ajuda em dois níveis

Resumo ou apresentação detalhada?

No add_parser, help resume o subcomando na ajuda principal. Já description aparece quando o usuário consulta a ajuda daquele subcomando, como em python catalogo.py buscar --help.

Os rótulos automáticos do argparse podem variar de idioma conforme o ambiente; os textos definidos em description e help são os que você controla.

Escolha o escopo correto

Qual chamada está de acordo com a interface declarada?

Complete a declaração

Agrupe as operações

Complete o código para criar o agrupamento de subcomandos:

subparsers = parser._____(dest="comando", required=True)

Atenção

Sem subcomando não há operação

Com required=True, python catalogo.py é uma entrada inválida: o argparse apresenta a mensagem de uso e informa que falta o argumento comando. O encaminhamento para as funções da aplicação será feito no próximo passo.

Passo 6 de 8

Encaminhar os argumentos ao caso de uso

Conecte a interface criada com argparse às funções da aplicação, mantendo cada responsabilidade separada.

Da interface à operação correta

Três responsabilidades separadas

A CLI fica mais simples de manter quando cada etapa tem uma responsabilidade:

  1. criar_parser() declara e devolve o parser.
  2. main() interpreta a chamada com parse_args().
  3. main() examina args.operacao e chama o caso de uso correspondente.

A construção do parser não deve buscar nem listar produtos. Ela apenas descreve a interface aceita.

Fluxo de encaminhamento

Os valores passam pela fronteira da CLI antes de chegar às funções da aplicação.

Diagrama mostrando tokens de um comando entrando em um parser, passando por uma decisão de operação e seguindo para uma de duas funções, busca ou listagem, antes de produzir resultados.

O Namespace é uma representação da interface: ele orienta a chamada, mas não precisa ser entregue ao caso de uso.

Script completo: parser, interpretação e despacho

Traduza atributos em parâmetros

Observe que as funções recebem parâmetros próprios, como termo, limite e ordem. Elas não recebem args. Assim, a interface de terminal não vira contrato interno dos casos de uso.

catalogo.py

Crie um arquivo com este conteúdo e execute os comandos sugeridos abaixo.

python
import argparse

CATALOGO = [
    {"titulo": "Python Fluente", "categoria": "programacao"},
    {"titulo": "Arquitetura Limpa", "categoria": "arquitetura"},
    {"titulo": "Python para Dados", "categoria": "dados"},
]


def buscar_catalogo(termo: str, limite: int, detalhes: bool) -> list[str]:
    termo_normalizado = termo.lower()
    encontrados = [
        item
        for item in CATALOGO
        if termo_normalizado in item["titulo"].lower()
    ]

    if detalhes:
        resultados = [
            f"{item['titulo']} — categoria: {item['categoria']}"
            for item in encontrados
        ]
    else:
        resultados = [item["titulo"] for item in encontrados]

    return resultados[:limite]


def listar_catalogo(ordem: str) -> list[str]:
    itens_ordenados = sorted(CATALOGO, key=lambda item: item[ordem])
    return [item["titulo"] for item in itens_ordenados]


def criar_parser() -> argparse.ArgumentParser:
    parser = argparse.ArgumentParser(description="Consulta um catálogo em memória.")
    subparsers = parser.add_subparsers(dest="operacao", required=True)

    buscar = subparsers.add_parser("buscar", help="Busca títulos no catálogo.")
    buscar.add_argument("termo", help="Termo presente no título.")
    buscar.add_argument("-l", "--limite", type=int, default=3, help="Máximo de resultados.")
    buscar.add_argument("-d", "--detalhes", action="store_true", help="Exibe a categoria.")

    listar = subparsers.add_parser("listar", help="Lista todos os títulos.")
    listar.add_argument(
        "--ordem",
        choices=["titulo", "categoria"],
        default="titulo",
        help="Campo usado para ordenar.",
    )

    return parser


def main() -> None:
    parser = criar_parser()
    args = parser.parse_args()

    if args.operacao == "buscar":
        resultados = buscar_catalogo(
            termo=args.termo,
            limite=args.limite,
            detalhes=args.detalhes,
        )
    elif args.operacao == "listar":
        resultados = listar_catalogo(ordem=args.ordem)

    for resultado in resultados:
        print(resultado)


if __name__ == "__main__":
    main()

Exemplo

Experimente localmente

No terminal, na pasta do arquivo:

python catalogo.py buscar Python --limite 2 --detalhes

A chamada seleciona buscar; por isso, somente termo, limite e detalhes são usados. Para a outra operação:

python catalogo.py listar --ordem categoria

Nesse caminho, main() acessa somente args.ordem e chama listar_catalogo().

Sequência de execução

Organize o fluxo

Coloque as etapas de uma execução da CLI na ordem correta.

  1. Exibir os resultados retornados
  2. Chamar apenas o caso de uso selecionado com parâmetros nomeados
  3. Interpretar a chamada com parse_args()
  4. Verificar qual valor está em args.operacao
  5. Construir o parser com criar_parser()

Escolha o encaminhamento adequado

Qual ramo trata a listagem?

Depois de interpretar os argumentos, qual trecho encaminha corretamente a operação listar?

Passo 7 de 8

Interpretar argumentos sem depender do terminal

Exercite o parser com listas explícitas e preserve a diferença entre os argumentos do processo e uma lista vazia.

Do terminal para uma lista

A mesma entrada, sem abrir o terminal

Além de ler os argumentos da execução, parse_args aceita uma lista explícita de strings. Isso permite conferir como a interface será interpretada diretamente no código.

A lista contém somente os argumentos da aplicação: não inclua python nem o nome do script. As aspas usadas no terminal também não entram na lista; elas apenas fazem o shell manter um texto com espaços como um único argumento.

Correspondência entre as duas formas

A chamada e a lista abaixo representam a mesma entrada para o parser.

Diagrama mostrando uma chamada de terminal dividida em tokens e a lista Python equivalente, onde o termo com espaços ocupa um único elemento.

"python avançado" vira o único elemento "python avançado" na lista.

Exemplo

Entrada equivalente

No terminal:

python catalogo.py buscar "python avançado" --limite 2 --detalhado

Na chamada de parse_args:

["buscar", "python avançado", "--limite", "2", "--detalhado"]

Embora "2" seja uma string na lista, o parser a converterá para int porque a opção --limite foi declarada com type=int.

Exercitar somente a interpretação

Separe interpretação de execução

Para verificar atributos, conversões, padrões e sinalizadores, chame parse_args com listas válidas e examine o Namespace. Assim, os assert abaixo não executam os casos de uso do catálogo.

A função main recebe o mesmo parâmetro opcional e o repassa diretamente a parse_args.

Parser, main e verificações locais

Salve como catalogo.py e execute com python catalogo.py para rodar apenas os assert.

python
import argparse


def criar_parser() -> argparse.ArgumentParser:
    parser = argparse.ArgumentParser(description="Consulta um catálogo em memória.")
    subcomandos = parser.add_subparsers(dest="comando", required=True)

    buscar = subcomandos.add_parser("buscar", help="Busca itens pelo termo informado.")
    buscar.add_argument("termo", help="Termo a procurar no catálogo.")

    listar = subcomandos.add_parser("listar", help="Lista os itens do catálogo.")

    for comando in (buscar, listar):
        comando.add_argument("-l", "--limite", type=int, default=10)
        comando.add_argument(
            "-o",
            "--ordenar",
            choices=("titulo", "id"),
            default="titulo",
        )
        comando.add_argument("-d", "--detalhado", action="store_true")

    return parser


def buscar_catalogo(termo: str, *, limite: int, ordenar: str, detalhado: bool) -> str:
    return f"Busca por {termo!r}: limite={limite}, ordenar={ordenar}, detalhado={detalhado}"


def listar_catalogo(*, limite: int, ordenar: str, detalhado: bool) -> str:
    return f"Listagem: limite={limite}, ordenar={ordenar}, detalhado={detalhado}"


def main(argv: list[str] | None = None) -> str:
    args = criar_parser().parse_args(argv)

    if args.comando == "buscar":
        return buscar_catalogo(
            args.termo,
            limite=args.limite,
            ordenar=args.ordenar,
            detalhado=args.detalhado,
        )

    return listar_catalogo(
        limite=args.limite,
        ordenar=args.ordenar,
        detalhado=args.detalhado,
    )


parser = criar_parser()

busca = parser.parse_args(
    ["buscar", "python avançado", "--limite", "2", "--detalhado"]
)
assert busca.comando == "buscar"
assert busca.termo == "python avançado"
assert busca.limite == 2
assert busca.detalhado is True

listagem = parser.parse_args(["listar"])
assert listagem.comando == "listar"
assert listagem.limite == 10
assert listagem.ordenar == "titulo"
assert listagem.detalhado is False

None não é uma lista vazia

Preserve o valor recebido

Em main(argv=None), os dois valores têm significados diferentes:

  • main() ou main(None): parse_args(None) usa os argumentos reais do processo.
  • main([]): parse_args([]) recebe uma ausência explícita de argumentos da aplicação.

Por isso, repasse argv diretamente. Não escreva parse_args(argv or None): como uma lista vazia é falsa em um teste lógico, essa expressão a trocaria indevidamente por None.

Preservação de argv

A expressão criar_parser().parse_args(argv or None) preserva corretamente a diferença entre argv=None e argv=[].

Dica

Checklist para a prática

Ao montar uma lista explícita, confira: inclua o subcomando; use uma string por argumento; mantenha valores com espaços em um único elemento; e não inclua python nem o nome do arquivo. Use listas válidas nos assert deste exercício.

Passo 8 de 8

Validar a CLI completa

Reúna subcomandos, opções, ajuda e listas explícitas em um script local e confira evidências observáveis da interface.

Um catálogo executável e autocontido

Monte o script local

Crie um arquivo chamado catalogo.py no seu computador. O script abaixo contém dados em memória, construção do parser, interpretação dos argumentos e encaminhamento para cada caso de uso. Ele não depende de instalação de pacote nem de arquivos anteriores.

catalogo.py

python
import argparse

CATALOGO = [
    {"id": 1, "titulo": "Python para Dados", "autor": "Ana Lima"},
    {"id": 2, "titulo": "Automatize com Python", "autor": "Bruno Reis"},
    {"id": 3, "titulo": "Algoritmos Práticos", "autor": "Carla Souza"},
]


def buscar(termo: str, limite: int, ordem: str, detalhes: bool) -> list[dict]:
    encontrados = [
        livro
        for livro in CATALOGO
        if termo.lower() in livro["titulo"].lower()
    ]
    encontrados.sort(key=lambda livro: livro[ordem])
    return encontrados[:limite]


def listar(ordem: str) -> list[dict]:
    return sorted(CATALOGO, key=lambda livro: livro[ordem])


def mostrar(livros: list[dict], detalhes: bool = False) -> None:
    for livro in livros:
        if detalhes:
            print(f"- #{livro['id']} | {livro['titulo']} | {livro['autor']}")
        else:
            print(f"- {livro['titulo']}")


def construir_parser() -> argparse.ArgumentParser:
    parser = argparse.ArgumentParser(
        description="Consulte o catálogo de livros em memória."
    )
    subparsers = parser.add_subparsers(
        dest="comando",
        required=True,
        help="operação a executar",
    )

    parser_buscar = subparsers.add_parser(
        "buscar",
        help="busca livros pelo título",
        description="Busca livros cujo título contém o termo informado.",
    )
    parser_buscar.add_argument("termo", help="termo presente no título")
    parser_buscar.add_argument(
        "-l",
        "--limit",
        type=int,
        default=2,
        help="quantidade máxima de resultados (padrão: 2)",
    )
    parser_buscar.add_argument(
        "-o",
        "--ordem",
        choices=["id", "titulo"],
        default="titulo",
        help="campo de ordenação: id ou titulo (padrão: titulo)",
    )
    parser_buscar.add_argument(
        "-d",
        "--detalhes",
        action="store_true",
        help="mostra identificador e autor",
    )

    parser_listar = subparsers.add_parser(
        "listar",
        help="lista todos os livros",
        description="Lista todos os livros do catálogo.",
    )
    parser_listar.add_argument(
        "-o",
        "--ordem",
        choices=["id", "titulo"],
        default="titulo",
        help="campo de ordenação: id ou titulo (padrão: titulo)",
    )

    return parser


def verificar_parser() -> None:
    parser = construir_parser()

    busca = parser.parse_args(
        ["buscar", "python", "--limit", "3", "--detalhes"]
    )
    assert busca.comando == "buscar"
    assert busca.termo == "python"
    assert busca.limit == 3
    assert busca.ordem == "titulo"
    assert busca.detalhes is True

    listagem = parser.parse_args(["listar"])
    assert listagem.comando == "listar"
    assert listagem.ordem == "titulo"
    assert not hasattr(listagem, "termo")
    assert not hasattr(listagem, "limit")


def main(argv: list[str] | None = None) -> None:
    parser = construir_parser()
    argumentos = parser.parse_args(argv)

    if argumentos.comando == "buscar":
        livros = buscar(
            termo=argumentos.termo,
            limite=argumentos.limit,
            ordem=argumentos.ordem,
            detalhes=argumentos.detalhes,
        )
        mostrar(livros, detalhes=argumentos.detalhes)
    elif argumentos.comando == "listar":
        livros = listar(ordem=argumentos.ordem)
        mostrar(livros)


if __name__ == "__main__":
    main()

Execute e confira a interface

Roteiro de conferência

No terminal aberto na pasta do arquivo, execute cada comando abaixo. A ajuda principal apresenta buscar e listar; a ajuda de buscar apresenta somente as entradas dessa operação. Ao omitir --limit, a busca usa o padrão 2. O sinalizador --detalhes altera a apresentação dos resultados.

Comandos e resultados esperados

bash
python catalogo.py --help
python catalogo.py buscar --help

python catalogo.py buscar python
# - Automatize com Python
# - Python para Dados

python catalogo.py buscar python --limit 1 --detalhes
# - #2 | Automatize com Python | Bruno Reis

python catalogo.py listar --ordem id
# - Python para Dados
# - Automatize com Python
# - Algoritmos Práticos

python catalogo.py buscar python --limit dois
python catalogo.py buscar python --ordem data
python catalogo.py

O que observar no terminal

A mesma estrutura de chamada produz saídas diferentes conforme o subcomando e as opções declaradas.

Diagrama de terminal comparando busca com padrão de dois resultados, busca detalhada limitada a um resultado e listagem ordenada por identificador.

Sem --limit, a busca mostra dois itens; com --detalhes, cada item inclui identificador e autor.

Atenção

Entradas recusadas também são evidência

Nos três últimos comandos, o parser deve recusar a chamada antes de executar a operação: dois não pode ser convertido para inteiro, data não pertence às alternativas e falta um subcomando. Confira a mensagem de uso e o diagnóstico exibidos no seu ambiente, sem depender do texto exato ou de códigos de saída.

Exercite a interpretação isoladamente

Verifique apenas o parser

A função verificar_parser() chama parse_args com listas explícitas. Portanto, ela confirma subcomando, conversão, padrão e sinalizador sem chamar buscar, listar ou mostrar. Execute-a em outro comando:

Rodar as verificações locais

bash
python -c "from catalogo import verificar_parser; verificar_parser(); print('Verificações concluídas')"

Dica

Leia as duas listas como argumentos da aplicação

Em ["buscar", "python", "--limit", "3", "--detalhes"], cada texto é um argumento que viria depois de catalogo.py no terminal. Aspas usadas pelo shell não entram na lista: se o termo fosse python para dados, ele seria um único elemento, "python para dados".

Registre sua evidência

Relate sua execução local: qual ajuda consultou, o resultado da busca com padrão, o efeito de --detalhes, uma entrada recusada e o que as listas explícitas confirmaram.

Escreva pelo menos 180 caracteres (0/180).

Síntese e aplicação final

Resumo

Uma CLI declarada, interpretada e encaminhada

Você fechou o ciclo de uma interface de terminal com argparse.

  • construir_parser() declara a interface: subcomandos, posicionais, opções, conversões, alternativas, padrões e ajuda.
  • parse_args(argv) interpreta a chamada do terminal ou uma lista explícita e devolve um Namespace apropriado ao subcomando.
  • main() seleciona o caso de uso e traduz atributos do Namespace em parâmetros nomeados, sem transformar o Namespace em contrato do domínio.
  • A ajuda e a recusa de entradas incompatíveis são comportamentos observáveis do parser; a política mais ampla de falhas será aprofundada depois.

CLI validada

Parabéns! Você concluiu: Criar comandos e opções com argparse

Concluído! Você consegue declarar uma CLI com subcomandos e opções, interpretar chamadas do terminal ou listas explícitas e encaminhar os valores ao caso de uso correto.

Baixe o Aplicativo agora para ter acesso a + de 5000 cursos gratuitos, exercícios, certificado e muito conteúdo sem pagar nada!

  • Cursos online 100% gratuitos do início ao fim

    Milhares de cursos online em vídeo, ebooks e áudiobooks.

  • Mais de 60 mil exercícios gratuitos

    Para testar seus conhecimentos no decorrer dos cursos online

  • Certificado Digital gratuito válido em todo o Brasil

    Gerado diretamente na galeria de fotos do seu celular e enviado ao seu e-mail

Aplicativo Cursa na tela de ebook, na tela de curso em vídeo e na tela de exercícios do curso, mais o certificado de conclusão de curso