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
@@ -1,3 +1,4 @@
1
+ from __future__ import annotations
1
2
  import atexit
2
3
  import os
3
4
  import random
@@ -60,18 +61,19 @@ try:
60
61
  from gevent.monkey import get_original
61
62
  from gevent.threadpool import ThreadPool as _ThreadPool
62
63
 
63
- ThreadPool = _ThreadPool # type: Optional[Type[_ThreadPool]]
64
+ ThreadPool: Optional[Type[_ThreadPool]] = _ThreadPool
64
65
  thread_sleep = get_original("time", "sleep")
65
66
  except ImportError:
66
67
  thread_sleep = time.sleep
67
68
  ThreadPool = None
68
69
 
69
70
 
70
- _scheduler = None # type: Optional[ContinuousScheduler]
71
+ _scheduler: Optional[ContinuousScheduler] = None
71
72
 
72
73
 
73
- def setup_continuous_profiler(options, sdk_info, capture_func):
74
- # type: (Dict[str, Any], SDKInfo, Callable[[Envelope], None]) -> bool
74
+ def setup_continuous_profiler(
75
+ options: Dict[str, Any], sdk_info: SDKInfo, capture_func: Callable[[Envelope], None]
76
+ ) -> bool:
75
77
  global _scheduler
76
78
 
77
79
  if _scheduler is not None:
@@ -115,8 +117,7 @@ def setup_continuous_profiler(options, sdk_info, capture_func):
115
117
  return True
116
118
 
117
119
 
118
- def try_autostart_continuous_profiler():
119
- # type: () -> None
120
+ def try_autostart_continuous_profiler() -> None:
120
121
 
121
122
  # TODO: deprecate this as it'll be replaced by the auto lifecycle option
122
123
 
@@ -129,47 +130,43 @@ def try_autostart_continuous_profiler():
129
130
  _scheduler.manual_start()
130
131
 
131
132
 
132
- def try_profile_lifecycle_trace_start():
133
- # type: () -> Union[ContinuousProfile, None]
133
+ def try_profile_lifecycle_trace_start() -> Union[ContinuousProfile, None]:
134
134
  if _scheduler is None:
135
135
  return None
136
136
 
137
137
  return _scheduler.auto_start()
138
138
 
139
139
 
140
- def start_profiler():
141
- # type: () -> None
140
+ def start_profiler() -> None:
142
141
  if _scheduler is None:
143
142
  return
144
143
 
145
144
  _scheduler.manual_start()
146
145
 
147
146
 
148
- def stop_profiler():
149
- # type: () -> None
147
+ def stop_profiler() -> None:
150
148
  if _scheduler is None:
151
149
  return
152
150
 
153
151
  _scheduler.manual_stop()
154
152
 
155
153
 
156
- def teardown_continuous_profiler():
157
- # type: () -> None
154
+ def teardown_continuous_profiler() -> None:
158
155
  stop_profiler()
159
156
 
160
157
  global _scheduler
161
158
  _scheduler = None
162
159
 
163
160
 
164
- def get_profiler_id():
165
- # type: () -> Union[str, None]
161
+ def get_profiler_id() -> Union[str, None]:
166
162
  if _scheduler is None:
167
163
  return None
168
164
  return _scheduler.profiler_id
169
165
 
170
166
 
171
- def determine_profile_session_sampling_decision(sample_rate):
172
- # type: (Union[float, None]) -> bool
167
+ def determine_profile_session_sampling_decision(
168
+ sample_rate: Union[float, None],
169
+ ) -> bool:
173
170
 
174
171
  # `None` is treated as `0.0`
175
172
  if not sample_rate:
@@ -181,16 +178,20 @@ def determine_profile_session_sampling_decision(sample_rate):
181
178
  class ContinuousProfile:
182
179
  active: bool = True
183
180
 
184
- def stop(self):
185
- # type: () -> None
181
+ def stop(self) -> None:
186
182
  self.active = False
187
183
 
188
184
 
189
185
  class ContinuousScheduler:
190
- mode = "unknown" # type: ContinuousProfilerMode
191
-
192
- def __init__(self, frequency, options, sdk_info, capture_func):
193
- # type: (int, Dict[str, Any], SDKInfo, Callable[[Envelope], None]) -> None
186
+ mode: ContinuousProfilerMode = "unknown"
187
+
188
+ def __init__(
189
+ self,
190
+ frequency: int,
191
+ options: Dict[str, Any],
192
+ sdk_info: SDKInfo,
193
+ capture_func: Callable[[Envelope], None],
194
+ ) -> None:
194
195
  self.interval = 1.0 / frequency
195
196
  self.options = options
196
197
  self.sdk_info = sdk_info
@@ -203,16 +204,16 @@ class ContinuousScheduler:
203
204
  )
204
205
 
