Manejo de excepciones¶
sys
incluye características para atrapar y trabajar con excepciones.
Excepciones no manejadas¶
Muchas aplicaciones están estructuradas con un bucle principal que envuelve la
ejecución en un controlador de excepción global para atrapar errores que no se
manejan en un nivel inferior. Otra forma de lograr lo mismo es estableciendo
sys.excepthook
en una función que tome tres argumentos (el tipo de error,
el valor de error y el rastreo) y permita que se ocupe de los errores no
controlados.
import sys
def my_excepthook(type, value, traceback):
print('Unhandled error:', type, value)
sys.excepthook = my_excepthook
print('Before exception')
raise RuntimeError('This is the error message')
print('After exception')
Dado que no hay un bloque try:except
alrededor de la línea donde se genera
la excepción, la siguiente llamada a print()
no se ejecuta, aunque el
gancho except esté establecido.
$ python3 sys_excepthook.py
Before exception
Unhandled error: <class 'RuntimeError'> This is the error
message
Excepción Actual¶
Hay momentos en los que se prefiere un controlador de excepción explícito, ya
sea por claridad de código o para evitar conflictos con las bibliotecas que
intentan instalar su propio excepthook
. En estos casos, se puede crear una
función de controlador común que no necesita que se le pase el objeto de
excepción explícitamente llamando a exc_info()
para recuperar la excepción
actual para un hilo.
El valor de retorno de exc_info()
es una tupla de tres miembros que
contiene la clase de excepción, una instancia de excepción y un rastreo. Se
prefiere usar exc_info()
sobre la forma anterior (con exc_type
,
exc_value
y exc_traceback
) porque es seguro para subprocesos.
import sys
import threading
import time
def do_something_with_exception():
exc_type, exc_value = sys.exc_info()[:2]
print('Handling {} exception with message "{}" in {}'.format(
exc_type.__name__, exc_value,
threading.current_thread().name))
def cause_exception(delay):
time.sleep(delay)
raise RuntimeError('This is the error message')
def thread_target(delay):
try:
cause_exception(delay)
except RuntimeError:
do_something_with_exception()
threads = [
threading.Thread(target=thread_target, args=(0.3,)),
threading.Thread(target=thread_target, args=(0.1,)),
]
for t in threads:
t.start()
for t in threads:
t.join()
Este ejemplo evita la introducción de una referencia circular entre el objeto
de rastreo y una variable local en el marco actual al ignorar esa parte del
valor de retorno de exc_info()
. Si se necesita el rastreo (por ejemplo,
para que se pueda registrar), elimina explícitamente la variable local (usando
del
) para evitar ciclos.
$ python3 sys_exc_info.py
Handling RuntimeError exception with message "This is the error
message" in Thread-2
Handling RuntimeError exception with message "This is the error
message" in Thread-1
Excepción interactiva anterior¶
En el intérprete interactivo, solo hay un hilo de interacción. Las excepciones
no controladas en ese hilo se guardan en tres variables en sys
(last_type
, last_value
y last_traceback
) para que sea fácil
recuperarlas para la depuración. El uso del depurador post mortem en
pdb
evita cualquier necesidad de usar los valores directamente.
$ python3
Python 3.4.2 (v3.4.2:ab2c023a9432, Oct 5 2014, 20:42:22)
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> def cause_exception():
... raise RuntimeError('This is the error message')
...
>>> cause_exception()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in cause_exception
RuntimeError: This is the error message
>>> import pdb
>>> pdb.pm()
> <stdin>(2)cause_exception()
(Pdb) where
<stdin>(1)<module>()
> <stdin>(2)cause_exception()
(Pdb)