Skip to content

Commit 5717f1b

Browse files
ref: Event Type (#2753)
Implements type hinting for Event via a TypedDict. This commit mainly adjusts type hints; however, there are also some minor code changes to make the code type-safe following the new changes. Some items in the Event could have their types expanded by being defined as TypedDicts themselves. These items have been indicated with TODO comments. Fixes GH-2357
1 parent 1a8db5e commit 5717f1b

33 files changed

+176
-96
lines changed

sentry_sdk/_types.py

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@
99

1010

1111
if TYPE_CHECKING:
12+
from collections.abc import MutableMapping
13+
14+
from datetime import datetime
15+
1216
from types import TracebackType
1317
from typing import Any
1418
from typing import Callable
@@ -19,13 +23,69 @@
1923
from typing import Tuple
2024
from typing import Type
2125
from typing import Union
22-
from typing_extensions import Literal
26+
from typing_extensions import Literal, TypedDict
27+
28+
# "critical" is an alias of "fatal" recognized by Relay
29+
LogLevelStr = Literal["fatal", "critical", "error", "warning", "info", "debug"]
30+
31+
Event = TypedDict(
32+
"Event",
33+
{
34+
"breadcrumbs": dict[
35+
Literal["values"], list[dict[str, Any]]
36+
], # TODO: We can expand on this type
37+
"check_in_id": str,
38+
"contexts": dict[str, dict[str, object]],
39+
"dist": str,
40+
"duration": Optional[float],
41+
"environment": str,
42+
"errors": list[dict[str, Any]], # TODO: We can expand on this type
43+
"event_id": str,
44+
"exception": dict[
45+
Literal["values"], list[dict[str, Any]]
46+
], # TODO: We can expand on this type
47+
"extra": MutableMapping[str, object],
48+
"fingerprint": list[str],
49+
"level": LogLevelStr,
50+
"logentry": Mapping[str, object],
51+
"logger": str,
52+
"measurements": dict[str, object],
53+
"message": str,
54+
"modules": dict[str, str],
55+
"monitor_config": Mapping[str, object],
56+
"monitor_slug": Optional[str],
57+
"platform": Literal["python"],
58+
"profile": object, # Should be sentry_sdk.profiler.Profile, but we can't import that here due to circular imports
59+
"release": str,
60+
"request": dict[str, object],
61+
"sdk": Mapping[str, object],
62+
"server_name": str,
63+
"spans": list[dict[str, object]],
64+
"stacktrace": dict[
65+
str, object
66+
], # We access this key in the code, but I am unsure whether we ever set it
67+
"start_timestamp": datetime,
68+
"status": Optional[str],
69+
"tags": MutableMapping[
70+
str, str
71+
], # Tags must be less than 200 characters each
72+
"threads": dict[
73+
Literal["values"], list[dict[str, Any]]
74+
], # TODO: We can expand on this type
75+
"timestamp": Optional[datetime], # Must be set before sending the event
76+
"transaction": str,
77+
"transaction_info": Mapping[str, Any], # TODO: We can expand on this type
78+
"type": Literal["check_in", "transaction"],
79+
"user": dict[str, object],
80+
"_metrics_summary": dict[str, object],
81+
},
82+
total=False,
83+
)
2384

2485
ExcInfo = Tuple[
2586
Optional[Type[BaseException]], Optional[BaseException], Optional[TracebackType]
2687
]
2788

28-
Event = Dict[str, Any]
2989
Hint = Dict[str, Any]
3090

3191
Breadcrumb = Dict[str, Any]

sentry_sdk/api.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
BreadcrumbHint,
2323
ExcInfo,
2424
MeasurementUnit,
25+
LogLevelStr,
2526
)
2627
from sentry_sdk.tracing import Span
2728

