Skip to content

Commit 758cdf3

Browse files
authored
Merge branch 'master' into feat/span-first-2
2 parents b3f4f98 + 07bddcf commit 758cdf3

File tree

19 files changed

+616
-112
lines changed

19 files changed

+616
-112
lines changed

scripts/find_raise_from_none.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import ast
2+
import pathlib
3+
from collections import defaultdict
4+
5+
6+
class RaiseFromNoneVisitor(ast.NodeVisitor):
7+
line_numbers = defaultdict(list)
8+
9+
def __init__(self, filename):
10+
self.filename = filename
11+
12+
def visit_Raise(self, node: ast.Raise):
13+
if node.cause is not None:
14+
if isinstance(node.cause, ast.Constant) and node.cause.value is None:
15+
RaiseFromNoneVisitor.line_numbers[self.filename].append(node.lineno)
16+
self.generic_visit(node)
17+
18+
19+
def scan_file(module_path: pathlib.Path):
20+
source = pathlib.Path(module_path).read_text(encoding="utf-8")
21+
tree = ast.parse(source, filename=module_path)
22+
23+
RaiseFromNoneVisitor(module_path).visit(tree)
24+
25+
26+
def walk_package_modules():
27+
for p in pathlib.Path("sentry_sdk").rglob("*.py"):
28+
yield p
29+
30+
31+
def format_detected_raises(line_numbers) -> str:
32+
lines = []
33+
for filepath, line_numbers_in_file in line_numbers.items():
34+
lines_string = ", ".join(f"line {ln}" for ln in sorted(line_numbers_in_file))
35+
lines.append(
36+
f"{filepath}: {len(line_numbers_in_file)} occurrence(s) at {lines_string}"
37+
)
38+
return "\n".join(lines)
39+
40+
41+
def main():
42+
for module_path in walk_package_modules():
43+
scan_file(module_path)
44+
45+
# TODO: Investigate why we suppress exception chains here.
46+
ignored_raises = {
47+
pathlib.Path("sentry_sdk/integrations/asgi.py"): 2,
48+
pathlib.Path("sentry_sdk/integrations/asyncio.py"): 1,
49+
}
50+
51+
raise_from_none_count = {
52+
file: len(occurences)
53+
for file, occurences in RaiseFromNoneVisitor.line_numbers.items()
54+
}
55+
if raise_from_none_count != ignored_raises:
56+
exc = Exception("Detected unexpected raise ... from None.")
57+
exc.add_note(
58+
"Raise ... from None suppresses chained exceptions, removing valuable context."
59+
)
60+
exc.add_note(format_detected_raises(RaiseFromNoneVisitor.line_numbers))
61+
raise exc
62+
63+
64+
if __name__ == "__main__":
65+
main()

scripts/populate_tox/tox.jinja

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,3 +230,4 @@ commands =
230230
ruff check tests sentry_sdk
231231
ruff format --check tests sentry_sdk
232232
mypy sentry_sdk
233+
python scripts/find_raise_from_none.py

sentry_sdk/_types.py

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -219,9 +219,18 @@ class SDKInfo(TypedDict):
219219
Hint = Dict[str, Any]
220220

221221
AttributeValue = (
222-
str | bool | float | int
223-
# TODO: relay support coming soon for
224-
# | list[str] | list[bool] | list[float] | list[int]
222+
str
223+
| bool
224+
| float
225+
| int
226+
| list[str]
227+
| list[bool]
228+
| list[float]
229+
| list[int]
230+
| tuple[str, ...]
231+
| tuple[bool, ...]
232+
| tuple[float, ...]
233+
| tuple[int, ...]
225234
)
226235
Attributes = dict[str, AttributeValue]
227236

@@ -234,11 +243,10 @@ class SDKInfo(TypedDict):
234243
"boolean",
235244
"double",
236245
"integer",
237-
# TODO: relay support coming soon for:
238-
# "string[]",
239-
# "boolean[]",
240-
# "double[]",
241-
# "integer[]",
246+
"string[]",
247+
"boolean[]",
248+
"double[]",
249+
"integer[]",
242250
],
243251
"value": AttributeValue,
244252
},

sentry_sdk/ai/monitoring.py

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import inspect
2+
import sys
23
from functools import wraps
34

