Skip to content

Commit 77f3171

Browse files
authored
Add "instruments-any" feature: unblock multi-target instrumentations while fixing dependency conflict breakage. (#3610)
1 parent f20fa77 commit 77f3171

File tree

20 files changed

+575
-91
lines changed

20 files changed

+575
-91
lines changed

CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1111
1212
## Unreleased
1313

14+
### Fixed
15+
16+
- `opentelemetry-instrumentation`: Fix dependency conflict detection when instrumented packages are not installed by moving check back to before instrumentors are loaded. Add "instruments-any" feature for instrumentations that target multiple packages.
17+
([#3610](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3610))
18+
19+
### Added
20+
21+
- `opentelemetry-instrumentation-psycopg2` Utilize instruments-any functionality.
22+
([#3610](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3610))
23+
- `opentelemetry-instrumentation-kafka-python` Utilize instruments-any functionality.
24+
([#3610](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3610))
25+
1426
## Version 1.35.0/0.56b0 (2025-07-11)
1527

1628
### Added

CONTRIBUTING.md

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -325,10 +325,25 @@ Below is a checklist of things to be mindful of when implementing a new instrume
325325
### Update supported instrumentation package versions
326326

327327
- Navigate to the **instrumentation package directory:**
328-
- Update **`pyproject.toml`** file by modifying _instruments_ entry in the `[project.optional-dependencies]` section with the new version constraint
329-
- Update `_instruments` variable in instrumentation **`package.py`** file with the new version constraint
328+
- Update **`pyproject.toml`** file by modifying `instruments` or `instruments-any` entry in the `[project.optional-dependencies]` section with the new version constraint
329+
- Update `_instruments` or `_instruments_any` variable in instrumentation **`package.py`** file with the new version constraint
330330
- At the **root of the project directory**, run `tox -e generate` to regenerate necessary files
331331

332+
Please note that `instruments-any` is an optional field that can be used instead of or in addition to `instruments`. While `instruments` is a list of dependencies, _all_ of which are expected by the instrumentation, `instruments-any` is a list _any_ of which but not all are expected. For example, the following entry requires both `util` and `common` plus either `foo` or `bar` to be present for the instrumentation to occur:
333+
```
334+
[project.optional-dependencies]
335+
instruments = [
336+
"util ~= 1.0"
337+
"common ~= 2.0"
338+
]
339+
instruments-any = [
340+
"foo ~= 3.0"
341+
"bar ~= 4.0"
342+
]
343+
```
344+
345+
<!-- See https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3610 for details on instruments-any -->
346+
332347
If you're adding support for a new version of the instrumentation package, follow these additional steps:
333348

334349
- At the **instrumentation package directory:** Add new test-requirements.txt file with the respective package version required for testing

instrumentation/opentelemetry-instrumentation-fastapi/tests/test_fastapi_instrumentation.py

Lines changed: 16 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@
4040
)
4141
from opentelemetry.instrumentation.dependencies import (
4242
DependencyConflict,
43-
DependencyConflictError,
4443
)
4544
from opentelemetry.sdk.metrics.export import (
4645
HistogramDataPoint,
@@ -1102,40 +1101,34 @@ def test_instruments_with_fastapi_installed(self, mock_logger):
11021101
[self._instrumentation_loaded_successfully_call()]
11031102
)
11041103

1104+
@patch(
1105+
"opentelemetry.instrumentation.auto_instrumentation._load.get_dist_dependency_conflicts"
1106+
)
11051107
@patch("opentelemetry.instrumentation.auto_instrumentation._load._logger")
1106-
def test_instruments_with_old_fastapi_installed(self, mock_logger): # pylint: disable=no-self-use
1108+
def test_instruments_with_old_fastapi_installed(
1109+
self, mock_logger, mock_dep
1110+
): # pylint: disable=no-self-use
11071111
dependency_conflict = DependencyConflict("0.58", "0.57")
11081112
mock_distro = Mock()
1109-
mock_distro.load_instrumentor.side_effect = DependencyConflictError(
1110-
dependency_conflict
1111-
)
1113+
mock_dep.return_value = dependency_conflict
11121114
_load_instrumentors(mock_distro)
1113-
self.assertEqual(len(mock_distro.load_instrumentor.call_args_list), 1)
1114-
(ep,) = mock_distro.load_instrumentor.call_args.args
1115-
self.assertEqual(ep.name, "fastapi")
1116-
assert (
1117-
self._instrumentation_loaded_successfully_call()
1118-
not in mock_logger.debug.call_args_list
1119-
)
1115+
mock_distro.load_instrumentor.assert_not_called()
11201116
mock_logger.debug.assert_has_calls(
11211117
[self._instrumentation_failed_to_load_call(dependency_conflict)]
11221118
)
11231119

1120+
@patch(
1121+
"opentelemetry.instrumentation.auto_instrumentation._load.get_dist_dependency_conflicts"
1122+
)
11241123
@patch("opentelemetry.instrumentation.auto_instrumentation._load._logger")
1125-
def test_instruments_without_fastapi_installed(self, mock_logger): # pylint: disable=no-self-use
1124+
def test_instruments_without_fastapi_installed(
1125+
self, mock_logger, mock_dep
1126+
): # pylint: disable=no-self-use
11261127
dependency_conflict = DependencyConflict("0.58", None)
11271128
mock_distro = Mock()
1128-
mock_distro.load_instrumentor.side_effect = DependencyConflictError(
1129-
dependency_conflict
1130-
)
1129+
mock_dep.return_value = dependency_conflict
11311130
_load_instrumentors(mock_distro)
1132-
self.assertEqual(len(mock_distro.load_instrumentor.call_args_list), 1)
1133-
(ep,) = mock_distro.load_instrumentor.call_args.args
1134-
self.assertEqual(ep.name, "fastapi")
1135-
assert (
1136-
self._instrumentation_loaded_successfully_call()
1137-
not in mock_logger.debug.call_args_list
1138-
)
1131+
mock_distro.load_instrumentor.assert_not_called()
11391132
mock_logger.debug.assert_has_calls(
11401133
[self._instrumentation_failed_to_load_call(dependency_conflict)]
11411134
)

instrumentation/opentelemetry-instrumentation-kafka-python/pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ dependencies = [
3131
]
3232

3333
[project.optional-dependencies]
34-
instruments = [
34+
instruments = []
35+
instruments-any = [
3536
"kafka-python >= 2.0, < 3.0",
3637
"kafka-python-ng >= 2.0, < 3.0"
3738
]

instrumentation/opentelemetry-instrumentation-kafka-python/src/opentelemetry/instrumentation/kafka/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ def process_msg(message):
9191
from opentelemetry import trace
9292
from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
9393
from opentelemetry.instrumentation.kafka.package import (
94-
_instruments,
94+
_instruments_any,
9595
_instruments_kafka_python,
9696
_instruments_kafka_python_ng,
9797
)
@@ -123,7 +123,7 @@ def instrumentation_dependencies(self) -> Collection[str]:
123123
except PackageNotFoundError:
124124
pass
125125

126-
return _instruments
126+
return _instruments_any
127127

128128
def _instrument(self, **kwargs):
129129
"""Instruments the kafka module

instrumentation/opentelemetry-instrumentation-kafka-python/src/opentelemetry/instrumentation/kafka/package.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,5 @@
1616
_instruments_kafka_python = "kafka-python >= 2.0, < 3.0"
1717
_instruments_kafka_python_ng = "kafka-python-ng >= 2.0, < 3.0"
1818

19-
_instruments = (_instruments_kafka_python, _instruments_kafka_python_ng)
19+
_instruments = ()
20+
_instruments_any = (_instruments_kafka_python, _instruments_kafka_python_ng)

instrumentation/opentelemetry-instrumentation-kafka-python/tests/test_instrumentation.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020

2121
from opentelemetry.instrumentation.kafka import KafkaInstrumentor
2222
from opentelemetry.instrumentation.kafka.package import (
23-
_instruments,
2423
_instruments_kafka_python,
2524
_instruments_kafka_python_ng,
2625
)
@@ -134,4 +133,7 @@ def _distribution(name):
134133
call("kafka-python"),
135134
],
136135
)
137-
self.assertEqual(package_to_instrument, _instruments)
136+
self.assertEqual(
137+
package_to_instrument,
138+
(_instruments_kafka_python, _instruments_kafka_python_ng),
139+
)

instrumentation/opentelemetry-instrumentation-psycopg2/pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ dependencies = [
3131
]
3232

3333
[project.optional-dependencies]
34-
instruments = [
34+
instruments = []
35+
instruments-any = [
3536
"psycopg2 >= 2.7.3.1",
3637
"psycopg2-binary >= 2.7.3.1",
3738
]

instrumentation/opentelemetry-instrumentation-psycopg2/src/opentelemetry/instrumentation/psycopg2/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@
151151
from opentelemetry.instrumentation import dbapi
152152
from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
153153
from opentelemetry.instrumentation.psycopg2.package import (
154-
_instruments,
154+
_instruments_any,
155155
_instruments_psycopg2,
156156
_instruments_psycopg2_binary,
157157
)
@@ -187,7 +187,7 @@ def instrumentation_dependencies(self) -> Collection[str]:
187187
except PackageNotFoundError:
188188
pass
189189

190-
return _instruments
190+
return _instruments_any
191191

192192
def _instrument(self, **kwargs):
193193
"""Integrate with PostgreSQL Psycopg library.

instrumentation/opentelemetry-instrumentation-psycopg2/src/opentelemetry/instrumentation/psycopg2/package.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@
1616
_instruments_psycopg2 = "psycopg2 >= 2.7.3.1"
1717
_instruments_psycopg2_binary = "psycopg2-binary >= 2.7.3.1"
1818

19-
_instruments = (
19+
_instruments = ()
20+
_instruments_any = (
2021
_instruments_psycopg2,
2122
_instruments_psycopg2_binary,
2223
)

0 commit comments

Comments
 (0)