sentry-sdk 3.0.0a1__py2.py3-none-any.whl → 3.0.0a3__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 (157) hide show
  1. sentry_sdk/__init__.py +2 -0
  2. sentry_sdk/_compat.py +5 -12
  3. sentry_sdk/_init_implementation.py +7 -7
  4. sentry_sdk/_log_batcher.py +17 -29
  5. sentry_sdk/_lru_cache.py +7 -9
  6. sentry_sdk/_queue.py +2 -4
  7. sentry_sdk/_types.py +11 -18
  8. sentry_sdk/_werkzeug.py +5 -7
  9. sentry_sdk/ai/monitoring.py +44 -31
  10. sentry_sdk/ai/utils.py +3 -4
  11. sentry_sdk/api.py +75 -87
  12. sentry_sdk/attachments.py +10 -12
  13. sentry_sdk/client.py +137 -155
  14. sentry_sdk/consts.py +430 -174
  15. sentry_sdk/crons/api.py +16 -17
  16. sentry_sdk/crons/decorator.py +25 -27
  17. sentry_sdk/debug.py +4 -6
  18. sentry_sdk/envelope.py +46 -112
  19. sentry_sdk/feature_flags.py +9 -15
  20. sentry_sdk/integrations/__init__.py +24 -19
  21. sentry_sdk/integrations/_asgi_common.py +15 -18
  22. sentry_sdk/integrations/_wsgi_common.py +22 -33
  23. sentry_sdk/integrations/aiohttp.py +32 -30
  24. sentry_sdk/integrations/anthropic.py +42 -37
  25. sentry_sdk/integrations/argv.py +3 -4
  26. sentry_sdk/integrations/ariadne.py +16 -18
  27. sentry_sdk/integrations/arq.py +21 -29
  28. sentry_sdk/integrations/asgi.py +63 -37
  29. sentry_sdk/integrations/asyncio.py +14 -16
  30. sentry_sdk/integrations/atexit.py +6 -10
  31. sentry_sdk/integrations/aws_lambda.py +26 -36
  32. sentry_sdk/integrations/beam.py +10 -18
  33. sentry_sdk/integrations/boto3.py +18 -16
  34. sentry_sdk/integrations/bottle.py +25 -34
  35. sentry_sdk/integrations/celery/__init__.py +41 -61
  36. sentry_sdk/integrations/celery/beat.py +23 -27
  37. sentry_sdk/integrations/celery/utils.py +15 -17
  38. sentry_sdk/integrations/chalice.py +8 -10
  39. sentry_sdk/integrations/clickhouse_driver.py +21 -31
  40. sentry_sdk/integrations/cloud_resource_context.py +9 -16
  41. sentry_sdk/integrations/cohere.py +27 -33
  42. sentry_sdk/integrations/dedupe.py +5 -8
  43. sentry_sdk/integrations/django/__init__.py +57 -72
  44. sentry_sdk/integrations/django/asgi.py +26 -34
  45. sentry_sdk/integrations/django/caching.py +23 -19
  46. sentry_sdk/integrations/django/middleware.py +17 -20
  47. sentry_sdk/integrations/django/signals_handlers.py +11 -10
  48. sentry_sdk/integrations/django/templates.py +19 -16
  49. sentry_sdk/integrations/django/transactions.py +16 -11
  50. sentry_sdk/integrations/django/views.py +6 -10
  51. sentry_sdk/integrations/dramatiq.py +21 -21
  52. sentry_sdk/integrations/excepthook.py +10 -10
  53. sentry_sdk/integrations/executing.py +3 -4
  54. sentry_sdk/integrations/falcon.py +27 -42
  55. sentry_sdk/integrations/fastapi.py +13 -16
  56. sentry_sdk/integrations/flask.py +31 -38
  57. sentry_sdk/integrations/gcp.py +13 -16
  58. sentry_sdk/integrations/gnu_backtrace.py +4 -6
  59. sentry_sdk/integrations/gql.py +16 -17
  60. sentry_sdk/integrations/graphene.py +13 -12
  61. sentry_sdk/integrations/grpc/__init__.py +19 -1
  62. sentry_sdk/integrations/grpc/aio/server.py +15 -14
  63. sentry_sdk/integrations/grpc/client.py +19 -9
  64. sentry_sdk/integrations/grpc/consts.py +2 -0
  65. sentry_sdk/integrations/grpc/server.py +12 -8
  66. sentry_sdk/integrations/httpx.py +9 -12
  67. sentry_sdk/integrations/huey.py +13 -20
  68. sentry_sdk/integrations/huggingface_hub.py +18 -18
  69. sentry_sdk/integrations/langchain.py +203 -113
  70. sentry_sdk/integrations/launchdarkly.py +13 -10
  71. sentry_sdk/integrations/litestar.py +37 -35
  72. sentry_sdk/integrations/logging.py +52 -65
  73. sentry_sdk/integrations/loguru.py +127 -57
  74. sentry_sdk/integrations/modules.py +3 -4
  75. sentry_sdk/integrations/openai.py +100 -88
  76. sentry_sdk/integrations/openai_agents/__init__.py +49 -0
  77. sentry_sdk/integrations/openai_agents/consts.py +1 -0
  78. sentry_sdk/integrations/openai_agents/patches/__init__.py +4 -0
  79. sentry_sdk/integrations/openai_agents/patches/agent_run.py +152 -0
  80. sentry_sdk/integrations/openai_agents/patches/models.py +52 -0
  81. sentry_sdk/integrations/openai_agents/patches/runner.py +42 -0
  82. sentry_sdk/integrations/openai_agents/patches/tools.py +84 -0
  83. sentry_sdk/integrations/openai_agents/spans/__init__.py +5 -0
  84. sentry_sdk/integrations/openai_agents/spans/agent_workflow.py +20 -0
  85. sentry_sdk/integrations/openai_agents/spans/ai_client.py +46 -0
  86. sentry_sdk/integrations/openai_agents/spans/execute_tool.py +47 -0
  87. sentry_sdk/integrations/openai_agents/spans/handoff.py +24 -0
  88. sentry_sdk/integrations/openai_agents/spans/invoke_agent.py +41 -0
  89. sentry_sdk/integrations/openai_agents/utils.py +201 -0
  90. sentry_sdk/integrations/openfeature.py +11 -6
  91. sentry_sdk/integrations/pure_eval.py +6 -10
  92. sentry_sdk/integrations/pymongo.py +13 -17
  93. sentry_sdk/integrations/pyramid.py +31 -36
  94. sentry_sdk/integrations/quart.py +23 -28
  95. sentry_sdk/integrations/ray.py +73 -64
  96. sentry_sdk/integrations/redis/__init__.py +7 -4
  97. sentry_sdk/integrations/redis/_async_common.py +25 -12
  98. sentry_sdk/integrations/redis/_sync_common.py +19 -13
  99. sentry_sdk/integrations/redis/modules/caches.py +17 -8
  100. sentry_sdk/integrations/redis/modules/queries.py +9 -8
  101. sentry_sdk/integrations/redis/rb.py +3 -2
  102. sentry_sdk/integrations/redis/redis.py +4 -4
  103. sentry_sdk/integrations/redis/redis_cluster.py +21 -13
  104. sentry_sdk/integrations/redis/redis_py_cluster_legacy.py +3 -2
  105. sentry_sdk/integrations/redis/utils.py +23 -24
  106. sentry_sdk/integrations/rq.py +13 -16
  107. sentry_sdk/integrations/rust_tracing.py +9 -6
  108. sentry_sdk/integrations/sanic.py +34 -46
  109. sentry_sdk/integrations/serverless.py +22 -27
  110. sentry_sdk/integrations/socket.py +27 -15
  111. sentry_sdk/integrations/spark/__init__.py +1 -0
  112. sentry_sdk/integrations/spark/spark_driver.py +45 -83
  113. sentry_sdk/integrations/spark/spark_worker.py +7 -11
  114. sentry_sdk/integrations/sqlalchemy.py +22 -19
  115. sentry_sdk/integrations/starlette.py +86 -90
  116. sentry_sdk/integrations/starlite.py +28 -34
  117. sentry_sdk/integrations/statsig.py +5 -4
  118. sentry_sdk/integrations/stdlib.py +28 -24
  119. sentry_sdk/integrations/strawberry.py +62 -49
  120. sentry_sdk/integrations/sys_exit.py +7 -11
  121. sentry_sdk/integrations/threading.py +12 -14
  122. sentry_sdk/integrations/tornado.py +28 -32
  123. sentry_sdk/integrations/trytond.py +4 -3
  124. sentry_sdk/integrations/typer.py +8 -6
  125. sentry_sdk/integrations/unleash.py +5 -4
  126. sentry_sdk/integrations/wsgi.py +47 -46
  127. sentry_sdk/logger.py +41 -10
  128. sentry_sdk/monitor.py +16 -28
  129. sentry_sdk/opentelemetry/consts.py +11 -4
  130. sentry_sdk/opentelemetry/contextvars_context.py +26 -16
  131. sentry_sdk/opentelemetry/propagator.py +38 -21
  132. sentry_sdk/opentelemetry/sampler.py +51 -34
  133. sentry_sdk/opentelemetry/scope.py +36 -37
  134. sentry_sdk/opentelemetry/span_processor.py +48 -58
  135. sentry_sdk/opentelemetry/tracing.py +58 -14
  136. sentry_sdk/opentelemetry/utils.py +186 -194
  137. sentry_sdk/profiler/continuous_profiler.py +108 -97
  138. sentry_sdk/profiler/transaction_profiler.py +70 -97
  139. sentry_sdk/profiler/utils.py +11 -15
  140. sentry_sdk/scope.py +251 -273
  141. sentry_sdk/scrubber.py +22 -26
  142. sentry_sdk/serializer.py +40 -54
  143. sentry_sdk/session.py +44 -61
  144. sentry_sdk/sessions.py +35 -49
  145. sentry_sdk/spotlight.py +15 -21
  146. sentry_sdk/tracing.py +121 -187
  147. sentry_sdk/tracing_utils.py +104 -122
  148. sentry_sdk/transport.py +131 -157
  149. sentry_sdk/utils.py +232 -309
  150. sentry_sdk/worker.py +16 -28
  151. {sentry_sdk-3.0.0a1.dist-info → sentry_sdk-3.0.0a3.dist-info}/METADATA +3 -3
  152. sentry_sdk-3.0.0a3.dist-info/RECORD +168 -0
  153. {sentry_sdk-3.0.0a1.dist-info → sentry_sdk-3.0.0a3.dist-info}/WHEEL +1 -1
  154. sentry_sdk-3.0.0a1.dist-info/RECORD +0 -154
  155. {sentry_sdk-3.0.0a1.dist-info → sentry_sdk-3.0.0a3.dist-info}/entry_points.txt +0 -0
  156. {sentry_sdk-3.0.0a1.dist-info → sentry_sdk-3.0.0a3.dist-info}/licenses/LICENSE +0 -0
  157. {sentry_sdk-3.0.0a1.dist-info → sentry_sdk-3.0.0a3.dist-info}/top_level.txt +0 -0
