90. Reading PEPs

PEP document structure, the steering council review process, and tracking PEP status on peps.python.org.

90. Reading PEPs

A Python Enhancement Proposal, or PEP, is a design document for a significant change to Python. PEPs describe language changes, standard library changes, process rules, compatibility policy, governance, release schedules, and implementation plans.

For CPython internals work, PEPs matter because many implementation choices exist to satisfy a design accepted years earlier. Reading only the C code often shows what CPython does. Reading the PEP explains why it does it.

90.1 What a PEP Is

A PEP is a structured proposal.

It may describe:

PEP kind Purpose
Standards Track New language feature, library feature, or interoperability standard
Informational Design explanation, guideline, or background
Process Governance, release, compatibility, or workflow rule

A PEP usually contains:

title
author
status
type
created date
abstract
motivation
rationale
specification
backward compatibility
reference implementation
rejected ideas
open issues
references

Not every PEP has every section. Older PEPs can be less uniform.

90.2 Why PEPs Matter for CPython Internals

A CPython implementation often follows constraints that are not obvious from code alone.

Examples:

why pattern matching has specific binding rules
why async functions create coroutine objects
why dictionaries preserve insertion order
why annotations changed evaluation rules
why subinterpreters need isolated state
why C API compatibility is conservative

The code implements decisions. The PEP records the design argument.

When changing CPython, read the relevant PEP before assuming the implementation can be simplified.

90.3 PEP Status

A PEP has a status.

Common statuses include:

Status Meaning
Draft Still being developed
Accepted Approved for implementation
Final Implemented or complete
Deferred Postponed
Rejected Declined
Withdrawn Removed by author
Superseded Replaced by another PEP
Active Ongoing process PEP

For internals work, Final and Accepted PEPs are usually the most important.

Rejected PEPs are also useful because they explain design alternatives that Python deliberately avoided.

90.4 Read the Header First

Start with the header.

Example fields:

PEP: 572
Title: Assignment Expressions
Author: ...
Status: Final
Type: Standards Track
Created: ...
Python-Version: 3.8

The header answers:

what feature this is
whether the proposal is active
which version it targets
who wrote it
whether it is language-level, informational, or process-level

Do not treat a Draft PEP as current Python behavior. Do not treat a Rejected PEP as design authority except for historical context.

90.5 Read Motivation Before Specification

The motivation explains the problem.

The specification explains the chosen design.

Read them in that order.

If you read only the specification, you may understand the rule but miss the reason. If you read only the motivation, you may understand the goal but miss the exact behavior CPython must implement.

Example pattern:

Motivation
    why the old behavior was insufficient

Rationale
    why this design was chosen

Specification
    exact behavior to implement

Rejected ideas
    alternatives deliberately avoided

For implementation work, the specification is binding. The rationale helps avoid accidental redesign.

90.6 Separate Language Semantics From Implementation Strategy

Many PEPs specify Python behavior without requiring a particular implementation.

Example:

language rule
    a match statement binds names according to defined pattern rules

implementation choice
    CPython compiles match statements into particular bytecode sequences

The PEP constrains the behavior. CPython may choose the internal representation.

When editing internals, preserve language semantics even if you change:

AST representation
compiler passes
bytecode layout
runtime helper functions
cache structure

Tests should usually target the semantic rule, not the current implementation detail.

90.7 Find the Relevant PEP

Use the feature name, version, or subsystem.

Examples:

Topic Likely PEP area
Assignment expressions PEP 572
Positional-only parameters PEP 570
Structural pattern matching PEP 634, 635, 636
Exception groups PEP 654
Type hinting PEP 484 and later typing PEPs
Module state in extensions PEP 3121, PEP 489
Stable ABI PEP 384
Per-interpreter GIL PEP 684
Free-threading PEP 703
Python release cycle PEP 602
Deprecation policy Relevant process PEPs and devguide

For modern changes, also read the linked issue, pull request, and discussion when available.

90.8 Standards Track PEPs

Standards Track PEPs are most relevant to implementation.

They often define:

syntax
runtime behavior
new APIs
C API changes
standard library additions
compatibility guarantees
migration path

When implementing or modifying a Standards Track feature, check:

grammar changes
AST changes
compiler changes
bytecode behavior
runtime helpers
documentation
tests
C API if exposed

A language PEP often touches the whole source-to-execution pipeline.

90.9 Informational PEPs

Informational PEPs explain a design, convention, or ecosystem practice.

They may not directly require code changes, but they provide context.

Examples of informational concerns:

style guidance
packaging conventions
interoperability norms
security context
historical explanation

For CPython internals, informational PEPs are useful when a behavior exists for ecosystem compatibility rather than pure implementation convenience.

90.10 Process PEPs

Process PEPs define how the project operates.

They may cover:

governance
release cadence
deprecation policy
feature acceptance
backporting
version numbering
maintenance branches

Process PEPs matter when deciding whether a change belongs in:

main branch only
a maintenance branch
a security branch
documentation only
a future version

They also affect whether a change needs a PEP at all.

90.11 Reading a PEP for Implementation

When reading a PEP as an implementer, extract the exact constraints.

Use this checklist:

What user-visible behavior is required?
What syntax or API changes are specified?
What errors must be raised?
What compatibility constraints exist?
What edge cases are explicitly mentioned?
Which alternatives were rejected?
What tests should exist?
What documentation should change?

