Exceções em Python – parte 2

excecoes_em_python-2_abertura

Seguindo com o tratamento de exceções em Python, a primeira parte apresentou a sintaxe básica para a captura das exceções dentro do programa — o bloco try-except — e como tratá-las tanto do modo global (todas as exceções) como também de um jeito mais específico.

Nesta parte, os demais comandos utilizados para o tratamento das exceções, como recuperar detalhes sobre elas e, claro, como forçar (ou simular) a ocorrência delas dentro do programa.

Outras opções para tratar as exceções

Além da dupla try e except há em Python outros comandos que podem ser utilizados para ajudar no tratamento das exceções. Um deles é o finally, que permite definir um bloco de comandos que sempre serão executados após o tratamento do bloco try-except (independente de ter sido gerada, ou não, uma exceção) e o outro é o else¹ que define um bloco de comandos para o caso da execução do bloco do try não gerar exceção.

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import print_function
from random import randint
from sys import argv

nome_do_arquivo = argv[1]
dados = ''.join([ chr(randint(32, 126)) for i in range(65536) ])

try:
    status = 0
    with open(nome_do_arquivo, 'w') as f:
    f.write(dados)
except IOError:
    print('Impossível escrever os dados em `{}`.'
          .format(nome_do_arquivo))
    status = 2
else:
    print('Arquivo salvo com sucesso!')
finally:
    if status > 0: exit(status)

print('Faz outras coisas e depois termina.')

Neste caso, não sendo possível criar o arquivo para escrita ou gravar os dados dentro dele a exceção gerada será capturada, uma mensagem será impressainformando o ocorrido (except), a variável ‘status’ receberá o valor 2, ao passar pelo bloco do finally o estado da operação será verificado e o programa encerrado.

$ python3 salva_arquivo-a.py /bin/arquivo.txt
Impossível escrever os dados em `/bin/arquivo.txt`.
$ echo $?
2

Mas se o arquivo for salvo corretamente, o ‘status’ terá valor 0 (o valor definido no começo do bloco do try) e a execução prosseguirá normalmente até o final do programa.

$ python3 salva_arquivo-a.py /tmp/arquivo.txt
Arquivo salvo com sucesso!
Faz outras coisas e depois termina.
$ echo $?
0

(¹) Que não é exclusivo do tratamento de exceções podendo ser usado em conjunto com o for, while e, claro, o próprio if.

Detalhes da exceção

No exemplo acima citei que a exceção seria capturada em dois casos:

  1. Não ser possível criar o arquivo para escrita² ou
  2. De erro na gravação de dados para o arquivo.

Mas como distinguir a ocorrência? É possível dentro do bloco do except receber os detalhes sobre “o que aconteceu” e assim saber melhor o que foi que aconteceu.

    ...
    except IOError as err:
        if 'Permission denied' in str(err):
            print('Você não pode criar arquivos aí!')
            status = 1
        else:
            print('Impossível escrever os dados em `{}`'
                  .format(nome_do_arquivo))
            status = 2
    ...

Para acessar os detalhes da exceção dentro do bloco do except você usa:

except «Nome da Exceção» as «variável»:

No caso do exemplo acima é verificado se a sequência “Permission denied” está contida em ‘err’ — como ela entra no bloco como um objeto, é necessário atribuí-la a outra variável ou então convertê-la para string antes de fazer a comparação. Em caso afirmativo será impressa a mensagem informando que o arquivo não pode ser criado no local que foi indicado, senão será impressa a mensagem anterior sobre a impossibilidade de gravar os dados no arquivo.

$ python3 ./salva_arquivo-b.py /bin/arquivo.txt 
Impossível escrever os dados em `/bin/arquivo.txt`
$ echo $?
1

(²) É possível também usar a exceção PermissionError para este caso mas ai não seria possível mostrar como detalhar a exceção… 🙂

Forçando uma exceção

