tokenize — Tokenizer für Python-Quellcode¶
Quellcode: Lib/tokenize.py
Das Modul tokenize stellt einen lexikalischen Scanner für Python-Quellcode bereit, der selbst in Python geschrieben ist. Der Scanner dieses Moduls gibt auch Kommentare als Tokens zurück und eignet sich damit für Pretty-Printer, etwa für die farbige Darstellung auf dem Bildschirm.
Damit sich der Tokenstrom einfacher verarbeiten lässt, werden alle Tokens für Operatoren und Trennzeichen sowie Ellipsis mit dem allgemeinen Tokentyp OP zurückgegeben. Das Property TokenInfo.exact_type nennt den genauen Tokentyp.
Warnung
Beachte, dass die Funktionen dieses Moduls nur darauf ausgelegt sind, syntaktisch gültigen Python-Code zu parsen, also Code, der beim Parsen mit ast.parse() keine Ausnahme auslöst. Bei ungültigem Python-Code ist das Verhalten der Funktionen dieses Moduls undefiniert und kann sich jederzeit ändern.
Eingabe zerlegen¶
Der wichtigste Einstiegspunkt ist ein Generator:
- tokenize.tokenize(readline)¶
Der Generator
tokenize()erwartet ein Argument, readline. Es muss ein aufrufbares Objekt sein, das dieselbe Schnittstelle bietet wie die Methodeio.IOBase.readline()von Dateiobjekten. Jeder Aufruf sollte eine Eingabezeile als Bytes zurückgeben.Der Generator erzeugt
TokenInfo-Objekte.Geändert in Version 3.1: Liefert jetzt
TokenInfo-Objekte statt einfacher Tupel.tokenize()ermittelt die Kodierung der Quelldatei, indem es gemäß PEP 263 nach einem UTF-8-BOM oder einem Encoding-Cookie sucht.
- tokenize.generate_tokens(readline)¶
Zerlegt einen Quelltext, der als Unicode-Zeichenketten statt als Bytes gelesen wird.
Wie bei
tokenize()ist das Argument readline ein aufrufbares Objekt, das eine einzelne Eingabezeile zurückgibt. Allerdings erwartetgenerate_tokens(), dass readline ein str-Objekt statt Bytes liefert.Das Ergebnis ist ein Iterator, der genau wie bei
tokenize()TokenInfo-Objekte liefert. EinENCODING-Token liefert er nicht.
- class tokenize.TokenInfo(type, string, start, end, line)¶
Ein benanntes Tupel, das ein Token beschreibt. Es hat fünf Felder und zusätzlich das Property
exact_type.Added in version 3.1.
- type¶
Eine ganze Zahl, die den Tokentyp angibt, entsprechend den Konstanten in
token, etwaNAMEundSTRING. Operatoren und Trennzeichen haben den TypOP. Für deren genauen Tokentyp dientexact_type.
- string¶
Der Text des Tokens als
str. BeiSTRING-Tokens sind die Anführungszeichen, ein etwaiges Präfix und die Escape-Sequenzen enthalten, ohne dass sie ausgewertet werden.Bei einem
ENCODING-Token ist das der Name der Kodierung. Tokens, die ohne zugehörigen Quelltext eingefügt werden, können eine leere Zeichenkette enthalten. In TokensFSTRING_MIDDLEundTSTRING_MIDDLEwerden doppelte geschweifte Klammern durch einfache ersetzt.
- start¶
Ein Zweiertupel
(srow, scol)aus ganzen Zahlen, das die Anfangsposition des Tokens im Quelltext angibt, einschließlich dieser Position. Zeilen werden ab 1 gezählt, Spalten ab 0. Spalten zählen Unicode-Codepunkte. Ein Tabulator zählt als eine Spalte und wird nicht bis zum nächsten Tabulatorstopp aufgefüllt.Bei einem
ENCODING-Token ist das(0, 0).
- end¶
Ein Zweiertupel
(erow, ecol)aus ganzen Zahlen, das die Endposition des Tokens angibt, diese Position selbst ausgenommen. Zeilen werden ab 1 gezählt, Spalten ab 0. Reicht ein Token über mehrere Zeilen, ist erow die letzte Zeile und ecol die Spalte in dieser Zeile.Beim Namen
spamam Anfang der ersten Zeile ist zum Beispielstartgleich(1, 0)undendgleich(1, 4).Bei einem
ENCODING-Token ist das(0, 0). Ein TokenNEWLINEoderNL, das am Ende einer Eingabe ohne abschließenden Zeilenumbruch eingefügt wird, kann eine Endspalte haben, die einen Schritt über den Quelltext hinausgeht.
Alle Konstanten des Moduls token werden auch von tokenize exportiert.
Eine weitere Funktion macht die Zerlegung wieder rückgängig. Das ist nützlich für Werkzeuge, die ein Skript zerlegen, den Tokenstrom verändern und das geänderte Skript zurückschreiben.
- tokenize.untokenize(iterable)¶
Wandelt Tokens zurück in Python-Quellcode. iterable muss Sequenzen mit mindestens zwei Elementen liefern: den Tokentyp und die Zeichenkette des Tokens. Weitere Elemente der Sequenz werden übergangen.
Das Ergebnis lässt sich garantiert so zerlegen, dass es der Eingabe entspricht. Die Umwandlung ist damit verlustfrei und ein Hin und Zurück sicher. Die Garantie gilt nur für Tokentyp und Zeichenkette des Tokens, denn die Abstände zwischen den Tokens, also die Spaltenpositionen, können sich ändern.
Zurückgegeben werden Bytes, kodiert nach dem
ENCODING-Token, dastokenize()als erste Tokensequenz ausgibt. Fehlt in der Eingabe ein Encoding-Token, wird stattdessen ein str zurückgegeben.
tokenize() muss die Kodierung der Quelldateien erkennen, die es zerlegt. Die Funktion, die es dafür verwendet, ist zugänglich:
- tokenize.detect_encoding(readline)¶
Die Funktion
detect_encoding()erkennt, mit welcher Kodierung eine Python-Quelldatei dekodiert werden soll. Sie erwartet ein Argument, readline, genau wie der Generatortokenize().Sie ruft readline höchstens zweimal auf und gibt die verwendete Kodierung als Zeichenkette sowie eine Liste der gelesenen Zeilen zurück, die nicht aus Bytes dekodiert sind.
Sie erkennt die Kodierung an einem UTF-8-BOM oder an einem Encoding-Cookie gemäß PEP 263. Sind BOM und Cookie beide vorhanden, widersprechen sich aber, wird
SyntaxErrorausgelöst. Beachte, dass bei gefundenem BOM'utf-8-sig'als Kodierung zurückgegeben wird.Ist keine Kodierung angegeben, wird die Vorgabe
'utf-8'zurückgegeben.Zum Öffnen von Python-Quelldateien dient
open(): Sie erkennt die Kodierung der Datei mitdetect_encoding().
- tokenize.open(filename)¶
Öffnet eine Datei im Nur-Lese-Modus mit der Kodierung, die
detect_encoding()erkannt hat.Added in version 3.2.
- exception tokenize.TokenError¶
Wird ausgelöst, wenn ein Docstring oder ein Ausdruck, der über mehrere Zeilen gehen darf, nirgends in der Datei abgeschlossen wird, zum Beispiel:
"""Beginning of docstring
oder:
[1, 2, 3
Verwendung über die Befehlszeile¶
Added in version 3.3.
Das Modul tokenize lässt sich von der Befehlszeile als Skript ausführen. Das geht so einfach wie:
python -m tokenize [-e] [filename.py]
Die folgenden Optionen sind möglich:
- -h, --help¶
diese Hilfe anzeigen und beenden
- -e, --exact¶
Tokennamen mit dem genauen Typ anzeigen
Ist filename.py angegeben, wird dessen Inhalt zerlegt und nach stdout ausgegeben. Andernfalls wird stdin zerlegt.
Added in version 3.15: Die Ausgabe ist standardmäßig farbig und lässt sich über Umgebungsvariablen steuern.
Beispiele¶
Beispiel für einen Skriptumschreiber, der Float-Literale in Decimal-Objekte umwandelt:
from tokenize import tokenize, untokenize, NUMBER, STRING, NAME, OP
from io import BytesIO
def decistmt(s):
"""Substitute Decimals for floats in a string of statements.
>>> from decimal import Decimal
>>> s = 'print(+21.3e-5*-.1234/81.7)'
>>> decistmt(s)
"print (+Decimal ('21.3e-5')*-Decimal ('.1234')/Decimal ('81.7'))"
The format of the exponent is inherited from the platform C library.
Known cases are "e-007" (Windows) and "e-07" (not Windows). Since
we're only showing 12 digits, and the 13th isn't close to 5, the
rest of the output should be platform-independent.
>>> exec(s) #doctest: +ELLIPSIS
-3.21716034272e-0...7
Output from calculations with Decimal should be identical across all
platforms.
>>> exec(decistmt(s))
-3.217160342717258261933904529E-7
"""
result = []
g = tokenize(BytesIO(s.encode('utf-8')).readline) # tokenize the string
for toknum, tokval, _, _, _ in g:
if toknum == NUMBER and '.' in tokval: # replace NUMBER tokens
result.extend([
(NAME, 'Decimal'),
(OP, '('),
(STRING, repr(tokval)),
(OP, ')')
])
else:
result.append((toknum, tokval))
return untokenize(result).decode('utf-8')
Beispiel für das Zerlegen von der Befehlszeile aus. Das Skript:
def say_hello():
print("Hello, World!")
say_hello()
wird zu der folgenden Ausgabe zerlegt. Die erste Spalte ist der Bereich der Zeilen- und Spaltenkoordinaten, in dem das Token gefunden wurde, die zweite Spalte der Name des Tokens und die letzte Spalte, falls vorhanden, sein Wert:
$ python -m tokenize hello.py
0,0-0,0: ENCODING 'utf-8'
1,0-1,3: NAME 'def'
1,4-1,13: NAME 'say_hello'
1,13-1,14: OP '('
1,14-1,15: OP ')'
1,15-1,16: OP ':'
1,16-1,17: NEWLINE '\n'
2,0-2,4: INDENT ' '
2,4-2,9: NAME 'print'
2,9-2,10: OP '('
2,10-2,25: STRING '"Hello, World!"'
2,25-2,26: OP ')'
2,26-2,27: NEWLINE '\n'
3,0-3,1: NL '\n'
4,0-4,0: DEDENT ''
4,0-4,9: NAME 'say_hello'
4,9-4,10: OP '('
4,10-4,11: OP ')'
4,11-4,12: NEWLINE '\n'
5,0-5,0: ENDMARKER ''
Die genauen Namen der Tokentypen lassen sich mit der Option -e anzeigen:
$ python -m tokenize -e hello.py
0,0-0,0: ENCODING 'utf-8'
1,0-1,3: NAME 'def'
1,4-1,13: NAME 'say_hello'
1,13-1,14: LPAR '('
1,14-1,15: RPAR ')'
1,15-1,16: COLON ':'
1,16-1,17: NEWLINE '\n'
2,0-2,4: INDENT ' '
2,4-2,9: NAME 'print'
2,9-2,10: LPAR '('
2,10-2,25: STRING '"Hello, World!"'
2,25-2,26: RPAR ')'
2,26-2,27: NEWLINE '\n'
3,0-3,1: NL '\n'
4,0-4,0: DEDENT ''
4,0-4,9: NAME 'say_hello'
4,9-4,10: LPAR '('
4,10-4,11: RPAR ')'
4,11-4,12: NEWLINE '\n'
5,0-5,0: ENDMARKER ''
Beispiel für das Zerlegen einer Datei aus dem Programm heraus, wobei mit generate_tokens() Unicode-Zeichenketten statt Bytes gelesen werden:
import tokenize
with tokenize.open('hello.py') as f:
tokens = tokenize.generate_tokens(f.readline)
for token in tokens:
print(token)
Oder, mit tokenize() direkt Bytes gelesen:
import tokenize
with open('hello.py', 'rb') as f:
tokens = tokenize.tokenize(f.readline)
for token in tokens:
print(token)