From 1e4d71d4b552494ff33ad03b76d2a590d58efe77 Mon Sep 17 00:00:00 2001
From: Stephen Finucane <stephenfin@redhat.com>
Date: Mon, 3 Oct 2022 16:14:50 +0100
Subject: [PATCH] Fix compatibility with Python 3.12, importlib-metadata 5.0

importlib-metadata 5.0 has removed support for dict-style interaction
with entrypoints [1]. This is going to eventually affect us when Python
3.12 is released but even before then anyone not properly using upper
constraints with an older Python 3.7-based release (the only Python
version where we require the third-party importlib-metadata package
rather than the stdlib importlib.metadata package) will be bitten. Fix
it now to address both.

[1] https://github.com/python/importlib_metadata/commit/dde2b9de2973ce1c6fa9ba21dfe81069b0baa77b

Signed-off-by: Stephen Finucane <stephenfin@redhat.com>
Change-Id: Ib9c2b0a14edea91e97d122d2ac93b650029f918e
Closes-Bug: #1991559
(cherry picked from commit 28fc7164dace83abc3ecc06fcb56f7ca880d735a)
---
 stevedore/_cache.py           | 12 ++++++++++--
 stevedore/tests/test_cache.py |  8 ++++++++
 2 files changed, 18 insertions(+), 2 deletions(-)

diff --git a/stevedore/_cache.py b/stevedore/_cache.py
index 20b631d..6a76bc8 100644
--- a/stevedore/_cache.py
+++ b/stevedore/_cache.py
@@ -97,8 +97,16 @@ def _hash_settings_for_path(path):
     return (h.hexdigest(), paths)
 
 
-def _build_cacheable_data(path):
+def _build_cacheable_data():
     real_groups = importlib_metadata.entry_points()
+
+    if not isinstance(real_groups, dict):
+        # importlib-metadata 4.0 or later (or stdlib importlib.metadata in
+        # Python 3.9 or later)
+        real_groups = {
+            name: real_groups.select(name=name) for name in real_groups.names
+        }
+
     # Convert the namedtuple values to regular tuples
     groups = {}
     for name, group_data in real_groups.items():
@@ -153,7 +161,7 @@ class Cache:
             with open(filename, 'r') as f:
                 data = json.load(f)
         except (IOError, json.JSONDecodeError):
-            data = _build_cacheable_data(path)
+            data = _build_cacheable_data()
             data['path_values'] = path_values
             if not self._disable_caching:
                 try:
diff --git a/stevedore/tests/test_cache.py b/stevedore/tests/test_cache.py
index 8bf49c8..0c5af67 100644
--- a/stevedore/tests/test_cache.py
+++ b/stevedore/tests/test_cache.py
@@ -54,3 +54,11 @@ class TestCache(utils.TestCase):
         mock_open.side_effect = IOError
         sot._get_data_for_path('fake')
         mock_mkdir.assert_not_called()
+
+    def test__build_cacheable_data(self):
+        # this is a rubbish test as we don't actually do anything with the
+        # data, but it's too hard to script since it's totally environmentally
+        # dependent and mocking out the underlying calls would remove the value
+        # of this test (we want to test those underlying API calls)
+        ret = _cache._build_cacheable_data()
+        self.assertIsInstance(ret['groups'], dict)