205
206
  self.sampler = self.make_sampler()
206
- self.buffer = None # type: Optional[ProfileBuffer]
207
- self.pid = None # type: Optional[int]
207
+ self.buffer: Optional[ProfileBuffer] = None
208
+ self.pid: Optional[int] = None
208
209
 
209
210
  self.running = False
211
+ self.soft_shutdown = False
210
212
 
211
- self.new_profiles = deque(maxlen=128) # type: Deque[ContinuousProfile]
212
- self.active_profiles = set() # type: Set[ContinuousProfile]
213
+ self.new_profiles: Deque[ContinuousProfile] = deque(maxlen=128)
214
+ self.active_profiles: Set[ContinuousProfile] = set()
213
215
 
214
- def is_auto_start_enabled(self):
215
- # type: () -> bool
216
+ def is_auto_start_enabled(self) -> bool:
216
217
 
217
218
  # Ensure that the scheduler only autostarts once per process.
218
219
  # This is necessary because many web servers use forks to spawn
@@ -228,8 +229,7 @@ class ContinuousScheduler:
228
229
 
229
230
  return experiments.get("continuous_profiling_auto_start")
230
231
 
231
- def auto_start(self):
232
- # type: () -> Union[ContinuousProfile, None]
232
+ def auto_start(self) -> Union[ContinuousProfile, None]:
233
233
  if not self.sampled:
234
234
  return None
235
235
 
@@ -245,8 +245,7 @@ class ContinuousScheduler:
245
245
 
246
246
  return profile
247
247
 
248
- def manual_start(self):
249
- # type: () -> None
248
+ def manual_start(self) -> None:
250
249
  if not self.sampled:
251
250
  return
252
251
 
@@ -255,48 +254,40 @@ class ContinuousScheduler:
255
254
 
256
255
  self.ensure_running()
257
256
 
258
- def manual_stop(self):
259
- # type: () -> None
257
+ def manual_stop(self) -> None:
260
258
  if self.lifecycle != "manual":
261
259
  return
262
260
 
263
261
  self.teardown()
264
262
 
265
- def ensure_running(self):
266
- # type: () -> None
263
+ def ensure_running(self) -> None:
267
264
  raise NotImplementedError
268
265
 
269
- def teardown(self):
270
- # type: () -> None
266
+ def teardown(self) -> None:
271
267
  raise NotImplementedError
272
268
 
273
- def pause(self):
274
- # type: () -> None
269
+ def pause(self) -> None:
275
270
  raise NotImplementedError
276
271
 
277
- def reset_buffer(self):
278
- # type: () -> None
272
+ def reset_buffer(self) -> None:
279
273
  self.buffer = ProfileBuffer(
280
274
  self.options, self.sdk_info, PROFILE_BUFFER_SECONDS, self.capture_func
281
275
  )
282
276
 
283
277
  @property
284
- def profiler_id(self):
285
- # type: () -> Union[str, None]
278
+ def profiler_id(self) -> Union[str, None]:
286
279
  if self.buffer is None:
287
280
  return None
288
281
  return self.buffer.profiler_id
289
282
 
290
- def make_sampler(self):
291
- # type: () -> Callable[..., None]
283
+ def make_sampler(self) -> Callable[..., bool]:
292
284
  cwd = os.getcwd()
293
285
 
294
286
  cache = LRUCache(max_size=256)
295
287
 
296
288
  if self.lifecycle == "trace":
297
289
 
298
- def _sample_stack(*args, **kwargs):
299
- # type: (*Any, **Any) -> None
290
+ def _sample_stack(*args: Any, **kwargs: Any) -> bool:
300
291
  """
301
292
  Take a sample of the stack on all the threads in the process.
302
293
  This should be called at a regular interval to collect samples.
@@ -304,8 +295,7 @@ class ContinuousScheduler:
304
295
 
305
296
  # no profiles taking place, so we can stop early
306
297
  if not self.new_profiles and not self.active_profiles:
307
- self.running = False
308
- return
298
+ return True
309
299
 
310
300
  # This is the number of profiles we want to pop off.
311
301
  # It's possible another thread adds a new profile to
@@ -328,7 +318,7 @@ class ContinuousScheduler:
328
318
  # For some reason, the frame we get doesn't have certain attributes.
329
319
  # When this happens, we abandon the current sample as it's bad.
330
320
  capture_internal_exception(sys.exc_info())
331
- return
321
+ return False
332
322
 
333
323
  # Move the new profiles into the active_profiles set.
334
324
  #
@@ -345,9 +335,7 @@ class ContinuousScheduler:
345
335
  inactive_profiles = []
346
336
 
347
337
  for profile in self.active_profiles:
348
- if profile.active:
349
- pass
350
- else:
338
+ if not profile.active:
351
339
  # If a profile is marked inactive, we buffer it
352
340
  # to `inactive_profiles` so it can be removed.
353
341
  # We cannot remove it here as it would result
@@ -360,10 +348,11 @@ class ContinuousScheduler:
360
348
  if self.buffer is not None:
361
349
  self.buffer.write(ts, sample)
362
350
 
351
+ return False
352
+
363
353
  else:
364
354
 
365
- def _sample_stack(*args, **kwargs):
366
- # type: (*Any, **Any) -> None
355
+ def _sample_stack(*args: Any, **kwargs: Any) -> bool:
367
356
  """
