sentry-sdk 2.30.0__py2.py3-none-any.whl → 3.0.0a2__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of sentry-sdk might be problematic. Click here for more details.

Files changed (109) hide show
  1. sentry_sdk/__init__.py +3 -8
  2. sentry_sdk/_compat.py +0 -1
  3. sentry_sdk/_init_implementation.py +6 -44
  4. sentry_sdk/_types.py +2 -64
  5. sentry_sdk/ai/monitoring.py +14 -10
  6. sentry_sdk/ai/utils.py +1 -1
  7. sentry_sdk/api.py +56 -169
  8. sentry_sdk/client.py +27 -72
  9. sentry_sdk/consts.py +60 -23
  10. sentry_sdk/debug.py +0 -10
  11. sentry_sdk/envelope.py +1 -3
  12. sentry_sdk/feature_flags.py +1 -1
  13. sentry_sdk/integrations/__init__.py +4 -2
  14. sentry_sdk/integrations/_asgi_common.py +5 -6
  15. sentry_sdk/integrations/_wsgi_common.py +11 -40
  16. sentry_sdk/integrations/aiohttp.py +104 -57
  17. sentry_sdk/integrations/anthropic.py +10 -7
  18. sentry_sdk/integrations/arq.py +24 -13
  19. sentry_sdk/integrations/asgi.py +102 -83
  20. sentry_sdk/integrations/asyncio.py +1 -0
  21. sentry_sdk/integrations/asyncpg.py +45 -30
  22. sentry_sdk/integrations/aws_lambda.py +109 -92
  23. sentry_sdk/integrations/boto3.py +38 -9
  24. sentry_sdk/integrations/bottle.py +1 -1
  25. sentry_sdk/integrations/celery/__init__.py +51 -41
  26. sentry_sdk/integrations/clickhouse_driver.py +59 -28
  27. sentry_sdk/integrations/cohere.py +2 -0
  28. sentry_sdk/integrations/django/__init__.py +25 -46
  29. sentry_sdk/integrations/django/asgi.py +6 -2
  30. sentry_sdk/integrations/django/caching.py +13 -22
  31. sentry_sdk/integrations/django/middleware.py +1 -0
  32. sentry_sdk/integrations/django/signals_handlers.py +3 -1
  33. sentry_sdk/integrations/django/templates.py +8 -12
  34. sentry_sdk/integrations/django/transactions.py +1 -6
  35. sentry_sdk/integrations/django/views.py +5 -2
  36. sentry_sdk/integrations/falcon.py +7 -25
  37. sentry_sdk/integrations/fastapi.py +3 -3
  38. sentry_sdk/integrations/flask.py +1 -1
  39. sentry_sdk/integrations/gcp.py +63 -38
  40. sentry_sdk/integrations/graphene.py +6 -13
  41. sentry_sdk/integrations/grpc/aio/client.py +14 -8
  42. sentry_sdk/integrations/grpc/aio/server.py +19 -21
  43. sentry_sdk/integrations/grpc/client.py +8 -6
  44. sentry_sdk/integrations/grpc/server.py +12 -14
  45. sentry_sdk/integrations/httpx.py +47 -12
  46. sentry_sdk/integrations/huey.py +26 -22
  47. sentry_sdk/integrations/huggingface_hub.py +1 -0
  48. sentry_sdk/integrations/langchain.py +22 -15
  49. sentry_sdk/integrations/litestar.py +4 -2
  50. sentry_sdk/integrations/logging.py +7 -2
  51. sentry_sdk/integrations/openai.py +2 -0
  52. sentry_sdk/integrations/pymongo.py +18 -25
  53. sentry_sdk/integrations/pyramid.py +1 -1
  54. sentry_sdk/integrations/quart.py +3 -3
  55. sentry_sdk/integrations/ray.py +23 -17
  56. sentry_sdk/integrations/redis/_async_common.py +29 -18
  57. sentry_sdk/integrations/redis/_sync_common.py +28 -19
  58. sentry_sdk/integrations/redis/modules/caches.py +13 -10
  59. sentry_sdk/integrations/redis/modules/queries.py +14 -11
  60. sentry_sdk/integrations/redis/rb.py +4 -4
  61. sentry_sdk/integrations/redis/redis.py +6 -6
  62. sentry_sdk/integrations/redis/redis_cluster.py +18 -18
  63. sentry_sdk/integrations/redis/redis_py_cluster_legacy.py +4 -4
  64. sentry_sdk/integrations/redis/utils.py +64 -24
  65. sentry_sdk/integrations/rq.py +68 -23
  66. sentry_sdk/integrations/rust_tracing.py +28 -43
  67. sentry_sdk/integrations/sanic.py +23 -13
  68. sentry_sdk/integrations/socket.py +9 -5
  69. sentry_sdk/integrations/sqlalchemy.py +8 -8
  70. sentry_sdk/integrations/starlette.py +11 -31
  71. sentry_sdk/integrations/starlite.py +4 -2
  72. sentry_sdk/integrations/stdlib.py +56 -9
  73. sentry_sdk/integrations/strawberry.py +40 -59
  74. sentry_sdk/integrations/threading.py +10 -26
  75. sentry_sdk/integrations/tornado.py +57 -18
  76. sentry_sdk/integrations/trytond.py +4 -1
  77. sentry_sdk/integrations/wsgi.py +84 -38
  78. sentry_sdk/opentelemetry/__init__.py +9 -0
  79. sentry_sdk/opentelemetry/consts.py +33 -0
  80. sentry_sdk/opentelemetry/contextvars_context.py +81 -0
  81. sentry_sdk/{integrations/opentelemetry → opentelemetry}/propagator.py +19 -28
  82. sentry_sdk/opentelemetry/sampler.py +326 -0
  83. sentry_sdk/opentelemetry/scope.py +218 -0
  84. sentry_sdk/opentelemetry/span_processor.py +335 -0
  85. sentry_sdk/opentelemetry/tracing.py +59 -0
  86. sentry_sdk/opentelemetry/utils.py +484 -0
  87. sentry_sdk/profiler/__init__.py +0 -40
  88. sentry_sdk/profiler/continuous_profiler.py +1 -30
  89. sentry_sdk/profiler/transaction_profiler.py +5 -56
  90. sentry_sdk/scope.py +108 -361
  91. sentry_sdk/sessions.py +0 -87
  92. sentry_sdk/tracing.py +415 -1161
  93. sentry_sdk/tracing_utils.py +130 -166
  94. sentry_sdk/transport.py +4 -104
  95. sentry_sdk/utils.py +169 -152
  96. {sentry_sdk-2.30.0.dist-info → sentry_sdk-3.0.0a2.dist-info}/METADATA +3 -5
  97. sentry_sdk-3.0.0a2.dist-info/RECORD +154 -0
  98. sentry_sdk-3.0.0a2.dist-info/entry_points.txt +2 -0
  99. sentry_sdk/hub.py +0 -739
  100. sentry_sdk/integrations/opentelemetry/__init__.py +0 -7
  101. sentry_sdk/integrations/opentelemetry/consts.py +0 -5
  102. sentry_sdk/integrations/opentelemetry/integration.py +0 -58
  103. sentry_sdk/integrations/opentelemetry/span_processor.py +0 -391
  104. sentry_sdk/metrics.py +0 -965
  105. sentry_sdk-2.30.0.dist-info/RECORD +0 -152
  106. sentry_sdk-2.30.0.dist-info/entry_points.txt +0 -2
  107. {sentry_sdk-2.30.0.dist-info → sentry_sdk-3.0.0a2.dist-info}/WHEEL +0 -0
  108. {sentry_sdk-2.30.0.dist-info → sentry_sdk-3.0.0a2.dist-info}/licenses/LICENSE +0 -0
  109. {sentry_sdk-2.30.0.dist-info → sentry_sdk-3.0.0a2.dist-info}/top_level.txt +0 -0
sentry_sdk/scope.py CHANGED
@@ -11,27 +11,23 @@ from itertools import chain
11
11
 
12
12
  from sentry_sdk._types import AnnotatedValue
13
13
  from sentry_sdk.attachments import Attachment
14
- from sentry_sdk.consts import DEFAULT_MAX_BREADCRUMBS, FALSE_VALUES, INSTRUMENTER
15
- from sentry_sdk.feature_flags import FlagBuffer, DEFAULT_FLAG_CAPACITY
16
- from sentry_sdk.profiler.continuous_profiler import (
17
- get_profiler_id,
18
- try_autostart_continuous_profiler,
19
- try_profile_lifecycle_trace_start,
14
+ from sentry_sdk.consts import (
15
+ DEFAULT_MAX_BREADCRUMBS,
16
+ FALSE_VALUES,
17
+ BAGGAGE_HEADER_NAME,
18
+ SENTRY_TRACE_HEADER_NAME,
20
19
  )
20
+ from sentry_sdk.feature_flags import FlagBuffer, DEFAULT_FLAG_CAPACITY
21
21
  from sentry_sdk.profiler.transaction_profiler import Profile
22
22
  from sentry_sdk.session import Session
23
23
  from sentry_sdk.tracing_utils import (
24
24
  Baggage,
25
25
  has_tracing_enabled,
26
- normalize_incoming_data,
27
26
  PropagationContext,
28
27
  )
29
28
  from sentry_sdk.tracing import (
30
- BAGGAGE_HEADER_NAME,
31
- SENTRY_TRACE_HEADER_NAME,
32
29
  NoOpSpan,
33
30
  Span,
34
- Transaction,
35
31
  )
36
32
  from sentry_sdk.utils import (
37
33
  capture_internal_exception,
@@ -44,7 +40,6 @@ from sentry_sdk.utils import (
44
40
  logger,
45
41
  )
46
42
 
47
- import typing
48
43
  from typing import TYPE_CHECKING
49
44
 
50
45
  if TYPE_CHECKING:
@@ -62,8 +57,7 @@ if TYPE_CHECKING:
62
57
  from typing import Tuple
63
58
  from typing import TypeVar
64
59
  from typing import Union
65
-
66
- from typing_extensions import Unpack
60
+ from typing import Self
67
61
 
68
62
  from sentry_sdk._types import (
69
63
  Breadcrumb,
@@ -74,12 +68,9 @@ if TYPE_CHECKING:
74
68
  ExcInfo,
75
69
  Hint,
76
70
  LogLevelStr,
77
- SamplingContext,
78
71
  Type,
79
72
  )
80
73
 
81
- from sentry_sdk.tracing import TransactionKwargs
82
-
83
74
  import sentry_sdk
84
75
 
85
76
  P = ParamSpec("P")
@@ -115,28 +106,6 @@ class ScopeType(Enum):
115
106
  MERGED = "merged"
116
107
 
117
108
 
118
- class _ScopeManager:
119
- def __init__(self, hub=None):
120
- # type: (Optional[Any]) -> None
121
- self._old_scopes = [] # type: List[Scope]
122
-
123
- def __enter__(self):
124
- # type: () -> Scope
125
- isolation_scope = Scope.get_isolation_scope()
126
-
127
- self._old_scopes.append(isolation_scope)
128
-
129
- forked_scope = isolation_scope.fork()
130
- _isolation_scope.set(forked_scope)
131
-
132
- return forked_scope
133
-
134
- def __exit__(self, exc_type, exc_value, tb):
135
- # type: (Any, Any, Any) -> None
136
- old_scope = self._old_scopes.pop()
137
- _isolation_scope.set(old_scope)
138
-
139
-
140
109
  def add_global_event_processor(processor):
141
110
  # type: (EventProcessor) -> None
142
111
  global_event_processors.append(processor)
@@ -203,8 +172,8 @@ class Scope:
203
172
  "_flags",
204
173
  )
205
174
 
206
- def __init__(self, ty=None, client=None):
207
- # type: (Optional[ScopeType], Optional[sentry_sdk.Client]) -> None
175
+ def __init__(self, ty=None):
176
+ # type: (Optional[ScopeType]) -> None
208
177
  self._type = ty
209
178
 
210
179
  self._event_processors = [] # type: List[EventProcessor]
@@ -216,21 +185,18 @@ class Scope:
216
185
 
217
186
  self.client = NonRecordingClient() # type: sentry_sdk.client.BaseClient
218
187
 
219
- if client is not None:
220
- self.set_client(client)
221
-
222
188
  self.clear()
223
189
 
224
190
  incoming_trace_information = self._load_trace_data_from_env()
225
191
  self.generate_propagation_context(incoming_data=incoming_trace_information)
226
192
 
227
193
  def __copy__(self):
228
- # type: () -> Scope
194
+ # type: () -> Self
229
195
  """
230
196
  Returns a copy of this scope.
231
197
  This also creates a copy of all referenced data structures.
232
198
  """
233
- rv = object.__new__(self.__class__) # type: Scope
199
+ rv = object.__new__(self.__class__) # type: Self
234
200
 
235
201
  rv._type = self._type
236
202
  rv.client = self.client
@@ -273,13 +239,21 @@ class Scope:
273
239
 
274
240
  Returns the current scope.
275
241
  """
276
- current_scope = _current_scope.get()
242
+ current_scope = cls._get_current_scope()
277
243
  if current_scope is None:
278
244
  current_scope = Scope(ty=ScopeType.CURRENT)
279
245
  _current_scope.set(current_scope)
280
246
 
281
247
  return current_scope
282
248
 
249
+ @classmethod
250
+ def _get_current_scope(cls):
251
+ # type: () -> Optional[Scope]
252
+ """
253
+ Returns the current scope without creating a new one. Internal use only.
254
+ """
255
+ return _current_scope.get()
256
+
283
257
  @classmethod
284
258
  def set_current_scope(cls, new_current_scope):
285
259
  # type: (Scope) -> None
@@ -299,13 +273,21 @@ class Scope:
299
273
 
300
274
  Returns the isolation scope.
301
275
  """
302
- isolation_scope = _isolation_scope.get()
276
+ isolation_scope = cls._get_isolation_scope()
303
277
  if isolation_scope is None:
304
278
  isolation_scope = Scope(ty=ScopeType.ISOLATION)
305
279
  _isolation_scope.set(isolation_scope)
306
280
 
307
281
  return isolation_scope
308
282
 
283
+ @classmethod
284
+ def _get_isolation_scope(cls):
285
+ # type: () -> Optional[Scope]
286
+ """
287
+ Returns the isolation scope without creating a new one. Internal use only.
288
+ """
289
+ return _isolation_scope.get()
290
+
309
291
  @classmethod
310
292
  def set_isolation_scope(cls, new_isolation_scope):
311
293
  # type: (Scope) -> None
@@ -349,7 +331,7 @@ class Scope:
349
331
  return cls.get_isolation_scope()._last_event_id
350
332
 
351
333
  def _merge_scopes(self, additional_scope=None, additional_scope_kwargs=None):
352
- # type: (Optional[Scope], Optional[Dict[str, Any]]) -> Scope
334
+ # type: (Optional[Scope], Optional[Dict[str, Any]]) -> Self
353
335
  """
354
336
  Merges global, isolation and current scope into a new scope and
355
337
  adds the given additional scope or additional scope kwargs to it.
@@ -357,16 +339,17 @@ class Scope:
357
339
  if additional_scope and additional_scope_kwargs:
358
340
  raise TypeError("cannot provide scope and kwargs")
359
341
 
360
- final_scope = copy(_global_scope) if _global_scope is not None else Scope()
342
+ final_scope = self.__class__()
361
343
  final_scope._type = ScopeType.MERGED
362
344
 
363
- isolation_scope = _isolation_scope.get()
364
- if isolation_scope is not None:
365
- final_scope.update_from_scope(isolation_scope)
345
+ global_scope = self.get_global_scope()
346
+ final_scope.update_from_scope(global_scope)
366
347
 
367
- current_scope = _current_scope.get()
368
- if current_scope is not None:
369
- final_scope.update_from_scope(current_scope)
348
+ isolation_scope = self.get_isolation_scope()
349
+ final_scope.update_from_scope(self.get_isolation_scope())
350
+
351
+ current_scope = self.get_current_scope()
352
+ final_scope.update_from_scope(current_scope)
370
353
 
371
354
  if self != current_scope and self != isolation_scope:
372
355
  final_scope.update_from_scope(self)
@@ -392,7 +375,7 @@ class Scope:
392
375
  This checks the current scope, the isolation scope and the global scope for a client.
393
376
  If no client is available a :py:class:`sentry_sdk.client.NonRecordingClient` is returned.
394
377
  """
395
- current_scope = _current_scope.get()
378
+ current_scope = cls.get_current_scope()
396
379
  try:
397
380
  client = current_scope.client
398
381
  except AttributeError:
@@ -401,7 +384,7 @@ class Scope:
401
384
  if client is not None and client.is_active():
402
385
  return client
403
386
 
404
- isolation_scope = _isolation_scope.get()
387
+ isolation_scope = cls.get_isolation_scope()
405
388
  try:
406
389
  client = isolation_scope.client
407
390
  except AttributeError:
@@ -434,7 +417,7 @@ class Scope:
434
417
  self.client = client if client is not None else NonRecordingClient()
435
418
 
436
419
  def fork(self):
437
- # type: () -> Scope
420
+ # type: () -> Self
438
421
  """
439
422
  .. versionadded:: 2.0.0
440
423
 
@@ -496,19 +479,10 @@ class Scope:
496
479
  def get_dynamic_sampling_context(self):
497
480
  # type: () -> Optional[Dict[str, str]]
498
481
  """
499
- Returns the Dynamic Sampling Context from the Propagation Context.
500
- If not existing, creates a new one.
482
+ Returns the Dynamic Sampling Context from the baggage or populates one.
501
483
  """
502
- if self._propagation_context is None:
503
- return None
504
-
505
484
  baggage = self.get_baggage()
506
- if baggage is not None:
507
- self._propagation_context.dynamic_sampling_context = (
508
- baggage.dynamic_sampling_context()
509
- )
510
-
511
- return self._propagation_context.dynamic_sampling_context
485
+ return baggage.dynamic_sampling_context() if baggage else None
512
486
 
513
487
  def get_traceparent(self, *args, **kwargs):
514
488
  # type: (Any, Any) -> Optional[str]
@@ -519,16 +493,16 @@ class Scope:
519
493
  client = self.get_client()
520
494
 
521
495
  # If we have an active span, return traceparent from there
522
- if has_tracing_enabled(client.options) and self.span is not None:
496
+ if (
497
+ has_tracing_enabled(client.options)
498
+ and self.span is not None
499
+ and self.span.is_valid
500
+ ):
523
501
  return self.span.to_traceparent()
524
502
 
525
503
  # If this scope has a propagation context, return traceparent from there
526
504
  if self._propagation_context is not None:
527
- traceparent = "%s-%s" % (
528
- self._propagation_context.trace_id,
529
- self._propagation_context.span_id,
530
- )
531
- return traceparent
505
+ return self._propagation_context.to_traceparent()
532
506
 
533
507
  # Fall back to isolation scope's traceparent. It always has one
534
508
  return self.get_isolation_scope().get_traceparent()
@@ -538,22 +512,24 @@ class Scope:
538
512
  """
539
513
  Returns the Sentry "baggage" header containing trace information from the
540
514
  currently active span or the scopes Propagation Context.
515
+ If not existing, creates a new one.
541
516
  """
542
517
  client = self.get_client()
543
518
 
544
519
  # If we have an active span, return baggage from there
545
- if has_tracing_enabled(client.options) and self.span is not None:
520
+ if (
521
+ has_tracing_enabled(client.options)
522
+ and self.span is not None
523
+ and self.span.is_valid
524
+ ):
546
525
  return self.span.to_baggage()
547
526
 
548
527
  # If this scope has a propagation context, return baggage from there
528
+ # populate a fresh one if it doesn't exist
549
529
  if self._propagation_context is not None:
550
- dynamic_sampling_context = (
551
- self._propagation_context.dynamic_sampling_context
552
- )
553
- if dynamic_sampling_context is None:
554
- return Baggage.from_options(self)
555
- else:
556
- return Baggage(dynamic_sampling_context)
530
+ if self._propagation_context.baggage is None:
531
+ self._propagation_context.baggage = Baggage.from_options(self)
532
+ return self._propagation_context.baggage
557
533
 
558
534
  # Fall back to isolation scope's baggage. It always has one
559
535
  return self.get_isolation_scope().get_baggage()
@@ -581,12 +557,6 @@ class Scope:
581
557
  Return meta tags which should be injected into HTML templates
582
558
  to allow propagation of trace information.
583
559
  """
584
- span = kwargs.pop("span", None)
585
- if span is not None:
586
- logger.warning(
587
- "The parameter `span` in trace_propagation_meta() is deprecated and will be removed in the future."
588
- )
589
-
590
560
  meta = ""
591
561
 
592
562
  sentry_trace = self.get_traceparent()
@@ -615,10 +585,9 @@ class Scope:
615
585
  if traceparent is not None:
616
586
  yield SENTRY_TRACE_HEADER_NAME, traceparent
617
587
 
618
- dsc = self.get_dynamic_sampling_context()
619
- if dsc is not None:
620
- baggage = Baggage(dsc).serialize()
621
- yield BAGGAGE_HEADER_NAME, baggage
588
+ baggage = self.get_baggage()
589
+ if baggage is not None:
590
+ yield BAGGAGE_HEADER_NAME, baggage.serialize()
622
591
 
623
592
  def iter_trace_propagation_headers(self, *args, **kwargs):
624
593
  # type: (Any, Any) -> Generator[Tuple[str, str], None, None]
@@ -629,18 +598,11 @@ class Scope:
629
598
  If no span is given, the trace data is taken from the scope.
630
599
  """
631
600
  client = self.get_client()
632
- if not client.options.get("propagate_traces"):
633
- warnings.warn(
634
- "The `propagate_traces` parameter is deprecated. Please use `trace_propagation_targets` instead.",
635
- DeprecationWarning,
636
- stacklevel=2,
637
- )
638
- return
639
601
 
640
602
  span = kwargs.pop("span", None)
641
603
  span = span or self.span
642
604
 
643
- if has_tracing_enabled(client.options) and span is not None:
605
+ if has_tracing_enabled(client.options) and span is not None and span.is_valid:
644
606
  for header in span.iter_headers():
645
607
  yield header
646
608
  else:
@@ -706,23 +668,6 @@ class Scope:
706
668
  self._last_event_id = None # type: Optional[str]
707
669
  self._flags = None # type: Optional[FlagBuffer]
708
670
 
709
- @_attr_setter
710
- def level(self, value):
711
- # type: (LogLevelStr) -> None
712
- """
713
- When set this overrides the level.
714
-
715
- .. deprecated:: 1.0.0
716
- Use :func:`set_level` instead.
717
-
718
- :param value: The level to set.
719
- """
720
- logger.warning(
721
- "Deprecated: use .set_level() instead. This will be removed in the future."
722
- )
723
-
724
- self._level = value
725
-
726
671
  def set_level(self, value):
727
672
  # type: (LogLevelStr) -> None
728
673
  """
@@ -739,71 +684,36 @@ class Scope:
739
684
  self._fingerprint = value
740
685
 
741
686
  @property
742
- def transaction(self):
743
- # type: () -> Any
744
- # would be type: () -> Optional[Transaction], see https://github.com/python/mypy/issues/3004
745
- """Return the transaction (root span) in the scope, if any."""
746
-
747
- # there is no span/transaction on the scope
687
+ def root_span(self):
688
+ # type: () -> Optional[Span]
689
+ """Return the root span in the scope, if any."""
748
690
  if self._span is None:
749
691
  return None
750
692
 
751
- # there is an orphan span on the scope
752
- if self._span.containing_transaction is None:
753
- return None
754
-
755
- # there is either a transaction (which is its own containing
756
- # transaction) or a non-orphan span on the scope
757
- return self._span.containing_transaction
758
-
759
- @transaction.setter
760
- def transaction(self, value):
761
- # type: (Any) -> None
762
- # would be type: (Optional[str]) -> None, see https://github.com/python/mypy/issues/3004
763
- """When set this forces a specific transaction name to be set.
764
-
765
- Deprecated: use set_transaction_name instead."""
766
-
767
- # XXX: the docstring above is misleading. The implementation of
768
- # apply_to_event prefers an existing value of event.transaction over
769
- # anything set in the scope.
770
- # XXX: note that with the introduction of the Scope.transaction getter,
771
- # there is a semantic and type mismatch between getter and setter. The
772
- # getter returns a Transaction, the setter sets a transaction name.
773
- # Without breaking version compatibility, we could make the setter set a
774
- # transaction name or transaction (self._span) depending on the type of
775
- # the value argument.
776
-
777
- logger.warning(
778
- "Assigning to scope.transaction directly is deprecated: use scope.set_transaction_name() instead."
779
- )
780
- self._transaction = value
781
- if self._span and self._span.containing_transaction:
782
- self._span.containing_transaction.name = value
693
+ return self._span.root_span
783
694
 
784
695
  def set_transaction_name(self, name, source=None):
785
696
  # type: (str, Optional[str]) -> None
786
697
  """Set the transaction name and optionally the transaction source."""
787
698
  self._transaction = name
788
699
 
789
- if self._span and self._span.containing_transaction:
790
- self._span.containing_transaction.name = name
700
+ if self._span and self._span.root_span:
701
+ self._span.root_span.name = name
791
702
  if source:
792
- self._span.containing_transaction.source = source
703
+ self._span.root_span.source = source
793
704
 
794
705
  if source:
795
706
  self._transaction_info["source"] = source
796
707
 
797
- @_attr_setter
798
- def user(self, value):
799
- # type: (Optional[Dict[str, Any]]) -> None
800
- """When set a specific user is bound to the scope. Deprecated in favor of set_user."""
801
- warnings.warn(
802
- "The `Scope.user` setter is deprecated in favor of `Scope.set_user()`.",
803
- DeprecationWarning,
804
- stacklevel=2,
805
- )
806
- self.set_user(value)
708
+ @property
709
+ def transaction_name(self):
710
+ # type: () -> Optional[str]
711
+ return self._transaction
712
+
713
+ @property
714
+ def transaction_source(self):
715
+ # type: () -> Optional[str]
716
+ return self._transaction_info.get("source")
807
717
 
808
718
  def set_user(self, value):
809
719
  # type: (Optional[Dict[str, Any]]) -> None
@@ -816,22 +726,9 @@ class Scope:
816
726
  @property
817
727
  def span(self):
818
728
  # type: () -> Optional[Span]
819
- """Get/set current tracing span or transaction."""
729
+ """Get current tracing span."""
820
730
  return self._span
821
731
 
822
- @span.setter
823
- def span(self, span):
824
- # type: (Optional[Span]) -> None
825
- self._span = span
826
- # XXX: this differs from the implementation in JS, there Scope.setSpan
827
- # does not set Scope._transactionName.
828
- if isinstance(span, Transaction):
829
- transaction = span
830
- if transaction.name:
831
- self._transaction = transaction.name
832
- if transaction.source:
833
- self._transaction_info["source"] = transaction.source
834
-
835
732
  @property
836
733
  def profile(self):
837
734
  # type: () -> Optional[Profile]
@@ -990,195 +887,41 @@ class Scope:
990
887
  self._breadcrumbs.popleft()
991
888
  self._n_breadcrumbs_truncated += 1
992
889
 
993
- def start_transaction(
994
- self,
995
- transaction=None,
996
- instrumenter=INSTRUMENTER.SENTRY,
997
- custom_sampling_context=None,
998
- **kwargs,
999
- ):
1000
- # type: (Optional[Transaction], str, Optional[SamplingContext], Unpack[TransactionKwargs]) -> Union[Transaction, NoOpSpan]
890
+ def start_transaction(self, **kwargs):
891
+ # type: (Any) -> Union[NoOpSpan, Span]
1001
892
  """
1002
- Start and return a transaction.
1003
-
1004
- Start an existing transaction if given, otherwise create and start a new
1005
- transaction with kwargs.
1006
-
1007
- This is the entry point to manual tracing instrumentation.
1008
-
1009
- A tree structure can be built by adding child spans to the transaction,
1010
- and child spans to other spans. To start a new child span within the
1011
- transaction or any span, call the respective `.start_child()` method.
1012
-
1013
- Every child span must be finished before the transaction is finished,
1014
- otherwise the unfinished spans are discarded.
1015
-
1016
- When used as context managers, spans and transactions are automatically
1017
- finished at the end of the `with` block. If not using context managers,
1018
- call the `.finish()` method.
1019
-
1020
- When the transaction is finished, it will be sent to Sentry with all its
1021
- finished child spans.
1022
-
1023
- :param transaction: The transaction to start. If omitted, we create and
1024
- start a new transaction.
1025
- :param instrumenter: This parameter is meant for internal use only. It
1026
- will be removed in the next major version.
1027
- :param custom_sampling_context: The transaction's custom sampling context.
1028
- :param kwargs: Optional keyword arguments to be passed to the Transaction
1029
- constructor. See :py:class:`sentry_sdk.tracing.Transaction` for
1030
- available arguments.
893
+ .. deprecated:: 3.0.0
894
+ This function is deprecated and will be removed in a future release.
895
+ Use :py:meth:`sentry_sdk.start_span` instead.
1031
896
  """
1032
- kwargs.setdefault("scope", self)
1033
-
1034
- client = self.get_client()
1035
-
1036
- configuration_instrumenter = client.options["instrumenter"]
1037
-
1038
- if instrumenter != configuration_instrumenter:
1039
- return NoOpSpan()
1040
-
1041
- try_autostart_continuous_profiler()
1042
-
1043
- custom_sampling_context = custom_sampling_context or {}
1044
-
1045
- # kwargs at this point has type TransactionKwargs, since we have removed
1046
- # the client and custom_sampling_context from it.
1047
- transaction_kwargs = kwargs # type: TransactionKwargs
1048
-
1049
- # if we haven't been given a transaction, make one
1050
- if transaction is None:
1051
- transaction = Transaction(**transaction_kwargs)
1052
-
1053
- # use traces_sample_rate, traces_sampler, and/or inheritance to make a
1054
- # sampling decision
1055
- sampling_context = {
1056
- "transaction_context": transaction.to_json(),
1057
- "parent_sampled": transaction.parent_sampled,
1058
- }
1059
- sampling_context.update(custom_sampling_context)
1060
- transaction._set_initial_sampling_decision(sampling_context=sampling_context)
1061
-
1062
- # update the sample rate in the dsc
1063
- if transaction.sample_rate is not None:
1064
- propagation_context = self.get_active_propagation_context()
1065
- if propagation_context:
1066
- dsc = propagation_context.dynamic_sampling_context
1067
- if dsc is not None:
1068
- dsc["sample_rate"] = str(transaction.sample_rate)
1069
- if transaction._baggage:
1070
- transaction._baggage.sentry_items["sample_rate"] = str(
1071
- transaction.sample_rate
1072
- )
1073
-
1074
- if transaction.sampled:
1075
- profile = Profile(
1076
- transaction.sampled, transaction._start_timestamp_monotonic_ns
1077
- )
1078
- profile._set_initial_sampling_decision(sampling_context=sampling_context)
1079
-
1080
- transaction._profile = profile
1081
-
1082
- transaction._continuous_profile = try_profile_lifecycle_trace_start()
1083
-
1084
- # Typically, the profiler is set when the transaction is created. But when
1085
- # using the auto lifecycle, the profiler isn't running when the first
1086
- # transaction is started. So make sure we update the profiler id on it.
1087
- if transaction._continuous_profile is not None:
1088
- transaction.set_profiler_id(get_profiler_id())
1089
-
1090
- # we don't bother to keep spans if we already know we're not going to
1091
- # send the transaction
1092
- max_spans = (client.options["_experiments"].get("max_spans")) or 1000
1093
- transaction.init_span_recorder(maxlen=max_spans)
1094
-
1095
- return transaction
897
+ warnings.warn(
898
+ "The `start_transaction` method is deprecated, please use `sentry_sdk.start_span instead.`",
899
+ DeprecationWarning,
900
+ stacklevel=2,
901
+ )
902
+ return NoOpSpan(**kwargs)
1096
903
 
1097
- def start_span(self, instrumenter=INSTRUMENTER.SENTRY, **kwargs):
1098
- # type: (str, Any) -> Span
904
+ def start_span(self, **kwargs):
905
+ # type: (Any) -> Union[NoOpSpan, Span]
1099
906
  """
1100
- Start a span whose parent is the currently active span or transaction, if any.
907
+ Start a span whose parent is the currently active span, if any.
1101
908
 
1102
909
  The return value is a :py:class:`sentry_sdk.tracing.Span` instance,
1103
910
  typically used as a context manager to start and stop timing in a `with`
1104
911
  block.
1105
912
 
1106
- Only spans contained in a transaction are sent to Sentry. Most
1107
- integrations start a transaction at the appropriate time, for example
1108
- for every incoming HTTP request. Use
1109
- :py:meth:`sentry_sdk.start_transaction` to start a new transaction when
1110
- one is not already in progress.
1111
-
1112
913
  For supported `**kwargs` see :py:class:`sentry_sdk.tracing.Span`.
1113
-
1114
- The instrumenter parameter is deprecated for user code, and it will
1115
- be removed in the next major version. Going forward, it should only
1116
- be used by the SDK itself.
1117
914
  """
1118
- if kwargs.get("description") is not None:
1119
- warnings.warn(
1120
- "The `description` parameter is deprecated. Please use `name` instead.",
1121
- DeprecationWarning,
1122
- stacklevel=2,
1123
- )
1124
-
1125
- with new_scope():
1126
- kwargs.setdefault("scope", self)
1127
-
1128
- client = self.get_client()
1129
-
1130
- configuration_instrumenter = client.options["instrumenter"]
1131
-
1132
- if instrumenter != configuration_instrumenter:
1133
- return NoOpSpan()
1134
-
1135
- # get current span or transaction
1136
- span = self.span or self.get_isolation_scope().span
1137
-
1138
- if span is None:
1139
- # New spans get the `trace_id` from the scope
1140
- if "trace_id" not in kwargs:
1141
- propagation_context = self.get_active_propagation_context()
1142
- if propagation_context is not None:
1143
- kwargs["trace_id"] = propagation_context.trace_id
915
+ return NoOpSpan(**kwargs)
1144
916
 
1145
- span = Span(**kwargs)
1146
- else:
1147
- # Children take `trace_id`` from the parent span.
1148
- span = span.start_child(**kwargs)
1149
-
1150
- return span
1151
-
1152
- def continue_trace(
1153
- self, environ_or_headers, op=None, name=None, source=None, origin="manual"
1154
- ):
1155
- # type: (Dict[str, Any], Optional[str], Optional[str], Optional[str], str) -> Transaction
917
+ @contextmanager
918
+ def continue_trace(self, environ_or_headers):
919
+ # type: (Dict[str, Any]) -> Generator[None, None, None]
1156
920
  """
1157
- Sets the propagation context from environment or headers and returns a transaction.
921
+ Sets the propagation context from environment or headers to continue an incoming trace.
1158
922
  """
1159
923
  self.generate_propagation_context(environ_or_headers)
1160
-
1161
- # When we generate the propagation context, the sample_rand value is set
1162
- # if missing or invalid (we use the original value if it's valid).
1163
- # We want the transaction to use the same sample_rand value. Due to duplicated
1164
- # propagation logic in the transaction, we pass it in to avoid recomputing it
1165
- # in the transaction.
1166
- # TYPE SAFETY: self.generate_propagation_context() ensures that self._propagation_context
1167
- # is not None.
1168
- sample_rand = typing.cast(
1169
- PropagationContext, self._propagation_context
1170
- )._sample_rand()
1171
-
1172
- transaction = Transaction.continue_from_headers(
1173
- normalize_incoming_data(environ_or_headers),
1174
- _sample_rand=sample_rand,
1175
- op=op,
1176
- origin=origin,
1177
- name=name,
1178
- source=source,
1179
- )
1180
-
1181
- return transaction
924
+ yield
1182
925
 
1183
926
  def capture_event(self, event, hint=None, scope=None, **scope_kwargs):
1184
927
  # type: (Event, Optional[Hint], Optional[Scope], Any) -> Optional[str]
@@ -1432,7 +1175,11 @@ class Scope:
1432
1175
 
1433
1176
  # Add "trace" context
1434
1177
  if contexts.get("trace") is None:
1435
- if has_tracing_enabled(options) and self._span is not None:
1178
+ if (
1179
+ has_tracing_enabled(options)
1180
+ and self._span is not None
1181
+ and self._span.is_valid
1182
+ ):
1436
1183
  contexts["trace"] = self._span.get_trace_context()
1437
1184
  else:
1438
1185
  contexts["trace"] = self.get_trace_context()
@@ -1482,8 +1229,8 @@ class Scope:
1482
1229
 
1483
1230
  if not is_check_in:
1484
1231
  # Get scopes without creating them to prevent infinite recursion
1485
- isolation_scope = _isolation_scope.get()
1486
- current_scope = _current_scope.get()
1232
+ isolation_scope = self._get_isolation_scope()
1233
+ current_scope = self._get_current_scope()
1487
1234
 
1488
1235
  event_processors = chain(
1489
1236
  global_event_processors,
@@ -1493,7 +1240,7 @@ class Scope:
1493
1240
  )
1494
1241
 
1495
1242
  for event_processor in event_processors:
1496
- new_event = event
1243
+ new_event = event # type: Optional[Event]
1497
1244
  with capture_internal_exceptions():
1498
1245
  new_event = event_processor(event, hint)
1499
1246
  if new_event is None: