The Evolution and Withdrawal of Python Enhancement Proposal 842 The Export Statement
The Python steering council and the broader developer community recently concluded a significant debate regarding the structural integrity and visibility of module-level names, following the formal withdrawal of Python Enhancement Proposal (PEP) 842. Titled "The export statement," the proposal sought to introduce a dedicated export keyword into the Python language. This keyword was intended to provide a formal, syntax-level mechanism for developers to define the public interface of a module, moving beyond the decades-old conventions of leading underscores and the __all__ list. While the proposal aimed to solve long-standing maintenance issues within the Python Standard Library, it was ultimately withdrawn by its author, Peter Bierma, after it became clear that the proposed solution did not sufficiently align with the diverse requirements of the third-party package ecosystem.
The Core Objectives of PEP 842
At its heart, PEP 842 was designed to address the ambiguity of "public" versus "private" names in Python modules. In the current state of the language, any name defined at the top level of a module is technically public and accessible via attribute access (e.g., module.name). The long-standing convention to signal privacy is to prefix a name with a single underscore (e.g., _helper). However, this is a social contract rather than a technical enforcement.
The proposal introduced a new export statement that would allow developers to explicitly mark variables, functions, and classes as part of the module’s public API. For example, a developer could write export class PublicAPI: to ensure the class was visible to introspection tools like dir() and help(), while leaving un-exported names effectively hidden from standard access.
The technical implementation involved the introduction of a new ExportError, a subclass of AttributeError, which would be raised whenever a user attempted to access a non-exported name from a module that had opted into the new system. This mechanism was not intended to be a strict access modifier—similar to those found in C++ or Java—but rather a way to prevent accidental usage of internal components.
Historical Context and the Motivation for Change
The motivation for PEP 842 was rooted in the increasing complexity of maintaining large-scale Python libraries. For thirty years, Python developers have relied on the _ prefix and the __all__ variable to manage visibility. However, these methods have proven fragile. The __all__ variable, in particular, is often criticized for violating the "Don’t Repeat Yourself" (DRY) principle, as developers must define a function and then manually add its name as a string to a list at the top or bottom of the file.
The proposal highlighted several systemic issues with the current convention:
- Introspection Pollution: Language servers and integrated development environments (IDEs) often suggest internal imports to users because those imports exist in the module’s namespace, even if they were never intended for public use.
- Ambiguity in Prefixes: In some contexts, such as the
ctypesmodule or C-accelerated modules like_asyncio, the underscore prefix is used for stable, public APIs or internal implementation details, creating confusion for consumers. - The "Consenting Adults" Dilemma: Python’s philosophy that "we are all consenting adults" encourages transparency, but it also leads to users unknowingly relying on private APIs. When these private APIs are changed or removed, it results in widespread breakage across the ecosystem.
Real-World Impact: Data and Case Studies
To support the necessity of the export statement, PEP 842 documented a series of high-profile "breakage events" where the lack of formal export controls led to significant downstream failures. These case studies served as the empirical backbone of the proposal.
The os.errno Incident
In Python 3.7, the development team removed an internal import of the errno module from the os module. Although os.errno was never documented as a public API, thousands of scripts had relied on its presence because it appeared in autocomplete and dir(os). The removal caused a cascade of failures in legacy systems that had to be patched globally.
Vendoring Breakage in Requests and Botocore
The requests library and botocore (the foundation of the AWS CLI) both utilized internal "vendoring" for dependencies. Users began importing these vendored packages directly (e.g., from requests.packages import urllib3). When these libraries eventually unvendored those dependencies or moved them, it broke a vast number of third-party tools that had treated the internal namespace as a stable source of truth.
SciPy and Pandas Deprecations
Both SciPy and pandas have faced similar struggles, where sub-packages visible at the top-level module were treated as public by users. The maintenance burden of keeping these "accidental" APIs alive for backward compatibility has cost these projects hundreds of developer hours.
Technical Specifications and Grammar Changes
The proposal suggested a sophisticated integration into Python’s grammar. It proposed making export a "soft keyword," similar to match or case in Python 3.10, ensuring that existing code using export as a variable name would not break.
The proposed syntax included:
- Standalone Exports:
export name1, name2 - Export Assignments:
export version = "1.0.0" - Export Definitions:
export def my_function():andexport class MyClass: - Module Re-exports:
from module export name
Under the hood, using any export statement would implicitly create a __export__ list in the module’s globals. The module object’s __getattribute__ and __dir__ methods would then be modified to filter results based on this list. This would effectively prune the output of dir(module) to show only what the author intended, significantly cleaning up the experience for developers using interactive REPLs or IDEs.
Community Discussion and the Turning Point
The discussion on the Python Discourse forums, spanning from late July to early August 2026, revealed a deep divide within the community. While many core developers favored the improved maintenance story for the Standard Library, third-party library authors expressed reservations.
Guido van Rossum, the creator of Python, contributed to the debate by noting that while the ergonomics of __all__ were poor, adding a new parallel system like __export__ could introduce its own set of complexities. He argued that it is often distracting to have export information scattered throughout a file rather than centralized.
Other critics argued that the proposal went against the "Pythonic" nature of the language. Eric Smith famously noted that "access to internals of other classes is a feature when you need it," expressing concern that ExportError might eventually evolve into a hard access modifier that prevents legitimate debugging and monkey-patching.
The Decision to Withdraw
On August 7, 2026, Peter Bierma officially withdrew PEP 842. The withdrawal notice cited a fundamental misalignment between the motivation of the proposal and the needs of the broader ecosystem. While the export statement solved the problem of "leaky" namespaces in the Standard Library, it did not provide a compelling enough advantage for smaller third-party packages to justify a change to the language’s core grammar.
Furthermore, a competing proposal, PEP 844 ("public and private builtins"), suggested using decorators like @public and @private instead of new syntax. This alternative was seen by many as a less intrusive way to achieve the same documentation and enforcement goals without altering the PEG parser.
Implications for the Future of Python
The withdrawal of PEP 842 does not mean the problem of API visibility has been solved; rather, it highlights the community’s preference for incremental, convention-based improvements over radical syntax changes.
In the wake of the withdrawal, several key trends are expected to emerge:
- Enhanced Linter Roles: Tools like Ruff, Flake8, and Pylance are expected to take on a larger role in enforcing visibility. If a linter can detect when a user is importing a name not listed in
__all__, the need for runtime enforcement viaExportErrordiminishes. - Strict Re-exports: The convention of
import name as nameto signal intentional re-exporting is likely to be more strictly enforced by static analysis tools. - Continued Reliance on all: For the foreseeable future,
__all__remains the standard for defining public interfaces, despite its known ergonomic flaws.
The journey of PEP 842 serves as a case study in the evolution of a mature programming language. It demonstrates the rigorous vetting process required to change Python’s syntax and the high bar set for proposals that affect the delicate balance between developer freedom and library stability. While the export keyword will not be joining the Python 3.16 release, the discussions it sparked have provided a clearer roadmap for how the language might handle API boundaries in the decade to come.