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,214 @@
1
+ import copy
2
+ import json
3
+
4
+ import sentry_sdk
5
+ from sentry_sdk.consts import SPANSTATUS, SPANDATA, OP
6
+ from sentry_sdk.integrations import DidNotEnable, Integration
7
+ from sentry_sdk.scope import should_send_default_pii
8
+ from sentry_sdk.tracing import Span
9
+ from sentry_sdk.utils import capture_internal_exceptions
10
+
11
+ try:
12
+ from pymongo import monitoring
13
+ except ImportError:
14
+ raise DidNotEnable("Pymongo not installed")
15
+
16
+ from typing import TYPE_CHECKING
17
+
18
+ if TYPE_CHECKING:
19
+ from typing import Any, Dict, Union
20
+
21
+ from pymongo.monitoring import (
22
+ CommandFailedEvent,
23
+ CommandStartedEvent,
24
+ CommandSucceededEvent,
25
+ )
26
+
27
+
28
+ SAFE_COMMAND_ATTRIBUTES = [
29
+ "insert",
30
+ "ordered",
31
+ "find",
32
+ "limit",
33
+ "singleBatch",
34
+ "aggregate",
35
+ "createIndexes",
36
+ "indexes",
37
+ "delete",
38
+ "findAndModify",
39
+ "renameCollection",
40
+ "to",
41
+ "drop",
42
+ ]
43
+
44
+
45
+ def _strip_pii(command):
46
+ # type: (Dict[str, Any]) -> Dict[str, Any]
47
+ for key in command:
48
+ is_safe_field = key in SAFE_COMMAND_ATTRIBUTES
49
+ if is_safe_field:
50
+ # Skip if safe key
51
+ continue
52
+
53
+ update_db_command = key == "update" and "findAndModify" not in command
54
+ if update_db_command:
55
+ # Also skip "update" db command because it is save.
56
+ # There is also an "update" key in the "findAndModify" command, which is NOT safe!
57
+ continue
58
+
59
+ # Special stripping for documents
60
+ is_document = key == "documents"
61
+ if is_document:
62
+ for doc in command[key]:
63
+ for doc_key in doc:
64
+ doc[doc_key] = "%s"
65
+ continue
66
+
67
+ # Special stripping for dict style fields
68
+ is_dict_field = key in ["filter", "query", "update"]
69
+ if is_dict_field:
70
+ for item_key in command[key]:
71
+ command[key][item_key] = "%s"
72
+ continue
73
+
74
+ # For pipeline fields strip the `$match` dict
75
+ is_pipeline_field = key == "pipeline"
76
+ if is_pipeline_field:
77
+ for pipeline in command[key]:
78
+ for match_key in pipeline["$match"] if "$match" in pipeline else []:
79
+ pipeline["$match"][match_key] = "%s"
80
+ continue
81
+
82
+ # Default stripping
83
+ command[key] = "%s"
84
+
85
+ return command
86
+
87
+
88
+ def _get_db_data(event):
89
+ # type: (Any) -> Dict[str, Any]
90
+ data = {}
91
+
92
+ data[SPANDATA.DB_SYSTEM] = "mongodb"
93
+
94
+ db_name = event.database_name
95
+ if db_name is not None:
96
+ data[SPANDATA.DB_NAME] = db_name
97
+
98
+ server_address = event.connection_id[0]
99
+ if server_address is not None:
100
+ data[SPANDATA.SERVER_ADDRESS] = server_address
101
+
102
+ server_port = event.connection_id[1]
103
+ if server_port is not None:
104
+ data[SPANDATA.SERVER_PORT] = server_port
105
+
106
+ return data
107
+
108
+
109
+ class CommandTracer(monitoring.CommandListener):
110
+ def __init__(self):
111
+ # type: () -> None
112
+ self._ongoing_operations = {} # type: Dict[int, Span]
113
+
114
+ def _operation_key(self, event):
115
+ # type: (Union[CommandFailedEvent, CommandStartedEvent, CommandSucceededEvent]) -> int
116
+ return event.request_id
117
+
118
+ def started(self, event):
119
+ # type: (CommandStartedEvent) -> None
120
+ if sentry_sdk.get_client().get_integration(PyMongoIntegration) is None:
121
+ return
122
+
123
+ with capture_internal_exceptions():
124
+ command = dict(copy.deepcopy(event.command))
125
+
126
+ command.pop("$db", None)
127
+ command.pop("$clusterTime", None)
128
+ command.pop("$signature", None)
129
+
130
+ tags = {
131
+ "db.name": event.database_name,
132
+ SPANDATA.DB_SYSTEM: "mongodb",
133
+ SPANDATA.DB_OPERATION: event.command_name,
134
+ SPANDATA.DB_MONGODB_COLLECTION: command.get(event.command_name),
135
+ }
136
+
137
+ try:
138
+ tags["net.peer.name"] = event.connection_id[0]
139
+ tags["net.peer.port"] = str(event.connection_id[1])
140
+ except TypeError:
141
+ pass
142
+
143
+ data = {"operation_ids": {}} # type: Dict[str, Any]
144
+ data["operation_ids"]["operation"] = event.operation_id
145
+ data["operation_ids"]["request"] = event.request_id
146
+
147
+ data.update(_get_db_data(event))
148
+
149
+ try:
150
+ lsid = command.pop("lsid")["id"]
151
+ data["operation_ids"]["session"] = str(lsid)
152
+ except KeyError:
153
+ pass
154
+
155
+ if not should_send_default_pii():
156
+ command = _strip_pii(command)
157
+
158
+ query = json.dumps(command, default=str)
159
+ span = sentry_sdk.start_span(
160
+ op=OP.DB,
161
+ name=query,
162
+ origin=PyMongoIntegration.origin,
163
+ )
164
+
165
+ for tag, value in tags.items():
166
+ # set the tag for backwards-compatibility.
167
+ # TODO: remove the set_tag call in the next major release!
168
+ span.set_tag(tag, value)
169
+
170
+ span.set_data(tag, value)
171
+
172
+ for key, value in data.items():
173
+ span.set_data(key, value)
174
+
175
+ with capture_internal_exceptions():
176
+ sentry_sdk.add_breadcrumb(
177
+ message=query, category="query", type=OP.DB, data=tags
178
+ )
179
+
180
+ self._ongoing_operations[self._operation_key(event)] = span.__enter__()
181
+
182
+ def failed(self, event):
183
+ # type: (CommandFailedEvent) -> None
184
+ if sentry_sdk.get_client().get_integration(PyMongoIntegration) is None:
185
+ return
186
+
187
+ try:
188
+ span = self._ongoing_operations.pop(self._operation_key(event))
189
+ span.set_status(SPANSTATUS.INTERNAL_ERROR)
190
+ span.__exit__(None, None, None)
191
+ except KeyError:
192
+ return
193
+
194
+ def succeeded(self, event):
195
+ # type: (CommandSucceededEvent) -> None
196
+ if sentry_sdk.get_client().get_integration(PyMongoIntegration) is None:
197
+ return
198
+
199
+ try:
200
+ span = self._ongoing_operations.pop(self._operation_key(event))
201
+ span.set_status(SPANSTATUS.OK)
202
+ span.__exit__(None, None, None)
203
+ except KeyError:
204
+ pass
205
+
206
+
207
+ class PyMongoIntegration(Integration):
208
+ identifier = "pymongo"
209
+ origin = f"auto.db.{identifier}"
210
+
211
+ @staticmethod
212
+ def setup_once():
213
+ # type: () -> None
214
+ monitoring.register(CommandTracer())
@@ -1,31 +1,41 @@
1
- from __future__ import absolute_import
2
-
1
+ import functools
3
2
  import os