sentry_sdk/client.py CHANGED
@@ -1,3 +1,4 @@
1
+ from __future__ import annotations
1
2
  import os
2
3
  import uuid
3
4
  import random
@@ -5,8 +6,9 @@ import socket
5
6
  from collections.abc import Mapping
6
7
  from datetime import datetime, timezone
7
8
  from importlib import import_module
8
- from typing import TYPE_CHECKING, List, Dict, cast, overload
9
+ from typing import TYPE_CHECKING, overload
9
10
 
11
+ import sentry_sdk
10
12
  from sentry_sdk._compat import check_uwsgi_thread_support
11
13
  from sentry_sdk.utils import (
12
14
  AnnotatedValue,
@@ -47,13 +49,16 @@ from sentry_sdk.monitor import Monitor
47
49
  from sentry_sdk.spotlight import setup_spotlight
48
50
 
49
51
  if TYPE_CHECKING:
50
- from typing import Any
51
- from typing import Callable
52
- from typing import Optional
53
- from typing import Sequence
54
- from typing import Type
55
- from typing import Union
56
- from typing import TypeVar
52
+ from typing import (
53
+ Any,
54
+ Callable,
55
+ Optional,
56
+ Sequence,
57
+ Type,
58
+ Union,
59
+ TypeVar,
60
+ Dict,
61
+ )
57
62
 
58
63
  from sentry_sdk._types import Event, Hint, SDKInfo, Log
59
64
  from sentry_sdk.integrations import Integration
@@ -63,22 +68,22 @@ if TYPE_CHECKING:
63
68
  from sentry_sdk.transport import Transport
64
69
  from sentry_sdk._log_batcher import LogBatcher
65
70
 
66
- I = TypeVar("I", bound=Integration) # noqa: E741
71
+ IntegrationType = TypeVar("IntegrationType", bound=Integration) # noqa: E741
72
+
67
73
 
68
74
  _client_init_debug = ContextVar("client_init_debug")
69
75
 
70
76
 
71
- SDK_INFO = {
77
+ SDK_INFO: SDKInfo = {
72
78
  "name": "sentry.python", # SDK name will be overridden after integrations have been loaded with sentry_sdk.integrations.setup_integrations()
73
79
  "version": VERSION,
74
80
  "packages": [{"name": "pypi:sentry-sdk", "version": VERSION}],
75
- } # type: SDKInfo
81
+ }
76
82
 
77
83
 
78
- def _get_options(*args, **kwargs):
79
- # type: (*Optional[str], **Any) -> Dict[str, Any]
84
+ def _get_options(*args: Optional[str], **kwargs: Any) -> Dict[str, Any]:
80
85
  if args and (isinstance(args[0], (bytes, str)) or args[0] is None):
81
- dsn = args[0] # type: Optional[str]
86
+ dsn: Optional[str] = args[0]
82
87
  args = args[1:]
83
88
  else:
84
89
  dsn = None
@@ -107,7 +112,7 @@ def _get_options(*args, **kwargs):
107
112
  rv["environment"] = os.environ.get("SENTRY_ENVIRONMENT") or "production"
108
113
 
109
114
  if rv["debug"] is None:
110
- rv["debug"] = env_to_bool(os.environ.get("SENTRY_DEBUG", "False"), strict=True)
115
+ rv["debug"] = env_to_bool(os.environ.get("SENTRY_DEBUG"), strict=True) or False
111
116
 
112
117
  if rv["server_name"] is None and hasattr(socket, "gethostname"):
113
118
  rv["server_name"] = socket.gethostname()
@@ -133,6 +138,11 @@ def _get_options(*args, **kwargs):
133
138
  )
