Notice: While Javascript is not essential for this website, your interaction with the content will be limited. Please turn Javascript on for the full experience.

PEP 562 -- Module __getattr__

PEP:562
Title:Module __getattr__
Author:Ivan Levkivskyi <levkivskyi at gmail.com>
Status:Draft
Type:Standards Track
Created:09-Sep-2017
Python-Version:3.7
Post-History:09-Sep-2017

Abstract

It is proposed to support __getattr__ function defined on modules to provide basic customization of module attribute access.

Rationale

It is sometimes convenient to customize or otherwise have control over access to module attributes. A typical example is managing deprecation warnings. Typical workarounds are assigning __class__ of a module object to a custom subclass of types.ModuleType or substituting sys.modules item with a custom wrapper instance. It would be convenient to simplify this procedure by recognizing __getattr__ defined directly in a module that would act like a normal __getattr__ method, except that it will be defined on module instances. For example:

# lib.py

from warnings import warn

deprecated_names = ["old_function", ...]

def _deprecated_old_function(arg, other):
    ...

def __getattr__(name):
    if name in deprecated_names:
        warn(f"{name} is deprecated", DeprecationWarning)
        return globals()[f"_deprecated_{name}"]
    raise AttributeError(f"module {__name__} has no attribute {name}")

# main.py

from lib import old_function  # Works, but emits the warning

Another widespread use case for __getattr__ would be lazy submodule imports. Consider a simple example:

# lib/__init__.py

import importlib

__all__ = ['submod', ...]

def __getattr__(name):
    if name in __all__:
        return importlib.import_module("." + name, __name__)
    raise AttributeError(f"module {__name__!r} has no attribute {name!r}")

# lib/submod.py

print("Submodule loaded")
class HeavyClass:
    ...

# main.py

import lib
lib.submodule.HeavyClass  # prints "Submodule loaded"

There is a related proposal PEP 549 that proposes to support instance properties for a similar functionality. The difference is this PEP proposes a faster and simpler mechanism, but provides more basic customization. An additional motivation for this proposal is that PEP 484 already defines the use of module __getattr__ for this purpose in Python stub files, see [1].

Specification

The __getattr__ function at the module level should accept one argument which is a name of an attribute and return the computed value or raise an AttributeError:

def __getattr__(name: str) -> Any: ...

This function will be called only if name is not found in the module through the normal attribute lookup.

The reference implementation for this PEP can be found in [2].

Backwards compatibility and impact on performance

This PEP may break code that uses module level (global) name __getattr__. The performance implications of this PEP are minimal, since __getattr__ is called only for missing attributes.

Source: https://github.com/python/peps/blob/master/pep-0562.txt