Python tokenize: Read Source Code Tokens

Published on: August 27, 2026
Reading time: 6 minutes
A developer typing code on a laptop with a Python book beside in an office.

The tokenize module converts Python source code into a sequence of lexical tokens. Each token describes a fragment such as a name, number, string, operator, comment, indentation change, newline, or end marker. This layer is useful for formatters, linters, converters, documentation tools, comment analysis, and small transformations that must preserve more textual detail than an AST.

Tokens do not represent the complete meaning of a program. They do not resolve scopes, imports, types, or runtime behavior. Use ast for syntax structure, symtable for symbol scopes, and dis for bytecode. Choose tokenize when whitespace, comments, positions, and literal spelling matter.

Tokenize bytes

tokenize.tokenize() accepts a readline function that returns bytes.

from io import BytesIO
from tokenize import tokenize

source = b"x = 10 + 2\n"
for item in tokenize(BytesIO(source).readline):
    print(item)

The sequence includes an ENCODING token, source tokens, and an ENDMARKER.

TokenInfo

Each result is a TokenInfo object with type, string, start, end, and line fields.

for item in tokenize(BytesIO(source).readline):
    print(item.type, item.string, item.start, item.end)

start and end are line-column pairs. line contains the original physical source line.

Readable token names

Token types are integers defined by the token module. Use token.tok_name for readable output.

import token

name = token.tok_name[item.type]

Do not persist raw numeric token IDs as a long-term format. They are tied to Python’s implementation and version.

exact_type for operators

Operators and delimiters generally use the broad OP type. TokenInfo.exact_type identifies the exact symbol.

import token

if item.type == token.OP:
    print(token.tok_name[item.exact_type])

This distinguishes +, +=, parentheses, colons, and other operators.

generate_tokens for text

generate_tokens() accepts a function that returns strings, which is convenient after source has already been decoded.

from io import StringIO
from tokenize import generate_tokens

for item in generate_tokens(StringIO("x = 1\n").readline):
    print(item)

This API does not produce an ENCODING token. For real Python files, prefer bytes or tokenize.open().

Source encoding

Python files may declare an encoding in their first lines. tokenize() detects it according to language rules.

Decoding every file as UTF-8 before detection can fail on legacy projects.

detect_encoding

detect_encoding() receives a byte-reading function and returns the encoding plus lines already consumed.

from tokenize import detect_encoding

with open("module.py", "rb") as file:
    encoding, lines = detect_encoding(file.readline)
    print(encoding, lines)

It may read up to two lines to inspect a BOM and encoding cookie.

tokenize.open

tokenize.open(filename) opens a Python file as text using the detected encoding.

import tokenize

with tokenize.open("module.py") as file:
    source = file.read()

This is a safe default for source-analysis tools that need decoded text.

Comments

Unlike the AST, tokenization preserves comments as COMMENT tokens.

import token

comments = [
    item for item in tokens
    if item.type == token.COMMENT
]

This enables TODO detection, pragmas, type comments, tool directives, and documentation rules.

Do not treat every comment as a directive

A directive needs a defined prefix, valid positions, and parsing rules. Searching for a substring creates false positives.

Define syntax such as # tool: and validate the remaining fields.

NL versus NEWLINE

NEWLINE ends a logical statement. NL represents a physical line break that does not end the statement, such as one inside parentheses or on a blank line.

result = (
    1
    + 2
)

Formatters and line-based analyzers must distinguish them.

Indentation

INDENT and DEDENT describe block changes.

if enabled:
    run()

The INDENT token contains the actual whitespace. Inconsistent tabs and spaces can produce errors.

Indentation errors

Tokenization or compilation may raise IndentationError or TabError. Report the filename, position, and source context.

Do not automatically rewrite ambiguous indentation without a documented formatting policy.

String tokens

A complete literal usually appears as one STRING token, including prefixes and quote style.

r"path\file"
f"value={x}"

Details around f-strings and newer syntax can vary by Python version. Understanding expressions inside them may require the parser.

Numbers

Integers, floats, complex values, and different bases appear as NUMBER with their original spelling.

0xff
1_000_000
3.14e-2

This preserves underscores and style that an AST normally reduces to a value.

Names and keywords

Identifiers and language keywords are both NAME tokens. Use keyword.iskeyword() to distinguish standard keywords.

import keyword

if item.type == token.NAME and keyword.iskeyword(item.string):
    print("keyword", item.string)

Soft keywords depend on syntactic context and require parser knowledge.

untokenize

untokenize() reconstructs source from tokens.

from tokenize import untokenize

new_source = untokenize(tokens)

It preserves a round trip for token type and string under supported conditions, but exact spacing can change.

A simple transformation

A tool can replace selected name tokens.

import token
from tokenize import TokenInfo, untokenize

new_tokens = []
for item in tokens:
    if item.type == token.NAME and item.string == "old_name":
        item = TokenInfo(
            item.type, "new_name", item.start, item.end, item.line
        )
    new_tokens.append(item)

result = untokenize(new_tokens)

A correct rename must account for scopes, attributes, imports, and shadowing. Tokens alone do not solve semantics.

Type-string pairs

untokenize() also accepts (type, string) pairs and determines necessary spacing.

Keeping full TokenInfo preserves more context, but original positions become stale after edits.

