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

    High-angle view of woman coding on a laptop, with a Python book nearby. Ideal for programming and tech content.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python zipapp: Build Executable .pyz Files

    Learn Python zipapp to build .pyz files, define entry points, bundle pure dependencies, handle resources, and distribute secure CLI tools.

    Ler mais

    Tempo de leitura: 6 minutos
    27/08/2026
    Close-up view of a computer screen displaying code in a software development environment.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python sysconfig: Paths and Build Info

    Learn Python sysconfig to inspect paths, schemes, headers, compiler flags, ABI details, native extensions, and virtual environments.

    Ler mais

    Tempo de leitura: 6 minutos
    27/08/2026
    A person in a hoodie coding on dual monitors, depicting cybersecurity and hacking themes.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python marshal: Internal Binary Format

    Learn Python marshal for internal objects and bytecode, including versions, allow_code, disposable caches, limits, and untrusted-input risks.

    Ler mais

    Tempo de leitura: 6 minutos
    27/08/2026
    A close-up view of fresh, green cucumbers ready for pickling and preservation in Estonia.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python copyreg: Customize Pickle Types

    Learn Python copyreg to customize pickle reducers, version serialized state, avoid global conflicts, and test safely across processes.

    Ler mais

    Tempo de leitura: 6 minutos
    27/08/2026
    Close-up of a python snake coiled in darkness, showcasing its scales and eyes.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python reprlib: Safe Object Summaries

    Learn Python reprlib to summarize large lists, strings, and recursive objects while keeping logs safe, bounded, and readable.

    Ler mais

    Tempo de leitura: 5 minutos
    27/08/2026
    A close-up shot showcasing the intricate scales of a snake, highlighting texture and color.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python graphlib: Topological Task Order

    Learn Python graphlib to order dependencies, detect cycles, run ready tasks in parallel, and build safe task pipelines.

    Ler mais

    Tempo de leitura: 6 minutos
    27/08/2026