- WAT es un inspector para identificar rápidamente objetos desconocidos en el runtime de Python; permite ver de una sola vez el tipo, valor, atributos, métodos, tipos padre, firmas, documentación y código fuente
- El uso básico es
wat / object, que funciona igual quewat(object), y admite varias sintaxis comowat.short / 'foo','foo' | wat.shortywat('foo', short=True) - Se pueden encadenar modificadores como
.short,.dunder,.long,.code,.caller,.public,.all,.rety.strpara ajustar el alcance de la salida, el modo de retorno, la salida con color y la visualización de la ubicación de llamada - Se instala con
pip install waty luegoimport wat; para debugging rápido también se puede pegar un snippet Insta-Load en una sesión de Python y usarlo en esa misma sesión sin instalarlo - Ejemplos como Django
User,re.match,pathlib,colorsys.hsv_to_rgb,typing.List[str]ystr | Nonemuestran que WAT puede servir para debugging, exploración en el REPL y aprendizaje de los internos de Python
Qué hace WAT
- WAT es una herramienta para explorar e inspeccionar objetos de Python en tiempo de ejecución
- Cuando es difícil entender qué es un objeto desconocido, se puede usar el inspector
watdesde la consola de Python para investigar su identidad - Al ejecutar
wat / objectsobre unobjectcualquiera, se puede ver la siguiente información- el tipo del objeto
- el valor formateado
- variables y métodos
- tipos padre
- firma
- documentación
- código fuente
- La misma inspección profunda también puede usarse con la sintaxis
wat(object) Watse presenta como una variante del ingléswhat, una expresión usada para manifestar confusión o desagrado
Uso básico y sintaxis
- Para escribir más rápido, usa el operador de división
wat / fooes equivalente awat(foo)
- Se pueden usar varias sintaxis para la misma inspección
wat.short / 'foo': sintaxis para entrada rápidawat.short('foo')wat('foo', short=True): sintaxis natural de Python'foo' | wat.short: sintaxis al estilo de pipes de Unix
- Se puede ajustar el comportamiento de la inspección con la forma
wat.modifier / foo - Los modificadores pueden encadenarse; un ejemplo es
wat.short.str.gray / 'foo' - En Python, los objetos incluyen no solo estructuras de datos, sino también funciones, clases, módulos, tipos integrados, etc., por lo que
watpuede explorar cualquier objeto - Si se escribe
waten el intérprete, se puede ver la ayuda del propio objetowat
Ajustar el alcance de la inspección con modificadores
.shorto.soculta atributos como variables y métodos dentro del objeto, y solo muestra valor, tipo, tipos padre, firma y documentación.dundermuestra los atributos dunder que empiezan con__.longmuestra valores y docstrings sin abreviar.codemuestra el código fuente de funciones, métodos y clases.nodocsoculta la documentación de funciones y clases.callermuestra cómo y desde dónde se llamó la inspección; funciona en archivos, no en el REPL.publicoculta atributos privados y muestra solo atributos públicos.allincluye toda la información posible.retdevuelve de nuevo el objeto después de la inspección.strdevuelve la cadena resultante en vez de imprimirla.graydesactiva la salida con color en la consola.colorfuerza la salida con color en la consolawat.localsinspecciona variables locales ywat.globalsinspecciona variables globales
Instalación e Insta-Load
- El flujo de instalación con pip es el siguiente
pip install wat- En Python,
import wat
- El paquete
watno tiene dependencias externas - Para debugging rápido, ofrece un método Insta-Load que permite usarlo en la misma sesión de Python sin instalarlo
- Insta-Load consiste en pegar en el intérprete un snippet de Python que importa
base64yzlib, restaura una cadena de código comprimida y codificada, y la ejecuta conexec(..., globals()) - Después de ejecutar el snippet de Insta-Load, se puede usar el objeto
wat - Antes de ejecutar el snippet, se recomienda verificar qué se va a ejecutar
- Se puede revisar previamente el código extraído con
print(zlib.decompress(base64.b64decode(code)).decode()) - Pegar en el intérprete el contenido de
inspection.pytiene el mismo efecto - También se propone instalar el paquete con pip y revisar el código
- Se puede revisar previamente el código extraído con
- WAT puede cargarse desde un único glyph Unicode
- El loader basado en una cadena Unicode toma una cadena larga de emojis y caracteres combinados, la convierte a bytes con
ord(c) & 255, y luego ejecutazlib.decompress(...)yexec(...)
Entender tipos de objetos y cómo usarlos
- En Python, al ser un lenguaje de tipado dinámico, a veces es difícil identificar el tipo de un objeto; WAT Inspector muestra el nombre del tipo y el módulo del que proviene
- Los ejemplos de verificación de tipo muestran juntos el valor, el tipo y la longitud
wat.short / (1,)imprime el valor(1,), el tipotupley la longitud1wat.short / {None}imprime el valor{None}, el tiposety la longitud1
- En el ejemplo con un objeto Django
User,wat.short / userimprimestr: admin,repr: <User: admin>, el tipodjango.contrib.auth.models.Usery una lista de tipos padre - Después de confirmar el tipo real, se pueden agregar anotaciones de tipo al código para reducir confusiones futuras
- Cuando se quiere entender cómo usar un objeto desconocido, se puede imprimir la lista de métodos, firmas y docstrings
- Se presenta como ejemplo
wat / ['foo'] - Para ver el docstring completo, se usa
wat.long
- Se presenta como ejemplo
- Para entender cómo usar una función, se puede ver su docstring y su firma
- Se presenta como ejemplo
wat / str.split
- Se presenta como ejemplo
Exploración de atributos, módulos y código fuente
- Para ver el interior del objeto inspeccionado, se pueden listar sus atributos y el tipo de cada atributo
- Se presenta como ejemplo
wat / re.match('(\d)_(.*)', '1_title')
- Se presenta como ejemplo
- También puede usarse para explorar módulos, listando las funciones, clases y submódulos del módulo elegido
- Hay un ejemplo que ejecuta
wat / pathlibdespués deimport pathlib - Luego se puede explorar más a fondo con
wat / pathlib.fnmatch
- Hay un ejemplo que ejecuta
- WAT Inspector oculta por defecto los atributos que empiezan con
__- Se pueden ver atributos dunder con
wat.dunder / {}
- Se pueden ver atributos dunder con
- Para comprobar cómo funciona realmente una función, se puede ver el código fuente
- Hay un ejemplo que ejecuta
wat.code / colorsys.hsv_to_rgbdespués deimport colorsys
- Hay un ejemplo que ejecuta
- Los dict y list anidados se formatean con indentación y de forma legible
Sesiones de debugging e inspección de variables
- Después de iniciar el debugger interactivo de Python con
breakpoint(), se pueden inspeccionar objetos ahí mismo - El ejemplo con Pdb inspecciona variables locales con
wat / foodespués deimport wato de pegar el snippet de Insta-Load, y continúa la ejecución conc - Las variables locales y globales pueden revisarse con
wat.localsywat.globals, respectivamente - Si se llama
wat()sin argumentos, imprime las variables locales del stack del llamador bajo el títuloLocal variables
Ejemplos para aprender los internos de Python
- Incluye ejemplos para aprender y entender el comportamiento interno de Python
reversed([]) == reversed([])esFalse, ywat.s / reversed([])muestra que el valor es un objetolist_reverseiteratory que el tipo eslist_reverseiteratorwat / type('ObjectCreator', (), {})muestra el valor de una clase creada dinámicamente, el tipotypeysignature: class ObjectCreator()wat / typemuestra el valor del propiotype, el tipotype, la firmaclass type(…), la documentacióntype(object) -> the object's typeytype(name, bases, dict, **kwds) -> a new type, y atributos públicos comomrowat.s / List[str]muestra el valortyping.List[str], el tipotyping._GenericAlias, los tipos padretyping._BaseGenericAliasytyping._Final, y la firmadef List(*args, **kwargs)wat(str | None)muestra el valorstr | Noney el tipotypes.UnionType- Como ejemplos de exploración de objetos integrados de Python, se presentan
wat / __builtins__ywat / ... - También se puede inspeccionar el propio WAT
- Se presentan como ejemplos
wat.dunder / watywat.code / wat.__truediv__
- Se presentan como ejemplos
Resumen del funcionamiento interno
inspect_format(obj, *, short=False, dunder=False, nodocs=False, long=False, code=False, caller=False, public=False, all=False)construye como cadena el resultado de la inspección del objeto- Si
all=True, se activan tambiéndunder,long,codeycaller - Si
public=True, se desactiva la salida de privados - Si
sys.stdout.isatty()es verdadero, obtiene el ancho de la terminal y agrega separadores─arriba y abajo de la salida
- Si
- La salida de la inspección se genera en este orden: valor del objeto, representación como cadena, tipo, tipos padre, longitud, firma, documentación, código fuente y sección de atributos
- La inspección de atributos recorre
dir(obj)en orden por nombre- Los atributos dunder se excluyen si la opción
dunderestá desactivada - Los atributos privados que empiezan con
_se excluyen si la configuración de privados está desactivada - Si
getattr(obj, key)lanza unaBaseException, se usa el objeto de la excepción como valor
- Los atributos dunder se excluyen si la opción
- Para objetos callable, formatea la firma con base en
inspect.signature(obj)- Si falla, devuelve una firma alternativa con la forma
(...) - Para clases agrega el prefijo
class; para coroutine functions,async def; y para funciones, métodos, builtins u objetos con__name__, el prefijodef
- Si falla, devuelve una firma alternativa con la forma
- Si
code=Truey el objeto es una clase o callable, imprime el código fuente coninspect.getsource(obj)- Si ocurre
OSError,TypeErroroIndentationError, devuelve un mensaje de fallo
- Si ocurre
- Los formateadores de dict y list devuelven
ERROR: too deeply nestedsi la profundidad de indentación supera 30
Salida con color y temas
- La salida con color puede controlarse mediante variables de entorno
WAT_COLOR="false"desactiva la salida con color en la consolaWAT_COLOR="true"fuerza la salida con color incluso en entornos non-tty
- La variable de entorno
WAT_COLORSpermite personalizar el tema de colores - El tema por defecto es un mapeo de códigos de color ANSI con la forma
BAR=0;34,TRAIT=1;34,HEAD=1;37,STR=0;32,NUMBER=0;31,NONE=0;35,TRUE=1;32,FALSE=1;31,DOCS=2;37,KEYWORD=0;34,CALLABLE=1;32,VARIABLE=1;33,CODE=0;33 _strip_color(text)elimina las secuencias de escape ANSI usando una expresión regular
Inspiración
- WAT se inspiró en Rich Inspect
1 comentarios
Opiniones de Hacker News
Wow, está muy bueno. Antes usaba python-ls[0] para algo parecido, pero por alguna razón que no recuerdo algo se rompió y ya no recibe mantenimiento.
Pienso sumarlo a mi caja de herramientas de depuración, que consiste principalmente en snoop[1] y pdbpp. Lo que me gustaría para wat es algo como un widget de ipy que facilite explorar objetos en Jupyter.
También me gusta el hack de exec con base64. Llevo mucho tiempo usando Python y hasta ahora no se me había ocurrido ni lo había visto, así que seguro lo voy a probar para varias cosas.
[0] https://github.com/gabrielcnr/python-ls
[1] https://pypi.org/project/snoop/
Se ve interesante. En Python uso dir todo el tiempo, y cuando la documentación no es muy buena, a veces resulta más útil que la documentación oficial.
El shell interactivo es una de las verdaderas fortalezas de Python, y me sorprende que no haya más herramientas nuevas o innovación alrededor de eso.
Parece una versión más vistosa del viejo icecream.
https://github.com/gruns/icecream
Si no lo conoces, también puedes ver más abajo la lista de implementaciones para otros lenguajes.
https://github.com/gruns/icecream#icecream-in-other-language...
Este tipo de herramientas es útil.
Hace 20 años hice un introspector de objetos para Zope.
Hoy uso devtools todos los días, e icecream y q de vez en cuando. También voy a probar wat.
from wat import watCon lo genial que es el carácter del proyecto, me sorprende que no ofrezca simplemente
import watcon la misma sintaxis de uso. Así incluso podría haber hecho que los usuarios curiosos descubrieran el truco probando wat/wat.import watestaría bien, pero en Python existe la limitación de que no se puede hacer invocable a un módulo. Por eso se terminó usando el más largofrom wat import wat.No estoy del todo seguro, pero
import wat; wat.wat / objectquizá sería más cómodo.Se ve muy útil, pero me pregunto si soy el único al que le molesta la tendencia reciente de sobrecargar operadores totalmente ajenos en nombre de la legibilidad, en este caso el operador
/./es una elección rara. Aun así, es una lástima que no se pueda sobrecargaris. Siendo realistas,wat(foo)habría sido suficiente.Para evitar imports engorrosos, también puedes agregar esto al archivo
$PYTHONSTARTUP:try:from wat import watexcept ImportError:passAl final imprimí esa salida y la dejé en un directorio apuntado por
PYTHONPATHpara tenerla siempre disponible.Habrá que ver si lo sigo usando.
Wow, si hubiera tenido una herramienta así cuando aprendía Python, creo que habría cambiado las reglas del juego. Cuando aprendes un lenguaje, ver qué pasa por dentro es una ruta clave, y la depuración básica de Python es decepcionante, siendo generosos.
En su lugar instalé pry y me volví un fan entusiasta de Ruby, pero esta herramienta quizá podría hacer que vuelva a intentar con Python.
El autor usa internamente el módulo inspect de Python de la biblioteca estándar para ofrecer la funcionalidad. Claro que agregó mucho valor encima de eso.
Vean
inspection.pydel módulo wat.En la línea 2 dice:
import inspect as std_inspect“Si quieres depurar algo rápido, puedes usar este inspector en la misma sesión sin instalar nada”.
“Pega este snippet en el intérprete de Python para cargarlo al instante”.
La idea de poner en el README del proyecto una copia completa del proyecto como datos comprimidos codificados en base64 es bastante ingeniosa.
Encaja especialmente bien para un proyecto de este tipo, que quizá no se te ocurrió preinstalar en el entorno donde justo terminarás necesitándolo.