Em algumas situações pode ser necessário forçar uma exceção, isto é feito com o comando raise e com duas sintaxes possíveis. A primeira é:

raise «Nome da Exceção»

Por exemplo:

Python 3.6.5 (default, Apr 1 2018, 05:46:30) 
[GCC 7.3.0] on linux
>>> raise ValueError
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
ValueError

Que para todos os efeitos equivale a ocorrência de uma exceção e será tratada como tal caso esteja dentro de um bloco try-except ou a execução será terminada em caso contrário.

Para melhor exemplificá-la, uma pequena ajuda do resiliente Cavaleiro Negro³… 😀

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import print_function
from time import sleep
import CavaleiroNegro

print("\x1b[3J{}\x1b[2;1H".format(CavaleiroNegro.imprime()))

for mensa in ["Ninguém passará! ", "Então você morrerá! ",
              "Não me afasto para ninguém. "]:
    sleep(.125)
    print(mensa)

fala = 0
while True:
    try:
        sleep(.25)
        if   fala == 0: raise NameError
        elif fala == 1: raise TypeError
        elif fala == 2: raise ValueError
        elif fala == 3: raise ZeroDivisionError

    except NameError:
        print("Foi apenas um arranhão. ")
    except TypeError:
        print("Vamos, seu maricas! ")
    except ValueError:
        print("Oh, está amarelando, hem? ")
    except ZeroDivisionError:
        if fala < 12: print("Covarde! Covarde! ")
    except KeyboardInterrupt:
        print("\nOk, considero um empate! ")
        break
    else:
        print("Sou invencível! ")
    finally:
        fala += 1

print("\x1b[24;1H",end="")
exit(0)

Este programa intencionalmente provoca diversas exceções de acordo com o valor da variável ‘fala’ e correspondem à algumas das frases ditas pelo Cavaleiro Negro e o bloco finally se encarrega de incrementar esta variável.

A única exceção que não é produzida pelo programa é a KeyboardInterrupt que justamente permitirá interromper do programa através da combinação das teclas «Control»+«C».

$ python3 cavaleiro_negro.py 
Ninguém passará! 
Então você morrerá! 
Não me afasto para ninguém. 
Foi apenas um arranhão. 
Vamos, seu maricas! 
Oh, está amarelando, hem? 
Covarde! Covarde! 
Sou invencível! 
Sou invencível! 
^C
Ok, considero um empate!

E, claro, a outra forma de utilizar o raise é simplesmente sem a especificação de uma exceção e que fará com que ele invoque a última exceção ocorrida dentro do programa.

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import print_function
from time import sleep

try:
    print('Aguarde 5 segundos para a exceção...')
    sleep(5)
    raise ValueError
except:
    print('Ocorreu uma exceção!')
    raise
    print('Esta mensagem não será impressa.')

Sua função é a de ser iutilizada dentro do bloco except para, por exemplo, realizar alguns procedimentos antes de deixar o Python seguir com o tratamento da exceção.

$ python3 tratando_o_raise.py 
Aguarde 5 segundos para a exceção...
Ocorreu uma exceção:
Traceback (most recent call last):
 File "tratando_o_raise.py", line 9, in <module>
 raise ValueError
ValueError

Como o raise dentro do bloco except será tratado efetivamente como um erro no programa a segunda mensagem nunca será impressa.

Claro que agora é um bom momento para dizer que é possível aninhar blocos try-except colocando um dentro do outro e fazer com que as exceções ocorridas em um bloco “filho” possam ser tratadas por seu “pai” através do uso do raise.

Fim desta parte

Por enquanto é isto, os arquivos usados nesta parte também estão no repositório Git deste blog e, para a próxima parte, como criar suas próprias exceções para utilizar dentro dos programas.

Até!

Um comentário sobre “Exceções em Python – parte 2

  1. Pingback: Exceções em Python – parte 3 | giovannireisnunes

Os comentários estão desativados.