Skip to content

Commit fe77be5

Browse files
committed
Use importlib_metadata for 3.8 and 3.9 only
1 parent d0fb920 commit fe77be5

File tree

6 files changed

+135
-29
lines changed

6 files changed

+135
-29
lines changed

opentelemetry-api/pyproject.toml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,7 @@ classifiers = [
2727
]
2828
dependencies = [
2929
"Deprecated >= 1.2.6",
30-
# FIXME This should be able to be removed after 3.12 is released if there is a reliable API
31-
# in importlib.metadata.
32-
"importlib-metadata >= 6.0, <= 8.4.0",
30+
"importlib-metadata >= 6.0, <= 8.4.0; python_version >= '3.8' and python_version <= '3.9'",
3331
]
3432
dynamic = [
3533
"version",

opentelemetry-api/src/opentelemetry/util/_importlib_metadata.py

Lines changed: 63 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,72 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
# FIXME: Use importlib.metadata when support for 3.11 is dropped if the rest of
16-
# the supported versions at that time have the same API.
17-
from importlib_metadata import ( # type: ignore
18-
EntryPoint,
19-
EntryPoints,
20-
entry_points,
21-
version,
22-
)
23-
2415
# The importlib-metadata library has introduced breaking changes before to its
2516
# API, this module is kept just to act as a layer between the
2617
# importlib-metadata library and our project if in any case it is necessary to
2718
# do so.
2819

20+
from sys import version_info
21+
22+
if version_info.minor < 10:
23+
24+
# pylint: disable=import-error
25+
from importlib_metadata import ( # type: ignore
26+
EntryPoint,
27+
EntryPoints,
28+
entry_points,
29+
version,
30+
)
31+
32+
else:
33+
34+
from importlib.metadata import (
35+
EntryPoint,
36+
EntryPoints,
37+
entry_points,
38+
version,
39+
)
40+
41+
42+
# pylint: disable=function-redefined
43+
def entry_points(**params) -> EntryPoints: # noqa
44+
"""
45+
Same as entry_points but requires at least one argument
46+
47+
For Python 3.8 or 3.9:
48+
49+
isinstance(
50+
importlib_metadata.entry_points(), importlib_metadata.EntryPoints
51+
)
52+
53+
evaluates to True.
54+
55+
For Python 3.10, 3.11:
56+
57+
isinstance(
58+
importlib.metadata.entry_points(), importlib.metadata.SelectableGroups
59+
)
60+
61+
evaluates to True.
62+
63+
For Python 3.12:
64+
65+
isinstance(
66+
importlib.metadata.entry_points(), importlib.metadata.EntryPoints
67+
)
68+
69+
evaluates to True.
70+
71+
So, when called with no arguments, entry_points returns objects of
72+
different types depending on the Python version that is being used. This is
73+
obviously very problematic. Nevertheless, in our code we don't ever call
74+
entry_points without arguments, so the approach here is to redefine
75+
entry_points so that it requires at least one argument.
76+
"""
77+
78+
if not params:
79+
raise ValueError("entry_points requires at least one argument")
80+
return entry_points(**params)
81+
82+
2983
__all__ = ["entry_points", "version", "EntryPoint", "EntryPoints"]
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
asgiref==3.7.2
2+
Deprecated==1.2.14
3+
iniconfig==2.0.0
4+
packaging==24.0
5+
pluggy==1.5.0
6+
py-cpuinfo==9.0.0
7+
pytest==7.4.4
8+
tomli==2.0.1
9+
typing_extensions==4.10.0
10+
wrapt==1.16.0
11+
zipp==3.19.2
12+
-e opentelemetry-sdk
13+
-e opentelemetry-semantic-conventions
14+
-e tests/opentelemetry-test-utils
15+
-e opentelemetry-api

opentelemetry-api/tests/util/test__importlib_metadata.py

Lines changed: 52 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,46 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15+
from sys import version_info
1516
from unittest import TestCase
1617

1718
from opentelemetry.metrics import MeterProvider
1819
from opentelemetry.util._importlib_metadata import EntryPoint, EntryPoints
1920
from opentelemetry.util._importlib_metadata import (
2021
entry_points as importlib_metadata_entry_points,
2122
)
23+
from opentelemetry.util._importlib_metadata import version
24+
25+
26+
class TestDependencies(TestCase):
27+
28+
def test_dependencies(self):
29+
30+
# pylint: disable=import-outside-toplevel
31+
# pylint: disable=unused-import
32+
# pylint: disable=import-error
33+
if version_info.minor < 10:
34+
try:
35+
import importlib_metadata # type: ignore
36+
37+
except ImportError:
38+
self.fail(
39+
"importlib_metadata not installed when testing with "
40+
f"{version_info.major}.{version_info.minor}"
41+
)
42+
43+
else:
44+
try:
45+
import importlib_metadata # noqa
46+
47+
except ImportError:
48+
pass
49+
50+
else:
51+
self.fail(
52+
"importlib_metadata installed when testing with "
53+
f"{version_info.major}.{version_info.minor}"
54+
)
2255

2356

2457
class TestEntryPoints(TestCase):
@@ -40,27 +73,28 @@ def test_uniform_behavior(self):
4073
"""
4174
Test that entry_points behaves the same regardless of the Python
4275
version.
43-
"""
44-
45-
entry_points = importlib_metadata_entry_points()
4676
47-
self.assertIsInstance(entry_points, EntryPoints)
77+
importlib.metadata was introduced in 3.8 as a replacement for
78+
pkg_resources. The problem is that the API of importlib.metadata
79+
changed in subsequent versions.
4880
49-
entry_points = entry_points.select(group="opentelemetry_propagator")
50-
self.assertIsInstance(entry_points, EntryPoints)
81+
For example, in 3.8 or 3.9 importlib.metadata.entry_points does not
82+
support the keyword arguments group or name, but those keyword
83+
arguments are supported in 3.10, 3.11 and 3.12.
5184
52-
entry_points = entry_points.select(name="baggage")
53-
self.assertIsInstance(entry_points, EntryPoints)
85+
There exists a package named importlib-metadata that has an API that
86+
includes a function named importlib_metadata.entry_points which
87+
supports the keyword arguments group and name, so we use
88+
importlib_metadata.entry_points when running in 3.8 or 3.9.
5489
55-
entry_point = next(iter(entry_points))
56-
self.assertIsInstance(entry_point, EntryPoint)
90+
importlib_metadata.entry_points and importlib.metadata.entry_points do
91+
not return objects of the same type when called without any arguments.
92+
That is why the implementation of the
93+
opentelemetry.util._importlib_metadata redefines entry_points so that
94+
it is mandatory to use an argument.
95+
"""
5796

58-
self.assertEqual(entry_point.name, "baggage")
59-
self.assertEqual(entry_point.group, "opentelemetry_propagator")
60-
self.assertEqual(
61-
entry_point.value,
62-
"opentelemetry.baggage.propagation:W3CBaggagePropagator",
63-
)
97+
self.assertRaises(ValueError, importlib_metadata_entry_points())
6498

6599
entry_points = importlib_metadata_entry_points(
66100
group="opentelemetry_propagator"
@@ -106,3 +140,5 @@ def test_uniform_behavior(self):
106140
entry_points = importlib_metadata_entry_points(group="abc", name="abc")
107141
self.assertIsInstance(entry_points, EntryPoints)
108142
self.assertEqual(len(entry_points), 0)
143+
144+
self.assertIsInstance(version("opentelemetry-api"), str)

tox.ini

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,10 @@ commands_pre =
134134

135135
mypy,mypyinstalled: pip install -r {toxinidir}/mypy-requirements.txt
136136

137-
api: pip install -r {toxinidir}/opentelemetry-api/test-requirements.txt
137+
py3{8,9}-test-opentelemetry-api: pip install -r {toxinidir}/opentelemetry-api/test-requirements-0.txt
138+
pypy3-test-opentelemetry-api: pip install -r {toxinidir}/opentelemetry-api/test-requirements-0.txt
139+
py3{10,11,12}-test-opentelemetry-api: pip install -r {toxinidir}/opentelemetry-api/test-requirements-1.txt
140+
lint-opentelemetry-api: pip install -r {toxinidir}/opentelemetry-api/test-requirements-1.txt
138141

139142
sdk: pip install -r {toxinidir}/opentelemetry-sdk/test-requirements.txt
140143
benchmark-opentelemetry-sdk: pip install -r {toxinidir}/opentelemetry-sdk/benchmark-requirements.txt

0 commit comments

Comments
 (0)