Following system colour scheme Selected dark colour scheme Selected light colour scheme

Python Enhancement Proposals

PEP 846 – Docstrings for Type Aliases

PEP 846 – Docstrings for Type Aliases

Author:
Bartosz Sławecki <bartosz at ilikepython.com>
Sponsor:
Jelle Zijlstra <jelle.zijlstra at gmail.com>
Discussions-To:
Pending
Status:
Draft
Type:
Standards Track
Topic:
Typing
Created:
06-Sep-2026
Python-Version:
3.16
Post-History:
06-Sep-2026

Table of Contents

Abstract

This PEP proposes preserving a string literal immediately following a type statement as the resulting type alias object’s __doc__ attribute, exposing that documentation through ast, and displaying it through pydoc and help(). It follows the placement already supported by source-based documentation tools. The parser stores the docstring in a new optional doc field on ast.TypeAlias instead of creating a separate ast.Expr node for it. ast.get_docstring() retrieves alias docstrings from this field.

Motivation

Several widely used development tools already recognize docstrings following type alias declarations. Pyright supports docstrings following type statements (since 2023), Sphinx’s autotype directive documents aliases and their docstrings (since 2025), and Pylint recognizes these strings as documentation (since 2023).

For example, an alias can explain how callers should interpret its values:

type Timeout = float | None
"""
Maximum wait in seconds.

Use None to wait indefinitely, or zero to return immediately.
"""

Calling help(Timeout) displays generic information about TypeAliasType rather than the alias’s docstring. A tool that needs the alias’s documentation must find and parse its source, which may be unavailable after installation or when the alias is passed in from another component.

The type statement introduced by PEP 695 creates a dedicated runtime object. That object can carry its own documentation, as functions and classes do. Preserving the docstring would make it available from the imported alias alone, including when the alias is re-exported.

Runtime documentation could also be consumed by third-party frameworks. Frameworks that already recognize TypeAliasType (such as Pydantic) could choose to use __doc__ as descriptive metadata. Such integrations would be up to those projects.

Specification

Docstring Placement

PEP 257 defines the convention of placing attribute docstrings immediately after assignments and calls strings following another docstring “additional docstrings”. This PEP uses the same placement convention for type statements: a string literal immediately following the statement becomes the alias’s docstring.

If the next logical line after a type statement in the same suite is an expression statement consisting of a string literal, that string is the alias’s docstring and is part of the type statement. Comments and blank lines may appear between the type statement and its docstring. A string literal on the same line as the alias, separated by a semicolon, does not qualify.

type Timeout = float | None
"""Maximum wait in seconds."""

type OtherTimeout = float | None
default_timeout = 30
"""This is not OtherTimeout's docstring."""

The rule applies wherever a type statement is allowed, including inside functions, classes, and control-flow suites. The string must be in the same suite as the alias. A string in a nested or enclosing suite does not qualify. Generic aliases follow the same rule:

type ListOrSet[T] = list[T] | set[T]
"""A collection whose order and duplicate handling depend on its type."""

As with modules, functions, and classes, a docstring must be an expression statement whose value is a string constant. Adjacent string literals combined by the parser qualify, as does a parenthesized string literal. Bytes literals, f-strings, t-strings, and expressions such as "first" + "second" do not qualify, even if compilation could reduce an expression to a constant string.

Only the first following string statement supplies __doc__. Additional docstrings, in the PEP 257 sense, remain ordinary expression statements. They are not concatenated or assigned to the alias.

Runtime Behavior

The alias stores its docstring in __doc__. An undocumented alias has __doc__ equal to None. Accessing this attribute does not evaluate the alias’s value.

Compilation applies the same docstring whitespace processing as it does for function and class docstrings. This expands tabs and cleans indentation while retaining surrounding blank lines. inspect.cleandoc() also removes surrounding blank lines.

After an alias is created, its __doc__ attribute can be reassigned. Deleting it resets it to None. The TypeAliasType constructor gains a keyword-only doc parameter, defaulting to None, that initializes __doc__ without whitespace processing:

from typing import TypeAliasType

Timeout = TypeAliasType(
    "Timeout", float | None, doc="Maximum wait in seconds."
)

This proposal requires no changes to type checker behavior. An alias’s docstring does not affect its meaning to a type checker or how its value is evaluated at runtime.

Optimization

Optimization level 2, selected by -OO or compile(..., optimize=2), strips alias docstrings as it strips function and class docstrings. The resulting alias has __doc__ equal to None. In the AST, preprocessing clears the doc field of ast.TypeAlias. Optimization levels 0 and 1 retain the docstring.

Assignments to __doc__ remain ordinary runtime assignments and are not stripped by -OO.

AST Support

The grammar of the type statement gains an optional trailing docstring, shown schematically:

type_alias:
   | "type" NAME [type_params] '=' expression [NEWLINE type_alias_docstring]

Here, type_alias_docstring denotes an expression statement whose value is a string constant, as defined under Docstring Placement.

This PEP adds an optional string field, doc, at the end of ast.TypeAlias (name, type_params, value, doc). An omitted doc defaults to None.

When docstrings are retained, ast.parse() populates this field with the original string, before compilation’s whitespace processing. The docstring does not appear as a following Expr(Constant(...)) statement. The node’s end position covers the docstring:

>>> import ast
>>> tree = ast.parse(
...     'type Timeout = float | None\n"Maximum wait in seconds."'
... )
>>> len(tree.body)
1
>>> tree.body[0].doc
'Maximum wait in seconds.'
>>> tree.body[0].end_lineno
2

