Skip to content

Commit 0b5dda0

Browse files
Merge branch 'main' into db-drivers-db-statement-comment-opt-in
2 parents 6969dbe + 3ebdb63 commit 0b5dda0

File tree

16 files changed

+662
-79
lines changed

16 files changed

+662
-79
lines changed

CHANGELOG.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1717
([#3100](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3100))
1818
- Add support to database stability opt-in in `_semconv` utilities and add tests
1919
([#3111](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3111))
20+
- `opentelemetry-opentelemetry-pymongo` Add `py.typed` file to enable PEP 561
21+
([#3136](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3136))
22+
- `opentelemetry-opentelemetry-requests` Add `py.typed` file to enable PEP 561
23+
([#3135](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3135))
2024
- `opentelemetry-instrumentation-system-metrics` Add `py.typed` file to enable PEP 561
2125
([#3132](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3132))
2226
- `opentelemetry-opentelemetry-sqlite3` Add `py.typed` file to enable PEP 561
@@ -27,6 +31,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2731
([#3148](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3148))
2832
- add support to Python 3.13
2933
([#3134](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3134))
34+
- `opentelemetry-util-http` Add `py.typed` file to enable PEP 561
35+
([#3127](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3127))
3036

3137
### Fixed
3238

@@ -37,6 +43,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3743
- `opentelemetry-instrumentation` Fix `get_dist_dependency_conflicts` if no distribution requires
3844
([#3168](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3168))
3945

46+
### Breaking changes
47+
48+
- `opentelemetry-instrumentation-sqlalchemy` including sqlcomment in `db.statement` span attribute value is now opt-in
49+
([#3112](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3112))
50+
51+
### Breaking changes
52+
53+
- `opentelemetry-instrumentation-dbapi` including sqlcomment in `db.statement` span attribute value is now opt-in
54+
([#3115](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3115))
55+
4056

4157
### Breaking changes
4258

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ The Python auto-instrumentation libraries for [OpenTelemetry](https://openteleme
4848
* [Installation](#installation)
4949
* [Releasing](#releasing)
5050
* [Releasing a package as `1.0` stable](#releasing-a-package-as-10-stable)
51+
* [Semantic Convention status of instrumentations](#semantic-convention-status-of-instrumentations)
5152
* [Contributing](#contributing)
5253
* [Thanks to all the people who already contributed](#thanks-to-all-the-people-who-already-contributed)
5354

@@ -100,7 +101,7 @@ To release a package as `1.0` stable, the package:
100101

101102
## Semantic Convention status of instrumentations
102103

103-
In our efforts to maintain optimal user experience and prevent breaking changes for transitioning into stable semantic conventions, OpenTelemetry Python is adopting the [semantic convention migration plan](https://github.com/open-telemetry/semantic-conventions/blob/main/docs/http/migration-guide.md) for several instrumentations. Currently this plan is only being adopted for HTTP-related instrumentations, but will eventually cover all types. Please refer to the `semconv status` column of the [instrumentation README](instrumentation/README.md) of the current status of instrumentations' semantic conventions. The possible values are `experimental`, `stable` and `migration` referring to [status](https://github.com/open-telemetry/opentelemetry-specification/blob/v1.31.0/specification/document-status.md#lifecycle-status) of that particular semantic convention. `Migration` refers to an instrumentation that currently supports the migration plan.
104+
In our efforts to maintain optimal user experience and prevent breaking changes for transitioning into stable semantic conventions, OpenTelemetry Python is adopting the semantic convention migration plan for several instrumentations. Currently this plan is only being adopted for [HTTP-related instrumentations](https://github.com/open-telemetry/semantic-conventions/blob/main/docs/non-normative/http-migration.md), but will eventually cover all types. Please refer to the `semconv status` column of the [instrumentation README](instrumentation/README.md) of the current status of instrumentations' semantic conventions. The possible values are `experimental`, `stable` and `migration` referring to [status](https://github.com/open-telemetry/opentelemetry-specification/blob/v1.31.0/specification/document-status.md#lifecycle-status) of that particular semantic convention. `Migration` refers to an instrumentation that currently supports the migration plan.
104105

105106
## Contributing
106107

instrumentation/opentelemetry-instrumentation-asyncpg/tests/test_asyncpg_wrapper.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from asyncpg import Connection, Record, cursor
66
from wrapt import ObjectProxy
77

8+
from opentelemetry import trace as trace_api
89
from opentelemetry.instrumentation.asyncpg import AsyncPGInstrumentor
910
from opentelemetry.test.test_base import TestBase
1011

@@ -105,3 +106,36 @@ async def exec_mock(*args, **kwargs):
105106
spans = self.memory_exporter.get_finished_spans()
106107
self.assertEqual(len(spans), 2)
107108
self.assertEqual([span.status.is_ok for span in spans], [True, True])
109+
110+
def test_no_op_tracer_provider(self):
111+
AsyncPGInstrumentor().uninstrument()
112+
AsyncPGInstrumentor().instrument(
113+
tracer_provider=trace_api.NoOpTracerProvider()
114+
)
115+
116+
# Mock out all interaction with postgres
117+
async def bind_mock(*args, **kwargs):
118+
return []
119+
120+
async def exec_mock(*args, **kwargs):
121+
return [], None, True
122+
123+
conn = mock.Mock()
124+
conn.is_closed = lambda: False
125+
126+
conn._protocol = mock.Mock()
127+
conn._protocol.bind = bind_mock
128+
conn._protocol.execute = exec_mock
129+
conn._protocol.bind_execute = exec_mock
130+
conn._protocol.close_portal = bind_mock
131+
132+
state = mock.Mock()
133+
state.closed = False
134+
135+
# init the cursor and fetch a single record
136+
crs = cursor.Cursor(conn, "SELECT * FROM test", state, [], Record)
137+
asyncio.run(crs._init(1))
138+
asyncio.run(crs.fetch(1))
139+
140+
spans = self.memory_exporter.get_finished_spans()
141+
self.assertEqual(len(spans), 0)

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

Lines changed: 47 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -39,30 +39,49 @@
3939
"""
4040
# pylint: disable=no-value-for-parameter
4141

42+
from __future__ import annotations
43+
4244
import logging
43-
from typing import Collection
45+
from types import CodeType
46+
from typing import Any, Callable, Collection, TypeVar
4447

4548
import jinja2
49+
from jinja2.environment import Template
4650
from wrapt import wrap_function_wrapper as _wrap
4751

4852
from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
4953
from opentelemetry.instrumentation.jinja2.package import _instruments
5054
from opentelemetry.instrumentation.jinja2.version import __version__
5155
from opentelemetry.instrumentation.utils import unwrap
52-
from opentelemetry.trace import SpanKind, get_tracer
56+
from opentelemetry.trace import SpanKind, Tracer, get_tracer
5357

5458
logger = logging.getLogger(__name__)
5559

5660
ATTRIBUTE_JINJA2_TEMPLATE_NAME = "jinja2.template_name"
5761
ATTRIBUTE_JINJA2_TEMPLATE_PATH = "jinja2.template_path"
5862
DEFAULT_TEMPLATE_NAME = "<memory>"
5963

64+
R = TypeVar("R")
65+
6066

61-
def _with_tracer_wrapper(func):
67+
def _with_tracer_wrapper(
68+
func: Callable[
69+
[Tracer, Callable[..., R], Any, list[Any], dict[str, Any]], R
70+
],
71+
) -> Callable[
72+
[Tracer], Callable[[Callable[..., R], Any, list[Any], dict[str, Any]], R]
73+
]:
6274
"""Helper for providing tracer for wrapper functions."""
6375

64-
def _with_tracer(tracer):
65-
def wrapper(wrapped, instance, args, kwargs):
76+
def _with_tracer(
77+
tracer: Tracer,
78+
) -> Callable[[Callable[..., R], Any, list[Any], dict[str, Any]], R]:
79+
def wrapper(
80+
wrapped: Callable[..., R],
81+
instance: Any,
82+
args: list[Any],
83+
kwargs: dict[str, Any],
84+
) -> R:
6685
return func(tracer, wrapped, instance, args, kwargs)
6786

6887
return wrapper
@@ -71,7 +90,13 @@ def wrapper(wrapped, instance, args, kwargs):
7190

7291

7392
@_with_tracer_wrapper
74-
def _wrap_render(tracer, wrapped, instance, args, kwargs):
93+
def _wrap_render(
94+
tracer: Tracer,
95+
wrapped: Callable[..., Any],
96+
instance: Template,
97+
args: list[Any],
98+
kwargs: dict[str, Any],
99+
):
75100
"""Wrap `Template.render()` or `Template.generate()`"""
76101
with tracer.start_as_current_span(
77102
"jinja2.render",
@@ -84,7 +109,13 @@ def _wrap_render(tracer, wrapped, instance, args, kwargs):
84109

85110

86111
@_with_tracer_wrapper
87-
def _wrap_compile(tracer, wrapped, _, args, kwargs):
112+
def _wrap_compile(
113+
tracer: Tracer,
114+
wrapped: Callable[..., CodeType],
115+
_,
116+
args: list[Any],
117+
kwargs: dict[str, Any],
118+
) -> CodeType:
88119
with tracer.start_as_current_span(
89120
"jinja2.compile",
90121
kind=SpanKind.INTERNAL,
@@ -100,7 +131,13 @@ def _wrap_compile(tracer, wrapped, _, args, kwargs):
100131

101132

102133
@_with_tracer_wrapper
103-
def _wrap_load_template(tracer, wrapped, _, args, kwargs):
134+
def _wrap_load_template(
135+
tracer: Tracer,
136+
wrapped: Callable[..., Template],
137+
_,
138+
args: list[Any],
139+
kwargs: dict[str, Any],
140+
) -> Template:
104141
with tracer.start_as_current_span(
105142
"jinja2.load",
106143
kind=SpanKind.INTERNAL,
@@ -128,7 +165,7 @@ class Jinja2Instrumentor(BaseInstrumentor):
128165
def instrumentation_dependencies(self) -> Collection[str]:
129166
return _instruments
130167

131-
def _instrument(self, **kwargs):
168+
def _instrument(self, **kwargs: Any):
132169
tracer_provider = kwargs.get("tracer_provider")
133170
tracer = get_tracer(
134171
__name__,
@@ -146,7 +183,7 @@ def _instrument(self, **kwargs):
146183
_wrap_load_template(tracer),
147184
)
148185

149-
def _uninstrument(self, **kwargs):
186+
def _uninstrument(self, **kwargs: Any):
150187
unwrap(jinja2.Template, "render")
151188
unwrap(jinja2.Template, "generate")
152189
unwrap(jinja2.Environment, "compile")

instrumentation/opentelemetry-instrumentation-jinja2/src/opentelemetry/instrumentation/jinja2/py.typed

Whitespace-only changes.

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

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,10 @@ def failed_hook(span, event):
7575
7676
"""
7777

78+
from __future__ import annotations
79+
7880
from logging import getLogger
79-
from typing import Callable, Collection
81+
from typing import Any, Callable, Collection, TypeVar
8082

8183
from pymongo import monitoring
8284

@@ -88,7 +90,7 @@ def failed_hook(span, event):
8890
from opentelemetry.instrumentation.pymongo.version import __version__
8991
from opentelemetry.instrumentation.utils import is_instrumentation_enabled
9092
from opentelemetry.semconv.trace import DbSystemValues, SpanAttributes
91-
from opentelemetry.trace import SpanKind, get_tracer
93+
from opentelemetry.trace import SpanKind, Tracer, get_tracer
9294
from opentelemetry.trace.span import Span
9395
from opentelemetry.trace.status import Status, StatusCode
9496

@@ -98,14 +100,21 @@ def failed_hook(span, event):
98100
ResponseHookT = Callable[[Span, monitoring.CommandSucceededEvent], None]
99101
FailedHookT = Callable[[Span, monitoring.CommandFailedEvent], None]
100102

103+
CommandEvent = TypeVar(
104+
"CommandEvent",
105+
monitoring.CommandStartedEvent,
106+
monitoring.CommandSucceededEvent,
107+
monitoring.CommandFailedEvent,
108+
)
109+
101110

102-
def dummy_callback(span, event): ...
111+
def dummy_callback(span: Span, event: CommandEvent): ...
103112

104113

105114
class CommandTracer(monitoring.CommandListener):
106115
def __init__(
107116
self,
108-
tracer,
117+
tracer: Tracer,
109118
request_hook: RequestHookT = dummy_callback,
110119
response_hook: ResponseHookT = dummy_callback,
111120
failed_hook: FailedHookT = dummy_callback,
@@ -195,10 +204,12 @@ def failed(self, event: monitoring.CommandFailedEvent):
195204
_LOG.exception(hook_exception)
196205
span.end()
197206

198-
def _pop_span(self, event):
207+
def _pop_span(self, event: CommandEvent) -> Span | None:
199208
return self._span_dict.pop(_get_span_dict_key(event), None)
200209

201-
def _get_statement_by_command_name(self, command_name, event):
210+
def _get_statement_by_command_name(
211+
self, command_name: str, event: CommandEvent
212+
) -> str:
202213
statement = command_name
203214
command_attribute = COMMAND_TO_ATTRIBUTE_MAPPING.get(command_name)
204215
command = event.command.get(command_attribute)
@@ -207,14 +218,16 @@ def _get_statement_by_command_name(self, command_name, event):
207218
return statement
208219

209220

210-
def _get_span_dict_key(event):
221+
def _get_span_dict_key(
222+
event: CommandEvent,
223+
) -> int | tuple[int, tuple[str, int | None]]:
211224
if event.connection_id is not None:
212225
return event.request_id, event.connection_id
213226
return event.request_id
214227

215228

216229
class PymongoInstrumentor(BaseInstrumentor):
217-
_commandtracer_instance = None # type CommandTracer
230+
_commandtracer_instance: CommandTracer | None = None
218231
# The instrumentation for PyMongo is based on the event listener interface
219232
# https://api.mongodb.com/python/current/api/pymongo/monitoring.html.
220233
# This interface only allows to register listeners and does not provide
@@ -225,7 +238,7 @@ class PymongoInstrumentor(BaseInstrumentor):
225238
def instrumentation_dependencies(self) -> Collection[str]:
226239
return _instruments
227240

228-
def _instrument(self, **kwargs):
241+
def _instrument(self, **kwargs: Any):
229242
"""Integrate with pymongo to trace it using event listener.
230243
https://api.mongodb.com/python/current/api/pymongo/monitoring.html
231244
@@ -259,6 +272,6 @@ def _instrument(self, **kwargs):
259272
# If already created, just enable it
260273
self._commandtracer_instance.is_enabled = True
261274

262-
def _uninstrument(self, **kwargs):
275+
def _uninstrument(self, **kwargs: Any):
263276
if self._commandtracer_instance is not None:
264277
self._commandtracer_instance.is_enabled = False

instrumentation/opentelemetry-instrumentation-pymongo/src/opentelemetry/instrumentation/pymongo/py.typed

Whitespace-only changes.

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

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -72,10 +72,12 @@ def response_hook(span, request_obj, response)
7272
---
7373
"""
7474

75+
from __future__ import annotations
76+
7577
import functools
7678
import types
7779
from timeit import default_timer
78-
from typing import Callable, Collection, Optional
80+
from typing import Any, Callable, Collection, Optional
7981
from urllib.parse import urlparse
8082

8183
from requests.models import PreparedRequest, Response
@@ -146,7 +148,7 @@ def _instrument(
146148
duration_histogram_new: Histogram,
147149
request_hook: _RequestHookT = None,
148150
response_hook: _ResponseHookT = None,
149-
excluded_urls: ExcludeList = None,
151+
excluded_urls: ExcludeList | None = None,
150152
sem_conv_opt_in_mode: _StabilityMode = _StabilityMode.DEFAULT,
151153
):
152154
"""Enables tracing of all requests calls that go through
@@ -164,7 +166,9 @@ def _instrument(
164166

165167
# pylint: disable-msg=too-many-locals,too-many-branches
166168
@functools.wraps(wrapped_send)
167-
def instrumented_send(self, request, **kwargs):
169+
def instrumented_send(
170+
self: Session, request: PreparedRequest, **kwargs: Any
171+
):
168172
if excluded_urls and excluded_urls.url_disabled(request.url):
169173
return wrapped_send(self, request, **kwargs)
170174

@@ -345,7 +349,7 @@ def _uninstrument():
345349
_uninstrument_from(Session)
346350

347351

348-
def _uninstrument_from(instr_root, restore_as_bound_func=False):
352+
def _uninstrument_from(instr_root, restore_as_bound_func: bool = False):
349353
for instr_func_name in ("request", "send"):
350354
instr_func = getattr(instr_root, instr_func_name)
351355
if not getattr(
@@ -361,7 +365,7 @@ def _uninstrument_from(instr_root, restore_as_bound_func=False):
361365
setattr(instr_root, instr_func_name, original)
362366

363367

364-
def get_default_span_name(method):
368+
def get_default_span_name(method: str) -> str:
365369
"""
366370
Default implementation for name_callback, returns HTTP {method_name}.
367371
https://opentelemetry.io/docs/reference/specification/trace/semantic_conventions/http/#name
@@ -385,7 +389,7 @@ class RequestsInstrumentor(BaseInstrumentor):
385389
def instrumentation_dependencies(self) -> Collection[str]:
386390
return _instruments
387391

388-
def _instrument(self, **kwargs):
392+
def _instrument(self, **kwargs: Any):
389393
"""Instruments requests module
390394
391395
Args:
@@ -443,10 +447,10 @@ def _instrument(self, **kwargs):
443447
sem_conv_opt_in_mode=semconv_opt_in_mode,
444448
)
445449

446-
def _uninstrument(self, **kwargs):
450+
def _uninstrument(self, **kwargs: Any):
447451
_uninstrument()
448452

449453
@staticmethod
450-
def uninstrument_session(session):
454+
def uninstrument_session(session: Session):
451455
"""Disables instrumentation on the session object."""
452456
_uninstrument_from(session, restore_as_bound_func=True)

instrumentation/opentelemetry-instrumentation-requests/src/opentelemetry/instrumentation/requests/py.typed

Whitespace-only changes.

0 commit comments

Comments
 (0)