45
from sentry_sdk.consts import SPANDATA
56
import sentry_sdk.utils
67
from sentry_sdk import start_span
78
from sentry_sdk.tracing import Span
8-
from sentry_sdk.utils import ContextVar
9+
from sentry_sdk.utils import ContextVar, reraise, capture_internal_exceptions
910

1011
from typing import TYPE_CHECKING
1112

@@ -44,13 +45,15 @@ def sync_wrapped(*args: "Any", **kwargs: "Any") -> "Any":
4445
try:
4546
res = f(*args, **kwargs)
4647
except Exception as e:
47-
event, hint = sentry_sdk.utils.event_from_exception(
48-
e,
49-
client_options=sentry_sdk.get_client().options,
50-
mechanism={"type": "ai_monitoring", "handled": False},
51-
)
52-
sentry_sdk.capture_event(event, hint=hint)
53-
raise e from None
48+
exc_info = sys.exc_info()
49+
with capture_internal_exceptions():
50+
event, hint = sentry_sdk.utils.event_from_exception(
51+
e,
52+
client_options=sentry_sdk.get_client().options,
53+
mechanism={"type": "ai_monitoring", "handled": False},
54+
)
55+
sentry_sdk.capture_event(event, hint=hint)
56+
reraise(*exc_info)
5457
finally:
5558
_ai_pipeline_name.set(None)
5659
return res
@@ -72,13 +75,15 @@ async def async_wrapped(*args: "Any", **kwargs: "Any") -> "Any":
7275
try:
7376
res = await f(*args, **kwargs)
7477
except Exception as e:
75-
event, hint = sentry_sdk.utils.event_from_exception(
76-
e,
77-
client_options=sentry_sdk.get_client().options,
78-
mechanism={"type": "ai_monitoring", "handled": False},
79-
)
80-
sentry_sdk.capture_event(event, hint=hint)
81-
raise e from None
78+
exc_info = sys.exc_info()
79+
with capture_internal_exceptions():
80+
event, hint = sentry_sdk.utils.event_from_exception(
81+
e,
82+
client_options=sentry_sdk.get_client().options,
83+
mechanism={"type": "ai_monitoring", "handled": False},
84+
)
85+
sentry_sdk.capture_event(event, hint=hint)
86+
reraise(*exc_info)
8287
finally:
8388
_ai_pipeline_name.set(None)
8489
return res

sentry_sdk/integrations/anthropic.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import sys
12
from collections.abc import Iterable
23
from functools import wraps
34
from typing import TYPE_CHECKING
@@ -20,6 +21,7 @@
2021
event_from_exception,
2122
package_version,
2223
safe_serialize,
24+
reraise,
2325
)
2426

2527
try:
@@ -386,8 +388,10 @@ def _execute_sync(f: "Any", *args: "Any", **kwargs: "Any") -> "Any":
386388
try:
387389
result = f(*args, **kwargs)
388390
except Exception as exc:
389-
_capture_exception(exc)
390-
raise exc from None
391+
exc_info = sys.exc_info()
392+
with capture_internal_exceptions():
393+
_capture_exception(exc)
394+
reraise(*exc_info)
391395

392396
return gen.send(result)
393397
except StopIteration as e:
@@ -422,8 +426,10 @@ async def _execute_async(f: "Any", *args: "Any", **kwargs: "Any") -> "Any":
422426
try:
423427
result = await f(*args, **kwargs)
424428
except Exception as exc:
425-
_capture_exception(exc)
426-
raise exc from None
429+
exc_info = sys.exc_info()
430+
with capture_internal_exceptions():
431+
_capture_exception(exc)
432+
reraise(*exc_info)
427433

428434
return gen.send(result)
429435
except StopIteration as e:

sentry_sdk/integrations/cohere.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import sys
12
from functools import wraps
23

34
from sentry_sdk import consts
@@ -16,7 +17,7 @@
1617
import sentry_sdk
1718
from sentry_sdk.scope import should_send_default_pii
1819
from sentry_sdk.integrations import DidNotEnable, Integration
19-
from sentry_sdk.utils import capture_internal_exceptions, event_from_exception
20+
from sentry_sdk.utils import capture_internal_exceptions, event_from_exception, reraise
2021

