Skip to content

90. Reading PEPs

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

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 kindPurpose
Standards TrackNew language feature, library feature, or interoperability standard
InformationalDesign explanation, guideline, or background
ProcessGovernance, 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:

StatusMeaning
DraftStill being developed
AcceptedApproved for implementation
FinalImplemented or complete
DeferredPostponed
RejectedDeclined
WithdrawnRemoved by author
SupersededReplaced by another PEP
ActiveOngoing 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:

TopicLikely PEP area
Assignment expressionsPEP 572
Positional-only parametersPEP 570
Structural pattern matchingPEP 634, 635, 636
Exception groupsPEP 654
Type hintingPEP 484 and later typing PEPs
Module state in extensionsPEP 3121, PEP 489
Stable ABIPEP 384
Per-interpreter GILPEP 684
Free-threadingPEP 703
Python release cyclePEP 602
Deprecation policyRelevant 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 concernCPython area
SyntaxGrammar/, parser, AST
Static scopingsymbol table
Runtime executionbytecode, evaluation loop
Object behaviorObjects/
Library APILib/, Modules/
C APIInclude/, Objects/, Doc/c-api/
Import behaviorLib/importlib/, Python/import.c
DocumentationDoc/
TestsLib/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 layerExample
Python sourceExisting Python programs continue to run
Runtime behaviorExisting semantics remain stable
C API sourceExtension code still compiles
ABIExisting extension binaries keep loading
Standard libraryExisting imports and functions remain available
ToolingDebuggers, 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.

AreaPEP themes
Syntax and compilerassignment expressions, pattern matching, exception groups
Object modeldescriptors, metaclasses, data model changes
Importsimport hooks, module specs, namespace packages
C APIstable ABI, multi-phase init, module state
Typingannotations, generics, postponed evaluation
Concurrencysubinterpreters, per-interpreter GIL, free-threading
Packagingwheels, metadata, build systems
Release processannual releases, support windows
Governancesteering council and decision process

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

90.21 Common Mistakes When Reading PEPs

MistakeBetter approach
Reading a Draft as final behaviorCheck status first
Ignoring rejected ideasUse them to understand design boundaries
Treating implementation notes as language lawSeparate semantics from implementation
Reading only the abstractRead motivation, specification, compatibility
Ignoring later PEPsCheck whether the PEP was superseded or amended
Using PEP as user docsUpdate proper documentation
Assuming current code exactly matches original PEPCompare 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.