sentry-sdk 2.59.0a1__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.
Files changed (197) hide show
  1. sentry_sdk/__init__.py +70 -0
  2. sentry_sdk/_batcher.py +183 -0
  3. sentry_sdk/_compat.py +94 -0
  4. sentry_sdk/_init_implementation.py +79 -0
  5. sentry_sdk/_log_batcher.py +56 -0
  6. sentry_sdk/_lru_cache.py +43 -0
  7. sentry_sdk/_metrics_batcher.py +45 -0
  8. sentry_sdk/_queue.py +289 -0
  9. sentry_sdk/_span_batcher.py +242 -0
  10. sentry_sdk/_types.py +376 -0
  11. sentry_sdk/_werkzeug.py +103 -0
  12. sentry_sdk/ai/__init__.py +8 -0
  13. sentry_sdk/ai/_openai_completions_api.py +66 -0
  14. sentry_sdk/ai/_openai_responses_api.py +22 -0
  15. sentry_sdk/ai/consts.py +6 -0
  16. sentry_sdk/ai/monitoring.py +148 -0
  17. sentry_sdk/ai/utils.py +195 -0
  18. sentry_sdk/api.py +572 -0
  19. sentry_sdk/attachments.py +72 -0
  20. sentry_sdk/client.py +1407 -0
  21. sentry_sdk/consts.py +1648 -0
  22. sentry_sdk/crons/__init__.py +10 -0
  23. sentry_sdk/crons/api.py +60 -0
  24. sentry_sdk/crons/consts.py +4 -0
  25. sentry_sdk/crons/decorator.py +137 -0
  26. sentry_sdk/debug.py +37 -0
  27. sentry_sdk/envelope.py +338 -0
  28. sentry_sdk/feature_flags.py +66 -0
  29. sentry_sdk/hub.py +741 -0
  30. sentry_sdk/integrations/__init__.py +349 -0
  31. sentry_sdk/integrations/_asgi_common.py +139 -0
  32. sentry_sdk/integrations/_wsgi_common.py +263 -0
  33. sentry_sdk/integrations/aiohttp.py +362 -0
  34. sentry_sdk/integrations/anthropic.py +1040 -0
  35. sentry_sdk/integrations/argv.py +29 -0
  36. sentry_sdk/integrations/ariadne.py +160 -0
  37. sentry_sdk/integrations/arq.py +237 -0
  38. sentry_sdk/integrations/asgi.py +504 -0
  39. sentry_sdk/integrations/asyncio.py +268 -0
  40. sentry_sdk/integrations/asyncpg.py +218 -0
  41. sentry_sdk/integrations/atexit.py +52 -0
  42. sentry_sdk/integrations/aws_lambda.py +489 -0
  43. sentry_sdk/integrations/beam.py +168 -0
  44. sentry_sdk/integrations/boto3.py +138 -0
  45. sentry_sdk/integrations/bottle.py +211 -0
  46. sentry_sdk/integrations/celery/__init__.py +618 -0
  47. sentry_sdk/integrations/celery/beat.py +290 -0
  48. sentry_sdk/integrations/celery/utils.py +31 -0
  49. sentry_sdk/integrations/chalice.py +132 -0
  50. sentry_sdk/integrations/clickhouse_driver.py +172 -0
  51. sentry_sdk/integrations/cloud_resource_context.py +272 -0
  52. sentry_sdk/integrations/cohere.py +269 -0
  53. sentry_sdk/integrations/dedupe.py +63 -0
  54. sentry_sdk/integrations/django/__init__.py +784 -0
  55. sentry_sdk/integrations/django/asgi.py +244 -0
  56. sentry_sdk/integrations/django/caching.py +211 -0
  57. sentry_sdk/integrations/django/middleware.py +182 -0
  58. sentry_sdk/integrations/django/signals_handlers.py +90 -0
  59. sentry_sdk/integrations/django/tasks.py +40 -0
  60. sentry_sdk/integrations/django/templates.py +188 -0
  61. sentry_sdk/integrations/django/transactions.py +159 -0
  62. sentry_sdk/integrations/django/views.py +96 -0
  63. sentry_sdk/integrations/dramatiq.py +228 -0
  64. sentry_sdk/integrations/excepthook.py +81 -0
  65. sentry_sdk/integrations/executing.py +66 -0
  66. sentry_sdk/integrations/falcon.py +259 -0
  67. sentry_sdk/integrations/fastapi.py +161 -0
  68. sentry_sdk/integrations/flask.py +265 -0
  69. sentry_sdk/integrations/gcp.py +234 -0
  70. sentry_sdk/integrations/gnu_backtrace.py +96 -0
  71. sentry_sdk/integrations/google_genai/__init__.py +315 -0
  72. sentry_sdk/integrations/google_genai/consts.py +16 -0
  73. sentry_sdk/integrations/google_genai/streaming.py +160 -0
  74. sentry_sdk/integrations/google_genai/utils.py +708 -0
  75. sentry_sdk/integrations/gql.py +168 -0
  76. sentry_sdk/integrations/graphene.py +156 -0
  77. sentry_sdk/integrations/grpc/__init__.py +190 -0
  78. sentry_sdk/integrations/grpc/aio/__init__.py +7 -0
  79. sentry_sdk/integrations/grpc/aio/client.py +99 -0
  80. sentry_sdk/integrations/grpc/aio/server.py +102 -0
  81. sentry_sdk/integrations/grpc/client.py +98 -0
  82. sentry_sdk/integrations/grpc/consts.py +1 -0
  83. sentry_sdk/integrations/grpc/server.py +68 -0
  84. sentry_sdk/integrations/httpx.py +261 -0
  85. sentry_sdk/integrations/huey.py +166 -0
  86. sentry_sdk/integrations/huggingface_hub.py +388 -0
  87. sentry_sdk/integrations/langchain.py +1168 -0
  88. sentry_sdk/integrations/langgraph.py +375 -0
  89. sentry_sdk/integrations/launchdarkly.py +63 -0
  90. sentry_sdk/integrations/litellm.py +301 -0
  91. sentry_sdk/integrations/litestar.py +311 -0
  92. sentry_sdk/integrations/logging.py +479 -0
  93. sentry_sdk/integrations/loguru.py +209 -0
  94. sentry_sdk/integrations/mcp.py +673 -0
  95. sentry_sdk/integrations/modules.py +27 -0
  96. sentry_sdk/integrations/openai.py +1326 -0
  97. sentry_sdk/integrations/openai_agents/__init__.py +242 -0
  98. sentry_sdk/integrations/openai_agents/consts.py +1 -0
  99. sentry_sdk/integrations/openai_agents/patches/__init__.py +10 -0
  100. sentry_sdk/integrations/openai_agents/patches/agent_run.py +255 -0
  101. sentry_sdk/integrations/openai_agents/patches/error_tracing.py +67 -0
  102. sentry_sdk/integrations/openai_agents/patches/models.py +191 -0
  103. sentry_sdk/integrations/openai_agents/patches/runner.py +172 -0
  104. sentry_sdk/integrations/openai_agents/patches/tools.py +71 -0
  105. sentry_sdk/integrations/openai_agents/spans/__init__.py +9 -0
  106. sentry_sdk/integrations/openai_agents/spans/agent_workflow.py +19 -0
  107. sentry_sdk/integrations/openai_agents/spans/ai_client.py +68 -0
  108. sentry_sdk/integrations/openai_agents/spans/execute_tool.py +55 -0
  109. sentry_sdk/integrations/openai_agents/spans/handoff.py +25 -0
  110. sentry_sdk/integrations/openai_agents/spans/invoke_agent.py +111 -0
  111. sentry_sdk/integrations/openai_agents/utils.py +232 -0
  112. sentry_sdk/integrations/openfeature.py +34 -0
  113. sentry_sdk/integrations/opentelemetry/__init__.py +7 -0
  114. sentry_sdk/integrations/opentelemetry/consts.py +9 -0
  115. sentry_sdk/integrations/opentelemetry/integration.py +55 -0
  116. sentry_sdk/integrations/opentelemetry/propagator.py +128 -0
  117. sentry_sdk/integrations/opentelemetry/span_processor.py +396 -0
  118. sentry_sdk/integrations/otlp.py +226 -0
  119. sentry_sdk/integrations/pure_eval.py +137 -0
  120. sentry_sdk/integrations/pydantic_ai/__init__.py +172 -0
  121. sentry_sdk/integrations/pydantic_ai/consts.py +1 -0
  122. sentry_sdk/integrations/pydantic_ai/patches/__init__.py +3 -0
  123. sentry_sdk/integrations/pydantic_ai/patches/agent_run.py +228 -0
  124. sentry_sdk/integrations/pydantic_ai/patches/graph_nodes.py +106 -0
  125. sentry_sdk/integrations/pydantic_ai/patches/tools.py +178 -0
  126. sentry_sdk/integrations/pydantic_ai/spans/__init__.py +3 -0
  127. sentry_sdk/integrations/pydantic_ai/spans/ai_client.py +278 -0
  128. sentry_sdk/integrations/pydantic_ai/spans/execute_tool.py +58 -0
  129. sentry_sdk/integrations/pydantic_ai/spans/invoke_agent.py +149 -0
  130. sentry_sdk/integrations/pydantic_ai/spans/utils.py +49 -0
  131. sentry_sdk/integrations/pydantic_ai/utils.py +217 -0
  132. sentry_sdk/integrations/pymongo.py +211 -0
  133. sentry_sdk/integrations/pyramid.py +223 -0
  134. sentry_sdk/integrations/pyreqwest.py +136 -0
  135. sentry_sdk/integrations/quart.py +233 -0
  136. sentry_sdk/integrations/ray.py +181 -0
  137. sentry_sdk/integrations/redis/__init__.py +50 -0
  138. sentry_sdk/integrations/redis/_async_common.py +160 -0
  139. sentry_sdk/integrations/redis/_sync_common.py +160 -0
  140. sentry_sdk/integrations/redis/consts.py +19 -0
  141. sentry_sdk/integrations/redis/modules/__init__.py +0 -0
  142. sentry_sdk/integrations/redis/modules/caches.py +135 -0
  143. sentry_sdk/integrations/redis/modules/queries.py +86 -0
  144. sentry_sdk/integrations/redis/rb.py +31 -0
  145. sentry_sdk/integrations/redis/redis.py +67 -0
  146. sentry_sdk/integrations/redis/redis_cluster.py +112 -0
  147. sentry_sdk/integrations/redis/redis_py_cluster_legacy.py +49 -0
  148. sentry_sdk/integrations/redis/utils.py +159 -0
  149. sentry_sdk/integrations/rq.py +184 -0
  150. sentry_sdk/integrations/rust_tracing.py +272 -0
  151. sentry_sdk/integrations/sanic.py +354 -0
  152. sentry_sdk/integrations/serverless.py +65 -0
  153. sentry_sdk/integrations/socket.py +98 -0
  154. sentry_sdk/integrations/spark/__init__.py +4 -0
  155. sentry_sdk/integrations/spark/spark_driver.py +278 -0
  156. sentry_sdk/integrations/spark/spark_worker.py +111 -0
  157. sentry_sdk/integrations/sqlalchemy.py +188 -0
  158. sentry_sdk/integrations/starlette.py +808 -0
  159. sentry_sdk/integrations/starlite.py +290 -0
  160. sentry_sdk/integrations/statsig.py +37 -0
  161. sentry_sdk/integrations/stdlib.py +368 -0
  162. sentry_sdk/integrations/strawberry.py +398 -0
  163. sentry_sdk/integrations/sys_exit.py +65 -0
  164. sentry_sdk/integrations/threading.py +200 -0
  165. sentry_sdk/integrations/tornado.py +224 -0
  166. sentry_sdk/integrations/trytond.py +53 -0
  167. sentry_sdk/integrations/typer.py +62 -0
  168. sentry_sdk/integrations/unleash.py +33 -0
  169. sentry_sdk/integrations/unraisablehook.py +52 -0
  170. sentry_sdk/integrations/wsgi.py +434 -0
  171. sentry_sdk/logger.py +88 -0
  172. sentry_sdk/metrics.py +62 -0
  173. sentry_sdk/monitor.py +139 -0
  174. sentry_sdk/profiler/__init__.py +49 -0
  175. sentry_sdk/profiler/continuous_profiler.py +710 -0
  176. sentry_sdk/profiler/transaction_profiler.py +809 -0
  177. sentry_sdk/profiler/utils.py +189 -0
  178. sentry_sdk/py.typed +0 -0
  179. sentry_sdk/scope.py +2180 -0
  180. sentry_sdk/scrubber.py +172 -0
  181. sentry_sdk/serializer.py +390 -0
  182. sentry_sdk/session.py +169 -0
  183. sentry_sdk/sessions.py +269 -0
  184. sentry_sdk/spotlight.py +331 -0
  185. sentry_sdk/traces.py +771 -0
  186. sentry_sdk/tracing.py +1470 -0
  187. sentry_sdk/tracing_utils.py +1728 -0
  188. sentry_sdk/transport.py +1168 -0
  189. sentry_sdk/types.py +52 -0
  190. sentry_sdk/utils.py +2153 -0
  191. sentry_sdk/worker.py +311 -0
  192. sentry_sdk-2.59.0a1.dist-info/METADATA +269 -0
  193. sentry_sdk-2.59.0a1.dist-info/RECORD +197 -0
  194. sentry_sdk-2.59.0a1.dist-info/WHEEL +5 -0
  195. sentry_sdk-2.59.0a1.dist-info/entry_points.txt +2 -0
  196. sentry_sdk-2.59.0a1.dist-info/licenses/LICENSE +21 -0
  197. sentry_sdk-2.59.0a1.dist-info/top_level.txt +1 -0
