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:
- Não ser possível criar o arquivo para escrita² ou
- 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é!
Pingback: Exceções em Python – parte 3 | giovannireisnunes