Then map those constraints to CPython subsystems:

PEP concern CPython area
Syntax Grammar/, parser, AST
Static scoping symbol table
Runtime execution bytecode, evaluation loop
Object behavior Objects/
Library API Lib/, Modules/
C API Include/, Objects/, Doc/c-api/
Import behavior Lib/importlib/, Python/import.c
Documentation Doc/
Tests Lib/test/

90.12 Reading Rejected Ideas

Rejected ideas are not filler.

They explain designs Python chose not to adopt.

This helps when you are tempted to "simplify" behavior.

Example reasons ideas get rejected:

ambiguous syntax
too much runtime cost
breaks existing code
inconsistent with data model
hard to teach
hard to optimize
bad interaction with existing tools
C API compatibility burden

Rejected ideas also help you answer code review questions. Often a reviewer will ask why a simpler alternative was not chosen. The PEP may already answer it.

90.13 Reading Backward Compatibility Sections

Backward compatibility sections are critical.

They describe:

source compatibility
binary compatibility
runtime behavior changes
deprecation path
warnings
migration strategy

For CPython, compatibility has several layers:

Compatibility layer Example
Python source Existing Python programs continue to run
Runtime behavior Existing semantics remain stable
C API source Extension code still compiles
ABI Existing extension binaries keep loading
Standard library Existing imports and functions remain available
Tooling Debuggers, profilers, linters, and type checkers can adapt

A patch that ignores compatibility can be technically clean but unacceptable.

90.14 Reading the Reference Implementation

Some PEPs link to a reference implementation.

Read it carefully, but do not assume it exactly matches the final merged version.

A reference implementation may be:

prototype-quality
outdated
partial
changed during review
superseded by later commits

Use it to understand the intended architecture, then compare with current CPython source.

90.15 Match PEP Text Against Current Code

A PEP may be older than the current implementation.

The final code may differ due to:

review changes
bug fixes
performance work
later PEPs
backward compatibility discoveries
implementation constraints

Therefore, read both:

PEP
current docs
current tests
current source

If they conflict, current language reference and tests usually reflect current behavior, but a serious conflict may indicate outdated documentation or an implementation bug.

90.16 PEPs and Tests

Tests should encode the accepted behavior.

When implementing a PEP, tests should cover:

normal cases
edge cases
syntax errors
runtime errors
interactions with existing features
documentation examples
backward compatibility cases

For syntax features, include invalid syntax tests.

For runtime features, include error-path tests.

For C API features, include ownership and failure behavior where possible.

90.17 PEPs and Documentation

A PEP is not a replacement for documentation.

A PEP explains a change proposal. Documentation explains the finished behavior to users.

After a PEP is implemented, update:

language reference
library reference
C API reference
tutorial if needed
What's New
docstrings if relevant

Do not require users to read the PEP to understand the feature.

90.18 PEPs and Code Review

PEPs are useful in code review because they provide an accepted design baseline.

A good review comment might say:

The PEP specifies that this error must be raised during compilation, not execution. This path currently raises at runtime.

or:

The rejected ideas section explicitly avoids this implicit conversion.

Use PEPs to clarify constraints, not to shut down discussion. Later bugs and implementation details may require careful interpretation.

90.19 When a Change Needs a PEP

A change may need a PEP when it affects:

Python syntax
core language semantics
major standard library design
public C API design
backward compatibility at scale
release policy
governance
large ecosystem behavior

A change probably does not need a PEP when it is:

small bug fix
internal refactor
test improvement
documentation clarification
minor performance improvement
private API cleanup

When unsure, inspect similar historical changes and project guidance.

90.20 Important PEP Families for CPython Internals

Some clusters of PEPs are especially important.

Area PEP themes
Syntax and compiler assignment expressions, pattern matching, exception groups
Object model descriptors, metaclasses, data model changes
Imports import hooks, module specs, namespace packages
C API stable ABI, multi-phase init, module state
Typing annotations, generics, postponed evaluation
Concurrency subinterpreters, per-interpreter GIL, free-threading
Packaging wheels, metadata, build systems
Release process annual releases, support windows
Governance steering council and decision process

A CPython internals reader should recognize which PEP family a subsystem belongs to.

90.21 Common Mistakes When Reading PEPs

Mistake Better approach
Reading a Draft as final behavior Check status first
Ignoring rejected ideas Use them to understand design boundaries
Treating implementation notes as language law Separate semantics from implementation
Reading only the abstract Read motivation, specification, compatibility
Ignoring later PEPs Check whether the PEP was superseded or amended
Using PEP as user docs Update proper documentation
Assuming current code exactly matches original PEP Compare against source and tests

90.22 Practical Reading Workflow

For a CPython change:

1. Identify the feature or subsystem.
2. Find relevant PEPs.
3. Check each PEP status.
4. Read the abstract and motivation.
5. Read the specification closely.
6. Read backward compatibility notes.
7. Read rejected ideas.
8. Compare with current docs.
9. Compare with current tests.
10. Compare with current source.
11. Implement or review against the accepted behavior.

For a bug fix, this workflow may take minutes. For a language change, it may define the whole project plan.

90.23 Core Principle

A PEP is design memory.

The CPython source records the implementation. The test suite records expected behavior. The documentation explains the public contract. The PEP records why the contract exists and which alternatives were considered. Serious CPython work uses all four.