- WAT est un inspector conçu pour identifier rapidement la nature d’objets inconnus dans le runtime Python, avec en un coup d’œil le type, la valeur, les attributs, les méthodes, les types parents, les signatures, la documentation et même le code source
- L’usage de base est
wat / object, équivalent àwat(object), et l’outil prend aussi en charge plusieurs syntaxes commewat.short / 'foo','foo' | wat.short,wat('foo', short=True) - On peut chaîner des modifiers comme
.short,.dunder,.long,.code,.caller,.public,.all,.ret,.strpour ajuster l’étendue de la sortie, le mode de retour, l’affichage en couleur et l’indication de l’emplacement d’appel - L’installation se fait avec
pip install watpuisimport wat, et pour du débogage rapide il est aussi possible de coller un snippet Insta-Load dans une session Python pour l’utiliser immédiatement sans installation dans cette même session - Des exemples avec
Userde Django,re.match,pathlib,colorsys.hsv_to_rgb,typing.List[str],str | Nonemontrent que WAT peut servir au débogage, à l’exploration REPL et à l’apprentissage des internals de Python
Ce que fait WAT
- WAT est un outil pour explorer et inspecter des objets Python à l’exécution
- Lorsqu’il est difficile de comprendre ce qu’est un objet inconnu, on peut enquêter sur sa nature dans la console Python avec l’inspector
wat - En exécutant
wat / objectsur unobjectquelconque, on peut voir les informations suivantes- le type de l’objet
- la valeur formatée
- les variables et les méthodes
- les types parents
- la signature
- la documentation
- le code source
- La même inspection approfondie peut aussi s’utiliser avec la syntaxe
wat(object) Watest présenté comme une variante anglaise dewhat, employée pour exprimer la confusion ou le mécontentement
Usage de base et syntaxe
- L’opérateur de division est utilisé pour une saisie rapide
wat / fooest équivalent àwat(foo)
- Plusieurs syntaxes permettent d’obtenir la même inspection
wat.short / 'foo': syntaxe pratique pour aller vitewat.short('foo')wat('foo', short=True): syntaxe Python naturelle'foo' | wat.short: syntaxe de type pipe Unix
- On peut ajuster le comportement d’inspection avec la forme
wat.modifier / foo - Les modifiers peuvent être chaînés, par exemple
wat.short.str.gray / 'foo' - En Python, les objets ne se limitent pas aux structures de données : ils incluent aussi fonctions, classes, modules et types intégrés, donc
watpeut explorer n’importe quel objet - Saisir
watdans l’interpréteur affiche l’aide sur l’objetwatlui-même
Ajuster le périmètre d’inspection avec les modifiers
.shortou.smasque les attributs comme les variables et méthodes internes, et n’affiche que la valeur, le type, les types parents, la signature et la documentation.dunderaffiche les attributs dunder commençant par__.longaffiche les valeurs et docstrings sans les tronquer.codeaffiche le code source des fonctions, méthodes et classes.nodocsmasque la documentation des fonctions et des classes.callermontre comment et où l’inspection a été appelée, et fonctionne dans un fichier plutôt que dans le REPL.publicmasque les attributs private et n’affiche que les attributs publics.allinclut toutes les informations possibles.retrenvoie de nouveau l’objet après l’inspection.strrenvoie la chaîne de résultat au lieu de l’afficher.graydésactive l’affichage en couleur dans la console.colorforce l’affichage en couleur dans la consolewat.localsinspecte les variables locales, etwat.globalsles variables globales
Installation et Insta-Load
- Le flux d’installation via pip est le suivant
pip install wat- dans Python,
import wat
- Le package
watn’a aucune dépendance externe - Pour du débogage rapide, il propose une méthode Insta-Load qui permet de l’utiliser dans la même session Python sans installation
- Insta-Load consiste à coller dans l’interpréteur un snippet Python qui importe
base64etzlib, reconstruit une chaîne de code compressée et encodée, puis l’exécute avecexec(..., globals()) - Après exécution du snippet Insta-Load, l’objet
watdevient disponible - Il est recommandé de vérifier ce qui va être exécuté avant de lancer le snippet
- on peut prévisualiser le code extrait avec
print(zlib.decompress(base64.b64decode(code)).decode()) - coller le contenu de
inspection.pydans l’interpréteur produit le même effet - il est aussi proposé d’installer le package avec pip puis de relire le code
- on peut prévisualiser le code extrait avec
- WAT peut être chargé à partir d’un seul glyphe Unicode
- Le loader basé sur une chaîne Unicode convertit une longue séquence d’emojis ou de caractères combinés en octets via
ord(c) & 255, puis exécute le résultat aprèszlib.decompress(...)avecexec(...)
Identifier le type d’un objet et comprendre son usage
- En Python, langage à typage dynamique, il peut être difficile d’identifier le type d’un objet, et WAT Inspector affiche le nom du type ainsi que le module dont il provient
- Les exemples de vérification de type montrent la valeur, le type et la longueur
wat.short / (1,)affiche la valeur(1,), le typetuple, la longueur1wat.short / {None}affiche la valeur{None}, le typeset, la longueur1
- Dans l’exemple d’objet Django
User,wat.short / useraffichestr: admin,repr: <User: admin>, le typedjango.contrib.auth.models.User, ainsi que la liste des types parents - Une fois le type réel identifié, on peut ajouter des annotations de type dans le code pour réduire la confusion par la suite
- Pour comprendre comment utiliser un objet inconnu, on peut afficher la liste de ses méthodes, sa signature et sa docstring
wat / ['foo']est donné en exemple- pour voir la docstring complète, il faut utiliser
wat.long
- Pour comprendre l’usage d’une fonction, on peut consulter sa docstring et sa signature
wat / str.splitest donné en exemple
Exploration des attributs, modules et code source
- Pour voir l’intérieur de l’objet inspecté, on peut lister ses attributs ainsi que le type de chaque attribut
wat / re.match('(\\d)_(.*)', '1_title')est donné en exemple
- L’outil peut aussi servir à explorer des modules en listant leurs fonctions, classes et sous-modules
- il y a un exemple avec
import pathlibpuiswat / pathlib - on peut ensuite approfondir avec
wat / pathlib.fnmatch
- il y a un exemple avec
- WAT Inspector masque par défaut les attributs commençant par
__wat.dunder / {}permet d’afficher les attributs dunder
- Pour voir concrètement comment une fonction fonctionne, on peut afficher son code source
- il y a un exemple avec
import colorsyspuiswat.code / colorsys.hsv_to_rgb
- il y a un exemple avec
- Les
dictetlistimbriqués sont formatés avec une indentation lisible
Session de débogage et inspection des variables
- Après avoir lancé le débogueur interactif de Python avec
breakpoint(), on peut inspecter les objets directement sur place - Dans l’exemple Pdb, après
import watou collage du snippet Insta-Load,wat / foosert à inspecter une variable locale, puiscpermet de reprendre l’exécution - Les variables locales et globales peuvent être consultées via
wat.localsetwat.globals - Appeler
wat()sans argument affiche les variables locales de la pile appelante sous le titreLocal variables
Exemples d’apprentissage des internals Python
- Le contenu inclut des exemples à visée pédagogique pour comprendre le fonctionnement interne de Python
reversed([]) == reversed([])vautFalse, etwat.s / reversed([])montre que la valeur est un objetlist_reverseiteratoret que son type estlist_reverseiteratorwat / type('ObjectCreator', (), {})affiche la valeur de la classe créée dynamiquement, le typetype, et lasignature: class ObjectCreator()wat / typeaffiche la valeur detype, son typetype, la signatureclass type(…), la documentationtype(object) -> the object's type,type(name, bases, dict, **kwds) -> a new type, ainsi que des attributs publics commemrowat.s / List[str]affiche la valeurtyping.List[str], le typetyping._GenericAlias, les types parentstyping._BaseGenericAlias,typing._Final, ainsi que la signaturedef List(*args, **kwargs)wat(str | None)affiche la valeurstr | Noneet le typetypes.UnionType- Des exemples d’exploration d’objets intégrés Python sont aussi donnés avec
wat / __builtins__,wat / ... - WAT peut aussi s’inspecter lui-même
- exemples :
wat.dunder / wat,wat.code / wat.__truediv__
- exemples :
Résumé du fonctionnement interne
inspect_format(obj, *, short=False, dunder=False, nodocs=False, long=False, code=False, caller=False, public=False, all=False)construit le résultat d’inspection sous forme de chaîne- avec
all=True,dunder,long,codeetcallersont activés ensemble - avec
public=True, l’affichage des éléments private est désactivé - si
sys.stdout.isatty()renvoie vrai, la largeur du terminal est récupérée et des séparateurs─sont ajoutés au-dessus et au-dessous de la sortie
- avec
- La sortie d’inspection est générée dans l’ordre suivant : valeur de l’objet, représentation chaîne, type, types parents, longueur, signature, documentation, code source, puis section des attributs
- L’inspection des attributs parcourt
dir(obj)par ordre alphabétique- les attributs dunder sont exclus si l’option
dunderest désactivée - les attributs private commençant par
_sont exclus si l’option private est désactivée - si
getattr(obj, key)lèveBaseException, l’objet exception est utilisé comme valeur
- les attributs dunder sont exclus si l’option
- Pour les objets callable, la signature est formatée à partir de
inspect.signature(obj)- en cas d’échec, une signature de repli de la forme
(...)est renvoyée - les classes reçoivent le préfixe
class, les coroutine functions le préfixeasync def, et les fonctions, méthodes, builtins ou objets disposant de__name__le préfixedef
- en cas d’échec, une signature de repli de la forme
- Avec
code=True, si l’objet est une classe ou un callable, le code source est affiché viainspect.getsource(obj)- en cas de
OSError,TypeErrorouIndentationError, un message d’échec est renvoyé
- en cas de
- Les formateurs de
dictetlistrenvoientERROR: too deeply nestedsi la profondeur d’indentation dépasse 30
Sortie couleur et thème
- L’affichage couleur peut être contrôlé via des variables d’environnement
WAT_COLOR="false"désactive l’affichage couleur dans la consoleWAT_COLOR="true"force la couleur même dans un environnement non TTY
- La variable d’environnement
WAT_COLORSpermet de personnaliser le thème de couleurs - Le thème par défaut est un mapping de codes couleur ANSI sous la forme
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)retire les séquences d’échappement ANSI avec une expression régulière
Inspiration
- WAT est inspiré de Rich Inspect
1 commentaires
Commentaires sur Hacker News
Waouh, c’est vraiment bien. J’utilisais autrefois python-ls[0] pour un usage similaire, mais quelque chose s’est cassé pour une raison dont je ne me souviens plus, et il n’est plus maintenu
Je compte l’ajouter à ma boîte à outils de débogage, principalement composée de snoop[1] et pdbpp. Ce que j’aimerais voir dans wat, c’est une sorte de widget ipy qui facilite l’exploration d’objets dans Jupyter
J’aime aussi le hack d’exec en base64. J’utilise Python depuis longtemps, mais je n’y avais jamais pensé et je ne l’avais jamais vu jusque-là ; je vais certainement m’en servir pour quelques usages à l’avenir
[0] https://github.com/gabrielcnr/python-ls
[1] https://pypi.org/project/snoop/
Ça a l’air intéressant. J’utilise tout le temps dir en Python, et quand la documentation est médiocre, c’est parfois plus utile que la doc officielle
Le shell interactif est l’un des vrais points forts de Python, et il est surprenant qu’il n’y ait pas davantage de nouveaux outils ou d’innovations autour de lui
On dirait une version plus sophistiquée du vieux icecream
https://github.com/gruns/icecream
Si vous ne connaissez pas, regardez aussi plus bas la liste des implémentations pour d’autres langages
https://github.com/gruns/icecream#icecream-in-other-language...
Ce genre d’outil est utile
Il y a 20 ans, j’avais créé un inspecteur d’objets pour Zope
Aujourd’hui, j’utilise devtools tous les jours, et icecream ainsi que q de temps en temps. Je vais aussi essayer wat
from wat import watÉtant donné le côté très cool du projet, je suis surpris qu’il ne propose pas simplement
import watavec la même syntaxe d’utilisation. Cela aurait pu permettre aux utilisateurs curieux de découvrir le truc en essayant wat/watimport watserait bien, mais Python a la limitation de ne pas permettre de rendre un module appelable. C’est pour cela qu’on en arrive au plus longfrom wat import watJe n’en suis pas certain, mais
import wat; wat.wat / objectserait peut-être plus pratiqueÇa a l’air très utile, mais suis-je le seul à être agacé par la tendance récente à surcharger des opérateurs sans aucun rapport, ici l’opérateur
/, au nom de la lisibilité ?/est un choix étrange. Cela dit, il est dommage de ne pas pouvoir surchargeris. En pratique,wat(foo)aurait probablement suffiPour éviter l’import fastidieux, vous pouvez aussi ajouter ceci à votre fichier
$PYTHONSTARTUPtry:from wat import watexcept ImportError:passAu final, j’ai imprimé sa sortie et je l’ai placée dans un répertoire pointé par
PYTHONPATHpour qu’elle soit toujours disponibleReste à voir si je continuerai à m’en servir
Waouh, si un outil comme celui-ci avait existé quand j’apprenais Python, cela aurait changé la donne. Quand on apprend un langage, voir ce qui se passe à l’intérieur est un passage essentiel, et le débogage de base de Python est, au mieux, décevant
À la place, j’ai installé pry et je suis devenu un fervent fan de Ruby, mais cet outil pourrait me donner envie de réessayer Python
L’auteur utilise en interne le module Python inspect de la bibliothèque standard pour fournir ces fonctionnalités. Bien sûr, il ajoute beaucoup de valeur par-dessus
Voir
inspection.pydans le module watÀ la 2e ligne, on trouve :
import inspect as std_inspect« Si vous voulez déboguer quelque chose rapidement, vous pouvez utiliser cet inspecteur dans la même session sans rien installer »
« Collez cet extrait dans l’interpréteur Python pour le charger à la volée »
L’idée de mettre dans le README du projet une copie complète du projet sous forme de données compressées encodées en base64 est assez ingénieuse
C’est particulièrement adapté à ce genre de projet, qu’on n’aurait pas forcément pensé à préinstaller dans l’environnement où il deviendra indispensable