From e22b31f0681c14949c22109e93c59941c2ada0b7 Mon Sep 17 00:00:00 2001 From: aatle <168398276+aatle@users.noreply.github.com> Date: Mon, 25 Nov 2024 00:24:51 -0800 Subject: [PATCH] Improve documentation of new lazy loading system --- docs/reST/ref/sndarray.rst | 2 ++ docs/reST/ref/surfarray.rst | 2 ++ src_py/__init__.py | 15 +++++++++++++++ 3 files changed, 19 insertions(+) diff --git a/docs/reST/ref/sndarray.rst b/docs/reST/ref/sndarray.rst index bc2899f614..95f0154fee 100644 --- a/docs/reST/ref/sndarray.rst +++ b/docs/reST/ref/sndarray.rst @@ -23,6 +23,8 @@ Each sample is an 8-bit or 16-bit integer, depending on the data format. A stereo sound file has two values per sample, while a mono sound file only has one. +.. versionchanged:: 2.5.3 sndarray module is lazily loaded + .. function:: array | :sl:`copy Sound samples into an array` diff --git a/docs/reST/ref/surfarray.rst b/docs/reST/ref/surfarray.rst index 48b917fbfd..2fd3435693 100644 --- a/docs/reST/ref/surfarray.rst +++ b/docs/reST/ref/surfarray.rst @@ -35,6 +35,8 @@ pixels from the surface and any changes performed to the array will make changes in the surface. As this last functions share memory with the surface, this one will be locked during the lifetime of the array. +.. versionchanged:: 2.5.3 surfarray module is lazily loaded + .. function:: array2d | :sl:`Copy pixels into a 2d array` diff --git a/src_py/__init__.py b/src_py/__init__.py index 2f8ea4e058..6676713fd3 100644 --- a/src_py/__init__.py +++ b/src_py/__init__.py @@ -264,17 +264,32 @@ def PixelArray(surface): # pylint: disable=unused-argument # lastly, the "optional" pygame modules +# Private, persisting alias for use in __getattr__ _MissingModule = MissingModule def __getattr__(name): + """Implementation of lazy loading for some optional pygame modules. + + The surfarray and sndarray submodules use numpy, so they are loaded + lazily to avoid a heavy numpy import if the modules are never used. + + The first access of a lazily loaded submodule loads it and sets it + as an attribute on the pygame module. Pygame itself doesn't import these modules. + If the first access is an attribute access and not an import, then __getattr__ is + invoked (as the attribute isn't set yet), which imports the module dynamically. + + All lazy submodules are directly referenced in the packager_imports function. + """ from importlib import import_module LAZY_MODULES = "surfarray", "sndarray" if name not in LAZY_MODULES: + # Normal behavior for attribute accesses that aren't lazy modules raise AttributeError(f"module '{__name__}' has no attribute '{name}'") try: module = import_module(f"{__name__}.{name}") + # A successful import automatically sets the module attribute on the package except (ImportError, OSError): module = _MissingModule(name, urgent=0) globals()[name] = module