From 598e59410015ca386af27843cc35771ce1261596 Mon Sep 17 00:00:00 2001 From: Neel Shah Date: Wed, 10 Dec 2025 18:40:47 +0100 Subject: [PATCH] Cleanup outgoing propagation_context logic --- sentry_sdk/scope.py | 78 +++++++++----------------------- sentry_sdk/tracing_utils.py | 41 +++++++++++++++-- tests/test_propagationcontext.py | 6 +-- 3 files changed, 61 insertions(+), 64 deletions(-) diff --git a/sentry_sdk/scope.py b/sentry_sdk/scope.py index 466e1b5b12..c93dc19e87 100644 --- a/sentry_sdk/scope.py +++ b/sentry_sdk/scope.py @@ -525,14 +525,12 @@ def get_dynamic_sampling_context(self): """ Returns the Dynamic Sampling Context from the Propagation Context. If not existing, creates a new one. + + Deprecated: Logic moved to PropagationContext, don't use directly. """ if self._propagation_context is None: return None - baggage = self.get_baggage() - if baggage is not None: - self._propagation_context.baggage = baggage - return self._propagation_context.dynamic_sampling_context def get_traceparent(self, *args, **kwargs): @@ -547,16 +545,13 @@ def get_traceparent(self, *args, **kwargs): if has_tracing_enabled(client.options) and self.span is not None: return self.span.to_traceparent() - # If this scope has a propagation context, return traceparent from there - if self._propagation_context is not None: - traceparent = "%s-%s" % ( - self._propagation_context.trace_id, - self._propagation_context.span_id, - ) - return traceparent + # else return traceparent from the propagation context + propagation_context = self.get_active_propagation_context() + if propagation_context is not None: + return propagation_context.to_traceparent() - # Fall back to isolation scope's traceparent. It always has one - return self.get_isolation_scope().get_traceparent() + # TODO-neel will never happen + return None def get_baggage(self, *args, **kwargs): # type: (Any, Any) -> Optional[Baggage] @@ -570,12 +565,13 @@ def get_baggage(self, *args, **kwargs): if has_tracing_enabled(client.options) and self.span is not None: return self.span.to_baggage() - # If this scope has a propagation context, return baggage from there - if self._propagation_context is not None: - return self._propagation_context.baggage or Baggage.from_options(self) + # else return baggage from the propagation context + propagation_context = self.get_active_propagation_context() + if propagation_context is not None: + return propagation_context.get_baggage() - # Fall back to isolation scope's baggage. It always has one - return self.get_isolation_scope().get_baggage() + # TODO-neel will never happen + return None def get_trace_context(self): # type: () -> Dict[str, Any] @@ -599,7 +595,7 @@ def get_trace_context(self): "trace_id": propagation_context.trace_id, "span_id": propagation_context.span_id, "parent_span_id": propagation_context.parent_span_id, - "dynamic_sampling_context": self.get_dynamic_sampling_context(), + "dynamic_sampling_context": propagation_context.dynamic_sampling_context, } def trace_propagation_meta(self, *args, **kwargs): @@ -616,19 +612,8 @@ def trace_propagation_meta(self, *args, **kwargs): meta = "" - sentry_trace = self.get_traceparent() - if sentry_trace is not None: - meta += '' % ( - SENTRY_TRACE_HEADER_NAME, - sentry_trace, - ) - - baggage = self.get_baggage() - if baggage is not None: - meta += '' % ( - BAGGAGE_HEADER_NAME, - baggage.serialize(), - ) + for name, content in self.iter_trace_propagation_headers(): + meta += f'' return meta @@ -636,16 +621,10 @@ def iter_headers(self): # type: () -> Iterator[Tuple[str, str]] """ Creates a generator which returns the `sentry-trace` and `baggage` headers from the Propagation Context. + Deprecated: use PropagationContext.iter_headers instead. """ if self._propagation_context is not None: - traceparent = self.get_traceparent() - if traceparent is not None: - yield SENTRY_TRACE_HEADER_NAME, traceparent - - dsc = self.get_dynamic_sampling_context() - if dsc is not None: - baggage = Baggage(dsc).serialize() - yield BAGGAGE_HEADER_NAME, baggage + yield from self._propagation_context.iter_headers() def iter_trace_propagation_headers(self, *args, **kwargs): # type: (Any, Any) -> Generator[Tuple[str, str], None, None] @@ -671,23 +650,10 @@ def iter_trace_propagation_headers(self, *args, **kwargs): for header in span.iter_headers(): yield header else: - # If this scope has a propagation context, return headers from there - # (it could be that self is not the current scope nor the isolation scope) - if self._propagation_context is not None: - for header in self.iter_headers(): + propagation_context = self.get_active_propagation_context() + if propagation_context is not None: + for header in propagation_context.iter_headers(): yield header - else: - # otherwise try headers from current scope - current_scope = self.get_current_scope() - if current_scope._propagation_context is not None: - for header in current_scope.iter_headers(): - yield header - else: - # otherwise fall back to headers from isolation scope - isolation_scope = self.get_isolation_scope() - if isolation_scope._propagation_context is not None: - for header in isolation_scope.iter_headers(): - yield header def get_active_propagation_context(self): # type: () -> Optional[PropagationContext] diff --git a/sentry_sdk/tracing_utils.py b/sentry_sdk/tracing_utils.py index 69ba197ddf..df922faaa2 100644 --- a/sentry_sdk/tracing_utils.py +++ b/sentry_sdk/tracing_utils.py @@ -35,6 +35,8 @@ from typing import Generator from typing import Optional from typing import Union + from typing import Iterator + from typing import Tuple from types import FrameType @@ -506,7 +508,28 @@ def span_id(self, value): @property def dynamic_sampling_context(self): # type: () -> Optional[Dict[str, Any]] - return self.baggage.dynamic_sampling_context() if self.baggage else None + return self.get_baggage().dynamic_sampling_context() + + def to_traceparent(self): + # type: () -> str + return f"{self.trace_id}-{self.span_id}" + + def get_baggage(self): + # type: () -> Baggage + if self.baggage is None: + self.baggage = Baggage.populate_from_propagation_context(self) + return self.baggage + + def iter_headers(self): + # type: () -> Iterator[Tuple[str, str]] + """ + Creates a generator which returns the propagation_context's ``sentry-trace`` and ``baggage`` headers. + """ + yield SENTRY_TRACE_HEADER_NAME, self.to_traceparent() + + baggage = self.get_baggage().serialize() + if baggage: + yield BAGGAGE_HEADER_NAME, baggage def update(self, other_dict): # type: (Dict[str, Any]) -> None @@ -649,21 +672,29 @@ def from_incoming_header( @classmethod def from_options(cls, scope): # type: (sentry_sdk.scope.Scope) -> Optional[Baggage] + """ + Deprecated: use populate_from_propagation_context + """ + if scope._propagation_context is None: + return Baggage({}) + return Baggage.populate_from_propagation_context(scope._propagation_context) + + @classmethod + def populate_from_propagation_context(cls, propagation_context): + # type: (PropagationContext) -> Baggage sentry_items = {} # type: Dict[str, str] third_party_items = "" mutable = False client = sentry_sdk.get_client() - if not client.is_active() or scope._propagation_context is None: + if not client.is_active(): return Baggage(sentry_items) options = client.options - propagation_context = scope._propagation_context - if propagation_context is not None: - sentry_items["trace_id"] = propagation_context.trace_id + sentry_items["trace_id"] = propagation_context.trace_id if options.get("environment"): sentry_items["environment"] = options["environment"] diff --git a/tests/test_propagationcontext.py b/tests/test_propagationcontext.py index e014012956..6c14aa2952 100644 --- a/tests/test_propagationcontext.py +++ b/tests/test_propagationcontext.py @@ -25,7 +25,7 @@ def test_empty_context(): assert ctx.parent_span_id is None assert ctx.parent_sampled is None - assert ctx.dynamic_sampling_context is None + assert ctx.dynamic_sampling_context == {} def test_context_with_values(): @@ -72,7 +72,7 @@ def test_property_setters(): assert ctx.trace_id == "X234567890abcdef1234567890abcdef" assert ctx._span_id == "X234567890abcdef" assert ctx.span_id == "X234567890abcdef" - assert ctx.dynamic_sampling_context is None + assert ctx.dynamic_sampling_context == {} def test_update(): @@ -93,7 +93,7 @@ def test_update(): assert ctx._span_id is not None assert ctx.parent_span_id == "Z234567890abcdef" assert not ctx.parent_sampled - assert ctx.dynamic_sampling_context is None + assert ctx.dynamic_sampling_context == {} assert not hasattr(ctx, "foo")