cgitb — Informes detallados de rastreo¶
Propósito: | cgitb proporciona información de rastreo más detallada que traceback . |
---|
cgitb
es una valiosa herramienta de depuración en la biblioteca estándar.
Originalmente fue diseñada para mostrar errores e información de depuración en
aplicaciones web y luego se actualizó para incluir también la salida de texto
sin formato, pero desafortunadamente nunca fue renombrada. Esto ha llevado a
la oscuridad, y el módulo no se usa con tanta frecuencia como podría ser.
Impresión estándar de rastreos¶
El comportamiento de manejo de excepciones predeterminado de Python es imprimir un rastreo a la secuencia de salida de error estándar con la pila de llamadas que conduce a la posición de error. Este resultado básico con frecuencia contiene suficiente información para comprender la causa de la excepción y permitir una solución.
def func2(a, divisor):
return a / divisor
def func1(a, b):
c = b - 5
return func2(a, c)
func1(1, 5)
Este programa de muestra tiene un error sutil en func2()
.
$ python3 cgitb_basic_traceback.py
Traceback (most recent call last):
File "cgitb_basic_traceback.py", line 18, in <module>
func1(1, 5)
File "cgitb_basic_traceback.py", line 16, in func1
return func2(a, c)
File "cgitb_basic_traceback.py", line 11, in func2
return a / divisor
ZeroDivisionError: division by zero
Habilitación de rastreos detallados¶
Si bien el rastreo básico incluye suficiente información para detectar el
error, habilitar cgitb
brinda más detalles. cgitb
reemplaza a
sys.excepthook
con una función que proporciona rastreos de seguimiento
extendidos.
import cgitb
cgitb.enable(format='text')
El informe de error de este ejemplo es mucho más extenso que el original. Cada cuadro de la pila está listado, junto con:
- La ruta completa al archivo fuente, en lugar de solo el nombre base
- Los valores de los argumentos para cada función en la pila
- Algunas líneas de contexto de origen alrededor de la línea en la ruta de error
- Los valores de las variables en la expresión que causan el error
Tener acceso a las variables involucradas en la pila de errores puede ayudar a encontrar un error lógico que ocurra en algún lugar más alto en la pila que la línea donde se genera la excepción real.
$ python3 cgitb_local_vars.py
ZeroDivisionError
Python 3.6.4: .../bin/python3
Sun Mar 18 16:20:19 2018
A problem occurred in a Python script. Here is the sequence of
function calls leading up to the error, in the order they
occurred.
.../cgitb_local_vars.py in <module>()
18 def func1(a, b):
19 c = b - 5
20 return func2(a, c)
21
22 func1(1, 5)
func1 = <function func1>
.../cgitb_local_vars.py in func1(a=1, b=5)
18 def func1(a, b):
19 c = b - 5
20 return func2(a, c)
21
22 func1(1, 5)
global func2 = <function func2>
a = 1
c = 0
.../cgitb_local_vars.py in func2(a=1, divisor=0)
13
14 def func2(a, divisor):
15 return a / divisor
16
17
a = 1
divisor = 0
ZeroDivisionError: division by zero
__cause__ = None
__class__ = <class 'ZeroDivisionError'>
__context__ = None
__delattr__ = <method-wrapper '__delattr__' of
ZeroDivisionError object>
__dict__ = {}
__dir__ = <built-in method __dir__ of ZeroDivisionError
object>
__doc__ = 'Second argument to a division or modulo operation
was zero.'
__eq__ = <method-wrapper '__eq__' of ZeroDivisionError
object>
__format__ = <built-in method __format__ of
ZeroDivisionError object>
__ge__ = <method-wrapper '__ge__' of ZeroDivisionError
object>
__getattribute__ = <method-wrapper '__getattribute__' of
ZeroDivisionError object>
__gt__ = <method-wrapper '__gt__' of ZeroDivisionError
object>
__hash__ = <method-wrapper '__hash__' of ZeroDivisionError
object>
__init__ = <method-wrapper '__init__' of ZeroDivisionError
object>
__init_subclass__ = <built-in method __init_subclass__ of
type object>
__le__ = <method-wrapper '__le__' of ZeroDivisionError
object>
__lt__ = <method-wrapper '__lt__' of ZeroDivisionError
object>
__ne__ = <method-wrapper '__ne__' of ZeroDivisionError
object>
__new__ = <built-in method __new__ of type object>
__reduce__ = <built-in method __reduce__ of
ZeroDivisionError object>
__reduce_ex__ = <built-in method __reduce_ex__ of
ZeroDivisionError object>
__repr__ = <method-wrapper '__repr__' of ZeroDivisionError
object>
__setattr__ = <method-wrapper '__setattr__' of
ZeroDivisionError object>
__setstate__ = <built-in method __setstate__ of
ZeroDivisionError object>
__sizeof__ = <built-in method __sizeof__ of
ZeroDivisionError object>
__str__ = <method-wrapper '__str__' of ZeroDivisionError
object>
__subclasshook__ = <built-in method __subclasshook__ of type
object>
__suppress_context__ = False
__traceback__ = <traceback object>
args = ('division by zero',)
with_traceback = <built-in method with_traceback of
ZeroDivisionError object>
The above is a description of an error in a Python program.
Here is
the original traceback:
Traceback (most recent call last):
File "cgitb_local_vars.py", line 22, in <module>
func1(1, 5)
File "cgitb_local_vars.py", line 20, in func1
return func2(a, c)
File "cgitb_local_vars.py", line 15, in func2
return a / divisor
ZeroDivisionError: division by zero
En el caso de este código con un ZeroDivisionError
, es evidente que el
problema se introduce en el cálculo del valor de c
en func1()
, en lugar
de donde se usa el valor en func2()
.
El final de la salida también incluye los detalles completos del objeto de
excepción (en caso de que tenga atributos distintos a message
que serían
útiles para la depuración) y la forma original de un Impresión de rastreo.
Variables locales en rastreos¶
El código en cgitb
que examina las variables utilizadas en el marco de la
pila que conduce al error es lo suficientemente inteligente como para evaluar
los atributos del objeto para mostrarlos también.
import cgitb
cgitb.enable(format='text', context=12)
class BrokenClass:
"""This class has an error.
"""
def __init__(self, a, b):
"""Be careful passing arguments in here.
"""
self.a = a
self.b = b
self.c = self.a * self.b
# Really
# long
# comment
# goes
# here.
self.d = self.a / self.b
return
o = BrokenClass(1, 0)
Si una función o método incluye muchos comentarios en línea, espacios en blanco
u otro código que lo hace muy largo, entonces tener el valor predeterminado de
cinco líneas de contexto puede no proporcionar suficiente dirección. Cuando el
cuerpo de la función se expulsa de la ventana de código que se muestra, no hay
suficiente contexto para comprender la ubicación del error. El uso de un valor
de contexto más grande con cgitb
resuelve este problema. Al pasar un
número entero como argumento de context
a enable()
se controla la
cantidad de código que se muestra para cada línea del rastreo.
Esta salida muestra que self.a
y self.b
están involucrados en el código
propenso a errores.
$ python3 cgitb_with_classes.py
ZeroDivisionError
Python 3.6.4: .../bin/python3
Sun Mar 18 16:20:19 2018
A problem occurred in a Python script. Here is the sequence of
function calls leading up to the error, in the order they
occurred.
.../cgitb_with_classes.py in <module>()
21 self.a = a
22 self.b = b
23 self.c = self.a * self.b
24 # Really
25 # long
26 # comment
27 # goes
28 # here.
29 self.d = self.a / self.b
30 return
31
32 o = BrokenClass(1, 0)
o undefined
BrokenClass = <class '__main__.BrokenClass'>
.../cgitb_with_classes.py in
__init__(self=<__main__.BrokenClass object>, a=1, b=0)
21 self.a = a
22 self.b = b
23 self.c = self.a * self.b
24 # Really
25 # long
26 # comment
27 # goes
28 # here.
29 self.d = self.a / self.b
30 return
31
32 o = BrokenClass(1, 0)
self = <__main__.BrokenClass object>
self.d undefined
self.a = 1
self.b = 0
ZeroDivisionError: division by zero
__cause__ = None
__class__ = <class 'ZeroDivisionError'>
__context__ = None
__delattr__ = <method-wrapper '__delattr__' of
ZeroDivisionError object>
__dict__ = {}
__dir__ = <built-in method __dir__ of ZeroDivisionError
object>
__doc__ = 'Second argument to a division or modulo operation
was zero.'
__eq__ = <method-wrapper '__eq__' of ZeroDivisionError
object>
__format__ = <built-in method __format__ of
ZeroDivisionError object>
__ge__ = <method-wrapper '__ge__' of ZeroDivisionError
object>
__getattribute__ = <method-wrapper '__getattribute__' of
ZeroDivisionError object>
__gt__ = <method-wrapper '__gt__' of ZeroDivisionError
object>
__hash__ = <method-wrapper '__hash__' of ZeroDivisionError
object>
__init__ = <method-wrapper '__init__' of ZeroDivisionError
object>
__init_subclass__ = <built-in method __init_subclass__ of
type object>
__le__ = <method-wrapper '__le__' of ZeroDivisionError
object>
__lt__ = <method-wrapper '__lt__' of ZeroDivisionError
object>
__ne__ = <method-wrapper '__ne__' of ZeroDivisionError
object>
__new__ = <built-in method __new__ of type object>
__reduce__ = <built-in method __reduce__ of
ZeroDivisionError object>
__reduce_ex__ = <built-in method __reduce_ex__ of
ZeroDivisionError object>
__repr__ = <method-wrapper '__repr__' of ZeroDivisionError
object>
__setattr__ = <method-wrapper '__setattr__' of
ZeroDivisionError object>
__setstate__ = <built-in method __setstate__ of
ZeroDivisionError object>
__sizeof__ = <built-in method __sizeof__ of
ZeroDivisionError object>
__str__ = <method-wrapper '__str__' of ZeroDivisionError
object>
__subclasshook__ = <built-in method __subclasshook__ of type
object>
__suppress_context__ = False
__traceback__ = <traceback object>
args = ('division by zero',)
with_traceback = <built-in method with_traceback of
ZeroDivisionError object>
The above is a description of an error in a Python program.
Here is
the original traceback:
Traceback (most recent call last):
File "cgitb_with_classes.py", line 32, in <module>
o = BrokenClass(1, 0)
File "cgitb_with_classes.py", line 29, in __init__
self.d = self.a / self.b
ZeroDivisionError: division by zero
Propiedades de excepción¶
Además de las variables locales de cada marco de pila, cgitb
muestra todas
las propiedades del objeto de excepción. Las propiedades adicionales en los
tipos de excepción personalizados se imprimen como parte del informe de error.
import cgitb
cgitb.enable(format='text')
class MyException(Exception):
"""Add extra properties to a special exception
"""
def __init__(self, message, bad_value):
self.bad_value = bad_value
Exception.__init__(self, message)
return
raise MyException('Normal message', bad_value=99)
En este ejemplo, la propiedad bad_value
se incluye junto con los valores
estándar de message
y args
.
$ python3 cgitb_exception_properties.py
MyException
Python 3.6.4: .../bin/python3
Sun Mar 18 16:20:19 2018
A problem occurred in a Python script. Here is the sequence of
function calls leading up to the error, in the order they
occurred.
.../cgitb_exception_properties.py in <module>()
19 self.bad_value = bad_value
20 Exception.__init__(self, message)
21 return
22
23 raise MyException('Normal message', bad_value=99)
MyException = <class '__main__.MyException'>
bad_value undefined
MyException: Normal message
__cause__ = None
__class__ = <class '__main__.MyException'>
__context__ = None
__delattr__ = <method-wrapper '__delattr__' of MyException
object>
__dict__ = {'bad_value': 99}
__dir__ = <built-in method __dir__ of MyException object>
__doc__ = 'Add extra properties to a special exception\n
'
__eq__ = <method-wrapper '__eq__' of MyException object>
__format__ = <built-in method __format__ of MyException
object>
__ge__ = <method-wrapper '__ge__' of MyException object>
__getattribute__ = <method-wrapper '__getattribute__' of
MyException object>
__gt__ = <method-wrapper '__gt__' of MyException object>
__hash__ = <method-wrapper '__hash__' of MyException object>
__init__ = <bound method MyException.__init__ of
MyException('Normal message',)>
__init_subclass__ = <built-in method __init_subclass__ of
type object>
__le__ = <method-wrapper '__le__' of MyException object>
__lt__ = <method-wrapper '__lt__' of MyException object>
__module__ = '__main__'
__ne__ = <method-wrapper '__ne__' of MyException object>
__new__ = <built-in method __new__ of type object>
__reduce__ = <built-in method __reduce__ of MyException
object>
__reduce_ex__ = <built-in method __reduce_ex__ of
MyException object>
__repr__ = <method-wrapper '__repr__' of MyException object>
__setattr__ = <method-wrapper '__setattr__' of MyException
object>
__setstate__ = <built-in method __setstate__ of MyException
object>
__sizeof__ = <built-in method __sizeof__ of MyException
object>
__str__ = <method-wrapper '__str__' of MyException object>
__subclasshook__ = <built-in method __subclasshook__ of type
object>
__suppress_context__ = False
__traceback__ = <traceback object>
__weakref__ = None
args = ('Normal message',)
bad_value = 99
with_traceback = <built-in method with_traceback of
MyException object>
The above is a description of an error in a Python program.
Here is
the original traceback:
Traceback (most recent call last):
File "cgitb_exception_properties.py", line 23, in <module>
raise MyException('Normal message', bad_value=99)
MyException: Normal message
Salida HTML¶
Debido a que cgitb
se desarrolló originalmente para manejar excepciones en
aplicaciones web, ninguna discusión estaría completa sin mencionar su formato
de salida HTML original. Todos los ejemplos anteriores muestran la salida de
texto sin formato. Para producir HTML en su lugar, omite el argumento
format
(o especifica "html"
). La mayoría de las aplicaciones web
modernas se construyen utilizando un marco que incluye una función de informe
de errores, por lo que el formulario HTML es en gran parte obsoleto.
Registro de rastreos¶
Para muchas situaciones, la impresión de los detalles de rastreo al error
estándar es la mejor resolución. En un sistema de producción, sin embargo,
registrar los errores es aún mejor. La función enable()
incluye un
argumento opcional, logdir
, para habilitar el registro de errores. Cuando
se proporciona un nombre de directorio, cada excepción se registra en su propio
archivo en el directorio dado.
import cgitb
import os
LOGDIR = os.path.join(os.path.dirname(__file__), 'LOGS')
if not os.path.exists(LOGDIR):
os.makedirs(LOGDIR)
cgitb.enable(
logdir=LOGDIR,
display=False,
format='text',
)
def func(a, divisor):
return a / divisor
func(1, 0)
Aunque se suprime la pantalla de error, se imprime un mensaje que describe a dónde ir para encontrar el registro de errores.
$ python3 cgitb_log_exception.py
<p>A problem occurred in a Python script.
.../LOGS/tmpq7icvee3.txt contains the description of this error.
$ ls LOGS
tmpq7icvee3.txt
$ cat LOGS/*.txt
ZeroDivisionError
Python 3.6.4: .../bin/python3
Sun Mar 18 16:20:19 2018
A problem occurred in a Python script. Here is the sequence of
function calls leading up to the error, in the order they
occurred.
.../cgitb_log_exception.py in <module>()
24
25 def func(a, divisor):
26 return a / divisor
27
28 func(1, 0)
func = <function func>
.../cgitb_log_exception.py in func(a=1, divisor=0)
24
25 def func(a, divisor):
26 return a / divisor
27
28 func(1, 0)
a = 1
divisor = 0
ZeroDivisionError: division by zero
__cause__ = None
__class__ = <class 'ZeroDivisionError'>
__context__ = None
__delattr__ = <method-wrapper '__delattr__' of
ZeroDivisionError object>
__dict__ = {}
__dir__ = <built-in method __dir__ of ZeroDivisionError
object>
__doc__ = 'Second argument to a division or modulo operation
was zero.'
__eq__ = <method-wrapper '__eq__' of ZeroDivisionError
object>
__format__ = <built-in method __format__ of
ZeroDivisionError object>
__ge__ = <method-wrapper '__ge__' of ZeroDivisionError
object>
__getattribute__ = <method-wrapper '__getattribute__' of
ZeroDivisionError object>
__gt__ = <method-wrapper '__gt__' of ZeroDivisionError
object>
__hash__ = <method-wrapper '__hash__' of ZeroDivisionError
object>
__init__ = <method-wrapper '__init__' of ZeroDivisionError
object>
__init_subclass__ = <built-in method __init_subclass__ of
type object>
__le__ = <method-wrapper '__le__' of ZeroDivisionError
object>
__lt__ = <method-wrapper '__lt__' of ZeroDivisionError
object>
__ne__ = <method-wrapper '__ne__' of ZeroDivisionError
object>
__new__ = <built-in method __new__ of type object>
__reduce__ = <built-in method __reduce__ of
ZeroDivisionError object>
__reduce_ex__ = <built-in method __reduce_ex__ of
ZeroDivisionError object>
__repr__ = <method-wrapper '__repr__' of ZeroDivisionError
object>
__setattr__ = <method-wrapper '__setattr__' of
ZeroDivisionError object>
__setstate__ = <built-in method __setstate__ of
ZeroDivisionError object>
__sizeof__ = <built-in method __sizeof__ of
ZeroDivisionError object>
__str__ = <method-wrapper '__str__' of ZeroDivisionError
object>
__subclasshook__ = <built-in method __subclasshook__ of type
object>
__suppress_context__ = False
__traceback__ = <traceback object>
args = ('division by zero',)
with_traceback = <built-in method with_traceback of
ZeroDivisionError object>
The above is a description of an error in a Python program.
Here is
the original traceback:
Traceback (most recent call last):
File "cgitb_log_exception.py", line 28, in <module>
func(1, 0)
File "cgitb_log_exception.py", line 26, in func
return a / divisor
ZeroDivisionError: division by zero
Ver también
- Documentación de la biblioteca estándar para cgitb
traceback
– Módulo de biblioteca estándar para trabajar con rastreos.inspect
– El móduloinspect
incluye más funciones para examinar la pila.sys
– El módulosys
proporciona acceso al valor de excepción actual y al controladorexcepthook
invocado cuando se produce una excepción.- Módulo de rastreo mejorado – Discusión en la lista de correo de desarrollo de Python sobre mejoras en el módulo de rastreo y mejoras relacionadas que otros desarrolladores usan localmente.