Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 64 additions & 13 deletions sentry_sdk/_types.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from typing import TYPE_CHECKING, TypeVar, Union


# Re-exported for compat, since code out there in the wild might use this variable.
MYPY = TYPE_CHECKING

Expand Down Expand Up @@ -93,20 +92,14 @@ def substituted_because_contains_sensitive_data(cls) -> "AnnotatedValue":

if TYPE_CHECKING:
from collections.abc import Container, MutableMapping, Sequence

from datetime import datetime

from types import TracebackType
from typing import Any
from typing import Callable
from typing import Dict
from typing import Mapping
from typing import NotRequired
from typing import Optional
from typing import Tuple
from typing import Type
from typing import Any, Callable, Dict, Mapping, NotRequired, Optional, Type

from typing_extensions import Literal, TypedDict

from sentry_sdk.tracing import TransactionSource

class SDKInfo(TypedDict):
name: str
version: str
Expand Down Expand Up @@ -285,8 +278,66 @@ class SDKInfo(TypedDict):
# TODO: Make a proper type definition for this (PRs welcome!)
BreadcrumbHint = Dict[str, Any]

# TODO: Make a proper type definition for this (PRs welcome!)
SamplingContext = Dict[str, Any]
_ASGIInfo = TypedDict("_ASGIInfo", {"version": str, "spec_version": str})
_ASGIScope = TypedDict(
"_ASGIScope",
{
"type": str,
"asgi": _ASGIInfo,
"http_version": str,
"method": str,
"path": str,
"query_string": bytes,
"headers": list[tuple[bytes, bytes]],
# Optional fields per ASGI spec
"scheme": NotRequired[str],
"raw_path": NotRequired[bytes],
"root_path": NotRequired[str],
"client": NotRequired[tuple[str, int]],
"server": NotRequired[tuple[str, int]],
"state": NotRequired[dict[str, Any]],
},
)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_ASGIScope marks optional ASGI fields as required

Medium Severity

The _ASGIScope TypedDict uses total=True (the default), marking all fields as required. Per the ASGI HTTP connection scope spec, server, client, scheme, raw_path, and state are optional. Developers relying on these type hints might access fields like scope["client"] or scope["server"] directly and hit KeyError at runtime when the ASGI server omits them. These fields need NotRequired wrapping.

Fix in Cursor Fix in Web

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cursoragent As per official ASGI Spec, I have made fields optional.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unable to authenticate your request. Please make sure to connect your GitHub account to Cursor. Go to Cursor

_TransactionContext = TypedDict(
"_TransactionContext",
{
"trace_id": str,
"span_id": str,
"parent_span_id": Optional[str],
"same_process_as_parent": bool,
"op": Optional[str],
"description": Optional[str],
"start_timestamp": datetime | int,
"timestamp": Optional[datetime],
"origin": str,
"tags": NotRequired[dict[str, str]],
"data": NotRequired[dict[str, Any]],
"name": str,
"source": TransactionSource,
"sampled": Optional[bool],
},
)
_SamplingContextTyped = TypedDict(
"_SamplingContextTyped",
{
"transaction_context": _TransactionContext,
"parent_sampled": Optional[bool],
"asgi_scope": NotRequired[_ASGIScope],
# `wsgi_environ` is only present for WSGI server (like Django), and it contains mainly env vars, and some wsgi specifics.
"wsgi_environ": NotRequired[dict[str, Any]],
# `aiohttp_request` is only present for AIOHTTP server, and contains <class 'aiohttp.web_request.Request'>
"aiohttp_request": NotRequired[Any],
# NOT tested these below, but documented for completeness sake to pass mypy.
"tornado_request": NotRequired[Any],
"celery_job": NotRequired[dict[str, Any]],
"rq_job": NotRequired[Any],
"aws_event": NotRequired[Any],
"aws_context": NotRequired[Any],
"gcp_env": NotRequired[dict[str, Any]],
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing gcp_event field in _SamplingContextTyped TypedDict

Low Severity

The _SamplingContextTyped TypedDict includes gcp_env but is missing the gcp_event field. The GCP integration passes both "gcp_env" and "gcp_event" in the custom sampling context. This omission means developers using GCP Cloud Functions won't see gcp_event in autocomplete, despite it being available at runtime. The Union with dict[str, Any] prevents a type error, but the field is still undocumented in the typed definition.

Fix in Cursor Fix in Web

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cursoragent I have added gcp_event as well.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unable to authenticate your request. Please make sure to connect your GitHub account to Cursor. Go to Cursor

"gcp_event": NotRequired[Any],
},
)
SamplingContext = Union[_SamplingContextTyped, dict[str, Any]]

EventProcessor = Callable[[Event, Hint], Optional[Event]]
ErrorProcessor = Callable[[Event, ExcInfo], Optional[Event]]
Expand Down
2 changes: 1 addition & 1 deletion sentry_sdk/scope.py
Original file line number Diff line number Diff line change
Expand Up @@ -1076,7 +1076,7 @@ def start_transaction(

# use traces_sample_rate, traces_sampler, and/or inheritance to make a
# sampling decision
sampling_context = {
sampling_context: "Dict[str, Any]" = {
"transaction_context": transaction.to_json(),
"parent_sampled": transaction.parent_sampled,
}
Expand Down
Loading