sentry-sdk 3.0.0a2__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 +9 -16
  8. sentry_sdk/_werkzeug.py +5 -7
  9. sentry_sdk/ai/monitoring.py +40 -28
  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 +110 -153
  14. sentry_sdk/consts.py +398 -220
  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 +16 -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 +19 -28
  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 +36 -56
  36. sentry_sdk/integrations/celery/beat.py +22 -26
  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 +17 -23
  42. sentry_sdk/integrations/dedupe.py +5 -8
  43. sentry_sdk/integrations/django/__init__.py +57 -72
  44. sentry_sdk/integrations/django/asgi.py +24 -32
  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 +3 -2
  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 +16 -16
  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 +28 -35
  73. sentry_sdk/integrations/loguru.py +15 -19
  74. sentry_sdk/integrations/modules.py +3 -4
  75. sentry_sdk/integrations/openai.py +96 -84
  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 +15 -9
  98. sentry_sdk/integrations/redis/_sync_common.py +13 -12
  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 +10 -8
  104. sentry_sdk/integrations/redis/redis_py_cluster_legacy.py +3 -2
  105. sentry_sdk/integrations/redis/utils.py +21 -22
  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 +13 -9
  128. sentry_sdk/monitor.py +16 -28
  129. sentry_sdk/opentelemetry/consts.py +11 -4
  130. sentry_sdk/opentelemetry/contextvars_context.py +17 -15
  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 +43 -59
  135. sentry_sdk/opentelemetry/tracing.py +32 -12
  136. sentry_sdk/opentelemetry/utils.py +180 -196
  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 -264
  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 +116 -182
  147. sentry_sdk/tracing_utils.py +100 -120
  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.0a2.dist-info → sentry_sdk-3.0.0a3.dist-info}/METADATA +1 -1
  152. sentry_sdk-3.0.0a3.dist-info/RECORD +168 -0
  153. sentry_sdk-3.0.0a2.dist-info/RECORD +0 -154
  154. {sentry_sdk-3.0.0a2.dist-info → sentry_sdk-3.0.0a3.dist-info}/WHEEL +0 -0
  155. {sentry_sdk-3.0.0a2.dist-info → sentry_sdk-3.0.0a3.dist-info}/entry_points.txt +0 -0
  156. {sentry_sdk-3.0.0a2.dist-info → sentry_sdk-3.0.0a3.dist-info}/licenses/LICENSE +0 -0
  157. {sentry_sdk-3.0.0a2.dist-info → sentry_sdk-3.0.0a3.dist-info}/top_level.txt +0 -0
sentry_sdk/utils.py CHANGED
@@ -1,3 +1,4 @@
1
+ from __future__ import annotations
1
2
  import base64
2
3
  import json
3
4
  import linecache
@@ -34,21 +35,19 @@ from sentry_sdk.consts import (
34
35
  )
35
36
  from sentry_sdk._types import Annotated, AnnotatedValue, SENSITIVE_DATA_SUBSTITUTE
36
37
 
37
- from typing import TYPE_CHECKING
38
+ from typing import TYPE_CHECKING, overload
38
39
 
39
40
  if TYPE_CHECKING:
40
41
  from types import FrameType, TracebackType
