2.41. Depuración¶
La mayoría de los scripts que fallan en la cámara lo hacen de una de estas tres formas: lanzan una excepción, producen un valor incorrecto o se quedan colgados. Cada caso cuenta con un conjunto de herramientas distinto.
2.41.1. Cómo leer una traza de error (traceback)¶
Cuando un script lanza una excepción y nada la gestiona, el REPL o el IDE imprimen una traza de error (traceback): un registro de la cadena de llamadas desde el script más externo hasta la línea que provocó el error.
Una traza de error se lee de abajo hacia arriba:
La línea inferior indica la clase de la excepción y su mensaje (
ValueError: invalid literal for int()...).Cada bloque
File "...", line N, in <name>que aparece encima es un fotograma: una llamada más profunda a medida que se asciende.El fotograma superior es donde comenzó el script; el fotograma inferior es donde se disparó el error.
Lee primero la parte inferior para saber qué salió mal y luego asciende para ver cómo llegó el script hasta allí. Los números de línea apuntan a las ubicaciones exactas del código fuente del script.
2.41.2. Depuración con prints¶
La forma más rápida de averiguar qué está haciendo un script es imprimir los valores sospechosos. Tres funciones integradas hacen que los prints sean más útiles:
repr(): devuelve la cadena de estilo de desarrollador de un valor.print(repr(value))distingue"5"de5yNonede"None", algo que un simpleprint()no puede hacer.type(): devuelve la clase de un valor.print(type(value))es la manera de averiguar si la variable que «debería ser un int» es en realidad una cadena.len(): la longitud de una secuencia o colección. Una fracción sorprendentemente grande de los errores son problemas de desfase por uno o de discordancia de tamaño.
print("got:", repr(value), "type:", type(value), "len:", len(value))
Coloca un print dentro de cada rama que te interese: ambas ramas de un if, cada bloque except, el cuerpo de un bucle que sospechas que se ejecuta cero veces. El coste es una línea de salida; el valor es descubrir si la ruta de código que crees que se está ejecutando es realmente la que se ejecuta.
2.41.3. Explorar un objeto¶
Dos funciones integradas responden a «qué puedo hacer con esto»:
dir(): devuelve una lista con todos los nombres definidos en un objeto: métodos, atributos, métodos dunder, todo.help(): imprime la cadena de documentación (y, en CPython, la firma) de una función, método o clase.
Úsalas juntas: dir encuentra el nombre y help explica qué hace.
2.41.3.1. Encontrar un nombre con dir¶
>>> dir([1, 2, 3])
['__add__', '__class__', '__contains__', '__delitem__',
'__eq__', '__ge__', ..., 'append', 'clear', 'copy',
'count', 'extend', 'index', 'insert', 'pop', 'remove',
'reverse', 'sort']
El primer tramo de la lista son métodos dunder, heredados de todos los objetos; los nombres que merece la pena buscar suelen estar después de ellos. dir funciona con cualquier cosa: una clase, una instancia, un módulo, un tipo integrado:
>>> import json
>>> dir(json)
['__name__', 'dump', 'dumps', 'load', 'loads']
Esa segunda forma es la manera de averiguar qué nombres de nivel superior expone realmente un módulo sin salir del REPL.
2.41.3.2. Consultarlo con help¶
Una vez que dir ha sacado a la luz un candidato, help lo describe:
>>> help(str.split)
split(sep=None, maxsplit=-1)
Return a list of the words in the string, ...
En MicroPython, help es más escueto que en CPython: a veces solo la firma, a veces una cadena de documentación de una línea, a veces nada en el caso de las funciones integradas en C. Aun así, es un recordatorio rápido cuando no se tiene a mano la información emergente del IDE.
2.41.4. Cuando algo se queda colgado¶
Un script que no termina es más difícil de diagnosticar que uno que lanza una excepción. Culpables habituales:
Un bucle
whilecuya condición nunca se vuelve falsa. Añade un print de la variable del bucle en cada iteración; si el valor no cambia, el cuerpo del bucle tiene un error.Una llamada bloqueante que espera una entrada que nunca llega: una lectura de una cola vacía, una espera sin fin. Rodea la llamada con prints para ver en qué línea se ha quedado atascado el script.
Una recursión infinita. La traza de error que aparece cuando finalmente se dispara (con
RecursionError) suele apuntar directamente a ella.
La recuperación más eficaz para un script colgado es el botón stop del IDE, que envía un KeyboardInterrupt al script a través de USB. La interrupción aparece como una traza de error en la línea que se está ejecutando en ese momento, a menudo la línea exacta que no termina.
Nota
Si un cuelgue resiste todos los diagnósticos (el script parece correcto, la traza de error de la interrupción apunta a una función integrada o al código del firmware en lugar de a tu script, o el mismo código funcionaba en una compilación de firmware anterior), la causa puede ser un error del firmware en lugar de un error del script. Reduce el script al reproductor más pequeño posible que siga quedándose colgado y abre un informe en el foro de OpenMV. Incluye la versión del firmware, la placa en la que se ejecutó y el script reducido.
2.41.5. Quita los diagnósticos antes de publicar¶
Los prints estratégicos durante el desarrollo son estupendos; un centenar de llamadas a print dejadas en un script de producción saturan la salida y consumen memoria dinámica (heap) que el trabajo real podría estar usando. Cuando se corrige un error, quita los prints (o protégelos detrás de una marca de depuración que puedas desactivar).
Para los diagnósticos que deban permanecer en la ruta de código a largo plazo, cambia de print() al módulo logging. Este asocia un nivel a cada mensaje (debug, info, warning, error) y permite que un único ajuste silencie los menos importantes en producción:
import logging
log = logging.getLogger("main")
log.info("starting up")
log.debug("loaded config: %s", config)
log.warning("falling back to defaults")
Establecer el nivel del logger en logging.WARNING hace que las llamadas info y debug no cuesten prácticamente nada (la cadena del mensaje nunca se construye), sin tener que comentar líneas. Eso convierte a logging en la herramienta adecuada para los diagnósticos permanentes; el simple print está bien para los desechables.