Skip to content

Commit dda4bb0

Browse files
authored
Merge branch 'master' into toxgen/update
2 parents 5bfa699 + 2ce933d commit dda4bb0

File tree

13 files changed

+430
-41
lines changed

13 files changed

+430
-41
lines changed

sentry_sdk/_span_batcher.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,9 +114,7 @@ def _flush(self) -> None:
114114
envelopes = []
115115
for trace_id, spans in self._span_buffer.items():
116116
if spans:
117-
# TODO[span-first]
118-
# dsc = spans[0].dynamic_sampling_context()
119-
dsc = None
117+
dsc = spans[0]._dynamic_sampling_context()
120118

121119
# Max per envelope is 1000, so if we happen to have more than
122120
# 1000 spans in one bucket, we'll need to separate them.

sentry_sdk/ai/utils.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212

1313
import sentry_sdk
1414
from sentry_sdk.utils import logger
15+
from sentry_sdk.traces import StreamedSpan
16+
from sentry_sdk.tracing_utils import has_span_streaming_enabled
1517

1618
MAX_GEN_AI_MESSAGE_BYTES = 20_000 # 20KB
1719
# Maximum characters when only a single message is left after bytes truncation
@@ -523,7 +525,14 @@ def normalize_message_roles(messages: "list[dict[str, Any]]") -> "list[dict[str,
523525

524526

525527
def get_start_span_function() -> "Callable[..., Any]":
528+
if has_span_streaming_enabled(sentry_sdk.get_client().options):
529+
return sentry_sdk.traces.start_span
530+
526531
current_span = sentry_sdk.get_current_span()
532+
if isinstance(current_span, StreamedSpan):
533+
# mypy
534+
return sentry_sdk.traces.start_span
535+
527536
transaction_exists = (
528537
current_span is not None and current_span.containing_transaction is not None
529538
)

sentry_sdk/api.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from sentry_sdk._init_implementation import init
77
from sentry_sdk.consts import INSTRUMENTER
88
from sentry_sdk.scope import Scope, _ScopeManager, new_scope, isolation_scope
9+
from sentry_sdk.traces import StreamedSpan
910
from sentry_sdk.tracing import NoOpSpan, Transaction, trace
1011
from sentry_sdk.crons import monitor
1112

@@ -37,6 +38,7 @@
3738
LogLevelStr,
3839
SamplingContext,
3940
)
41+
from sentry_sdk.traces import StreamedSpan
4042
from sentry_sdk.tracing import Span, TransactionKwargs
4143

4244
T = TypeVar("T")
@@ -409,7 +411,9 @@ def set_measurement(name: str, value: float, unit: "MeasurementUnit" = "") -> No
409411
transaction.set_measurement(name, value, unit)
410412

411413