41
42
  from typing import (
42
43
  Any,
43
44
  Callable,
44
- cast,
45
45
  ContextManager,
46
46
  Dict,
47
47
  Iterator,
48
48
  List,
49
49
  NoReturn,
50
50
  Optional,
51
- overload,
52
51
  ParamSpec,
53
52
  Set,
54
53
  Tuple,
@@ -95,8 +94,7 @@ of the exception tree.
95
94
  """
96
95
 
97
96
 
98
- def env_to_bool(value, *, strict=False):
99
- # type: (Any, Optional[bool]) -> bool | None
97
+ def env_to_bool(value: Any, *, strict: Optional[bool] = False) -> Optional[bool]:
100
98
  """Casts an ENV variable value to boolean using the constants defined above.
101
99
  In strict mode, it may return None if the value doesn't match any of the predefined values.
102
100
  """
@@ -111,14 +109,12 @@ def env_to_bool(value, *, strict=False):
111
109
  return None if strict else bool(value)
112
110
 
113
111
 
114
- def json_dumps(data):
115
- # type: (Any) -> bytes
112
+ def json_dumps(data: Any) -> bytes:
116
113
  """Serialize data into a compact JSON representation encoded as UTF-8."""
117
114
  return json.dumps(data, allow_nan=False, separators=(",", ":")).encode("utf-8")
118
115
 
119
116
 
120
- def get_git_revision():
121
- # type: () -> Optional[str]
117
+ def get_git_revision() -> Optional[str]:
122
118
  try:
123
119
  with open(os.path.devnull, "w+") as null:
124
120
  # prevent command prompt windows from popping up on windows
@@ -145,8 +141,7 @@ def get_git_revision():
145
141
  return revision
146
142
 
147
143
 
148
- def get_default_release():
149
- # type: () -> Optional[str]
144
+ def get_default_release() -> Optional[str]:
150
145
  """Try to guess a default release."""
151
146
  release = os.environ.get("SENTRY_RELEASE")
152
147
  if release:
@@ -169,8 +164,7 @@ def get_default_release():
169
164
  return None
170
165
 
171
166
 
172
- def get_sdk_name(installed_integrations):
173
- # type: (List[str]) -> str
167
+ def get_sdk_name(installed_integrations: List[str]) -> str:
174
168
  """Return the SDK name including the name of the used web framework."""
175
169
 
176
170
  # Note: I can not use for example sentry_sdk.integrations.django.DjangoIntegration.identifier
@@ -208,12 +202,15 @@ def get_sdk_name(installed_integrations):
208
202
  class CaptureInternalException:
209
203
  __slots__ = ()
210
204
 
211
- def __enter__(self):
212
- # type: () -> ContextManager[Any]
205
+ def __enter__(self) -> ContextManager[Any]:
213
206
  return self
214
207
 
215
- def __exit__(self, ty, value, tb):
216
- # type: (Optional[Type[BaseException]], Optional[BaseException], Optional[TracebackType]) -> bool
208
+ def __exit__(
209
+ self,
210
+ ty: Optional[Type[BaseException]],
211
+ value: Optional[BaseException],
212
+ tb: Optional[TracebackType],
213
+ ) -> bool:
217
214
  if ty is not None and value is not None:
218
215
  capture_internal_exception((ty, value, tb))
219
216
 
@@ -223,13 +220,11 @@ class CaptureInternalException:
223
220
  _CAPTURE_INTERNAL_EXCEPTION = CaptureInternalException()
224
221
 
225
222
 
226
- def capture_internal_exceptions():
227
- # type: () -> ContextManager[Any]
223
+ def capture_internal_exceptions() -> ContextManager[Any]:
228
224
  return _CAPTURE_INTERNAL_EXCEPTION
229
225
 
230
226
 
231
- def capture_internal_exception(exc_info):
232
- # type: (ExcInfo) -> None
227
+ def capture_internal_exception(exc_info: ExcInfo) -> None:
233
228
  """
234
229
  Capture an exception that is likely caused by a bug in the SDK
235
230
  itself.
@@ -240,13 +235,11 @@ def capture_internal_exception(exc_info):
240
235
  logger.error("Internal error in sentry_sdk", exc_info=exc_info)
241
236
 
242
237
 
243
- def to_timestamp(value):
244
- # type: (datetime) -> float
238
+ def to_timestamp(value: datetime) -> float:
245
239
  return (value - epoch).total_seconds()
246
240
 
247
241
 
248
- def format_timestamp(value):
249
- # type: (datetime) -> str
242
+ def format_timestamp(value: datetime) -> str:
250
243
  """Formats a timestamp in RFC 3339 format.
251
244
 
252
245
  Any datetime objects with a non-UTC timezone are converted to UTC, so that all timestamps are formatted in UTC.
@@ -258,8 +251,9 @@ def format_timestamp(value):
258
251
  return utctime.strftime("%Y-%m-%dT%H:%M:%S.%fZ")
259
252
 
260
253
 
261
- def event_hint_with_exc_info(exc_info=None):
262
- # type: (Optional[ExcInfo]) -> Dict[str, Optional[ExcInfo]]
254
+ def event_hint_with_exc_info(
255
+ exc_info: Optional[ExcInfo] = None,
256
+ ) -> Dict[str, Optional[ExcInfo]]:
263
257
  """Creates a hint with the exc info filled in."""
264
258
  if exc_info is None:
265
259
  exc_info = sys.exc_info()
@@ -277,8 +271,7 @@ class BadDsn(ValueError):
277
271
  class Dsn:
278
272
  """Represents a DSN."""
279
273
 
280
- def __init__(self, value):
281
- # type: (Union[Dsn, str]) -> None
274
+ def __init__(self, value: Union[Dsn, str]) -> None:
282
275
  if isinstance(value, Dsn):
283
276
  self.__dict__ = dict(value.__dict__)
284
277
  return
@@ -294,7 +287,7 @@ class Dsn:
294
287
  self.host = parts.hostname
295
288
 
296
289
  if parts.port is None:
297
- self.port = self.scheme == "https" and 443 or 80 # type: int
290
+ self.port: int = self.scheme == "https" and 443 or 80
298
291
  else:
299
292
  self.port = parts.port
300
293
 
@@ -314,16 +307,14 @@ class Dsn:
314
307
  self.path = "/".join(path) + "/"
315
308
 
316
309
  @property
317
- def netloc(self):
318
- # type: () -> str
310
+ def netloc(self) -> str:
319
311
  """The netloc part of a DSN."""
320
312
  rv = self.host
321
313
  if (self.scheme, self.port) not in (("http", 80), ("https", 443)):
322
314
  rv = "%s:%s" % (rv, self.port)
323
315
  return rv
324
316
 
325
- def to_auth(self, client=None):
326
- # type: (Optional[Any]) -> Auth
317
+ def to_auth(self, client: Optional[Any] = None) -> Auth:
327
318
  """Returns the auth info object for this dsn."""
328
319
  return Auth(
329
320
  scheme=self.scheme,
@@ -335,8 +326,7 @@ class Dsn:
335
326
  client=client,
336
327
  )
337
328
 
338
- def __str__(self):
339
- # type: () -> str
329
+ def __str__(self) -> str:
340
330
  return "%s://%s%s@%s%s%s" % (
341
331
  self.scheme,
342
332
  self.public_key,
@@ -352,16 +342,15 @@ class Auth:
352
342
 
353
343
  def __init__(
354
344
  self,
355
- scheme,
356
- host,
357
- project_id,
358
- public_key,
359
- secret_key=None,
360
- version=7,
361
- client=None,
362
- path="/",
363
- ):
364
- # type: (str, str, str, str, Optional[str], int, Optional[Any], str) -> None
345
+ scheme: str,
346
+ host: str,
347
+ project_id: str,
348
+ public_key: str,
349
+ secret_key: Optional[str] = None,
350
+ version: int = 7,
351
+ client: Optional[Any] = None,
352
+ path: str = "/",
353
+ ) -> None:
365
354
  self.scheme = scheme
366
355
  self.host = host
367
356
  self.path = path
@@ -371,10 +360,7 @@ class Auth:
371
360
  self.version = version
372
361
  self.client = client
373
362
 
374
- def get_api_url(
375
- self, type=EndpointType.ENVELOPE # type: EndpointType
376
- ):
377
- # type: (...) -> str
363
+ def get_api_url(self, type: EndpointType = EndpointType.ENVELOPE) -> str:
378
364
  """Returns the API url for storing events."""
379
365
  return "%s://%s%sapi/%s/%s/" % (
380
366
  self.scheme,
@@ -384,8 +370,7 @@ class Auth:
384
370
  type.value,
385
371
  )
386
372
 
387
- def to_header(self):
388
- # type: () -> str
373
+ def to_header(self) -> str:
389
374
  """Returns the auth header a string."""
390
375
  rv = [("sentry_key", self.public_key), ("sentry_version", self.version)]
391
376
  if self.client is not None:
@@ -395,21 +380,18 @@ class Auth:
395
380
  return "Sentry " + ", ".join("%s=%s" % (key, value) for key, value in rv)
396
381
 
397
382
 
398
- def get_type_name(cls):
399
- # type: (Optional[type]) -> Optional[str]
383
+ def get_type_name(cls: Optional[type]) -> Optional[str]:
400
384
  return getattr(cls, "__qualname__", None) or getattr(cls, "__name__", None)
401
385
 
402
386
 
403
- def get_type_module(cls):
404
- # type: (Optional[type]) -> Optional[str]
387
+ def get_type_module(cls: Optional[type]) -> Optional[str]:
405
388
  mod = getattr(cls, "__module__", None)
406
389
  if mod not in (None, "builtins", "__builtins__"):
407
390
  return mod
408
391
  return None
409
392
 
410
393
 
411
- def should_hide_frame(frame):
412
- # type: (FrameType) -> bool
394
+ def should_hide_frame(frame: FrameType) -> bool:
413
395
  try:
414
396
  mod = frame.f_globals["__name__"]
415
397
  if mod.startswith("sentry_sdk."):
@@ -427,9 +409,8 @@ def should_hide_frame(frame):
427
409
  return False
428
410
 
429
411
 
430
- def iter_stacks(tb):
431
- # type: (Optional[TracebackType]) -> Iterator[TracebackType]
432
- tb_ = tb # type: Optional[TracebackType]
412
+ def iter_stacks(tb: Optional[TracebackType]) -> Iterator[TracebackType]:
413
+ tb_: Optional[TracebackType] = tb
433
414
  while tb_ is not None:
434
415
  if not should_hide_frame(tb_.tb_frame):
435
416
  yield tb_
@@ -437,18 +418,17 @@ def iter_stacks(tb):
437
418
 
438
419
 
439
420
  def get_lines_from_file(
440
- filename, # type: str
441
- lineno, # type: int
442
- max_length=None, # type: Optional[int]
443
- loader=None, # type: Optional[Any]
444
- module=None, # type: Optional[str]
445
- ):
446
- # type: (...) -> Tuple[List[Annotated[str]], Optional[Annotated[str]], List[Annotated[str]]]
421
+ filename: str,
422
+ lineno: int,
423
+ max_length: Optional[int] = None,
424
+ loader: Optional[Any] = None,
425
+ module: Optional[str] = None,
426
+ ) -> Tuple[List[Annotated[str]], Optional[Annotated[str]], List[Annotated[str]]]:
447
427
  context_lines = 5
448
428
  source = None
449
429
  if loader is not None and hasattr(loader, "get_source"):
450
430
  try:
451
- source_str = loader.get_source(module) # type: Optional[str]
431
+ source_str: Optional[str] = loader.get_source(module)
452
432
  except (ImportError, IOError):
453
433
  source_str = None
454
434
  if source_str is not None:
@@ -483,13 +463,12 @@ def get_lines_from_file(
483
463
 
484
464
 
485
465
  def get_source_context(
486
- frame, # type: FrameType
487
- tb_lineno, # type: Optional[int]
488
- max_value_length=None, # type: Optional[int]
489
- ):
490
- # type: (...) -> Tuple[List[Annotated[str]], Optional[Annotated[str]], List[Annotated[str]]]
466
+ frame: FrameType,
467
+ tb_lineno: Optional[int],
468
+ max_value_length: Optional[int] = None,
469
+ ) -> Tuple[List[Annotated[str]], Optional[Annotated[str]], List[Annotated[str]]]:
491
470
  try:
492
- abs_path = frame.f_code.co_filename # type: Optional[str]
471
+ abs_path: Optional[str] = frame.f_code.co_filename
493
472
  except Exception:
494
473
  abs_path = None
495
474
  try:
@@ -510,24 +489,23 @@ def get_source_context(
510
489
  return [], None, []
511
490
 
512
491
 
513
- def safe_str(value):
514
- # type: (Any) -> str
492
+ def safe_str(value: Any) -> str:
515
493
  try:
516
494
  return str(value)
517
495
  except Exception:
518
496
  return safe_repr(value)
519
497
 
520
498
 
521
- def safe_repr(value):
522
- # type: (Any) -> str
499
+ def safe_repr(value: Any) -> str:
523
500
  try:
524
501
  return repr(value)
525
502
  except Exception:
526
503
  return "<broken repr>"
527
504
 
528
505
 
529
- def filename_for_module(module, abs_path):
530
- # type: (Optional[str], Optional[str]) -> Optional[str]
506
+ def filename_for_module(
507
+ module: Optional[str], abs_path: Optional[str]
508
+ ) -> Optional[str]:
531
509
  if not abs_path or not module:
532
510
  return abs_path
533
511
 
@@ -551,14 +529,13 @@ def filename_for_module(module, abs_path):
551
529
 
552
530
 
553
531
  def serialize_frame(
554
- frame,
555
- tb_lineno=None,
556
- include_local_variables=True,
557
- include_source_context=True,
558
- max_value_length=None,
559
- custom_repr=None,
560
- ):
561
- # type: (FrameType, Optional[int], bool, bool, Optional[int], Optional[Callable[..., Optional[str]]]) -> Dict[str, Any]
532
+ frame: FrameType,
533
+ tb_lineno: Optional[int] = None,
534
+ include_local_variables: bool = True,
535
+ include_source_context: bool = True,
536
+ max_value_length: Optional[int] = None,
537
+ custom_repr: Optional[Callable[..., Optional[str]]] = None,
538
+ ) -> Dict[str, Any]:
562
539
  f_code = getattr(frame, "f_code", None)
563
540
  if not f_code:
564
541
  abs_path = None
@@ -574,13 +551,18 @@ def serialize_frame(
574
551
  if tb_lineno is None:
575
552
  tb_lineno = frame.f_lineno
576
553
 
577
- rv = {
554
+ try:
555
+ os_abs_path = os.path.abspath(abs_path) if abs_path else None
556
+ except Exception:
557
+ os_abs_path = None
558
+
559
+ rv: Dict[str, Any] = {
578
560
  "filename": filename_for_module(module, abs_path) or None,
579
- "abs_path": os.path.abspath(abs_path) if abs_path else None,
561
+ "abs_path": os_abs_path,
580
562
  "function": function or "<unknown>",
581
563
  "module": module,
582
564
  "lineno": tb_lineno,
583
- } # type: Dict[str, Any]
565
+ }
584
566
 
585
567
  if include_source_context:
586
568
  rv["pre_context"], rv["context_line"], rv["post_context"] = get_source_context(
@@ -598,15 +580,14 @@ def serialize_frame(
598
580
 
599
581
 
600
582
  def current_stacktrace(
601
- include_local_variables=True, # type: bool
602
- include_source_context=True, # type: bool
603
- max_value_length=None, # type: Optional[int]
604
- ):
605
- # type: (...) -> Dict[str, Any]
583
+ include_local_variables: bool = True,
584
+ include_source_context: bool = True,
585
+ max_value_length: Optional[int] = None,
586
+ ) -> Dict[str, Any]:
606
587
  __tracebackhide__ = True
607
588
  frames = []
608
589
 
609
- f = sys._getframe() # type: Optional[FrameType]
590
+ f: Optional[FrameType] = sys._getframe()
610
591
  while f is not None:
611
592
  if not should_hide_frame(f):
612
593
  frames.append(
@@ -624,24 +605,22 @@ def current_stacktrace(
624
605
  return {"frames": frames}
625
606
 
626
607
 
627
- def get_errno(exc_value):
628
- # type: (BaseException) -> Optional[Any]
608
+ def get_errno(exc_value: BaseException) -> Optional[Any]:
629
609
  return getattr(exc_value, "errno", None)
630
610
 
631
611
 
632
- def get_error_message(exc_value):
633
- # type: (Optional[BaseException]) -> str
634
- message = (
612
+ def get_error_message(exc_value: Optional[BaseException]) -> str:
613
+ message: str = (
635
614
  getattr(exc_value, "message", "")
636
615
  or getattr(exc_value, "detail", "")
637
616
  or safe_str(exc_value)
638
- ) # type: str
617
+ )
639
618
 
640
619
  # __notes__ should be a list of strings when notes are added
641
620
  # via add_note, but can be anything else if __notes__ is set
642
621
  # directly. We only support strings in __notes__, since that
643
622
  # is the correct use.
644
- notes = getattr(exc_value, "__notes__", None) # type: object
623
+ notes: object = getattr(exc_value, "__notes__", None)
645
624
  if isinstance(notes, list) and len(notes) > 0:
646
625
  message += "\n" + "\n".join(note for note in notes if isinstance(note, str))
647
626
 
@@ -649,24 +628,23 @@ def get_error_message(exc_value):
649
628
 
650
629
 
651
630
  def single_exception_from_error_tuple(
652
- exc_type, # type: Optional[type]
653
- exc_value, # type: Optional[BaseException]
654
- tb, # type: Optional[TracebackType]
655
- client_options=None, # type: Optional[Dict[str, Any]]
656
- mechanism=None, # type: Optional[Dict[str, Any]]
657
- exception_id=None, # type: Optional[int]
658
- parent_id=None, # type: Optional[int]
659
- source=None, # type: Optional[str]
660
- full_stack=None, # type: Optional[list[dict[str, Any]]]
661
- ):
662
- # type: (...) -> Dict[str, Any]
631
+ exc_type: Optional[type],
632
+ exc_value: Optional[BaseException],
633
+ tb: Optional[TracebackType],
634
+ client_options: Optional[Dict[str, Any]] = None,
635
+ mechanism: Optional[Dict[str, Any]] = None,
636
+ exception_id: Optional[int] = None,
637
+ parent_id: Optional[int] = None,
638
+ source: Optional[str] = None,
639
+ full_stack: Optional[list[dict[str, Any]]] = None,
640
+ ) -> Dict[str, Any]:
663
641
  """
664
642
  Creates a dict that goes into the events `exception.values` list and is ingestible by Sentry.
665
643
 
666
644
  See the Exception Interface documentation for more details:
667
645
  https://develop.sentry.dev/sdk/event-payloads/exception/
668
646
  """
669
- exception_value = {} # type: Dict[str, Any]
647
+ exception_value: Dict[str, Any] = {}
670
648
  exception_value["mechanism"] = (
671
649
  mechanism.copy() if mechanism else {"type": "generic", "handled": True}
672
650
  )
@@ -715,7 +693,7 @@ def single_exception_from_error_tuple(
715
693
  max_value_length = client_options["max_value_length"]
716
694
  custom_repr = client_options.get("custom_repr")
717
695
 
718
- frames = [
696
+ frames: List[Dict[str, Any]] = [
719
697
  serialize_frame(
720
698
  tb.tb_frame,
721
699
  tb_lineno=tb.tb_lineno,
@@ -727,7 +705,7 @@ def single_exception_from_error_tuple(
727
705
  # Process at most MAX_STACK_FRAMES + 1 frames, to avoid hanging on
728
706
  # processing a super-long stacktrace.
729
707
  for tb, _ in zip(iter_stacks(tb), range(MAX_STACK_FRAMES + 1))
730
- ] # type: List[Dict[str, Any]]
708
+ ]
731
709
 
732
710
  if len(frames) > MAX_STACK_FRAMES:
733
711
  # If we have more frames than the limit, we remove the stacktrace completely.
@@ -755,12 +733,11 @@ HAS_CHAINED_EXCEPTIONS = hasattr(Exception, "__suppress_context__")
755
733
 
756
734
  if HAS_CHAINED_EXCEPTIONS:
757
735
 
758
- def walk_exception_chain(exc_info):
759
- # type: (ExcInfo) -> Iterator[ExcInfo]
736
+ def walk_exception_chain(exc_info: ExcInfo) -> Iterator[ExcInfo]:
760
737
  exc_type, exc_value, tb = exc_info
761
738
 
762
739
  seen_exceptions = []
763
- seen_exception_ids = set() # type: Set[int]
740
+ seen_exception_ids: Set[int] = set()
764
741
 
765
742
  while (
766
743
  exc_type is not None
@@ -787,23 +764,21 @@ if HAS_CHAINED_EXCEPTIONS:
787
764
 
788
765
  else:
789
766
 
790
- def walk_exception_chain(exc_info):
791
- # type: (ExcInfo) -> Iterator[ExcInfo]
767
+ def walk_exception_chain(exc_info: ExcInfo) -> Iterator[ExcInfo]:
792
768
  yield exc_info
793
769
 
794
770
 
795
771
  def exceptions_from_error(
796
- exc_type, # type: Optional[type]
797
- exc_value, # type: Optional[BaseException]
798
- tb, # type: Optional[TracebackType]
799
- client_options=None, # type: Optional[Dict[str, Any]]
800
- mechanism=None, # type: Optional[Dict[str, Any]]
801
- exception_id=0, # type: int
802
- parent_id=0, # type: int
803
- source=None, # type: Optional[str]
804
- full_stack=None, # type: Optional[list[dict[str, Any]]]
805
- ):
806
- # type: (...) -> Tuple[int, List[Dict[str, Any]]]
772
+ exc_type: Optional[type],
773
+ exc_value: Optional[BaseException],
774
+ tb: Optional[TracebackType],
775
+ client_options: Optional[Dict[str, Any]] = None,
776
+ mechanism: Optional[Dict[str, Any]] = None,
777
+ exception_id: int = 0,
778
+ parent_id: int = 0,
779
+ source: Optional[str] = None,
780
+ full_stack: Optional[list[dict[str, Any]]] = None,
781
+ ) -> Tuple[int, List[Dict[str, Any]]]:
807
782
  """
808
783
  Converts the given exception information into the Sentry structured "exception" format.
809
784
  This will return a list of exceptions (a flattened tree of exceptions) in the
@@ -838,7 +813,9 @@ def exceptions_from_error(
838
813
  exception_source = None
839
814
 
840
815
  # Add any causing exceptions, if present.
841
- should_suppress_context = hasattr(exc_value, "__suppress_context__") and exc_value.__suppress_context__ # type: ignore
816
+ should_suppress_context = (
817
+ hasattr(exc_value, "__suppress_context__") and exc_value.__suppress_context__ # type: ignore[union-attr]
818
+ )
842
819
  # Note: __suppress_context__ is True if the exception is raised with the `from` keyword.
843
820
  if should_suppress_context:
844
821
  # Explicitly chained exceptions (Like: raise NewException() from OriginalException())
@@ -862,7 +839,7 @@ def exceptions_from_error(
862
839
  )
863
840
  if has_implicit_causing_exception:
864
841
  exception_source = "__context__"
865
- causing_exception = exc_value.__context__ # type: ignore
842
+ causing_exception = exc_value.__context__ # type: ignore
866
843
 
867
844
  if causing_exception:
868
845
  # Some frameworks (e.g. FastAPI) wrap the causing exception in an
@@ -912,12 +889,11 @@ def exceptions_from_error(
912
889
 
913
890
 
914
891
  def exceptions_from_error_tuple(
915
- exc_info, # type: ExcInfo
916
- client_options=None, # type: Optional[Dict[str, Any]]
917
- mechanism=None, # type: Optional[Dict[str, Any]]
918
- full_stack=None, # type: Optional[list[dict[str, Any]]]
919
- ):
920
- # type: (...) -> List[Dict[str, Any]]
892
+ exc_info: ExcInfo,
893
+ client_options: Optional[Dict[str, Any]] = None,
894
+ mechanism: Optional[Dict[str, Any]] = None,
895
+ full_stack: Optional[list[dict[str, Any]]] = None,
896
+ ) -> List[Dict[str, Any]]:
921
897
  """
922
898
  Convert Python's exception information into Sentry's structured "exception" format in the event.
923
899
  See https://develop.sentry.dev/sdk/data-model/event-payloads/exception/
@@ -946,16 +922,14 @@ def exceptions_from_error_tuple(
946
922
  return exceptions
947
923
 
948
924
 
949
- def to_string(value):
950
- # type: (str) -> str
925
+ def to_string(value: Any) -> str:
951
926
  try:
952
927
  return str(value)
953
928
  except UnicodeDecodeError:
954
929
  return repr(value)[1:-1]
955
930
 
956
931
 
957
- def iter_event_stacktraces(event):
958
- # type: (Event) -> Iterator[Annotated[Dict[str, Any]]]
932
+ def iter_event_stacktraces(event: Event) -> Iterator[Annotated[Dict[str, Any]]]:
959
933
  if "stacktrace" in event:
960
934
  yield event["stacktrace"]
961
935
  if "threads" in event:
@@ -968,8 +942,7 @@ def iter_event_stacktraces(event):
968
942
  yield exception["stacktrace"]
969
943
 
970
944
 
971
- def iter_event_frames(event):
972
- # type: (Event) -> Iterator[Dict[str, Any]]
945
+ def iter_event_frames(event: Event) -> Iterator[Dict[str, Any]]:
973
946
  for stacktrace in iter_event_stacktraces(event):
974
947
  if isinstance(stacktrace, AnnotatedValue):
975
948
  stacktrace = stacktrace.value or {}
@@ -978,8 +951,12 @@ def iter_event_frames(event):
978
951
  yield frame
979
952
 
980
953
 
981
- def handle_in_app(event, in_app_exclude=None, in_app_include=None, project_root=None):
982
- # type: (Event, Optional[List[str]], Optional[List[str]], Optional[str]) -> Event
954
+ def handle_in_app(
955
+ event: Event,
956
+ in_app_exclude: Optional[List[str]] = None,
957
+ in_app_include: Optional[List[str]] = None,
958
+ project_root: Optional[str] = None,
959
+ ) -> Event:
983
960
  for stacktrace in iter_event_stacktraces(event):
984
961
  if isinstance(stacktrace, AnnotatedValue):
985
962
  stacktrace = stacktrace.value or {}
@@ -994,8 +971,12 @@ def handle_in_app(event, in_app_exclude=None, in_app_include=None, project_root=
994
971
  return event
995
972
 
996
973
 
997
- def set_in_app_in_frames(frames, in_app_exclude, in_app_include, project_root=None):
998
- # type: (Any, Optional[List[str]], Optional[List[str]], Optional[str]) -> Optional[Any]
974
+ def set_in_app_in_frames(
975
+ frames: Any,
976
+ in_app_exclude: Optional[List[str]],
977
+ in_app_include: Optional[List[str]],
978
+ project_root: Optional[str] = None,
979
+ ) -> Optional[Any]:
999
980
  if not frames:
1000
981
  return None
1001
982
 
@@ -1033,8 +1014,7 @@ def set_in_app_in_frames(frames, in_app_exclude, in_app_include, project_root=No
1033
1014
  return frames
1034
1015
 
1035
1016
 
1036
- def exc_info_from_error(error):
1037
- # type: (Union[BaseException, ExcInfo]) -> ExcInfo
1017
+ def exc_info_from_error(error: Union[BaseException, ExcInfo]) -> ExcInfo:
1038
1018
  if isinstance(error, tuple) and len(error) == 3:
1039
1019
  exc_type, exc_value, tb = error
1040
1020
  elif isinstance(error, BaseException):
@@ -1052,18 +1032,17 @@ def exc_info_from_error(error):
1052
1032
  else:
1053
1033
  raise ValueError("Expected Exception object to report, got %s!" % type(error))
1054
1034
 
1055
- exc_info = (exc_type, exc_value, tb)
1056
-
1057
- if TYPE_CHECKING:
1058
- # This cast is safe because exc_type and exc_value are either both
1059
- # None or both not None.
1060
- exc_info = cast(ExcInfo, exc_info)
1061
-
1062
- return exc_info
1035
+ if exc_type is not None and exc_value is not None:
1036
+ return (exc_type, exc_value, tb)
1037
+ else:
1038
+ return (None, None, None)
1063
1039
 
1064
1040
 
1065
- def merge_stack_frames(frames, full_stack, client_options):
1066
- # type: (List[Dict[str, Any]], List[Dict[str, Any]], Optional[Dict[str, Any]]) -> List[Dict[str, Any]]
1041
+ def merge_stack_frames(
1042
+ frames: List[Dict[str, Any]],
1043
+ full_stack: List[Dict[str, Any]],
1044
+ client_options: Optional[Dict[str, Any]],
1045
+ ) -> List[Dict[str, Any]]:
1067
1046
  """
1068
1047
  Add the missing frames from full_stack to frames and return the merged list.
1069
1048
  """
@@ -1103,11 +1082,10 @@ def merge_stack_frames(frames, full_stack, client_options):
1103
1082
 
1104
1083
 
1105
1084
  def event_from_exception(
1106
- exc_info, # type: Union[BaseException, ExcInfo]
1107
- client_options=None, # type: Optional[Dict[str, Any]]
1108
- mechanism=None, # type: Optional[Dict[str, Any]]
1109
- ):
1110
- # type: (...) -> Tuple[Event, Dict[str, Any]]
1085
+ exc_info: Union[BaseException, ExcInfo],
1086
+ client_options: Optional[Dict[str, Any]] = None,
1087
+ mechanism: Optional[Dict[str, Any]] = None,
1088
+ ) -> Tuple[Event, Dict[str, Any]]:
1111
1089
  exc_info = exc_info_from_error(exc_info)
1112
1090
  hint = event_hint_with_exc_info(exc_info)
1113
1091
 
@@ -1132,8 +1110,7 @@ def event_from_exception(
1132
1110
  )
1133
1111
 
1134
1112
 
1135
- def _module_in_list(name, items):
1136
- # type: (Optional[str], Optional[List[str]]) -> bool
1113
+ def _module_in_list(name: Optional[str], items: Optional[List[str]]) -> bool:
1137
1114
  if name is None:
1138
1115
  return False
1139
1116
 
@@ -1147,8 +1124,7 @@ def _module_in_list(name, items):
1147
1124
  return False
1148
1125
 
1149
1126
 
1150
- def _is_external_source(abs_path):
1151
- # type: (Optional[str]) -> bool
1127
+ def _is_external_source(abs_path: Optional[str]) -> bool:
1152
1128
  # check if frame is in 'site-packages' or 'dist-packages'
1153
1129
  if abs_path is None:
1154
1130
  return False
@@ -1159,8 +1135,7 @@ def _is_external_source(abs_path):
1159
1135
  return external_source
1160
1136
 
1161
1137
 
1162
- def _is_in_project_root(abs_path, project_root):
1163
- # type: (Optional[str], Optional[str]) -> bool
1138
+ def _is_in_project_root(abs_path: Optional[str], project_root: Optional[str]) -> bool:
1164
1139
  if abs_path is None or project_root is None:
1165
1140
  return False
1166
1141
 
@@ -1171,8 +1146,7 @@ def _is_in_project_root(abs_path, project_root):
1171
1146
  return False
1172
1147
 
1173
1148
 
1174
- def _truncate_by_bytes(string, max_bytes):
1175
- # type: (str, int) -> str
1149
+ def _truncate_by_bytes(string: str, max_bytes: int) -> str:
1176
1150
  """
1177
1151
  Truncate a UTF-8-encodable string to the last full codepoint so that it fits in max_bytes.
1178
1152
  """
@@ -1181,16 +1155,16 @@ def _truncate_by_bytes(string, max_bytes):
1181
1155
  return truncated + "..."
1182
1156
 
1183
1157
 
1184
- def _get_size_in_bytes(value):
1185
- # type: (str) -> Optional[int]
1158
+ def _get_size_in_bytes(value: str) -> Optional[int]:
1186
1159
  try:
1187
1160
  return len(value.encode("utf-8"))
1188
1161
  except (UnicodeEncodeError, UnicodeDecodeError):
1189
1162
  return None
1190
1163
 
1191
1164
 
1192
- def strip_string(value, max_length=None):
1193
- # type: (str, Optional[int]) -> Union[AnnotatedValue, str]
1165
+ def strip_string(
1166
+ value: str, max_length: Optional[int] = None
1167
+ ) -> Union[AnnotatedValue, str]:
1194
1168
  if not value:
1195
1169
  return value
1196
1170
 
@@ -1218,8 +1192,7 @@ def strip_string(value, max_length=None):
1218
1192
  )
1219
1193
 
1220
1194
 
1221
- def parse_version(version):
1222
- # type: (str) -> Optional[Tuple[int, ...]]
1195
+ def parse_version(version: str) -> Optional[Tuple[int, ...]]:
1223
1196
  """
1224
1197
  Parses a version string into a tuple of integers.
1225
1198
  This uses the parsing loging from PEP 440:
@@ -1263,17 +1236,16 @@ def parse_version(version):
1263
1236
 
1264
1237
  try:
1265
1238
  release = pattern.match(version).groupdict()["release"] # type: ignore
1266
- release_tuple = tuple(map(int, release.split(".")[:3])) # type: Tuple[int, ...]
1239
+ release_tuple: Tuple[int, ...] = tuple(map(int, release.split(".")[:3]))
1267
1240
  except (TypeError, ValueError, AttributeError):
1268
1241
  return None
1269
1242
 
1270
1243
  return release_tuple
1271
1244
 
1272
1245
 
1273
- def _is_contextvars_broken():
1274
- # type: () -> bool
1246
+ def _is_contextvars_broken() -> bool:
1275
1247
  """
1276
- Returns whether gevent/eventlet have patched the stdlib in a way where thread locals are now more "correct" than contextvars.
1248
+ Returns whether gevent has patched the stdlib in a way where thread locals are now more "correct" than contextvars.
1277
1249
  """
1278
1250
  try:
1279
1251
  import gevent
@@ -1302,52 +1274,30 @@ def _is_contextvars_broken():
1302
1274
  except ImportError:
1303
1275
  pass
1304
1276
 
1305
- try:
1306
- import greenlet
1307
- from eventlet.patcher import is_monkey_patched # type: ignore
1308
-
1309
- greenlet_version = parse_version(greenlet.__version__)
1310
-
1311
- if greenlet_version is None:
1312
- logger.error(
1313
- "Internal error in Sentry SDK: Could not parse Greenlet version from greenlet.__version__."
1314
- )
1315
- return False
1316
-
1317
- if is_monkey_patched("thread") and greenlet_version < (0, 5):
1318
- return True
1319
- except ImportError:
1320
- pass
1321
-
1322
1277
  return False
1323
1278
 
1324
1279
 
1325
- def _make_threadlocal_contextvars(local):
1326
- # type: (type) -> type
1280
+ def _make_threadlocal_contextvars(local: type) -> type:
1327
1281
  class ContextVar:
1328
1282
  # Super-limited impl of ContextVar
1329
1283
 
1330
- def __init__(self, name, default=None):
1331
- # type: (str, Any) -> None
1284
+ def __init__(self, name: str, default: Optional[Any] = None) -> None:
1332
1285
  self._name = name
1333
1286
  self._default = default
1334
1287
  self._local = local()
1335
1288
  self._original_local = local()
1336
1289
 
1337
- def get(self, default=None):
1338
- # type: (Any) -> Any
1290
+ def get(self, default: Optional[Any] = None) -> Any:
1339
1291
  return getattr(self._local, "value", default or self._default)
1340
1292
 
1341
- def set(self, value):
1342
- # type: (Any) -> Any
1293
+ def set(self, value: Any) -> Any:
1343
1294
  token = str(random.getrandbits(64))
1344
1295
  original_value = self.get()
1345
1296
  setattr(self._original_local, token, original_value)
1346
1297
  self._local.value = value
1347
1298
  return token
1348
1299
 
1349
- def reset(self, token):
1350
- # type: (Any) -> None
1300
+ def reset(self, token: Any) -> None:
1351
1301
  self._local.value = getattr(self._original_local, token)
1352
1302
  # delete the original value (this way it works in Python 3.6+)
1353
1303
  del self._original_local.__dict__[token]
@@ -1355,8 +1305,7 @@ def _make_threadlocal_contextvars(local):
1355
1305
  return ContextVar
1356
1306
 
1357
1307
 
1358
- def _get_contextvars():
1359
- # type: () -> Tuple[bool, type]
1308
+ def _get_contextvars() -> Tuple[bool, type]:
1360
1309
  """
1361
1310
  Figure out the "right" contextvars installation to use. Returns a
1362
1311
  `contextvars.ContextVar`-like class with a limited API.
@@ -1391,10 +1340,9 @@ Please refer to https://docs.sentry.io/platforms/python/contextvars/ for more in
1391
1340
  """
1392
1341
 
1393
1342
 
1394
- def qualname_from_function(func):
1395
- # type: (Callable[..., Any]) -> Optional[str]
1343
+ def qualname_from_function(func: Callable[..., Any]) -> Optional[str]:
1396
1344
  """Return the qualified name of func. Works with regular function, lambda, partial and partialmethod."""
1397
- func_qualname = None # type: Optional[str]
1345
+ func_qualname: Optional[str] = None
1398
1346
 
1399
1347
  # Python 2
1400
1348
  try:
@@ -1435,8 +1383,7 @@ def qualname_from_function(func):
1435
1383
  return func_qualname
1436
1384
 
1437
1385
 
1438
- def transaction_from_function(func):
1439
- # type: (Callable[..., Any]) -> Optional[str]
1386
+ def transaction_from_function(func: Callable[..., Any]) -> Optional[str]:
1440
1387
  return qualname_from_function(func)
1441
1388
 
1442
1389
 
@@ -1454,19 +1401,16 @@ class TimeoutThread(threading.Thread):
1454
1401
  waiting_time and raises a custom ServerlessTimeout exception.
1455
1402
  """
1456
1403
 
1457
- def __init__(self, waiting_time, configured_timeout):
1458
- # type: (float, int) -> None
1404
+ def __init__(self, waiting_time: float, configured_timeout: int) -> None:
1459
1405
  threading.Thread.__init__(self)
1460
1406
  self.waiting_time = waiting_time
1461
1407
  self.configured_timeout = configured_timeout
1462
1408
  self._stop_event = threading.Event()
1463
1409
 
1464
- def stop(self):
1465
- # type: () -> None
1410
+ def stop(self) -> None:
1466
1411
  self._stop_event.set()
1467
1412
 
1468
- def run(self):
1469
- # type: () -> None
1413
+ def run(self) -> None:
1470
1414
 
1471
1415
  self._stop_event.wait(self.waiting_time)
1472
1416
 
@@ -1487,8 +1431,7 @@ class TimeoutThread(threading.Thread):
1487
1431
  )
1488
1432
 
1489
1433
 
1490
- def to_base64(original):
1491
- # type: (str) -> Optional[str]
1434
+ def to_base64(original: str) -> Optional[str]:
1492
1435
  """
1493
1436
  Convert a string to base64, via UTF-8. Returns None on invalid input.
1494
1437
  """
@@ -1504,8 +1447,7 @@ def to_base64(original):
1504
1447
  return base64_string
1505
1448
 
1506
1449
 
1507
- def from_base64(base64_string):
1508
- # type: (str) -> Optional[str]
1450
+ def from_base64(base64_string: str) -> Optional[str]:
1509
1451
  """
1510
1452
  Convert a string from base64, via UTF-8. Returns None on invalid input.
1511
1453
  """
@@ -1529,8 +1471,12 @@ def from_base64(base64_string):
1529
1471
  Components = namedtuple("Components", ["scheme", "netloc", "path", "query", "fragment"])
1530
1472
 
1531
1473
 
1532
- def sanitize_url(url, remove_authority=True, remove_query_values=True, split=False):
1533
- # type: (str, bool, bool, bool) -> Union[str, Components]
1474
+ def sanitize_url(
1475
+ url: str,
1476
+ remove_authority: bool = True,
1477
+ remove_query_values: bool = True,
1478
+ split: bool = False,
1479
+ ) -> Union[str, Components]:
1534
1480
  """
1535
1481
  Removes the authority and query parameter values from a given URL.
1536
1482
  """
@@ -1576,8 +1522,7 @@ def sanitize_url(url, remove_authority=True, remove_query_values=True, split=Fal
1576
1522
  ParsedUrl = namedtuple("ParsedUrl", ["url", "query", "fragment"])
1577
1523
 
1578
1524
 
1579
- def parse_url(url, sanitize=True):
1580
- # type: (str, bool) -> ParsedUrl
1525
+ def parse_url(url: str, sanitize: bool = True) -> ParsedUrl:
1581
1526
  """
1582
1527
  Splits a URL into a url (including path), query and fragment. If sanitize is True, the query
1583
1528
  parameters will be sanitized to remove sensitive data. The autority (username and password)
@@ -1604,11 +1549,11 @@ def parse_url(url, sanitize=True):
1604
1549
  )
1605
1550
 
1606
1551
 
1607
- def is_valid_sample_rate(rate, source):
1608
- # type: (Any, str) -> bool
1552
+ def is_valid_sample_rate(rate: Any, source: str) -> Optional[float]:
1609
1553
  """
1610
1554
  Checks the given sample rate to make sure it is valid type and value (a
1611
1555
  boolean or a number between 0 and 1, inclusive).
1556
+ Returns the final float value to use if valid.
1612
1557
  """
1613
1558
 
1614
1559
  # both booleans and NaN are instances of Real, so a) checking for Real
@@ -1620,7 +1565,7 @@ def is_valid_sample_rate(rate, source):
1620
1565
  source=source, rate=rate, type=type(rate)
1621
1566
  )
