Skip to content

Commit 7ddc4bf

Browse files
authored
Fix for issue #42337 (#42342)
* Fix for issue #42337 * Updated CHANGELOG * Fixed lint errors * Fixed mypy errors * Updated CHANGELOG * Fixed spell check
1 parent 470b70c commit 7ddc4bf

File tree

2 files changed

+73
-4
lines changed

2 files changed

+73
-4
lines changed

sdk/monitor/azure-monitor-opentelemetry/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
### Breaking Changes
88

99
### Bugs Fixed
10+
- Fixed issue #42337, removes warning messages for instrumentations that target multiple packages. The logic for dependency conflict detection has been enhanced by adding "instruments-any" feature. This feature is used when an instrumentation requires any of a set of dependencies rather than all. Follows upstream dependency conflict detection logic - https://github.com/open-telemetry/opentelemetry-python-contrib/blob/main/opentelemetry-instrumentation/src/opentelemetry/instrumentation/dependencies.py
11+
([#42342](https://github.com/Azure/azure-sdk-for-python/pull/42342))
1012

1113
### Other Changes
1214

sdk/monitor/azure-monitor-opentelemetry/azure/monitor/opentelemetry/_utils/instrumentation.py

Lines changed: 71 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,36 +31,57 @@
3131
class DependencyConflict:
3232
required: str | None = None
3333
found: str | None = None
34-
35-
def __init__(self, required: str | None, found: str | None = None):
34+
# The following fields are used when an instrumentation requires any of a set of dependencies rather than all.
35+
required_any: list[str] | None = None
36+
found_any: list[str] | None = None
37+
38+
def __init__(
39+
self,
40+
required: str | None = None,
41+
found: str | None = None,
42+
required_any: list[str] | None = None,
43+
found_any: list[str] | None = None
44+
):
3645
self.required = required
3746
self.found = found
47+
self.required_any = required_any
48+
self.found_any = found_any
3849

3950
def __str__(self):
51+
if not self.required and (self.required_any or self.found_any):
52+
return (f'DependencyConflict: requested any of the following: "{self.required_any}" '
53+
f'but found: "{self.found_any}"')
4054
return f'DependencyConflict: requested: "{self.required}" but found: "{self.found}"'
4155

4256

4357
def get_dist_dependency_conflicts(
4458
dist: Distribution,
4559
) -> DependencyConflict | None:
4660
instrumentation_deps = []
61+
instrumentation_any_deps = []
4762
extra = "extra"
4863
instruments = "instruments"
64+
instruments_any = "instruments-any"
4965
instruments_marker = {extra: instruments}
66+
instruments_any_marker = {extra: instruments_any}
5067
if dist.requires:
5168
for dep in dist.requires:
52-
if extra not in dep or instruments not in dep:
69+
if extra not in dep or instruments not in dep and instruments_any not in dep:
5370
continue
5471

5572
req = Requirement(dep)
5673
if req.marker.evaluate(instruments_marker): # type: ignore
5774
instrumentation_deps.append(req)
75+
if req.marker.evaluate(instruments_any_marker): # type: ignore
76+
instrumentation_any_deps.append(req)
5877

59-
return get_dependency_conflicts(instrumentation_deps)
78+
return get_dependency_conflicts(instrumentation_deps, instrumentation_any_deps)
6079

6180

6281
def get_dependency_conflicts(
6382
deps: Collection[str | Requirement],
83+
deps_any: Collection[str | Requirement]
84+
| None = None,
6485
) -> DependencyConflict | None:
6586
for dep in deps:
6687
if isinstance(dep, Requirement):
@@ -83,4 +104,50 @@ def get_dependency_conflicts(
83104

84105
if not req.specifier.contains(dist_version):
85106
return DependencyConflict(dep, f"{req.name} {dist_version}") # type: ignore
107+
108+
if deps_any:
109+
return _get_dependency_conflicts_any(deps_any)
110+
return None
111+
112+
def _get_dependency_conflicts_any(
113+
deps_any: Collection[str | Requirement],
114+
) -> DependencyConflict | None:
115+
if not deps_any:
116+
return None
117+
is_dependency_conflict = True
118+
required_any: list[str] = []
119+
found_any: list[str] = []
120+
for dep in deps_any:
121+
if isinstance(dep, Requirement):
122+
req = dep
123+
else:
124+
try:
125+
req = Requirement(dep)
126+
except InvalidRequirement as exc:
127+
logger.warning(
128+
'error parsing dependency, reporting as a conflict: "%s" - %s',
129+
dep,
130+
exc,
131+
)
132+
return DependencyConflict(dep)
133+
134+
try:
135+
dist_version = version(req.name)
136+
except PackageNotFoundError:
137+
required_any.append(str(dep))
138+
continue
139+
140+
if req.specifier.contains(dist_version):
141+
# Since only one of the instrumentation_any dependencies is required, there is no dependency conflict.
142+
is_dependency_conflict = False
143+
break
144+
# If the version does not match, add it to the list of unfulfilled requirement options.
145+
required_any.append(str(dep))
146+
found_any.append(f"{req.name} {dist_version}")
147+
148+
if is_dependency_conflict:
149+
return DependencyConflict(
150+
required_any=required_any,
151+
found_any=found_any,
152+
)
86153
return None

0 commit comments

Comments
 (0)