2
0
Fork 0

fix: fallback to importlib_metadata when loading entry points

since `pkg_resources` is deprecated for this purpose, per
https://setuptools.pypa.io/en/latest/pkg_resources.html
This commit is contained in:
Lance Edgar 2024-06-14 17:27:22 -05:00
parent 5802391382
commit f654906029
2 changed files with 56 additions and 96 deletions

View file

@ -2,7 +2,7 @@
################################################################################
#
# WuttJamaican -- Base package for Wutta Framework
# Copyright © 2023 Lance Edgar
# Copyright © 2023-2024 Lance Edgar
#
# This file is part of Wutta Framework.
#
@ -58,44 +58,27 @@ def load_entry_points(group, ignore_errors=False):
try:
# nb. this package was added in python 3.8
import importlib.metadata
import importlib.metadata as importlib_metadata
except ImportError:
# older setup, must use pkg_resources
# TODO: remove this section once we require python 3.8
from pkg_resources import iter_entry_points
for entry_point in iter_entry_points(group):
try:
ep = entry_point.load()
except:
if not ignore_errors:
raise
log.warning("failed to load entry point: %s", entry_point,
exc_info=True)
else:
entry_points[entry_point.name] = ep
import importlib_metadata
eps = importlib_metadata.entry_points()
if not hasattr(eps, 'select'):
# python < 3.10
eps = eps.get(group, [])
else:
# newer setup (python >= 3.8); can use importlib, but the
# details may vary
eps = importlib.metadata.entry_points()
if not hasattr(eps, 'select'):
# python < 3.10
eps = eps.get(group, [])
# python >= 3.10
eps = eps.select(group=group)
for entry_point in eps:
try:
ep = entry_point.load()
except:
if not ignore_errors:
raise
log.warning("failed to load entry point: %s", entry_point,
exc_info=True)
else:
# python >= 3.10
eps = eps.select(group=group)
for entry_point in eps:
try:
ep = entry_point.load()
except:
if not ignore_errors:
raise
log.warning("failed to load entry point: %s", entry_point,
exc_info=True)
else:
entry_points[entry_point.name] = ep
entry_points[entry_point.name] = ep
return entry_points

View file

@ -51,7 +51,44 @@ class TestLoadEntryPoints(TestCase):
# even in a testing environment. basic sanity check
result = util.load_entry_points('console_scripts', ignore_errors=True)
self.assertTrue(len(result) >= 1)
self.assertIn('pip', result)
self.assertIn('pytest', result)
def test_basic_pre_python_3_8(self):
# the goal here is to get coverage for code which would only
# run on python 3.7 and older, but we only need that coverage
# if we are currently testing python 3.8+
if sys.version_info.major == 3 and sys.version_info.minor < 8:
pytest.skip("this test is not relevant before python 3.8")
from importlib.metadata import entry_points
real_entry_points = entry_points()
class FakeEntryPoints(dict):
def get(self, group, default):
if hasattr(real_entry_points, 'select'):
return real_entry_points.select(group=group)
return real_entry_points.get(group, [])
importlib_metadata = MagicMock()
importlib_metadata.entry_points.return_value = FakeEntryPoints()
orig_import = __import__
def mock_import(name, *args, **kwargs):
if name == 'importlib.metadata':
raise ImportError
if name == 'importlib_metadata':
return importlib_metadata
return orig_import(name, *args, **kwargs)
with patch('builtins.__import__', side_effect=mock_import):
# load some entry points which should "always" be present,
# even in a testing environment. basic sanity check
result = util.load_entry_points('console_scripts', ignore_errors=True)
self.assertTrue(len(result) >= 1)
self.assertIn('pytest', result)
def test_error(self):
@ -86,66 +123,6 @@ class TestLoadEntryPoints(TestCase):
entry_points.select.assert_called_once_with(group='wuttatest.thingers')
entry_point.load.assert_called_once_with()
def test_pkg_resources_empty(self):
orig_import = __import__
def mock_import(name, *args, **kwargs):
if name == 'importlib.metadata':
raise ImportError
return orig_import(name, *args, **kwargs)
with patch('builtins.__import__', side_effect=mock_import):
# empty set returned for unknown group
result = util.load_entry_points('this_should_never_exist!!!!!!')
self.assertEqual(result, {})
def test_pkg_resources_basic(self):
orig_import = __import__
def mock_import(name, *args, **kwargs):
if name == 'importlib.metadata':
raise ImportError
return orig_import(name, *args, **kwargs)
with patch('builtins.__import__', side_effect=mock_import):
# load some entry points which should "always" be present,
# even in a testing environment. basic sanity check
result = util.load_entry_points('console_scripts', ignore_errors=True)
self.assertTrue(len(result) >= 1)
self.assertIn('pip', result)
def test_pkg_resources_error(self):
orig_import = __import__
entry_point = MagicMock()
entry_point.load.side_effect = NotImplementedError
iter_entry_points = MagicMock(return_value=[entry_point])
pkg_resources = MagicMock(iter_entry_points=iter_entry_points)
def mock_import(name, *args, **kwargs):
if name == 'importlib.metadata':
raise ImportError
return orig_import(name, *args, **kwargs)
with patch('builtins.__import__', side_effect=mock_import):
with patch.dict('sys.modules', **{'pkg_resources': pkg_resources}):
# empty set returned if errors suppressed
result = util.load_entry_points('wuttatest.thingers', ignore_errors=True)
self.assertEqual(result, {})
iter_entry_points.assert_called_once_with('wuttatest.thingers')
entry_point.load.assert_called_once_with()
# error is raised, if not suppressed
iter_entry_points.reset_mock()
entry_point.load.reset_mock()
self.assertRaises(NotImplementedError, util.load_entry_points, 'wuttatest.thingers')
iter_entry_points.assert_called_once_with('wuttatest.thingers')
entry_point.load.assert_called_once_with()
class TestLoadObject(TestCase):