1622
1567
  )
1623
- return False
1568
+ return None
1624
1569
 
1625
1570
  # in case rate is a boolean, it will get cast to 1 if it's True and 0 if it's False
1626
1571
  rate = float(rate)
@@ -1630,13 +1575,14 @@ def is_valid_sample_rate(rate, source):
1630
1575
  source=source, rate=rate
1631
1576
  )
1632
1577
  )
1633
- return False
1578
+ return None
1634
1579
 
1635
- return True
1580
+ return rate
1636
1581
 
1637
1582
 
1638
- def match_regex_list(item, regex_list=None, substring_matching=False):
1639
- # type: (str, Optional[List[str]], bool) -> bool
1583
+ def match_regex_list(
1584
+ item: str, regex_list: Optional[List[str]] = None, substring_matching: bool = False
1585
+ ) -> bool:
1640
1586
  if regex_list is None:
1641
1587
  return False
1642
1588
 
@@ -1651,8 +1597,7 @@ def match_regex_list(item, regex_list=None, substring_matching=False):
1651
1597
  return False
1652
1598
 
1653
1599
 
1654
- def is_sentry_url(client, url):
1655
- # type: (sentry_sdk.client.BaseClient, str) -> bool
1600
+ def is_sentry_url(client: sentry_sdk.client.BaseClient, url: str) -> bool:
1656
1601
  """
1657
1602
  Determines whether the given URL matches the Sentry DSN.
1658
1603
  """