@@ -91,7 +92,7 @@ def capture_event(
9192
@hubmethod
9293
def capture_message(
9394
message, # type: str
94-
level=None, # type: Optional[str]
95+
level=None, # type: Optional[LogLevelStr]
9596
scope=None, # type: Optional[Any]
9697
**scope_kwargs # type: Any
9798
):
@@ -189,7 +190,7 @@ def set_user(value):
189190

190191
@scopemethod
191192
def set_level(value):
192-
# type: (str) -> None
193+
# type: (LogLevelStr) -> None
193194
return Hub.current.scope.set_level(value)
194195

195196

sentry_sdk/client.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
try:
2+
from collections.abc import Mapping
3+
except ImportError:
4+
from collections import Mapping # type: ignore[attr-defined]
5+
16
from importlib import import_module
27
import os
38
import uuid
@@ -38,7 +43,7 @@
3843
from sentry_sdk.utils import ContextVar
3944
from sentry_sdk.sessions import SessionFlusher
4045
from sentry_sdk.envelope import Envelope
41-
from sentry_sdk.profiler import has_profiling_enabled, setup_profiler
46+
from sentry_sdk.profiler import has_profiling_enabled, Profile, setup_profiler
4247
from sentry_sdk.scrubber import EventScrubber
4348
from sentry_sdk.monitor import Monitor
4449
from sentry_sdk.spotlight import setup_spotlight
@@ -393,7 +398,7 @@ def _prepare_event(
393398

394399
for key in "release", "environment", "server_name", "dist":
395400
if event.get(key) is None and self.options[key] is not None:
396-
event[key] = text_type(self.options[key]).strip()
401+
event[key] = text_type(self.options[key]).strip() # type: ignore[literal-required]
397402
if event.get("sdk") is None:
398403
sdk_info = dict(SDK_INFO)
399404
sdk_info["integrations"] = sorted(self.integrations.keys())
@@ -567,7 +572,7 @@ def _update_session_from_event(
567572
errored = True
568573
for error in exceptions:
569574
mechanism = error.get("mechanism")
570-
if mechanism and mechanism.get("handled") is False:
575+
if isinstance(mechanism, Mapping) and mechanism.get("handled") is False:
571576
crashed = True
572577
break
573578

@@ -659,15 +664,15 @@ def capture_event(
659664
headers = {
660665
"event_id": event_opt["event_id"],
661666
"sent_at": format_timestamp(datetime_utcnow()),
662-
}
667+
} # type: dict[str, object]
663668

664669
if dynamic_sampling_context:
665670
headers["trace"] = dynamic_sampling_context
666671

667672
envelope = Envelope(headers=headers)
668673

669674
if is_transaction:
670-
if profile is not None:
675+
if isinstance(profile, Profile):
671676
envelope.add_profile(profile.to_json(event_opt, self.options))
672677
envelope.add_transaction(event_opt)
673678
elif is_checkin:

sentry_sdk/crons/api.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
if TYPE_CHECKING:
88
from typing import Any, Dict, Optional
9+
from sentry_sdk._types import Event
910

1011

1112
def _create_check_in_event(
@@ -15,7 +16,7 @@ def _create_check_in_event(
1516
duration_s=None,
1617
monitor_config=None,
1718
):
18-
# type: (Optional[str], Optional[str], Optional[str], Optional[float], Optional[Dict[str, Any]]) -> Dict[str, Any]
19+
# type: (Optional[str], Optional[str], Optional[str], Optional[float], Optional[Dict[str, Any]]) -> Event
1920
options = Hub.current.client.options if Hub.current.client else {}
2021
check_in_id = check_in_id or uuid.uuid4().hex # type: str
2122

@@ -27,7 +28,7 @@ def _create_check_in_event(
2728
"duration": duration_s,
2829
"environment": options.get("environment", None),
2930
"release": options.get("release", None),
30-
}
31+
} # type: Event
3132

3233
if monitor_config:
3334
check_in["monitor_config"] = monitor_config

sentry_sdk/hub.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
Breadcrumb,
4141
BreadcrumbHint,
4242
ExcInfo,
43+
LogLevelStr,
4344
)
4445
from sentry_sdk.consts import ClientConstructor
4546

@@ -335,7 +336,7 @@ def capture_event(self, event, hint=None, scope=None, **scope_kwargs):
335336
return last_event_id
336337

337338
def capture_message(self, message, level=None, scope=None, **scope_kwargs):
338-
# type: (str, Optional[str], Optional[Scope], Any) -> Optional[str]
339+
# type: (str, Optional[LogLevelStr], Optional[Scope], Any) -> Optional[str]
339340
"""
340341
Captures a message.
341342

sentry_sdk/integrations/_wsgi_common.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
from typing import Dict
2323
from typing import Optional
2424
from typing import Union
25+
from sentry_sdk._types import Event
2526

2627

2728
SENSITIVE_ENV_KEYS = (
@@ -59,7 +60,7 @@ def __init__(self, request):
5960
self.request = request
6061

6162
def extract_into_event(self, event):
62-
# type: (Dict[str, Any]) -> None
63+
# type: (Event) -> None
6364
client = Hub.current.client
6465
if client is None:
6566
return

sentry_sdk/integrations/aiohttp.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,13 +48,12 @@
4848
from aiohttp import TraceRequestStartParams, TraceRequestEndParams
4949
from types import SimpleNamespace
5050
from typing import Any
51-
from typing import Dict
5251
from typing import Optional
5352
from typing import Tuple
5453
from typing import Union
5554

5655
from sentry_sdk.utils import ExcInfo
57-
from sentry_sdk._types import EventProcessor
56+
from sentry_sdk._types import Event, EventProcessor
5857

5958

6059
TRANSACTION_STYLE_VALUES = ("handler_name", "method_and_path_pattern")
@@ -256,10 +255,10 @@ async def on_request_end(session, trace_config_ctx, params):
256255
def _make_request_processor(weak_request):
257256
# type: (weakref.ReferenceType[Request]) -> EventProcessor
258257
def aiohttp_processor(
259-
event, # type: Dict[str, Any]
260-
hint, # type: Dict[str, Tuple[type, BaseException, Any]]
258+
event, # type: Event
259+
hint, # type: dict[str, Tuple[type, BaseException, Any]]
261260
):
262-
# type: (...) -> Dict[str, Any]
261+
# type: (...) -> Event
263262
request = weak_request()
264263
if request is None:
265264
return event

sentry_sdk/integrations/ariadne.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
from typing import Any, Dict, List, Optional
2424
from ariadne.types import GraphQLError, GraphQLResult, GraphQLSchema, QueryParser # type: ignore
2525
from graphql.language.ast import DocumentNode # type: ignore
26-
from sentry_sdk._types import EventProcessor
26+
from sentry_sdk._types import Event, EventProcessor
2727

2828

2929
class AriadneIntegration(Integration):
@@ -131,7 +131,7 @@ def _make_request_event_processor(data):
131131
"""Add request data and api_target to events."""
132132

133133
def inner(event, hint):
134-
# type: (Dict[str, Any], Dict[str, Any]) -> Dict[str, Any]
134+
# type: (Event, dict[str, Any]) -> Event
135135
if not isinstance(data, dict):
136136
return event
137137

@@ -163,7 +163,7 @@ def _make_response_event_processor(response):
163163
"""Add response data to the event's response context."""
164164

165165
def inner(event, hint):
166-
# type: (Dict[str, Any], Dict[str, Any]) -> Dict[str, Any]
166+
# type: (Event, dict[str, Any]) -> Event
167167
with capture_internal_exceptions():
168168
if _should_send_default_pii() and response.get("errors"):
169169
contexts = event.setdefault("contexts", {})

sentry_sdk/integrations/bottle.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ def _make_request_event_processor(app, request, integration):
200200
# type: (Bottle, LocalRequest, BottleIntegration) -> EventProcessor
201201

202202
def event_processor(event, hint):
203-
# type: (Dict[str, Any], Dict[str, Any]) -> Dict[str, Any]
203+
# type: (Event, dict[str, Any]) -> Event
204204
_set_transaction_name_and_source(event, integration.transaction_style, request)
205205

206206
with capture_internal_exceptions():

sentry_sdk/integrations/django/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -472,7 +472,7 @@ def sentry_patched_get_response(self, request):
472472
def _make_wsgi_request_event_processor(weak_request, integration):
473473
# type: (Callable[[], WSGIRequest], DjangoIntegration) -> EventProcessor
474474
def wsgi_request_event_processor(event, hint):
475-
# type: (Dict[str, Any], Dict[str, Any]) -> Dict[str, Any]
475+
# type: (Event, dict[str, Any]) -> Event
476476
# if the request is gone we are fine not logging the data from
477477
# it. This might happen if the processor is pushed away to
478478
# another thread.
@@ -570,7 +570,7 @@ def parsed_body(self):
570570

571571

572572
def _set_user_info(request, event):
573-
# type: (WSGIRequest, Dict[str, Any]) -> None
573+
# type: (WSGIRequest, Event) -> None
574574
user_info = event.setdefault("user", {})
575575

576576
user = getattr(request, "user", None)

sentry_sdk/integrations/django/asgi.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,13 @@
2626
from django.core.handlers.asgi import ASGIRequest
2727
from django.http.response import HttpResponse
2828

29-
from sentry_sdk._types import EventProcessor
29+
from sentry_sdk._types import Event, EventProcessor
3030

3131

3232
def _make_asgi_request_event_processor(request):
3333
# type: (ASGIRequest) -> EventProcessor
3434
def asgi_request_event_processor(event, hint):
35-
# type: (dict[str, Any], dict[str, Any]) -> dict[str, Any]
35+
# type: (Event, dict[str, Any]) -> Event
3636
# if the request is gone we are fine not logging the data from
3737
# it. This might happen if the processor is pushed away to
3838
# another thread.

sentry_sdk/integrations/falcon.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
from typing import Dict
1919
from typing import Optional
2020

21-
from sentry_sdk._types import EventProcessor
21+
from sentry_sdk._types import Event, EventProcessor
2222

2323
# In Falcon 3.0 `falcon.api_helpers` is renamed to `falcon.app_helpers`
2424
# and `falcon.API` to `falcon.App`
@@ -258,7 +258,7 @@ def _has_http_5xx_status(response):
258258

259259

260260
def _set_transaction_name_and_source(event, transaction_style, request):
261-
# type: (Dict[str, Any], str, falcon.Request) -> None
261+
# type: (Event, str, falcon.Request) -> None
262262
name_for_style = {
263263
"uri_template": request.uri_template,
264264
"path": request.path,
@@ -271,7 +271,7 @@ def _make_request_event_processor(req, integration):
271271
# type: (falcon.Request, FalconIntegration) -> EventProcessor
272272

273273
def event_processor(event, hint):
274-
# type: (Dict[str, Any], Dict[str, Any]) -> Dict[str, Any]
274+
# type: (Event, dict[str, Any]) -> Event
275275
_set_transaction_name_and_source(event, integration.transaction_style, req)
276276

277277
with capture_internal_exceptions():

sentry_sdk/integrations/fastapi.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
if TYPE_CHECKING:
1212
from typing import Any, Callable, Dict
1313
from sentry_sdk.scope import Scope
14+
from sentry_sdk._types import Event
1415

1516
try:
1617
from sentry_sdk.integrations.starlette import (
@@ -111,9 +112,9 @@ async def _sentry_app(*args, **kwargs):
111112
info = await extractor.extract_request_info()
112113

113114
def _make_request_event_processor(req, integration):
114-
# type: (Any, Any) -> Callable[[Dict[str, Any], Dict[str, Any]], Dict[str, Any]]
115+
# type: (Any, Any) -> Callable[[Event, Dict[str, Any]], Event]
115116
def event_processor(event, hint):
116-
# type: (Dict[str, Any], Dict[str, Any]) -> Dict[str, Any]
117+
# type: (Event, Dict[str, Any]) -> Event
117118

118119
# Extract information from request
119120
request_info = event.get("request", {})

sentry_sdk/integrations/flask.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
if TYPE_CHECKING:
1717
from typing import Any, Callable, Dict, Union
1818

19-
from sentry_sdk._types import EventProcessor
19+
from sentry_sdk._types import Event, EventProcessor
2020
from sentry_sdk.integrations.wsgi import _ScopedResponse
2121
from werkzeug.datastructures import FileStorage, ImmutableMultiDict
2222

@@ -172,7 +172,7 @@ def _make_request_event_processor(app, request, integration):
172172
# type: (Flask, Callable[[], Request], FlaskIntegration) -> EventProcessor
173173

174174
def inner(event, hint):
175-
# type: (Dict[str, Any], Dict[str, Any]) -> Dict[str, Any]
175+
# type: (Event, dict[str, Any]) -> Event
176176

177177
# if the request is gone we are fine not logging the data from
178178
# it. This might happen if the processor is pushed away to
@@ -211,7 +211,7 @@ def _capture_exception(sender, exception, **kwargs):
211211

212212

213213
def _add_user_to_event(event):
214-
# type: (Dict[str, Any]) -> None
214+
# type: (Event) -> None
215215
if flask_login is None:
216216
return
217217

0 commit comments

Comments
 (0)