2122
try:
2223
from cohere.client import Client
@@ -151,9 +152,11 @@ def new_chat(*args: "Any", **kwargs: "Any") -> "Any":
151152
try:
152153
res = f(*args, **kwargs)
153154
except Exception as e:
154-
_capture_exception(e)
155-
span.__exit__(None, None, None)
156-
raise e from None
155+
exc_info = sys.exc_info()
156+
with capture_internal_exceptions():
157+
_capture_exception(e)
158+
span.__exit__(None, None, None)
159+
reraise(*exc_info)
157160

158161
with capture_internal_exceptions():
159162
if should_send_default_pii() and integration.include_prompts:
@@ -247,8 +250,10 @@ def new_embed(*args: "Any", **kwargs: "Any") -> "Any":
247250
try:
248251
res = f(*args, **kwargs)
249252
except Exception as e:
250-
_capture_exception(e)
251-
raise e from None
253+
exc_info = sys.exc_info()
254+
with capture_internal_exceptions():
255+
_capture_exception(e)
256+
reraise(*exc_info)
252257
if (
253258
hasattr(res, "meta")
254259
and hasattr(res.meta, "billed_units")

sentry_sdk/integrations/httpx.py

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44
from sentry_sdk.integrations import Integration, DidNotEnable
55
from sentry_sdk.tracing import BAGGAGE_HEADER_NAME
66
from sentry_sdk.tracing_utils import (
7-
Baggage,
87
should_propagate_trace,
98
add_http_request_source,
9+
add_sentry_baggage_to_headers,
1010
)
1111
from sentry_sdk.utils import (
1212
SENSITIVE_DATA_SUBSTITUTE,
@@ -19,7 +19,6 @@
1919
from typing import TYPE_CHECKING
2020

2121
if TYPE_CHECKING:
22-
from collections.abc import MutableMapping
2322
from typing import Any
2423

2524

@@ -81,7 +80,7 @@ def send(self: "Client", request: "Request", **kwargs: "Any") -> "Response":
8180
)
8281

8382
if key == BAGGAGE_HEADER_NAME:
84-
_add_sentry_baggage_to_headers(request.headers, value)
83+
add_sentry_baggage_to_headers(request.headers, value)
8584
else:
8685
request.headers[key] = value
8786

@@ -155,22 +154,3 @@ async def send(
155154
return rv
156155

157156
AsyncClient.send = send
158-
159-
160-
def _add_sentry_baggage_to_headers(
161-
headers: "MutableMapping[str, str]", sentry_baggage: str
162-
) -> None:
163-
"""Add the Sentry baggage to the headers.
164-
165-
This function directly mutates the provided headers. The provided sentry_baggage
166-
is appended to the existing baggage. If the baggage already contains Sentry items,
167-
they are stripped out first.
168-
"""
169-
existing_baggage = headers.get(BAGGAGE_HEADER_NAME, "")
170-
stripped_existing_baggage = Baggage.strip_sentry_baggage(existing_baggage)
171-
172-
separator = "," if len(stripped_existing_baggage) > 0 else ""
173-
174-
headers[BAGGAGE_HEADER_NAME] = (
175-
stripped_existing_baggage + separator + sentry_baggage
176-
)

sentry_sdk/integrations/huggingface_hub.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import sys
12
import inspect
23
from functools import wraps
34

@@ -11,6 +12,7 @@
1112
from sentry_sdk.utils import (
1213
capture_internal_exceptions,
1314
event_from_exception,
15+
reraise,
1416
)
1517

1618
from typing import TYPE_CHECKING
@@ -126,9 +128,11 @@ def new_huggingface_task(*args: "Any", **kwargs: "Any") -> "Any":
126128
try:
127129
res = f(*args, **kwargs)
128130
except Exception as e:
129-
_capture_exception(e)
130-
span.__exit__(None, None, None)
131-
raise e from None
131+
exc_info = sys.exc_info()
132+
with capture_internal_exceptions():
133+
_capture_exception(e)
134+
span.__exit__(None, None, None)
135+
reraise(*exc_info)
132136

133137
# Output attributes
134138
finish_reason = None

0 commit comments

Comments
 (0)