Skip to content

Commit a311b33

Browse files
authored
Fix import logic for importlib.metadata and pkg_resources (#1399)
* Rework imports of importlib.metadata and pkg_resources * Fix entrypoints kwargs * Tweak comment * Adjust tests for new package version checking
1 parent c7fe73b commit a311b33

File tree

4 files changed

+86
-41
lines changed

4 files changed

+86
-41
lines changed

newrelic/admin/__init__.py

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -121,21 +121,29 @@ def load_internal_plugins():
121121

122122
def load_external_plugins():
123123
try:
124-
# Preferred after Python 3.10
125-
if sys.version_info >= (3, 10):
126-
from importlib.metadata import entry_points
127-
# Introduced in Python 3.8
128-
elif sys.version_info >= (3, 8) and sys.version_info < (3, 9):
129-
from importlib_metadata import entry_points
130-
# Removed in Python 3.12
131-
else:
132-
from pkg_resources import iter_entry_points as entry_points
124+
# importlib.metadata was introduced into the standard library starting in Python 3.8.
125+
from importlib.metadata import entry_points
133126
except ImportError:
134-
return
127+
try:
128+
# importlib_metadata is a backport library installable from PyPI.
129+
from importlib_metadata import entry_points
130+
except ImportError:
131+
try:
132+
# Fallback to pkg_resources, which is available in older versions of setuptools.
133+
from pkg_resources import iter_entry_points as entry_points
134+
except ImportError:
135+
return
135136

136137
group = "newrelic.admin"
137138

138-
for entrypoint in entry_points(group=group):
139+
try:
140+
# group kwarg was only added to importlib.metadata.entry_points in Python 3.10.
141+
_entry_points = entry_points(group=group)
142+
except TypeError:
143+
# Grab entire entry_points dictionary and select group from it.
144+
_entry_points = entry_points().get(group, ())
145+
146+
for entrypoint in _entry_points:
139147
__import__(entrypoint.module_name)
140148

141149

newrelic/common/package_version_utils.py

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -95,23 +95,33 @@ def _get_package_version(name):
9595
except Exception:
9696
pass
9797

98-
# importlib was introduced into the standard library starting in Python3.8.
99-
if "importlib" in sys.modules and hasattr(sys.modules["importlib"], "metadata"):
98+
importlib_metadata = None
99+
# importlib.metadata was introduced into the standard library starting in Python 3.8.
100+
importlib_metadata = getattr(sys.modules.get("importlib", None), "metadata", None)
101+
if importlib_metadata is None:
102+
# importlib_metadata is a backport library installable from PyPI.
100103
try:
101-
# In Python3.10+ packages_distribution can be checked for as well
102-
if hasattr(sys.modules["importlib"].metadata, "packages_distributions"):
103-
distributions = sys.modules["importlib"].metadata.packages_distributions()
104+
import importlib_metadata
105+
except ImportError:
106+
pass
107+
108+
if importlib_metadata is not None:
109+
try:
110+
# In Python 3.10+ packages_distribution can be checked for as well.
111+
if hasattr(importlib_metadata, "packages_distributions"):
112+
distributions = importlib_metadata.packages_distributions()
104113
distribution_name = distributions.get(name, name)
105114
distribution_name = distribution_name[0] if isinstance(distribution_name, list) else distribution_name
106115
else:
107116
distribution_name = name
108117

109-
version = sys.modules["importlib"].metadata.version(distribution_name)
118+
version = importlib_metadata.version(distribution_name)
110119
if version not in NULL_VERSIONS:
111120
return version
112121
except Exception:
113122
pass
114123

124+
# Fallback to pkg_resources, which is available in older versions of setuptools.
115125
if "pkg_resources" in sys.modules:
116126
try:
117127
version = sys.modules["pkg_resources"].get_distribution(name).version

newrelic/config.py

Lines changed: 38 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4092,21 +4092,29 @@ def _process_module_builtin_defaults():
40924092

40934093
def _process_module_entry_points():
40944094
try:
4095-
# Preferred after Python 3.10
4096-
if sys.version_info >= (3, 10):
4097-
from importlib.metadata import entry_points
4098-
# Introduced in Python 3.8
4099-
elif sys.version_info >= (3, 8) and sys.version_info < (3, 9):
4100-
from importlib_metadata import entry_points
4101-
# Removed in Python 3.12
4102-
else:
4103-
from pkg_resources import iter_entry_points as entry_points
4095+
# importlib.metadata was introduced into the standard library starting in Python 3.8.
4096+
from importlib.metadata import entry_points
41044097
except ImportError:
4105-
return
4098+
try:
4099+
# importlib_metadata is a backport library installable from PyPI.
4100+
from importlib_metadata import entry_points
4101+
except ImportError:
4102+
try:
4103+
# Fallback to pkg_resources, which is available in older versions of setuptools.
4104+
from pkg_resources import iter_entry_points as entry_points
4105+
except ImportError:
4106+
return
41064107

41074108
group = "newrelic.hooks"
41084109

4109-
for entrypoint in entry_points(group=group):
4110+
try:
4111+
# group kwarg was only added to importlib.metadata.entry_points in Python 3.10.
4112+
_entry_points = entry_points(group=group)
4113+
except TypeError:
4114+
# Grab entire entry_points dictionary and select group from it.
4115+
_entry_points = entry_points().get(group, ())
4116+
4117+
for entrypoint in _entry_points:
41104118
target = entrypoint.name
41114119

41124120
if target in _module_import_hook_registry:
@@ -4164,21 +4172,29 @@ def _setup_instrumentation():
41644172

41654173
def _setup_extensions():
41664174
try:
4167-
# Preferred after Python 3.10
4168-
if sys.version_info >= (3, 10):
4169-
from importlib.metadata import entry_points
4170-
# Introduced in Python 3.8
4171-
elif sys.version_info >= (3, 8) and sys.version_info < (3, 9):
4172-
from importlib_metadata import entry_points
4173-
# Removed in Python 3.12
4174-
else:
4175-
from pkg_resources import iter_entry_points as entry_points
4175+
# importlib.metadata was introduced into the standard library starting in Python 3.8.
4176+
from importlib.metadata import entry_points
41764177
except ImportError:
4177-
return
4178+
try:
4179+
# importlib_metadata is a backport library installable from PyPI.
4180+
from importlib_metadata import entry_points
4181+
except ImportError:
4182+
try:
4183+
# Fallback to pkg_resources, which is available in older versions of setuptools.
4184+
from pkg_resources import iter_entry_points as entry_points
4185+
except ImportError:
4186+
return
41784187

41794188
group = "newrelic.extension"
41804189

4181-
for entrypoint in entry_points(group=group):
4190+
try:
4191+
# group kwarg was only added to importlib.metadata.entry_points in Python 3.10.
4192+
_entry_points = entry_points(group=group)
4193+
except TypeError:
4194+
# Grab entire entry_points dictionary and select group from it.
4195+
_entry_points = entry_points().get(group, ())
4196+
4197+
for entrypoint in _entry_points:
41824198
__import__(entrypoint.module_name)
41834199
module = sys.modules[entrypoint.module_name]
41844200
module.initialize()

tests/agent_unittests/test_package_version_utils.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,16 @@ def test_get_package_version_tuple(monkeypatch, attr, value, expected_value):
103103

104104
@SKIP_IF_NOT_IMPORTLIB_METADATA
105105
@validate_function_called("importlib.metadata", "version")
106-
def test_importlib_metadata():
106+
def test_importlib_dot_metadata():
107+
# Test for importlib.metadata from the standard library.
108+
version = get_package_version("pytest")
109+
assert version not in NULL_VERSIONS, version
110+
111+
112+
@SKIP_IF_IMPORTLIB_METADATA
113+
@validate_function_called("importlib_metadata", "version")
114+
def test_importlib_underscore_metadata():
115+
# Test for importlib_metadata, a backport library available on PyPI.
107116
version = get_package_version("pytest")
108117
assert version not in NULL_VERSIONS, version
109118

@@ -117,7 +126,9 @@ def test_mapping_import_to_distribution_packages():
117126

118127
@SKIP_IF_IMPORTLIB_METADATA
119128
@validate_function_called("pkg_resources", "get_distribution")
120-
def test_pkg_resources_metadata():
129+
def test_pkg_resources_metadata(monkeypatch):
130+
# Prevent importlib_metadata from being used by these tests
131+
monkeypatch.setitem(sys.modules, "importlib_metadata", None)
121132
version = get_package_version("pytest")
122133
assert version not in NULL_VERSIONS, version
123134

0 commit comments

Comments
 (0)