368
357
  Take a sample of the stack on all the threads in the process.
369
358
  This should be called at a regular interval to collect samples.
@@ -380,19 +369,20 @@ class ContinuousScheduler:
380
369
  # For some reason, the frame we get doesn't have certain attributes.
381
370
  # When this happens, we abandon the current sample as it's bad.
382
371
  capture_internal_exception(sys.exc_info())
383
- return
372
+ return False
384
373
 
385
374
  if self.buffer is not None:
386
375
  self.buffer.write(ts, sample)
387
376
 
377
+ return False
378
+
388
379
  return _sample_stack
389
380
 
390
- def run(self):
391
- # type: () -> None
381
+ def run(self) -> None:
392
382
  last = time.perf_counter()
393
383
 
394
384
  while self.running:
395
- self.sampler()
385
+ self.soft_shutdown = self.sampler()
396
386
 
397
387
  # some time may have elapsed since the last time
398
388
  # we sampled, so we need to account for that and
@@ -401,6 +391,15 @@ class ContinuousScheduler:
401
391
  if elapsed < self.interval:
402
392
  thread_sleep(self.interval - elapsed)
403
393
 
394
+ # the soft shutdown happens here to give it a chance
395
+ # for the profiler to be reused
396
+ if self.soft_shutdown:
397
+ self.running = False
398
+
399
+ # make sure to explicitly exit the profiler here or there might
400
+ # be multiple profilers at once
401
+ break
402
+
404
403
  # after sleeping, make sure to take the current
405
404
  # timestamp so we can use it next iteration
406
405
  last = time.perf_counter()
@@ -416,18 +415,24 @@ class ThreadContinuousScheduler(ContinuousScheduler):
416
415
  the sampler at a regular interval.
417
416
  """
418
417
 
419
- mode = "thread" # type: ContinuousProfilerMode
418
+ mode: ContinuousProfilerMode = "thread"
420
419
  name = "sentry.profiler.ThreadContinuousScheduler"
421
420
 
422
- def __init__(self, frequency, options, sdk_info, capture_func):
423
- # type: (int, Dict[str, Any], SDKInfo, Callable[[Envelope], None]) -> None
421
+ def __init__(
422
+ self,
423
+ frequency: int,
424
+ options: Dict[str, Any],
425
+ sdk_info: SDKInfo,
426
+ capture_func: Callable[[Envelope], None],
427
+ ) -> None:
424
428
  super().__init__(frequency, options, sdk_info, capture_func)
425
429
 
426
- self.thread = None # type: Optional[threading.Thread]
430
+ self.thread: Optional[threading.Thread] = None
427
431
  self.lock = threading.Lock()
428
432
 
429
- def ensure_running(self):
430
- # type: () -> None
433
+ def ensure_running(self) -> None:
434
+
435
+ self.soft_shutdown = False
431
436
 
432
437
  pid = os.getpid()
433
438
 
@@ -462,8 +467,7 @@ class ThreadContinuousScheduler(ContinuousScheduler):
462
467
  self.running = False
463
468
  self.thread = None
464
469
 
465
- def teardown(self):
466
- # type: () -> None
470
+ def teardown(self) -> None:
467
471
  if self.running:
468
472
  self.running = False
469
473
 
@@ -488,21 +492,28 @@ class GeventContinuousScheduler(ContinuousScheduler):
488
492
  results in a sample containing only the sampler's code.
489
493
  """
490
494
 
491
- mode = "gevent" # type: ContinuousProfilerMode
495
+ mode: ContinuousProfilerMode = "gevent"
492
496
 
493
- def __init__(self, frequency, options, sdk_info, capture_func):
494
- # type: (int, Dict[str, Any], SDKInfo, Callable[[Envelope], None]) -> None
497
+ def __init__(
498
+ self,
499
+ frequency: int,
500
+ options: Dict[str, Any],
501
+ sdk_info: SDKInfo,
502
+ capture_func: Callable[[Envelope], None],
503
+ ) -> None:
495
504
 
496
505
  if ThreadPool is None:
497
506
  raise ValueError("Profiler mode: {} is not available".format(self.mode))
498
507
 
499
508
  super().__init__(frequency, options, sdk_info, capture_func)
500
509
 
