MapeandoErrosParaExcecoes

Receita: Mapeando Números de Erro (errno) para exceções

Há casos em que é interessante ter uma hieraraquia de exceções para vários tipos de erros relacionados que podem ocorrer quando lidamos com chamadas do sistema operacional. Podemos mapear esses erros diretamente para exceções específicas usando uma classe especialmente projetada para isso. Aqui temos um exemplo de uma hierarquia de exceções e uma classe que automaticamente mapeia erros de sockets para suas respectivas exceções:

Código

   1 class Error(Exception):
   2     """Exceção base da hierarquia"""
   3 
   4 class SocketError(Error):
   5     """Um erro ocorreu com um socket"""
   6 
   7 class ConnectionError(SocketError):
   8     """Um erro ocorreu com a conexão"""
   9 
  10 class ClientClosed(ConnectionError):
  11     """-1"""
  12 
  13 class AddressAlreadyInUse(ConnectionError):
  14     """errno: 98"""
  15 
  16 class AddressNotAvailable(ConnectionError):
  17     """errno: 99"""
  18 
  19 class NetworkIsDown(ConnectionError):
  20     """errno: 100"""
  21 
  22 class NetworkUnreachable(ConnectionError):
  23     """errno: 101"""
  24 
  25 class NetworkReset(ConnectionError):
  26     """errno: 102"""
  27 
  28 class ConnectionAborted(ConnectionError):
  29     """errno: 103"""
  30 
  31 class ConnectionResetByPeer(ConnectionError):
  32     """errno: 104"""
  33 
  34 class NoBufferSpaceAvailable(ConnectionError):
  35     """errno: 105"""
  36 
  37 class SocketIsConnected(ConnectionError):
  38     """errno: 106"""
  39 
  40 class NotConnected(ConnectionError):
  41     """errno: 107"""
  42 
  43 class TCPTimeOut(ConnectionError):
  44     """errno: 110"""
  45 
  46 class ConnectionRefused(ConnectionError):
  47     """errno: 111"""
  48 
  49 class HostIsDown(ConnectionError):
  50     """errno: 112"""
  51 
  52 class HostUnreachable(ConnectionError):
  53     """errno: 113"""
  54 
  55 class ConnectionAlreadyInProgress(ConnectionError):
  56     """errno: 114"""
  57 
  58 # Mapa dos códigos de erro para as exceções:
  59 
  60 _connErrorMap = {-1:ClientClosed,
  61                  98:AddressAlreadyInUse,
  62                  99:AddressNotAvailable,
  63                  100:NetworkIsDown,
  64                  101:NetworkUnreachable,
  65                  102:NetworkReset,
  66                  103:ConnectionAborted,
  67                  104:ConnectionResetByPeer,
  68                  105:NoBufferSpaceAvailable,
  69                  106:SocketIsConnected,
  70                  107:NotConnected,
  71                  110:TCPTimeOut,
  72                  111:ConnectionRefused,
  73                  112:HostIsDown,
  74                  113:HostUnreachable,
  75                  114:ConnectionAlreadyInProgress}
  76 
  77 # Classe especial que mapeia os códigos para as exceções:
  78 
  79 class MapSocketError(ConnectionError):
  80     def __init__(self, reason):
  81         # reason é uma tupla ou lista contendo (código de erro,
  82         # mensagem de erro)
  83         code, msg = reason
  84         # caso o erro não exista, ConnectionError é usada
  85         exc = _connErrorMap.get(code, ConnectionError)
  86         self.__class__ = exc
  87         ConnectionError.__init__(self)
  88 
  89 
  90 #Testando o funcionamento da classe:
  91 
  92 if __name__ == "__main__":
  93 
  94     for code, exc in _connErrorMap.items():
  95         try:
  96             raise MapSocketError((code, ""))
  97         except exc:
  98             print "OK - ", exc
  99         except:
 100             print "FALHOU - ", exc

Exemplo de uso

Aqui temos um exemplo de uso num objeto socket usado em um servidor. Não precisamos nos preocupar com qual exceção devemos levantar em cada erro. Basta passar o código de erro e a classe faz o resto.

   1 import socket
   2 from Error import MapSocketError
   3 
   4 class ServerSocket:
   5 
   6     def __init__(self, addr):
   7         self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)        
   8         self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
   9         self.fileno = self.socket.fileno
  10 
  11     def bind(self, host, port):
  12         try:
  13             self.socket.bind((host, port))
  14             self.addr = (host, port)
  15         except socket.error, reason:
  16             raise MapSocketError(reason.args)
  17 
  18     def listen(self, backlog=1):
  19         try:
  20             self.socket.listen(backlog)
  21         except socket.error, reason:
  22             raise MapSocketError(reason.args)
  23 
  24     def accept(self):
  25         try:
  26             sock, addr = self.socket.accept()
  27             return sock, addr[0], addr[1]
  28         except socket.error, reason:
  29             raise MapSocketError(reason.args)

Depois, na utilização da classe ServerSocket, podemos capturar as exceções individualmente, de acordo com o código de erro, ou podemos usar a classe base da hierarquia, ConnectionError.


Volta para CookBook.


PedroWerneck

MapeandoErrosParaExcecoes (editada pela última vez em 2008-09-26 14:07:42 por localhost)