From 3b07b41c23d466bf37f9e32d908aab56f9c5082e Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Fri, 12 Sep 2025 13:04:46 +0200 Subject: [PATCH 1/3] More trivial typing for sdk --- opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py index 960be82be2..10741a8a96 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py @@ -116,6 +116,7 @@ def on_start( span: The :class:`opentelemetry.trace.Span` that just started. parent_context: The parent context of the span that just started. """ + ... def _on_ending(self, span: "Span") -> None: """Called when a :class:`opentelemetry.trace.Span` is ending. @@ -136,9 +137,11 @@ def on_end(self, span: "ReadableSpan") -> None: Args: span: The :class:`opentelemetry.trace.Span` that just ended. """ + ... def shutdown(self) -> None: """Called when a :class:`opentelemetry.sdk.trace.TracerProvider` is shutdown.""" + ... def force_flush(self, timeout_millis: int = 30000) -> bool: """Export all ended spans to the configured Exporter that have not yet @@ -151,6 +154,7 @@ def force_flush(self, timeout_millis: int = 30000) -> bool: Returns: False if the timeout is exceeded, True otherwise. """ + ... # Temporary fix until https://github.com/PyCQA/pylint/issues/4098 is resolved From 0992c3801ddcfe0b8efa53a8d138fd8243ae478d Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Thu, 27 Nov 2025 15:57:22 +0100 Subject: [PATCH 2/3] Last bits for sdk/trace typechecking --- .../src/opentelemetry/sdk/trace/__init__.py | 26 +++++++++++++------ .../shared_internal/test_batch_processor.py | 1 + opentelemetry-sdk/tests/trace/test_trace.py | 7 ++--- pyproject.toml | 1 - 4 files changed, 23 insertions(+), 12 deletions(-) diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py index 10741a8a96..31b8ed7c96 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py @@ -416,7 +416,7 @@ class ReadableSpan: def __init__( self, name: str, - context: Optional[trace_api.SpanContext] = None, + context: trace_api.SpanContext, parent: Optional[trace_api.SpanContext] = None, resource: Optional[Resource] = None, attributes: types.Attributes = None, @@ -660,12 +660,12 @@ def __init__( max_span_attribute_length: Optional[int] = None, ): # span events and links count - self.max_events = self._from_env_if_absent( + self.max_events = self._from_env_if_absent_with_default( max_events, OTEL_SPAN_EVENT_COUNT_LIMIT, _DEFAULT_OTEL_SPAN_EVENT_COUNT_LIMIT, ) - self.max_links = self._from_env_if_absent( + self.max_links = self._from_env_if_absent_with_default( max_links, OTEL_SPAN_LINK_COUNT_LIMIT, _DEFAULT_OTEL_SPAN_LINK_COUNT_LIMIT, @@ -681,7 +681,7 @@ def __init__( else _DEFAULT_OTEL_ATTRIBUTE_COUNT_LIMIT ) - self.max_span_attributes = self._from_env_if_absent( + self.max_span_attributes = self._from_env_if_absent_with_default( max_span_attributes, OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT, ( @@ -690,7 +690,7 @@ def __init__( else _DEFAULT_OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT ), ) - self.max_event_attributes = self._from_env_if_absent( + self.max_event_attributes = self._from_env_if_absent_with_default( max_event_attributes, OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT, ( @@ -699,7 +699,7 @@ def __init__( else _DEFAULT_OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT ), ) - self.max_link_attributes = self._from_env_if_absent( + self.max_link_attributes = self._from_env_if_absent_with_default( max_link_attributes, OTEL_LINK_ATTRIBUTE_COUNT_LIMIT, ( @@ -752,6 +752,13 @@ def _from_env_if_absent( raise ValueError(err_msg.format(env_var, value)) return value + @classmethod + def _from_env_if_absent_with_default( + cls, value: Optional[int], env_var: str, default: int + ) -> int: + value_from_env = cls._from_env_if_absent(value, env_var, default) + return value_from_env if value_from_env is not None else default + _UnsetLimits = SpanLimits( max_attributes=SpanLimits.UNSET, @@ -1039,7 +1046,7 @@ def __exit__( self.record_exception(exception=exc_val, escaped=True) # Records status if span is used as context manager # i.e. with tracer.start_span() as span: - if self._set_status_on_exception: + if exc_type and self._set_status_on_exception: self.set_status( Status( status_code=StatusCode.ERROR, @@ -1169,11 +1176,14 @@ def start_span( # pylint: disable=too-many-locals context: Optional[context_api.Context] = None, kind: trace_api.SpanKind = trace_api.SpanKind.INTERNAL, attributes: types.Attributes = None, - links: Optional[Sequence[trace_api.Link]] = (), + links: Optional[Sequence[trace_api.Link]] = None, start_time: Optional[int] = None, record_exception: bool = True, set_status_on_exception: bool = True, ) -> trace_api.Span: + if links is None: + links = () + parent_span_context = trace_api.get_current_span( context ).get_span_context() diff --git a/opentelemetry-sdk/tests/shared_internal/test_batch_processor.py b/opentelemetry-sdk/tests/shared_internal/test_batch_processor.py index f5ed15110b..868b23cefe 100644 --- a/opentelemetry-sdk/tests/shared_internal/test_batch_processor.py +++ b/opentelemetry-sdk/tests/shared_internal/test_batch_processor.py @@ -50,6 +50,7 @@ BASIC_SPAN = ReadableSpan( "MySpan", + context=(), instrumentation_scope=InstrumentationScope("example", "example"), ) diff --git a/opentelemetry-sdk/tests/trace/test_trace.py b/opentelemetry-sdk/tests/trace/test_trace.py index e9a59c6cde..d2bb557ba4 100644 --- a/opentelemetry-sdk/tests/trace/test_trace.py +++ b/opentelemetry-sdk/tests/trace/test_trace.py @@ -665,11 +665,12 @@ def test_surplus_span_attributes(self): class TestReadableSpan(unittest.TestCase): def test_links(self): - span = trace.ReadableSpan("test") + span = trace.ReadableSpan("test", context=()) self.assertEqual(span.links, ()) span = trace.ReadableSpan( "test", + context=(), links=[trace_api.Link(context=trace_api.INVALID_SPAN_CONTEXT)] * 2, ) self.assertEqual(len(span.links), 2) @@ -677,13 +678,13 @@ def test_links(self): self.assertFalse(link.context.is_valid) def test_events(self): - span = trace.ReadableSpan("test") + span = trace.ReadableSpan("test", context=()) self.assertEqual(span.events, ()) events = [ trace.Event("foo1", {"bar1": "baz1"}), trace.Event("foo2", {"bar2": "baz2"}), ] - span = trace.ReadableSpan("test", events=events) + span = trace.ReadableSpan("test", context=(), events=events) self.assertEqual(span.events, tuple(events)) def test_event_dropped_attributes(self): diff --git a/pyproject.toml b/pyproject.toml index b6970c666d..f4d300fcca 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -112,7 +112,6 @@ exclude = [ "opentelemetry-sdk/tests", "opentelemetry-sdk/src/opentelemetry/sdk/_events", "opentelemetry-sdk/src/opentelemetry/sdk/metrics/_internal/", - "opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py", "opentelemetry-sdk/benchmarks", "exporter/opentelemetry-exporter-otlp-proto-grpc/tests", ] From a9f762694c341c745fe49e4b042fe8c1f949b686 Mon Sep 17 00:00:00 2001 From: Riccardo Magliocchetti Date: Fri, 13 Feb 2026 17:04:00 +0100 Subject: [PATCH 3/3] Silence last warnings --- opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py index 31b8ed7c96..c804065a63 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/trace/__init__.py @@ -102,6 +102,8 @@ class SpanProcessor: in the same order as they were registered. """ + # pylint: disable=unnecessary-ellipsis,no-self-use + def on_start( self, span: "Span", @@ -127,6 +129,7 @@ def _on_ending(self, span: "Span") -> None: Args: span: The :class:`opentelemetry.trace.Span` that is ending. """ + ... def on_end(self, span: "ReadableSpan") -> None: """Called when a :class:`opentelemetry.trace.Span` is ended. @@ -254,7 +257,7 @@ def __init__(self, num_threads: int = 2): # needs to be re-instantiated to get a fresh pool of threads: weak_reinit = weakref.WeakMethod(self._init_executor) os.register_at_fork( - after_in_child=lambda: weak_reinit()(num_threads) + after_in_child=lambda: weak_reinit()(num_threads) # pyright: ignore[reportOptionalCall] ) def _init_executor(self, num_threads: int) -> None: