Skip to content

Commit 5b125b1

Browse files
authored
Fix auto-instrumentation dependency conflict detection (#530)
1 parent 9c834f0 commit 5b125b1

File tree

3 files changed

+54
-9
lines changed

3 files changed

+54
-9
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
66

77
## [Unreleased](https://github.com/open-telemetry/opentelemetry-python/compare/v1.3.0-0.22b0...HEAD)
88

9+
- `opentelemetry-instrumentation` Fixed cases where trying to use an instrumentation package without the
10+
target library was crashing auto instrumentation agent.
11+
([#530](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/530))
12+
913
## [0.22b0](https://github.com/open-telemetry/opentelemetry-python/releases/tag/v1.3.0-0.22b0) - 2021-06-01
1014

1115
### Changed

opentelemetry-instrumentation/src/opentelemetry/instrumentation/dependencies.py

+25-7
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
1+
from logging import getLogger
12
from typing import Collection, Optional
23

34
from pkg_resources import (
45
Distribution,
56
DistributionNotFound,
7+
RequirementParseError,
68
VersionConflict,
79
get_distribution,
810
)
911

12+
logger = getLogger(__file__)
13+
1014

1115
class DependencyConflict:
1216
required: str = None
@@ -25,22 +29,36 @@ def __str__(self):
2529
def get_dist_dependency_conflicts(
2630
dist: Distribution,
2731
) -> Optional[DependencyConflict]:
28-
deps = [
29-
dep
30-
for dep in dist.requires(("instruments",))
31-
if dep not in dist.requires()
32-
]
33-
return get_dependency_conflicts(deps)
32+
main_deps = dist.requires()
33+
instrumentation_deps = []
34+
for dep in dist.requires(("instruments",)):
35+
if dep not in main_deps:
36+
# we set marker to none so string representation of the dependency looks like
37+
# requests ~= 1.0
38+
# instead of
39+
# requests ~= 1.0; extra = "instruments"
40+
# which does not work with `get_distribution()`
41+
dep.marker = None
42+
instrumentation_deps.append(str(dep))
43+
44+
return get_dependency_conflicts(instrumentation_deps)
3445

3546

3647
def get_dependency_conflicts(
3748
deps: Collection[str],
3849
) -> Optional[DependencyConflict]:
3950
for dep in deps:
4051
try:
41-
get_distribution(str(dep))
52+
get_distribution(dep)
4253
except VersionConflict as exc:
4354
return DependencyConflict(dep, exc.dist)
4455
except DistributionNotFound:
4556
return DependencyConflict(dep)
57+
except RequirementParseError as exc:
58+
logger.warning(
59+
'error parsing dependency, reporting as a conflict: "%s" - %s',
60+
dep,
61+
exc,
62+
)
63+
return DependencyConflict(dep)
4664
return None

opentelemetry-instrumentation/tests/test_dependencies.py

+25-2
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,13 @@
1414

1515
# pylint: disable=protected-access
1616

17+
import pkg_resources
1718
import pytest
1819

1920
from opentelemetry.instrumentation.dependencies import (
2021
DependencyConflict,
2122
get_dependency_conflicts,
23+
get_dist_dependency_conflicts,
2224
)
2325
from opentelemetry.test.test_base import TestBase
2426

@@ -37,7 +39,6 @@ def test_get_dependency_conflicts_not_installed(self):
3739
conflict = get_dependency_conflicts(["this-package-does-not-exist"])
3840
self.assertTrue(conflict is not None)
3941
self.assertTrue(isinstance(conflict, DependencyConflict))
40-
print(conflict)
4142
self.assertEqual(
4243
str(conflict),
4344
'DependencyConflict: requested: "this-package-does-not-exist" but found: "None"',
@@ -47,10 +48,32 @@ def test_get_dependency_conflicts_mismatched_version(self):
4748
conflict = get_dependency_conflicts(["pytest == 5000"])
4849
self.assertTrue(conflict is not None)
4950
self.assertTrue(isinstance(conflict, DependencyConflict))
50-
print(conflict)
5151
self.assertEqual(
5252
str(conflict),
5353
'DependencyConflict: requested: "pytest == 5000" but found: "pytest {0}"'.format(
5454
pytest.__version__
5555
),
5656
)
57+
58+
def test_get_dist_dependency_conflicts(self):
59+
def mock_requires(extras=()):
60+
if "instruments" in extras:
61+
return [
62+
pkg_resources.Requirement(
63+
'test-pkg ~= 1.0; extra == "instruments"'
64+
)
65+
]
66+
return []
67+
68+
dist = pkg_resources.Distribution(
69+
project_name="test-instrumentation", version="1.0"
70+
)
71+
dist.requires = mock_requires
72+
73+
conflict = get_dist_dependency_conflicts(dist)
74+
self.assertTrue(conflict is not None)
75+
self.assertTrue(isinstance(conflict, DependencyConflict))
76+
self.assertEqual(
77+
str(conflict),
78+
'DependencyConflict: requested: "test-pkg~=1.0" but found: "None"',
79+
)

0 commit comments

Comments
 (0)