ast.get_docstring() accepts TypeAlias nodes. As with the node kinds it already supports, its default behavior cleans the docstring using inspect.cleandoc(). With clean=False, the function returns the original string. It returns None for an undocumented alias.

Both ast.dump() and AST repr() display the docstring in the doc field. By default, ast.dump() omits the field when its value is None, as it does for other optional fields.

Code generation reads the doc field, and it does not inspect neighboring statements. For programmatically constructed ast.TypeAlias nodes, the doc field supplies the alias’s docstring. ast.unparse() emits the docstring as a string statement on the line after the alias, so that parsing the result populates the field again.

Standard Library Support

pydoc, including help(), will recognize type aliases and display their own documentation. Aliases will also be distinguished from other data members in module documentation. This applies to both text and HTML output.

For the opening example, the reference implementation displays:

Help on type alias Timeout in module mymodule:

type Timeout = float | None
    Maximum wait in seconds.

    Use None to wait indefinitely, or zero to return immediately.

    Lazy value access:

    __value__
        Lazily evaluated value of the type alias.

    evaluate_value
        Evaluation function for __value__.

    See help(typing.TypeAliasType) for the full type alias interface.

This PEP does not add automatic discovery of type alias docstrings to doctest. Supporting this would require a separate change to its discovery rules.

Rationale

Placing the string after the declaration follows the convention already used by tools for type aliases and described for attribute docstrings in PEP 257. Existing documented aliases would gain runtime documentation without requiring their authors to rewrite them.

Storing the docstring in the alias node’s doc field lets ast.get_docstring() retrieve it without searching the surrounding statements. Tools that inspect or modify alias docstrings can read or update the field directly. The cost is the AST change described under Backwards Compatibility.

Backwards Compatibility

Previously, a string literal immediately following a type statement had no effect at runtime. Under this proposal, a qualifying string becomes the alias’s __doc__ and is removed from the AST as a separate ast.Expr statement. The string is stored in the alias node’s doc field, and the node’s end position extends to cover it.

Tools that find alias docstrings by looking at the following statement, or that rely on the alias node’s end position, need adjusting when parsing with Python 3.16. ast.TypeAlias gains a fourth, optional field. Constructing the node with three positional arguments continues to work.

Security Implications

This PEP has no known security implications.

How to Teach This

The reference documentation for the type statement should show a docstring immediately after the declaration, note that the accepted forms match function and class docstrings, then demonstrate Alias.__doc__ and help(Alias). The typing.TypeAliasType documentation should describe the new attribute and how to assign it for aliases created with the constructor.

Users already familiar with source-based alias documentation can keep writing the same strings. Documentation should emphasize that only type statements gain runtime docstrings. Ordinary assignments, including those annotated with typing.TypeAlias, do not.

Documentation for ast.TypeAlias and ast.get_docstring() should explain how to read and modify the doc field and how whitespace is processed. It should also note that the docstring no longer has a separate ast.Expr node.

Reference Implementation

A CPython prototype is available at these revisions:

The grammar rule for the type statement gains an optional group that parses the expression statement on the following logical line. The type_alias_docstring[expr_ty] rule uses an action helper that returns the expression node if it is a string constant. Otherwise it returns NULL without setting an error, the group fails, and the parser backtracks to before the newline. Blank lines and comment-only lines do not prevent the parser from recognizing the docstring. The string must be in the same suite as the type statement. The type alias action extracts the constant’s string value and stores it in the doc field.

The pydoc implementation requests the alias expression in string format. This evaluation can trigger lazy imports. If evaluation raises an Exception, pydoc tries to recover the original alias expression from the source without evaluating it. If source recovery also fails, the declaration contains a placeholder with repr() of the original exception, and rendering continues with the docstring. A full traceback is not included. Failures to render type parameter bounds, constraints, or defaults cause that part of the declaration to be omitted.

Rejected Ideas

Retaining the String Statement

An alternative design would have AST preprocessing populate doc while retaining the string as a separate ast.Expr statement following the alias. Existing tools could continue to inspect that statement, but the AST would contain the same documentation in both the field and the statement. After an AST transformation, the two could contain different strings. The compiler would then need a rule for choosing which string to use. ast.unparse() and compile() could also produce different docstrings from the same tree if they used different copies.

When stripping the docstring under -OO, AST preprocessing would also need to prevent an additional docstring from taking its place if the tree were compiled again. With parser-level recognition, the AST contains the docstring only in the doc field, so these rules are unnecessary.

In one variant, doc would refer to the original Constant node. In-place edits would be visible through both references, but visitors would reach the same node twice. Replacing the node through one reference would leave the other reference pointing to the old node.

In another variant, a private attribute would hold the docstring, accessible only through ast.get_docstring(). This design would avoid a public field, but AST preprocessing would still need rules for invalidating the stored documentation when surrounding statements change.

Acknowledgements

Thanks to Jelle Zijlstra for reviewing the proposal and agreeing to sponsor the PEP, to Guido van Rossum for suggesting that the parser recognize the docstring, and to the participants in the initial discussion on Discourse.

Thanks to Peter Bierma and Jakub Romańczuk for convincing me to pursue the idea.

Change History

  • 06-Sep-2026: Initial proposal and first PEP draft.
  • 15-Sep-2026: The parser recognizes the docstring as part of the type statement instead of AST preprocessing associating a following statement with the alias. The AST no longer retains the string as a separate statement.