PEP 842 – Module Exports | peps.python.org
Following system colour scheme
Selected dark colour scheme
Selected light colour scheme
PEP 842 – Module Exports
PEP 842 – Module Exports
Author:<br>Peter Bierma<br>Discussions-To:<br>Discourse thread<br>Status:<br>Draft<br>Type:<br>Standards Track<br>Created:<br>25-Jul-2026<br>Python-Version:<br>3.16<br>Post-History:<br>24-Jul-2026,<br>31-Jul-2026
Table of Contents<br>Abstract
Motivation<br>Module-level names need privacy
Prefixed names aren’t necessarily a great solution<br>It’s not always clear where names need prefixing
Prefixed names are not a universal rule
We want to be nice to users, not shrug them away<br>Library consumers use runtime introspection for documentation
__all__ is only a convention
Specification<br>__export__ rules<br>Object requirements
Item requirements
Module attribute access<br>Dunder names
Module __getattr__ functions
__dir__ behavior<br>User-defined module __dir__ functions
Implicit __all__ definitions
Semantic implementation
Rationale<br>__export__ is not an access modifier
Backwards Compatibility
Security Implications
How to Teach This
Reference Implementation<br>Performance
Rejected Ideas<br>Reuse __all__ for exports
Add new export syntax
Raising an exception upon accessing unexported attributes
Open Issues
Acknowledgements
Change History
Copyright
Abstract
This PEP proposes an __export__ variable that modules can define to<br>express intent about the visibility of variables from outside the module.
For example:
# spam.py<br>__export__ = ["Public"]
class Public:<br>...
class Private:<br>...
>>> import spam<br>>>> 'Public' in dir(spam)<br>True<br>>>> 'Private' in dir(spam)<br>False<br>>>> spam.Public
>>> spam.Private<br>:1: RuntimeWarning: 'Private' is not exported by 'spam'
This is not intended to be an access modifier for Python; see<br>__export__ is not an access modifier.
Motivation
Module-level names need privacy
A developer is writing a Python module. The module is intended to have one<br>“public” class – a class that is intended for users of the module – called<br>PublicAPI. As part of implementing PublicAPI, the developer wants to<br>create another class, called Helper. However, Helper is not meant to<br>be public in the same way that PublicAPI is public. Helper is supposed<br>to only be used by the developer of the module – a “private” API.
Nonetheless, the developer declares the two classes as such:
# spam.py<br>class Helper:<br>...
class PublicAPI:<br>...
The problem with this is that Helper comes with no indication that it’s not<br>a public API. It shows up in autocomplete by language servers, the dir()<br>function, Python’s interactive help() function, and every other API meant<br>for introspection. How are users supposed to know that they aren’t supposed to<br>use this?
Prefixed names aren’t necessarily a great solution
In Python, the convention for declaring private names is to prefix it<br>with _. So, the developer changes Helper into _Helper:
# spam.py<br>class _Helper:<br>...
This is generally the standard for Python libraries today, but it’s not clear<br>that this is the best long term solution. This works (with some caveats; see the<br>sections below), but this is (subjectively) less readable, and does require<br>more keystrokes by the maintainer. Ideally, users shouldn’t be tempted to<br>reach for private names from modules in the first place.
However, it is acknowledged that this idea is going against 30 years of<br>convention; even if this PEP is accepted, it’s expected that “underscored”<br>names (names prefixed with a leading _) will remain a staple of Python<br>for years to come. The purpose of this PEP is not to eliminate the need for<br>_ in module-level names, but instead to clear up corner cases where a<br>private name is ambiguous or tempting. In other words, this PEP is intended<br>to improve expressiveness and clarity with private APIs, not to add brand<br>new functionality.
It’s not always clear where names need prefixing
Python defines names through many different constructs, some of which are not<br>always clear or intuitive to the developer. As a result, it can be difficult to<br>remember where names need to be prefixed. To put this issue into perspective,<br>imagine that a developer wants to import some other modules in their code:
# spam.py<br>import argparse<br>import asyncio<br>import tabnanny
In the above example, the spam module will have argparse, asyncio,<br>and tabnanny as seemingly public attributes. In practice, this is not good<br>for a maintainer, because maintainers may want to remove and change imports as<br>they please, so these attributes should not be treated as public APIs.
Python’s standard library currently sidesteps this problem through a note in<br>the backwards compatibility policy (PEP 387) that states that imported<br>modules are not considered public APIs and may change at any time, but<br>unfortunately, users are unable to determine this without directly reading<br>the backwards compatibility policy, which is not a common thing to do.<br>The solution to this is to also prefix every imported name with _:
# spam.py<br>import argparse as...