@@ -1664,8 +1609,7 @@ def is_sentry_url(client, url):
1664
1609
  )
1665
1610
 
1666
1611
 
1667
- def _generate_installed_modules():
1668
- # type: () -> Iterator[Tuple[str, str]]
1612
+ def _generate_installed_modules() -> Iterator[Tuple[str, str]]:
1669
1613
  try:
1670
1614
  from importlib import metadata
1671
1615
 
@@ -1693,21 +1637,18 @@ def _generate_installed_modules():
1693
1637
  yield _normalize_module_name(info.key), info.version
1694
1638
 
1695
1639
 
1696
- def _normalize_module_name(name):
1697
- # type: (str) -> str
1640
+ def _normalize_module_name(name: str) -> str:
1698
1641
  return name.lower()
1699
1642
 
1700
1643
 
1701
- def _get_installed_modules():
1702
- # type: () -> Dict[str, str]
1644
+ def _get_installed_modules() -> Dict[str, str]:
1703
1645
  global _installed_modules
1704
1646
  if _installed_modules is None:
1705
1647
  _installed_modules = dict(_generate_installed_modules())
1706
1648
  return _installed_modules
1707
1649
 
1708
1650
 
1709
- def package_version(package):
1710
- # type: (str) -> Optional[Tuple[int, ...]]
1651
+ def package_version(package: str) -> Optional[Tuple[int, ...]]:
1711
1652
  installed_packages = _get_installed_modules()
