sentry-sdk 3.0.0a2__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 +9 -16
- sentry_sdk/_werkzeug.py +5 -7
- sentry_sdk/ai/monitoring.py +40 -28
- sentry_sdk/ai/utils.py +3 -4
- sentry_sdk/api.py +75 -87
- sentry_sdk/attachments.py +10 -12
- sentry_sdk/client.py +110 -153
- sentry_sdk/consts.py +398 -220
- 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 +16 -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 +19 -28
- 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 +36 -56
- sentry_sdk/integrations/celery/beat.py +22 -26
- 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 +17 -23
- sentry_sdk/integrations/dedupe.py +5 -8
- sentry_sdk/integrations/django/__init__.py +57 -72
- sentry_sdk/integrations/django/asgi.py +24 -32
- 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 +3 -2
- 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 +16 -16
- 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 +28 -35
- sentry_sdk/integrations/loguru.py +15 -19
- sentry_sdk/integrations/modules.py +3 -4
- sentry_sdk/integrations/openai.py +96 -84
- 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 +15 -9
- sentry_sdk/integrations/redis/_sync_common.py +13 -12
- 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 +10 -8
- sentry_sdk/integrations/redis/redis_py_cluster_legacy.py +3 -2
- sentry_sdk/integrations/redis/utils.py +21 -22
- 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 +13 -9
- sentry_sdk/monitor.py +16 -28
- sentry_sdk/opentelemetry/consts.py +11 -4
- sentry_sdk/opentelemetry/contextvars_context.py +17 -15
- 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 +43 -59
- sentry_sdk/opentelemetry/tracing.py +32 -12
- sentry_sdk/opentelemetry/utils.py +180 -196
- 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 -264
- 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 +116 -182
- sentry_sdk/tracing_utils.py +100 -120
- sentry_sdk/transport.py +131 -157
- sentry_sdk/utils.py +232 -309
- sentry_sdk/worker.py +16 -28
- {sentry_sdk-3.0.0a2.dist-info → sentry_sdk-3.0.0a3.dist-info}/METADATA +1 -1
- sentry_sdk-3.0.0a3.dist-info/RECORD +168 -0
- sentry_sdk-3.0.0a2.dist-info/RECORD +0 -154
- {sentry_sdk-3.0.0a2.dist-info → sentry_sdk-3.0.0a3.dist-info}/WHEEL +0 -0
- {sentry_sdk-3.0.0a2.dist-info → sentry_sdk-3.0.0a3.dist-info}/entry_points.txt +0 -0
- {sentry_sdk-3.0.0a2.dist-info → sentry_sdk-3.0.0a3.dist-info}/licenses/LICENSE +0 -0
- {sentry_sdk-3.0.0a2.dist-info → sentry_sdk-3.0.0a3.dist-info}/top_level.txt +0 -0
sentry_sdk/crons/api.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
1
2
|
import uuid
|
|
2
3
|
|
|
3
4
|
import sentry_sdk
|
|
@@ -10,17 +11,16 @@ if TYPE_CHECKING:
|
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
def _create_check_in_event(
|
|
13
|
-
monitor_slug
|
|
14
|
-
check_in_id
|
|
15
|
-
status
|
|
16
|
-
duration_s
|
|
17
|
-
monitor_config
|
|
18
|
-
):
|
|
19
|
-
# type: (...) -> Event
|
|
14
|
+
monitor_slug: Optional[str] = None,
|
|
15
|
+
check_in_id: Optional[str] = None,
|
|
16
|
+
status: Optional[str] = None,
|
|
17
|
+
duration_s: Optional[float] = None,
|
|
18
|
+
monitor_config: Optional[MonitorConfig] = None,
|
|
19
|
+
) -> Event:
|
|
20
20
|
options = sentry_sdk.get_client().options
|
|
21
|
-
check_in_id = check_in_id or uuid.uuid4().hex
|
|
21
|
+
check_in_id = check_in_id or uuid.uuid4().hex
|
|
22
22
|
|
|
23
|
-
check_in = {
|
|
23
|
+
check_in: Event = {
|
|
24
24
|
"type": "check_in",
|
|
25
25
|
"monitor_slug": monitor_slug,
|
|
26
26
|
"check_in_id": check_in_id,
|
|
@@ -28,7 +28,7 @@ def _create_check_in_event(
|
|
|
28
28
|
"duration": duration_s,
|
|
29
29
|
"environment": options.get("environment", None),
|
|
30
30
|
"release": options.get("release", None),
|
|
31
|
-
}
|
|
31
|
+
}
|
|
32
32
|
|
|
33
33
|
if monitor_config:
|
|
34
34
|
check_in["monitor_config"] = monitor_config
|
|
@@ -37,13 +37,12 @@ def _create_check_in_event(
|
|
|
37
37
|
|
|
38
38
|
|
|
39
39
|
def capture_checkin(
|
|
40
|
-
monitor_slug
|
|
41
|
-
check_in_id
|
|
42
|
-
status
|
|
43
|
-
duration
|
|
44
|
-
monitor_config
|
|
45
|
-
):
|
|
46
|
-
# type: (...) -> str
|
|
40
|
+
monitor_slug: Optional[str] = None,
|
|
41
|
+
check_in_id: Optional[str] = None,
|
|
42
|
+
status: Optional[str] = None,
|
|
43
|
+
duration: Optional[float] = None,
|
|
44
|
+
monitor_config: Optional[MonitorConfig] = None,
|
|
45
|
+
) -> str:
|
|
47
46
|
check_in_event = _create_check_in_event(
|
|
48
47
|
monitor_slug=monitor_slug,
|
|
49
48
|
check_in_id=check_in_id,
|
sentry_sdk/crons/decorator.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
1
2
|
from functools import wraps
|
|
2
3
|
from inspect import iscoroutinefunction
|
|
3
4
|
|
|
@@ -16,8 +17,6 @@ if TYPE_CHECKING:
|
|
|
16
17
|
ParamSpec,
|
|
17
18
|
Type,
|
|
18
19
|
TypeVar,
|
|
19
|
-
Union,
|
|
20
|
-
cast,
|
|
21
20
|
overload,
|
|
22
21
|
)
|
|
23
22
|
from sentry_sdk._types import MonitorConfig
|
|
@@ -55,13 +54,15 @@ class monitor: # noqa: N801
|
|
|
55
54
|
```
|
|
56
55
|
"""
|
|
57
56
|
|
|
58
|
-
def __init__(
|
|
59
|
-
|
|
57
|
+
def __init__(
|
|
58
|
+
self,
|
|
59
|
+
monitor_slug: Optional[str] = None,
|
|
60
|
+
monitor_config: Optional[MonitorConfig] = None,
|
|
61
|
+
) -> None:
|
|
60
62
|
self.monitor_slug = monitor_slug
|
|
61
63
|
self.monitor_config = monitor_config
|
|
62
64
|
|
|
63
|
-
def __enter__(self):
|
|
64
|
-
# type: () -> None
|
|
65
|
+
def __enter__(self) -> None:
|
|
65
66
|
self.start_timestamp = now()
|
|
66
67
|
self.check_in_id = capture_checkin(
|
|
67
68
|
monitor_slug=self.monitor_slug,
|
|
@@ -69,8 +70,12 @@ class monitor: # noqa: N801
|
|
|
69
70
|
monitor_config=self.monitor_config,
|
|
70
71
|
)
|
|
71
72
|
|
|
72
|
-
def __exit__(
|
|
73
|
-
|
|
73
|
+
def __exit__(
|
|
74
|
+
self,
|
|
75
|
+
exc_type: Optional[Type[BaseException]],
|
|
76
|
+
exc_value: Optional[BaseException],
|
|
77
|
+
traceback: Optional[TracebackType],
|
|
78
|
+
) -> None:
|
|
74
79
|
duration_s = now() - self.start_timestamp
|
|
75
80
|
|
|
76
81
|
if exc_type is None and exc_value is None and traceback is None:
|
|
@@ -89,46 +94,39 @@ class monitor: # noqa: N801
|
|
|
89
94
|
if TYPE_CHECKING:
|
|
90
95
|
|
|
91
96
|
@overload
|
|
92
|
-
def __call__(
|
|
93
|
-
|
|
97
|
+
def __call__(
|
|
98
|
+
self, fn: Callable[P, Awaitable[Any]]
|
|
99
|
+
) -> Callable[P, Awaitable[Any]]:
|
|
94
100
|
# Unfortunately, mypy does not give us any reliable way to type check the
|
|
95
101
|
# return value of an Awaitable (i.e. async function) for this overload,
|
|
96
102
|
# since calling iscouroutinefunction narrows the type to Callable[P, Awaitable[Any]].
|
|
97
103
|
...
|
|
98
104
|
|
|
99
105
|
@overload
|
|
100
|
-
def __call__(self, fn):
|
|
101
|
-
# type: (Callable[P, R]) -> Callable[P, R]
|
|
102
|
-
...
|
|
106
|
+
def __call__(self, fn: Callable[P, R]) -> Callable[P, R]: ...
|
|
103
107
|
|
|
104
108
|
def __call__(
|
|
105
109
|
self,
|
|
106
|
-
fn
|
|
107
|
-
):
|
|
108
|
-
# type: (...) -> Union[Callable[P, R], Callable[P, Awaitable[Any]]]
|
|
110
|
+
fn: Callable[..., Any],
|
|
111
|
+
) -> Callable[..., Any]:
|
|
109
112
|
if iscoroutinefunction(fn):
|
|
110
113
|
return self._async_wrapper(fn)
|
|
111
|
-
|
|
112
114
|
else:
|
|
113
|
-
if TYPE_CHECKING:
|
|
114
|
-
fn = cast("Callable[P, R]", fn)
|
|
115
115
|
return self._sync_wrapper(fn)
|
|
116
116
|
|
|
117
|
-
def _async_wrapper(
|
|
118
|
-
|
|
117
|
+
def _async_wrapper(
|
|
118
|
+
self, fn: Callable[P, Awaitable[Any]]
|
|
119
|
+
) -> Callable[P, Awaitable[Any]]:
|
|
119
120
|
@wraps(fn)
|
|
120
|
-
async def inner(*args:
|
|
121
|
-
# type: (...) -> R
|
|
121
|
+
async def inner(*args: P.args, **kwargs: P.kwargs) -> R:
|
|
122
122
|
with self:
|
|
123
123
|
return await fn(*args, **kwargs)
|
|
124
124
|
|
|
125
125
|
return inner
|
|
126
126
|
|
|
127
|
-
def _sync_wrapper(self, fn):
|
|
128
|
-
# type: (Callable[P, R]) -> Callable[P, R]
|
|
127
|
+
def _sync_wrapper(self, fn: Callable[P, R]) -> Callable[P, R]:
|
|
129
128
|
@wraps(fn)
|
|
130
|
-
def inner(*args:
|
|
131
|
-
# type: (...) -> R
|
|
129
|
+
def inner(*args: P.args, **kwargs: P.kwargs) -> R:
|
|
132
130
|
with self:
|
|
133
131
|
return fn(*args, **kwargs)
|
|
134
132
|
|
sentry_sdk/debug.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
1
2
|
import sys
|
|
2
3
|
import logging
|
|
3
4
|
|
|
@@ -8,22 +9,19 @@ from logging import LogRecord
|
|
|
8
9
|
|
|
9
10
|
|
|
10
11
|
class _DebugFilter(logging.Filter):
|
|
11
|
-
def filter(self, record):
|
|
12
|
-
# type: (LogRecord) -> bool
|
|
12
|
+
def filter(self, record: LogRecord) -> bool:
|
|
13
13
|
if _client_init_debug.get(False):
|
|
14
14
|
return True
|
|
15
15
|
|
|
16
16
|
return get_client().options["debug"]
|
|
17
17
|
|
|
18
18
|
|
|
19
|
-
def init_debug_support():
|
|
20
|
-
# type: () -> None
|
|
19
|
+
def init_debug_support() -> None:
|
|
21
20
|
if not logger.handlers:
|
|
22
21
|
configure_logger()
|
|
23
22
|
|
|
24
23
|
|
|
25
|
-
def configure_logger():
|
|
26
|
-
# type: () -> None
|
|
24
|
+
def configure_logger() -> None:
|
|
27
25
|
_handler = logging.StreamHandler(sys.stderr)
|
|
28
26
|
_handler.setFormatter(logging.Formatter(" [sentry] %(levelname)s: %(message)s"))
|
|
29
27
|
logger.addHandler(_handler)
|
sentry_sdk/envelope.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
1
2
|
import io
|
|
2
3
|
import json
|
|
3
4
|
import mimetypes
|
|
@@ -8,18 +9,11 @@ from sentry_sdk.utils import json_dumps, capture_internal_exceptions
|
|
|
8
9
|
from typing import TYPE_CHECKING
|
|
9
10
|
|
|
10
11
|
if TYPE_CHECKING:
|
|
11
|
-
from typing import Any
|
|
12
|
-
from typing import Optional
|
|
13
|
-
from typing import Union
|
|
14
|
-
from typing import Dict
|
|
15
|
-
from typing import List
|
|
16
|
-
from typing import Iterator
|
|
17
|
-
|
|
18
12
|
from sentry_sdk._types import Event, EventDataCategory
|
|
13
|
+
from typing import Any, Optional, Union, Dict, List, Iterator
|
|
19
14
|
|
|
20
15
|
|
|
21
|
-
def parse_json(data):
|
|
22
|
-
# type: (Union[bytes, str]) -> Any
|
|
16
|
+
def parse_json(data: Union[bytes, str]) -> Any:
|
|
23
17
|
# on some python 3 versions this needs to be bytes
|
|
24
18
|
if isinstance(data, bytes):
|
|
25
19
|
data = data.decode("utf-8", "replace")
|
|
@@ -35,10 +29,9 @@ class Envelope:
|
|
|
35
29
|
|
|
36
30
|
def __init__(
|
|
37
31
|
self,
|
|
38
|
-
headers
|
|
39
|
-
items
|
|
40
|
-
):
|
|
41
|
-
# type: (...) -> None
|
|
32
|
+
headers: Optional[Dict[str, Any]] = None,
|
|
33
|
+
items: Optional[List[Item]] = None,
|
|
34
|
+
) -> None:
|
|
42
35
|
if headers is not None:
|
|
43
36
|
headers = dict(headers)
|
|
44
37
|
self.headers = headers or {}
|
|
@@ -49,35 +42,22 @@ class Envelope:
|
|
|
49
42
|
self.items = items
|
|
50
43
|
|
|
51
44
|
@property
|
|
52
|
-
def description(self):
|
|
53
|
-
# type: (...) -> str
|
|
45
|
+
def description(self) -> str:
|
|
54
46
|
return "envelope with %s items (%s)" % (
|
|
55
47
|
len(self.items),
|
|
56
48
|
", ".join(x.data_category for x in self.items),
|
|
57
49
|
)
|
|
58
50
|
|
|
59
|
-
def add_event(
|
|
60
|
-
self, event # type: Event
|
|
61
|
-
):
|
|
62
|
-
# type: (...) -> None
|
|
51
|
+
def add_event(self, event: Event) -> None:
|
|
63
52
|
self.add_item(Item(payload=PayloadRef(json=event), type="event"))
|
|
64
53
|
|
|
65
|
-
def add_transaction(
|
|
66
|
-
self, transaction # type: Event
|
|
67
|
-
):
|
|
68
|
-
# type: (...) -> None
|
|
54
|
+
def add_transaction(self, transaction: Event) -> None:
|
|
69
55
|
self.add_item(Item(payload=PayloadRef(json=transaction), type="transaction"))
|
|
70
56
|
|
|
71
|
-
def add_profile(
|
|
72
|
-
self, profile # type: Any
|
|
73
|
-
):
|
|
74
|
-
# type: (...) -> None
|
|
57
|
+
def add_profile(self, profile: Any) -> None:
|
|
75
58
|
self.add_item(Item(payload=PayloadRef(json=profile), type="profile"))
|
|
76
59
|
|
|
77
|
-
def add_profile_chunk(
|
|
78
|
-
self, profile_chunk # type: Any
|
|
79
|
-
):
|
|
80
|
-
# type: (...) -> None
|
|
60
|
+
def add_profile_chunk(self, profile_chunk: Any) -> None:
|
|
81
61
|
self.add_item(
|
|
82
62
|
Item(
|
|
83
63
|
payload=PayloadRef(json=profile_chunk),
|
|
@@ -86,72 +66,50 @@ class Envelope:
|
|
|
86
66
|
)
|
|
87
67
|
)
|
|
88
68
|
|
|
89
|
-
def add_checkin(
|
|
90
|
-
self, checkin # type: Any
|
|
91
|
-
):
|
|
92
|
-
# type: (...) -> None
|
|
69
|
+
def add_checkin(self, checkin: Any) -> None:
|
|
93
70
|
self.add_item(Item(payload=PayloadRef(json=checkin), type="check_in"))
|
|
94
71
|
|
|
95
|
-
def add_session(
|
|
96
|
-
self, session # type: Union[Session, Any]
|
|
97
|
-
):
|
|
98
|
-
# type: (...) -> None
|
|
72
|
+
def add_session(self, session: Union[Session, Any]) -> None:
|
|
99
73
|
if isinstance(session, Session):
|
|
100
74
|
session = session.to_json()
|
|
101
75
|
self.add_item(Item(payload=PayloadRef(json=session), type="session"))
|
|
102
76
|
|
|
103
|
-
def add_sessions(
|
|
104
|
-
self, sessions # type: Any
|
|
105
|
-
):
|
|
106
|
-
# type: (...) -> None
|
|
77
|
+
def add_sessions(self, sessions: Any) -> None:
|
|
107
78
|
self.add_item(Item(payload=PayloadRef(json=sessions), type="sessions"))
|
|
108
79
|
|
|
109
|
-
def add_item(
|
|
110
|
-
self, item # type: Item
|
|
111
|
-
):
|
|
112
|
-
# type: (...) -> None
|
|
80
|
+
def add_item(self, item: Item) -> None:
|
|
113
81
|
self.items.append(item)
|
|
114
82
|
|
|
115
|
-
def get_event(self):
|
|
116
|
-
# type: (...) -> Optional[Event]
|
|
83
|
+
def get_event(self) -> Optional[Event]:
|
|
117
84
|
for items in self.items:
|
|
118
85
|
event = items.get_event()
|
|
119
86
|
if event is not None:
|
|
120
87
|
return event
|
|
121
88
|
return None
|
|
122
89
|
|
|
123
|
-
def get_transaction_event(self):
|
|
124
|
-
# type: (...) -> Optional[Event]
|
|
90
|
+
def get_transaction_event(self) -> Optional[Event]:
|
|
125
91
|
for item in self.items:
|
|
126
92
|
event = item.get_transaction_event()
|
|
127
93
|
if event is not None:
|
|
128
94
|
return event
|
|
129
95
|
return None
|
|
130
96
|
|
|
131
|
-
def __iter__(self):
|
|
132
|
-
# type: (...) -> Iterator[Item]
|
|
97
|
+
def __iter__(self) -> Iterator[Item]:
|
|
133
98
|
return iter(self.items)
|
|
134
99
|
|
|
135
|
-
def serialize_into(
|
|
136
|
-
self, f # type: Any
|
|
137
|
-
):
|
|
138
|
-
# type: (...) -> None
|
|
100
|
+
def serialize_into(self, f: Any) -> None:
|
|
139
101
|
f.write(json_dumps(self.headers))
|
|
140
102
|
f.write(b"\n")
|
|
141
103
|
for item in self.items:
|
|
142
104
|
item.serialize_into(f)
|
|
143
105
|
|
|
144
|
-
def serialize(self):
|
|
145
|
-
# type: (...) -> bytes
|
|
106
|
+
def serialize(self) -> bytes:
|
|
146
107
|
out = io.BytesIO()
|
|
147
108
|
self.serialize_into(out)
|
|
148
109
|
return out.getvalue()
|
|
149
110
|
|
|
150
111
|
@classmethod
|
|
151
|
-
def deserialize_from(
|
|
152
|
-
cls, f # type: Any
|
|
153
|
-
):
|
|
154
|
-
# type: (...) -> Envelope
|
|
112
|
+
def deserialize_from(cls, f: Any) -> Envelope:
|
|
155
113
|
headers = parse_json(f.readline())
|
|
156
114
|
items = []
|
|
157
115
|
while 1:
|
|
@@ -162,31 +120,25 @@ class Envelope:
|
|
|
162
120
|
return cls(headers=headers, items=items)
|
|
163
121
|
|
|
164
122
|
@classmethod
|
|
165
|
-
def deserialize(
|
|
166
|
-
cls, bytes # type: bytes
|
|
167
|
-
):
|
|
168
|
-
# type: (...) -> Envelope
|
|
123
|
+
def deserialize(cls, bytes: bytes) -> Envelope:
|
|
169
124
|
return cls.deserialize_from(io.BytesIO(bytes))
|
|
170
125
|
|
|
171
|
-
def __repr__(self):
|
|
172
|
-
# type: (...) -> str
|
|
126
|
+
def __repr__(self) -> str:
|
|
173
127
|
return "<Envelope headers=%r items=%r>" % (self.headers, self.items)
|
|
174
128
|
|
|
175
129
|
|
|
176
130
|
class PayloadRef:
|
|
177
131
|
def __init__(
|
|
178
132
|
self,
|
|
179
|
-
bytes
|
|
180
|
-
path
|
|
181
|
-
json
|
|
182
|
-
):
|
|
183
|
-
# type: (...) -> None
|
|
133
|
+
bytes: Optional[bytes] = None,
|
|
134
|
+
path: Optional[Union[bytes, str]] = None,
|
|
135
|
+
json: Optional[Any] = None,
|
|
136
|
+
) -> None:
|
|
184
137
|
self.json = json
|
|
185
138
|
self.bytes = bytes
|
|
186
139
|
self.path = path
|
|
187
140
|
|
|
188
|
-
def get_bytes(self):
|
|
189
|
-
# type: (...) -> bytes
|
|
141
|
+
def get_bytes(self) -> bytes:
|
|
190
142
|
if self.bytes is None:
|
|
191
143
|
if self.path is not None:
|
|
192
144
|
with capture_internal_exceptions():
|
|
@@ -197,8 +149,7 @@ class PayloadRef:
|
|
|
197
149
|
return self.bytes or b""
|
|
198
150
|
|
|
199
151
|
@property
|
|
200
|
-
def inferred_content_type(self):
|
|
201
|
-
# type: (...) -> str
|
|
152
|
+
def inferred_content_type(self) -> str:
|
|
202
153
|
if self.json is not None:
|
|
203
154
|
return "application/json"
|
|
204
155
|
elif self.path is not None:
|
|
@@ -210,20 +161,19 @@ class PayloadRef:
|
|
|
210
161
|
return ty
|
|
211
162
|
return "application/octet-stream"
|
|
212
163
|
|
|
213
|
-
def __repr__(self):
|
|
214
|
-
# type: (...) -> str
|
|
164
|
+
def __repr__(self) -> str:
|
|
215
165
|
return "<Payload %r>" % (self.inferred_content_type,)
|
|
216
166
|
|
|
217
167
|
|
|
218
168
|
class Item:
|
|
219
169
|
def __init__(
|
|
220
170
|
self,
|
|
221
|
-
payload
|
|
222
|
-
headers
|
|
223
|
-
type
|
|
224
|
-
content_type
|
|
225
|
-
filename
|
|
226
|
-
):
|
|
171
|
+
payload: Union[bytes, str, PayloadRef],
|
|
172
|
+
headers: Optional[Dict[str, Any]] = None,
|
|
173
|
+
type: Optional[str] = None,
|
|
174
|
+
content_type: Optional[str] = None,
|
|
175
|
+
filename: Optional[str] = None,
|
|
176
|
+
) -> None:
|
|
227
177
|
if headers is not None:
|
|
228
178
|
headers = dict(headers)
|
|
229
179
|
elif headers is None:
|
|
@@ -247,8 +197,7 @@ class Item:
|
|
|
247
197
|
|
|
248
198
|
self.payload = payload
|
|
249
199
|
|
|
250
|
-
def __repr__(self):
|
|
251
|
-
# type: (...) -> str
|
|
200
|
+
def __repr__(self) -> str:
|
|
252
201
|
return "<Item headers=%r payload=%r data_category=%r>" % (
|
|
253
202
|
self.headers,
|
|
254
203
|
self.payload,
|
|
@@ -256,13 +205,11 @@ class Item:
|
|
|
256
205
|
)
|
|
257
206
|
|
|
258
207
|
@property
|
|
259
|
-
def type(self):
|
|
260
|
-
# type: (...) -> Optional[str]
|
|
208
|
+
def type(self) -> Optional[str]:
|
|
261
209
|
return self.headers.get("type")
|
|
262
210
|
|
|
263
211
|
@property
|
|
264
|
-
def data_category(self):
|
|
265
|
-
# type: (...) -> EventDataCategory
|
|
212
|
+
def data_category(self) -> EventDataCategory:
|
|
266
213
|
ty = self.headers.get("type")
|
|
267
214
|
if ty == "session" or ty == "sessions":
|
|
268
215
|
return "session"
|
|
@@ -285,12 +232,10 @@ class Item:
|
|
|
285
232
|
else:
|
|
286
233
|
return "default"
|
|
287
234
|
|
|
288
|
-
def get_bytes(self):
|
|
289
|
-
# type: (...) -> bytes
|
|
235
|
+
def get_bytes(self) -> bytes:
|
|
290
236
|
return self.payload.get_bytes()
|
|
291
237
|
|
|
292
|
-
def get_event(self):
|
|
293
|
-
# type: (...) -> Optional[Event]
|
|
238
|
+
def get_event(self) -> Optional[Event]:
|
|
294
239
|
"""
|
|
295
240
|
Returns an error event if there is one.
|
|
296
241
|
"""
|
|
@@ -298,16 +243,12 @@ class Item:
|
|
|
298
243
|
return self.payload.json
|
|
299
244
|
return None
|
|
300
245
|
|
|
301
|
-
def get_transaction_event(self):
|
|
302
|
-
# type: (...) -> Optional[Event]
|
|
246
|
+
def get_transaction_event(self) -> Optional[Event]:
|
|
303
247
|
if self.type == "transaction" and self.payload.json is not None:
|
|
304
248
|
return self.payload.json
|
|
305
249
|
return None
|
|
306
250
|
|
|
307
|
-
def serialize_into(
|
|
308
|
-
self, f # type: Any
|
|
309
|
-
):
|
|
310
|
-
# type: (...) -> None
|
|
251
|
+
def serialize_into(self, f: Any) -> None:
|
|
311
252
|
headers = dict(self.headers)
|
|
312
253
|
bytes = self.get_bytes()
|
|
313
254
|
headers["length"] = len(bytes)
|
|
@@ -316,17 +257,13 @@ class Item:
|
|
|
316
257
|
f.write(bytes)
|
|
317
258
|
f.write(b"\n")
|
|
318
259
|
|
|
319
|
-
def serialize(self):
|
|
320
|
-
# type: (...) -> bytes
|
|
260
|
+
def serialize(self) -> bytes:
|
|
321
261
|
out = io.BytesIO()
|
|
322
262
|
self.serialize_into(out)
|
|
323
263
|
return out.getvalue()
|
|
324
264
|
|
|
325
265
|
@classmethod
|
|
326
|
-
def deserialize_from(
|
|
327
|
-
cls, f # type: Any
|
|
328
|
-
):
|
|
329
|
-
# type: (...) -> Optional[Item]
|
|
266
|
+
def deserialize_from(cls, f: Any) -> Optional[Item]:
|
|
330
267
|
line = f.readline().rstrip()
|
|
331
268
|
if not line:
|
|
332
269
|
return None
|
|
@@ -346,8 +283,5 @@ class Item:
|
|
|
346
283
|
return rv
|
|
347
284
|
|
|
348
285
|
@classmethod
|
|
349
|
-
def deserialize(
|
|
350
|
-
cls, bytes # type: bytes
|
|
351
|
-
):
|
|
352
|
-
# type: (...) -> Optional[Item]
|
|
286
|
+
def deserialize(cls, bytes: bytes) -> Optional[Item]:
|
|
353
287
|
return cls.deserialize_from(io.BytesIO(bytes))
|
sentry_sdk/feature_flags.py
CHANGED
|
@@ -1,23 +1,22 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
1
2
|
import copy
|
|
2
3
|
import sentry_sdk
|
|
3
4
|
from sentry_sdk._lru_cache import LRUCache
|
|
4
5
|
from threading import Lock
|
|
5
6
|
|
|
6
|
-
from typing import TYPE_CHECKING
|
|
7
|
+
from typing import TYPE_CHECKING
|
|
7
8
|
|
|
8
9
|
if TYPE_CHECKING:
|
|
9
|
-
from typing import TypedDict
|
|
10
|
+
from typing import Any, TypedDict
|
|
10
11
|
|
|
11
12
|
FlagData = TypedDict("FlagData", {"flag": str, "result": bool})
|
|
12
13
|
|
|
13
|
-
|
|
14
14
|
DEFAULT_FLAG_CAPACITY = 100
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
class FlagBuffer:
|
|
18
18
|
|
|
19
|
-
def __init__(self, capacity):
|
|
20
|
-
# type: (int) -> None
|
|
19
|
+
def __init__(self, capacity: int) -> None:
|
|
21
20
|
self.capacity = capacity
|
|
22
21
|
self.lock = Lock()
|
|
23
22
|
|
|
@@ -25,26 +24,22 @@ class FlagBuffer:
|
|
|
25
24
|
# directly you're on your own!
|
|
26
25
|
self.__buffer = LRUCache(capacity)
|
|
27
26
|
|
|
28
|
-
def clear(self):
|
|
29
|
-
# type: () -> None
|
|
27
|
+
def clear(self) -> None:
|
|
30
28
|
self.__buffer = LRUCache(self.capacity)
|
|
31
29
|
|
|
32
|
-
def __deepcopy__(self, memo):
|
|
33
|
-
# type: (dict[int, Any]) -> FlagBuffer
|
|
30
|
+
def __deepcopy__(self, memo: dict[int, Any]) -> FlagBuffer:
|
|
34
31
|
with self.lock:
|
|
35
32
|
buffer = FlagBuffer(self.capacity)
|
|
36
33
|
buffer.__buffer = copy.deepcopy(self.__buffer, memo)
|
|
37
34
|
return buffer
|
|
38
35
|
|
|
39
|
-
def get(self):
|
|
40
|
-
# type: () -> list[FlagData]
|
|
36
|
+
def get(self) -> list[FlagData]:
|
|
41
37
|
with self.lock:
|
|
42
38
|
return [
|
|
43
39
|
{"flag": key, "result": value} for key, value in self.__buffer.get_all()
|
|
44
40
|
]
|
|
45
41
|
|
|
46
|
-
def set(self, flag, result):
|
|
47
|
-
# type: (str, bool) -> None
|
|
42
|
+
def set(self, flag: str, result: bool) -> None:
|
|
48
43
|
if isinstance(result, FlagBuffer):
|
|
49
44
|
# If someone were to insert `self` into `self` this would create a circular dependency
|
|
50
45
|
# on the lock. This is of course a deadlock. However, this is far outside the expected
|
|
@@ -58,8 +53,7 @@ class FlagBuffer:
|
|
|
58
53
|
self.__buffer.set(flag, result)
|
|
59
54
|
|
|
60
55
|
|
|
61
|
-
def add_feature_flag(flag, result):
|
|
62
|
-
# type: (str, bool) -> None
|
|
56
|
+
def add_feature_flag(flag: str, result: bool) -> None:
|
|
63
57
|
"""
|
|
64
58
|
Records a flag and its value to be sent on subsequent error events.
|
|
65
59
|
We recommend you do this on flag evaluations. Flags are buffered per Sentry scope.
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
1
2
|
from abc import ABC, abstractmethod
|
|
2
3
|
from threading import Lock
|
|
3
4
|
|
|
@@ -23,20 +24,20 @@ _DEFAULT_FAILED_REQUEST_STATUS_CODES = frozenset(range(500, 600))
|
|
|
23
24
|
_installer_lock = Lock()
|
|
24
25
|
|
|
25
26
|
# Set of all integration identifiers we have attempted to install
|
|
26
|
-
_processed_integrations = set()
|
|
27
|
+
_processed_integrations: Set[str] = set()
|
|
27
28
|
|
|
28
29
|
# Set of all integration identifiers we have actually installed
|
|
29
|
-
_installed_integrations = set()
|
|
30
|
+
_installed_integrations: Set[str] = set()
|
|
30
31
|
|
|
31
32
|
|
|
32
33
|
def _generate_default_integrations_iterator(
|
|
33
|
-
integrations
|
|
34
|
-
auto_enabling_integrations
|
|
35
|
-
):
|
|
36
|
-
# type: (...) -> Callable[[bool], Iterator[Type[Integration]]]
|
|
34
|
+
integrations: List[str],
|
|
35
|
+
auto_enabling_integrations: List[str],
|
|
36
|
+
) -> Callable[[bool], Iterator[Type[Integration]]]:
|
|
37
37
|
|
|
38
|
-
def iter_default_integrations(
|
|
39
|
-
|
|
38
|
+
def iter_default_integrations(
|
|
39
|
+
with_auto_enabling_integrations: bool,
|
|
40
|
+
) -> Iterator[Type[Integration]]:
|
|
40
41
|
"""Returns an iterator of the default integration classes:"""
|
|
41
42
|
from importlib import import_module
|
|
42
43
|
|
|
@@ -146,6 +147,7 @@ _MIN_VERSIONS = {
|
|
|
146
147
|
"launchdarkly": (9, 8, 0),
|
|
147
148
|
"loguru": (0, 7, 0),
|
|
148
149
|
"openai": (1, 0, 0),
|
|
150
|
+
"openai_agents": (0, 0, 19),
|
|
149
151
|
"openfeature": (0, 7, 1),
|
|
150
152
|
"quart": (0, 16, 0),
|
|
151
153
|
"ray": (2, 7, 0),
|
|
@@ -165,12 +167,13 @@ _MIN_VERSIONS = {
|
|
|
165
167
|
|
|
166
168
|
|
|
167
169
|
def setup_integrations(
|
|
168
|
-
integrations,
|
|
169
|
-
with_defaults=True,
|
|
170
|
-
with_auto_enabling_integrations=False,
|
|
171
|
-
disabled_integrations
|
|
172
|
-
|
|
173
|
-
|
|
170
|
+
integrations: Sequence[Integration],
|
|
171
|
+
with_defaults: bool = True,
|
|
172
|
+
with_auto_enabling_integrations: bool = False,
|
|
173
|
+
disabled_integrations: Optional[
|
|
174
|
+
Sequence[Union[type[Integration], Integration]]
|
|
175
|
+
] = None,
|
|
176
|
+
) -> Dict[str, Integration]:
|
|
174
177
|
"""
|
|
175
178
|
Given a list of integration instances, this installs them all.
|
|
176
179
|
|
|
@@ -239,8 +242,11 @@ def setup_integrations(
|
|
|
239
242
|
return integrations
|
|
240
243
|
|
|
241
244
|
|
|
242
|
-
def _check_minimum_version(
|
|
243
|
-
|
|
245
|
+
def _check_minimum_version(
|
|
246
|
+
integration: type[Integration],
|
|
247
|
+
version: Optional[tuple[int, ...]],
|
|
248
|
+
package: Optional[str] = None,
|
|
249
|
+
) -> None:
|
|
244
250
|
package = package or integration.identifier
|
|
245
251
|
|
|
246
252
|
if version is None:
|
|
@@ -276,13 +282,12 @@ class Integration(ABC):
|
|
|
276
282
|
install = None
|
|
277
283
|
"""Legacy method, do not implement."""
|
|
278
284
|
|
|
279
|
-
identifier
|
|
285
|
+
identifier: str
|
|
280
286
|
"""String unique ID of integration type"""
|
|
281
287
|
|
|
282
288
|
@staticmethod
|
|
283
289
|
@abstractmethod
|
|
284
|
-
def setup_once():
|
|
285
|
-
# type: () -> None
|
|
290
|
+
def setup_once() -> None:
|
|
286
291
|
"""
|
|
287
292
|
Initialize the integration.
|
|
288
293
|
|