134
139
  rv["socket_options"] = None
135
140
 
141
+ if rv["keep_alive"] is None:
142
+ rv["keep_alive"] = (
143
+ env_to_bool(os.environ.get("SENTRY_KEEP_ALIVE"), strict=True) or False
144
+ )
145
+
136
146
  return rv
137
147
 
138
148
 
@@ -143,37 +153,31 @@ class BaseClient:
143
153
  The basic definition of a client that is used for sending data to Sentry.
144
154
  """
145
155
 
146
- spotlight = None # type: Optional[SpotlightClient]
156
+ spotlight: Optional[SpotlightClient] = None
147
157
 
148
- def __init__(self, options=None):
149
- # type: (Optional[Dict[str, Any]]) -> None
150
- self.options = (
158
+ def __init__(self, options: Optional[Dict[str, Any]] = None) -> None:
159
+ self.options: Dict[str, Any] = (
151
160
  options if options is not None else DEFAULT_OPTIONS
152
- ) # type: Dict[str, Any]
161
+ )
153
162
 
154
- self.transport = None # type: Optional[Transport]
155
- self.monitor = None # type: Optional[Monitor]
156
- self.log_batcher = None # type: Optional[LogBatcher]
163
+ self.transport: Optional[Transport] = None
164
+ self.monitor: Optional[Monitor] = None
165
+ self.log_batcher: Optional[LogBatcher] = None
157
166
 
158
- def __getstate__(self, *args, **kwargs):
159
- # type: (*Any, **Any) -> Any
167
+ def __getstate__(self, *args: Any, **kwargs: Any) -> Any:
160
168
  return {"options": {}}
161
169
 
162
- def __setstate__(self, *args, **kwargs):
163
- # type: (*Any, **Any) -> None
170
+ def __setstate__(self, *args: Any, **kwargs: Any) -> None:
164
171
  pass
165
172
 
166
173
  @property
167
- def dsn(self):
168
- # type: () -> Optional[str]
174
+ def dsn(self) -> Optional[str]:
169
175
  return None
170
176
 
171
- def should_send_default_pii(self):
172
- # type: () -> bool
177
+ def should_send_default_pii(self) -> bool:
173
178
  return False
174
179
 
175
- def is_active(self):
176
- # type: () -> bool
180
+ def is_active(self) -> bool:
177
181
  """
178
182
  .. versionadded:: 2.0.0
179
183
 
@@ -181,48 +185,40 @@ class BaseClient:
181
185
  """
182
186
  return False
183
187
 
184
- def capture_event(self, *args, **kwargs):
185
- # type: (*Any, **Any) -> Optional[str]
188
+ def capture_event(self, *args: Any, **kwargs: Any) -> Optional[str]:
186
189
  return None
187
190
 
188
- def _capture_experimental_log(self, scope, log):
189
- # type: (Scope, Log) -> None
191
+ def _capture_experimental_log(self, log: "Log") -> None:
190
192
  pass
191
193
 
192
- def capture_session(self, *args, **kwargs):
193
- # type: (*Any, **Any) -> None
194
+ def capture_session(self, *args: Any, **kwargs: Any) -> None:
194
195
  return None
195
196
 
196
197
  if TYPE_CHECKING:
197
198
 
198
199
  @overload
199
- def get_integration(self, name_or_class):
200
- # type: (str) -> Optional[Integration]
201
- ...
200
+ def get_integration(self, name_or_class: str) -> Optional[Integration]: ...
202
201
 
203
202
  @overload
204
- def get_integration(self, name_or_class):
205
- # type: (type[I]) -> Optional[I]
206
- ...
203
+ def get_integration(
204
+ self, name_or_class: type[IntegrationType]
205
+ ) -> Optional[IntegrationType]: ...
207
206
 
208
- def get_integration(self, name_or_class):
209
- # type: (Union[str, type[Integration]]) -> Optional[Integration]
207
+ def get_integration(
208
+ self, name_or_class: Union[str, type[Integration]]
209
+ ) -> Optional[Integration]:
210
210
  return None
211
211
 
212
- def close(self, *args, **kwargs):
213
- # type: (*Any, **Any) -> None
212
+ def close(self, *args: Any, **kwargs: Any) -> None:
214
213
  return None
215
214
 
216
- def flush(self, *args, **kwargs):
217
- # type: (*Any, **Any) -> None
215
+ def flush(self, *args: Any, **kwargs: Any) -> None:
218
216
  return None
219
217
 
220
- def __enter__(self):
221
- # type: () -> BaseClient
218
+ def __enter__(self) -> BaseClient:
222
219
  return self
223
220
 
224
- def __exit__(self, exc_type, exc_value, tb):
225
- # type: (Any, Any, Any) -> None
221
+ def __exit__(self, exc_type: Any, exc_value: Any, tb: Any) -> None:
226
222
  return None
227
223
 
228
224
 
@@ -246,22 +242,20 @@ class _Client(BaseClient):
246
242
  Alias of :py:class:`sentry_sdk.Client`. (Was created for better intelisense support)
247
243
  """
248
244
 
249
- def __init__(self, *args, **kwargs):
250
- # type: (*Any, **Any) -> None
251
- super(_Client, self).__init__(options=get_options(*args, **kwargs))
245
+ def __init__(self, *args: Any, **kwargs: Any) -> None:
246
+ super(_Client, self).__init__(options=_get_options(*args, **kwargs))
252
247
  self._init_impl()
253
248
 
254
- def __getstate__(self):
255
- # type: () -> Any
249
+ def __getstate__(self) -> Any:
256
250
  return {"options": self.options}
257
251
 
258
- def __setstate__(self, state):
259
- # type: (Any) -> None
252
+ def __setstate__(self, state: Any) -> None:
260
253
  self.options = state["options"]
261
254
  self._init_impl()
262
255
 
263
- def _setup_instrumentation(self, functions_to_trace):
264
- # type: (Sequence[Dict[str, str]]) -> None
256
+ def _setup_instrumentation(
257
+ self, functions_to_trace: Sequence[Dict[str, str]]
258
+ ) -> None:
265
259
  """
266
260
  Instruments the functions given in the list `functions_to_trace` with the `@sentry_sdk.tracing.trace` decorator.
267
261
  """
@@ -311,12 +305,10 @@ class _Client(BaseClient):
311
305
  e,
312
306
  )
313
307
 
314
- def _init_impl(self):
315
- # type: () -> None
308
+ def _init_impl(self) -> None:
316
309
  old_debug = _client_init_debug.get(False)
317
310
 
318
- def _capture_envelope(envelope):
319
- # type: (Envelope) -> None
311
+ def _capture_envelope(envelope: Envelope) -> None:
320
312
  if self.transport is not None:
321
313
  self.transport.capture_envelope(envelope)
322
314
 
@@ -400,6 +392,8 @@ class _Client(BaseClient):
400
392
 
401
393
  patch_readable_span()
402
394
  setup_sentry_tracing()
395
+
396
+ logger.debug("[Tracing] Finished setting up OpenTelemetry")
403
397
  finally:
404
398
  _client_init_debug.set(old_debug)
405
399
 
@@ -415,8 +409,7 @@ class _Client(BaseClient):
415
409
  # need to check if it's safe to use them.
416
410
  check_uwsgi_thread_support()
417
411
 
418
- def is_active(self):
419
- # type: () -> bool
412
+ def is_active(self) -> bool:
420
413
  """
421
414
  .. versionadded:: 2.0.0
422
415
 
@@ -424,8 +417,7 @@ class _Client(BaseClient):
424
417
  """
425
418
  return True
426
419
 
