- WAT ist ein Inspector, mit dem sich die Natur unbekannter Objekte in der Python-Runtime schnell bestimmen lässt; Typ, Wert, Attribute, Methoden, Eltern-Typen, Signatur, Dokumentation und Quellcode können auf einmal geprüft werden
- Die grundlegende Verwendung ist
wat / objectund verhält sich genauso wiewat(object); unterstützt werden außerdem mehrere Syntaxvarianten wiewat.short / 'foo','foo' | wat.short,wat('foo', short=True) - Mit Modifizierern wie
.short,.dunder,.long,.code,.caller,.public,.all,.ret,.strlässt sich die Ausgabe verketten und in Umfang, Rückgabeart, Farbausgabe und Anzeige der Aufrufposition steuern - Die Installation erfolgt mit
pip install watund anschließendimport wat; für schnelles Debugging kann auch ein Insta-Load-Snippet in eine Python-Session eingefügt werden, um es in derselben Session ohne Installation zu verwenden - Beispiele wie Django
User,re.match,pathlib,colorsys.hsv_to_rgb,typing.List[str],str | Nonezeigen, dass WAT sich für Debugging, REPL-Erkundung und zum Lernen von Python-Interna eignet
Was WAT macht
- WAT ist ein Werkzeug zum Erkunden und Inspizieren von Python-Objekten zur Laufzeit
- Wenn sich schwer erkennen lässt, was ein unbekanntes Objekt ist, kann man in der Python-Konsole mit dem
wat-Inspector dessen Natur untersuchen - Führt man für ein beliebiges
objectwat / objectaus, lassen sich folgende Informationen prüfen- der type des Objekts
- der formatierte Wert
- Variablen und Methoden
- Eltern-Typen
- Signatur
- Dokumentation
- Quellcode
- Dieselbe tiefgehende Inspektion ist auch mit der Syntax
wat(object)möglich Watwird als Abwandlung des englischenwhatbeschrieben, das verwendet wird, um Verwirrung oder Unmut auszudrücken
Grundlegende Verwendung und Syntax
- Für schnelle Eingabe wird der Divisionsoperator verwendet
wat / fooist dasselbe wiewat(foo)
- Für dieselbe Inspektion können mehrere Syntaxvarianten verwendet werden
wat.short / 'foo': Syntax für schnelle Eingabewat.short('foo')wat('foo', short=True): natürliche Python-Syntax'foo' | wat.short: Syntax im Stil einer Unix-Pipe
- Mit Formen wie
wat.modifier / foolässt sich das Verhalten der Inspektion anpassen - Modifizierer können verkettet werden; ein Beispiel ist
wat.short.str.gray / 'foo' - Da Objekte in Python nicht nur Datenstrukturen, sondern auch Funktionen, Klassen, Module und eingebaute Typen umfassen, kann
watbeliebige Objekte erkunden - Gibt man im Interpreter
watein, erhält man Hilfe zumwat-Objekt selbst
Mit Modifizierern den Inspektionsumfang steuern
.shortoder.sblendet Attribute wie interne Variablen und Methoden aus und gibt nur Wert, Typ, Eltern-Typen, Signatur und Dokumentation aus.dunderzeigt dunder-Attribute an, die mit__beginnen.longzeigt nicht gekürzte Werte und Docstrings an.codezeigt den Quellcode von Funktionen, Methoden und Klassen.nodocsblendet die Dokumentation von Funktionen und Klassen aus.callerzeigt, wie und an welcher Stelle die Inspektion aufgerufen wurde, und funktioniert außerhalb der REPL in Dateien.publicblendet private Attribute aus und zeigt nur öffentliche Attribute.allenthält alle verfügbaren Informationen.retgibt nach der Inspektion das Objekt erneut zurück.strgibt statt der Ausgabe den Ergebnis-String zurück.graydeaktiviert die Farbausgabe in der Konsole.colorerzwingt die Farbausgabe in der Konsolewat.localsinspiziert lokale Variablen,wat.globalsglobale Variablen
Installation und Insta-Load
- Der Ablauf für die Installation mit pip ist wie folgt
pip install wat- in Python
import wat
- Das
wat-Paket hat keine externen Abhängigkeiten - Für schnelles Debugging gibt es eine Insta-Load-Methode, die sich in derselben Python-Session ohne Installation verwenden lässt
- Insta-Load funktioniert, indem nach dem Import von
base64undzlibein komprimierter und kodierter Code-String wiederhergestellt und mitexec(..., globals())ausgeführt wird; dazu wird ein Python-Snippet in den Interpreter eingefügt - Nach Ausführung des Insta-Load-Snippets kann das
wat-Objekt verwendet werden - Es wird empfohlen, vor der Ausführung zu prüfen, was ausgeführt wird
- Mit
print(zlib.decompress(base64.b64decode(code)).decode())lässt sich der extrahierte Code vorab ansehen - Das Einfügen des Inhalts von
inspection.pyin den Interpreter hat denselben Effekt - Als weitere Methode wird vorgeschlagen, das Paket mit pip zu installieren und den Code zu prüfen
- Mit
- WAT kann aus einem einzelnen Unicode-Glyph geladen werden
- Der Unicode-String-basierte Loader wandelt eine lange Zeichenfolge aus Emojis und kombinierenden Zeichen mit
ord(c) & 255in Bytes um, dekomprimiert sie mitzlib.decompress(...)und führt sie danach mitexec(...)aus
Objekttyp und Verwendung verstehen
- In Python als dynamisch typisierter Sprache kann es schwierig sein, den Typ eines Objekts zu erkennen; der WAT Inspector zeigt den Typnamen und das Modul, aus dem dieser Typ stammt
- Die Beispiele zur Typprüfung zeigen Wert, Typ und Länge gemeinsam an
wat.short / (1,)gibt den Wert(1,), den Typtupleund die Länge1auswat.short / {None}gibt den Wert{None}, den Typsetund die Länge1aus
- Im Beispiel mit dem Django-
User-Objekt gibtwat.short / userstr: admin,repr: <User: admin>, den Typdjango.contrib.auth.models.Userund eine Liste der Eltern-Typen aus - Nachdem der tatsächliche Typ bestätigt wurde, können Typannotationen in den Code aufgenommen werden, um spätere Verwirrung zu verringern
- Wenn man die Verwendung eines unbekannten Objekts verstehen will, lassen sich Methodenliste, Signatur und Docstring ausgeben
- Als Beispiel wird
wat / ['foo']gezeigt - Wenn man den vollständigen Docstring sehen möchte, verwendet man
wat.long
- Als Beispiel wird
- Um die Verwendung einer Funktion zu verstehen, kann man Docstring und Signatur der Funktion ansehen
- Als Beispiel wird
wat / str.splitgezeigt
- Als Beispiel wird
Attribute, Module und Quellcode erkunden
- Um das Innere eines inspizierten Objekts zu sehen, können Attribute und die Typen der einzelnen Attribute aufgelistet werden
- Als Beispiel wird
wat / re.match('(\\d)_(.*)', '1_title')gezeigt
- Als Beispiel wird
- Das Tool kann auch zur Modulerkundung verwendet werden; Funktionen, Klassen und Untermodule des gewählten Moduls können aufgelistet werden
- Es gibt ein Beispiel mit
import pathlibgefolgt vonwat / pathlib - Danach kann man tiefer einsteigen, etwa mit
wat / pathlib.fnmatch
- Es gibt ein Beispiel mit
- Der WAT Inspector blendet Attribute, die mit
__beginnen, standardmäßig aus- Mit
wat.dunder / {}lassen sich dunder-Attribute anzeigen
- Mit
- Um zu sehen, wie eine Funktion tatsächlich arbeitet, kann man ihren Quellcode ansehen
- Es gibt ein Beispiel mit
import colorsysgefolgt vonwat.code / colorsys.hsv_to_rgb
- Es gibt ein Beispiel mit
- Verschachtelte dicts und lists werden in einer eingerückten, gut lesbaren Form formatiert
Debugging-Sitzungen und Variableninspektion
- Nach dem Start eines interaktiven Debuggers mit
breakpoint()in Python können Objekte direkt an Ort und Stelle inspiziert werden - Im Pdb-Beispiel wird nach
import watoder dem Einfügen des Insta-Load-Snippets mitwat / fooeine lokale Variable inspiziert und mitcdie Ausführung fortgesetzt - Lokale und globale Variablen können jeweils mit
wat.localsundwat.globalsgeprüft werden - Wenn
wat()ohne Argumente aufgerufen wird, werden die lokalen Variablen des aufrufenden Stacks unter dem TitelLocal variablesausgegeben
Lernbeispiele zu Python-Interna
- Es sind Beispiele für den Einsatz zum Verständnis der internen Funktionsweise von Python enthalten
reversed([]) == reversed([])ergibtFalse, undwat.s / reversed([])zeigt, dass der Wert ein Objekt vom Typlist_reverseiteratoristwat / type('ObjectCreator', (), {})zeigt den Wert der dynamisch erzeugten Klasse, den Typtypeund die Signatursignature: class ObjectCreator()wat / typezeigt den Wert vontypeselbst, den Typtype, die Signaturclass type(…), die Dokumentationtype(object) -> the object's type,type(name, bases, dict, **kwds) -> a new typesowie öffentliche Attribute wiemrowat.s / List[str]zeigt den Werttyping.List[str], den Typtyping._GenericAlias, die Eltern-Typentyping._BaseGenericAlias,typing._Finalund die Signaturdef List(*args, **kwargs)wat(str | None)zeigt den Wertstr | Noneund den Typtypes.UnionType- Als Beispiele für die Erkundung eingebauter Python-Objekte werden
wat / __builtins__undwat / ...gezeigt - Auch WAT selbst kann inspiziert werden
- Beispiele sind
wat.dunder / watundwat.code / wat.__truediv__
- Beispiele sind
Zusammenfassung der internen Funktionsweise
inspect_format(obj, *, short=False, dunder=False, nodocs=False, long=False, code=False, caller=False, public=False, all=False)baut das Ergebnis der Objektinspektion als String auf- Wenn
all=True, werdendunder,long,codeundcallergemeinsam aktiviert - Wenn
public=True, wird die Ausgabe privater Elemente deaktiviert - Wenn
sys.stdout.isatty()wahr ist, wird die Terminalbreite ermittelt und ober- und unterhalb der Ausgabe eine Trennlinie aus─hinzugefügt
- Wenn
- Die Inspektionsausgabe wird in der Reihenfolge Objektwert, String-Darstellung, Typ, Eltern-Typen, Länge, Signatur, Dokumentation, Quellcode und Attributabschnitte erzeugt
- Die Attributinspektion durchläuft
dir(obj)in alphabetischer Reihenfolge- dunder-Attribute werden ausgeschlossen, wenn die Einstellung
dunderdeaktiviert ist - private Attribute, die mit
_beginnen, werden ausgeschlossen, wenn die Einstellung für private Ausgaben deaktiviert ist - Wenn bei
getattr(obj, key)eineBaseExceptionauftritt, wird das Ausnahmeobjekt als Wert verwendet
- dunder-Attribute werden ausgeschlossen, wenn die Einstellung
- Für aufrufbare Objekte wird die Signatur auf Basis von
inspect.signature(obj)formatiert- Im Fehlerfall wird eine Ersatzsignatur in der Form
(...)zurückgegeben - Klassen erhalten das Präfix
class, Coroutine-Funktionenasync def, Funktionen, Methoden, Builtins und Objekte mit__name__das Präfixdef
- Im Fehlerfall wird eine Ersatzsignatur in der Form
- Wenn
code=Truegesetzt ist und das Objekt eine Klasse oder callable ist, wird der Quellcode mitinspect.getsource(obj)ausgegeben- Bei
OSError,TypeErroroderIndentationErrorwird eine Fehlermeldung zurückgegeben
- Bei
- Die Formatter für dict und list geben bei einer Einrückungstiefe von mehr als 30
ERROR: too deeply nestedzurück
Farbausgabe und Themes
- Die Farbausgabe kann über Umgebungsvariablen gesteuert werden
WAT_COLOR="false"deaktiviert die Farbausgabe in der KonsoleWAT_COLOR="true"erzwingt Farbausgabe auch in Nicht-TTY-Umgebungen
- Mit der Umgebungsvariable
WAT_COLORSlässt sich das Farbthema anpassen - Das Standard-Theme ist eine Zuordnung von ANSI-Farbcodes in der Form
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)entfernt ANSI-Escape-Sequenzen per regulärem Ausdruck
Inspiration
- WAT ist von Rich Inspect inspiriert
1 Kommentare
Hacker-News-Kommentare
Wow, wirklich großartig. Ich habe früher für einen ähnlichen Zweck python-ls[0] verwendet, aber aus einem Grund, an den ich mich nicht mehr erinnere, ging irgendetwas kaputt, und es wird auch nicht mehr gewartet.
Kommt auf jeden Fall in meine Debugging-Toolbox, die hauptsächlich aus snoop[1] und pdbpp besteht. Was ich mir noch von wat wünschen würde, wäre ein ipy-Widget, das die Objekterkundung in Jupyter einfacher macht.
Auch der base64-exec-Hack gefällt mir. Ich benutze Python schon lange, aber darauf gekommen oder darüber gestolpert bin ich bisher nie, daher werde ich ihn künftig sicher für ein paar Dinge verwenden.
[0] https://github.com/gabrielcnr/python-ls
[1] https://pypi.org/project/snoop/
Sieht interessant aus. Ich benutze in Python ständig dir, und wenn die Dokumentation dürftig ist, ist das manchmal nützlicher als die offizielle Doku.
Die interaktive Shell ist eine der echten Stärken von Python, deshalb überrascht es mich, dass es darum herum nicht mehr neue Tools oder Innovationen gibt.
Sieht aus wie eine schickere Version des alten icecream.
https://github.com/gruns/icecream
Falls du es nicht kennst, lohnt sich auch ein Blick auf die Liste der Implementierungen für andere Sprachen weiter unten.
https://github.com/gruns/icecream#icecream-in-other-language...
Solche Tools sind nützlich.
Vor 20 Jahren habe ich für Zope einen Objekt-Introspektor gebaut.
Heute benutze ich devtools täglich und icecream und q gelegentlich. wat werde ich auch mal ausprobieren.
from wat import watBei einem Projekt mit so viel Stil ist es überraschend, dass es nicht einfach
import watmit derselben Nutzungsweise anbietet. Dann könnten neugierige Nutzer mit wat/wat herumspielen und den Trick entdecken.import watwäre schön, aber in Python gibt es die Einschränkung, dass sich Module nicht aufrufbar machen lassen. Deshalb ist es bei dem längerenfrom wat import watgelandet.Ich bin nicht ganz sicher, aber
import wat; wat.wat / objectkönnte sogar praktischer sein.Sieht sehr nützlich aus, aber bin ich der Einzige, den der neuere Trend stört, völlig unzusammenhängende Operatoren im Namen der Lesbarkeit zu überladen, hier also den
/-Operator?/hier eine seltsame Wahl ist. Schade nur, dass manisnicht überladen kann. Realistisch gesehen hättewat(foo)auch völlig gereicht.Um den lästigen Import zu vermeiden, kann man der Datei
$PYTHONSTARTUPauch Folgendes hinzufügen:try:from wat import watexcept ImportError:passIch habe am Ende einfach dessen Ausgabe ausgegeben und in ein Verzeichnis gelegt, auf das
PYTHONPATHzeigt, damit es immer verfügbar ist.Mal sehen, ob ich es dauerhaft weiterverwende.
Wow, wenn es so ein Tool gegeben hätte, als ich Python gelernt habe, hätte das alles verändert. Wenn man eine Sprache lernt, ist es ein zentraler Weg zu sehen, was intern passiert, und Pythons eingebaute Debugging-Möglichkeiten sind selbst wohlwollend betrachtet eher enttäuschend.
Stattdessen habe ich pry installiert und bin zu einem begeisterten Ruby-Fan geworden, aber dieses Tool könnte mich dazu bringen, Python noch einmal eine Chance zu geben.
Der Autor verwendet intern das inspect-Modul der Python-Standardbibliothek, um die Funktionalität bereitzustellen. Natürlich hat er darauf noch viel zusätzlichen Mehrwert aufgebaut.
Siehe
inspection.pyim wat-Modul.In Zeile 2 steht:
import inspect as std_inspect„Wenn du schnell etwas debuggen willst, kannst du diesen Inspektor verwenden, ohne in derselben Sitzung irgendetwas zu installieren.“
„Füge dieses Snippet in den Python-Interpreter ein, um es on the fly zu laden.“
Die Idee, im README des Projekts eine vollständige Kopie des gesamten Projekts als base64-kodierte komprimierte Daten unterzubringen, ist ziemlich clever.
Das passt besonders gut zu Projekten dieser Art, bei denen man vielleicht nicht daran gedacht hat, sie vorab in genau die Umgebung zu legen, in der man sie am dringendsten brauchen wird.