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
@@ -1,29 +1,40 @@
1
- """This package"""
2
- from __future__ import absolute_import
3
-
1
+ from abc import ABC, abstractmethod
4
2
  from threading import Lock
5
3
 
6
- from sentry_sdk._compat import iteritems
7
4
  from sentry_sdk.utils import logger
8
5
 
9
- from sentry_sdk._types import MYPY
6
+ from typing import TYPE_CHECKING
10
7
 
11
- if MYPY:
8
+ if TYPE_CHECKING:
9
+ from collections.abc import Sequence
12
10
  from typing import Callable
13
11
  from typing import Dict
14
12
  from typing import Iterator
15
13
  from typing import List
14
+ from typing import Optional
16
15
  from typing import Set
17
- from typing import Tuple
18
16
  from typing import Type
17
+ from typing import Union
18
+ from typing import Any
19
+
20
+
21
+ _DEFAULT_FAILED_REQUEST_STATUS_CODES = frozenset(range(500, 600))
19
22
 
20
23
 
21
24
  _installer_lock = Lock()
25
+
26
+ # Set of all integration identifiers we have attempted to install
27
+ _processed_integrations = set() # type: Set[str]
28
+
29
+ # Set of all integration identifiers we have actually installed
22
30
  _installed_integrations = set() # type: Set[str]
23
31
 
24
32
 
25
- def _generate_default_integrations_iterator(integrations, auto_enabling_integrations):
26
- # type: (Tuple[str, ...], Tuple[str, ...]) -> Callable[[bool], Iterator[Type[Integration]]]
33
+ def _generate_default_integrations_iterator(
34
+ integrations, # type: List[str]
35
+ auto_enabling_integrations, # type: List[str]
36
+ ):
37
+ # type: (...) -> Callable[[bool], Iterator[Type[Integration]]]
27
38
 
28
39
  def iter_default_integrations(with_auto_enabling_integrations):
29
40
  # type: (bool) -> Iterator[Type[Integration]]
@@ -51,45 +62,148 @@ def _generate_default_integrations_iterator(integrations, auto_enabling_integrat
51
62
  return iter_default_integrations
52
63
 
53
64
 
54
- _AUTO_ENABLING_INTEGRATIONS = (
55
- "sentry_sdk.integrations.django.DjangoIntegration",
56
- "sentry_sdk.integrations.flask.FlaskIntegration",
65
+ _DEFAULT_INTEGRATIONS = [
66
+ # stdlib/base runtime integrations
67
+ "sentry_sdk.integrations.argv.ArgvIntegration",
68
+ "sentry_sdk.integrations.atexit.AtexitIntegration",
69
+ "sentry_sdk.integrations.dedupe.DedupeIntegration",
70
+ "sentry_sdk.integrations.excepthook.ExcepthookIntegration",
71
+ "sentry_sdk.integrations.logging.LoggingIntegration",
72
+ "sentry_sdk.integrations.modules.ModulesIntegration",
73
+ "sentry_sdk.integrations.stdlib.StdlibIntegration",
74
+ "sentry_sdk.integrations.threading.ThreadingIntegration",
75
+ ]
76
+
77
+ _AUTO_ENABLING_INTEGRATIONS = [
78
+ "sentry_sdk.integrations.aiohttp.AioHttpIntegration",
79
+ "sentry_sdk.integrations.anthropic.AnthropicIntegration",
80
+ "sentry_sdk.integrations.ariadne.AriadneIntegration",
81
+ "sentry_sdk.integrations.arq.ArqIntegration",
82
+ "sentry_sdk.integrations.asyncpg.AsyncPGIntegration",
83
+ "sentry_sdk.integrations.boto3.Boto3Integration",
57
84
  "sentry_sdk.integrations.bottle.BottleIntegration",
58
- "sentry_sdk.integrations.falcon.FalconIntegration",
59
- "sentry_sdk.integrations.sanic.SanicIntegration",
60
85
  "sentry_sdk.integrations.celery.CeleryIntegration",
86
+ "sentry_sdk.integrations.chalice.ChaliceIntegration",
87
+ "sentry_sdk.integrations.clickhouse_driver.ClickhouseDriverIntegration",
88
+ "sentry_sdk.integrations.cohere.CohereIntegration",
89
+ "sentry_sdk.integrations.django.DjangoIntegration",
90
+ "sentry_sdk.integrations.falcon.FalconIntegration",
91
+ "sentry_sdk.integrations.fastapi.FastApiIntegration",
92
+ "sentry_sdk.integrations.flask.FlaskIntegration",
93
+ "sentry_sdk.integrations.gql.GQLIntegration",
94
+ "sentry_sdk.integrations.graphene.GrapheneIntegration",
95
+ "sentry_sdk.integrations.httpx.HttpxIntegration",
96
+ "sentry_sdk.integrations.huey.HueyIntegration",
97
+ "sentry_sdk.integrations.huggingface_hub.HuggingfaceHubIntegration",
98
+ "sentry_sdk.integrations.langchain.LangchainIntegration",
99
+ "sentry_sdk.integrations.langgraph.LanggraphIntegration",
100
+ "sentry_sdk.integrations.litestar.LitestarIntegration",
101
+ "sentry_sdk.integrations.loguru.LoguruIntegration",
102
+ "sentry_sdk.integrations.mcp.MCPIntegration",
103
+ "sentry_sdk.integrations.openai.OpenAIIntegration",
104
+ "sentry_sdk.integrations.openai_agents.OpenAIAgentsIntegration",
105
+ "sentry_sdk.integrations.pydantic_ai.PydanticAIIntegration",
106
+ "sentry_sdk.integrations.pymongo.PyMongoIntegration",
107
+ "sentry_sdk.integrations.pyramid.PyramidIntegration",
108
+ "sentry_sdk.integrations.quart.QuartIntegration",
109
+ "sentry_sdk.integrations.redis.RedisIntegration",
61
110
  "sentry_sdk.integrations.rq.RqIntegration",
62
- "sentry_sdk.integrations.aiohttp.AioHttpIntegration",
63
- "sentry_sdk.integrations.tornado.TornadoIntegration",
111
+ "sentry_sdk.integrations.sanic.SanicIntegration",
64
112
  "sentry_sdk.integrations.sqlalchemy.SqlalchemyIntegration",
65
- )
66
-
113
+ "sentry_sdk.integrations.starlette.StarletteIntegration",
114
+ "sentry_sdk.integrations.starlite.StarliteIntegration",
115
+ "sentry_sdk.integrations.strawberry.StrawberryIntegration",
116
+ "sentry_sdk.integrations.tornado.TornadoIntegration",
117
+ ]
67
118
 
68
119
  iter_default_integrations = _generate_default_integrations_iterator(
69
- integrations=(
70
- # stdlib/base runtime integrations
71
- "sentry_sdk.integrations.logging.LoggingIntegration",
72
- "sentry_sdk.integrations.stdlib.StdlibIntegration",
73
- "sentry_sdk.integrations.excepthook.ExcepthookIntegration",
74
- "sentry_sdk.integrations.dedupe.DedupeIntegration",
75
- "sentry_sdk.integrations.atexit.AtexitIntegration",
76
- "sentry_sdk.integrations.modules.ModulesIntegration",
77
- "sentry_sdk.integrations.argv.ArgvIntegration",
78
- "sentry_sdk.integrations.threading.ThreadingIntegration",
79
- ),
120
+ integrations=_DEFAULT_INTEGRATIONS,
80
121
  auto_enabling_integrations=_AUTO_ENABLING_INTEGRATIONS,
81
122
  )
82
123
 
83
124
  del _generate_default_integrations_iterator
84
125
 
85
126
 
127
+ _MIN_VERSIONS = {
128
+ "aiohttp": (3, 4),
129
+ "anthropic": (0, 16),
130
+ "ariadne": (0, 20),
131
+ "arq": (0, 23),
132
+ "asyncpg": (0, 23),
133
+ "beam": (2, 12),
134
+ "boto3": (1, 12), # botocore
135
+ "bottle": (0, 12),
136
+ "celery": (4, 4, 7),
137
+ "chalice": (1, 16, 0),
138
+ "clickhouse_driver": (0, 2, 0),
139
+ "cohere": (5, 4, 0),
140
+ "django": (1, 8),
141
+ "dramatiq": (1, 9),
142
+ "falcon": (1, 4),
143
+ "fastapi": (0, 79, 0),
144
+ "flask": (1, 1, 4),
145
+ "gql": (3, 4, 1),
146
+ "graphene": (3, 3),
147
+ "google_genai": (1, 29, 0), # google-genai
148
+ "grpc": (1, 32, 0), # grpcio
149
+ "httpx": (0, 16, 0),
150
+ "huggingface_hub": (0, 24, 7),
151
+ "langchain": (0, 1, 0),
152
+ "langgraph": (0, 6, 6),
153
+ "launchdarkly": (9, 8, 0),
154
+ "litellm": (1, 77, 5),
155
+ "loguru": (0, 7, 0),
156
+ "mcp": (1, 15, 0),
157
+ "openai": (1, 0, 0),
158
+ "openai_agents": (0, 0, 19),
159
+ "openfeature": (0, 7, 1),
160
+ "pydantic_ai": (1, 0, 0),
161
+ "quart": (0, 16, 0),
162
+ "ray": (2, 7, 0),
163
+ "requests": (2, 0, 0),
164
+ "rq": (0, 6),
165
+ "sanic": (0, 8),
166
+ "sqlalchemy": (1, 2),
167
+ "starlette": (0, 16),
168
+ "starlite": (1, 48),
169
+ "statsig": (0, 55, 3),
170
+ "strawberry": (0, 209, 5),
171
+ "tornado": (6, 0),
172
+ "typer": (0, 15),
173
+ "unleash": (6, 0, 1),
174
+ }
175
+
176
+
177
+ _INTEGRATION_DEACTIVATES = {
178
+ "langchain": {"openai", "anthropic"},
179
+ }
180
+
181
+
86
182
  def setup_integrations(
87
- integrations, with_defaults=True, with_auto_enabling_integrations=False
183
+ integrations, # type: Sequence[Integration]
184
+ with_defaults=True, # type: bool
185
+ with_auto_enabling_integrations=False, # type: bool
186
+ disabled_integrations=None, # type: Optional[Sequence[Union[type[Integration], Integration]]]
187
+ options=None, # type: Optional[Dict[str, Any]]
88
188
  ):
89
- # type: (List[Integration], bool, bool) -> Dict[str, Integration]
90
- """Given a list of integration instances this installs them all. When
91
- `with_defaults` is set to `True` then all default integrations are added
189
+ # type: (...) -> Dict[str, Integration]
190
+ """
191
+ Given a list of integration instances, this installs them all.
192
+
193
+ When `with_defaults` is set to `True` all default integrations are added
92
194
  unless they were already provided before.
195
+
196
+ `disabled_integrations` takes precedence over `with_defaults` and
197
+ `with_auto_enabling_integrations`.
198
+
199
+ Some integrations are designed to automatically deactivate other integrations
200
+ in order to avoid conflicts and prevent duplicate telemetry from being collected.
201
+ For example, enabling the `langchain` integration will auto-deactivate both the
202
+ `openai` and `anthropic` integrations.
203
+
204
+ Users can override this behavior by:
205
+ - Explicitly providing an integration in the `integrations=[]` list, or
206
+ - Disabling the higher-level integration via the `disabled_integrations` option.
93
207
  """
94
208
  integrations = dict(
95
209
  (integration.identifier, integration) for integration in integrations or ()
@@ -97,6 +211,14 @@ def setup_integrations(
97
211
 
98
212
  logger.debug("Setting up integrations (with default = %s)", with_defaults)
99
213
 
214
+ user_provided_integrations = set(integrations.keys())
215
+
216
+ # Integrations that will not be enabled
217
+ disabled_integrations = [
218
+ integration if isinstance(integration, type) else type(integration)
219
+ for integration in disabled_integrations or []
220
+ ]
221
+
100
222
  # Integrations that are not explicitly set up by the user.
101
223
  used_as_default_integration = set()
102
224
 
@@ -109,33 +231,56 @@ def setup_integrations(
109
231
  integrations[instance.identifier] = instance
110
232
  used_as_default_integration.add(instance.identifier)
111
233
 
112
- for identifier, integration in iteritems(integrations):
234
+ disabled_integration_identifiers = {
235
+ integration.identifier for integration in disabled_integrations
236
+ }
237
+
238
+ for integration, targets_to_deactivate in _INTEGRATION_DEACTIVATES.items():
239
+ if (
240
+ integration in integrations
241
+ and integration not in disabled_integration_identifiers
242
+ ):
243
+ for target in targets_to_deactivate:
244
+ if target not in user_provided_integrations:
245
+ for cls in iter_default_integrations(True):
246
+ if cls.identifier == target:
247
+ if cls not in disabled_integrations:
248
+ disabled_integrations.append(cls)
249
+ logger.debug(
250
+ "Auto-deactivating %s integration because %s integration is active",
251
+ target,
252
+ integration,
253
+ )
254
+
255
+ for identifier, integration in integrations.items():
113
256
  with _installer_lock:
114
- if identifier not in _installed_integrations:
115
- logger.debug(
116
- "Setting up previously not enabled integration %s", identifier
117
- )
118
- try:
119
- type(integration).setup_once()
120
- except NotImplementedError:
121
- if getattr(integration, "install", None) is not None:
122
- logger.warning(
123
- "Integration %s: The install method is "
124
- "deprecated. Use `setup_once`.",
125
- identifier,
257
+ if identifier not in _processed_integrations:
258
+ if type(integration) in disabled_integrations:
259
+ logger.debug("Ignoring integration %s", identifier)
260
+ else:
261
+ logger.debug(
262
+ "Setting up previously not enabled integration %s", identifier
263
+ )
264
+ try:
265
+ type(integration).setup_once()
266
+ integration.setup_once_with_options(options)
267
+ except DidNotEnable as e:
268
+ if identifier not in used_as_default_integration:
269
+ raise
270
+
271
+ logger.debug(
272
+ "Did not enable default integration %s: %s", identifier, e
126
273
  )
127
- integration.install()
128
274
  else:
129
- raise
130
- except DidNotEnable as e:
131
- if identifier not in used_as_default_integration:
132
- raise
275
+ _installed_integrations.add(identifier)
133
276
 
134
- logger.debug(
135
- "Did not enable default integration %s: %s", identifier, e
136
- )
277
+ _processed_integrations.add(identifier)
137
278
 
138
- _installed_integrations.add(identifier)
279
+ integrations = {
280
+ identifier: integration
281
+ for identifier, integration in integrations.items()
282
+ if identifier in _installed_integrations
283
+ }
139
284
 
140
285
  for identifier in integrations:
141
286
  logger.debug("Enabling integration %s", identifier)
@@ -143,7 +288,24 @@ def setup_integrations(
143
288
  return integrations
144
289
 
145
290
 
146
- class DidNotEnable(Exception):
291
+ def _check_minimum_version(integration, version, package=None):
292
+ # type: (type[Integration], Optional[tuple[int, ...]], Optional[str]) -> None
293
+ package = package or integration.identifier
294
+
295
+ if version is None:
296
+ raise DidNotEnable(f"Unparsable {package} version.")
297
+
298
+ min_version = _MIN_VERSIONS.get(integration.identifier)
299
+ if min_version is None:
300
+ return
301
+
302
+ if version < min_version:
303
+ raise DidNotEnable(
304
+ f"Integration only supports {package} {'.'.join(map(str, min_version))} or newer."
305
+ )
306
+
307
+
308
+ class DidNotEnable(Exception): # noqa: N818
147
309
  """
148
310
  The integration could not be enabled due to a trivial user error like
149
311
  `flask` not being installed for the `FlaskIntegration`.
@@ -153,7 +315,7 @@ class DidNotEnable(Exception):
153
315
  """
154
316
 
155
317
 
156
- class Integration(object):
318
+ class Integration(ABC):
157
319
  """Baseclass for all integrations.
158
320
 
159
321
  To accept options for an integration, implement your own constructor that
@@ -167,6 +329,7 @@ class Integration(object):
167
329
  """String unique ID of integration type"""
168
330
 
169
331
  @staticmethod
332
+ @abstractmethod
170
333
  def setup_once():
171
334
  # type: () -> None
172
335
  """
@@ -179,4 +342,11 @@ class Integration(object):
179
342
  Inside those hooks `Integration.current` can be used to access the
180
343
  instance again.
181
344
  """
182
- raise NotImplementedError()
345
+ pass
346
+
347
+ def setup_once_with_options(self, options=None):
348
+ # type: (Optional[Dict[str, Any]]) -> None
349
+ """
350
+ Called after setup_once in rare cases on the instance and with options since we don't have those available above.
351
+ """
352
+ pass
@@ -0,0 +1,108 @@
1
+ import urllib
2
+
3
+ from sentry_sdk.scope import should_send_default_pii
4
+ from sentry_sdk.integrations._wsgi_common import _filter_headers
5
+
6
+ from typing import TYPE_CHECKING
7
+
8
+ if TYPE_CHECKING:
9
+ from typing import Any
10
+ from typing import Dict
11
+ from typing import Optional
12
+ from typing import Union
13
+ from typing_extensions import Literal
14
+
15
+ from sentry_sdk.utils import AnnotatedValue
16
+
17
+
18
+ def _get_headers(asgi_scope):
19
+ # type: (Any) -> Dict[str, str]
20
+ """
21
+ Extract headers from the ASGI scope, in the format that the Sentry protocol expects.
22
+ """
23
+ headers = {} # type: Dict[str, str]
24
+ for raw_key, raw_value in asgi_scope["headers"]:
25
+ key = raw_key.decode("latin-1")
26
+ value = raw_value.decode("latin-1")
27
+ if key in headers:
28
+ headers[key] = headers[key] + ", " + value
29
+ else:
30
+ headers[key] = value
31
+
32
+ return headers
33
+
34
+
35
+ def _get_url(asgi_scope, default_scheme, host):
36
+ # type: (Dict[str, Any], Literal["ws", "http"], Optional[Union[AnnotatedValue, str]]) -> str
37
+ """
38
+ Extract URL from the ASGI scope, without also including the querystring.
39
+ """
40
+ scheme = asgi_scope.get("scheme", default_scheme)
41
+
42
+ server = asgi_scope.get("server", None)
43
+ path = asgi_scope.get("root_path", "") + asgi_scope.get("path", "")
44
+
45
+ if host:
46
+ return "%s://%s%s" % (scheme, host, path)
47
+
48
+ if server is not None:
49
+ host, port = server
50
+ default_port = {"http": 80, "https": 443, "ws": 80, "wss": 443}.get(scheme)
51
+ if port != default_port:
52
+ return "%s://%s:%s%s" % (scheme, host, port, path)
53
+ return "%s://%s%s" % (scheme, host, path)
54
+ return path
55
+
56
+
57
+ def _get_query(asgi_scope):
58
+ # type: (Any) -> Any
59
+ """
60
+ Extract querystring from the ASGI scope, in the format that the Sentry protocol expects.
61
+ """
62
+ qs = asgi_scope.get("query_string")
63
+ if not qs:
64
+ return None
65
+ return urllib.parse.unquote(qs.decode("latin-1"))
66
+
67
+
68
+ def _get_ip(asgi_scope):
69
+ # type: (Any) -> str
70
+ """
71
+ Extract IP Address from the ASGI scope based on request headers with fallback to scope client.
72
+ """
73
+ headers = _get_headers(asgi_scope)
74
+ try:
75
+ return headers["x-forwarded-for"].split(",")[0].strip()
76
+ except (KeyError, IndexError):
77
+ pass
78
+
79
+ try:
80
+ return headers["x-real-ip"]
81
+ except KeyError:
82
+ pass
83
+
84
+ return asgi_scope.get("client")[0]
85
+
86
+
87
+ def _get_request_data(asgi_scope):
88
+ # type: (Any) -> Dict[str, Any]
89
+ """
90
+ Returns data related to the HTTP request from the ASGI scope.
91
+ """
92
+ request_data = {} # type: Dict[str, Any]
93
+ ty = asgi_scope["type"]
94
+ if ty in ("http", "websocket"):
95
+ request_data["method"] = asgi_scope.get("method")
96
+
97
+ request_data["headers"] = headers = _filter_headers(_get_headers(asgi_scope))
98
+ request_data["query_string"] = _get_query(asgi_scope)
99
+
100
+ request_data["url"] = _get_url(
101
+ asgi_scope, "http" if ty == "http" else "ws", headers.get("host")
102
+ )
103
+
104
+ client = asgi_scope.get("client")
105
+ if client and should_send_default_pii():
106
+ request_data["env"] = {"REMOTE_ADDR": _get_ip(asgi_scope)}
107
+
108
+ return request_data