427
- def should_send_default_pii(self):
428
- # type: () -> bool
420
+ def should_send_default_pii(self) -> bool:
429
421
  """
430
422
  .. versionadded:: 2.0.0
431
423
 
@@ -434,28 +426,26 @@ class _Client(BaseClient):
434
426
  return self.options.get("send_default_pii") or False
435
427
 
436
428
  @property
437
- def dsn(self):
438
- # type: () -> Optional[str]
429
+ def dsn(self) -> Optional[str]:
439
430
  """Returns the configured DSN as string."""
440
431
  return self.options["dsn"]
441
432
 
442
433
  def _prepare_event(
443
434
  self,
444
- event, # type: Event
445
- hint, # type: Hint
446
- scope, # type: Optional[Scope]
447
- ):
448
- # type: (...) -> Optional[Event]
435
+ event: Event,
436
+ hint: Hint,
437
+ scope: Optional[Scope],
438
+ ) -> Optional[Event]:
449
439
 
450
- previous_total_spans = None # type: Optional[int]
451
- previous_total_breadcrumbs = None # type: Optional[int]
440
+ previous_total_spans: Optional[int] = None
441
+ previous_total_breadcrumbs: Optional[int] = None
452
442
 
453
443
  if event.get("timestamp") is None:
454
444
  event["timestamp"] = datetime.now(timezone.utc)
455
445
 
456
446
  if scope is not None:
457
447
  is_transaction = event.get("type") == "transaction"
458
- spans_before = len(cast(List[Dict[str, object]], event.get("spans", [])))
448
+ spans_before = len(event.get("spans", []))
459
449
  event_ = scope.apply_to_event(event, hint, self.options)
460
450
 
461
451
  # one of the event/error processors returned None
@@ -473,16 +463,14 @@ class _Client(BaseClient):
473
463
  )
474
464
  return None
475
465
 
476
- event = event_ # type: Optional[Event] # type: ignore[no-redef]
477
- spans_delta = spans_before - len(
478
- cast(List[Dict[str, object]], event.get("spans", []))
479
- )
466
+ event = event_
467
+ spans_delta = spans_before - len(event.get("spans", []))
480
468
  if is_transaction and spans_delta > 0 and self.transport is not None:
481
469
  self.transport.record_lost_event(
482
470
  "event_processor", data_category="span", quantity=spans_delta
483
471
  )
484
472
 
485
- dropped_spans = event.pop("_dropped_spans", 0) + spans_delta # type: int
473
+ dropped_spans: int = event.pop("_dropped_spans", 0) + spans_delta
486
474
  if dropped_spans > 0:
487
475
  previous_total_spans = spans_before + dropped_spans
488
476
  if scope._n_breadcrumbs_truncated > 0:
@@ -554,14 +542,11 @@ class _Client(BaseClient):
554
542
  # Postprocess the event here so that annotated types do
555
543
  # generally not surface in before_send
556
544
  if event is not None:
557
- event = cast(
558
- "Event",
559
- serialize(
560
- cast("Dict[str, Any]", event),
561
- max_request_body_size=self.options.get("max_request_body_size"),
562
- max_value_length=self.options.get("max_value_length"),
563
- custom_repr=self.options.get("custom_repr"),
564
- ),
545
+ event: Event = serialize( # type: ignore[no-redef]
546
+ event,
547
+ max_request_body_size=self.options.get("max_request_body_size"),
548
+ max_value_length=self.options.get("max_value_length"),
549
+ custom_repr=self.options.get("custom_repr"),
565
550
  )
566
551
 
567
552
  before_send = self.options["before_send"]
@@ -570,7 +555,7 @@ class _Client(BaseClient):
570
555
  and event is not None
571
556
  and event.get("type") != "transaction"
572
557
  ):
573
- new_event = None # type: Optional[Event]
558
+ new_event: Optional["Event"] = None
574
559
  with capture_internal_exceptions():
575
560
  new_event = before_send(event, hint or {})
576
561
  if new_event is None:
@@ -587,7 +572,7 @@ class _Client(BaseClient):
587
572
  if event.get("exception"):
588
573
  DedupeIntegration.reset_last_seen()
589
574
 
590
- event = new_event # type: Optional[Event] # type: ignore[no-redef]
575
+ event = new_event
591
576
 
592
577
  before_send_transaction = self.options["before_send_transaction"]
593
578
  if (
@@ -596,7 +581,7 @@ class _Client(BaseClient):
596
581
  and event.get("type") == "transaction"
597
582
  ):
598
583
  new_event = None
599
- spans_before = len(cast(List[Dict[str, object]], event.get("spans", [])))
584
+ spans_before = len(event.get("spans", []))
600
585
  with capture_internal_exceptions():
601
586
  new_event = before_send_transaction(event, hint or {})
602
587
  if new_event is None:
@@ -611,20 +596,17 @@ class _Client(BaseClient):
611
596
  quantity=spans_before + 1, # +1 for the transaction itself
612
597
  )
613
598
  else:
614
- spans_delta = spans_before - len(
615
- cast(List[Dict[str, object]], new_event.get("spans", []))
616
- )
599
+ spans_delta = spans_before - len(new_event.get("spans", []))
617
600
  if spans_delta > 0 and self.transport is not None:
618
601
  self.transport.record_lost_event(
619
602
  reason="before_send", data_category="span", quantity=spans_delta
620
603
  )
621
604
 
622
- event = new_event # type: Optional[Event] # type: ignore[no-redef]
605
+ event = new_event
623
606
 
624
607
  return event
625
608
 
626
- def _is_ignored_error(self, event, hint):
627
- # type: (Event, Hint) -> bool
609
+ def _is_ignored_error(self, event: Event, hint: Hint) -> bool:
628
610
  exc_info = hint.get("exc_info")
629
611
  if exc_info is None:
630
612
  return False
@@ -647,11 +629,10 @@ class _Client(BaseClient):
647
629
 
648
630
  def _should_capture(
649
631
  self,
650
- event, # type: Event
651
- hint, # type: Hint
652
- scope=None, # type: Optional[Scope]
653
- ):
654
- # type: (...) -> bool
632
+ event: "Event",
633
+ hint: "Hint",
634
+ scope: Optional["Scope"] = None,
635
+ ) -> bool:
655
636
  # Transactions are sampled independent of error events.
656
637
  is_transaction = event.get("type") == "transaction"
657
638
  if is_transaction:
@@ -669,10 +650,9 @@ class _Client(BaseClient):
669
650
 
670
651
  def _should_sample_error(
671
652
  self,
672
- event, # type: Event
673
- hint, # type: Hint
674
- ):
675
- # type: (...) -> bool
653
+ event: Event,
654
+ hint: Hint,
655
+ ) -> bool:
676
656
  error_sampler = self.options.get("error_sampler", None)
677
657
 
678
658
  if callable(error_sampler):
@@ -717,10 +697,9 @@ class _Client(BaseClient):
717
697
 
718
698
  def _update_session_from_event(
719
699
  self,
720
- session, # type: Session
721
- event, # type: Event
722
- ):
723
- # type: (...) -> None
700
+ session: Session,
701
+ event: Event,
702
+ ) -> None:
724
703
 
725
704
  crashed = False
726
705
  errored = False
@@ -756,11 +735,10 @@ class _Client(BaseClient):
756
735
 
757
736
  def capture_event(
758
737
  self,
759
- event, # type: Event
760
- hint=None, # type: Optional[Hint]
761
- scope=None, # type: Optional[Scope]
762
- ):
763
- # type: (...) -> Optional[str]
738
+ event: Event,
739
+ hint: Optional[Hint] = None,
740
+ scope: Optional[Scope] = None,
741
+ ) -> Optional[str]:
764
742
  """Captures an event.