1712
1653
  version = installed_packages.get(package)
1713
1654
  if version is None:
@@ -1716,43 +1657,35 @@ def package_version(package):
1716
1657
  return parse_version(version)
1717
1658
 
1718
1659
 
1719
- def reraise(tp, value, tb=None):
1720
- # type: (Optional[Type[BaseException]], Optional[BaseException], Optional[Any]) -> NoReturn
1660
+ def reraise(
1661
+ tp: Optional[Type[BaseException]],
1662
+ value: Optional[BaseException],
1663
+ tb: Optional[Any] = None,
1664
+ ) -> NoReturn:
1721
1665
  assert value is not None
1722
1666
  if value.__traceback__ is not tb:
1723
1667
  raise value.with_traceback(tb)
1724
1668
  raise value
1725
1669
 
1726
1670
 
1727
- def _no_op(*_a, **_k):
1728
- # type: (*Any, **Any) -> None
1729
- """No-op function for ensure_integration_enabled."""
1730
- pass
1731
-
1732
-
1733
1671
  if TYPE_CHECKING:
1734
1672
 
1735
1673
  @overload
1736
1674
  def ensure_integration_enabled(
1737
- integration, # type: type[sentry_sdk.integrations.Integration]
1738
- original_function, # type: Callable[P, R]
1739
- ):
1740
- # type: (...) -> Callable[[Callable[P, R]], Callable[P, R]]
1741
- ...
1675
+ integration: type[sentry_sdk.integrations.Integration],
1676
+ original_function: Callable[P, R],
1677
+ ) -> Callable[[Callable[P, R]], Callable[P, R]]: ...
1742
1678
 
