sentry-sdk 3.0.0a1__py2.py3-none-any.whl → 3.0.0a3__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.
Potentially problematic release.
This version of sentry-sdk might be problematic. Click here for more details.
- sentry_sdk/__init__.py +2 -0
- sentry_sdk/_compat.py +5 -12
- sentry_sdk/_init_implementation.py +7 -7
- sentry_sdk/_log_batcher.py +17 -29
- sentry_sdk/_lru_cache.py +7 -9
- sentry_sdk/_queue.py +2 -4
- sentry_sdk/_types.py +11 -18
- sentry_sdk/_werkzeug.py +5 -7
- sentry_sdk/ai/monitoring.py +44 -31
- sentry_sdk/ai/utils.py +3 -4
- sentry_sdk/api.py +75 -87
- sentry_sdk/attachments.py +10 -12
- sentry_sdk/client.py +137 -155
- sentry_sdk/consts.py +430 -174
- sentry_sdk/crons/api.py +16 -17
- sentry_sdk/crons/decorator.py +25 -27
- sentry_sdk/debug.py +4 -6
- sentry_sdk/envelope.py +46 -112
- sentry_sdk/feature_flags.py +9 -15
- sentry_sdk/integrations/__init__.py +24 -19
- sentry_sdk/integrations/_asgi_common.py +15 -18
- sentry_sdk/integrations/_wsgi_common.py +22 -33
- sentry_sdk/integrations/aiohttp.py +32 -30
- sentry_sdk/integrations/anthropic.py +42 -37
- sentry_sdk/integrations/argv.py +3 -4
- sentry_sdk/integrations/ariadne.py +16 -18
- sentry_sdk/integrations/arq.py +21 -29
- sentry_sdk/integrations/asgi.py +63 -37
- sentry_sdk/integrations/asyncio.py +14 -16
- sentry_sdk/integrations/atexit.py +6 -10
- sentry_sdk/integrations/aws_lambda.py +26 -36
- sentry_sdk/integrations/beam.py +10 -18
- sentry_sdk/integrations/boto3.py +18 -16
- sentry_sdk/integrations/bottle.py +25 -34
- sentry_sdk/integrations/celery/__init__.py +41 -61
- sentry_sdk/integrations/celery/beat.py +23 -27
- sentry_sdk/integrations/celery/utils.py +15 -17
- sentry_sdk/integrations/chalice.py +8 -10
- sentry_sdk/integrations/clickhouse_driver.py +21 -31
- sentry_sdk/integrations/cloud_resource_context.py +9 -16
- sentry_sdk/integrations/cohere.py +27 -33
- sentry_sdk/integrations/dedupe.py +5 -8
- sentry_sdk/integrations/django/__init__.py +57 -72
- sentry_sdk/integrations/django/asgi.py +26 -34
- sentry_sdk/integrations/django/caching.py +23 -19
- sentry_sdk/integrations/django/middleware.py +17 -20
- sentry_sdk/integrations/django/signals_handlers.py +11 -10
- sentry_sdk/integrations/django/templates.py +19 -16
- sentry_sdk/integrations/django/transactions.py +16 -11
- sentry_sdk/integrations/django/views.py +6 -10
- sentry_sdk/integrations/dramatiq.py +21 -21
- sentry_sdk/integrations/excepthook.py +10 -10
- sentry_sdk/integrations/executing.py +3 -4
- sentry_sdk/integrations/falcon.py +27 -42
- sentry_sdk/integrations/fastapi.py +13 -16
- sentry_sdk/integrations/flask.py +31 -38
- sentry_sdk/integrations/gcp.py +13 -16
- sentry_sdk/integrations/gnu_backtrace.py +4 -6
- sentry_sdk/integrations/gql.py +16 -17
- sentry_sdk/integrations/graphene.py +13 -12
- sentry_sdk/integrations/grpc/__init__.py +19 -1
- sentry_sdk/integrations/grpc/aio/server.py +15 -14
- sentry_sdk/integrations/grpc/client.py +19 -9
- sentry_sdk/integrations/grpc/consts.py +2 -0
- sentry_sdk/integrations/grpc/server.py +12 -8
- sentry_sdk/integrations/httpx.py +9 -12
- sentry_sdk/integrations/huey.py +13 -20
- sentry_sdk/integrations/huggingface_hub.py +18 -18
- sentry_sdk/integrations/langchain.py +203 -113
- sentry_sdk/integrations/launchdarkly.py +13 -10
- sentry_sdk/integrations/litestar.py +37 -35
- sentry_sdk/integrations/logging.py +52 -65
- sentry_sdk/integrations/loguru.py +127 -57
- sentry_sdk/integrations/modules.py +3 -4
- sentry_sdk/integrations/openai.py +100 -88
- sentry_sdk/integrations/openai_agents/__init__.py +49 -0
- sentry_sdk/integrations/openai_agents/consts.py +1 -0
- sentry_sdk/integrations/openai_agents/patches/__init__.py +4 -0
- sentry_sdk/integrations/openai_agents/patches/agent_run.py +152 -0
- sentry_sdk/integrations/openai_agents/patches/models.py +52 -0
- sentry_sdk/integrations/openai_agents/patches/runner.py +42 -0
- sentry_sdk/integrations/openai_agents/patches/tools.py +84 -0
- sentry_sdk/integrations/openai_agents/spans/__init__.py +5 -0
- sentry_sdk/integrations/openai_agents/spans/agent_workflow.py +20 -0
- sentry_sdk/integrations/openai_agents/spans/ai_client.py +46 -0
- sentry_sdk/integrations/openai_agents/spans/execute_tool.py +47 -0
- sentry_sdk/integrations/openai_agents/spans/handoff.py +24 -0
- sentry_sdk/integrations/openai_agents/spans/invoke_agent.py +41 -0
- sentry_sdk/integrations/openai_agents/utils.py +201 -0
- sentry_sdk/integrations/openfeature.py +11 -6
- sentry_sdk/integrations/pure_eval.py +6 -10
- sentry_sdk/integrations/pymongo.py +13 -17
- sentry_sdk/integrations/pyramid.py +31 -36
- sentry_sdk/integrations/quart.py +23 -28
- sentry_sdk/integrations/ray.py +73 -64
- sentry_sdk/integrations/redis/__init__.py +7 -4
- sentry_sdk/integrations/redis/_async_common.py +25 -12
- sentry_sdk/integrations/redis/_sync_common.py +19 -13
- sentry_sdk/integrations/redis/modules/caches.py +17 -8
- sentry_sdk/integrations/redis/modules/queries.py +9 -8
- sentry_sdk/integrations/redis/rb.py +3 -2
- sentry_sdk/integrations/redis/redis.py +4 -4
- sentry_sdk/integrations/redis/redis_cluster.py +21 -13
- sentry_sdk/integrations/redis/redis_py_cluster_legacy.py +3 -2
- sentry_sdk/integrations/redis/utils.py +23 -24
- sentry_sdk/integrations/rq.py +13 -16
- sentry_sdk/integrations/rust_tracing.py +9 -6
- sentry_sdk/integrations/sanic.py +34 -46
- sentry_sdk/integrations/serverless.py +22 -27
- sentry_sdk/integrations/socket.py +27 -15
- sentry_sdk/integrations/spark/__init__.py +1 -0
- sentry_sdk/integrations/spark/spark_driver.py +45 -83
- sentry_sdk/integrations/spark/spark_worker.py +7 -11
- sentry_sdk/integrations/sqlalchemy.py +22 -19
- sentry_sdk/integrations/starlette.py +86 -90
- sentry_sdk/integrations/starlite.py +28 -34
- sentry_sdk/integrations/statsig.py +5 -4
- sentry_sdk/integrations/stdlib.py +28 -24
- sentry_sdk/integrations/strawberry.py +62 -49
- sentry_sdk/integrations/sys_exit.py +7 -11
- sentry_sdk/integrations/threading.py +12 -14
- sentry_sdk/integrations/tornado.py +28 -32
- sentry_sdk/integrations/trytond.py +4 -3
- sentry_sdk/integrations/typer.py +8 -6
- sentry_sdk/integrations/unleash.py +5 -4
- sentry_sdk/integrations/wsgi.py +47 -46
- sentry_sdk/logger.py +41 -10
- sentry_sdk/monitor.py +16 -28
- sentry_sdk/opentelemetry/consts.py +11 -4
- sentry_sdk/opentelemetry/contextvars_context.py +26 -16
- sentry_sdk/opentelemetry/propagator.py +38 -21
- sentry_sdk/opentelemetry/sampler.py +51 -34
- sentry_sdk/opentelemetry/scope.py +36 -37
- sentry_sdk/opentelemetry/span_processor.py +48 -58
- sentry_sdk/opentelemetry/tracing.py +58 -14
- sentry_sdk/opentelemetry/utils.py +186 -194
- sentry_sdk/profiler/continuous_profiler.py +108 -97
- sentry_sdk/profiler/transaction_profiler.py +70 -97
- sentry_sdk/profiler/utils.py +11 -15
- sentry_sdk/scope.py +251 -273
- sentry_sdk/scrubber.py +22 -26
- sentry_sdk/serializer.py +40 -54
- sentry_sdk/session.py +44 -61
- sentry_sdk/sessions.py +35 -49
- sentry_sdk/spotlight.py +15 -21
- sentry_sdk/tracing.py +121 -187
- sentry_sdk/tracing_utils.py +104 -122
- sentry_sdk/transport.py +131 -157
- sentry_sdk/utils.py +232 -309
- sentry_sdk/worker.py +16 -28
- {sentry_sdk-3.0.0a1.dist-info → sentry_sdk-3.0.0a3.dist-info}/METADATA +3 -3
- sentry_sdk-3.0.0a3.dist-info/RECORD +168 -0
- {sentry_sdk-3.0.0a1.dist-info → sentry_sdk-3.0.0a3.dist-info}/WHEEL +1 -1
- sentry_sdk-3.0.0a1.dist-info/RECORD +0 -154
- {sentry_sdk-3.0.0a1.dist-info → sentry_sdk-3.0.0a3.dist-info}/entry_points.txt +0 -0
- {sentry_sdk-3.0.0a1.dist-info → sentry_sdk-3.0.0a3.dist-info}/licenses/LICENSE +0 -0
- {sentry_sdk-3.0.0a1.dist-info → sentry_sdk-3.0.0a3.dist-info}/top_level.txt +0 -0
sentry_sdk/transport.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
1
2
|
from abc import ABC, abstractmethod
|
|
2
3
|
import io
|
|
3
4
|
import os
|
|
@@ -14,6 +15,14 @@ try:
|
|
|
14
15
|
except ImportError:
|
|
15
16
|
brotli = None
|
|
16
17
|
|
|
18
|
+
try:
|
|
19
|
+
import httpcore
|
|
20
|
+
import h2 # noqa: F401
|
|
21
|
+
|
|
22
|
+
HTTP2_ENABLED = True
|
|
23
|
+
except ImportError:
|
|
24
|
+
HTTP2_ENABLED = False
|
|
25
|
+
|
|
17
26
|
import urllib3
|
|
18
27
|
import certifi
|
|
19
28
|
|
|
@@ -22,20 +31,23 @@ from sentry_sdk.utils import Dsn, logger, capture_internal_exceptions
|
|
|
22
31
|
from sentry_sdk.worker import BackgroundWorker
|
|
23
32
|
from sentry_sdk.envelope import Envelope, Item, PayloadRef
|
|
24
33
|
|
|
25
|
-
from typing import TYPE_CHECKING
|
|
34
|
+
from typing import TYPE_CHECKING
|
|
26
35
|
|
|
27
36
|
if TYPE_CHECKING:
|
|
28
|
-
from typing import
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
from typing import (
|
|
38
|
+
List,
|
|
39
|
+
Dict,
|
|
40
|
+
Any,
|
|
41
|
+
Callable,
|
|
42
|
+
DefaultDict,
|
|
43
|
+
Iterable,
|
|
44
|
+
Mapping,
|
|
45
|
+
Optional,
|
|
46
|
+
Tuple,
|
|
47
|
+
Type,
|
|
48
|
+
Union,
|
|
49
|
+
Self,
|
|
50
|
+
)
|
|
39
51
|
from urllib3.poolmanager import PoolManager
|
|
40
52
|
from urllib3.poolmanager import ProxyManager
|
|
41
53
|
|
|
@@ -62,10 +74,9 @@ class Transport(ABC):
|
|
|
62
74
|
A transport is used to send an event to sentry.
|
|
63
75
|
"""
|
|
64
76
|
|
|
65
|
-
parsed_dsn
|
|
77
|
+
parsed_dsn: Optional[Dsn] = None
|
|
66
78
|
|
|
67
|
-
def __init__(self, options=None):
|
|
68
|
-
# type: (Self, Optional[Dict[str, Any]]) -> None
|
|
79
|
+
def __init__(self: Self, options: Optional[Dict[str, Any]] = None) -> None:
|
|
69
80
|
self.options = options
|
|
70
81
|
if options and options["dsn"] is not None and options["dsn"]:
|
|
71
82
|
self.parsed_dsn = Dsn(options["dsn"])
|
|
@@ -73,8 +84,7 @@ class Transport(ABC):
|
|
|
73
84
|
self.parsed_dsn = None
|
|
74
85
|
|
|
75
86
|
@abstractmethod
|
|
76
|
-
def capture_envelope(self, envelope):
|
|
77
|
-
# type: (Self, Envelope) -> None
|
|
87
|
+
def capture_envelope(self: Self, envelope: Envelope) -> None:
|
|
78
88
|
"""
|
|
79
89
|
Send an envelope to Sentry.
|
|
80
90
|
|
|
@@ -85,11 +95,10 @@ class Transport(ABC):
|
|
|
85
95
|
pass
|
|
86
96
|
|
|
87
97
|
def flush(
|
|
88
|
-
self,
|
|
89
|
-
timeout,
|
|
90
|
-
callback=None,
|
|
91
|
-
):
|
|
92
|
-
# type: (Self, float, Optional[Any]) -> None
|
|
98
|
+
self: Self,
|
|
99
|
+
timeout: float,
|
|
100
|
+
callback: Optional[Any] = None,
|
|
101
|
+
) -> None:
|
|
93
102
|
"""
|
|
94
103
|
Wait `timeout` seconds for the current events to be sent out.
|
|
95
104
|
|
|
@@ -98,8 +107,7 @@ class Transport(ABC):
|
|
|
98
107
|
"""
|
|
99
108
|
return None
|
|
100
109
|
|
|
101
|
-
def kill(self):
|
|
102
|
-
# type: (Self) -> None
|
|
110
|
+
def kill(self: Self) -> None:
|
|
103
111
|
"""
|
|
104
112
|
Forcefully kills the transport.
|
|
105
113
|
|
|
@@ -109,14 +117,13 @@ class Transport(ABC):
|
|
|
109
117
|
return None
|
|
110
118
|
|
|
111
119
|
def record_lost_event(
|
|
112
|
-
self,
|
|
113
|
-
reason
|
|
114
|
-
data_category
|
|
115
|
-
item
|
|
120
|
+
self: Self,
|
|
121
|
+
reason: str,
|
|
122
|
+
data_category: Optional[EventDataCategory] = None,
|
|
123
|
+
item: Optional[Item] = None,
|
|
116
124
|
*,
|
|
117
|
-
quantity=1,
|
|
118
|
-
):
|
|
119
|
-
# type: (...) -> None
|
|
125
|
+
quantity: int = 1,
|
|
126
|
+
) -> None:
|
|
120
127
|
"""This increments a counter for event loss by reason and
|
|
121
128
|
data category by the given positive-int quantity (default 1).
|
|
122
129
|
|
|
@@ -133,20 +140,13 @@ class Transport(ABC):
|
|
|
133
140
|
"""
|
|
134
141
|
return None
|
|
135
142
|
|
|
136
|
-
def is_healthy(self):
|
|
137
|
-
# type: (Self) -> bool
|
|
143
|
+
def is_healthy(self: Self) -> bool:
|
|
138
144
|
return True
|
|
139
145
|
|
|
140
|
-
def __del__(self):
|
|
141
|
-
# type: (Self) -> None
|
|
142
|
-
try:
|
|
143
|
-
self.kill()
|
|
144
|
-
except Exception:
|
|
145
|
-
pass
|
|
146
|
-
|
|
147
146
|
|
|
148
|
-
def _parse_rate_limits(
|
|
149
|
-
|
|
147
|
+
def _parse_rate_limits(
|
|
148
|
+
header: str, now: Optional[datetime] = None
|
|
149
|
+
) -> Iterable[Tuple[Optional[str], datetime]]:
|
|
150
150
|
if now is None:
|
|
151
151
|
now = datetime.now(timezone.utc)
|
|
152
152
|
|
|
@@ -157,7 +157,6 @@ def _parse_rate_limits(header, now=None):
|
|
|
157
157
|
|
|
158
158
|
retry_after = now + timedelta(seconds=int(retry_after_val))
|
|
159
159
|
for category in categories and categories.split(";") or (None,):
|
|
160
|
-
category = cast("Optional[EventDataCategory]", category)
|
|
161
160
|
yield category, retry_after
|
|
162
161
|
except (LookupError, ValueError):
|
|
163
162
|
continue
|
|
@@ -168,21 +167,20 @@ class BaseHttpTransport(Transport):
|
|
|
168
167
|
|
|
169
168
|
TIMEOUT = 30 # seconds
|
|
170
169
|
|
|
171
|
-
def __init__(self, options):
|
|
172
|
-
# type: (Self, Dict[str, Any]) -> None
|
|
170
|
+
def __init__(self: Self, options: Dict[str, Any]) -> None:
|
|
173
171
|
from sentry_sdk.consts import VERSION
|
|
174
172
|
|
|
175
173
|
Transport.__init__(self, options)
|
|
176
174
|
assert self.parsed_dsn is not None
|
|
177
|
-
self.options
|
|
175
|
+
self.options: Dict[str, Any] = options
|
|
178
176
|
self._worker = BackgroundWorker(queue_size=options["transport_queue_size"])
|
|
179
177
|
self._auth = self.parsed_dsn.to_auth("sentry.python/%s" % VERSION)
|
|
180
|
-
self._disabled_until
|
|
178
|
+
self._disabled_until: Dict[Optional[str], datetime] = {}
|
|
181
179
|
# We only use this Retry() class for the `get_retry_after` method it exposes
|
|
182
180
|
self._retry = urllib3.util.Retry()
|
|
183
|
-
self._discarded_events =
|
|
184
|
-
int
|
|
185
|
-
)
|
|
181
|
+
self._discarded_events: DefaultDict[Tuple[EventDataCategory, str], int] = (
|
|
182
|
+
defaultdict(int)
|
|
183
|
+
)
|
|
186
184
|
self._last_client_report_sent = time.time()
|
|
187
185
|
|
|
188
186
|
self._pool = self._make_pool()
|
|
@@ -227,14 +225,13 @@ class BaseHttpTransport(Transport):
|
|
|
227
225
|
self._compression_level = 4
|
|
228
226
|
|
|
229
227
|
def record_lost_event(
|
|
230
|
-
self,
|
|
231
|
-
reason
|
|
232
|
-
data_category
|
|
233
|
-
item
|
|
228
|
+
self: Self,
|
|
229
|
+
reason: str,
|
|
230
|
+
data_category: Optional[EventDataCategory] = None,
|
|
231
|
+
item: Optional[Item] = None,
|
|
234
232
|
*,
|
|
235
|
-
quantity=1,
|
|
236
|
-
):
|
|
237
|
-
# type: (...) -> None
|
|
233
|
+
quantity: int = 1,
|
|
234
|
+
) -> None:
|
|
238
235
|
if not self.options["send_client_reports"]:
|
|
239
236
|
return
|
|
240
237
|
|
|
@@ -247,9 +244,7 @@ class BaseHttpTransport(Transport):
|
|
|
247
244
|
event = item.get_transaction_event() or {}
|
|
248
245
|
|
|
249
246
|
# +1 for the transaction itself
|
|
250
|
-
span_count = (
|
|
251
|
-
len(cast(List[Dict[str, object]], event.get("spans") or [])) + 1
|
|
252
|
-
)
|
|
247
|
+
span_count = len(event.get("spans") or []) + 1
|
|
253
248
|
self.record_lost_event(reason, "span", quantity=span_count)
|
|
254
249
|
|
|
255
250
|
elif data_category == "attachment":
|
|
@@ -262,12 +257,12 @@ class BaseHttpTransport(Transport):
|
|
|
262
257
|
|
|
263
258
|
self._discarded_events[data_category, reason] += quantity
|
|
264
259
|
|
|
265
|
-
def _get_header_value(self, response, header):
|
|
266
|
-
# type: (Self, Any, str) -> Optional[str]
|
|
260
|
+
def _get_header_value(self: Self, response: Any, header: str) -> Optional[str]:
|
|
267
261
|
return response.headers.get(header)
|
|
268
262
|
|
|
269
|
-
def _update_rate_limits(
|
|
270
|
-
|
|
263
|
+
def _update_rate_limits(
|
|
264
|
+
self: Self, response: Union[urllib3.BaseHTTPResponse, httpcore.Response]
|
|
265
|
+
) -> None:
|
|
271
266
|
|
|
272
267
|
# new sentries with more rate limit insights. We honor this header
|
|
273
268
|
# no matter of the status code to update our internal rate limits.
|
|
@@ -292,16 +287,13 @@ class BaseHttpTransport(Transport):
|
|
|
292
287
|
)
|
|
293
288
|
|
|
294
289
|
def _send_request(
|
|
295
|
-
self,
|
|
296
|
-
body,
|
|
297
|
-
headers,
|
|
298
|
-
endpoint_type=EndpointType.ENVELOPE,
|
|
299
|
-
envelope=None,
|
|
300
|
-
):
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
def record_loss(reason):
|
|
304
|
-
# type: (str) -> None
|
|
290
|
+
self: Self,
|
|
291
|
+
body: bytes,
|
|
292
|
+
headers: Dict[str, str],
|
|
293
|
+
endpoint_type: EndpointType = EndpointType.ENVELOPE,
|
|
294
|
+
envelope: Optional[Envelope] = None,
|
|
295
|
+
) -> None:
|
|
296
|
+
def record_loss(reason: str) -> None:
|
|
305
297
|
if envelope is None:
|
|
306
298
|
self.record_lost_event(reason, data_category="error")
|
|
307
299
|
else:
|
|
@@ -348,12 +340,12 @@ class BaseHttpTransport(Transport):
|
|
|
348
340
|
finally:
|
|
349
341
|
response.close()
|
|
350
342
|
|
|
351
|
-
def on_dropped_event(self, _reason):
|
|
352
|
-
# type: (Self, str) -> None
|
|
343
|
+
def on_dropped_event(self: Self, _reason: str) -> None:
|
|
353
344
|
return None
|
|
354
345
|
|
|
355
|
-
def _fetch_pending_client_report(
|
|
356
|
-
|
|
346
|
+
def _fetch_pending_client_report(
|
|
347
|
+
self: Self, force: bool = False, interval: int = 60
|
|
348
|
+
) -> Optional[Item]:
|
|
357
349
|
if not self.options["send_client_reports"]:
|
|
358
350
|
return None
|
|
359
351
|
|
|
@@ -383,37 +375,30 @@ class BaseHttpTransport(Transport):
|
|
|
383
375
|
type="client_report",
|
|
384
376
|
)
|
|
385
377
|
|
|
386
|
-
def _flush_client_reports(self, force=False):
|
|
387
|
-
# type: (Self, bool) -> None
|
|
378
|
+
def _flush_client_reports(self: Self, force: bool = False) -> None:
|
|
388
379
|
client_report = self._fetch_pending_client_report(force=force, interval=60)
|
|
389
380
|
if client_report is not None:
|
|
390
381
|
self.capture_envelope(Envelope(items=[client_report]))
|
|
391
382
|
|
|
392
|
-
def _check_disabled(self, category):
|
|
393
|
-
|
|
394
|
-
def _disabled(bucket):
|
|
395
|
-
# type: (Any) -> bool
|
|
383
|
+
def _check_disabled(self: Self, category: EventDataCategory) -> bool:
|
|
384
|
+
def _disabled(bucket: Optional[EventDataCategory]) -> bool:
|
|
396
385
|
ts = self._disabled_until.get(bucket)
|
|
397
386
|
return ts is not None and ts > datetime.now(timezone.utc)
|
|
398
387
|
|
|
399
388
|
return _disabled(category) or _disabled(None)
|
|
400
389
|
|
|
401
|
-
def _is_rate_limited(self):
|
|
402
|
-
# type: (Self) -> bool
|
|
390
|
+
def _is_rate_limited(self: Self) -> bool:
|
|
403
391
|
return any(
|
|
404
392
|
ts > datetime.now(timezone.utc) for ts in self._disabled_until.values()
|
|
405
393
|
)
|
|
406
394
|
|
|
407
|
-
def _is_worker_full(self):
|
|
408
|
-
# type: (Self) -> bool
|
|
395
|
+
def _is_worker_full(self: Self) -> bool:
|
|
409
396
|
return self._worker.full()
|
|
410
397
|
|
|
411
|
-
def is_healthy(self):
|
|
412
|
-
# type: (Self) -> bool
|
|
398
|
+
def is_healthy(self: Self) -> bool:
|
|
413
399
|
return not (self._is_worker_full() or self._is_rate_limited())
|
|
414
400
|
|
|
415
|
-
def _send_envelope(self, envelope):
|
|
416
|
-
# type: (Self, Envelope) -> None
|
|
401
|
+
def _send_envelope(self: Self, envelope: Envelope) -> None:
|
|
417
402
|
|
|
418
403
|
# remove all items from the envelope which are over quota
|
|
419
404
|
new_items = []
|
|
@@ -465,8 +450,9 @@ class BaseHttpTransport(Transport):
|
|
|
465
450
|
)
|
|
466
451
|
return None
|
|
467
452
|
|
|
468
|
-
def _serialize_envelope(
|
|
469
|
-
|
|
453
|
+
def _serialize_envelope(
|
|
454
|
+
self: Self, envelope: Envelope
|
|
455
|
+
) -> tuple[Optional[str], io.BytesIO]:
|
|
470
456
|
content_encoding = None
|
|
471
457
|
body = io.BytesIO()
|
|
472
458
|
if self._compression_level == 0 or self._compression_algo is None:
|
|
@@ -487,12 +473,10 @@ class BaseHttpTransport(Transport):
|
|
|
487
473
|
|
|
488
474
|
return content_encoding, body
|
|
489
475
|
|
|
490
|
-
def _get_pool_options(self):
|
|
491
|
-
# type: (Self) -> Dict[str, Any]
|
|
476
|
+
def _get_pool_options(self: Self) -> Dict[str, Any]:
|
|
492
477
|
raise NotImplementedError()
|
|
493
478
|
|
|
494
|
-
def _in_no_proxy(self, parsed_dsn):
|
|
495
|
-
# type: (Self, Dsn) -> bool
|
|
479
|
+
def _in_no_proxy(self: Self, parsed_dsn: Dsn) -> bool:
|
|
496
480
|
no_proxy = getproxies().get("no")
|
|
497
481
|
if not no_proxy:
|
|
498
482
|
return False
|
|
@@ -502,26 +486,28 @@ class BaseHttpTransport(Transport):
|
|
|
502
486
|
return True
|
|
503
487
|
return False
|
|
504
488
|
|
|
505
|
-
def _make_pool(
|
|
506
|
-
|
|
489
|
+
def _make_pool(
|
|
490
|
+
self: Self,
|
|
491
|
+
) -> Union[
|
|
492
|
+
PoolManager,
|
|
493
|
+
ProxyManager,
|
|
494
|
+
httpcore.SOCKSProxy,
|
|
495
|
+
httpcore.HTTPProxy,
|
|
496
|
+
httpcore.ConnectionPool,
|
|
497
|
+
]:
|
|
507
498
|
raise NotImplementedError()
|
|
508
499
|
|
|
509
500
|
def _request(
|
|
510
|
-
self,
|
|
511
|
-
method,
|
|
512
|
-
endpoint_type,
|
|
513
|
-
body,
|
|
514
|
-
headers,
|
|
515
|
-
):
|
|
516
|
-
# type: (Self, str, EndpointType, Any, Mapping[str, str]) -> Union[urllib3.BaseHTTPResponse, httpcore.Response]
|
|
501
|
+
self: Self,
|
|
502
|
+
method: str,
|
|
503
|
+
endpoint_type: EndpointType,
|
|
504
|
+
body: Any,
|
|
505
|
+
headers: Mapping[str, str],
|
|
506
|
+
) -> Union[urllib3.BaseHTTPResponse, httpcore.Response]:
|
|
517
507
|
raise NotImplementedError()
|
|
518
508
|
|
|
519
|
-
def capture_envelope(
|
|
520
|
-
|
|
521
|
-
):
|
|
522
|
-
# type: (...) -> None
|
|
523
|
-
def send_envelope_wrapper():
|
|
524
|
-
# type: () -> None
|
|
509
|
+
def capture_envelope(self: Self, envelope: Envelope) -> None:
|
|
510
|
+
def send_envelope_wrapper() -> None:
|
|
525
511
|
with capture_internal_exceptions():
|
|
526
512
|
self._send_envelope(envelope)
|
|
527
513
|
self._flush_client_reports()
|
|
@@ -532,19 +518,17 @@ class BaseHttpTransport(Transport):
|
|
|
532
518
|
self.record_lost_event("queue_overflow", item=item)
|
|
533
519
|
|
|
534
520
|
def flush(
|
|
535
|
-
self,
|
|
536
|
-
timeout,
|
|
537
|
-
callback=None,
|
|
538
|
-
):
|
|
539
|
-
# type: (Self, float, Optional[Callable[[int, float], None]]) -> None
|
|
521
|
+
self: Self,
|
|
522
|
+
timeout: float,
|
|
523
|
+
callback: Optional[Callable[[int, float], None]] = None,
|
|
524
|
+
) -> None:
|
|
540
525
|
logger.debug("Flushing HTTP transport")
|
|
541
526
|
|
|
542
527
|
if timeout > 0:
|
|
543
528
|
self._worker.submit(lambda: self._flush_client_reports(force=True))
|
|
544
529
|
self._worker.flush(timeout, callback)
|
|
545
530
|
|
|
546
|
-
def kill(self):
|
|
547
|
-
# type: (Self) -> None
|
|
531
|
+
def kill(self: Self) -> None:
|
|
548
532
|
logger.debug("Killing HTTP transport")
|
|
549
533
|
self._worker.kill()
|
|
550
534
|
|
|
@@ -553,8 +537,7 @@ class HttpTransport(BaseHttpTransport):
|
|
|
553
537
|
if TYPE_CHECKING:
|
|
554
538
|
_pool: Union[PoolManager, ProxyManager]
|
|
555
539
|
|
|
556
|
-
def _get_pool_options(self):
|
|
557
|
-
# type: (Self) -> Dict[str, Any]
|
|
540
|
+
def _get_pool_options(self: Self) -> Dict[str, Any]:
|
|
558
541
|
|
|
559
542
|
num_pools = self.options.get("_experiments", {}).get("transport_num_pools")
|
|
560
543
|
options = {
|
|
@@ -563,7 +546,7 @@ class HttpTransport(BaseHttpTransport):
|
|
|
563
546
|
"timeout": urllib3.Timeout(total=self.TIMEOUT),
|
|
564
547
|
}
|
|
565
548
|
|
|
566
|
-
socket_options
|
|
549
|
+
socket_options: Optional[List[Tuple[int, int, int | bytes]]] = None
|
|
567
550
|
|
|
568
551
|
if self.options["socket_options"] is not None:
|
|
569
552
|
socket_options = self.options["socket_options"]
|
|
@@ -596,8 +579,7 @@ class HttpTransport(BaseHttpTransport):
|
|
|
596
579
|
|
|
597
580
|
return options
|
|
598
581
|
|
|
599
|
-
def _make_pool(self):
|
|
600
|
-
# type: (Self) -> Union[PoolManager, ProxyManager]
|
|
582
|
+
def _make_pool(self: Self) -> Union[PoolManager, ProxyManager]:
|
|
601
583
|
if self.parsed_dsn is None:
|
|
602
584
|
raise ValueError("Cannot create HTTP-based transport without valid DSN")
|
|
603
585
|
|
|
@@ -643,13 +625,12 @@ class HttpTransport(BaseHttpTransport):
|
|
|
643
625
|
return urllib3.PoolManager(**opts)
|
|
644
626
|
|
|
645
627
|
def _request(
|
|
646
|
-
self,
|
|
647
|
-
method,
|
|
648
|
-
endpoint_type,
|
|
649
|
-
body,
|
|
650
|
-
headers,
|
|
651
|
-
):
|
|
652
|
-
# type: (Self, str, EndpointType, Any, Mapping[str, str]) -> urllib3.BaseHTTPResponse
|
|
628
|
+
self: Self,
|
|
629
|
+
method: str,
|
|
630
|
+
endpoint_type: EndpointType,
|
|
631
|
+
body: Any,
|
|
632
|
+
headers: Mapping[str, str],
|
|
633
|
+
) -> urllib3.BaseHTTPResponse:
|
|
653
634
|
return self._pool.request(
|
|
654
635
|
method,
|
|
655
636
|
self._auth.get_api_url(endpoint_type),
|
|
@@ -658,14 +639,10 @@ class HttpTransport(BaseHttpTransport):
|
|
|
658
639
|
)
|
|
659
640
|
|
|
660
641
|
|
|
661
|
-
|
|
662
|
-
import httpcore
|
|
663
|
-
import h2 # noqa: F401
|
|
664
|
-
except ImportError:
|
|
642
|
+
if not HTTP2_ENABLED:
|
|
665
643
|
# Sorry, no Http2Transport for you
|
|
666
644
|
class Http2Transport(HttpTransport):
|
|
667
|
-
def __init__(self, options):
|
|
668
|
-
# type: (Self, Dict[str, Any]) -> None
|
|
645
|
+
def __init__(self: Self, options: Dict[str, Any]) -> None:
|
|
669
646
|
super().__init__(options)
|
|
670
647
|
logger.warning(
|
|
671
648
|
"You tried to use HTTP2Transport but don't have httpcore[http2] installed. Falling back to HTTPTransport."
|
|
@@ -683,8 +660,7 @@ else:
|
|
|
683
660
|
httpcore.SOCKSProxy, httpcore.HTTPProxy, httpcore.ConnectionPool
|
|
684
661
|
]
|
|
685
662
|
|
|
686
|
-
def _get_header_value(self, response, header):
|
|
687
|
-
# type: (Self, httpcore.Response, str) -> Optional[str]
|
|
663
|
+
def _get_header_value(self: Self, response: Any, header: str) -> Optional[str]:
|
|
688
664
|
return next(
|
|
689
665
|
(
|
|
690
666
|
val.decode("ascii")
|
|
@@ -695,13 +671,12 @@ else:
|
|
|
695
671
|
)
|
|
696
672
|
|
|
697
673
|
def _request(
|
|
698
|
-
self,
|
|
699
|
-
method,
|
|
700
|
-
endpoint_type,
|
|
701
|
-
body,
|
|
702
|
-
headers,
|
|
703
|
-
):
|
|
704
|
-
# type: (Self, str, EndpointType, Any, Mapping[str, str]) -> httpcore.Response
|
|
674
|
+
self: Self,
|
|
675
|
+
method: str,
|
|
676
|
+
endpoint_type: EndpointType,
|
|
677
|
+
body: Any,
|
|
678
|
+
headers: Mapping[str, str],
|
|
679
|
+
) -> httpcore.Response:
|
|
705
680
|
response = self._pool.request(
|
|
706
681
|
method,
|
|
707
682
|
self._auth.get_api_url(endpoint_type),
|
|
@@ -718,13 +693,12 @@ else:
|
|
|
718
693
|
)
|
|
719
694
|
return response
|
|
720
695
|
|
|
721
|
-
def _get_pool_options(self):
|
|
722
|
-
|
|
723
|
-
options = {
|
|
696
|
+
def _get_pool_options(self: Self) -> Dict[str, Any]:
|
|
697
|
+
options: Dict[str, Any] = {
|
|
724
698
|
"http2": self.parsed_dsn is not None
|
|
725
699
|
and self.parsed_dsn.scheme == "https",
|
|
726
700
|
"retries": 3,
|
|
727
|
-
}
|
|
701
|
+
}
|
|
728
702
|
|
|
729
703
|
socket_options = (
|
|
730
704
|
self.options["socket_options"]
|
|
@@ -755,8 +729,9 @@ else:
|
|
|
755
729
|
|
|
756
730
|
return options
|
|
757
731
|
|
|
758
|
-
def _make_pool(
|
|
759
|
-
|
|
732
|
+
def _make_pool(
|
|
733
|
+
self: Self,
|
|
734
|
+
) -> Union[httpcore.SOCKSProxy, httpcore.HTTPProxy, httpcore.ConnectionPool]:
|
|
760
735
|
if self.parsed_dsn is None:
|
|
761
736
|
raise ValueError("Cannot create HTTP-based transport without valid DSN")
|
|
762
737
|
proxy = None
|
|
@@ -799,16 +774,15 @@ else:
|
|
|
799
774
|
return httpcore.ConnectionPool(**opts)
|
|
800
775
|
|
|
801
776
|
|
|
802
|
-
def make_transport(options):
|
|
803
|
-
# type: (Dict[str, Any]) -> Optional[Transport]
|
|
777
|
+
def make_transport(options: Dict[str, Any]) -> Optional[Transport]:
|
|
804
778
|
ref_transport = options["transport"]
|
|
805
779
|
|
|
806
780
|
use_http2_transport = options.get("_experiments", {}).get("transport_http2", False)
|
|
807
781
|
|
|
808
782
|
# By default, we use the http transport class
|
|
809
|
-
transport_cls = (
|
|
783
|
+
transport_cls: Type[Transport] = (
|
|
810
784
|
Http2Transport if use_http2_transport else HttpTransport
|
|
811
|
-
)
|
|
785
|
+
)
|
|
812
786
|
|
|
813
787
|
if isinstance(ref_transport, Transport):
|
|
814
788
|
return ref_transport
|