765
743
 
766
744
  :param event: A ready-made event that can be directly sent to Sentry.
@@ -771,7 +749,7 @@ class _Client(BaseClient):
771
749
 
772
750
  :returns: An event ID. May be `None` if there is no DSN set or of if the SDK decided to discard the event for other reasons. In such situations setting `debug=True` on `init()` may help.
773
751
  """
774
- hint = dict(hint or ()) # type: Hint
752
+ hint: Hint = dict(hint or ())
775
753
 
776
754
  if not self._should_capture(event, hint, scope):
777
755
  return None
@@ -806,10 +784,10 @@ class _Client(BaseClient):
806
784
  trace_context = event_opt.get("contexts", {}).get("trace") or {}
807
785
  dynamic_sampling_context = trace_context.pop("dynamic_sampling_context", {})
808
786
 
809
- headers = {
787
+ headers: dict[str, object] = {
810
788
  "event_id": event_opt["event_id"],
811
789
  "sent_at": format_timestamp(datetime.now(timezone.utc)),
812
- } # type: dict[str, object]
790
+ }
813
791
 
814
792
  if dynamic_sampling_context:
815
793
  headers["trace"] = dynamic_sampling_context
@@ -839,12 +817,13 @@ class _Client(BaseClient):
839
817
 
840
818
  return return_value
841
819
 
842
- def _capture_experimental_log(self, current_scope, log):
843
- # type: (Scope, Log) -> None
820
+ def _capture_experimental_log(self, log: Log) -> None:
844
821
  logs_enabled = self.options["_experiments"].get("enable_logs", False)
845
822
  if not logs_enabled:
846
823
  return
847
- isolation_scope = current_scope.get_isolation_scope()
824
+
825
+ current_scope = sentry_sdk.get_current_scope()
826
+ isolation_scope = sentry_sdk.get_isolation_scope()
848
827
 
849
828
  log["attributes"]["sentry.sdk.name"] = SDK_INFO["name"]
850
829
  log["attributes"]["sentry.sdk.version"] = SDK_INFO["version"]
@@ -873,6 +852,21 @@ class _Client(BaseClient):
873
852
  elif propagation_context is not None:
874
853
  log["trace_id"] = propagation_context.trace_id
875
854
 
855
+ # The user, if present, is always set on the isolation scope.
856
+ if isolation_scope._user is not None:
857
+ for log_attribute, user_attribute in (
858
+ ("user.id", "id"),
859
+ ("user.name", "username"),
860
+ ("user.email", "email"),
861
+ ):
862
+ if (
863
+ user_attribute in isolation_scope._user
864
+ and log_attribute not in log["attributes"]
865
+ ):
866
+ log["attributes"][log_attribute] = isolation_scope._user[
867
+ user_attribute
868
+ ]
869
+
876
870
  # If debug is enabled, log the log to the console
877
871
  debug = self.options.get("debug", False)
878
872
  if debug:
@@ -889,10 +883,7 @@ class _Client(BaseClient):
889
883
  if self.log_batcher:
890
884
  self.log_batcher.add(log)
891
885
 
892
- def capture_session(
893
- self, session # type: Session
894
- ):
895
- # type: (...) -> None
886
+ def capture_session(self, session: Session) -> None:
896
887
  if not session.release:
897
888
  logger.info("Discarded session update because of missing release")
898
889
  else:
@@ -901,19 +892,16 @@ class _Client(BaseClient):
901
892
  if TYPE_CHECKING:
902
893
 
903
894
  @overload
904
- def get_integration(self, name_or_class):
905
- # type: (str) -> Optional[Integration]
906
- ...
895
+ def get_integration(self, name_or_class: str) -> Optional[Integration]: ...
907
896
 
908
897
  @overload
909
- def get_integration(self, name_or_class):
910
- # type: (type[I]) -> Optional[I]
911
- ...
898
+ def get_integration(
899
+ self, name_or_class: type[IntegrationType]
900
+ ) -> Optional[IntegrationType]: ...
912
901
 
913
902
  def get_integration(
914
- self, name_or_class # type: Union[str, Type[Integration]]
915
- ):
916
- # type: (...) -> Optional[Integration]
903
+ self, name_or_class: Union[str, Type[Integration]]
904
+ ) -> Optional[Integration]:
917
905
  """Returns the integration for this client by name or class.
