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:
parent
5802391382
commit
f654906029
|
@ -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
|
||||
|
||||
|
|
|
@ -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):
|
||||
|
||||
|
|
Loading…
Reference in a new issue