Ir para o conteúdo

Erros

Como evitar erros de entrada de usuário

Voltando ao programa C2F:

import sys
C = float(sys.argv[1])
F = 5./9*C + 32
print F

Nosso programa para com uma mensagem de erro estranha quando não é passado nada como argumento de linha de comando:

Terminal> python c2f.py
Traceback (most recent call last):
  File "c2f.py", line 2, in ?
    C = float(sys.argv[1])
IndexError: list index out of range

Porque?

  1. O usuário esquece de escrever o argumento de linha de comando;
  2. sys.argv então possui apenas um elemento, sys.argv[0], que é o nome do programa c2f.py;
  3. Índice 1, em sys.argv[1], aponta para um elemento inexistente na lista sys.argv;
  4. Qualquer índice correspondente a um elemento inexistente leva a um IndexError.

Nós devemos manipular erros de entrada!

Como podemos ter controle, explicar o que está errado na entrada e parar o programa sem uma mensagem de erro estranha?

## Program c2f_ctrl.py

import sys
if len(sys.argv) < 2:
    print 'Você não passou nenhum argumento na linha de comando!'
    sys.exit(1)  ## abortando
F = 9.0*C/5 + 32
print '%gC is %.1fF' % (C, F)
Terminal> python c2f_ctrl.py
Você não passou nenhum argumento na linha de comando!

Exceções como uma alternativa para ramificações

Ao invés de testar se algo está errado, recuperar do erro e fazer o que era para ser feito é comum em python (e muitas outras linguagens) tentar fazer o que deveria ser feito e se falhar, recuperar do erro.

Esse princípio faz o uso do bloco try-except

try:
    <declarações que queremos executar>
except:
    <declarações para manipular erros>

Se algo vai errado no bloco try, o Python levanta uma exceção e a execução pula imediatamente para o bloco except.

O programa de conversão de temperatura com try-except

Tente ler C da linha de comando, se falhar, avise ao usuário e aborte a execução. Salve como c2f_try_except.py:

import sys
try:
    C = float(sys.argv[1])
except:
    print 'Você falhou ao passar um argumento de linha de comando!'
    sys.exit(1)  ## aborta
F = 9.0*C/5 + 32
print '%gC is %.1fF' % (C, F)

Execução:

Terminal> python c2f_try_except.py
Você falhou ao passar um argumento de linha de comando!

Terminal> python c2f_try_except.py 21C
Você falhou ao passar um argumento de linha de comando!

Um bom estilo de programação testa por exceções específicas

try:
    C = float(sys.argv[1])
except IndexError:
    print 'Você falhou ao passar um argumento de linha de comando!'

Se tivermos um índice fora dos limites em sys.argv uma exceção IndexError é levantada e a execução salta automaticamente para o bloco except.

Se qualquer outra exceção for levantada, o Python aborta a execução.

Terminal> python c2f_try_except.py 21C
Traceback (most recent call last):
  File "c2f_try_except.py", line 3, in <module>
    C = float(sys.argv[1])
ValueError: invalid literal for float(): 21C

Testando para as exceções IndexError e ValueError

import sys
try:
    C = float(sys.argv[1])
except IndexError:
    print 'Nenhum argumento na linha de comando para C!'
    sys.exit(1)  ## abort execution
except ValueError:
    print 'Graus Celsius devem ser um número, não "%s"' % sys.argv[1]
    sys.exit(1)

F = 9.0*C/5 + 32
print '%gC é %.1fF' % (C, F)

Execuções:

Terminal> python c2f_try_except.py
Nenhum argumento na linha de comando para C!

Terminal> python c2f_try_except.py 21C
Graus Celsius devem ser um número, não "21C"

O programador pode levantar exceções

Ao invés de simplesmente deixar o Python levantar exceções, nós podemos levantar a nossa própria e adaptar a mensagem do problema em mãos.

Dois exemplos:

  • pegando uma exceção, mas levantando uma nova exceção com uma mensagem de erro melhorada;
  • levantando uma exceção por causa de entrada de dados errada.

Sintaxe básica: raise ExceptionType(mensagem)

Exemplos no levantamento de exceções com mensagens melhores

def ler_C():
    try:
        C = float(sys.argv[1])
    except IndexError:
        ## levantando novamente, mas com explicação específica:
        raise IndexError(
          'Graus Celsius dever ser fornecidos na linha de comando')
    except ValueError:
        ## levantando novamente, mas com explicação específica:
        raise ValueError(
         'Graus Celsius devem ser um número, não "%s"' % sys.argv[1])

    ## C pode ser lido como corretamente como número, mas com valor errado:
    if C < -273.15:
        raise ValueError('C=%g não é um valor físico!' % C)
    return C

Chamando a função anterior e rodando o programa

try:
    C = ler_C()
    F = 9.0*C/5 + 32
    print '%gC é %.1fF' % (C, F)
except (IndexError, ValueError) as e:
    ## mostre a exceção e pare o programa
    print e
    sys.exit(1)

Execuções:

Terminal> c2f_try_except.py
Graus Celsius dever ser fornecidos na linha de comando

Terminal> c2f_try_except.py 21C
Graus Celsius devem ser um número, não "21C"

Terminal> c2f_try_except.py -500
C=-500 não é um valor físico!

Terminal> c2f_try_except.py 21
21C é 69.8F