4
3
  import sys
5
4
  import weakref
6
5
 
7
- from pyramid.httpexceptions import HTTPException # type: ignore
8
- from pyramid.request import Request # type: ignore
9
-
10
- from sentry_sdk.hub import Hub, _should_send_default_pii
11
- from sentry_sdk.utils import capture_internal_exceptions, event_from_exception
12
- from sentry_sdk._compat import reraise
13
-
14
- from sentry_sdk.integrations import Integration
6
+ import sentry_sdk
7
+ from sentry_sdk.integrations import Integration, DidNotEnable
15
8
  from sentry_sdk.integrations._wsgi_common import RequestExtractor
16
9
  from sentry_sdk.integrations.wsgi import SentryWsgiMiddleware
17
-
18
- if False:
19
- from pyramid.response import Response # type: ignore
10
+ from sentry_sdk.scope import should_send_default_pii
11
+ from sentry_sdk.tracing import SOURCE_FOR_STYLE
12
+ from sentry_sdk.utils import (
13
+ capture_internal_exceptions,
14
+ ensure_integration_enabled,
15
+ event_from_exception,
16
+ reraise,
17
+ )
18
+
19
+ try:
20
+ from pyramid.httpexceptions import HTTPException
21
+ from pyramid.request import Request
22
+ except ImportError:
23
+ raise DidNotEnable("Pyramid not installed")
24
+
25
+ from typing import TYPE_CHECKING
26
+
27
+ if TYPE_CHECKING:
28
+ from pyramid.response import Response
20
29
  from typing import Any
