From e9204ee7b702f67c4de1c777db9482b065126407 Mon Sep 17 00:00:00 2001 From: Doug Hellmann Date: Sun, 12 Jul 2020 15:26:07 -0400 Subject: [PATCH] add property methods to extension for more entry point values The underlying EntryPoint class has some similar methods to fetch these values, but they depend on the version of importlib.metadata that is installed, so this provides a consistent API Change-Id: I7a1b9541f0e4042a224e49c890ba3c63e8a5a259 Signed-off-by: Doug Hellmann --- ...try-point-properties-6f2d868d4342fc0d.yaml | 6 +++ stevedore/extension.py | 21 +++++++++ stevedore/tests/test_extension.py | 46 +++++++++++++++++++ 3 files changed, 73 insertions(+) create mode 100644 releasenotes/notes/expose-entry-point-properties-6f2d868d4342fc0d.yaml diff --git a/releasenotes/notes/expose-entry-point-properties-6f2d868d4342fc0d.yaml b/releasenotes/notes/expose-entry-point-properties-6f2d868d4342fc0d.yaml new file mode 100644 index 0000000..22d7494 --- /dev/null +++ b/releasenotes/notes/expose-entry-point-properties-6f2d868d4342fc0d.yaml @@ -0,0 +1,6 @@ +--- +features: + - | + Add `extras` and `attr` properties to the `Extension` class to + make it easier for consumers to access the underlying properties, + regardless of the version of `importlib.metadata` being used. diff --git a/stevedore/extension.py b/stevedore/extension.py index abbc5c3..2ccdc27 100644 --- a/stevedore/extension.py +++ b/stevedore/extension.py @@ -58,6 +58,27 @@ class Extension(object): match = self.entry_point.pattern.match(self.entry_point.value) return match.group('module') + @property + def extras(self): + """The 'extras' settings for the plugin.""" + # NOTE: The underlying package returns re.Match objects for + # some reason. Translate those to the matched strings, which + # seem more useful. + return [ + # Python 3.6 returns _sre.SRE_Match objects. Later + # versions of python return re.Match objects. Both types + # have a 'string' attribute containing the text that + # matched the pattern. + getattr(e, 'string', e) + for e in self.entry_point.extras + ] + + @property + def attr(self): + """The attribute of the module to be loaded.""" + match = self.entry_point.pattern.match(self.entry_point.value) + return match.group('attr') + @property def entry_point_target(self): """The module and attribute referenced by this extension's entry_point. diff --git a/stevedore/tests/test_extension.py b/stevedore/tests/test_extension.py index 8fc9149..405fb88 100644 --- a/stevedore/tests/test_extension.py +++ b/stevedore/tests/test_extension.py @@ -16,6 +16,13 @@ import operator from unittest import mock +try: + # For python 3.8 and later + import importlib.metadata as importlib_metadata +except ImportError: + # For everyone else + import importlib_metadata + from stevedore import exception from stevedore import extension from stevedore.tests import utils @@ -241,3 +248,42 @@ class TestLoadRequirementsOldSetuptools(utils.TestCase): self.em._load_one_plugin(self.mock_ep, False, (), {}, verify_requirements=False) self.mock_ep.load.assert_called_once_with() + + +class TestExtensionProperties(utils.TestCase): + + def setUp(self): + self.ext1 = extension.Extension( + 'name', + importlib_metadata.EntryPoint( + 'name', 'module.name:attribute.name [extra]', 'group_name', + ), + mock.Mock(), + None, + ) + self.ext2 = extension.Extension( + 'name', + importlib_metadata.EntryPoint( + 'name', 'module:attribute', 'group_name', + ), + mock.Mock(), + None, + ) + + def test_module_name(self): + self.assertEqual('module.name', self.ext1.module_name) + self.assertEqual('module', self.ext2.module_name) + + def test_extras(self): + self.assertEqual(['[extra]'], self.ext1.extras) + self.assertEqual([], self.ext2.extras) + + def test_attr(self): + self.assertEqual('attribute.name', self.ext1.attr) + self.assertEqual('attribute', self.ext2.attr) + + def test_entry_point_target(self): + self.assertEqual('module.name:attribute.name [extra]', + self.ext1.entry_point_target) + self.assertEqual('module:attribute', + self.ext2.entry_point_target)