Categorias
Artigos

Gerando uma exceção em Python com um nome significativo, e uma mensagem significativa

Aqui está uma tarefa que é para ser simples mas que considero mal documentada no mundo Python.

O problema: estou desenvolvendo uma biblioteca para simular uma determinada topologia de ímãs permanentes. O usuário deve entrar com alguns parâmetros geométricos, mas antes de qualquer coisa o programa deve verificá-los e avisar quais são inválidos.

Em termos de programação, estamos falando de exceções: condições literalmente excepcionais que não devem acontecer mas, se acontecem, é melhor parar o programa para que o usuário possa verificar. O tutorial oficial de Python explica como criar exceções personalizadas, e a documentação de referência mostra os tipos de exceções que já existem em Python. Este tópico também é bem abordado em [1].

Pesquisando a documentação sobre as exceções já construídas, parece-me que a exceção aproximada que descreve esse problema é ValueError. O que eu quero então é definir um erro que diga que o “valor errado” é precisamente um “parâmetro inválido”. O problema na documentação que citei antes é precisamente que eu não estava conseguindo entender como a mensagem de erro pode ser customizada para indicar qual parâmetro é inválido, pois as exceções aparentemente não aceitam strings como argumentos…

…mas exceções aceitam uma tupla como argumento, e é isso que não estava claro para mim. Veja o código a seguir:

class InvalidParameterError(ValueError):
pass
view raw custom_errors.py hosted with ❤ by GitHub
from custom_errors import InvalidParameterError
def validate(p):
for k, v in p.items():
if v < 0:
emsg = "Parameter '%s' cannot be negative" %(k,)
raise InvalidParameterError(emsg)
view raw parse_parameters.py hosted with ❤ by GitHub
import pytest
from custom_error import InvalidParameterError
from parse_parameters import validate
def test_negative_parameters_raise_error():
sample_params = {
'a': 1.43,
'b': 57,
'c': -10
}
with pytest.raises(InvalidParameterError) as excinfo:
validate(sample_params)
# The object returned by this pytest context manager
# has a 'value' field assigned to the exception object itself.
# In addition, all built-in exceptions derived from Exception
# have an 'args' tuple field assigned to the passed arguments.
# Hence, when we create a custom error with a string as the first argument,
# this string is assigned to the first element of the tuple 'args'
exception_msg = excinfo.value.args[0]
expected_message = "Parameter 'c' cannot be negative".
assert exception_msg == expected_message

No primeiro programa, eu defino uma exceção que deriva de ValueError. No segundo, crio uma função de validação, que cria essa minha exceção personalizada, e a constrói com uma string de mensagem indicando qual foi o problema. E posso testar essa função usando pytest, conforme está nos comentários do terceiro programa: tudo que eu passei para InvalidParameterErrorfoi armazenado internamente como uma tupla args; se só passei uma string, esta foi armazenada no primeiro elemento, ou seja, em args[0]. E posso testar se a mensagem criada era a que eu estava esperando.

A minha biblioteca de ímãs permanentes é mais complexa que isto, mas a lógica é idêntica: se alguém tentar usá-la com um parâmetro inválido, o programa vai fazer uma verificação e imprimir uma mensagem de InvalidParameterError na tela.

É como falei no início: era para ser simples, mas no meio do caminho me diverti muito aprendendo sobre exceções em Python e escrevendo sobre elas. E agora vou voltar a realmente trabalhar na minha biblioteca, e estou muito mais seguro de que ela vai funcionar como deveria.

[1]: Beazley, David & Jones, Brian K. Python Cookbook. São Paulo: Novatec, 2013 (link afiliado para compra)

Por Fábio Fortkamp

Pai do João Pedro, Marido da Maria Elisa, Professor do Departamento de Engenharia Mecânica da Universidade do Estado de Santa Catarina, católico devoto, nerd

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair /  Alterar )

Foto do Google

Você está comentando utilizando sua conta Google. Sair /  Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair /  Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair /  Alterar )

Conectando a %s

Este site utiliza o Akismet para reduzir spam. Saiba como seus dados em comentários são processados.