sentry_sdk/__init__.py ADDED
@@ -0,0 +1,70 @@
1
+ from sentry_sdk import profiler
2
+ from sentry_sdk import metrics
3
+ from sentry_sdk.scope import Scope
4
+ from sentry_sdk.transport import Transport, HttpTransport
5
+ from sentry_sdk.client import Client
6
+
7
+ from sentry_sdk.api import * # noqa
8
+ from sentry_sdk.consts import VERSION
9
+
10
+ __all__ = [ # noqa
11
+ "Hub",
12
+ "Scope",
13
+ "Client",
14
+ "Transport",
15
+ "HttpTransport",
16
+ "VERSION",
17
+ "integrations",
18
+ # From sentry_sdk.api
19
+ "init",
20
+ "add_attachment",
21
+ "add_breadcrumb",
22
+ "capture_event",
23
+ "capture_exception",
24
+ "capture_message",
25
+ "configure_scope",
26
+ "continue_trace",
27
+ "flush",
28
+ "flush_async",
29
+ "get_baggage",
30
+ "get_client",
31
+ "get_global_scope",
32
+ "get_isolation_scope",
33
+ "get_current_scope",
34
+ "get_current_span",
35
+ "get_traceparent",
36
+ "is_initialized",
37
+ "isolation_scope",
38
+ "last_event_id",
39
+ "new_scope",
40
+ "push_scope",
41
+ "remove_attribute",
42
+ "set_attribute",
43
+ "set_context",
44
+ "set_extra",
45
+ "set_level",
46
+ "set_measurement",
47
+ "set_tag",
48
+ "set_tags",
49
+ "set_user",
50
+ "start_span",
51
+ "start_transaction",
52
+ "trace",
53
+ "monitor",
54
+ "logger",
55
+ "metrics",
56
+ "profiler",
57
+ "start_session",
58
+ "end_session",
59
+ "set_transaction_name",
60
+ "update_current_span",
61
+ ]
62
+
63
+ # Initialize the debug support after everything is loaded
64
+ from sentry_sdk.debug import init_debug_support
65
+
66
+ init_debug_support()
67
+ del init_debug_support
68
+
69
+ # circular imports
70
+ from sentry_sdk.hub import Hub
sentry_sdk/_batcher.py ADDED
@@ -0,0 +1,183 @@
1
+ import os
2
+ import random
3
+ import threading
4
+ from datetime import datetime, timezone
5
+ from typing import TYPE_CHECKING, TypeVar, Generic
6
+ import weakref
7
+
8
+ from sentry_sdk.utils import format_timestamp
9
+ from sentry_sdk.envelope import Envelope, Item, PayloadRef
10
+
11
+ if TYPE_CHECKING:
12
+ from typing import Optional, Callable, Any
13
+
14
+ T = TypeVar("T")
15
+
16
+
17
+ class Batcher(Generic[T]):
18
+ MAX_BEFORE_FLUSH = 100
19
+ MAX_BEFORE_DROP = 1_000
20
+ FLUSH_WAIT_TIME = 5.0
21
+
22
+ TYPE = ""
23
+ CONTENT_TYPE = ""
24
+
25
+ def __init__(
26
+ self,
27
+ capture_func: "Callable[[Envelope], None]",
28
+ record_lost_func: "Callable[..., None]",
29
+ ) -> None:
30
+ self._buffer: "list[T]" = []
31
+ self._capture_func = capture_func
32
+ self._record_lost_func = record_lost_func
33
+ self._running = True
34
+ self._lock = threading.Lock()
35
+ self._active: "threading.local" = threading.local()
36
+
37
+ self._flush_event: "threading.Event" = threading.Event()
38
+
39
+ self._flusher: "Optional[threading.Thread]" = None
40
+ self._flusher_pid: "Optional[int]" = None
41
+
42
+ # See https://github.com/getsentry/sentry-python/blob/051cc01640a29bfd64b1f1e2e3414c02f027dd1b/sentry_sdk/monitor.py#L41-L50
43
+ if hasattr(os, "register_at_fork"):
44
+ weak_reset = weakref.WeakMethod(self._reset_thread_state)
45
+
46
+ def _reset_in_child() -> None:
47
+ method = weak_reset()
48
+ if method is not None:
49
+ method()
50
+
51
+ os.register_at_fork(after_in_child=_reset_in_child)
52
+
53
+ def _reset_thread_state(self) -> None:
54
+ self._buffer = []
55
+ self._running = True
56
+ self._lock = threading.Lock()
57
+ self._active = threading.local()
58
+ self._flush_event = threading.Event()
59
+ self._flusher = None
60
+ self._flusher_pid = None
61
+
62
+ def _ensure_thread(self) -> bool:
63
+ """For forking processes we might need to restart this thread.
64
+ This ensures that our process actually has that thread running.
65
+ """
66
+ if not self._running:
67
+ return False
68
+
69
+ pid = os.getpid()
70
+ if self._flusher_pid == pid:
71
+ return True
72
+
73
+ with self._lock:
74
+ # Recheck to make sure another thread didn't get here and start the
75
+ # the flusher in the meantime
76
+ if self._flusher_pid == pid:
77
+ return True
78
+
79
+ self._flusher_pid = pid
80
+
81
+ self._flusher = threading.Thread(target=self._flush_loop)
82
+ self._flusher.daemon = True
83
+
84
+ try:
85
+ self._flusher.start()
86
+ except RuntimeError:
87
+ # Unfortunately at this point the interpreter is in a state that no
88
+ # longer allows us to spawn a thread and we have to bail.
89
+ self._running = False
90
+ return False
91
+
92
+ return True
93
+
94
+ def _flush_loop(self) -> None:
95
+ # Mark the flush-loop thread as active for its entire lifetime so
96
+ # that any re-entrant add() triggered by GC warnings during wait(),
97
+ # flush(), or Event operations is silently dropped instead of
98
+ # deadlocking on internal locks.
99
+ self._active.flag = True
100
+ while self._running:
101
+ self._flush_event.wait(self.FLUSH_WAIT_TIME + random.random())
102
+ self._flush_event.clear()
103
+ self._flush()
104
+
105
+ def add(self, item: "T") -> None:
106
+ # Bail out if the current thread is already executing batcher code.
107
+ # This prevents deadlocks when code running inside the batcher (e.g.
108
+ # _add_to_envelope during flush, or _flush_event.wait/set) triggers
109
+ # a GC-emitted warning that routes back through the logging
110
+ # integration into add().
111
+ if getattr(self._active, "flag", False):
112
+ return None
113
+
114
+ self._active.flag = True
115
+ try:
116
+ if not self._ensure_thread() or self._flusher is None:
117
+ return None
118
+
119
+ with self._lock:
120
+ if len(self._buffer) >= self.MAX_BEFORE_DROP:
121
+ self._record_lost(item)
122
+ return None
123
+
124
+ self._buffer.append(item)
125
+ if len(self._buffer) >= self.MAX_BEFORE_FLUSH:
126
+ self._flush_event.set()
127
+ finally:
128
+ self._active.flag = False
129
+
130
+ def kill(self) -> None:
131
+ if self._flusher is None:
132
+ return
133
+
134
+ self._running = False
135
+ self._flush_event.set()
136
+ self._flusher = None
137
+
138
+ def flush(self) -> None:
139
+ was_active = getattr(self._active, "flag", False)
140
+ self._active.flag = True
141
+ try:
142
+ self._flush()
143
+ finally:
144
+ self._active.flag = was_active
145
+
146
+ def _add_to_envelope(self, envelope: "Envelope") -> None:
147
+ envelope.add_item(
148
+ Item(
149
+ type=self.TYPE,
150
+ content_type=self.CONTENT_TYPE,
151
+ headers={
152
+ "item_count": len(self._buffer),
153
+ },
154
+ payload=PayloadRef(
155
+ json={
156
+ "items": [
157
+ self._to_transport_format(item) for item in self._buffer
158
+ ]
159
+ }
160
+ ),
161
+ )
162
+ )
163
+
164
+ def _flush(self) -> "Optional[Envelope]":
165
+ envelope = Envelope(
166
+ headers={"sent_at": format_timestamp(datetime.now(timezone.utc))}
167
+ )
168
+ with self._lock:
169
+ if len(self._buffer) == 0:
170
+ return None
171
+
172
+ self._add_to_envelope(envelope)
173
+ self._buffer.clear()
174
+
175
+ self._capture_func(envelope)
176
+ return envelope
177
+
178
+ def _record_lost(self, item: "T") -> None:
179
+ pass
180
+
181
+ @staticmethod
182
+ def _to_transport_format(item: "T") -> "Any":
183
+ pass
sentry_sdk/_compat.py ADDED
@@ -0,0 +1,94 @@
1
+ import sys
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ if TYPE_CHECKING:
6
+ from typing import Any
7
+ from typing import TypeVar
8
+
9
+ T = TypeVar("T")
10
+
11
+
12
+ PY37 = sys.version_info[0] == 3 and sys.version_info[1] >= 7
13
+ PY38 = sys.version_info[0] == 3 and sys.version_info[1] >= 8
14
+ PY310 = sys.version_info[0] == 3 and sys.version_info[1] >= 10
15
+ PY311 = sys.version_info[0] == 3 and sys.version_info[1] >= 11
16
+
17
+
18
+ def with_metaclass(meta: "Any", *bases: "Any") -> "Any":
19
+ class MetaClass(type):
20
+ def __new__(metacls: "Any", name: "Any", this_bases: "Any", d: "Any") -> "Any":
21
+ return meta(name, bases, d)
22
+
23
+ return type.__new__(MetaClass, "temporary_class", (), {})
24
+
25
+
26
+ def check_uwsgi_thread_support() -> bool:
27
+ # We check two things here:
28
+ #
29
+ # 1. uWSGI doesn't run in threaded mode by default -- issue a warning if
30
+ # that's the case.
31
+ #
32
+ # 2. Additionally, if uWSGI is running in preforking mode (default), it needs
33
+ # the --py-call-uwsgi-fork-hooks option for the SDK to work properly. This
34
+ # is because any background threads spawned before the main process is
35
+ # forked are NOT CLEANED UP IN THE CHILDREN BY DEFAULT even if
36
+ # --enable-threads is on. One has to explicitly provide
37
+ # --py-call-uwsgi-fork-hooks to force uWSGI to run regular cpython
38
+ # after-fork hooks that take care of cleaning up stale thread data.
39
+ try:
40
+ from uwsgi import opt # type: ignore
41
+ except ImportError:
42
+ return True
43
+
44
+ from sentry_sdk.consts import FALSE_VALUES
45
+
46
+ def enabled(option: str) -> bool:
47
+ value = opt.get(option, False)
48
+ if isinstance(value, bool):
49
+ return value
50
+
51
+ if isinstance(value, bytes):
52
+ try:
53
+ value = value.decode()
54
+ except Exception:
55
+ pass
56
+
57
+ return value and str(value).lower() not in FALSE_VALUES
58
+
59
+ # When `threads` is passed in as a uwsgi option,
60
+ # `enable-threads` is implied on.
61
+ threads_enabled = "threads" in opt or enabled("enable-threads")
62
+ fork_hooks_on = enabled("py-call-uwsgi-fork-hooks")
63
+ lazy_mode = enabled("lazy-apps") or enabled("lazy")
64
+
65
+ if lazy_mode and not threads_enabled:
66
+ from warnings import warn
67
+
68
+ warn(
69
+ Warning(
70
+ "IMPORTANT: "
71
+ "We detected the use of uWSGI without thread support. "
72
+ "This might lead to unexpected issues. "
73
+ 'Please run uWSGI with "--enable-threads" for full support.'
74
+ )
75
+ )
76
+
77
+ return False
78
+
79
+ elif not lazy_mode and (not threads_enabled or not fork_hooks_on):
80
+ from warnings import warn
81
+
82
+ warn(
83
+ Warning(
84
+ "IMPORTANT: "
85
+ "We detected the use of uWSGI in preforking mode without "
86
+ "thread support. This might lead to crashing workers. "
87
+ 'Please run uWSGI with both "--enable-threads" and '
88
+ '"--py-call-uwsgi-fork-hooks" for full support.'
89
+ )
90
+ )
91
+
92
+ return False
93
+
94
+ return True
@@ -0,0 +1,79 @@
1
+ import warnings
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ import sentry_sdk
6
+
7
+ if TYPE_CHECKING:
8
+ from typing import Any, ContextManager, Optional
9
+
10
+ import sentry_sdk.consts
11
+
12
+
13
+ class _InitGuard:
14
+ _CONTEXT_MANAGER_DEPRECATION_WARNING_MESSAGE = (
15
+ "Using the return value of sentry_sdk.init as a context manager "
16
+ "and manually calling the __enter__ and __exit__ methods on the "
17
+ "return value are deprecated. We are no longer maintaining this "
18
+ "functionality, and we will remove it in the next major release."
19
+ )
20
+
21
+ def __init__(self, client: "sentry_sdk.Client") -> None:
22
+ self._client = client
23
+
24
+ def __enter__(self) -> "_InitGuard":
25
+ warnings.warn(
26
+ self._CONTEXT_MANAGER_DEPRECATION_WARNING_MESSAGE,
27
+ stacklevel=2,
28
+ category=DeprecationWarning,
29
+ )
30
+
31
+ return self
32
+
33
+ def __exit__(self, exc_type: "Any", exc_value: "Any", tb: "Any") -> None:
34
+ warnings.warn(
35
+ self._CONTEXT_MANAGER_DEPRECATION_WARNING_MESSAGE,
36
+ stacklevel=2,
37
+ category=DeprecationWarning,
38
+ )
39
+
40
+ c = self._client
41
+ if c is not None:
42
+ c.close()
43
+
44
+
45
+ def _check_python_deprecations() -> None:
46
+ # Since we're likely to deprecate Python versions in the future, I'm keeping
47
+ # this handy function around. Use this to detect the Python version used and
48
+ # to output logger.warning()s if it's deprecated.
49
+ pass
50
+
51
+
52
+ def _init(*args: "Optional[str]", **kwargs: "Any") -> "ContextManager[Any]":
53
+ """Initializes the SDK and optionally integrations.
54
+
55
+ This takes the same arguments as the client constructor.
56
+ """
57
+ client = sentry_sdk.Client(*args, **kwargs)
58
+ sentry_sdk.get_global_scope().set_client(client)
59
+ _check_python_deprecations()
60
+ rv = _InitGuard(client)
61
+ return rv
62
+
63
+
64
+ if TYPE_CHECKING:
65
+ # Make mypy, PyCharm and other static analyzers think `init` is a type to
66
+ # have nicer autocompletion for params.
67
+ #
68
+ # Use `ClientConstructor` to define the argument types of `init` and
69
+ # `ContextManager[Any]` to tell static analyzers about the return type.
70
+
71
+ class init(sentry_sdk.consts.ClientConstructor, _InitGuard): # noqa: N801
72
+ pass
73
+
74
+ else:
75
+ # Alias `init` for actual usage. Go through the lambda indirection to throw
76
+ # PyCharm off of the weakly typed signature (it would otherwise discover
77
+ # both the weakly typed signature of `_init` and our faked `init` type).
78
+
79
+ init = (lambda: _init)()
@@ -0,0 +1,56 @@
1
+ from typing import TYPE_CHECKING
2
+
3
+ from sentry_sdk._batcher import Batcher
4
+ from sentry_sdk.utils import serialize_attribute
5
+ from sentry_sdk.envelope import Item, PayloadRef
6
+
7
+ if TYPE_CHECKING:
8
+ from typing import Any
9
+ from sentry_sdk._types import Log
10
+
11
+
12
+ class LogBatcher(Batcher["Log"]):
13
+ MAX_BEFORE_FLUSH = 100
14
+ MAX_BEFORE_DROP = 1_000
15
+ FLUSH_WAIT_TIME = 5.0
16
+
17
+ TYPE = "log"
18
+ CONTENT_TYPE = "application/vnd.sentry.items.log+json"
19
+
20
+ @staticmethod
21
+ def _to_transport_format(item: "Log") -> "Any":
22
+ if "sentry.severity_number" not in item["attributes"]:
23
+ item["attributes"]["sentry.severity_number"] = item["severity_number"]
24
+ if "sentry.severity_text" not in item["attributes"]:
25
+ item["attributes"]["sentry.severity_text"] = item["severity_text"]
26
+
27
+ res = {
28
+ "timestamp": int(item["time_unix_nano"]) / 1.0e9,
29
+ "trace_id": item.get("trace_id", "00000000-0000-0000-0000-000000000000"),
30
+ "span_id": item.get("span_id"),
31
+ "level": str(item["severity_text"]),
32
+ "body": str(item["body"]),
33
+ "attributes": {
34
+ k: serialize_attribute(v) for (k, v) in item["attributes"].items()
35
+ },
36
+ }
37
+
38
+ return res
39
+
40
+ def _record_lost(self, item: "Log") -> None:
41
+ # Construct log envelope item without sending it to report lost bytes
42
+ log_item = Item(
43
+ type=self.TYPE,
44
+ content_type=self.CONTENT_TYPE,
45
+ headers={
46
+ "item_count": 1,
47
+ },
48
+ payload=PayloadRef(json={"items": [self._to_transport_format(item)]}),
49
+ )
50
+
51
+ self._record_lost_func(
52
+ reason="queue_overflow",
53
+ data_category="log_item",
54
+ item=log_item,
55
+ quantity=1,
56
+ )
@@ -0,0 +1,43 @@
1
+ from typing import TYPE_CHECKING
2
+
3
+ if TYPE_CHECKING:
4
+ from typing import Any
5
+
6
+
7
+ _SENTINEL = object()
8
+
9
+
10
+ class LRUCache:
11
+ def __init__(self, max_size: int) -> None:
12
+ if max_size <= 0:
13
+ raise AssertionError(f"invalid max_size: {max_size}")
14
+ self.max_size = max_size
15
+ self._data: "dict[Any, Any]" = {}
16
+ self.hits = self.misses = 0
17
+ self.full = False
18
+
19
+ def set(self, key: "Any", value: "Any") -> None:
20
+ current = self._data.pop(key, _SENTINEL)
21
+ if current is not _SENTINEL:
22
+ self._data[key] = value
23
+ elif self.full:
24
+ self._data.pop(next(iter(self._data)))
25
+ self._data[key] = value
26
+ else:
27
+ self._data[key] = value
28
+ self.full = len(self._data) >= self.max_size
29
+
30
+ def get(self, key: "Any", default: "Any" = None) -> "Any":
31
+ try:
32
+ ret = self._data.pop(key)
33
+ except KeyError:
34
+ self.misses += 1
35
+ ret = default
36
+ else:
37
+ self.hits += 1
38
+ self._data[key] = ret
39
+
40
+ return ret
41
+
42
+ def get_all(self) -> "list[tuple[Any, Any]]":
43
+ return list(self._data.items())
@@ -0,0 +1,45 @@
1
+ from typing import TYPE_CHECKING
2
+
3
+ from sentry_sdk._batcher import Batcher
4
+ from sentry_sdk.utils import serialize_attribute
5
+
6
+ if TYPE_CHECKING:
7
+ from typing import Any
8
+ from sentry_sdk._types import Metric
9
+
10
+
11
+ class MetricsBatcher(Batcher["Metric"]):
12
+ MAX_BEFORE_FLUSH = 1000
13
+ MAX_BEFORE_DROP = 10_000
14
+ FLUSH_WAIT_TIME = 5.0
15
+
16
+ TYPE = "trace_metric"
17
+ CONTENT_TYPE = "application/vnd.sentry.items.trace-metric+json"
18
+
19
+ @staticmethod
20
+ def _to_transport_format(item: "Metric") -> "Any":
21
+ res = {
22
+ "timestamp": item["timestamp"],
23
+ "trace_id": item["trace_id"],
24
+ "name": item["name"],
25
+ "type": item["type"],
26
+ "value": item["value"],
27
+ "attributes": {
28
+ k: serialize_attribute(v) for (k, v) in item["attributes"].items()
29
+ },
30
+ }
31
+
32
+ if item.get("span_id") is not None:
33
+ res["span_id"] = item["span_id"]
34
+
35
+ if item.get("unit") is not None:
36
+ res["unit"] = item["unit"]
37
+
38
+ return res
39
+
40
+ def _record_lost(self, item: "Metric") -> None:
41
+ self._record_lost_func(
42
+ reason="queue_overflow",
43
+ data_category="trace_metric",
44
+ quantity=1,
45
+ )