tokenize — Tokenizer för Python-källor

Källkod: Lib/tokenize.py


Modulen tokenize tillhandahåller en lexikal skanner för Python-källkod, implementerad i Python. Skannern i den här modulen returnerar även kommentarer som tokens, vilket gör den användbar för att implementera ”pretty-printers”, inklusive färgläggare för skärmvisning.

För att förenkla hanteringen av tokenflöden, returneras alla operator och delimiter tokens och Ellipsis med den generiska OP token-typen. Den exakta typen kan bestämmas genom att kontrollera egenskapen exact_typenamed tuple som returneras från tokenize.tokenize().

Varning

Observera att funktionerna i den här modulen endast är utformade för att analysera syntaktiskt giltig Python-kod (kod som inte ger utslag när den analyseras med ast.parse()). Beteendet hos funktionerna i den här modulen är odefinierat när de tillhandahåller ogiltig Python-kod och det kan ändras när som helst.

Tokenisering av inmatning

Den primära ingångspunkten är en generator:

tokenize.tokenize(readline)

Generatorn tokenize() kräver ett argument, readline, som måste vara ett anropbart objekt som ger samma gränssnitt som metoden io.IOBase.readline() för filobjekt. Varje anrop till funktionen bör returnera en rad indata som bytes.

Generatorn producerar 5-tupler med dessa medlemmar: token-typen; token-strängen; en 2-tupel (srow, scol) av ints som anger raden och kolumnen där token börjar i källan; en 2-tupel (erow, ecol) av ints som anger raden och kolumnen där token slutar i källan; och raden där token hittades. Den rad som skickas (den sista tupeln) är den fysiska raden. Tupeln 5 returneras som en named tuple med fältnamnen: type string start end line.

Den returnerade named tuple har en ytterligare egenskap med namnet exact_type som innehåller den exakta operatortypen för OP-tokens. För alla andra token-typer är exact_type lika med den namngivna tupelns fält type.

Ändrad i version 3.1: Lagt till stöd för namngivna tupler.

Ändrad i version 3.3: Lagt till stöd för exact_type.

tokenize() bestämmer källkodningen för filen genom att leta efter en UTF-8 BOM eller kodningskaka, enligt PEP 263.

tokenize.generate_tokens(readline)

Tokenisera en källa som läser unicode-strängar istället för bytes.

Precis som tokenize() är argumentet readline en callable som returnerar en enda rad med indata. Men generate_tokens() förväntar sig att readline returnerar ett str-objekt istället för bytes.

Resultatet är en iterator som ger namngivna tupler, precis som tokenize(). Det ger inte en ENCODING-token.

Alla konstanter från modulen token exporteras också från tokenize.

En annan funktion finns för att vända tokeniseringsprocessen. Detta är användbart för att skapa verktyg som tokeniserar ett skript, modifierar tokenflödet och skriver tillbaka det modifierade skriptet.

tokenize.untokenize(iterable)

Konverterar tokens tillbaka till Python-källkod. iterable måste returnera sekvenser med minst två element, token-typen och token-strängen. Eventuella ytterligare sekvenselement ignoreras.

Resultatet garanteras att tokeniseras tillbaka för att matcha indata så att konverteringen är förlustfri och rundresor garanteras. Garantin gäller endast för token-typen och token-strängen eftersom avståndet mellan tokens (kolumnpositioner) kan ändras.

Den returnerar bytes, kodade med ENCODING-token, som är den första tokensekvensen som matas ut av tokenize(). Om det inte finns någon kodningstoken i indata returneras en str istället.

tokenize() behöver upptäcka kodningen av källfiler som den tokeniserar. Funktionen som den använder för att göra detta är tillgänglig:

tokenize.detect_encoding(readline)

Funktionen detect_encoding() används för att upptäcka den kodning som ska användas för att avkoda en Python-källfil. Den kräver ett argument, readline, på samma sätt som generatorn tokenize().

Den kommer att anropa readline högst två gånger och returnera den kodning som använts (som en sträng) och en lista över alla rader (som inte avkodats från bytes) som den har läst in.

Den detekterar kodningen från närvaron av en UTF-8 BOM eller en kodningskaka enligt specifikationen i PEP 263. Om både en BOM och en cookie finns, men är oense, kommer ett SyntaxError att uppstå. Observera att om BOM hittas, kommer 'utf-8-sig' att returneras som kodning.

Om ingen kodning anges kommer standardvärdet 'utf-8' att returneras.

Använd open() för att öppna Python-källfiler: den använder detect_encoding() för att upptäcka filkodningen.

tokenize.open(filename)

Öppna en fil i skrivskyddat läge med hjälp av den kodning som upptäcktes av detect_encoding().

Tillagd i version 3.2.

exception tokenize.TokenError

Utlöses när antingen en dokumentsträng eller ett uttryck som kan vara uppdelat på flera rader inte avslutas någonstans i filen, till exempel:

"""Början av
dokumentsträng

eller:

[1,
 2,
 3

Användning av kommandoraden

Tillagd i version 3.3.

Modulen tokenize kan köras som ett skript från kommandoraden. Det är så enkelt som att:

python -m tokenize [-e] [filnamn.py]

Följande alternativ accepteras:

-h, --help

visa detta hjälpmeddelande och avsluta

-e, --exact

visa tokennamn med exakt typ

Om filnamn.py anges tokeniseras dess innehåll till stdout. I annat fall utförs tokenisering på stdin.

Exempel

Exempel på en script rewriter som omvandlar float-literaler till Decimal-objekt:

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')

Exempel på tokenisering från kommandoraden. Skriptet:

def say_hello():
    print("Hello, World!")

say_hello()

kommer att tokeniseras till följande utdata där den första kolumnen är intervallet för rad- / kolumnkoordinaterna där token hittas, den andra kolumnen är namnet på token och den sista kolumnen är värdet på token (om någon)

$ 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      ''

De exakta namnen på token-typerna kan visas med hjälp av alternativet -e:

$ 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      ''

Exempel på tokenisering av en fil programmatiskt, läsning av unicode-strängar istället för bytes med generate_tokens():

import tokenize

with tokenize.open('hello.py') as f:
    tokens = tokenize.generate_tokens(f.readline)
    for token in tokens:
        print(token)

Eller läsa bytes direkt med tokenize():

import tokenize

with open('hello.py', 'rb') as f:
    tokens = tokenize.tokenize(f.readline)
    for token in tokens:
        print(token)