1743
1679
  @overload
1744
1680
  def ensure_integration_enabled(
1745
- integration, # type: type[sentry_sdk.integrations.Integration]
1746
- ):
1747
- # type: (...) -> Callable[[Callable[P, None]], Callable[P, None]]
1748
- ...
1681
+ integration: type[sentry_sdk.integrations.Integration],
1682
+ ) -> Callable[[Callable[P, None]], Callable[P, None]]: ...
1749
1683
 
1750
1684
 
1751
1685
  def ensure_integration_enabled(
1752
- integration, # type: type[sentry_sdk.integrations.Integration]
1753
- original_function=_no_op, # type: Union[Callable[P, R], Callable[P, None]]
1754
- ):
1755
- # type: (...) -> Callable[[Callable[P, R]], Callable[P, R]]
1686
+ integration: type[sentry_sdk.integrations.Integration],
1687
+ original_function: Optional[Callable[P, R]] = None,
1688
+ ) -> Callable[[Callable[P, R]], Callable[P, Optional[R]]]:
1756
1689
  """
1757
1690
  Ensures a given integration is enabled prior to calling a Sentry-patched function.
1758
1691
 
@@ -1774,30 +1707,25 @@ def ensure_integration_enabled(
1774
1707
  return my_function()
1775
1708
  ```
1776
1709
  """
