que - Python: ¿Cómo puedo saber qué excepciones se pueden lanzar desde una llamada a un método?
que es try en python (7)
¿Hay alguna forma de saber (en el momento de la codificación) qué excepciones esperar al ejecutar el código python? Terminé atrapando la clase Excepción base el 90% del tiempo, ya que no sé qué tipo de excepción podría arrojarse (y no me pidas que lea la documentación. Muchas veces se puede propagar una excepción desde la profundidad. veces que la documentación no está actualizada o es correcta). ¿Hay algún tipo de herramienta para verificar esto? (como leyendo el código python y libs)?
Hay dos formas que encuentro informativas. El primero, ejecuta las instrucciones en iPython, que mostrará el tipo de excepción.
n = 2
str = ''me ''
str + 2
TypeError: unsupported operand type(s) for +: ''int'' and ''str''
En la segunda forma, nos conformamos con atrapar demasiado y mejorarlo. Incluya una expresión try en su código y capture, excepto Exception as err . Imprima datos suficientes para saber qué excepción se produjo. A medida que se lanzan excepciones, mejore su código agregando una cláusula except más precisa. Cuando sienta que ha guardado en caché todas las excepciones relevantes, elimine el todo incluido. Algo bueno que hacer de todos modos porque absorbe los errores de programación.
try:
so something
except Exception as err:
print "Some message"
print err.__class__
print err
exit(1)
La herramienta correcta para resolver este problema es unittest. Si tiene excepciones planteadas por código real que los unittest no aumentan, entonces necesita más unittest.
Considera esto
def f(duck):
try:
duck.quack()
except ??? could be anything
pato puede ser cualquier objeto
Obviamente, puede tener un AttributeError
si el pato no tiene cuack, un TypeError
si el pato tiene un curandero pero no se puede llamar. No tienes idea de lo que podría plantear duck.quack()
, tal vez incluso un DuckError
o algo
Ahora suponiendo que tienes un código como este
arr[i] = get_something_from_database()
Si genera un IndexError
, no se sabe si proviene de arr [i] o de lo más profundo de la función de la base de datos. Por lo general, no importa tanto dónde ocurrió la excepción, sino que algo salió mal y no sucedió lo que quería que sucediera.
Una técnica útil es atrapar y quizás volver a publicar la excepción como esta
except Exception as e
#inspect e, decide what to do
raise
Me encontré con esto cuando uso socket, quería averiguar todas las condiciones de error en las que me iba a encontrar (así que en vez de tratar de crear errores y averiguar qué socket solo quería una lista concisa). Finalmente terminé grep''ing "/usr/lib64/python2.4/test/test_socket.py" para "subir":
$ grep raise test_socket.py
Any exceptions raised by the clients during their tests
raise TypeError, "test_func must be a callable function"
raise NotImplementedError, "clientSetUp must be implemented."
def raise_error(*args, **kwargs):
raise socket.error
def raise_herror(*args, **kwargs):
raise socket.herror
def raise_gaierror(*args, **kwargs):
raise socket.gaierror
self.failUnlessRaises(socket.error, raise_error,
self.failUnlessRaises(socket.error, raise_herror,
self.failUnlessRaises(socket.error, raise_gaierror,
raise socket.error
# Check that setting it to an invalid value raises ValueError
# Check that setting it to an invalid type raises TypeError
def raise_timeout(*args, **kwargs):
self.failUnlessRaises(socket.timeout, raise_timeout,
def raise_timeout(*args, **kwargs):
self.failUnlessRaises(socket.timeout, raise_timeout,
Que es una lista bastante concisa de errores. Ahora, por supuesto, esto solo funciona caso por caso y depende de que las pruebas sean precisas (que generalmente son). De lo contrario, es necesario capturar todas las excepciones, registrarlas y diseccionarlas y descubrir cómo manejarlas (lo que con las pruebas unitarias no sería demasiado difícil).
Nadie explicó hasta ahora, por qué no puedes tener una lista completa y 100% correcta de excepciones, así que pensé que vale la pena comentar. Una de las razones es una función de primera clase. Digamos que tienes una función como esta:
def apl(f,arg):
return f(arg)
Ahora apl
puede plantear cualquier excepción que aumente. Si bien no hay muchas funciones como esa en la biblioteca principal, todo lo que utiliza la comprensión de listas con filtros personalizados, mapas, reducir, etc. se ve afectado.
La documentación y los analizadores de origen son las únicas fuentes de información "serias" aquí. Solo tenga en cuenta lo que no pueden hacer.
Solo debe capturar las excepciones que manejará.
Capturar todas las excepciones por sus tipos concretos es una tontería. Debería detectar excepciones específicas que puede y manejará. Para otras excepciones, puede escribir una captura genérica que capte "Excepción base", lo registre (use la función str()
) y finalice su programa (o haga otra cosa que sea apropiada en una situación crashy).
Si realmente va a manejar todas las excepciones y está seguro de que ninguna de ellas es fatal (por ejemplo, si está ejecutando el código en algún tipo de entorno de espacio aislado), entonces su enfoque de capturar BaseException genérico se ajusta a sus objetivos.
Es posible que también le interese la referencia de excepción de idioma , no una referencia para la biblioteca que está utilizando.
Si la referencia de la biblioteca es realmente pobre y no vuelve a lanzar sus propias excepciones al detectar las del sistema, el único enfoque útil es ejecutar pruebas (tal vez agregarlo al banco de pruebas, porque si algo no está documentado, ¡puede cambiar!) . Elimine un archivo crucial para su código y verifique qué excepción se está lanzando. Suministre demasiados datos y compruebe qué error produce.
Deberá ejecutar pruebas de todos modos, ya que, incluso si existiera el método para obtener las excepciones por código fuente, no le daría ninguna idea de cómo debe manejar cualquiera de ellas . Tal vez debería mostrar el mensaje de error "¡No se encuentra el archivo needful.txt!" cuando capturas IndexError
? Solo la prueba puede decir.
Supongo que una solución solo podría ser imprecisa debido a la falta de reglas de tipado estáticas.
No conozco ninguna herramienta que verifique las excepciones, pero podría encontrar su propia herramienta que coincida con sus necesidades (una buena oportunidad para jugar un poco con el análisis estático).
Como primer intento, podría escribir una función que genere un AST, encuentre todos los nodos Raise
, y luego trate de descubrir patrones comunes para elevar excepciones (por ejemplo, llamar directamente a un constructor)
Deje x
ser el siguiente programa:
x = ''''''/
if f(x):
raise IOError(errno.ENOENT, ''not found'')
else:
e = g(x)
raise e
''''''
Construya el AST usando el paquete del compiler
:
tree = compiler.parse(x)
Luego defina una clase de visitante de aumento:
class RaiseVisitor(object):
def __init__(self):
self.nodes = []
def visitRaise(self, n):
self.nodes.append(n)
Y recorra el AST recolectando nodos Raise
:
v = RaiseVisitor()
compiler.walk(tree, v)
>>> print v.nodes
[
Raise(
CallFunc(
Name(''IOError''),
[Getattr(Name(''errno''), ''ENOENT''), Const(''not found'')],
None, None),
None, None),
Raise(Name(''e''), None, None),
]
Puede continuar resolviendo símbolos usando tablas de símbolos del compilador, analizando dependencias de datos, etc. O puede deducir que CallFunc(Name(''IOError''), ...)
"definitivamente debería significar aumentar IOError
", lo cual está bastante bien para Resultados prácticos rápidos :)
normalmente, necesitaría detectar excepciones solo alrededor de unas pocas líneas de código. No querrás poner toda tu función main
en la cláusula try except
. por cada pocas líneas que siempre debería (o puede verificar fácilmente) qué tipo de excepción podría surgir.
los documentos tienen una lista exhaustiva de excepciones incorporadas . no intente exceptuar aquellas excepciones que no espera, pueden ser manejadas / esperadas en el código de llamada.
editar : ¡lo que se arroje depende obviamente de lo que estás haciendo! acceder al elemento aleatorio de una secuencia: IndexError
, elemento aleatorio de un dict: KeyError
, etc.
Solo intente ejecutar esas pocas líneas en IDLE y cause una excepción. Pero la prueba de unidad sería una mejor solución, naturalmente.