diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e33b8f..9fe8950 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,12 +5,6 @@ All notable changes to WuttJamaican will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). -## v0.28.10 (2026-03-17) - -### Fix - -- add `lists` param for `load_entry_points()` function - ## v0.28.9 (2026-03-04) ### Fix diff --git a/docs/conf.py b/docs/conf.py index a0ce914..81229b2 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -42,7 +42,6 @@ intersphinx_mapping = { "rattail": ("https://docs.wuttaproject.org/rattail/", None), "rattail-manual": ("https://docs.wuttaproject.org/rattail-manual/", None), "rich": ("https://rich.readthedocs.io/en/latest/", None), - "setuptools": ("https://setuptools.pypa.io/en/latest/", None), "sqlalchemy": ("http://docs.sqlalchemy.org/en/latest/", None), "wutta-continuum": ("https://docs.wuttaproject.org/wutta-continuum/", None), "wuttasync": ("https://docs.wuttaproject.org/wuttasync/", None), diff --git a/docs/glossary.rst b/docs/glossary.rst index f41aa60..47df4b0 100644 --- a/docs/glossary.rst +++ b/docs/glossary.rst @@ -222,9 +222,13 @@ Glossary entry point This refers to a "setuptools-style" entry point specifically, which is a mechanism used to register "plugins" and the like. - This lets the app / config discover features dynamically. + This lets the app / config discover features dynamically. Most + notably used to register :term:`commands` and + :term:`subcommands`. - For more info see :doc:`setuptools:userguide/entry_point` in the setuptools docs. + For more info see the `Python Packaging User Guide`_. + + .. _Python Packaging User Guide: https://packaging.python.org/en/latest/specifications/entry-points/ handler Similar to a "plugin" concept but only *one* handler may be used diff --git a/pyproject.toml b/pyproject.toml index 154a72d..cf9182a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "hatchling.build" [project] name = "WuttJamaican" -version = "0.28.10" +version = "0.28.9" description = "Base package for Wutta Framework" readme = "README.md" authors = [{name = "Lance Edgar", email = "lance@wuttaproject.org"}] diff --git a/src/wuttjamaican/util.py b/src/wuttjamaican/util.py index a3ac393..b1ef966 100644 --- a/src/wuttjamaican/util.py +++ b/src/wuttjamaican/util.py @@ -2,7 +2,7 @@ ################################################################################ # # WuttJamaican -- Base package for Wutta Framework -# Copyright © 2023-2026 Lance Edgar +# Copyright © 2023-2025 Lance Edgar # # This file is part of Wutta Framework. # @@ -110,36 +110,22 @@ def get_value(obj, key): return getattr(obj, key) -def load_entry_points(group, lists=False, ignore_errors=False): +def load_entry_points(group, ignore_errors=False): """ - Load a set of ``setuptools``-style :term:`entry points `. + Load a set of ``setuptools``-style entry points. - This is used to locate "plugins" and similar things, e.g. discover - which batch handlers are installed. - - Logic will inspect the registered entry points and return a dict - whose keys are the entry point names. By default the dict values - will be the loaded objects as referenced by each entry point. - - In some cases (notably, import handlers for wuttasync) the keys - may not always be unique. This allows multiple projects to define - entry points for the same key. If you specify ``lists=True`` then - the dict values will each be *lists* of loaded objects instead. - (Otherwise some entry points would be discarded when duplicate - keys are found.) + This is used to locate "plugins" and similar things, e.g. the set + of subcommands which belong to a main command. :param group: The group (string name) of entry points to be loaded, e.g. ``'wutta.commands'``. - :param lists: Whether to return lists instead of single object - values. - :param ignore_errors: If false (the default), any errors will be raised normally. If true, errors will be logged but not raised. - :returns: A dict of entry points, as described above. + :returns: A dictionary whose keys are the entry point names, and + values are the loaded entry points. """ entry_points = {} @@ -164,16 +150,7 @@ def load_entry_points(group, lists=False, ignore_errors=False): raise log.warning("failed to load entry point: %s", entry_point, exc_info=True) else: - if lists: - entry_points.setdefault(entry_point.name, []).append(ep) - else: - if entry_point.name in entry_points: - log.warning( - "overwriting existing key '%s' with entry point: %s", - entry_point.name, - ep, - ) - entry_points[entry_point.name] = ep + entry_points[entry_point.name] = ep return entry_points diff --git a/tests/test_util.py b/tests/test_util.py index 9106d8c..ddf220e 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -184,46 +184,6 @@ class TestLoadEntryPoints(TestCase): entry_points.select.assert_called_once_with(group="wuttatest.thingers") entry_point.load.assert_called_once_with() - def test_duplicate_key(self): - - # two entry points, with same name - ep1 = MagicMock() - ep2 = MagicMock() - ep1.name = "not-quite-unique-key" - ep2.name = "not-quite-unique-key" - ep1.load.return_value = ep1 - ep2.load.return_value = ep2 - - # they both are included for "all" entry points - entry_points = MagicMock() - entry_points.select.return_value = [ep1, ep2] - importlib = MagicMock() - importlib.metadata.entry_points.return_value = entry_points - with patch.dict("sys.modules", **{"importlib": importlib}): - - # but only the 2nd entry point is returned - result = mod.load_entry_points("console_scripts") - self.assertIsInstance(result, dict) - self.assertGreaterEqual(len(result), 1) - self.assertIn("not-quite-unique-key", result) - self.assertIs(result["not-quite-unique-key"], ep2) - - def test_lists(self): - - # classic behvaior - result = mod.load_entry_points("console_scripts", lists=False) - self.assertIsInstance(result, dict) - self.assertIn("pip", result) - self.assertTrue(callable(result["pip"])) - - # lists behavior - result = mod.load_entry_points("console_scripts", lists=True) - self.assertIsInstance(result, dict) - self.assertIn("pip", result) - self.assertIsInstance(result["pip"], list) - self.assertEqual(len(result["pip"]), 1) - self.assertTrue(callable(result["pip"][0])) - class TestLoadObject(TestCase):