A barreira de erros
Quando algo dá errado (o arquivo de configuração não existe, um valor é inválido, o arquivo de saída não pode ser gravado), o Python levanta (lança) uma exceção. Se ninguém a trata, o programa para na hora e imprime um traceback.
O enunciado proíbe isso. O programa precisa tratar todos os erros: configuração inválida, arquivo não encontrado, sintaxe errada, parâmetros impossíveis. Ele nunca pode travar inesperadamente e precisa sempre dar uma mensagem de erro clara. Na avaliação, um programa que trava com uma exceção não tratada é considerado não funcional.
O main cumpre isso com um único try, a "barreira" por onde toda exceção do programa passa
antes de chegar ao usuário.
O que é um traceback
É a listagem que o Python imprime quando uma exceção chega ao topo do programa sem ser tratada.
Na raiz do projeto, os comandos abaixo criam um script de cinco linhas, sem_barreira.py, que lê
um arquivo de configuração que não existe sem barreira; depois rodam o script e o apagam:
cat > sem_barreira.py <<'EOF'
import sys
from amazeing.repositories.config_repository import FileConfigRepository
FileConfigRepository().load(sys.argv[1])
EOF
.venv/bin/python sem_barreira.py nao_existe.txt
rm sem_barreira.py
A saída, com o começo de cada caminho (a pasta do projeto) encurtado para .../:
Traceback (most recent call last):
File ".../sem_barreira.py", line 5, in <module>
FileConfigRepository().load(sys.argv[1])
~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^
File ".../amazeing/repositories/config_repository.py", line 30, in load
raise ConfigError(
f"configuration file not found: {path}") from None
amazeing.errors.ConfigError: configuration file not found: nao_existe.txt
O traceback lista as chamadas de função que levaram ao erro, da mais externa para a mais interna, com o arquivo e a linha de cada uma, e termina com o tipo da exceção e a mensagem. É útil para quem programa e está procurando um defeito. Para o usuário, é ruído: a única informação que importa está na última linha, misturada com nomes internos. E, na avaliação, um traceback quer dizer que o programa travou.
Com a barreira, o mesmo erro sai assim, e o programa termina com código 1 (a página 10 roda o programa pronto com esse arquivo):
Error: configuration file not found: nao_existe.txt
O try do main
try:
config = FileConfigRepository().load(argv[0])
generator = MazeGenerator(config.width, config.height, config.entry,
config.exit, config.perfect, config.seed)
generate = GenerateMaze(generator, FileMazeRepository(), config)
return Menu(generate, RenderService(), ViewState()).run()
except (AppError, MazeError) as error:
print(f"Error: {error}", file=sys.stderr)
return EXIT_ERROR
except (KeyboardInterrupt, EOFError):
# Keeps the shell prompt off the "Choice? (1-4): " line.
print()
return EXIT_OK
except Exception as error:
print(f"Error: unexpected: {type(error).__name__}: {error}",
file=sys.stderr)
return EXIT_ERROR
Como o try funciona:
- o Python roda o bloco do
try; se nada for levantado, osexceptsão ignorados; - se uma exceção for levantada em qualquer ponto do bloco, inclusive dentro das funções que
ele chama (a leitura do config, o gerador, o laço inteiro do menu), o Python interrompe o bloco
e procura o primeiro
exceptcujo tipo combina com a exceção; except (A, B)pega uma exceção do tipoAou do tipoB(ou de uma subclasse delas);as errorguarda a exceção na variávelerror, e{error}numa f-string mostra a mensagem dela.
Por que essa decisão fica só no main, e quais erros ele já conhece, estão na
próxima página.