mirror of
https://github.com/panda3d/panda3d.git
synced 2025-09-28 15:53:55 -04:00
showbase: Switch Loader entry point detection to importlib.metadata
Only in Python 3.8 and up, where this module is available, otherwise it falls back to pkg_resources Add unit test for custom entry point loaders
This commit is contained in:
parent
c77697a2c0
commit
c1c035d5c9
@ -8,6 +8,7 @@ from panda3d.core import *
|
||||
from panda3d.core import Loader as PandaLoader
|
||||
from direct.directnotify.DirectNotifyGlobal import *
|
||||
from direct.showbase.DirectObject import DirectObject
|
||||
import sys
|
||||
|
||||
# You can specify a phaseChecker callback to check
|
||||
# a modelPath to see if it is being loaded in the correct
|
||||
@ -167,16 +168,25 @@ class Loader(DirectObject):
|
||||
if not ConfigVariableBool('loader-support-entry-points', True):
|
||||
return
|
||||
|
||||
import importlib
|
||||
try:
|
||||
pkg_resources = importlib.import_module('pkg_resources')
|
||||
except ImportError:
|
||||
pkg_resources = None
|
||||
if sys.version_info >= (3, 8):
|
||||
from importlib.metadata import entry_points
|
||||
eps = entry_points()
|
||||
if isinstance(eps, dict): # Python 3.8 and 3.9
|
||||
loaders = eps.get('panda3d.loaders', ())
|
||||
else:
|
||||
loaders = entry_points().select(group='panda3d.loaders')
|
||||
else:
|
||||
import importlib
|
||||
try:
|
||||
pkg_resources = importlib.import_module('pkg_resources')
|
||||
loaders = pkg_resources.iter_entry_points('panda3d.loaders')
|
||||
except ImportError:
|
||||
loaders = ()
|
||||
|
||||
if pkg_resources:
|
||||
if loaders:
|
||||
registry = LoaderFileTypeRegistry.getGlobalPtr()
|
||||
|
||||
for entry_point in pkg_resources.iter_entry_points('panda3d.loaders'):
|
||||
for entry_point in loaders:
|
||||
registry.register_deferred_type(entry_point)
|
||||
|
||||
cls._loadedPythonFileTypes = True
|
||||
|
@ -1,6 +1,7 @@
|
||||
from panda3d.core import Filename, NodePath
|
||||
from panda3d.core import Filename, NodePath, LoaderFileTypeRegistry
|
||||
from direct.showbase.Loader import Loader
|
||||
import pytest
|
||||
import sys
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@ -68,3 +69,105 @@ def test_load_model_missing(loader):
|
||||
def test_load_model_okmissing(loader):
|
||||
model = loader.load_model('/nonexistent.bam', okMissing=True)
|
||||
assert model is None
|
||||
|
||||
|
||||
def test_loader_entry_points(tmp_path):
|
||||
# A dummy loader for .fnrgl files.
|
||||
(tmp_path / "fnargle.py").write_text("""
|
||||
from panda3d.core import ModelRoot
|
||||
import sys
|
||||
|
||||
sys._fnargle_loaded = True
|
||||
|
||||
class FnargleLoader:
|
||||
name = "Fnargle"
|
||||
extensions = ['fnrgl']
|
||||
supports_compressed = False
|
||||
|
||||
@staticmethod
|
||||
def load_file(path, options, record=None):
|
||||
return ModelRoot("fnargle")
|
||||
""")
|
||||
(tmp_path / "fnargle.dist-info").mkdir()
|
||||
(tmp_path / "fnargle.dist-info" / "METADATA").write_text("""
|
||||
Metadata-Version: 2.0
|
||||
Name: fnargle
|
||||
Version: 1.0.0
|
||||
""")
|
||||
(tmp_path / "fnargle.dist-info" / "entry_points.txt").write_text("""
|
||||
[panda3d.loaders]
|
||||
fnrgl = fnargle:FnargleLoader
|
||||
""")
|
||||
|
||||
model_path = tmp_path / "test.fnrgl"
|
||||
model_path.write_text("")
|
||||
|
||||
if sys.version_info >= (3, 11):
|
||||
import sysconfig
|
||||
stdlib = sysconfig.get_path("stdlib")
|
||||
platstdlib = sysconfig.get_path("platstdlib")
|
||||
else:
|
||||
from distutils import sysconfig
|
||||
stdlib = sysconfig.get_python_lib(False, True)
|
||||
platstdlib = sysconfig.get_python_lib(True, True)
|
||||
|
||||
if sys.version_info < (3, 8):
|
||||
# Older Python versions don't have importlib.metadata, so we rely on
|
||||
# pkg_resources - but this caches the results once. Fortunately, it
|
||||
# provides this function for reinitializing the cached entry points.
|
||||
# See pypa/setuptools#373
|
||||
pkg_resources = pytest.importorskip("pkg_resources")
|
||||
if not hasattr(pkg_resources, "_initialize_master_working_set"):
|
||||
pytest.skip("pkg_resources too old")
|
||||
|
||||
registry = LoaderFileTypeRegistry.get_global_ptr()
|
||||
prev_loaded = Loader._loadedPythonFileTypes
|
||||
prev_path = sys.path
|
||||
file_type = None
|
||||
try:
|
||||
# We do this so we don't re-register thirdparty loaders
|
||||
sys.path = [str(tmp_path), platstdlib, stdlib]
|
||||
if sys.version_info < (3, 8):
|
||||
pkg_resources._initialize_master_working_set()
|
||||
|
||||
Loader._loadedPythonFileTypes = False
|
||||
|
||||
# base parameter is only used for audio
|
||||
loader = Loader(None)
|
||||
assert Loader._loadedPythonFileTypes
|
||||
|
||||
# Should be registered, not yet loaded
|
||||
file_type = registry.get_type_from_extension('fnrgl')
|
||||
assert file_type is not None
|
||||
assert not hasattr(sys, '_fnargle_loaded')
|
||||
|
||||
assert file_type.supports_load()
|
||||
assert not file_type.supports_save()
|
||||
assert not file_type.supports_compressed()
|
||||
assert file_type.get_extension() == 'fnrgl'
|
||||
|
||||
# The above should have caused it to load
|
||||
assert sys._fnargle_loaded
|
||||
assert 'fnargle' in sys.modules
|
||||
|
||||
# Now try loading a fnargle file
|
||||
model = loader.load_model(model_path)
|
||||
assert model is not None
|
||||
assert model.name == "fnargle"
|
||||
|
||||
finally:
|
||||
# Set everything back to what it was
|
||||
Loader._loadedPythonFileTypes = prev_loaded
|
||||
sys.path = prev_path
|
||||
|
||||
if hasattr(sys, '_fnargle_loaded'):
|
||||
del sys._fnargle_loaded
|
||||
|
||||
if 'fnargle' in sys.modules:
|
||||
del sys.modules['fnargle']
|
||||
|
||||
if file_type is not None:
|
||||
registry.unregister_type(file_type)
|
||||
|
||||
if sys.version_info < (3, 8):
|
||||
pkg_resources._initialize_master_working_set()
|
||||
|
Loading…
x
Reference in New Issue
Block a user