From b6947644121446acffa707641f947f4315e28294 Mon Sep 17 00:00:00 2001 From: Alexander Alderman Webb Date: Thu, 30 Apr 2026 10:15:28 +0200 Subject: [PATCH 01/16] fix: Introduce get_current_streamed_span to keep types backwards compatible --- sentry_sdk/api.py | 11 ++++++++++- sentry_sdk/scope.py | 7 ++++++- sentry_sdk/tracing_utils.py | 13 ++++++++++++- 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/sentry_sdk/api.py b/sentry_sdk/api.py index 105f531e5f..2ab1ab3f8f 100644 --- a/sentry_sdk/api.py +++ b/sentry_sdk/api.py @@ -422,13 +422,22 @@ def set_measurement(name: str, value: float, unit: "MeasurementUnit" = "") -> No def get_current_span( scope: "Optional[Scope]" = None, -) -> "Optional[Union[Span, StreamedSpan]]": +) -> "Optional[Span]": """ Returns the currently active span if there is one running, otherwise `None` """ return tracing_utils.get_current_span(scope) +def get_current_streamed_span( + scope: "Optional[Scope]" = None, +) -> "Optional[StreamedSpan]": + """ + Returns the currently active streamed span if there is one running, otherwise `None` + """ + return tracing_utils.get_current_streamed_span(scope) + + def get_traceparent() -> "Optional[str]": """ Returns the traceparent either from the active span or from the scope. diff --git a/sentry_sdk/scope.py b/sentry_sdk/scope.py index f24edcf137..0993ac404f 100644 --- a/sentry_sdk/scope.py +++ b/sentry_sdk/scope.py @@ -877,10 +877,15 @@ def set_user(self, value: "Optional[Dict[str, Any]]") -> None: session.update(user=value) @property - def span(self) -> "Optional[Union[Span, StreamedSpan]]": + def span(self) -> "Optional[Span]": """Get/set current tracing span or transaction.""" return self._span + @property + def streamed_span(self) -> "Optional[StreamedSpan]": + """Get/set current tracing span.""" + return self._span + @span.setter def span(self, span: "Optional[Union[Span, StreamedSpan]]") -> None: self._span = span diff --git a/sentry_sdk/tracing_utils.py b/sentry_sdk/tracing_utils.py index 034af715d8..f1e4319275 100644 --- a/sentry_sdk/tracing_utils.py +++ b/sentry_sdk/tracing_utils.py @@ -1194,7 +1194,7 @@ def sync_wrapper(*args: "Any", **kwargs: "Any") -> "Any": def get_current_span( scope: "Optional[sentry_sdk.Scope]" = None, -) -> "Optional[Union[Span, StreamedSpan]]": +) -> "Optional[Span]": """ Returns the currently active span if there is one running, otherwise `None` """ @@ -1203,6 +1203,17 @@ def get_current_span( return current_span +def get_current_streamed_span( + scope: "Optional[sentry_sdk.Scope]" = None, +) -> "Optional[StreamedSpan]": + """ + Returns the currently active span if there is one running, otherwise `None` + """ + scope = scope or sentry_sdk.get_current_scope() + current_span = scope.streamed_span + return current_span + + def set_span_errored(span: "Optional[Union[Span, StreamedSpan]]" = None) -> None: """ Set the status of the current or given span to INTERNAL_ERROR. From b4530d0afe16829231c3ebecfbc023b8f2d79277 Mon Sep 17 00:00:00 2001 From: Alexander Alderman Webb Date: Thu, 30 Apr 2026 10:30:53 +0200 Subject: [PATCH 02/16] . --- sentry_sdk/scope.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/sentry_sdk/scope.py b/sentry_sdk/scope.py index 0993ac404f..71c1bec30e 100644 --- a/sentry_sdk/scope.py +++ b/sentry_sdk/scope.py @@ -879,15 +879,10 @@ def set_user(self, value: "Optional[Dict[str, Any]]") -> None: @property def span(self) -> "Optional[Span]": """Get/set current tracing span or transaction.""" - return self._span - - @property - def streamed_span(self) -> "Optional[StreamedSpan]": - """Get/set current tracing span.""" - return self._span + return self._span if isinstance(self._span, Span) else None @span.setter - def span(self, span: "Optional[Union[Span, StreamedSpan]]") -> None: + def span(self, span: "Optional[Span]") -> None: self._span = span # XXX: this differs from the implementation in JS, there Scope.setSpan # does not set Scope._transactionName. @@ -898,6 +893,15 @@ def span(self, span: "Optional[Union[Span, StreamedSpan]]") -> None: if transaction.source: self._transaction_info["source"] = transaction.source + @property + def streamed_span(self) -> "Optional[StreamedSpan]": + """Get/set current tracing span.""" + return self._span if isinstance(self._span, StreamedSpan) else None + + @streamed_span.setter + def streamed_span(self, span: "Optional[StreamedSpan]") -> None: + self._span = span + # Also set _transaction and _transaction_info in streaming mode as this # is used for populating events and linking them to segments if ( From 54280c7fcfd97b6180de187fa314f228af8f3433 Mon Sep 17 00:00:00 2001 From: Alexander Alderman Webb Date: Thu, 30 Apr 2026 10:42:01 +0200 Subject: [PATCH 03/16] . --- sentry_sdk/__init__.py | 1 + sentry_sdk/api.py | 1 + sentry_sdk/scope.py | 4 +++- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/sentry_sdk/__init__.py b/sentry_sdk/__init__.py index 7fd0e1953d..3e97326a87 100644 --- a/sentry_sdk/__init__.py +++ b/sentry_sdk/__init__.py @@ -32,6 +32,7 @@ "get_isolation_scope", "get_current_scope", "get_current_span", + "get_current_streamed_span", "get_traceparent", "is_initialized", "isolation_scope", diff --git a/sentry_sdk/api.py b/sentry_sdk/api.py index 2ab1ab3f8f..568482e707 100644 --- a/sentry_sdk/api.py +++ b/sentry_sdk/api.py @@ -67,6 +67,7 @@ def overload(x: "T") -> "T": "get_isolation_scope", "get_current_scope", "get_current_span", + "get_current_streamed_span", "get_traceparent", "is_initialized", "isolation_scope", diff --git a/sentry_sdk/scope.py b/sentry_sdk/scope.py index 71c1bec30e..ca0042f97a 100644 --- a/sentry_sdk/scope.py +++ b/sentry_sdk/scope.py @@ -680,7 +680,9 @@ def iter_trace_propagation_headers( return span = kwargs.pop("span", None) - span = span or self.span + if not span: + span_streaming = has_span_streaming_enabled(client.options) + span = self.streamed_span if span_streaming else self.span if ( has_tracing_enabled(client.options) From 32b8c5a2061a8526596afc80916745810a9ea985 Mon Sep 17 00:00:00 2001 From: Alexander Alderman Webb Date: Thu, 30 Apr 2026 10:48:25 +0200 Subject: [PATCH 04/16] . --- sentry_sdk/traces.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/sentry_sdk/traces.py b/sentry_sdk/traces.py index 485be2a3e2..4788cb6173 100644 --- a/sentry_sdk/traces.py +++ b/sentry_sdk/traces.py @@ -341,8 +341,8 @@ def finish(self, end_timestamp: "Optional[Union[float, datetime]]" = None) -> No def _start(self) -> None: if self._active: - old_span = self._scope.span - self._scope.span = self + old_span = self._scope.streamed_span + self._scope.streamed_span = self self._previous_span_on_scope = old_span def _end(self, end_timestamp: "Optional[Union[float, datetime]]" = None) -> None: @@ -360,7 +360,7 @@ def _end(self, end_timestamp: "Optional[Union[float, datetime]]" = None) -> None with capture_internal_exceptions(): old_span = self._previous_span_on_scope del self._previous_span_on_scope - self._scope.span = old_span + self._scope.streamed_span = old_span # Set attributes from the segment. These are set on span end on purpose # so that we have the best chance to capture the segment's final name @@ -586,8 +586,8 @@ def _start(self) -> None: if self._scope is None: return - old_span = self._scope.span - self._scope.span = self + old_span = self._scope.streamed_span + self._scope.streamed_span = self self._previous_span_on_scope = old_span def _end(self, end_timestamp: "Optional[Union[float, datetime]]" = None) -> None: @@ -610,7 +610,7 @@ def _end(self, end_timestamp: "Optional[Union[float, datetime]]" = None) -> None with capture_internal_exceptions(): old_span = self._previous_span_on_scope del self._previous_span_on_scope - self._scope.span = old_span + self._scope.streamed_span = old_span self._finished = True From 2e81110706f003f1ea1b24ac3c1501414fcbecbc Mon Sep 17 00:00:00 2001 From: Alexander Alderman Webb Date: Thu, 30 Apr 2026 11:05:41 +0200 Subject: [PATCH 05/16] . --- sentry_sdk/integrations/celery/__init__.py | 13 ++++++++----- sentry_sdk/integrations/fastapi.py | 16 +++++++++------- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/sentry_sdk/integrations/celery/__init__.py b/sentry_sdk/integrations/celery/__init__.py index 6c033bf3d1..5afc05adbf 100644 --- a/sentry_sdk/integrations/celery/__init__.py +++ b/sentry_sdk/integrations/celery/__init__.py @@ -98,13 +98,16 @@ def setup_once() -> None: def _set_status(status: str) -> None: + client = sentry_sdk.get_client() + span_streaming = has_span_streaming_enabled(client.options) + with capture_internal_exceptions(): scope = sentry_sdk.get_current_scope() - if scope.span is not None: - if isinstance(scope.span, Span): - scope.span.set_status(status) - else: - scope.span.status = "ok" if status == "ok" else "error" + + if span_streaming and scope.streamed_span is not None: + scope.streamed_span.status = "ok" if status == "ok" else "error" + elif not span_streaming and scope.span is not None: + scope.span.set_status(status) def _capture_exception(task: "Any", exc_info: "ExcInfo") -> None: diff --git a/sentry_sdk/integrations/fastapi.py b/sentry_sdk/integrations/fastapi.py index 12874a10b3..0729e08b65 100644 --- a/sentry_sdk/integrations/fastapi.py +++ b/sentry_sdk/integrations/fastapi.py @@ -5,7 +5,7 @@ import sentry_sdk from sentry_sdk.integrations import DidNotEnable from sentry_sdk.scope import should_send_default_pii -from sentry_sdk.traces import NoOpStreamedSpan, StreamedSpan +from sentry_sdk.traces import NoOpStreamedSpan from sentry_sdk.tracing import SOURCE_FOR_STYLE, TransactionSource from sentry_sdk.tracing_utils import has_span_streaming_enabled from sentry_sdk.utils import transaction_from_function @@ -90,13 +90,15 @@ def _sentry_get_request_handler(*args: "Any", **kwargs: "Any") -> "Any": @wraps(old_call) def _sentry_call(*args: "Any", **kwargs: "Any") -> "Any": current_scope = sentry_sdk.get_current_scope() - current_span = current_scope.span - if isinstance(current_span, StreamedSpan) and not isinstance( - current_span, NoOpStreamedSpan - ): - segment = current_span._segment - segment._update_active_thread() + client = sentry_sdk.get_client() + if has_span_streaming_enabled(client.options): + current_span = current_scope.streamed_span + + if not isinstance(current_span, NoOpStreamedSpan): + segment = current_span._segment + segment._update_active_thread() + elif current_scope.transaction is not None: current_scope.transaction.update_active_thread() From f4d2b93c00f46c0c5febccc906449475badb3c32 Mon Sep 17 00:00:00 2001 From: Alexander Alderman Webb Date: Thu, 30 Apr 2026 11:14:53 +0200 Subject: [PATCH 06/16] . --- sentry_sdk/integrations/celery/__init__.py | 7 +++++-- sentry_sdk/integrations/starlette.py | 19 +++++++++++-------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/sentry_sdk/integrations/celery/__init__.py b/sentry_sdk/integrations/celery/__init__.py index 5afc05adbf..ab6e85cfbb 100644 --- a/sentry_sdk/integrations/celery/__init__.py +++ b/sentry_sdk/integrations/celery/__init__.py @@ -292,7 +292,10 @@ def apply_async(*args: "Any", **kwargs: "Any") -> "Any": span_mgr: "Union[StreamedSpan, Span, NoOpMgr]" = NoOpMgr() if span_streaming: - if not task_started_from_beat and sentry_sdk.get_current_span() is not None: + if ( + not task_started_from_beat + and sentry_sdk.get_current_streamed_span() is not None + ): span_mgr = sentry_sdk.traces.start_span( name=task_name, attributes={ @@ -573,7 +576,7 @@ def sentry_publish(self: "Producer", *args: "Any", **kwargs: "Any") -> "Any": span: "Union[StreamedSpan, Span, None]" = None if span_streaming: - if sentry_sdk.get_current_span() is not None: + if sentry_sdk.get_current_streamed_span() is not None: span = sentry_sdk.traces.start_span( name=task_name, attributes={ diff --git a/sentry_sdk/integrations/starlette.py b/sentry_sdk/integrations/starlette.py index 3312604b69..1a4cae9cbd 100644 --- a/sentry_sdk/integrations/starlette.py +++ b/sentry_sdk/integrations/starlette.py @@ -545,19 +545,22 @@ def event_processor( @functools.wraps(old_func) def _sentry_sync_func(*args: "Any", **kwargs: "Any") -> "Any": - integration = sentry_sdk.get_client().get_integration( - StarletteIntegration - ) + client = sentry_sdk.get_client() + + integration = client.get_integration(StarletteIntegration) if integration is None: return old_func(*args, **kwargs) current_scope = sentry_sdk.get_current_scope() - current_span = current_scope.span - if isinstance(current_span, StreamedSpan) and not isinstance( - current_span, NoOpStreamedSpan - ): - current_span._segment._update_active_thread() + span_streaming = has_span_streaming_enabled(client.options) + if span_streaming: + current_span = current_scope.streamed_span + + if isinstance(current_span, StreamedSpan) and not isinstance( + current_span, NoOpStreamedSpan + ): + current_span._segment._update_active_thread() elif current_scope.transaction is not None: current_scope.transaction.update_active_thread() From 8d3d3f05f834e4df6667ca88c9dcc72f7b4a50c8 Mon Sep 17 00:00:00 2001 From: Alexander Alderman Webb Date: Thu, 30 Apr 2026 11:17:02 +0200 Subject: [PATCH 07/16] . --- sentry_sdk/integrations/fastapi.py | 6 ++++-- sentry_sdk/integrations/starlette.py | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/sentry_sdk/integrations/fastapi.py b/sentry_sdk/integrations/fastapi.py index 0729e08b65..10d4fd81ac 100644 --- a/sentry_sdk/integrations/fastapi.py +++ b/sentry_sdk/integrations/fastapi.py @@ -5,7 +5,7 @@ import sentry_sdk from sentry_sdk.integrations import DidNotEnable from sentry_sdk.scope import should_send_default_pii -from sentry_sdk.traces import NoOpStreamedSpan +from sentry_sdk.traces import NoOpStreamedSpan, StreamedSpan from sentry_sdk.tracing import SOURCE_FOR_STYLE, TransactionSource from sentry_sdk.tracing_utils import has_span_streaming_enabled from sentry_sdk.utils import transaction_from_function @@ -95,7 +95,9 @@ def _sentry_call(*args: "Any", **kwargs: "Any") -> "Any": if has_span_streaming_enabled(client.options): current_span = current_scope.streamed_span - if not isinstance(current_span, NoOpStreamedSpan): + if isinstance(current_span, StreamedSpan) and not isinstance( + current_span, NoOpStreamedSpan + ): segment = current_span._segment segment._update_active_thread() diff --git a/sentry_sdk/integrations/starlette.py b/sentry_sdk/integrations/starlette.py index 1a4cae9cbd..6a2f68f109 100644 --- a/sentry_sdk/integrations/starlette.py +++ b/sentry_sdk/integrations/starlette.py @@ -254,7 +254,7 @@ def _default(value: "Any") -> "Any": def _set_request_body_data_on_streaming_segment( info: "Optional[Dict[str, Any]]", ) -> None: - current_span = sentry_sdk.get_current_span() + current_span = sentry_sdk.get_current_streamed_span() if ( info and "data" in info From 811b05d05afb00607b94fd51ec2c8371f3a5b055 Mon Sep 17 00:00:00 2001 From: Alexander Alderman Webb Date: Thu, 30 Apr 2026 11:22:24 +0200 Subject: [PATCH 08/16] . --- sentry_sdk/scope.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sentry_sdk/scope.py b/sentry_sdk/scope.py index ca0042f97a..87dd2588bf 100644 --- a/sentry_sdk/scope.py +++ b/sentry_sdk/scope.py @@ -1278,7 +1278,7 @@ def start_streamed_span( if parent_span is _DEFAULT_PARENT_SPAN or isinstance( parent_span, NoOpStreamedSpan ): - parent_span = self.span # type: ignore + parent_span = self.streamed_span # type: ignore # If no eligible parent_span was provided and there is no currently # active span, this is a segment From 5f8835bfdc6b872d0e2ce53b33ea6dca226daa8d Mon Sep 17 00:00:00 2001 From: Alexander Alderman Webb Date: Thu, 30 Apr 2026 11:29:40 +0200 Subject: [PATCH 09/16] . --- sentry_sdk/scope.py | 2 +- tests/integrations/celery/test_celery.py | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/sentry_sdk/scope.py b/sentry_sdk/scope.py index 87dd2588bf..4fcfca6dcd 100644 --- a/sentry_sdk/scope.py +++ b/sentry_sdk/scope.py @@ -1278,7 +1278,7 @@ def start_streamed_span( if parent_span is _DEFAULT_PARENT_SPAN or isinstance( parent_span, NoOpStreamedSpan ): - parent_span = self.streamed_span # type: ignore + parent_span = self.streamed_span # If no eligible parent_span was provided and there is no currently # active span, this is a segment diff --git a/tests/integrations/celery/test_celery.py b/tests/integrations/celery/test_celery.py index 8e1689296f..b32e1cae10 100644 --- a/tests/integrations/celery/test_celery.py +++ b/tests/integrations/celery/test_celery.py @@ -7,7 +7,7 @@ from celery.bin import worker import sentry_sdk -from sentry_sdk import start_transaction, get_current_span +from sentry_sdk import start_transaction, get_current_span, get_current_streamed_span from sentry_sdk.integrations.celery import ( CeleryIntegration, _wrap_task_run, @@ -663,7 +663,11 @@ def test_sentry_propagate_traces_override(span_streaming, init_celery): @celery.task(name="dummy_task", bind=True) def dummy_task(self, message): - trace_id = get_current_span().trace_id + trace_id = ( + get_current_streamed_span().trace_id + if span_streaming + else get_current_span().trace_id + ) return trace_id if span_streaming: From 9c2ea89a320679a66ade05f165d56c101e6a3dc8 Mon Sep 17 00:00:00 2001 From: Alexander Alderman Webb Date: Thu, 30 Apr 2026 11:38:42 +0200 Subject: [PATCH 10/16] . --- sentry_sdk/scope.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sentry_sdk/scope.py b/sentry_sdk/scope.py index 4fcfca6dcd..3aa79fff45 100644 --- a/sentry_sdk/scope.py +++ b/sentry_sdk/scope.py @@ -583,10 +583,10 @@ def get_traceparent(self, *args: "Any", **kwargs: "Any") -> "Optional[str]": # If we have an active span, return traceparent from there if ( has_tracing_enabled(client.options) - and self.span is not None + and self.streamed_span is not None and not isinstance(self.span, NoOpStreamedSpan) ): - return self.span._to_traceparent() + return self.streamed_span._to_traceparent() # else return traceparent from the propagation context return self.get_active_propagation_context().to_traceparent() @@ -601,10 +601,10 @@ def get_baggage(self, *args: "Any", **kwargs: "Any") -> "Optional[Baggage]": # If we have an active span, return baggage from there if ( has_tracing_enabled(client.options) - and self.span is not None + and self.streamed_span is not None and not isinstance(self.span, NoOpStreamedSpan) ): - return self.span._to_baggage() + return self.streamed_span._to_baggage() # else return baggage from the propagation context return self.get_active_propagation_context().get_baggage() From f592690fef0ce88d7cd3afafa58abbd9b49c8e09 Mon Sep 17 00:00:00 2001 From: Alexander Alderman Webb Date: Thu, 30 Apr 2026 11:49:23 +0200 Subject: [PATCH 11/16] . --- sentry_sdk/scope.py | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/sentry_sdk/scope.py b/sentry_sdk/scope.py index 3aa79fff45..b880d4c91f 100644 --- a/sentry_sdk/scope.py +++ b/sentry_sdk/scope.py @@ -580,13 +580,19 @@ def get_traceparent(self, *args: "Any", **kwargs: "Any") -> "Optional[str]": """ client = self.get_client() + if not has_tracing_enabled(client.options): + return self.get_active_propagation_context().to_traceparent() + + span_streaming = has_span_streaming_enabled(client.options) # If we have an active span, return traceparent from there if ( - has_tracing_enabled(client.options) + span_streaming and self.streamed_span is not None - and not isinstance(self.span, NoOpStreamedSpan) + and not isinstance(self.streamed_span, NoOpStreamedSpan) ): return self.streamed_span._to_traceparent() + elif not span_streaming and self.span is not None: + return self.span._to_traceparent() # else return traceparent from the propagation context return self.get_active_propagation_context().to_traceparent() @@ -598,13 +604,19 @@ def get_baggage(self, *args: "Any", **kwargs: "Any") -> "Optional[Baggage]": """ client = self.get_client() + if not has_tracing_enabled(client.options): + return self.get_active_propagation_context().get_baggage() + + span_streaming = has_span_streaming_enabled(client.options) # If we have an active span, return baggage from there if ( - has_tracing_enabled(client.options) + span_streaming and self.streamed_span is not None - and not isinstance(self.span, NoOpStreamedSpan) + and not isinstance(self.streamed_span, NoOpStreamedSpan) ): return self.streamed_span._to_baggage() + elif not span_streaming and self.span is not None: + return self.span._to_baggage() # else return baggage from the propagation context return self.get_active_propagation_context().get_baggage() From e0824dfd13c5e035b29c44f8645ece24061ecade Mon Sep 17 00:00:00 2001 From: Alexander Alderman Webb Date: Thu, 30 Apr 2026 12:54:12 +0200 Subject: [PATCH 12/16] remove mock --- tests/test_api.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/test_api.py b/tests/test_api.py index bd6b245841..a066ce1172 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -21,6 +21,8 @@ get_isolation_scope, ) +from sentry_sdk.tracing import Span + from sentry_sdk.client import Client, NonRecordingClient from tests.conftest import TestTransportWithOptions @@ -40,7 +42,7 @@ def test_get_current_span_default_hub(sentry_init): assert get_current_span() is None scope = get_current_scope() - fake_span = mock.MagicMock() + fake_span = Span() scope.span = fake_span assert get_current_span() == fake_span From a8adaeebe70a16dae670d4977896cd93cb2fff82 Mon Sep 17 00:00:00 2001 From: Alexander Alderman Webb Date: Thu, 30 Apr 2026 13:20:39 +0200 Subject: [PATCH 13/16] update update_current_span() --- sentry_sdk/api.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/sentry_sdk/api.py b/sentry_sdk/api.py index 568482e707..f52bdcabce 100644 --- a/sentry_sdk/api.py +++ b/sentry_sdk/api.py @@ -543,12 +543,7 @@ def update_current_span( attributes={"user_id": 123, "batch_size": 50} ) """ - current_span = get_current_span() - - if current_span is None: - return - - if isinstance(current_span, StreamedSpan): + if isinstance(get_current_streamed_span(), StreamedSpan): warnings.warn( "The `update_current_span` API isn't available in streaming mode. " "Retrieve the current span with get_current_span() and use its API " @@ -558,6 +553,11 @@ def update_current_span( ) return + current_span = get_current_span() + + if current_span is None: + return + if op is not None: current_span.op = op From 11fe22827ff5c017ca71b82e52cc27796f07e7a4 Mon Sep 17 00:00:00 2001 From: Alexander Alderman Webb Date: Thu, 30 Apr 2026 13:34:55 +0200 Subject: [PATCH 14/16] limit api to traces.py and mark private --- sentry_sdk/__init__.py | 1 - sentry_sdk/api.py | 14 ++------------ sentry_sdk/integrations/celery/__init__.py | 9 +++------ sentry_sdk/integrations/starlette.py | 4 ++-- sentry_sdk/traces.py | 11 +++++++++++ sentry_sdk/tracing_utils.py | 21 +++++++++------------ tests/integrations/celery/test_celery.py | 5 +++-- 7 files changed, 30 insertions(+), 35 deletions(-) diff --git a/sentry_sdk/__init__.py b/sentry_sdk/__init__.py index 3e97326a87..7fd0e1953d 100644 --- a/sentry_sdk/__init__.py +++ b/sentry_sdk/__init__.py @@ -32,7 +32,6 @@ "get_isolation_scope", "get_current_scope", "get_current_span", - "get_current_streamed_span", "get_traceparent", "is_initialized", "isolation_scope", diff --git a/sentry_sdk/api.py b/sentry_sdk/api.py index f52bdcabce..23f003557c 100644 --- a/sentry_sdk/api.py +++ b/sentry_sdk/api.py @@ -6,7 +6,7 @@ from sentry_sdk._init_implementation import init from sentry_sdk.consts import INSTRUMENTER from sentry_sdk.scope import Scope, _ScopeManager, new_scope, isolation_scope -from sentry_sdk.traces import StreamedSpan +from sentry_sdk.traces import StreamedSpan, _get_current_streamed_span from sentry_sdk.tracing import NoOpSpan, Transaction, trace from sentry_sdk.crons import monitor @@ -67,7 +67,6 @@ def overload(x: "T") -> "T": "get_isolation_scope", "get_current_scope", "get_current_span", - "get_current_streamed_span", "get_traceparent", "is_initialized", "isolation_scope", @@ -430,15 +429,6 @@ def get_current_span( return tracing_utils.get_current_span(scope) -def get_current_streamed_span( - scope: "Optional[Scope]" = None, -) -> "Optional[StreamedSpan]": - """ - Returns the currently active streamed span if there is one running, otherwise `None` - """ - return tracing_utils.get_current_streamed_span(scope) - - def get_traceparent() -> "Optional[str]": """ Returns the traceparent either from the active span or from the scope. @@ -543,7 +533,7 @@ def update_current_span( attributes={"user_id": 123, "batch_size": 50} ) """ - if isinstance(get_current_streamed_span(), StreamedSpan): + if isinstance(_get_current_streamed_span(), StreamedSpan): warnings.warn( "The `update_current_span` API isn't available in streaming mode. " "Retrieve the current span with get_current_span() and use its API " diff --git a/sentry_sdk/integrations/celery/__init__.py b/sentry_sdk/integrations/celery/__init__.py index ab6e85cfbb..6eb295d81c 100644 --- a/sentry_sdk/integrations/celery/__init__.py +++ b/sentry_sdk/integrations/celery/__init__.py @@ -15,7 +15,7 @@ from sentry_sdk.integrations.celery.utils import _now_seconds_since_epoch from sentry_sdk.integrations.logging import ignore_logger from sentry_sdk.scope import should_send_default_pii -from sentry_sdk.traces import StreamedSpan +from sentry_sdk.traces import StreamedSpan, _get_current_streamed_span from sentry_sdk.tracing import BAGGAGE_HEADER_NAME, Span, TransactionSource from sentry_sdk.tracing_utils import Baggage, has_span_streaming_enabled from sentry_sdk.utils import ( @@ -292,10 +292,7 @@ def apply_async(*args: "Any", **kwargs: "Any") -> "Any": span_mgr: "Union[StreamedSpan, Span, NoOpMgr]" = NoOpMgr() if span_streaming: - if ( - not task_started_from_beat - and sentry_sdk.get_current_streamed_span() is not None - ): + if not task_started_from_beat and _get_current_streamed_span() is not None: span_mgr = sentry_sdk.traces.start_span( name=task_name, attributes={ @@ -576,7 +573,7 @@ def sentry_publish(self: "Producer", *args: "Any", **kwargs: "Any") -> "Any": span: "Union[StreamedSpan, Span, None]" = None if span_streaming: - if sentry_sdk.get_current_streamed_span() is not None: + if _get_current_streamed_span() is not None: span = sentry_sdk.traces.start_span( name=task_name, attributes={ diff --git a/sentry_sdk/integrations/starlette.py b/sentry_sdk/integrations/starlette.py index 6a2f68f109..68ff98cc02 100644 --- a/sentry_sdk/integrations/starlette.py +++ b/sentry_sdk/integrations/starlette.py @@ -21,7 +21,7 @@ ) from sentry_sdk.integrations.asgi import SentryAsgiMiddleware from sentry_sdk.scope import should_send_default_pii -from sentry_sdk.traces import NoOpStreamedSpan, StreamedSpan +from sentry_sdk.traces import NoOpStreamedSpan, StreamedSpan, _get_current_streamed_span from sentry_sdk.tracing import ( SOURCE_FOR_STYLE, TransactionSource, @@ -254,7 +254,7 @@ def _default(value: "Any") -> "Any": def _set_request_body_data_on_streaming_segment( info: "Optional[Dict[str, Any]]", ) -> None: - current_span = sentry_sdk.get_current_streamed_span() + current_span = _get_current_streamed_span() if ( info and "data" in info diff --git a/sentry_sdk/traces.py b/sentry_sdk/traces.py index 4788cb6173..f6022c29b6 100644 --- a/sentry_sdk/traces.py +++ b/sentry_sdk/traces.py @@ -755,3 +755,14 @@ def make_db_query(sql): return decorator(func) else: return decorator + + +def _get_current_streamed_span( + scope: "Optional[sentry_sdk.Scope]" = None, +) -> "Optional[StreamedSpan]": + """ + Returns the currently active span if there is one running, otherwise `None` + """ + scope = scope or sentry_sdk.get_current_scope() + current_span = scope.streamed_span + return current_span diff --git a/sentry_sdk/tracing_utils.py b/sentry_sdk/tracing_utils.py index f1e4319275..ac3cb23303 100644 --- a/sentry_sdk/tracing_utils.py +++ b/sentry_sdk/tracing_utils.py @@ -35,6 +35,7 @@ _module_in_list, ) from sentry_sdk.tracing import Span as LegacySpan +from sentry_sdk.traces import _get_current_streamed_span from typing import TYPE_CHECKING @@ -1203,17 +1204,6 @@ def get_current_span( return current_span -def get_current_streamed_span( - scope: "Optional[sentry_sdk.Scope]" = None, -) -> "Optional[StreamedSpan]": - """ - Returns the currently active span if there is one running, otherwise `None` - """ - scope = scope or sentry_sdk.get_current_scope() - current_span = scope.streamed_span - return current_span - - def set_span_errored(span: "Optional[Union[Span, StreamedSpan]]" = None) -> None: """ Set the status of the current or given span to INTERNAL_ERROR. @@ -1221,7 +1211,14 @@ def set_span_errored(span: "Optional[Union[Span, StreamedSpan]]" = None) -> None """ from sentry_sdk.traces import StreamedSpan, SpanStatus - span = span or get_current_span() + client = sentry_sdk.get_client() + + if not span: + span = ( + _get_current_streamed_span() + if has_span_streaming_enabled(client.options) + else sentry_sdk.get_current_span() + ) if span is not None: if isinstance(span, Span): diff --git a/tests/integrations/celery/test_celery.py b/tests/integrations/celery/test_celery.py index b32e1cae10..4255dac211 100644 --- a/tests/integrations/celery/test_celery.py +++ b/tests/integrations/celery/test_celery.py @@ -7,7 +7,8 @@ from celery.bin import worker import sentry_sdk -from sentry_sdk import start_transaction, get_current_span, get_current_streamed_span +from sentry_sdk import start_transaction, get_current_span +from sentry_sdk.traces import _get_current_streamed_span from sentry_sdk.integrations.celery import ( CeleryIntegration, _wrap_task_run, @@ -664,7 +665,7 @@ def test_sentry_propagate_traces_override(span_streaming, init_celery): @celery.task(name="dummy_task", bind=True) def dummy_task(self, message): trace_id = ( - get_current_streamed_span().trace_id + _get_current_streamed_span().trace_id if span_streaming else get_current_span().trace_id ) From faece2f00dfb7356bf21f9796701e5abc2bd7b8e Mon Sep 17 00:00:00 2001 From: Alexander Alderman Webb Date: Thu, 30 Apr 2026 13:37:02 +0200 Subject: [PATCH 15/16] avoid circular import --- sentry_sdk/tracing_utils.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sentry_sdk/tracing_utils.py b/sentry_sdk/tracing_utils.py index ac3cb23303..33b1a33814 100644 --- a/sentry_sdk/tracing_utils.py +++ b/sentry_sdk/tracing_utils.py @@ -35,7 +35,6 @@ _module_in_list, ) from sentry_sdk.tracing import Span as LegacySpan -from sentry_sdk.traces import _get_current_streamed_span from typing import TYPE_CHECKING @@ -1209,7 +1208,7 @@ def set_span_errored(span: "Optional[Union[Span, StreamedSpan]]" = None) -> None Set the status of the current or given span to INTERNAL_ERROR. Also sets the status of the transaction (root span) to INTERNAL_ERROR. """ - from sentry_sdk.traces import StreamedSpan, SpanStatus + from sentry_sdk.traces import StreamedSpan, SpanStatus, _get_current_streamed_span client = sentry_sdk.get_client() From 76467e4bf0481c9cd4ba3594ab0dbfd339b6fe43 Mon Sep 17 00:00:00 2001 From: Alexander Alderman Webb Date: Thu, 30 Apr 2026 13:41:15 +0200 Subject: [PATCH 16/16] update docstring --- sentry_sdk/traces.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sentry_sdk/traces.py b/sentry_sdk/traces.py index f6022c29b6..47c532c523 100644 --- a/sentry_sdk/traces.py +++ b/sentry_sdk/traces.py @@ -761,7 +761,10 @@ def _get_current_streamed_span( scope: "Optional[sentry_sdk.Scope]" = None, ) -> "Optional[StreamedSpan]": """ - Returns the currently active span if there is one running, otherwise `None` + Returns the currently active span on the scope if the span is a `StreamedSpan`, otherwise `None`. + + This function will only return a non-`None` value when the streaming trace lifecycle is enabled. + To enable the lifecycle, pass `_experiments={"trace_lifecycle": "stream"}` to `sentry.init()`. """ scope = scope or sentry_sdk.get_current_scope() current_span = scope.streamed_span