Positions after edits

Changing token length invalidates later offsets. Reconstructed source is valid, but diagnostics based on original columns need remapping.

Editorial tools should maintain a source map between old and new text.

Preserving comments

AST transformations and ast.unparse() generally lose comments. Tokens preserve them, but large structural refactoring becomes difficult.

For faithful round trips, consider a concrete syntax tree library.

TokenError

TokenError is raised for cases such as unfinished multiline strings or unclosed brackets.

from tokenize import TokenError

try:
    tokens = list(tokenize(readline))
except TokenError as error:
    print("incomplete source", error)

Temporarily incomplete code is normal in editors, so handle the condition without crashing the full analysis.

ERRORTOKEN

Invalid characters and selected special cases may appear as ERRORTOKEN. Inspect text and position before deciding the diagnostic.

Some whitespace can appear in special contexts, so do not label every error token as hostile input.

Partial editor buffers

An IDE may tokenize while a user types. Add debounce, cancellation, and partial results.

Source execution is never required to generate tokens.

Large files

Converting the generator into a list uses memory proportional to file size. Process tokens as a stream whenever possible.

Transformations that need lookahead can use a bounded window.

Abuse limits

Untrusted input can contain huge files, extremely long lines, and deep nesting. Limit bytes, lines, token count, and processing time.

Public services should isolate expensive analysis in a separate process.

Unicode identifiers

Python supports Unicode identifiers. Do not assume ASCII when validating names.

Security rules can normalize and detect confusing characters while still supporting legitimate languages.

Invisible characters

A scanner may report controls, unusual spaces, or bidirectional characters in comments and strings. Distinguish legitimate text from risk.

Display code points and source positions in diagnostics.

Combine with AST

Use tokens for comments and spelling and AST for structure. A common workflow associates nodes with line ranges and locates matching tokens.

See Python ast for visitors and structural transformations.

Combine with symtable

Tokens reveal spelling; symtable identifies scopes, locals, globals, and free variables.

This combination is safer for renaming than replacing every NAME token.

Formatters

A formatter must understand tokens, syntax, comments, and layout rules. Adding spaces around every OP is insufficient.

Use a complete grammar and a large regression suite.

Comment linters

Tokenization is enough for rules such as overly long comments, TODOs without owners, or invalid pragmas.

A # inside a string is not a COMMENT token.

Secret detection

Tokens can help find strings assigned to names such as password or token, but lexical analysis creates false positives.

Never log a detected value. Report only location and rule type.

Command line

The module provides a CLI that displays tokens from a file.

python -m tokenize module.py

It is useful for learning and debugging analysis rules.

Testing

Include alternate encodings, BOM, comments, multiline strings, f-strings, tabs, blank lines, open brackets, Unicode, incomplete files, and very large input.

After a transformation, tokenize and compile the generated source again.

Common mistakes

Common failures include decoding everything as UTF-8, confusing NL and NEWLINE, treating keywords as a separate token type, renaming NAME tokens without scope analysis, losing comments after switching to AST, trusting stale positions, materializing huge token lists, and omitting limits for public input.

Conclusion

tokenize exposes the lexical form of Python source while preserving comments, spelling, lines, and operators. Use tokenize.open() for correct encoding, exact_type for operators, and untokenize() for controlled reconstruction.

Combine tokens with AST and symbol tables when transformations need semantic context. Consult the official tokenize documentation and the token documentation.

Share:

Facebook
WhatsApp
Twitter
LinkedIn

Article content

    Related articles

    Python asynchronous code on a laptop for inspect.markcoroutinefunction
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    markcoroutinefunction: Detect Async Wrappers

    Learn inspect.markcoroutinefunction in Python to identify async wrappers, integrate frameworks, and avoid incorrect coroutine detection.

    Ler mais

    Tempo de leitura: 5 minutos
    10/10/2026
    Python code for traversing folders and files with Path.walk
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Path.walk: Traverse Directories Safely

    Learn Python Path.walk to traverse directories, filter files, handle errors, and control directory-tree processing safely.

    Ler mais

    Tempo de leitura: 5 minutos
    10/10/2026
    Python process debugging in a terminal with source code
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    pdb -p: Debug Running Python Processes

    Learn how to attach pdb to a running Python process, inspect stacks, and diagnose hangs safely.

    Ler mais

    Tempo de leitura: 6 minutos
    09/10/2026
    Python programming code for exact fractions
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    fractions.from_number: Convert Numbers to Fractions

    Learn Python fractions.from_number for exact rational conversion, float and Decimal handling, approximation, validation, and safe arithmetic.

    Ler mais

    Tempo de leitura: 4 minutos
    09/10/2026
    Developer configuring a Python HTTPS server and TLS certificate
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    HTTPSServer: Build a Local HTTPS Server in Python

    Learn Python HTTPSServer for local HTTPS services, TLS certificates, threaded handling, testing, and practical security limits.

    Ler mais

    Tempo de leitura: 4 minutos
    08/10/2026
    Protected files representing secure TAR extraction with Python
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    tarfile extraction_filter: Extract TAR Safely

    Learn Python tarfile extraction_filter for safer TAR extraction, path validation, links, permissions, and resource limits.

    Ler mais

    Tempo de leitura: 5 minutos
    08/10/2026