2025-12-01

This commit is contained in:
2026-03-17 14:58:51 -06:00
parent 183e865f8b
commit 4b82b57113
6846 changed files with 954887 additions and 162606 deletions
@@ -9,6 +9,7 @@ from datetime import datetime, timezone
from functools import wraps
from itertools import chain
from sentry_sdk._types import AnnotatedValue
from sentry_sdk.attachments import Attachment
from sentry_sdk.consts import DEFAULT_MAX_BREADCRUMBS, FALSE_VALUES, INSTRUMENTER
from sentry_sdk.feature_flags import FlagBuffer, DEFAULT_FLAG_CAPACITY
@@ -43,10 +44,11 @@ from sentry_sdk.utils import (
logger,
)
import typing
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from collections.abc import Mapping, MutableMapping
from collections.abc import Mapping
from typing import Any
from typing import Callable
@@ -185,6 +187,8 @@ class Scope:
"_contexts",
"_extras",
"_breadcrumbs",
"_n_breadcrumbs_truncated",
"_gen_ai_original_message_count",
"_event_processors",
"_error_processors",
"_should_capture",
@@ -209,6 +213,8 @@ class Scope:
self._name = None # type: Optional[str]
self._propagation_context = None # type: Optional[PropagationContext]
self._n_breadcrumbs_truncated = 0 # type: int
self._gen_ai_original_message_count = {} # type: Dict[str, int]
self.client = NonRecordingClient() # type: sentry_sdk.client.BaseClient
@@ -234,23 +240,25 @@ class Scope:
rv._name = self._name
rv._fingerprint = self._fingerprint
rv._transaction = self._transaction
rv._transaction_info = dict(self._transaction_info)
rv._transaction_info = self._transaction_info.copy()
rv._user = self._user
rv._tags = dict(self._tags)
rv._contexts = dict(self._contexts)
rv._extras = dict(self._extras)
rv._tags = self._tags.copy()
rv._contexts = self._contexts.copy()
rv._extras = self._extras.copy()
rv._breadcrumbs = copy(self._breadcrumbs)
rv._event_processors = list(self._event_processors)
rv._error_processors = list(self._error_processors)
rv._n_breadcrumbs_truncated = self._n_breadcrumbs_truncated
rv._gen_ai_original_message_count = self._gen_ai_original_message_count.copy()
rv._event_processors = self._event_processors.copy()
rv._error_processors = self._error_processors.copy()
rv._propagation_context = self._propagation_context
rv._should_capture = self._should_capture
rv._span = self._span
rv._session = self._session
rv._force_auto_session_tracking = self._force_auto_session_tracking
rv._attachments = list(self._attachments)
rv._attachments = self._attachments.copy()
rv._profile = self._profile
@@ -678,12 +686,12 @@ class Scope:
self._level = None # type: Optional[LogLevelStr]
self._fingerprint = None # type: Optional[List[str]]
self._transaction = None # type: Optional[str]
self._transaction_info = {} # type: MutableMapping[str, str]
self._transaction_info = {} # type: dict[str, str]
self._user = None # type: Optional[Dict[str, Any]]
self._tags = {} # type: Dict[str, Any]
self._contexts = {} # type: Dict[str, Dict[str, Any]]
self._extras = {} # type: MutableMapping[str, Any]
self._extras = {} # type: dict[str, Any]
self._attachments = [] # type: List[Attachment]
self.clear_breadcrumbs()
@@ -793,6 +801,11 @@ class Scope:
def user(self, value):
# type: (Optional[Dict[str, Any]]) -> None
"""When set a specific user is bound to the scope. Deprecated in favor of set_user."""
warnings.warn(
"The `Scope.user` setter is deprecated in favor of `Scope.set_user()`.",
DeprecationWarning,
stacklevel=2,
)
self.set_user(value)
def set_user(self, value):
@@ -884,7 +897,8 @@ class Scope:
self._contexts[key] = value
def remove_context(
self, key # type: str
self,
key, # type: str
):
# type: (...) -> None
"""Removes a context."""
@@ -900,7 +914,8 @@ class Scope:
self._extras[key] = value
def remove_extra(
self, key # type: str
self,
key, # type: str
):
# type: (...) -> None
"""Removes a specific extra key."""
@@ -910,6 +925,7 @@ class Scope:
# type: () -> None
"""Clears breadcrumb buffer."""
self._breadcrumbs = deque() # type: Deque[Breadcrumb]
self._n_breadcrumbs_truncated = 0
def add_attachment(
self,
@@ -977,6 +993,7 @@ class Scope:
while len(self._breadcrumbs) > max_breadcrumbs:
self._breadcrumbs.popleft()
self._n_breadcrumbs_truncated += 1
def start_transaction(
self,
@@ -1146,8 +1163,20 @@ class Scope:
"""
self.generate_propagation_context(environ_or_headers)
# When we generate the propagation context, the sample_rand value is set
# if missing or invalid (we use the original value if it's valid).
# We want the transaction to use the same sample_rand value. Due to duplicated
# propagation logic in the transaction, we pass it in to avoid recomputing it
# in the transaction.
# TYPE SAFETY: self.generate_propagation_context() ensures that self._propagation_context
# is not None.
sample_rand = typing.cast(
PropagationContext, self._propagation_context
)._sample_rand()
transaction = Transaction.continue_from_headers(
normalize_incoming_data(environ_or_headers),
_sample_rand=sample_rand,
op=op,
origin=origin,
name=name,
@@ -1297,7 +1326,8 @@ class Scope:
self._force_auto_session_tracking = None
def add_event_processor(
self, func # type: EventProcessor
self,
func, # type: EventProcessor
):
# type: (...) -> None
"""Register a scope local event processor on the scope.
@@ -1348,17 +1378,23 @@ class Scope:
def _apply_breadcrumbs_to_event(self, event, hint, options):
# type: (Event, Hint, Optional[Dict[str, Any]]) -> None
event.setdefault("breadcrumbs", {}).setdefault("values", []).extend(
self._breadcrumbs
)
event.setdefault("breadcrumbs", {})
# This check is just for mypy -
if not isinstance(event["breadcrumbs"], AnnotatedValue):
event["breadcrumbs"].setdefault("values", [])
event["breadcrumbs"]["values"].extend(self._breadcrumbs)
# Attempt to sort timestamps
try:
for crumb in event["breadcrumbs"]["values"]:
if isinstance(crumb["timestamp"], str):
crumb["timestamp"] = datetime_from_isoformat(crumb["timestamp"])
if not isinstance(event["breadcrumbs"], AnnotatedValue):
for crumb in event["breadcrumbs"]["values"]:
if isinstance(crumb["timestamp"], str):
crumb["timestamp"] = datetime_from_isoformat(crumb["timestamp"])
event["breadcrumbs"]["values"].sort(key=lambda crumb: crumb["timestamp"])
event["breadcrumbs"]["values"].sort(
key=lambda crumb: crumb["timestamp"]
)
except Exception as err:
logger.debug("Error when sorting breadcrumbs", exc_info=err)
pass
@@ -1546,6 +1582,14 @@ class Scope:
self._extras.update(scope._extras)
if scope._breadcrumbs:
self._breadcrumbs.extend(scope._breadcrumbs)
if scope._n_breadcrumbs_truncated:
self._n_breadcrumbs_truncated = (
self._n_breadcrumbs_truncated + scope._n_breadcrumbs_truncated
)
if scope._gen_ai_original_message_count:
self._gen_ai_original_message_count.update(
scope._gen_ai_original_message_count
)
if scope._span:
self._span = scope._span
if scope._attachments:
@@ -1568,7 +1612,7 @@ class Scope:
user=None, # type: Optional[Any]
level=None, # type: Optional[LogLevelStr]
extras=None, # type: Optional[Dict[str, Any]]
contexts=None, # type: Optional[Dict[str, Any]]
contexts=None, # type: Optional[Dict[str, Dict[str, Any]]]
tags=None, # type: Optional[Dict[str, str]]
fingerprint=None, # type: Optional[List[str]]
):
@@ -1639,8 +1683,11 @@ def new_scope():
yield new_scope
finally:
# restore original scope
_current_scope.reset(token)
try:
# restore original scope
_current_scope.reset(token)
except (LookupError, ValueError):
capture_internal_exception(sys.exc_info())
@contextmanager
@@ -1674,8 +1721,11 @@ def use_scope(scope):
yield scope
finally:
# restore original scope
_current_scope.reset(token)
try:
# restore original scope
_current_scope.reset(token)
except (LookupError, ValueError):
capture_internal_exception(sys.exc_info())
@contextmanager
@@ -1716,8 +1766,15 @@ def isolation_scope():
finally:
# restore original scopes
_current_scope.reset(current_token)
_isolation_scope.reset(isolation_token)
try:
_current_scope.reset(current_token)
except (LookupError, ValueError):
capture_internal_exception(sys.exc_info())
try:
_isolation_scope.reset(isolation_token)
except (LookupError, ValueError):
capture_internal_exception(sys.exc_info())
@contextmanager
@@ -1756,8 +1813,15 @@ def use_isolation_scope(isolation_scope):
finally:
# restore original scopes
_current_scope.reset(current_token)
_isolation_scope.reset(isolation_token)
try:
_current_scope.reset(current_token)
except (LookupError, ValueError):
capture_internal_exception(sys.exc_info())
try:
_isolation_scope.reset(isolation_token)
except (LookupError, ValueError):
capture_internal_exception(sys.exc_info())
def should_send_default_pii():