python Python PEPs ·

PEP 842 proposes __export__ for controlled module visibility in Python

engineer
announcement

PEP 842 introduces a new standard for Python modules, proposing an `__export__` variable to explicitly define and limit the visibility of module members. This aims to improve code maintainability and prevent accidental reliance on internal implementation details, a common issue with current underscore-based conventions. If adopted, this PEP would affect all Python developers writing or consuming modules, providing a more robust mechanism for defining public APIs and enhancing the clarity of module interfaces. The proposal is currently in the Draft status for Python 3.16.

  • Introduce __export__ variable for module member control
  • Improve module API clarity and maintainability
  • __export__ affects attribute access and __dir__ behavior
  • Implicit __all__ definition from __export__
  • Interaction with module __getattr__ and lazy imports
Features (1)
  • Introduce __export__ variable for module member control

    This PEP proposes the introduction of an `__export__` variable within Python modules. Modules can define this variable, typically a list or tuple of strings, to explicitly declare which members (variables, classes, functions) are intended for external access. Any member not listed in `__export__` will raise an `ImportError` when accessed from outside the module, effectively limiting visibility and preventing accidental use of internal components.

Enhancements (1)
  • Improve module API clarity and maintainability

    The motivation behind `__export__` is to address the ambiguity of private names (often denoted by underscores) and the difficulties in managing imported module visibility. By requiring explicit export, developers can clearly distinguish between public and internal APIs, reducing the risk of breaking changes for users and decreasing the mental overhead associated with prefixing names.

Notes (3)
  • __export__ affects attribute access and __dir__ behavior

    When `__export__` is defined, all module attribute accesses will be filtered against it, raising `ImportError` for unexported names. Additionally, the module's `__dir__()` function will be modified to exclude unexported names, unless a custom `__dir__` is defined. Dunder names (`__name__`, `__dict__`, etc.) remain accessible regardless of `__export__`.

  • Implicit __all__ definition from __export__

    If a module defines `__export__` but not `__all__`, the `__all__` variable will be implicitly set to the contents of `__export__`. This ensures that wildcard imports (`from module import *`) will only import the explicitly exported members, further enforcing module API boundaries.

  • Interaction with module __getattr__ and lazy imports

    The behavior of `__export__` is not overridden by a module's `__getattr__()` function, as `__getattr__` is only invoked for names not already defined. Furthermore, lazy imports not listed in `__export__` will not be resolved when accessed outside the module, maintaining their lazy state.

Read the original announcement →

https://peps.python.org/pep-0842/

Related releases