diff --git a/CHANGELOG.md b/CHANGELOG.md index 85876a1801..488da341a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `opentelemetry-instrumentation` Catch `ModuleNotFoundError` when the library is not installed and log as debug instead of exception ([#3423](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3423)) +- Add `get_dist_dependency_conflicts` back to opentelemetry-instrumentation + ([#3418](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3418)) ## Version 1.32.0/0.53b0 (2025-04-10) diff --git a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/dependencies.py b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/dependencies.py index 080f3553bb..8e5ee9aaaf 100644 --- a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/dependencies.py +++ b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/dependencies.py @@ -14,12 +14,14 @@ from __future__ import annotations +import warnings from logging import getLogger from typing import Collection from packaging.requirements import InvalidRequirement, Requirement from opentelemetry.util._importlib_metadata import ( + Distribution, PackageNotFoundError, version, ) @@ -49,6 +51,30 @@ def __str__(self): return str(self.conflict) +def get_dist_dependency_conflicts( + dist: Distribution, +) -> DependencyConflict | None: + warnings.warn( + "get_dist_dependency_conflicts is deprecated since 0.53b0 and will be removed in a future release.", + DeprecationWarning, + stacklevel=2, + ) + instrumentation_deps = [] + extra = "extra" + instruments = "instruments" + instruments_marker = {extra: instruments} + if dist.requires: + for dep in dist.requires: + if extra not in dep or instruments not in dep: + continue + + req = Requirement(dep) + if req.marker.evaluate(instruments_marker): + instrumentation_deps.append(req) + + return get_dependency_conflicts(instrumentation_deps) + + def get_dependency_conflicts( deps: Collection[str | Requirement], ) -> DependencyConflict | None: diff --git a/opentelemetry-instrumentation/tests/test_dependencies.py b/opentelemetry-instrumentation/tests/test_dependencies.py index a06d3923ea..ca04833181 100644 --- a/opentelemetry-instrumentation/tests/test_dependencies.py +++ b/opentelemetry-instrumentation/tests/test_dependencies.py @@ -20,8 +20,10 @@ from opentelemetry.instrumentation.dependencies import ( DependencyConflict, get_dependency_conflicts, + get_dist_dependency_conflicts, ) from opentelemetry.test.test_base import TestBase +from opentelemetry.util._importlib_metadata import Distribution class TestDependencyConflicts(TestBase): @@ -62,3 +64,41 @@ def test_get_dependency_conflicts_mismatched_version(self): str(conflict), f'DependencyConflict: requested: "pytest == 5000" but found: "pytest {pytest.__version__}"', ) + + def test_get_dist_dependency_conflicts(self): + class MockDistribution(Distribution): + def locate_file(self, path): + pass + + def read_text(self, filename): + pass + + @property + def requires(self): + return ['test-pkg ~= 1.0; extra == "instruments"'] + + dist = MockDistribution() + + conflict = get_dist_dependency_conflicts(dist) + self.assertTrue(conflict is not None) + self.assertTrue(isinstance(conflict, DependencyConflict)) + self.assertEqual( + str(conflict), + 'DependencyConflict: requested: "test-pkg~=1.0; extra == "instruments"" but found: "None"', + ) + + def test_get_dist_dependency_conflicts_requires_none(self): + class MockDistribution(Distribution): + def locate_file(self, path): + pass + + def read_text(self, filename): + pass + + @property + def requires(self): + return None + + dist = MockDistribution() + conflict = get_dist_dependency_conflicts(dist) + self.assertTrue(conflict is None)