501
- self.thread = None # type: Optional[_ThreadPool]
510
+ self.thread: Optional[_ThreadPool] = None
502
511
  self.lock = threading.Lock()
503
512
 
504
- def ensure_running(self):
505
- # type: () -> None
513
+ def ensure_running(self) -> None:
514
+
515
+ self.soft_shutdown = False
516
+
506
517
  pid = os.getpid()
507
518
 
508
519
  # is running on the right process
@@ -532,8 +543,7 @@ class GeventContinuousScheduler(ContinuousScheduler):
532
543
  self.running = False
533
544
  self.thread = None
534
545
 
535
- def teardown(self):
536
- # type: () -> None
546
+ def teardown(self) -> None:
537
547
  if self.running:
538
548
  self.running = False
539
549
 
@@ -548,8 +558,13 @@ PROFILE_BUFFER_SECONDS = 60
548
558
 
549
559
 
550
560
  class ProfileBuffer:
551
- def __init__(self, options, sdk_info, buffer_size, capture_func):
552
- # type: (Dict[str, Any], SDKInfo, int, Callable[[Envelope], None]) -> None
561
+ def __init__(
562
+ self,
563
+ options: Dict[str, Any],
564
+ sdk_info: SDKInfo,
565
+ buffer_size: int,
566
+ capture_func: Callable[[Envelope], None],
567
+ ) -> None:
553
568
  self.options = options
554
569
  self.sdk_info = sdk_info
555
570
  self.buffer_size = buffer_size
@@ -571,8 +586,7 @@ class ProfileBuffer:
571
586
  datetime.now(timezone.utc).timestamp() - self.start_monotonic_time
572
587
  )
573
588
 
574
- def write(self, monotonic_time, sample):
575
- # type: (float, ExtractedSample) -> None
589
+ def write(self, monotonic_time: float, sample: ExtractedSample) -> None:
576
590
  if self.should_flush(monotonic_time):
577
591
  self.flush()
578
592
  self.chunk = ProfileChunk()
@@ -580,15 +594,13 @@ class ProfileBuffer:
580
594
 
581
595
  self.chunk.write(self.start_timestamp + monotonic_time, sample)
582
596
 
583
- def should_flush(self, monotonic_time):
584
- # type: (float) -> bool
597
+ def should_flush(self, monotonic_time: float) -> bool:
585
598
 
586
599
  # If the delta between the new monotonic time and the start monotonic time
587
600
  # exceeds the buffer size, it means we should flush the chunk
588
601
  return monotonic_time - self.start_monotonic_time >= self.buffer_size
589
602
 
590
- def flush(self):
591
- # type: () -> None
603
+ def flush(self) -> None:
592
604
  chunk = self.chunk.to_json(self.profiler_id, self.options, self.sdk_info)
593
605
  envelope = Envelope()
594
606
  envelope.add_profile_chunk(chunk)
@@ -596,18 +608,16 @@ class ProfileBuffer:
596
608
 
597
609
 
598
610
  class ProfileChunk:
599
- def __init__(self):
600
- # type: () -> None
611
+ def __init__(self) -> None:
601
612
  self.chunk_id = uuid.uuid4().hex
602
613
 
603
- self.indexed_frames = {} # type: Dict[FrameId, int]
604
- self.indexed_stacks = {} # type: Dict[StackId, int]
605
- self.frames = [] # type: List[ProcessedFrame]
606
- self.stacks = [] # type: List[ProcessedStack]
607
- self.samples = [] # type: List[ProcessedSample]
614
+ self.indexed_frames: Dict[FrameId, int] = {}
615
+ self.indexed_stacks: Dict[StackId, int] = {}
616
+ self.frames: List[ProcessedFrame] = []
617
+ self.stacks: List[ProcessedStack] = []
618
+ self.samples: List[ProcessedSample] = []
608
619
 
609
- def write(self, ts, sample):
610
- # type: (float, ExtractedSample) -> None
620
+ def write(self, ts: float, sample: ExtractedSample) -> None:
611
621
  for tid, (stack_id, frame_ids, frames) in sample:
612
622
  try:
613
623
  # Check if the stack is indexed first, this lets us skip
@@ -635,8 +645,9 @@ class ProfileChunk:
635
645
  # When this happens, we abandon the current sample as it's bad.
636
646
  capture_internal_exception(sys.exc_info())
637
647
 
638
- def to_json(self, profiler_id, options, sdk_info):
639
- # type: (str, Dict[str, Any], SDKInfo) -> Dict[str, Any]
648
+ def to_json(
649
+ self, profiler_id: str, options: Dict[str, Any], sdk_info: SDKInfo
650
+ ) -> Dict[str, Any]:
640
651
  profile = {
641
652
  "frames": self.frames,
642
653
  "stacks": self.stacks,