PEP 842 – Module Exports

Ravencentric1 pts0 comments

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...

module names spam public python private

Related Articles