412-
def get_current_span(scope: "Optional[Scope]" = None) -> "Optional[Span]":
414+
def get_current_span(
415+
scope: "Optional[Scope]" = None,
416+
) -> "Optional[Union[Span, StreamedSpan]]":
413417
"""
414418
Returns the currently active span if there is one running, otherwise `None`
415419
"""
@@ -525,6 +529,16 @@ def update_current_span(
525529
if current_span is None:
526530
return
527531

532+
if isinstance(current_span, StreamedSpan):
533+
warnings.warn(
534+
"The `update_current_span` API isn't available in streaming mode. "
535+
"Retrieve the current span with get_current_span() and use its API "
536+
"directly.",
537+
DeprecationWarning,
538+
stacklevel=2,
539+
)
540+
return
541+
528542
if op is not None:
529543
current_span.op = op
530544

sentry_sdk/feature_flags.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import copy
22
import sentry_sdk
33
from sentry_sdk._lru_cache import LRUCache
4+
from sentry_sdk.tracing import Span
45
from threading import Lock
56

67
from typing import TYPE_CHECKING, Any
@@ -61,5 +62,5 @@ def add_feature_flag(flag: str, result: bool) -> None:
6162
flags.set(flag, result)
6263

6364
span = sentry_sdk.get_current_span()
64-
if span:
65+
if span and isinstance(span, Span):
6566
span.set_flag(f"flag.evaluation.{flag}", result)

sentry_sdk/integrations/celery/__init__.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
)
1515
from sentry_sdk.integrations.celery.utils import _now_seconds_since_epoch
1616
from sentry_sdk.integrations.logging import ignore_logger
17-
from sentry_sdk.tracing import BAGGAGE_HEADER_NAME, TransactionSource
17+
from sentry_sdk.tracing import BAGGAGE_HEADER_NAME, Span, TransactionSource
1818
from sentry_sdk.tracing_utils import Baggage
1919
from sentry_sdk.utils import (
2020
capture_internal_exceptions,
@@ -34,7 +34,6 @@
3434
from typing import Union
3535

3636
from sentry_sdk._types import EventProcessor, Event, Hint, ExcInfo
37-
from sentry_sdk.tracing import Span
3837

3938
F = TypeVar("F", bound=Callable[..., Any])
4039

@@ -100,7 +99,10 @@ def _set_status(status: str) -> None:
10099
with capture_internal_exceptions():
101100
scope = sentry_sdk.get_current_scope()
102101
if scope.span is not None:
103-
scope.span.set_status(status)
102+
if isinstance(scope.span, Span):
103+
scope.span.set_status(status)
104+
else:
105+
scope.span.status = "ok" if status == "ok" else "error"
104106

105107

106108
def _capture_exception(task: "Any", exc_info: "ExcInfo") -> None:

sentry_sdk/integrations/openai_agents/utils.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from sentry_sdk.consts import SPANDATA, SPANSTATUS, OP
1212
from sentry_sdk.integrations import DidNotEnable
1313
from sentry_sdk.scope import should_send_default_pii
14+
from sentry_sdk.tracing import Span
1415
from sentry_sdk.tracing_utils import set_span_errored
1516
from sentry_sdk.utils import event_from_exception, safe_serialize
1617
from sentry_sdk.ai._openai_completions_api import _transform_system_instructions
@@ -22,10 +23,10 @@
2223
from typing import TYPE_CHECKING
2324

2425
if TYPE_CHECKING:
25-
from typing import Any
26+
from typing import Any, Union
2627
from agents import Usage, TResponseInputItem
2728

28-
from sentry_sdk.tracing import Span
29+
from sentry_sdk.traces import StreamedSpan
2930
from sentry_sdk._types import TextPart
3031

3132
try:
@@ -46,8 +47,15 @@ def _capture_exception(exc: "Any") -> None:
4647
sentry_sdk.capture_event(event, hint=hint)
4748

4849

49-
def _record_exception_on_span(span: "Span", error: Exception) -> "Any":
50+
def _record_exception_on_span(
51+
span: "Union[Span, StreamedSpan]", error: Exception
52+
) -> "Any":
5053
set_span_errored(span)
54+
55+
if not isinstance(span, Span):
56+
# TODO[span-first]: make this work with streamedspans
57+
return
58+
5159
span.set_data("span.status", "error")
5260

5361
# Optionally capture the error details if we have them

sentry_sdk/profiler/continuous_profiler.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,7 @@ def reset_buffer(self) -> None:
308308

309309
@property
310310
def profiler_id(self) -> "Union[str, None]":
311-
if self.buffer is None:
311+
if not self.running or self.buffer is None:
312312
return None
313313
return self.buffer.profiler_id
314314

@@ -436,9 +436,9 @@ def run(self) -> None:
436436
# timestamp so we can use it next iteration
437437
last = time.perf_counter()
438438

439-
if self.buffer is not None:
440-
self.buffer.flush()
441-
self.buffer = None
439+
buffer = self.buffer
440+
if buffer is not None:
441+
buffer.flush()
442442

443443

444444
class ThreadContinuousScheduler(ContinuousScheduler):

sentry_sdk/scope.py

Lines changed: 73 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -581,8 +581,12 @@ def get_traceparent(self, *args: "Any", **kwargs: "Any") -> "Optional[str]":
581581
client = self.get_client()
582582

583583
# If we have an active span, return traceparent from there
584-
if has_tracing_enabled(client.options) and self.span is not None:
585-
return self.span.to_traceparent()
584+
if (
585+
has_tracing_enabled(client.options)
586+
and self.span is not None
587+
and not isinstance(self.span, NoOpStreamedSpan)
588+
):
589+
return self.span._to_traceparent()
586590

587591
# else return traceparent from the propagation context
588592
return self.get_active_propagation_context().to_traceparent()
@@ -595,8 +599,12 @@ def get_baggage(self, *args: "Any", **kwargs: "Any") -> "Optional[Baggage]":
595599
client = self.get_client()
596600

597601
# If we have an active span, return baggage from there
598-
if has_tracing_enabled(client.options) and self.span is not None:
599-
return self.span.to_baggage()
602+
if (
603+
has_tracing_enabled(client.options)
604+
and self.span is not None
605+
and not isinstance(self.span, NoOpStreamedSpan)
606+
):
607+
return self.span._to_baggage()
600608

601609
# else return baggage from the propagation context
602610
return self.get_active_propagation_context().get_baggage()
@@ -605,8 +613,12 @@ def get_trace_context(self) -> "Dict[str, Any]":
605613
"""
606614
Returns the Sentry "trace" context from the Propagation Context.
607615
"""
608-
if has_tracing_enabled(self.get_client().options) and self._span is not None:
609-
return self._span.get_trace_context()
616+
if (
617+
has_tracing_enabled(self.get_client().options)
618+
and self._span is not None
619+
and not isinstance(self._span, NoOpStreamedSpan)
620+
):
621+
return self._span._get_trace_context()
610622

611623
# if we are tracing externally (otel), those values take precedence
612624
external_propagation_context = get_external_propagation_context()
@@ -670,8 +682,12 @@ def iter_trace_propagation_headers(
670682
span = kwargs.pop("span", None)
671683
span = span or self.span
672684

673-
if has_tracing_enabled(client.options) and span is not None:
674-
for header in span.iter_headers():
685+
if (
686+
has_tracing_enabled(client.options)
687+
and span is not None
688+
and not isinstance(span, NoOpStreamedSpan)
689+
):
690+
for header in span._iter_headers():
675691
yield header
676692
elif has_external_propagation_context():
677693
# when we have an external_propagation_context (otlp)
@@ -718,7 +734,7 @@ def clear(self) -> None:
718734
self.clear_breadcrumbs()
719735
self._should_capture: bool = True
720736

721-
self._span: "Optional[Span]" = None
737+
self._span: "Optional[Union[Span, StreamedSpan]]" = None
722738
self._session: "Optional[Session]" = None
723739
self._force_auto_session_tracking: "Optional[bool]" = None
724740

@@ -772,6 +788,14 @@ def transaction(self) -> "Any":
772788
if self._span is None:
773789
return None
774790

791+
if isinstance(self._span, StreamedSpan):
792+
warnings.warn(
793+
"Scope.transaction is not available in streaming mode.",
794+
DeprecationWarning,
795+
stacklevel=2,
796+
)
797+
return None
798+
775799
# there is an orphan span on the scope
776800
if self._span.containing_transaction is None:
777801
return None
@@ -801,17 +825,36 @@ def transaction(self, value: "Any") -> None:
801825
"Assigning to scope.transaction directly is deprecated: use scope.set_transaction_name() instead."
802826
)
803827
self._transaction = value
804-
if self._span and self._span.containing_transaction:
805-
self._span.containing_transaction.name = value
828+
if self._span:
829+
if isinstance(self._span, StreamedSpan):
830+
warnings.warn(
831+
"Scope.transaction is not available in streaming mode.",
832+
DeprecationWarning,
833+
stacklevel=2,
834+
)
835+
return None
836+
837+
if self._span.containing_transaction:
838+
self._span.containing_transaction.name = value
806839

807840
def set_transaction_name(self, name: str, source: "Optional[str]" = None) -> None:
808841
"""Set the transaction name and optionally the transaction source."""
809842
self._transaction = name
810-
811-
if self._span and self._span.containing_transaction:
812-
self._span.containing_transaction.name = name
813-
if source:
814-
self._span.containing_transaction.source = source
843+
if self._span:
844+
if isinstance(self._span, NoOpStreamedSpan):
845+
return
846+
847+
elif isinstance(self._span, StreamedSpan):
848+
self._span._segment.name = name
849+
if source:
850+
self._span._segment.set_attribute(
851+
"sentry.span.source", getattr(source, "value", source)
852+
)
853+
854+
elif self._span.containing_transaction:
855+
self._span.containing_transaction.name = name
856+
if source:
857+
self._span.containing_transaction.source = source
815858

816859
if source:
817860
self._transaction_info["source"] = source
@@ -834,12 +877,12 @@ def set_user(self, value: "Optional[Dict[str, Any]]") -> None:
834877
session.update(user=value)
835878

836879
@property
837-
def span(self) -> "Optional[Span]":
880+
def span(self) -> "Optional[Union[Span, StreamedSpan]]":
838881
"""Get/set current tracing span or transaction."""
839882
return self._span
840883

841884
@span.setter
842-
def span(self, span: "Optional[Span]") -> None:
885+
def span(self, span: "Optional[Union[Span, StreamedSpan]]") -> None:
843886
self._span = span
844887
# XXX: this differs from the implementation in JS, there Scope.setSpan
845888
# does not set Scope._transactionName.
@@ -1148,6 +1191,15 @@ def start_span(
11481191
be removed in the next major version. Going forward, it should only
11491192
be used by the SDK itself.
11501193
"""
1194+
client = sentry_sdk.get_client()
1195+
if has_span_streaming_enabled(client.options):
1196+
warnings.warn(
1197+
"Scope.start_span is not available in streaming mode.",
1198+
DeprecationWarning,
1199+
stacklevel=2,
1200+
)
1201+
return NoOpSpan()
1202+
11511203
if kwargs.get("description") is not None:
11521204
warnings.warn(
11531205
"The `description` parameter is deprecated. Please use `name` instead.",
@@ -1167,6 +1219,9 @@ def start_span(
11671219

11681220
# get current span or transaction
11691221
span = self.span or self.get_isolation_scope().span
1222+
if isinstance(span, StreamedSpan):
1223+
# make mypy happy
1224+
return NoOpSpan()
11701225

11711226
if span is None:
11721227
# New spans get the `trace_id` from the scope

0 commit comments

Comments
 (0)