21
30
  from sentry_sdk.integrations.wsgi import _ScopedResponse
22
31
  from typing import Callable
23
32
  from typing import Dict
24
33
  from typing import Optional
25
- from webob.cookies import RequestCookies # type: ignore
26
- from webob.compat import cgi_FieldStorage # type: ignore
34
+ from webob.cookies import RequestCookies
35
+ from webob.request import _FieldStorageWithFile
27
36
 
28
37
  from sentry_sdk.utils import ExcInfo
38
+ from sentry_sdk._types import Event, EventProcessor
29
39
 
30
40
 
31
41
  if getattr(Request, "authenticated_userid", None):
@@ -34,20 +44,22 @@ if getattr(Request, "authenticated_userid", None):
34
44
  # type: (Request) -> Optional[Any]
35
45
  return request.authenticated_userid
36
46
 
37
-
38
47
  else:
39
48
  # bw-compat for pyramid < 1.5
40
49
  from pyramid.security import authenticated_userid # type: ignore
41
50
 
42
51
 
52
+ TRANSACTION_STYLE_VALUES = ("route_name", "route_pattern")
53
+
54
+
43
55
  class PyramidIntegration(Integration):
44
56
  identifier = "pyramid"
57
+ origin = f"auto.http.{identifier}"
45
58
 
46
- transaction_style = None
59
+ transaction_style = ""
47
60
 
48
61
  def __init__(self, transaction_style="route_name"):
49
62
  # type: (str) -> None
50
- TRANSACTION_STYLE_VALUES = ("route_name", "route_pattern")
51
63
  if transaction_style not in TRANSACTION_STYLE_VALUES:
52
64
  raise ValueError(
53
65
  "Invalid value for transaction_style: %s (must be in %s)"
@@ -58,65 +70,105 @@ class PyramidIntegration(Integration):
58
70
  @staticmethod
59
71
  def setup_once():
60
72
  # type: () -> None
61
- from pyramid.router import Router # type: ignore
73
+ from pyramid import router
62
74
 
63
- old_handle_request = Router.handle_request
75
+ old_call_view = router._call_view
64
76
 
65
- def sentry_patched_handle_request(self, request, *args, **kwargs):
77
+ @functools.wraps(old_call_view)
78
+ def sentry_patched_call_view(registry, request, *args, **kwargs):
66
79
  # type: (Any, Request, *Any, **Any) -> Response
67
- hub = Hub.current
68
- integration = hub.get_integration(PyramidIntegration)
80
+ integration = sentry_sdk.get_client().get_integration(PyramidIntegration)
69
81
  if integration is None:
70
- return old_handle_request(self, request, *args, **kwargs)
82
+ return old_call_view(registry, request, *args, **kwargs)
71
83
 
72
- with hub.configure_scope() as scope:
73
- scope.add_event_processor(
74
- _make_event_processor(weakref.ref(request), integration)
75
- )
84
+ _set_transaction_name_and_source(
85
+ sentry_sdk.get_current_scope(), integration.transaction_style, request
86
+ )
87
+ scope = sentry_sdk.get_isolation_scope()
88
+ scope.add_event_processor(
89
+ _make_event_processor(weakref.ref(request), integration)
90
+ )
76
91
 
77
- try:
78
- return old_handle_request(self, request, *args, **kwargs)
79
- except Exception:
80
- exc_info = sys.exc_info()
81
- _capture_exception(exc_info)
82
- reraise(*exc_info)
92
+ return old_call_view(registry, request, *args, **kwargs)
83
93
 
84
- Router.handle_request = sentry_patched_handle_request
94
+ router._call_view = sentry_patched_call_view
85
95
 
86
- old_wsgi_call = Router.__call__
96
+ if hasattr(Request, "invoke_exception_view"):
97
+ old_invoke_exception_view = Request.invoke_exception_view
87
98
 
88
- def sentry_patched_wsgi_call(self, environ, start_response):
89
- # type: (Any, Dict[str, str], Callable) -> _ScopedResponse
90
- hub = Hub.current
91
- integration = hub.get_integration(PyramidIntegration)
92
- if integration is None:
93
- return old_wsgi_call(self, environ, start_response)
99
+ def sentry_patched_invoke_exception_view(self, *args, **kwargs):
100
+ # type: (Request, *Any, **Any) -> Any
101
+ rv = old_invoke_exception_view(self, *args, **kwargs)
102
+
103
+ if (
104
+ self.exc_info
105
+ and all(self.exc_info)
106
+ and rv.status_int == 500
107
+ and sentry_sdk.get_client().get_integration(PyramidIntegration)
108
+ is not None
109
+ ):
110
+ _capture_exception(self.exc_info)
111
+
112
+ return rv
94
113
 
95
- return SentryWsgiMiddleware(lambda *a, **kw: old_wsgi_call(self, *a, **kw))(
96
- environ, start_response
114
+ Request.invoke_exception_view = sentry_patched_invoke_exception_view
115
+
116
+ old_wsgi_call = router.Router.__call__
117
+
118
+ @ensure_integration_enabled(PyramidIntegration, old_wsgi_call)
119
+ def sentry_patched_wsgi_call(self, environ, start_response):
120
+ # type: (Any, Dict[str, str], Callable[..., Any]) -> _ScopedResponse
121
+ def sentry_patched_inner_wsgi_call(environ, start_response):
122
+ # type: (Dict[str, Any], Callable[..., Any]) -> Any
123
+ try:
124
+ return old_wsgi_call(self, environ, start_response)
125
+ except Exception:
126
+ einfo = sys.exc_info()
127
+ _capture_exception(einfo)
128
+ reraise(*einfo)
129
+
130
+ middleware = SentryWsgiMiddleware(
131
+ sentry_patched_inner_wsgi_call,
132
+ span_origin=PyramidIntegration.origin,
97
133
  )
134
+ return middleware(environ, start_response)
98
135
 
99
- Router.__call__ = sentry_patched_wsgi_call
136
+ router.Router.__call__ = sentry_patched_wsgi_call
100
137
 
101
138
 
102
- def _capture_exception(exc_info, **kwargs):
103
- # type: (ExcInfo, **Any) -> None
139
+ @ensure_integration_enabled(PyramidIntegration)
140
+ def _capture_exception(exc_info):
141
+ # type: (ExcInfo) -> None
104
142
  if exc_info[0] is None or issubclass(exc_info[0], HTTPException):
105
143
  return
106
- hub = Hub.current
107
- if hub.get_integration(PyramidIntegration) is None:
108
- return
144
+
109
145
  event, hint = event_from_exception(
110
146
  exc_info,
111
- client_options=hub.client.options,
147
+ client_options=sentry_sdk.get_client().options,
112
148
  mechanism={"type": "pyramid", "handled": False},
113
149
  )
114
150
 
115
- hub.capture_event(event, hint=hint)
151
+ sentry_sdk.capture_event(event, hint=hint)
152
+
153
+
154
+ def _set_transaction_name_and_source(scope, transaction_style, request):
155
+ # type: (sentry_sdk.Scope, str, Request) -> None
156
+ try:
157
+ name_for_style = {
158
+ "route_name": request.matched_route.name,
159
+ "route_pattern": request.matched_route.pattern,
160
+ }
161
+ scope.set_transaction_name(
162
+ name_for_style[transaction_style],
163
+ source=SOURCE_FOR_STYLE[transaction_style],
164
+ )
165
+ except Exception:
166
+ pass
116
167
 
117
168
 
118
169
  class PyramidRequestExtractor(RequestExtractor):
119
170
  def url(self):
171
+ # type: () -> str
120
172
  return self.request.path_url
121
173
 
122
174
  def env(self):
@@ -140,7 +192,7 @@ class PyramidRequestExtractor(RequestExtractor):
140
192
  }
141
193
 
142
194
  def files(self):
143
- # type: () -> Dict[str, cgi_FieldStorage]
195
+ # type: () -> Dict[str, _FieldStorageWithFile]
144
196
  return {
145
197
  key: value
146
198
  for key, value in self.request.POST.items()
@@ -148,7 +200,7 @@ class PyramidRequestExtractor(RequestExtractor):
148
200
  }
149
201
 
150
202
  def size_of_file(self, postdata):
151
- # type: (cgi_FieldStorage) -> int
203
+ # type: (_FieldStorageWithFile) -> int
152
204
  file = postdata.file
153
205
  try:
154
206
  return os.fstat(file.fileno()).st_size
@@ -157,29 +209,21 @@ class PyramidRequestExtractor(RequestExtractor):
157
209
 
158
210
 
159
211
  def _make_event_processor(weak_request, integration):
160
- # type: (Callable[[], Request], PyramidIntegration) -> Callable
161
- def event_processor(event, hint):
162
- # type: (Dict[str, Any], Dict[str, Any]) -> Dict[str, Any]
212
+ # type: (Callable[[], Request], PyramidIntegration) -> EventProcessor
213
+ def pyramid_event_processor(event, hint):
214
+ # type: (Event, Dict[str, Any]) -> Event
163
215
  request = weak_request()
164
216
  if request is None:
165
217
  return event
166
218
 
167
- try:
168
- if integration.transaction_style == "route_name":
169
- event["transaction"] = request.matched_route.name
170
- elif integration.transaction_style == "route_pattern":
171
- event["transaction"] = request.matched_route.pattern
172
- except Exception:
173
- pass
174
-
175
219
  with capture_internal_exceptions():
176
220
  PyramidRequestExtractor(request).extract_into_event(event)
177
221
 
178
- if _should_send_default_pii():
222
+ if should_send_default_pii():
179
223
  with capture_internal_exceptions():
180
224
  user_info = event.setdefault("user", {})
181
- user_info["id"] = authenticated_userid(request)
225
+ user_info.setdefault("id", authenticated_userid(request))
182
226
 
183
227
  return event
184
228
 
185
- return event_processor
229
+ return pyramid_event_processor