sentry-sdk 0.7.5__py2.py3-none-any.whl → 2.46.0__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.
Files changed (193) hide show
  1. sentry_sdk/__init__.py +48 -30
  2. sentry_sdk/_compat.py +74 -61
  3. sentry_sdk/_init_implementation.py +84 -0
  4. sentry_sdk/_log_batcher.py +172 -0
  5. sentry_sdk/_lru_cache.py +47 -0
  6. sentry_sdk/_metrics_batcher.py +167 -0
  7. sentry_sdk/_queue.py +289 -0
  8. sentry_sdk/_types.py +338 -0
  9. sentry_sdk/_werkzeug.py +98 -0
  10. sentry_sdk/ai/__init__.py +7 -0
  11. sentry_sdk/ai/monitoring.py +137 -0
  12. sentry_sdk/ai/utils.py +144 -0
  13. sentry_sdk/api.py +496 -80
  14. sentry_sdk/attachments.py +75 -0
  15. sentry_sdk/client.py +1023 -103
  16. sentry_sdk/consts.py +1438 -66
  17. sentry_sdk/crons/__init__.py +10 -0
  18. sentry_sdk/crons/api.py +62 -0
  19. sentry_sdk/crons/consts.py +4 -0
  20. sentry_sdk/crons/decorator.py +135 -0
  21. sentry_sdk/debug.py +15 -14
  22. sentry_sdk/envelope.py +369 -0
  23. sentry_sdk/feature_flags.py +71 -0
  24. sentry_sdk/hub.py +611 -280
  25. sentry_sdk/integrations/__init__.py +276 -49
  26. sentry_sdk/integrations/_asgi_common.py +108 -0
  27. sentry_sdk/integrations/_wsgi_common.py +180 -44
  28. sentry_sdk/integrations/aiohttp.py +291 -42
  29. sentry_sdk/integrations/anthropic.py +439 -0
  30. sentry_sdk/integrations/argv.py +9 -8
  31. sentry_sdk/integrations/ariadne.py +161 -0
  32. sentry_sdk/integrations/arq.py +247 -0
  33. sentry_sdk/integrations/asgi.py +341 -0
  34. sentry_sdk/integrations/asyncio.py +144 -0
  35. sentry_sdk/integrations/asyncpg.py +208 -0
  36. sentry_sdk/integrations/atexit.py +17 -10
  37. sentry_sdk/integrations/aws_lambda.py +377 -62
  38. sentry_sdk/integrations/beam.py +176 -0
  39. sentry_sdk/integrations/boto3.py +137 -0
  40. sentry_sdk/integrations/bottle.py +221 -0
  41. sentry_sdk/integrations/celery/__init__.py +529 -0
  42. sentry_sdk/integrations/celery/beat.py +293 -0
  43. sentry_sdk/integrations/celery/utils.py +43 -0
  44. sentry_sdk/integrations/chalice.py +134 -0
  45. sentry_sdk/integrations/clickhouse_driver.py +177 -0
  46. sentry_sdk/integrations/cloud_resource_context.py +280 -0
  47. sentry_sdk/integrations/cohere.py +274 -0
  48. sentry_sdk/integrations/dedupe.py +48 -14
  49. sentry_sdk/integrations/django/__init__.py +584 -191
  50. sentry_sdk/integrations/django/asgi.py +245 -0
  51. sentry_sdk/integrations/django/caching.py +204 -0
  52. sentry_sdk/integrations/django/middleware.py +187 -0
  53. sentry_sdk/integrations/django/signals_handlers.py +91 -0
  54. sentry_sdk/integrations/django/templates.py +79 -5
  55. sentry_sdk/integrations/django/transactions.py +49 -22
  56. sentry_sdk/integrations/django/views.py +96 -0
  57. sentry_sdk/integrations/dramatiq.py +226 -0
  58. sentry_sdk/integrations/excepthook.py +50 -13
  59. sentry_sdk/integrations/executing.py +67 -0
  60. sentry_sdk/integrations/falcon.py +272 -0
  61. sentry_sdk/integrations/fastapi.py +141 -0
  62. sentry_sdk/integrations/flask.py +142 -88
  63. sentry_sdk/integrations/gcp.py +239 -0
  64. sentry_sdk/integrations/gnu_backtrace.py +99 -0
  65. sentry_sdk/integrations/google_genai/__init__.py +301 -0
  66. sentry_sdk/integrations/google_genai/consts.py +16 -0
  67. sentry_sdk/integrations/google_genai/streaming.py +155 -0
  68. sentry_sdk/integrations/google_genai/utils.py +576 -0
  69. sentry_sdk/integrations/gql.py +162 -0
  70. sentry_sdk/integrations/graphene.py +151 -0
  71. sentry_sdk/integrations/grpc/__init__.py +168 -0
  72. sentry_sdk/integrations/grpc/aio/__init__.py +7 -0
  73. sentry_sdk/integrations/grpc/aio/client.py +95 -0
  74. sentry_sdk/integrations/grpc/aio/server.py +100 -0
  75. sentry_sdk/integrations/grpc/client.py +91 -0
  76. sentry_sdk/integrations/grpc/consts.py +1 -0
  77. sentry_sdk/integrations/grpc/server.py +66 -0
  78. sentry_sdk/integrations/httpx.py +178 -0
  79. sentry_sdk/integrations/huey.py +174 -0
  80. sentry_sdk/integrations/huggingface_hub.py +378 -0
  81. sentry_sdk/integrations/langchain.py +1132 -0
  82. sentry_sdk/integrations/langgraph.py +337 -0
  83. sentry_sdk/integrations/launchdarkly.py +61 -0
  84. sentry_sdk/integrations/litellm.py +287 -0
  85. sentry_sdk/integrations/litestar.py +315 -0
  86. sentry_sdk/integrations/logging.py +307 -96
  87. sentry_sdk/integrations/loguru.py +213 -0
  88. sentry_sdk/integrations/mcp.py +566 -0
  89. sentry_sdk/integrations/modules.py +14 -31
  90. sentry_sdk/integrations/openai.py +725 -0
  91. sentry_sdk/integrations/openai_agents/__init__.py +61 -0
  92. sentry_sdk/integrations/openai_agents/consts.py +1 -0
  93. sentry_sdk/integrations/openai_agents/patches/__init__.py +5 -0
  94. sentry_sdk/integrations/openai_agents/patches/agent_run.py +140 -0
  95. sentry_sdk/integrations/openai_agents/patches/error_tracing.py +77 -0
  96. sentry_sdk/integrations/openai_agents/patches/models.py +50 -0
  97. sentry_sdk/integrations/openai_agents/patches/runner.py +45 -0
  98. sentry_sdk/integrations/openai_agents/patches/tools.py +77 -0
  99. sentry_sdk/integrations/openai_agents/spans/__init__.py +5 -0
  100. sentry_sdk/integrations/openai_agents/spans/agent_workflow.py +21 -0
  101. sentry_sdk/integrations/openai_agents/spans/ai_client.py +42 -0
  102. sentry_sdk/integrations/openai_agents/spans/execute_tool.py +48 -0
  103. sentry_sdk/integrations/openai_agents/spans/handoff.py +19 -0
  104. sentry_sdk/integrations/openai_agents/spans/invoke_agent.py +86 -0
  105. sentry_sdk/integrations/openai_agents/utils.py +199 -0
  106. sentry_sdk/integrations/openfeature.py +35 -0
  107. sentry_sdk/integrations/opentelemetry/__init__.py +7 -0
  108. sentry_sdk/integrations/opentelemetry/consts.py +5 -0
  109. sentry_sdk/integrations/opentelemetry/integration.py +58 -0
  110. sentry_sdk/integrations/opentelemetry/propagator.py +117 -0
  111. sentry_sdk/integrations/opentelemetry/span_processor.py +391 -0
  112. sentry_sdk/integrations/otlp.py +82 -0
  113. sentry_sdk/integrations/pure_eval.py +141 -0
  114. sentry_sdk/integrations/pydantic_ai/__init__.py +47 -0
  115. sentry_sdk/integrations/pydantic_ai/consts.py +1 -0
  116. sentry_sdk/integrations/pydantic_ai/patches/__init__.py +4 -0
  117. sentry_sdk/integrations/pydantic_ai/patches/agent_run.py +215 -0
  118. sentry_sdk/integrations/pydantic_ai/patches/graph_nodes.py +110 -0
  119. sentry_sdk/integrations/pydantic_ai/patches/model_request.py +40 -0
  120. sentry_sdk/integrations/pydantic_ai/patches/tools.py +98 -0
  121. sentry_sdk/integrations/pydantic_ai/spans/__init__.py +3 -0
  122. sentry_sdk/integrations/pydantic_ai/spans/ai_client.py +246 -0
  123. sentry_sdk/integrations/pydantic_ai/spans/execute_tool.py +49 -0
  124. sentry_sdk/integrations/pydantic_ai/spans/invoke_agent.py +112 -0
  125. sentry_sdk/integrations/pydantic_ai/utils.py +223 -0
  126. sentry_sdk/integrations/pymongo.py +214 -0
  127. sentry_sdk/integrations/pyramid.py +112 -68
  128. sentry_sdk/integrations/quart.py +237 -0
  129. sentry_sdk/integrations/ray.py +165 -0
  130. sentry_sdk/integrations/redis/__init__.py +48 -0
  131. sentry_sdk/integrations/redis/_async_common.py +116 -0
  132. sentry_sdk/integrations/redis/_sync_common.py +119 -0
  133. sentry_sdk/integrations/redis/consts.py +19 -0
  134. sentry_sdk/integrations/redis/modules/__init__.py +0 -0
  135. sentry_sdk/integrations/redis/modules/caches.py +118 -0
  136. sentry_sdk/integrations/redis/modules/queries.py +65 -0
  137. sentry_sdk/integrations/redis/rb.py +32 -0
  138. sentry_sdk/integrations/redis/redis.py +69 -0
  139. sentry_sdk/integrations/redis/redis_cluster.py +107 -0
  140. sentry_sdk/integrations/redis/redis_py_cluster_legacy.py +50 -0
  141. sentry_sdk/integrations/redis/utils.py +148 -0
  142. sentry_sdk/integrations/rq.py +95 -37
  143. sentry_sdk/integrations/rust_tracing.py +284 -0
  144. sentry_sdk/integrations/sanic.py +294 -123
  145. sentry_sdk/integrations/serverless.py +48 -19
  146. sentry_sdk/integrations/socket.py +96 -0
  147. sentry_sdk/integrations/spark/__init__.py +4 -0
  148. sentry_sdk/integrations/spark/spark_driver.py +316 -0
  149. sentry_sdk/integrations/spark/spark_worker.py +116 -0
  150. sentry_sdk/integrations/sqlalchemy.py +142 -0
  151. sentry_sdk/integrations/starlette.py +737 -0
  152. sentry_sdk/integrations/starlite.py +292 -0
  153. sentry_sdk/integrations/statsig.py +37 -0
  154. sentry_sdk/integrations/stdlib.py +235 -29
  155. sentry_sdk/integrations/strawberry.py +394 -0
  156. sentry_sdk/integrations/sys_exit.py +70 -0
  157. sentry_sdk/integrations/threading.py +158 -28
  158. sentry_sdk/integrations/tornado.py +84 -52
  159. sentry_sdk/integrations/trytond.py +50 -0
  160. sentry_sdk/integrations/typer.py +60 -0
  161. sentry_sdk/integrations/unleash.py +33 -0
  162. sentry_sdk/integrations/unraisablehook.py +53 -0
  163. sentry_sdk/integrations/wsgi.py +201 -119
  164. sentry_sdk/logger.py +96 -0
  165. sentry_sdk/metrics.py +81 -0
  166. sentry_sdk/monitor.py +120 -0
  167. sentry_sdk/profiler/__init__.py +49 -0
  168. sentry_sdk/profiler/continuous_profiler.py +730 -0
  169. sentry_sdk/profiler/transaction_profiler.py +839 -0
  170. sentry_sdk/profiler/utils.py +195 -0
  171. sentry_sdk/py.typed +0 -0
  172. sentry_sdk/scope.py +1713 -85
  173. sentry_sdk/scrubber.py +177 -0
  174. sentry_sdk/serializer.py +405 -0
  175. sentry_sdk/session.py +177 -0
  176. sentry_sdk/sessions.py +275 -0
  177. sentry_sdk/spotlight.py +242 -0
  178. sentry_sdk/tracing.py +1486 -0
  179. sentry_sdk/tracing_utils.py +1236 -0
  180. sentry_sdk/transport.py +806 -134
  181. sentry_sdk/types.py +52 -0
  182. sentry_sdk/utils.py +1625 -465
  183. sentry_sdk/worker.py +54 -25
  184. sentry_sdk-2.46.0.dist-info/METADATA +268 -0
  185. sentry_sdk-2.46.0.dist-info/RECORD +189 -0
  186. {sentry_sdk-0.7.5.dist-info → sentry_sdk-2.46.0.dist-info}/WHEEL +1 -1
  187. sentry_sdk-2.46.0.dist-info/entry_points.txt +2 -0
  188. sentry_sdk-2.46.0.dist-info/licenses/LICENSE +21 -0
  189. sentry_sdk/integrations/celery.py +0 -119
  190. sentry_sdk-0.7.5.dist-info/LICENSE +0 -9
  191. sentry_sdk-0.7.5.dist-info/METADATA +0 -36
  192. sentry_sdk-0.7.5.dist-info/RECORD +0 -39
  193. {sentry_sdk-0.7.5.dist-info → sentry_sdk-2.46.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,292 @@
1
+ from copy import deepcopy
2
+
3
+ import sentry_sdk
4
+ from sentry_sdk.consts import OP
5
+ from sentry_sdk.integrations import DidNotEnable, Integration
6
+ from sentry_sdk.integrations.asgi import SentryAsgiMiddleware
7
+ from sentry_sdk.scope import should_send_default_pii
8
+ from sentry_sdk.tracing import SOURCE_FOR_STYLE, TransactionSource
9
+ from sentry_sdk.utils import (
10
+ ensure_integration_enabled,
11
+ event_from_exception,
12
+ transaction_from_function,
13
+ )
14
+
15
+ try:
16
+ from starlite import Request, Starlite, State # type: ignore
17
+ from starlite.handlers.base import BaseRouteHandler # type: ignore
18
+ from starlite.middleware import DefineMiddleware # type: ignore
19
+ from starlite.plugins.base import get_plugin_for_value # type: ignore
20
+ from starlite.routes.http import HTTPRoute # type: ignore
21
+ from starlite.utils import ConnectionDataExtractor, is_async_callable, Ref # type: ignore
22
+ from pydantic import BaseModel # type: ignore
23
+ except ImportError:
24
+ raise DidNotEnable("Starlite is not installed")
25
+
26
+ from typing import TYPE_CHECKING
27
+
28
+ if TYPE_CHECKING:
29
+ from typing import Any, Optional, Union
30
+ from starlite.types import ( # type: ignore
31
+ ASGIApp,
32
+ Hint,
33
+ HTTPReceiveMessage,
34
+ HTTPScope,
35
+ Message,
36
+ Middleware,
37
+ Receive,
38
+ Scope as StarliteScope,
39
+ Send,
40
+ WebSocketReceiveMessage,
41
+ )
42
+ from starlite import MiddlewareProtocol
43
+ from sentry_sdk._types import Event
44
+
45
+
46
+ _DEFAULT_TRANSACTION_NAME = "generic Starlite request"
47
+
48
+
49
+ class StarliteIntegration(Integration):
50
+ identifier = "starlite"
51
+ origin = f"auto.http.{identifier}"
52
+
53
+ @staticmethod
54
+ def setup_once():
55
+ # type: () -> None
56
+ patch_app_init()
57
+ patch_middlewares()
58
+ patch_http_route_handle()
59
+
60
+
61
+ class SentryStarliteASGIMiddleware(SentryAsgiMiddleware):
62
+ def __init__(self, app, span_origin=StarliteIntegration.origin):
63
+ # type: (ASGIApp, str) -> None
64
+ super().__init__(
65
+ app=app,
66
+ unsafe_context_data=False,
67
+ transaction_style="endpoint",
68
+ mechanism_type="asgi",
69
+ span_origin=span_origin,
70
+ asgi_version=3,
71
+ )
72
+
73
+
74
+ def patch_app_init():
75
+ # type: () -> None
76
+ """
77
+ Replaces the Starlite class's `__init__` function in order to inject `after_exception` handlers and set the
78
+ `SentryStarliteASGIMiddleware` as the outmost middleware in the stack.
79
+ See:
80
+ - https://starlite-api.github.io/starlite/usage/0-the-starlite-app/5-application-hooks/#after-exception
81
+ - https://starlite-api.github.io/starlite/usage/7-middleware/0-middleware-intro/
82
+ """
83
+ old__init__ = Starlite.__init__
84
+
85
+ @ensure_integration_enabled(StarliteIntegration, old__init__)
86
+ def injection_wrapper(self, *args, **kwargs):
87
+ # type: (Starlite, *Any, **Any) -> None
88
+ after_exception = kwargs.pop("after_exception", [])
89
+ kwargs.update(
90
+ after_exception=[
91
+ exception_handler,
92
+ *(
93
+ after_exception
94
+ if isinstance(after_exception, list)
95
+ else [after_exception]
96
+ ),
97
+ ]
98
+ )
99
+
100
+ middleware = kwargs.get("middleware") or []
101
+ kwargs["middleware"] = [SentryStarliteASGIMiddleware, *middleware]
102
+ old__init__(self, *args, **kwargs)
103
+
104
+ Starlite.__init__ = injection_wrapper
105
+
106
+
107
+ def patch_middlewares():
108
+ # type: () -> None
109
+ old_resolve_middleware_stack = BaseRouteHandler.resolve_middleware
110
+
111
+ @ensure_integration_enabled(StarliteIntegration, old_resolve_middleware_stack)
112
+ def resolve_middleware_wrapper(self):
113
+ # type: (BaseRouteHandler) -> list[Middleware]
114
+ return [
115
+ enable_span_for_middleware(middleware)
116
+ for middleware in old_resolve_middleware_stack(self)
117
+ ]
118
+
119
+ BaseRouteHandler.resolve_middleware = resolve_middleware_wrapper
120
+
121
+
122
+ def enable_span_for_middleware(middleware):
123
+ # type: (Middleware) -> Middleware
124
+ if (
125
+ not hasattr(middleware, "__call__") # noqa: B004
126
+ or middleware is SentryStarliteASGIMiddleware
127
+ ):
128
+ return middleware
129
+
130
+ if isinstance(middleware, DefineMiddleware):
131
+ old_call = middleware.middleware.__call__ # type: ASGIApp
132
+ else:
133
+ old_call = middleware.__call__
134
+
135
+ async def _create_span_call(self, scope, receive, send):
136
+ # type: (MiddlewareProtocol, StarliteScope, Receive, Send) -> None
137
+ if sentry_sdk.get_client().get_integration(StarliteIntegration) is None:
138
+ return await old_call(self, scope, receive, send)
139
+
140
+ middleware_name = self.__class__.__name__
141
+ with sentry_sdk.start_span(
142
+ op=OP.MIDDLEWARE_STARLITE,
143
+ name=middleware_name,
144
+ origin=StarliteIntegration.origin,
145
+ ) as middleware_span:
146
+ middleware_span.set_tag("starlite.middleware_name", middleware_name)
147
+
148
+ # Creating spans for the "receive" callback
149
+ async def _sentry_receive(*args, **kwargs):
150
+ # type: (*Any, **Any) -> Union[HTTPReceiveMessage, WebSocketReceiveMessage]
151
+ if sentry_sdk.get_client().get_integration(StarliteIntegration) is None:
152
+ return await receive(*args, **kwargs)
153
+ with sentry_sdk.start_span(
154
+ op=OP.MIDDLEWARE_STARLITE_RECEIVE,
155
+ name=getattr(receive, "__qualname__", str(receive)),
156
+ origin=StarliteIntegration.origin,
157
+ ) as span:
158
+ span.set_tag("starlite.middleware_name", middleware_name)
159
+ return await receive(*args, **kwargs)
160
+
161
+ receive_name = getattr(receive, "__name__", str(receive))
162
+ receive_patched = receive_name == "_sentry_receive"
163
+ new_receive = _sentry_receive if not receive_patched else receive
164
+
165
+ # Creating spans for the "send" callback
166
+ async def _sentry_send(message):
167
+ # type: (Message) -> None
168
+ if sentry_sdk.get_client().get_integration(StarliteIntegration) is None:
169
+ return await send(message)
170
+ with sentry_sdk.start_span(
171
+ op=OP.MIDDLEWARE_STARLITE_SEND,
172
+ name=getattr(send, "__qualname__", str(send)),
173
+ origin=StarliteIntegration.origin,
174
+ ) as span:
175
+ span.set_tag("starlite.middleware_name", middleware_name)
176
+ return await send(message)
177
+
178
+ send_name = getattr(send, "__name__", str(send))
179
+ send_patched = send_name == "_sentry_send"
180
+ new_send = _sentry_send if not send_patched else send
181
+
182
+ return await old_call(self, scope, new_receive, new_send)
183
+
184
+ not_yet_patched = old_call.__name__ not in ["_create_span_call"]
185
+
186
+ if not_yet_patched:
187
+ if isinstance(middleware, DefineMiddleware):
188
+ middleware.middleware.__call__ = _create_span_call
189
+ else:
190
+ middleware.__call__ = _create_span_call
191
+
192
+ return middleware
193
+
194
+
195
+ def patch_http_route_handle():
196
+ # type: () -> None
197
+ old_handle = HTTPRoute.handle
198
+
199
+ async def handle_wrapper(self, scope, receive, send):
200
+ # type: (HTTPRoute, HTTPScope, Receive, Send) -> None
201
+ if sentry_sdk.get_client().get_integration(StarliteIntegration) is None:
202
+ return await old_handle(self, scope, receive, send)
203
+
204
+ sentry_scope = sentry_sdk.get_isolation_scope()
205
+ request = scope["app"].request_class(scope=scope, receive=receive, send=send) # type: Request[Any, Any]
206
+ extracted_request_data = ConnectionDataExtractor(
207
+ parse_body=True, parse_query=True
208
+ )(request)
209
+ body = extracted_request_data.pop("body")
210
+
211
+ request_data = await body
212
+
213
+ def event_processor(event, _):
214
+ # type: (Event, Hint) -> Event
215
+ route_handler = scope.get("route_handler")
216
+
217
+ request_info = event.get("request", {})
218
+ request_info["content_length"] = len(scope.get("_body", b""))
219
+ if should_send_default_pii():
220
+ request_info["cookies"] = extracted_request_data["cookies"]
221
+ if request_data is not None:
222
+ request_info["data"] = request_data
223
+
224
+ func = None
225
+ if route_handler.name is not None:
226
+ tx_name = route_handler.name
227
+ elif isinstance(route_handler.fn, Ref):
228
+ func = route_handler.fn.value
229
+ else:
230
+ func = route_handler.fn
231
+ if func is not None:
232
+ tx_name = transaction_from_function(func)
233
+
234
+ tx_info = {"source": SOURCE_FOR_STYLE["endpoint"]}
235
+
236
+ if not tx_name:
237
+ tx_name = _DEFAULT_TRANSACTION_NAME
238
+ tx_info = {"source": TransactionSource.ROUTE}
239
+
240
+ event.update(
241
+ {
242
+ "request": deepcopy(request_info),
243
+ "transaction": tx_name,
244
+ "transaction_info": tx_info,
245
+ }
246
+ )
247
+ return event
248
+
249
+ sentry_scope._name = StarliteIntegration.identifier
250
+ sentry_scope.add_event_processor(event_processor)
251
+
252
+ return await old_handle(self, scope, receive, send)
253
+
254
+ HTTPRoute.handle = handle_wrapper
255
+
256
+
257
+ def retrieve_user_from_scope(scope):
258
+ # type: (StarliteScope) -> Optional[dict[str, Any]]
259
+ scope_user = scope.get("user")
260
+ if not scope_user:
261
+ return None
262
+ if isinstance(scope_user, dict):
263
+ return scope_user
264
+ if isinstance(scope_user, BaseModel):
265
+ return scope_user.dict()
266
+ if hasattr(scope_user, "asdict"): # dataclasses
267
+ return scope_user.asdict()
268
+
269
+ plugin = get_plugin_for_value(scope_user)
270
+ if plugin and not is_async_callable(plugin.to_dict):
271
+ return plugin.to_dict(scope_user)
272
+
273
+ return None
274
+
275
+
276
+ @ensure_integration_enabled(StarliteIntegration)
277
+ def exception_handler(exc, scope, _):
278
+ # type: (Exception, StarliteScope, State) -> None
279
+ user_info = None # type: Optional[dict[str, Any]]
280
+ if should_send_default_pii():
281
+ user_info = retrieve_user_from_scope(scope)
282
+ if user_info and isinstance(user_info, dict):
283
+ sentry_scope = sentry_sdk.get_isolation_scope()
284
+ sentry_scope.set_user(user_info)
285
+
286
+ event, hint = event_from_exception(
287
+ exc,
288
+ client_options=sentry_sdk.get_client().options,
289
+ mechanism={"type": StarliteIntegration.identifier, "handled": False},
290
+ )
291
+
292
+ sentry_sdk.capture_event(event, hint=hint)
@@ -0,0 +1,37 @@
1
+ from functools import wraps
2
+ from typing import Any, TYPE_CHECKING
3
+
4
+ from sentry_sdk.feature_flags import add_feature_flag
5
+ from sentry_sdk.integrations import Integration, DidNotEnable, _check_minimum_version
6
+ from sentry_sdk.utils import parse_version
7
+
8
+ try:
9
+ from statsig import statsig as statsig_module
10
+ from statsig.version import __version__ as STATSIG_VERSION
11
+ except ImportError:
12
+ raise DidNotEnable("statsig is not installed")
13
+
14
+ if TYPE_CHECKING:
15
+ from statsig.statsig_user import StatsigUser
16
+
17
+
18
+ class StatsigIntegration(Integration):
19
+ identifier = "statsig"
20
+
21
+ @staticmethod
22
+ def setup_once():
23
+ # type: () -> None
24
+ version = parse_version(STATSIG_VERSION)
25
+ _check_minimum_version(StatsigIntegration, version, "statsig")
26
+
27
+ # Wrap and patch evaluation method(s) in the statsig module
28
+ old_check_gate = statsig_module.check_gate
29
+
30
+ @wraps(old_check_gate)
31
+ def sentry_check_gate(user, gate, *args, **kwargs):
32
+ # type: (StatsigUser, str, *Any, **Any) -> Any
33
+ enabled = old_check_gate(user, gate, *args, **kwargs)
34
+ add_feature_flag(gate, enabled)
35
+ return enabled
36
+
37
+ statsig_module.check_gate = sentry_check_gate
@@ -1,11 +1,45 @@
1
- from sentry_sdk.hub import Hub
1
+ import os
2
+ import subprocess
3
+ import sys
4
+ import platform
5
+ from http.client import HTTPConnection
6
+
7
+ import sentry_sdk
8
+ from sentry_sdk.consts import OP, SPANDATA
2
9
  from sentry_sdk.integrations import Integration
10
+ from sentry_sdk.scope import add_global_event_processor
11
+ from sentry_sdk.tracing_utils import (
12
+ EnvironHeaders,
13
+ should_propagate_trace,
14
+ add_http_request_source,
15
+ )
16
+ from sentry_sdk.utils import (
17
+ SENSITIVE_DATA_SUBSTITUTE,
18
+ capture_internal_exceptions,
19
+ ensure_integration_enabled,
20
+ is_sentry_url,
21
+ logger,
22
+ safe_repr,
23
+ parse_url,
24
+ )
25
+
26
+ from typing import TYPE_CHECKING
27
+
28
+ if TYPE_CHECKING:
29
+ from typing import Any
30
+ from typing import Callable
31
+ from typing import Dict
32
+ from typing import Optional
33
+ from typing import List
34
+
35
+ from sentry_sdk._types import Event, Hint
3
36
 
4
37
 
5
- try:
6
- from httplib import HTTPConnection # type: ignore
7
- except ImportError:
8
- from http.client import HTTPConnection
38
+ _RUNTIME_CONTEXT = {
39
+ "name": platform.python_implementation(),
40
+ "version": "%s.%s.%s" % (sys.version_info[:3]),
41
+ "build": sys.version,
42
+ } # type: dict[str, object]
9
43
 
10
44
 
11
45
  class StdlibIntegration(Integration):
@@ -14,27 +48,39 @@ class StdlibIntegration(Integration):
14
48
  @staticmethod
15
49
  def setup_once():
16
50
  # type: () -> None
17
- install_httplib()
51
+ _install_httplib()
52
+ _install_subprocess()
18
53
 
54
+ @add_global_event_processor
55
+ def add_python_runtime_context(event, hint):
56
+ # type: (Event, Hint) -> Optional[Event]
57
+ if sentry_sdk.get_client().get_integration(StdlibIntegration) is not None:
58
+ contexts = event.setdefault("contexts", {})
59
+ if isinstance(contexts, dict) and "runtime" not in contexts:
60
+ contexts["runtime"] = _RUNTIME_CONTEXT
19
61
 
20
- def install_httplib():
62
+ return event
63
+
64
+
65
+ def _install_httplib():
21
66
  # type: () -> None
22
67
  real_putrequest = HTTPConnection.putrequest
23
68
  real_getresponse = HTTPConnection.getresponse
24
69
 
25
70
  def putrequest(self, method, url, *args, **kwargs):
26
- rv = real_putrequest(self, method, url, *args, **kwargs)
27
- if Hub.current.get_integration(StdlibIntegration) is None:
28
- return rv
29
-
30
- self._sentrysdk_data_dict = data = {}
31
-
71
+ # type: (HTTPConnection, str, str, *Any, **Any) -> Any
32
72
  host = self.host
33
73
  port = self.port
34
74
  default_port = self.default_port
35
75
 
76
+ client = sentry_sdk.get_client()
77
+ if client.get_integration(StdlibIntegration) is None or is_sentry_url(
78
+ client, host
79
+ ):
80
+ return real_putrequest(self, method, url, *args, **kwargs)
81
+
36
82
  real_url = url
37
- if not real_url.startswith(("http://", "https://")):
83
+ if real_url is None or not real_url.startswith(("http://", "https://")):
38
84
  real_url = "%s://%s%s%s" % (
39
85
  default_port == 443 and "https" or "http",
40
86
  host,
@@ -42,25 +88,185 @@ def install_httplib():
42
88
  url,
43
89
  )
44
90
 
45
- data["url"] = real_url
46
- data["method"] = method
91
+ parsed_url = None
92
+ with capture_internal_exceptions():
93
+ parsed_url = parse_url(real_url, sanitize=False)
94
+
95
+ span = sentry_sdk.start_span(
96
+ op=OP.HTTP_CLIENT,
97
+ name="%s %s"
98
+ % (method, parsed_url.url if parsed_url else SENSITIVE_DATA_SUBSTITUTE),
99
+ origin="auto.http.stdlib.httplib",
100
+ )
101
+ span.set_data(SPANDATA.HTTP_METHOD, method)
102
+ if parsed_url is not None:
103
+ span.set_data("url", parsed_url.url)
104
+ span.set_data(SPANDATA.HTTP_QUERY, parsed_url.query)
105
+ span.set_data(SPANDATA.HTTP_FRAGMENT, parsed_url.fragment)
106
+
107
+ rv = real_putrequest(self, method, url, *args, **kwargs)
108
+
109
+ if should_propagate_trace(client, real_url):
110
+ for (
111
+ key,
112
+ value,
113
+ ) in sentry_sdk.get_current_scope().iter_trace_propagation_headers(
114
+ span=span
115
+ ):
116
+ logger.debug(
117
+ "[Tracing] Adding `{key}` header {value} to outgoing request to {real_url}.".format(
118
+ key=key, value=value, real_url=real_url
119
+ )
120
+ )
121
+ self.putheader(key, value)
122
+
123
+ self._sentrysdk_span = span # type: ignore[attr-defined]
124
+
47
125
  return rv
48
126
 
49
127
  def getresponse(self, *args, **kwargs):
50
- rv = real_getresponse(self, *args, **kwargs)
51
- hub = Hub.current
52
- if hub.get_integration(StdlibIntegration) is None:
53
- return rv
128
+ # type: (HTTPConnection, *Any, **Any) -> Any
129
+ span = getattr(self, "_sentrysdk_span", None)
54
130
 
55
- data = getattr(self, "_sentrysdk_data_dict", None) or {}
131
+ if span is None:
132
+ return real_getresponse(self, *args, **kwargs)
133
+
134
+ try:
135
+ rv = real_getresponse(self, *args, **kwargs)
136
+
137
+ span.set_http_status(int(rv.status))
138
+ span.set_data("reason", rv.reason)
139
+ finally:
140
+ span.finish()
141
+
142
+ with capture_internal_exceptions():
143
+ add_http_request_source(span)
56
144
 
57
- if "status_code" not in data:
58
- data["status_code"] = rv.status
59
- data["reason"] = rv.reason
60
- hub.add_breadcrumb(
61
- type="http", category="httplib", data=data, hint={"httplib_response": rv}
62
- )
63
145
  return rv
64
146
 
65
- HTTPConnection.putrequest = putrequest
66
- HTTPConnection.getresponse = getresponse
147
+ HTTPConnection.putrequest = putrequest # type: ignore[method-assign]
148
+ HTTPConnection.getresponse = getresponse # type: ignore[method-assign]
149
+
150
+
151
+ def _init_argument(args, kwargs, name, position, setdefault_callback=None):
152
+ # type: (List[Any], Dict[Any, Any], str, int, Optional[Callable[[Any], Any]]) -> Any
153
+ """
154
+ given (*args, **kwargs) of a function call, retrieve (and optionally set a
155
+ default for) an argument by either name or position.
156
+
157
+ This is useful for wrapping functions with complex type signatures and
158
+ extracting a few arguments without needing to redefine that function's
159
+ entire type signature.
160
+ """
161
+
162
+ if name in kwargs:
163
+ rv = kwargs[name]
164
+ if setdefault_callback is not None:
165
+ rv = setdefault_callback(rv)
166
+ if rv is not None:
167
+ kwargs[name] = rv
168
+ elif position < len(args):
169
+ rv = args[position]
170
+ if setdefault_callback is not None:
171
+ rv = setdefault_callback(rv)
172
+ if rv is not None:
173
+ args[position] = rv
174
+ else:
175
+ rv = setdefault_callback and setdefault_callback(None)
176
+ if rv is not None:
177
+ kwargs[name] = rv
178
+
179
+ return rv
180
+
181
+
182
+ def _install_subprocess():
183
+ # type: () -> None
184
+ old_popen_init = subprocess.Popen.__init__
185
+
186
+ @ensure_integration_enabled(StdlibIntegration, old_popen_init)
187
+ def sentry_patched_popen_init(self, *a, **kw):
188
+ # type: (subprocess.Popen[Any], *Any, **Any) -> None
189
+ # Convert from tuple to list to be able to set values.
190
+ a = list(a)
191
+
192
+ args = _init_argument(a, kw, "args", 0) or []
193
+ cwd = _init_argument(a, kw, "cwd", 9)
194
+
195
+ # if args is not a list or tuple (and e.g. some iterator instead),
196
+ # let's not use it at all. There are too many things that can go wrong
197
+ # when trying to collect an iterator into a list and setting that list
198
+ # into `a` again.
199
+ #
200
+ # Also invocations where `args` is not a sequence are not actually
201
+ # legal. They just happen to work under CPython.
202
+ description = None
203
+
204
+ if isinstance(args, (list, tuple)) and len(args) < 100:
205
+ with capture_internal_exceptions():
206
+ description = " ".join(map(str, args))
207
+
208
+ if description is None:
209
+ description = safe_repr(args)
210
+
211
+ env = None
212
+
213
+ with sentry_sdk.start_span(
214
+ op=OP.SUBPROCESS,
215
+ name=description,
216
+ origin="auto.subprocess.stdlib.subprocess",
217
+ ) as span:
218
+ for k, v in sentry_sdk.get_current_scope().iter_trace_propagation_headers(
219
+ span=span
220
+ ):
221
+ if env is None:
222
+ env = _init_argument(
223
+ a,
224
+ kw,
225
+ "env",
226
+ 10,
227
+ lambda x: dict(x if x is not None else os.environ),
228
+ )
229
+ env["SUBPROCESS_" + k.upper().replace("-", "_")] = v
230
+
231
+ if cwd:
232
+ span.set_data("subprocess.cwd", cwd)
233
+
234
+ rv = old_popen_init(self, *a, **kw)
235
+
236
+ span.set_tag("subprocess.pid", self.pid)
237
+ return rv
238
+
239
+ subprocess.Popen.__init__ = sentry_patched_popen_init # type: ignore
240
+
241
+ old_popen_wait = subprocess.Popen.wait
242
+
243
+ @ensure_integration_enabled(StdlibIntegration, old_popen_wait)
244
+ def sentry_patched_popen_wait(self, *a, **kw):
245
+ # type: (subprocess.Popen[Any], *Any, **Any) -> Any
246
+ with sentry_sdk.start_span(
247
+ op=OP.SUBPROCESS_WAIT,
248
+ origin="auto.subprocess.stdlib.subprocess",
249
+ ) as span:
250
+ span.set_tag("subprocess.pid", self.pid)
251
+ return old_popen_wait(self, *a, **kw)
252
+
253
+ subprocess.Popen.wait = sentry_patched_popen_wait # type: ignore
254
+
255
+ old_popen_communicate = subprocess.Popen.communicate
256
+
257
+ @ensure_integration_enabled(StdlibIntegration, old_popen_communicate)
258
+ def sentry_patched_popen_communicate(self, *a, **kw):
259
+ # type: (subprocess.Popen[Any], *Any, **Any) -> Any
260
+ with sentry_sdk.start_span(
261
+ op=OP.SUBPROCESS_COMMUNICATE,
262
+ origin="auto.subprocess.stdlib.subprocess",
263
+ ) as span:
264
+ span.set_tag("subprocess.pid", self.pid)
265
+ return old_popen_communicate(self, *a, **kw)
266
+
267
+ subprocess.Popen.communicate = sentry_patched_popen_communicate # type: ignore
268
+
269
+
270
+ def get_subprocess_traceparent_headers():
271
+ # type: () -> EnvironHeaders
272
+ return EnvironHeaders(os.environ, prefix="SUBPROCESS_")