1777
- if TYPE_CHECKING:
1778
- # Type hint to ensure the default function has the right typing. The overloads
1779
- # ensure the default _no_op function is only used when R is None.
1780
- original_function = cast(Callable[P, R], original_function)
1781
-
1782
- def patcher(sentry_patched_function):
1783
- # type: (Callable[P, R]) -> Callable[P, R]
1784
- def runner(*args: "P.args", **kwargs: "P.kwargs"):
1785
- # type: (...) -> R
1786
- if sentry_sdk.get_client().get_integration(integration) is None:
1787
- return original_function(*args, **kwargs)
1788
1710
 
1789
- return sentry_patched_function(*args, **kwargs)
1711
+ def patcher(sentry_patched_function: Callable[P, R]) -> Callable[P, Optional[R]]:
1712
+ def runner(*args: P.args, **kwargs: P.kwargs) -> Optional[R]:
1713
+ if sentry_sdk.get_client().get_integration(integration) is not None:
1714
+ return sentry_patched_function(*args, **kwargs)
1715
+ elif original_function is not None:
1716
+ return original_function(*args, **kwargs)
1717
+ else:
1718
+ return None
1790
1719
 
1791
- if original_function is _no_op:
1720
+ if original_function:
1721
+ return wraps(original_function)(runner)
1722
+ else:
1792
1723
  return wraps(sentry_patched_function)(runner)
