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 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
referencesNot 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 conservativeThe 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.8The 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-levelDo 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 avoidedFor 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 sequencesThe 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 structureTests 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 pathWhen implementing or modifying a Standards Track feature, check:
grammar changes
AST changes
compiler changes
bytecode behavior
runtime helpers
documentation
tests
C API if exposedA 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 explanationFor 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 branchesProcess PEPs matter when deciding whether a change belongs in:
main branch only
a maintenance branch
a security branch
documentation only
a future versionThey 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 burdenRejected 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 strategyFor 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 commitsUse 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 constraintsTherefore, read both:
PEP
current docs
current tests
current sourceIf 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 casesFor 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 relevantDo 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 behaviorA change probably does not need a PEP when it is:
small bug fix
internal refactor
test improvement
documentation clarification
minor performance improvement
private API cleanupWhen 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.