sentry-sdk 0.18.0__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 -6
  2. sentry_sdk/_compat.py +64 -56
  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 +81 -19
  8. sentry_sdk/_types.py +311 -11
  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 +409 -67
  14. sentry_sdk/attachments.py +75 -0
  15. sentry_sdk/client.py +849 -103
  16. sentry_sdk/consts.py +1389 -34
  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 +12 -15
  22. sentry_sdk/envelope.py +112 -61
  23. sentry_sdk/feature_flags.py +71 -0
  24. sentry_sdk/hub.py +442 -386
  25. sentry_sdk/integrations/__init__.py +228 -58
  26. sentry_sdk/integrations/_asgi_common.py +108 -0
  27. sentry_sdk/integrations/_wsgi_common.py +131 -40
  28. sentry_sdk/integrations/aiohttp.py +221 -72
  29. sentry_sdk/integrations/anthropic.py +439 -0
  30. sentry_sdk/integrations/argv.py +4 -6
  31. sentry_sdk/integrations/ariadne.py +161 -0
  32. sentry_sdk/integrations/arq.py +247 -0
  33. sentry_sdk/integrations/asgi.py +237 -135
  34. sentry_sdk/integrations/asyncio.py +144 -0
  35. sentry_sdk/integrations/asyncpg.py +208 -0
  36. sentry_sdk/integrations/atexit.py +13 -18
  37. sentry_sdk/integrations/aws_lambda.py +233 -80
  38. sentry_sdk/integrations/beam.py +27 -35
  39. sentry_sdk/integrations/boto3.py +137 -0
  40. sentry_sdk/integrations/bottle.py +91 -69
  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 +35 -28
  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 +32 -8
  49. sentry_sdk/integrations/django/__init__.py +343 -89
  50. sentry_sdk/integrations/django/asgi.py +201 -22
  51. sentry_sdk/integrations/django/caching.py +204 -0
  52. sentry_sdk/integrations/django/middleware.py +80 -32
  53. sentry_sdk/integrations/django/signals_handlers.py +91 -0
  54. sentry_sdk/integrations/django/templates.py +69 -2
  55. sentry_sdk/integrations/django/transactions.py +39 -14
  56. sentry_sdk/integrations/django/views.py +69 -16
  57. sentry_sdk/integrations/dramatiq.py +226 -0
  58. sentry_sdk/integrations/excepthook.py +19 -13
  59. sentry_sdk/integrations/executing.py +5 -6
  60. sentry_sdk/integrations/falcon.py +128 -65
  61. sentry_sdk/integrations/fastapi.py +141 -0
  62. sentry_sdk/integrations/flask.py +114 -75
  63. sentry_sdk/integrations/gcp.py +67 -36
  64. sentry_sdk/integrations/gnu_backtrace.py +14 -22
  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 +261 -85
  87. sentry_sdk/integrations/loguru.py +213 -0
  88. sentry_sdk/integrations/mcp.py +566 -0
  89. sentry_sdk/integrations/modules.py +6 -33
  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 +20 -11
  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 +71 -60
  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 +62 -52
  143. sentry_sdk/integrations/rust_tracing.py +284 -0
  144. sentry_sdk/integrations/sanic.py +248 -114
  145. sentry_sdk/integrations/serverless.py +13 -22
  146. sentry_sdk/integrations/socket.py +96 -0
  147. sentry_sdk/integrations/spark/spark_driver.py +115 -62
  148. sentry_sdk/integrations/spark/spark_worker.py +42 -50
  149. sentry_sdk/integrations/sqlalchemy.py +82 -37
  150. sentry_sdk/integrations/starlette.py +737 -0
  151. sentry_sdk/integrations/starlite.py +292 -0
  152. sentry_sdk/integrations/statsig.py +37 -0
  153. sentry_sdk/integrations/stdlib.py +100 -58
  154. sentry_sdk/integrations/strawberry.py +394 -0
  155. sentry_sdk/integrations/sys_exit.py +70 -0
  156. sentry_sdk/integrations/threading.py +142 -38
  157. sentry_sdk/integrations/tornado.py +68 -53
  158. sentry_sdk/integrations/trytond.py +15 -20
  159. sentry_sdk/integrations/typer.py +60 -0
  160. sentry_sdk/integrations/unleash.py +33 -0
  161. sentry_sdk/integrations/unraisablehook.py +53 -0
  162. sentry_sdk/integrations/wsgi.py +126 -125
  163. sentry_sdk/logger.py +96 -0
  164. sentry_sdk/metrics.py +81 -0
  165. sentry_sdk/monitor.py +120 -0
  166. sentry_sdk/profiler/__init__.py +49 -0
  167. sentry_sdk/profiler/continuous_profiler.py +730 -0
  168. sentry_sdk/profiler/transaction_profiler.py +839 -0
  169. sentry_sdk/profiler/utils.py +195 -0
  170. sentry_sdk/scope.py +1542 -112
  171. sentry_sdk/scrubber.py +177 -0
  172. sentry_sdk/serializer.py +152 -210
  173. sentry_sdk/session.py +177 -0
  174. sentry_sdk/sessions.py +202 -179
  175. sentry_sdk/spotlight.py +242 -0
  176. sentry_sdk/tracing.py +1202 -294
  177. sentry_sdk/tracing_utils.py +1236 -0
  178. sentry_sdk/transport.py +693 -189
  179. sentry_sdk/types.py +52 -0
  180. sentry_sdk/utils.py +1395 -228
  181. sentry_sdk/worker.py +30 -17
  182. sentry_sdk-2.46.0.dist-info/METADATA +268 -0
  183. sentry_sdk-2.46.0.dist-info/RECORD +189 -0
  184. {sentry_sdk-0.18.0.dist-info → sentry_sdk-2.46.0.dist-info}/WHEEL +1 -1
  185. sentry_sdk-2.46.0.dist-info/entry_points.txt +2 -0
  186. sentry_sdk-2.46.0.dist-info/licenses/LICENSE +21 -0
  187. sentry_sdk/_functools.py +0 -66
  188. sentry_sdk/integrations/celery.py +0 -275
  189. sentry_sdk/integrations/redis.py +0 -103
  190. sentry_sdk-0.18.0.dist-info/LICENSE +0 -9
  191. sentry_sdk-0.18.0.dist-info/METADATA +0 -66
  192. sentry_sdk-0.18.0.dist-info/RECORD +0 -65
  193. {sentry_sdk-0.18.0.dist-info → sentry_sdk-2.46.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,137 @@
1
+ from functools import partial
2
+
3
+ import sentry_sdk
4
+ from sentry_sdk.consts import OP, SPANDATA
5
+ from sentry_sdk.integrations import _check_minimum_version, Integration, DidNotEnable
6
+ from sentry_sdk.tracing import Span
7
+ from sentry_sdk.utils import (
8
+ capture_internal_exceptions,
9
+ ensure_integration_enabled,
10
+ parse_url,
11
+ parse_version,
12
+ )
13
+
14
+ from typing import TYPE_CHECKING
15
+
16
+ if TYPE_CHECKING:
17
+ from typing import Any
18
+ from typing import Dict
19
+ from typing import Optional
20
+ from typing import Type
21
+
22
+ try:
23
+ from botocore import __version__ as BOTOCORE_VERSION # type: ignore
24
+ from botocore.client import BaseClient # type: ignore
25
+ from botocore.response import StreamingBody # type: ignore
26
+ from botocore.awsrequest import AWSRequest # type: ignore
27
+ except ImportError:
28
+ raise DidNotEnable("botocore is not installed")
29
+
30
+
31
+ class Boto3Integration(Integration):
32
+ identifier = "boto3"
33
+ origin = f"auto.http.{identifier}"
34
+
35
+ @staticmethod
36
+ def setup_once():
37
+ # type: () -> None
38
+ version = parse_version(BOTOCORE_VERSION)
39
+ _check_minimum_version(Boto3Integration, version, "botocore")
40
+
41
+ orig_init = BaseClient.__init__
42
+
43
+ def sentry_patched_init(self, *args, **kwargs):
44
+ # type: (Type[BaseClient], *Any, **Any) -> None
45
+ orig_init(self, *args, **kwargs)
46
+ meta = self.meta
47
+ service_id = meta.service_model.service_id.hyphenize()
48
+ meta.events.register(
49
+ "request-created",
50
+ partial(_sentry_request_created, service_id=service_id),
51
+ )
52
+ meta.events.register("after-call", _sentry_after_call)
53
+ meta.events.register("after-call-error", _sentry_after_call_error)
54
+
55
+ BaseClient.__init__ = sentry_patched_init
56
+
57
+
58
+ @ensure_integration_enabled(Boto3Integration)
59
+ def _sentry_request_created(service_id, request, operation_name, **kwargs):
60
+ # type: (str, AWSRequest, str, **Any) -> None
61
+ description = "aws.%s.%s" % (service_id, operation_name)
62
+ span = sentry_sdk.start_span(
63
+ op=OP.HTTP_CLIENT,
64
+ name=description,
65
+ origin=Boto3Integration.origin,
66
+ )
67
+
68
+ with capture_internal_exceptions():
69
+ parsed_url = parse_url(request.url, sanitize=False)
70
+ span.set_data("aws.request.url", parsed_url.url)
71
+ span.set_data(SPANDATA.HTTP_QUERY, parsed_url.query)
72
+ span.set_data(SPANDATA.HTTP_FRAGMENT, parsed_url.fragment)
73
+
74
+ span.set_tag("aws.service_id", service_id)
75
+ span.set_tag("aws.operation_name", operation_name)
76
+ span.set_data(SPANDATA.HTTP_METHOD, request.method)
77
+
78
+ # We do it in order for subsequent http calls/retries be
79
+ # attached to this span.
80
+ span.__enter__()
81
+
82
+ # request.context is an open-ended data-structure
83
+ # where we can add anything useful in request life cycle.
84
+ request.context["_sentrysdk_span"] = span
85
+
86
+
87
+ def _sentry_after_call(context, parsed, **kwargs):
88
+ # type: (Dict[str, Any], Dict[str, Any], **Any) -> None
89
+ span = context.pop("_sentrysdk_span", None) # type: Optional[Span]
90
+
91
+ # Span could be absent if the integration is disabled.
92
+ if span is None:
93
+ return
94
+ span.__exit__(None, None, None)
95
+
96
+ body = parsed.get("Body")
97
+ if not isinstance(body, StreamingBody):
98
+ return
99
+
100
+ streaming_span = span.start_child(
101
+ op=OP.HTTP_CLIENT_STREAM,
102
+ name=span.description,
103
+ origin=Boto3Integration.origin,
104
+ )
105
+
106
+ orig_read = body.read
107
+ orig_close = body.close
108
+
109
+ def sentry_streaming_body_read(*args, **kwargs):
110
+ # type: (*Any, **Any) -> bytes
111
+ try:
112
+ ret = orig_read(*args, **kwargs)
113
+ if not ret:
114
+ streaming_span.finish()
115
+ return ret
116
+ except Exception:
117
+ streaming_span.finish()
118
+ raise
119
+
120
+ body.read = sentry_streaming_body_read
121
+
122
+ def sentry_streaming_body_close(*args, **kwargs):
123
+ # type: (*Any, **Any) -> None
124
+ streaming_span.finish()
125
+ orig_close(*args, **kwargs)
126
+
127
+ body.close = sentry_streaming_body_close
128
+
129
+
130
+ def _sentry_after_call_error(context, exception, **kwargs):
131
+ # type: (Dict[str, Any], Type[BaseException], **Any) -> None
132
+ span = context.pop("_sentrysdk_span", None) # type: Optional[Span]
133
+
134
+ # Span could be absent if the integration is disabled.
135
+ if span is None:
136
+ return
137
+ span.__exit__(type(exception), exception, None)
@@ -1,18 +1,28 @@
1
- from __future__ import absolute_import
1
+ import functools
2
2
 
3
- from sentry_sdk.hub import Hub
3
+ import sentry_sdk
4
+ from sentry_sdk.tracing import SOURCE_FOR_STYLE
4
5
  from sentry_sdk.utils import (
5
6
  capture_internal_exceptions,
7
+ ensure_integration_enabled,
6
8
  event_from_exception,
9
+ parse_version,
7
10
  transaction_from_function,
8
11
  )
9
- from sentry_sdk.integrations import Integration, DidNotEnable
12
+ from sentry_sdk.integrations import (
13
+ Integration,
14
+ DidNotEnable,
15
+ _DEFAULT_FAILED_REQUEST_STATUS_CODES,
16
+ _check_minimum_version,
17
+ )
10
18
  from sentry_sdk.integrations.wsgi import SentryWsgiMiddleware
11
19
  from sentry_sdk.integrations._wsgi_common import RequestExtractor
12
20
 
13
- from sentry_sdk._types import MYPY
21
+ from typing import TYPE_CHECKING
22
+
23
+ if TYPE_CHECKING:
24
+ from collections.abc import Set
14
25
 
15
- if MYPY:
16
26
  from sentry_sdk.integrations.wsgi import _ScopedResponse
17
27
  from typing import Any
18
28
  from typing import Dict
@@ -20,14 +30,14 @@ if MYPY:
20
30
  from typing import Optional
21
31
  from bottle import FileUpload, FormsDict, LocalRequest # type: ignore
22
32
 
23
- from sentry_sdk._types import EventProcessor
33
+ from sentry_sdk._types import EventProcessor, Event
24
34
 
25
35
  try:
26
36
  from bottle import (
27
37
  Bottle,
38
+ HTTPResponse,
28
39
  Route,
29
40
  request as bottle_request,
30
- HTTPResponse,
31
41
  __version__ as BOTTLE_VERSION,
32
42
  )
33
43
  except ImportError:
@@ -39,11 +49,17 @@ TRANSACTION_STYLE_VALUES = ("endpoint", "url")
39
49
 
40
50
  class BottleIntegration(Integration):
41
51
  identifier = "bottle"
52
+ origin = f"auto.http.{identifier}"
42
53
 
43
- transaction_style = None
54
+ transaction_style = ""
44
55
 
45
- def __init__(self, transaction_style="endpoint"):
46
- # type: (str) -> None
56
+ def __init__(
57
+ self,
58
+ transaction_style="endpoint", # type: str
59
+ *,
60
+ failed_request_status_codes=_DEFAULT_FAILED_REQUEST_STATUS_CODES, # type: Set[int]
61
+ ):
62
+ # type: (...) -> None
47
63
 
48
64
  if transaction_style not in TRANSACTION_STYLE_VALUES:
49
65
  raise ValueError(
@@ -51,93 +67,73 @@ class BottleIntegration(Integration):
51
67
  % (transaction_style, TRANSACTION_STYLE_VALUES)
52
68
  )
53
69
  self.transaction_style = transaction_style
70
+ self.failed_request_status_codes = failed_request_status_codes
54
71
 
55
72
  @staticmethod
56
73
  def setup_once():
57
74
  # type: () -> None
75
+ version = parse_version(BOTTLE_VERSION)
76
+ _check_minimum_version(BottleIntegration, version)
58
77
 
59
- try:
60
- version = tuple(map(int, BOTTLE_VERSION.split(".")))
61
- except (TypeError, ValueError):
62
- raise DidNotEnable("Unparsable Bottle version: {}".format(version))
63
-
64
- if version < (0, 12):
65
- raise DidNotEnable("Bottle 0.12 or newer required.")
66
-
67
- # monkey patch method Bottle.__call__
68
78
  old_app = Bottle.__call__
69
79
 
80
+ @ensure_integration_enabled(BottleIntegration, old_app)
70
81
  def sentry_patched_wsgi_app(self, environ, start_response):
71
82
  # type: (Any, Dict[str, str], Callable[..., Any]) -> _ScopedResponse
72
-
73
- hub = Hub.current
74
- integration = hub.get_integration(BottleIntegration)
75
- if integration is None:
76
- return old_app(self, environ, start_response)
77
-
78
- return SentryWsgiMiddleware(lambda *a, **kw: old_app(self, *a, **kw))(
79
- environ, start_response
83
+ middleware = SentryWsgiMiddleware(
84
+ lambda *a, **kw: old_app(self, *a, **kw),
85
+ span_origin=BottleIntegration.origin,
80
86
  )
81
87
 
88
+ return middleware(environ, start_response)
89
+
82
90
  Bottle.__call__ = sentry_patched_wsgi_app
83
91
 
84
- # monkey patch method Bottle._handle
85
92
  old_handle = Bottle._handle
86
93
 
94
+ @functools.wraps(old_handle)
87
95
  def _patched_handle(self, environ):
88
96
  # type: (Bottle, Dict[str, Any]) -> Any
89
- hub = Hub.current
90
- integration = hub.get_integration(BottleIntegration)
97
+ integration = sentry_sdk.get_client().get_integration(BottleIntegration)
91
98
  if integration is None:
92
99
  return old_handle(self, environ)
93
100
 
94
- # create new scope
95
- scope_manager = hub.push_scope()
96
-
97
- with scope_manager:
98
- app = self
99
- with hub.configure_scope() as scope:
100
- scope._name = "bottle"
101
- scope.add_event_processor(
102
- _make_request_event_processor(app, bottle_request, integration)
103
- )
104
- res = old_handle(self, environ)
101
+ scope = sentry_sdk.get_isolation_scope()
102
+ scope._name = "bottle"
103
+ scope.add_event_processor(
104
+ _make_request_event_processor(self, bottle_request, integration)
105
+ )
106
+ res = old_handle(self, environ)
105
107
 
106
- # scope cleanup
107
108
  return res
108
109
 
109
110
  Bottle._handle = _patched_handle
110
111
 
111
- # monkey patch method Route._make_callback
112
112
  old_make_callback = Route._make_callback
113
113
 
114
+ @functools.wraps(old_make_callback)
114
115
  def patched_make_callback(self, *args, **kwargs):
115
116
  # type: (Route, *object, **object) -> Any
116
- hub = Hub.current
117
- integration = hub.get_integration(BottleIntegration)
118
117
  prepared_callback = old_make_callback(self, *args, **kwargs)
118
+
119
+ integration = sentry_sdk.get_client().get_integration(BottleIntegration)
119
120
  if integration is None:
120
121
  return prepared_callback
121
122
 
122
- # If an integration is there, a client has to be there.
123
- client = hub.client # type: Any
124
-
125
123
  def wrapped_callback(*args, **kwargs):
126
124
  # type: (*object, **object) -> Any
127
-
128
125
  try:
129
126
  res = prepared_callback(*args, **kwargs)
130
- except HTTPResponse:
131
- raise
132
127
  except Exception as exception:
133
- event, hint = event_from_exception(
134
- exception,
135
- client_options=client.options,
136
- mechanism={"type": "bottle", "handled": False},
137
- )
138
- hub.capture_event(event, hint=hint)
128
+ _capture_exception(exception, handled=False)
139
129
  raise exception
140
130
 
131
+ if (
132
+ isinstance(res, HTTPResponse)
133
+ and res.status_code in integration.failed_request_status_codes
134
+ ):
135
+ _capture_exception(res, handled=True)
136
+
141
137
  return res
142
138
 
143
139
  return wrapped_callback
@@ -176,24 +172,50 @@ class BottleRequestExtractor(RequestExtractor):
176
172
  return file.content_length
177
173
 
178
174
 
179
- def _make_request_event_processor(app, request, integration):
180
- # type: (Bottle, LocalRequest, BottleIntegration) -> EventProcessor
181
- def inner(event, hint):
182
- # type: (Dict[str, Any], Dict[str, Any]) -> Dict[str, Any]
175
+ def _set_transaction_name_and_source(event, transaction_style, request):
176
+ # type: (Event, str, Any) -> None
177
+ name = ""
178
+
179
+ if transaction_style == "url":
180
+ try:
181
+ name = request.route.rule or ""
182
+ except RuntimeError:
183
+ pass
183
184
 
185
+ elif transaction_style == "endpoint":
184
186
  try:
185
- if integration.transaction_style == "endpoint":
186
- event["transaction"] = request.route.name or transaction_from_function(
187
- request.route.callback
188
- )
189
- elif integration.transaction_style == "url":
190
- event["transaction"] = request.route.rule
191
- except Exception:
187
+ name = (
188
+ request.route.name
189
+ or transaction_from_function(request.route.callback)
190
+ or ""
191
+ )
192
+ except RuntimeError:
192
193
  pass
193
194
 
195
+ event["transaction"] = name
196
+ event["transaction_info"] = {"source": SOURCE_FOR_STYLE[transaction_style]}
197
+
198
+
199
+ def _make_request_event_processor(app, request, integration):
200
+ # type: (Bottle, LocalRequest, BottleIntegration) -> EventProcessor
201
+
202
+ def event_processor(event, hint):
203
+ # type: (Event, dict[str, Any]) -> Event
204
+ _set_transaction_name_and_source(event, integration.transaction_style, request)
205
+
194
206
  with capture_internal_exceptions():
195
207
  BottleRequestExtractor(request).extract_into_event(event)
196
208
 
197
209
  return event
198
210
 
199
- return inner
211
+ return event_processor
212
+
213
+
214
+ def _capture_exception(exception, handled):
215
+ # type: (BaseException, bool) -> None
216
+ event, hint = event_from_exception(
217
+ exception,
218
+ client_options=sentry_sdk.get_client().options,
219
+ mechanism={"type": "bottle", "handled": handled},
220
+ )
221
+ sentry_sdk.capture_event(event, hint=hint)