1793
1724
 
1794
- return wraps(original_function)(runner)
1795
-
1796
1725
  return patcher
1797
1726
 
1798
1727
 
1799
- def now():
1800
- # type: () -> float
1728
+ def now() -> float:
1801
1729
  return time.perf_counter()
1802
1730
 
1803
1731
 
@@ -1808,23 +1736,21 @@ except ImportError:
1808
1736
 
1809
1737
  # it's not great that the signatures are different, get_hub can't return None
1810
1738
  # consider adding an if TYPE_CHECKING to change the signature to Optional[GeventHub]
1811
- def get_gevent_hub(): # type: ignore[misc]
1812
- # type: () -> Optional[GeventHub]
1739
+ def get_gevent_hub() -> Optional[GeventHub]: # type: ignore[misc]
1813
1740
  return None
1814
1741
 
1815
- def is_module_patched(mod_name):
1816
- # type: (str) -> bool
1742
+ def is_module_patched(mod_name: str) -> bool:
1817
1743
  # unable to import from gevent means no modules have been patched
1818
1744
  return False
1819
1745
 
1820
1746
 
1821
- def is_gevent():
1822
- # type: () -> bool
1747
+ def is_gevent() -> bool:
1823
1748
  return is_module_patched("threading") or is_module_patched("_thread")
1824
1749
 
1825
1750
 
1826
- def get_current_thread_meta(thread=None):
1827
- # type: (Optional[threading.Thread]) -> Tuple[Optional[int], Optional[str]]
1751
+ def get_current_thread_meta(
1752
+ thread: Optional[threading.Thread] = None,
1753
+ ) -> Tuple[Optional[int], Optional[str]]:
1828
1754
  """
1829
1755
  Try to get the id of the current thread, with various fall backs.
1830
1756
  """
@@ -1874,8 +1800,7 @@ def get_current_thread_meta(thread=None):
1874
1800
  return None, None
1875
1801
 
1876
1802
 
1877
- def _serialize_span_attribute(value):
1878
- # type: (Any) -> Optional[AttributeValue]
1803
+ def _serialize_span_attribute(value: Any) -> Optional[AttributeValue]:
1879
1804
  """Serialize an object so that it's OTel-compatible and displays nicely in Sentry."""
1880
1805
  # check for allowed primitives
1881
1806
  if isinstance(value, (int, str, float, bool)):
@@ -1902,8 +1827,7 @@ def _serialize_span_attribute(value):
1902
1827
  ISO_TZ_SEPARATORS = frozenset(("+", "-"))
1903
1828
 
1904
1829
 
1905
- def datetime_from_isoformat(value):
1906
- # type: (str) -> datetime
1830
+ def datetime_from_isoformat(value: str) -> datetime:
1907
1831
  try:
1908
1832
  result = datetime.fromisoformat(value)
1909
1833
  except (AttributeError, ValueError):
@@ -1924,8 +1848,7 @@ def datetime_from_isoformat(value):
1924
1848
  return result.astimezone(timezone.utc)
1925
1849
 
1926
1850
 
1927
- def should_be_treated_as_error(ty, value):
1928
- # type: (Any, Any) -> bool
1851
+ def should_be_treated_as_error(ty: Any, value: Any) -> bool:
1929
1852
  if ty == SystemExit and hasattr(value, "code") and value.code in (0, None):
1930
1853
  # https://docs.python.org/3/library/exceptions.html#SystemExit
1931
1854
  return False
@@ -1933,8 +1856,7 @@ def should_be_treated_as_error(ty, value):
1933
1856
  return True
1934
1857
 
1935
1858
 
1936
- def http_client_status_to_breadcrumb_level(status_code):
1937
- # type: (Optional[int]) -> str
1859
+ def http_client_status_to_breadcrumb_level(status_code: Optional[int]) -> str:
1938
1860
  if status_code is not None:
1939
1861
  if 500 <= status_code <= 599:
1940
1862
  return "error"
@@ -1944,8 +1866,9 @@ def http_client_status_to_breadcrumb_level(status_code):
1944
1866
  return "info"
1945
1867
 
1946
1868
 
1947
- def set_thread_info_from_span(data, span):
1948
- # type: (Dict[str, Any], sentry_sdk.tracing.Span) -> None
1869
+ def set_thread_info_from_span(
1870
+ data: Dict[str, Any], span: sentry_sdk.tracing.Span
1871
+ ) -> None:
1949
1872
  if span.get_attribute(SPANDATA.THREAD_ID) is not None:
1950
1873
  data[SPANDATA.THREAD_ID] = span.get_attribute(SPANDATA.THREAD_ID)
1951
1874
  if span.get_attribute(SPANDATA.THREAD_NAME) is not None: