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
@@ -1,63 +1,209 @@
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
- if False:
10
- from typing import Iterator
6
+ from typing import TYPE_CHECKING
7
+
8
+ if TYPE_CHECKING:
9
+ from collections.abc import Sequence
10
+ from typing import Callable
11
11
  from typing import Dict
12
+ from typing import Iterator
12
13
  from typing import List
14
+ from typing import Optional
13
15
  from typing import Set
14
16
  from typing import Type
15
- from typing import Callable
17
+ from typing import Union
18
+ from typing import Any
19
+
20
+
21
+ _DEFAULT_FAILED_REQUEST_STATUS_CODES = frozenset(range(500, 600))
16
22
 
17
23
 
18
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
19
30
  _installed_integrations = set() # type: Set[str]
20
31
 
21
32
 
22
- def _generate_default_integrations_iterator(*import_strings):
23
- # type: (*str) -> Callable[[], Iterator[Type[Integration]]]
24
- def iter_default_integrations():
25
- # type: () -> Iterator[Type[Integration]]
26
- """Returns an iterator of the default integration classes:
27
- """
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]]]
38
+
39
+ def iter_default_integrations(with_auto_enabling_integrations):
40
+ # type: (bool) -> Iterator[Type[Integration]]
41
+ """Returns an iterator of the default integration classes:"""
28
42
  from importlib import import_module
29
43
 
30
- for import_string in import_strings:
31
- module, cls = import_string.rsplit(".", 1)
32
- yield getattr(import_module(module), cls)
44
+ if with_auto_enabling_integrations:
45
+ all_import_strings = integrations + auto_enabling_integrations
46
+ else:
47
+ all_import_strings = integrations
33
48
 
34
- for import_string in import_strings:
35
- iter_default_integrations.__doc__ += "\n- `{}`".format( # type: ignore
36
- import_string
37
- )
49
+ for import_string in all_import_strings:
50
+ try:
51
+ module, cls = import_string.rsplit(".", 1)
52
+ yield getattr(import_module(module), cls)
53
+ except (DidNotEnable, SyntaxError) as e:
54
+ logger.debug(
55
+ "Did not import default integration %s: %s", import_string, e
56
+ )
57
+
58
+ if isinstance(iter_default_integrations.__doc__, str):
59
+ for import_string in integrations:
60
+ iter_default_integrations.__doc__ += "\n- `{}`".format(import_string)
38
61
 
39
62
  return iter_default_integrations
40
63
 
41
64
 
42
- iter_default_integrations = _generate_default_integrations_iterator(
43
- "sentry_sdk.integrations.logging.LoggingIntegration",
44
- "sentry_sdk.integrations.stdlib.StdlibIntegration",
45
- "sentry_sdk.integrations.excepthook.ExcepthookIntegration",
46
- "sentry_sdk.integrations.dedupe.DedupeIntegration",
65
+ _DEFAULT_INTEGRATIONS = [
66
+ # stdlib/base runtime integrations
67
+ "sentry_sdk.integrations.argv.ArgvIntegration",
47
68
  "sentry_sdk.integrations.atexit.AtexitIntegration",
69
+ "sentry_sdk.integrations.dedupe.DedupeIntegration",
70
+ "sentry_sdk.integrations.excepthook.ExcepthookIntegration",
71
+ "sentry_sdk.integrations.logging.LoggingIntegration",
48
72
  "sentry_sdk.integrations.modules.ModulesIntegration",
49
- "sentry_sdk.integrations.argv.ArgvIntegration",
73
+ "sentry_sdk.integrations.stdlib.StdlibIntegration",
50
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",
84
+ "sentry_sdk.integrations.bottle.BottleIntegration",
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",
110
+ "sentry_sdk.integrations.rq.RqIntegration",
111
+ "sentry_sdk.integrations.sanic.SanicIntegration",
112
+ "sentry_sdk.integrations.sqlalchemy.SqlalchemyIntegration",
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
+ ]
118
+
119
+ iter_default_integrations = _generate_default_integrations_iterator(
120
+ integrations=_DEFAULT_INTEGRATIONS,
121
+ auto_enabling_integrations=_AUTO_ENABLING_INTEGRATIONS,
51
122
  )
52
123
 
53
124
  del _generate_default_integrations_iterator
54
125
 
55
126
 
56
- def setup_integrations(integrations, with_defaults=True):
57
- # type: (List[Integration], bool) -> Dict[str, Integration]
58
- """Given a list of integration instances this installs them all. When
59
- `with_defaults` is set to `True` then all default integrations are added
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
+
182
+ def setup_integrations(
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]]
188
+ ):
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
60
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.
61
207
  """
62
208
  integrations = dict(
63
209
  (integration.identifier, integration) for integration in integrations or ()
@@ -65,31 +211,76 @@ def setup_integrations(integrations, with_defaults=True):
65
211
 
66
212
  logger.debug("Setting up integrations (with default = %s)", with_defaults)
67
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
+
222
+ # Integrations that are not explicitly set up by the user.
223
+ used_as_default_integration = set()
224
+
68
225
  if with_defaults:
69
- for integration_cls in iter_default_integrations():
226
+ for integration_cls in iter_default_integrations(
227
+ with_auto_enabling_integrations
228
+ ):
70
229
  if integration_cls.identifier not in integrations:
71
230
  instance = integration_cls()
72
231
  integrations[instance.identifier] = instance
232
+ used_as_default_integration.add(instance.identifier)
233
+
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
+ )
73
254
 
74
- for identifier, integration in iteritems(integrations):
255
+ for identifier, integration in integrations.items():
75
256
  with _installer_lock:
76
- if identifier not in _installed_integrations:
77
- logger.debug(
78
- "Setting up previously not enabled integration %s", identifier
79
- )
80
- try:
81
- type(integration).setup_once()
82
- except NotImplementedError:
83
- if getattr(integration, "install", None) is not None:
84
- logger.warn(
85
- "Integration %s: The install method is "
86
- "deprecated. Use `setup_once`.",
87
- 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
88
273
  )
89
- integration.install()
90
274
  else:
91
- raise
92
- _installed_integrations.add(identifier)
275
+ _installed_integrations.add(identifier)
276
+
277
+ _processed_integrations.add(identifier)
278
+
279
+ integrations = {
280
+ identifier: integration
281
+ for identifier, integration in integrations.items()
282
+ if identifier in _installed_integrations
283
+ }
93
284
 
94
285
  for identifier in integrations:
95
286
  logger.debug("Enabling integration %s", identifier)
@@ -97,7 +288,34 @@ def setup_integrations(integrations, with_defaults=True):
97
288
  return integrations
98
289
 
99
290
 
100
- class Integration(object):
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
309
+ """
310
+ The integration could not be enabled due to a trivial user error like
311
+ `flask` not being installed for the `FlaskIntegration`.
312
+
313
+ This exception is silently swallowed for default integrations, but reraised
314
+ for explicitly enabled integrations.
315
+ """
316
+
317
+
318
+ class Integration(ABC):
101
319
  """Baseclass for all integrations.
102
320
 
103
321
  To accept options for an integration, implement your own constructor that
@@ -111,7 +329,9 @@ class Integration(object):
111
329
  """String unique ID of integration type"""
112
330
 
113
331
  @staticmethod
332
+ @abstractmethod
114
333
  def setup_once():
334
+ # type: () -> None
115
335
  """
116
336
  Initialize the integration.
117
337
 
@@ -122,4 +342,11 @@ class Integration(object):
122
342
  Inside those hooks `Integration.current` can be used to access the
123
343
  instance again.
124
344
  """
125
- 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