918
906
  If the client does not have that integration then `None` is returned.
919
907
  """
@@ -928,10 +916,9 @@ class _Client(BaseClient):
928
916
 
929
917
  def close(
930
918
  self,
931
- timeout=None, # type: Optional[float]
932
- callback=None, # type: Optional[Callable[[int, float], None]]
933
- ):
934
- # type: (...) -> None
919
+ timeout: Optional[float] = None,
920
+ callback: Optional[Callable[[int, float], None]] = None,
921
+ ) -> None:
935
922
  """
936
923
  Close the client and shut down the transport. Arguments have the same
937
924
  semantics as :py:meth:`Client.flush`.
@@ -952,10 +939,9 @@ class _Client(BaseClient):
952
939
 
953
940
  def flush(
954
941
  self,
955
- timeout=None, # type: Optional[float]
956
- callback=None, # type: Optional[Callable[[int, float], None]]
957
- ):
958
- # type: (...) -> None
942
+ timeout: Optional[float] = None,
943
+ callback: Optional[Callable[[int, float], None]] = None,
944
+ ) -> None:
959
945
  """
960
946
  Wait for the current events to be sent.
961
947
 
@@ -973,17 +959,13 @@ class _Client(BaseClient):
973
959
 
974
960
  self.transport.flush(timeout=timeout, callback=callback)
975
961
 
976
- def __enter__(self):
977
- # type: () -> _Client
962
+ def __enter__(self) -> _Client:
978
963
  return self
979
964
 
980
- def __exit__(self, exc_type, exc_value, tb):
981
- # type: (Any, Any, Any) -> None
965
+ def __exit__(self, exc_type: Any, exc_value: Any, tb: Any) -> None:
982
966
  self.close()
983
967
 
984
968
 
985
- from typing import TYPE_CHECKING
986
-
987
969
  if TYPE_CHECKING:
988
970
  # Make mypy, PyCharm and other static analyzers think `get_options` is a
989
971
  # type to have nicer autocompletion for params.