Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New approach to lazy loading of pygame submodules (surfarray, sndarray) #3249

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/reST/ref/sndarray.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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 to avoid an expensive NumPy import when unnecessary

.. function:: array

| :sl:`copy Sound samples into an array`
Expand Down
2 changes: 2 additions & 0 deletions docs/reST/ref/surfarray.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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 sndarray module is lazily loaded to avoid an expensive NumPy import when unnecessary

.. function:: array2d

| :sl:`Copy pixels into a 2d array`
Expand Down
47 changes: 45 additions & 2 deletions src_py/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -300,15 +300,54 @@ def PixelArray(surface): # pylint: disable=unused-argument
except (ImportError, OSError):
scrap = MissingModule("scrap", urgent=0)

# Two lazily imported modules to avoid loading numpy unnecessarily

from importlib.util import LazyLoader, find_spec, module_from_spec


def lazy_import(name):
"""Lazily import a pygame module.

See https://docs.python.org/3/library/importlib.html#implementing-lazy-imports
Only load the module upon its first attribute access.

Lazily imported modules are directly referenced in packager_imports function.
"""
spec = find_spec("pygame." + name)
loader = LazyLoader(spec.loader)
spec.loader = loader
module = module_from_spec(spec)
sys.modules[spec.name] = module
loader.exec_module(module)
return module


# Check if numpy is available for surfarray and sndarray modules
numpy_missing = find_spec("numpy") is None

try:
import pygame.surfarray
if numpy_missing:
# Always fails here. Need the error message for MissingModule.reason
import numpy # pylint: disable=ungrouped-imports
# Check that module dependencies are not missing, or get error message
import pygame.pixelcopy # pylint: disable=ungrouped-imports
except (ImportError, OSError):
surfarray = MissingModule("surfarray", urgent=0)
else:
surfarray = lazy_import("surfarray")

try:
import pygame.sndarray
if numpy_missing:
# Always fails here. Need the error message for MissingModule.reason
import numpy # pylint: disable=ungrouped-imports
# Check that module dependencies are not missing, or get error message
import pygame.mixer # pylint: disable=ungrouped-imports
except (ImportError, OSError):
sndarray = MissingModule("sndarray", urgent=0)
else:
sndarray = lazy_import("sndarray")

del LazyLoader, find_spec, lazy_import, module_from_spec, numpy_missing

try:
import pygame._debug
Expand Down Expand Up @@ -366,6 +405,10 @@ def packager_imports():
import pygame.macosx
import pygame.colordict

# lazy imports
import pygame.surfarray
import pygame.sndarray


# make Rects pickleable

Expand Down
Loading