Skip to content

Commit a8362d2

Browse files
chore: use importlib.metadata instead of pkg_resource (#428)
1 parent 412cab7 commit a8362d2

File tree

2 files changed

+88
-42
lines changed

2 files changed

+88
-42
lines changed

src/aws_encryption_sdk_cli/internal/master_key_parsing.py

Lines changed: 34 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@
1414
import copy
1515
import logging
1616
from collections import defaultdict
17+
from importlib.metadata import EntryPoint, distributions
1718

1819
import aws_encryption_sdk
19-
import pkg_resources
2020
from aws_encryption_sdk import CachingCryptoMaterialsManager # noqa pylint: disable=unused-import
2121
from aws_encryption_sdk import DefaultCryptoMaterialsManager # noqa pylint: disable=unused-import
2222
from aws_encryption_sdk.key_providers.base import MasterKeyProvider # noqa pylint: disable=unused-import
@@ -38,41 +38,49 @@
3838

3939
__all__ = ("build_crypto_materials_manager_from_args",)
4040
_LOGGER = logging.getLogger(LOGGER_NAME)
41-
_ENTRY_POINTS = defaultdict(dict) # type: DefaultDict[str, Dict[str, pkg_resources.EntryPoint]]
41+
_ENTRY_POINTS = defaultdict(dict) # type: DefaultDict[str, Dict[str, EntryPoint]]
4242

4343

4444
def _discover_entry_points():
4545
# type: () -> None
4646
"""Discover all registered entry points."""
4747
_LOGGER.debug("Discovering master key provider plugins")
4848

49-
for entry_point in pkg_resources.iter_entry_points(MASTER_KEY_PROVIDERS_ENTRY_POINT):
50-
_LOGGER.info('Collecting plugin "%s" registered by "%s"', entry_point.name, entry_point.dist)
51-
_LOGGER.debug(
52-
"Plugin details: %s",
53-
dict(
54-
name=entry_point.name,
55-
module_name=entry_point.module_name,
56-
attrs=entry_point.attrs,
57-
extras=entry_point.extras,
58-
dist=entry_point.dist,
59-
),
60-
)
61-
62-
if PLUGIN_NAMESPACE_DIVIDER in entry_point.name:
63-
_LOGGER.warning(
64-
'Invalid substring "%s" in discovered entry point "%s". It will not be usable.',
65-
PLUGIN_NAMESPACE_DIVIDER,
66-
entry_point.name,
49+
for dist in distributions():
50+
dist_name = dist.metadata.get("Name") or dist.metadata.get("name") or "unknown"
51+
52+
for entry_point in dist.entry_points:
53+
if entry_point.group != MASTER_KEY_PROVIDERS_ENTRY_POINT:
54+
continue
55+
56+
# entry_point.value looks like "pkg.module:attr"
57+
module_name, _, attr = entry_point.value.partition(":")
58+
59+
_LOGGER.info('Collecting plugin "%s" registered by "%s"', entry_point.name, dist_name)
60+
_LOGGER.debug(
61+
"Plugin details: %s",
62+
dict(
63+
name=entry_point.name,
64+
module_name=module_name,
65+
attrs=[attr] if attr else [],
66+
extras=getattr(entry_point, "extras", ()),
67+
dist=dist_name,
68+
),
6769
)
68-
continue
6970

70-
# mypy has trouble with pkgs_resources.iter_entry_points members
71-
_ENTRY_POINTS[entry_point.name][entry_point.dist.project_name] = entry_point # type: ignore
71+
if PLUGIN_NAMESPACE_DIVIDER in entry_point.name:
72+
_LOGGER.warning(
73+
'Invalid substring "%s" in discovered entry point "%s". It will not be usable.',
74+
PLUGIN_NAMESPACE_DIVIDER,
75+
entry_point.name,
76+
)
77+
continue
78+
79+
_ENTRY_POINTS[entry_point.name][dist_name] = entry_point
7280

7381

7482
def _entry_points():
75-
# type: () -> DefaultDict[str, Dict[str, pkg_resources.EntryPoint]]
83+
# type: () -> DefaultDict[str, Dict[str, EntryPoint]]
7684
"""Discover all entry points for required groups if they have not already been found.
7785
7886
:returns: Mapping of group to name to entry points
@@ -109,7 +117,7 @@ def _load_master_key_provider(name):
109117

110118
raise BadUserArgumentError(
111119
"Multiple entry points discovered and no package specified. Packages discovered registered by: ({})".format(
112-
", ".join([str(entry.dist) for entry in entry_points.values()])
120+
", ".join(entry_points.keys())
113121
)
114122
)
115123

@@ -123,7 +131,7 @@ def _load_master_key_provider(name):
123131
).format(
124132
requested=name,
125133
entry_point=entry_point_name,
126-
discovered=", ".join([str(entry.dist) for entry in entry_points.values()]),
134+
discovered=", ".join(entry_points.keys()),
127135
)
128136
)
129137

test/unit/internal/test_master_key_parsing.py

Lines changed: 54 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,8 @@ def patch_aws_encryption_sdk(mocker):
5959

6060

6161
@pytest.fixture
62-
def patch_iter_entry_points(mocker):
63-
mocker.patch.object(master_key_parsing.pkg_resources, "iter_entry_points")
64-
yield master_key_parsing.pkg_resources.iter_entry_points
62+
def patch_distributions(mocker):
63+
yield mocker.patch.object(master_key_parsing, "distributions")
6564

6665

6766
@pytest.fixture
@@ -84,8 +83,10 @@ def entry_points_cleaner():
8483

8584

8685
# "name" is a special, non-overridable attribute on mock objects
87-
FakeEntryPoint = namedtuple("FakeEntryPoint", ["name", "module_name", "attrs", "extras", "dist"])
88-
FakeEntryPoint.__new__.__defaults__ = ("MODULE", "ATTRS", "EXTRAS", MagicMock(project_name="PROJECT"))
86+
FakeEntryPoint = namedtuple("FakeEntryPoint", ["name", "value", "group", "extras"])
87+
FakeEntryPoint.__new__.__defaults__ = ("MODULE:ATTR", "GROUP", "EXTRAS")
88+
89+
FakeDistribution = namedtuple("FakeDistribution", ["metadata", "entry_points"])
8990

9091

9192
def test_entry_points(monkeypatch):
@@ -99,8 +100,15 @@ def test_entry_points_aws_kms():
99100
assert master_key_parsing._entry_points()["aws-kms"]["aws-encryption-sdk-cli"].load() is aws_kms_master_key_provider
100101

101102

102-
def test_entry_points_invalid_substring(logger_stream, patch_iter_entry_points):
103-
patch_iter_entry_points.return_value = [FakeEntryPoint("BAD::NAME")]
103+
def test_entry_points_invalid_substring(logger_stream, patch_distributions):
104+
bad_ep = FakeEntryPoint(
105+
name="BAD::NAME",
106+
value="module:attr",
107+
group=master_key_parsing.MASTER_KEY_PROVIDERS_ENTRY_POINT,
108+
)
109+
fake_dist = FakeDistribution(metadata={"Name": "fake-dist"}, entry_points=[bad_ep])
110+
patch_distributions.return_value = [fake_dist]
111+
104112
master_key_parsing._discover_entry_points()
105113

106114
key = 'Invalid substring "::" in discovered entry point "BAD::NAME". It will not be usable.'
@@ -109,11 +117,28 @@ def test_entry_points_invalid_substring(logger_stream, patch_iter_entry_points):
109117
assert "BAD::NAME" not in master_key_parsing._ENTRY_POINTS
110118

111119

112-
def test_entry_points_multiple_per_name(entry_points_cleaner, patch_iter_entry_points):
113-
entry_point_a = FakeEntryPoint(name="aws-kms", dist=MagicMock(project_name="aws-encryption-sdk-cli"))
114-
entry_point_b = FakeEntryPoint(name="aws-kms", dist=MagicMock(project_name="some-other-thing"))
115-
entry_point_c = FakeEntryPoint(name="zzz", dist=MagicMock(project_name="yet-another-thing"))
116-
patch_iter_entry_points.return_value = [entry_point_a, entry_point_b, entry_point_c]
120+
def test_entry_points_multiple_per_name(entry_points_cleaner, patch_distributions):
121+
entry_point_a = FakeEntryPoint(
122+
name="aws-kms",
123+
value="module_a:attr",
124+
group=master_key_parsing.MASTER_KEY_PROVIDERS_ENTRY_POINT,
125+
)
126+
entry_point_b = FakeEntryPoint(
127+
name="aws-kms",
128+
value="module_b:attr",
129+
group=master_key_parsing.MASTER_KEY_PROVIDERS_ENTRY_POINT,
130+
)
131+
entry_point_c = FakeEntryPoint(
132+
name="zzz",
133+
value="module_c:attr",
134+
group=master_key_parsing.MASTER_KEY_PROVIDERS_ENTRY_POINT,
135+
)
136+
137+
dist_a = FakeDistribution(metadata={"Name": "aws-encryption-sdk-cli"}, entry_points=[entry_point_a])
138+
dist_b = FakeDistribution(metadata={"Name": "some-other-thing"}, entry_points=[entry_point_b])
139+
dist_c = FakeDistribution(metadata={"Name": "yet-another-thing"}, entry_points=[entry_point_c])
140+
141+
patch_distributions.return_value = [dist_a, dist_b, dist_c]
117142

118143
test = master_key_parsing._entry_points()
119144

@@ -142,9 +167,15 @@ def test_load_master_key_provider_known_name_only_multiple_entry_points(monkeypa
142167
"aws-kms",
143168
{
144169
"aws-encryption-sdk-cli": FakeEntryPoint(
145-
name="aws-kms", dist=MagicMock(project_name="aws-encryption-sdk-cli")
170+
name="aws-kms",
171+
value="module_a:attr",
172+
group=master_key_parsing.MASTER_KEY_PROVIDERS_ENTRY_POINT,
173+
),
174+
"my-fake-package": FakeEntryPoint(
175+
name="aws-kms",
176+
value="module_b:attr",
177+
group=master_key_parsing.MASTER_KEY_PROVIDERS_ENTRY_POINT,
146178
),
147-
"my-fake-package": FakeEntryPoint(name="aws-kms", module_name="my-fake-package"),
148179
},
149180
)
150181

@@ -164,14 +195,21 @@ def test_load_master_key_provider_known_name_unknown_name(monkeypatch):
164195
monkeypatch.setitem(
165196
master_key_parsing._ENTRY_POINTS,
166197
"aws-kms",
167-
{"my-fake-package": FakeEntryPoint(name="aws-kms", module_name="my-fake-package")},
198+
{
199+
"my-fake-package": FakeEntryPoint(
200+
name="aws-kms",
201+
value="module_b:attr",
202+
group=master_key_parsing.MASTER_KEY_PROVIDERS_ENTRY_POINT,
203+
)
204+
},
168205
)
169206

170207
with pytest.raises(BadUserArgumentError) as excinfo:
171208
master_key_parsing._load_master_key_provider("aws-encryption-sdk-cli::aws-kms")
172209

173210
excinfo.match(
174-
r'Requested master key provider not found: "aws-encryption-sdk-cli::aws-kms". Packages discovered for *'
211+
r'Requested master key provider not found: "aws-encryption-sdk-cli::aws-kms". '
212+
r'Packages discovered for "aws-kms" registered by: .*'
175213
)
176214

177215

0 commit comments

Comments
 (0)