Skip to content

Commit 2b9cbfd

Browse files
authored
Merge branch 'master' into antonpirker/openai-overhaul
2 parents c39d47f + 19ed1bb commit 2b9cbfd

File tree

8 files changed

+105
-44
lines changed

8 files changed

+105
-44
lines changed

sentry_sdk/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
"logger",
5050
"start_session",
5151
"end_session",
52+
"set_transaction_name",
5253
]
5354

5455
# Initialize the debug support after everything is loaded

sentry_sdk/api.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ def overload(x):
8484
"monitor",
8585
"start_session",
8686
"end_session",
87+
"set_transaction_name",
8788
]
8889

8990

@@ -466,3 +467,9 @@ def start_session(
466467
def end_session():
467468
# type: () -> None
468469
return get_isolation_scope().end_session()
470+
471+
472+
@scopemethod
473+
def set_transaction_name(name, source=None):
474+
# type: (str, Optional[str]) -> None
475+
return get_current_scope().set_transaction_name(name, source)

sentry_sdk/integrations/django/__init__.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import sentry_sdk
88
from sentry_sdk.consts import OP, SPANDATA
99
from sentry_sdk.scope import add_global_event_processor, should_send_default_pii
10-
from sentry_sdk.serializer import add_global_repr_processor
10+
from sentry_sdk.serializer import add_global_repr_processor, add_repr_sequence_type
1111
from sentry_sdk.tracing import SOURCE_FOR_STYLE, TransactionSource
1212
from sentry_sdk.tracing_utils import add_query_source, record_sql_queries
1313
from sentry_sdk.utils import (
@@ -269,6 +269,7 @@ def _django_queryset_repr(value, hint):
269269
patch_views()
270270
patch_templates()
271271
patch_signals()
272+
add_template_context_repr_sequence()
272273

273274
if patch_caching is not None:
274275
patch_caching()
@@ -745,3 +746,13 @@ def _set_db_data(span, cursor_or_db):
745746
server_socket_address = connection_params.get("unix_socket")
746747
if server_socket_address is not None:
747748
span.set_data(SPANDATA.SERVER_SOCKET_ADDRESS, server_socket_address)
749+
750+
751+
def add_template_context_repr_sequence():
752+
# type: () -> None
753+
try:
754+
from django.template.context import BaseContext
755+
756+
add_repr_sequence_type(BaseContext)
757+
except Exception:
758+
pass

sentry_sdk/serializer.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,14 @@ def add_global_repr_processor(processor):
6363
global_repr_processors.append(processor)
6464

6565

66+
sequence_types = [Sequence, Set] # type: List[type]
67+
68+
69+
def add_repr_sequence_type(ty):
70+
# type: (type) -> None
71+
sequence_types.append(ty)
72+
73+
6674
class Memo:
6775
__slots__ = ("_ids", "_objs")
6876

@@ -332,7 +340,7 @@ def _serialize_node_impl(
332340
return rv_dict
333341

334342
elif not isinstance(obj, serializable_str_types) and isinstance(
335-
obj, (Set, Sequence)
343+
obj, tuple(sequence_types)
336344
):
337345
rv_list = []
338346

tests/integrations/celery/test_celery.py

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -246,25 +246,34 @@ def dummy_task(x, y):
246246
]
247247

248248

249-
def test_no_stackoverflows(celery):
250-
"""We used to have a bug in the Celery integration where its monkeypatching
249+
def test_no_double_patching(celery):
250+
"""Ensure that Celery tasks are only patched once to prevent stack overflows.
251+
252+
We used to have a bug in the Celery integration where its monkeypatching
251253
was repeated for every task invocation, leading to stackoverflows.
252254
253255
See https://github.com/getsentry/sentry-python/issues/265
254256
"""
255257

256-
results = []
257-
258258
@celery.task(name="dummy_task")
259259
def dummy_task():
260-
sentry_sdk.get_isolation_scope().set_tag("foo", "bar")
261-
results.append(42)
260+
return 42
262261

263-
for _ in range(10000):
264-
dummy_task.delay()
262+
# Initially, the task should not be marked as patched
263+
assert not hasattr(dummy_task, "_sentry_is_patched")
264+
265+
# First invocation should trigger patching
266+
result1 = dummy_task.delay()
267+
assert result1.get() == 42
268+
assert getattr(dummy_task, "_sentry_is_patched", False) is True
269+
270+
patched_run = dummy_task.run
265271

266-
assert results == [42] * 10000
267-
assert not sentry_sdk.get_isolation_scope()._tags
272+
# Second invocation should not re-patch
273+
result2 = dummy_task.delay()
274+
assert result2.get() == 42
275+
assert dummy_task.run is patched_run
276+
assert getattr(dummy_task, "_sentry_is_patched", False) is True
268277

269278

270279
def test_simple_no_propagation(capture_events, init_celery):

tests/integrations/django/test_basic.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,13 @@
1010
from werkzeug.test import Client
1111

1212
from django import VERSION as DJANGO_VERSION
13+
1314
from django.contrib.auth.models import User
1415
from django.core.management import execute_from_command_line
1516
from django.db.utils import OperationalError, ProgrammingError, DataError
1617
from django.http.request import RawPostDataException
1718
from django.utils.functional import SimpleLazyObject
19+
from django.template.context import make_context
1820

1921
try:
2022
from django.urls import reverse
@@ -310,6 +312,27 @@ def test_queryset_repr(sentry_init, capture_events):
310312
)
311313

312314

315+
@pytest.mark.forked
316+
@pytest_mark_django_db_decorator()
317+
def test_context_nested_queryset_repr(sentry_init, capture_events):
318+
sentry_init(integrations=[DjangoIntegration()])
319+
events = capture_events()
320+
User.objects.create_user("john", "[email protected]", "johnpassword")
321+
322+
try:
323+
context = make_context({"entries": User.objects.all()}) # noqa
324+
1 / 0
325+
except Exception:
326+
capture_exception()
327+
328+
(event,) = events
329+
330+
(exception,) = event["exception"]["values"]
331+
assert exception["type"] == "ZeroDivisionError"
332+
(frame,) = exception["stacktrace"]["frames"]
333+
assert "<User: " not in frame["vars"]["context"]
334+
335+
313336
def test_custom_error_handler_request_context(sentry_init, client, capture_events):
314337
sentry_init(integrations=[DjangoIntegration()])
315338
events = capture_events()

tests/integrations/socket/test_socket.py

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,27 @@
22

33
from sentry_sdk import start_transaction
44
from sentry_sdk.integrations.socket import SocketIntegration
5-
from tests.conftest import ApproxDict
5+
from tests.conftest import ApproxDict, create_mock_http_server
6+
7+
PORT = create_mock_http_server()
68

79

810
def test_getaddrinfo_trace(sentry_init, capture_events):
911
sentry_init(integrations=[SocketIntegration()], traces_sample_rate=1.0)
1012
events = capture_events()
1113

1214
with start_transaction():
13-
socket.getaddrinfo("example.com", 443)
15+
socket.getaddrinfo("localhost", PORT)
1416

1517
(event,) = events
1618
(span,) = event["spans"]
1719

1820
assert span["op"] == "socket.dns"
19-
assert span["description"] == "example.com:443"
21+
assert span["description"] == f"localhost:{PORT}" # noqa: E231
2022
assert span["data"] == ApproxDict(
2123
{
22-
"host": "example.com",
23-
"port": 443,
24+
"host": "localhost",
25+
"port": PORT,
2426
}
2527
)
2628

@@ -32,28 +34,28 @@ def test_create_connection_trace(sentry_init, capture_events):
3234
events = capture_events()
3335

3436
with start_transaction():
35-
socket.create_connection(("example.com", 443), timeout, None)
37+
socket.create_connection(("localhost", PORT), timeout, None)
3638

3739
(event,) = events
3840
(connect_span, dns_span) = event["spans"]
3941
# as getaddrinfo gets called in create_connection it should also contain a dns span
4042

4143
assert connect_span["op"] == "socket.connection"
42-
assert connect_span["description"] == "example.com:443"
44+
assert connect_span["description"] == f"localhost:{PORT}" # noqa: E231
4345
assert connect_span["data"] == ApproxDict(
4446
{
45-
"address": ["example.com", 443],
47+
"address": ["localhost", PORT],
4648
"timeout": timeout,
4749
"source_address": None,
4850
}
4951
)
5052

5153
assert dns_span["op"] == "socket.dns"
52-
assert dns_span["description"] == "example.com:443"
54+
assert dns_span["description"] == f"localhost:{PORT}" # noqa: E231
5355
assert dns_span["data"] == ApproxDict(
5456
{
55-
"host": "example.com",
56-
"port": 443,
57+
"host": "localhost",
58+
"port": PORT,
5759
}
5860
)
5961

@@ -66,7 +68,7 @@ def test_span_origin(sentry_init, capture_events):
6668
events = capture_events()
6769

6870
with start_transaction(name="foo"):
69-
socket.create_connection(("example.com", 443), 1, None)
71+
socket.create_connection(("localhost", PORT), 1, None)
7072

7173
(event,) = events
7274

tox.ini

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
# The file (and all resulting CI YAMLs) then need to be regenerated via
1111
# "scripts/generate-test-files.sh".
1212
#
13-
# Last generated: 2025-07-23T07:24:30.467173+00:00
13+
# Last generated: 2025-07-29T06:07:22.069934+00:00
1414

1515
[tox]
1616
requires =
@@ -136,9 +136,9 @@ envlist =
136136

137137
# ~~~ AI ~~~
138138
{py3.8,py3.11,py3.12}-anthropic-v0.16.0
139-
{py3.8,py3.11,py3.12}-anthropic-v0.30.1
140-
{py3.8,py3.11,py3.12}-anthropic-v0.44.0
141-
{py3.8,py3.12,py3.13}-anthropic-v0.58.2
139+
{py3.8,py3.11,py3.12}-anthropic-v0.31.2
140+
{py3.8,py3.11,py3.12}-anthropic-v0.46.0
141+
{py3.8,py3.12,py3.13}-anthropic-v0.60.0
142142

143143
{py3.9,py3.10,py3.11}-cohere-v5.4.0
144144
{py3.9,py3.11,py3.12}-cohere-v5.9.4
@@ -152,7 +152,8 @@ envlist =
152152
{py3.8,py3.10,py3.11}-huggingface_hub-v0.22.2
153153
{py3.8,py3.11,py3.12}-huggingface_hub-v0.26.5
154154
{py3.8,py3.12,py3.13}-huggingface_hub-v0.30.2
155-
{py3.8,py3.12,py3.13}-huggingface_hub-v0.33.4
155+
{py3.8,py3.12,py3.13}-huggingface_hub-v0.34.2
156+
{py3.8,py3.12,py3.13}-huggingface_hub-v0.35.0rc0
156157

157158

158159
# ~~~ DBs ~~~
@@ -184,7 +185,7 @@ envlist =
184185
{py3.7,py3.12,py3.13}-statsig-v0.55.3
185186
{py3.7,py3.12,py3.13}-statsig-v0.57.3
186187
{py3.7,py3.12,py3.13}-statsig-v0.59.1
187-
{py3.7,py3.12,py3.13}-statsig-v0.60.0
188+
{py3.7,py3.12,py3.13}-statsig-v0.61.0
188189

189190
{py3.8,py3.12,py3.13}-unleash-v6.0.1
190191
{py3.8,py3.12,py3.13}-unleash-v6.1.0
@@ -215,8 +216,7 @@ envlist =
215216
{py3.7,py3.8}-grpc-v1.32.0
216217
{py3.7,py3.9,py3.10}-grpc-v1.46.5
217218
{py3.7,py3.11,py3.12}-grpc-v1.60.2
218-
{py3.9,py3.12,py3.13}-grpc-v1.73.1
219-
{py3.9,py3.12,py3.13}-grpc-v1.74.0rc1
219+
{py3.9,py3.12,py3.13}-grpc-v1.74.0
220220

221221

222222
# ~~~ Tasks ~~~
@@ -267,7 +267,7 @@ envlist =
267267
{py3.7}-aiohttp-v3.4.4
268268
{py3.7,py3.8,py3.9}-aiohttp-v3.7.4
269269
{py3.8,py3.12,py3.13}-aiohttp-v3.10.11
270-
{py3.9,py3.12,py3.13}-aiohttp-v3.12.14
270+
{py3.9,py3.12,py3.13}-aiohttp-v3.12.15
271271

272272
{py3.6,py3.7}-bottle-v0.12.25
273273
{py3.8,py3.12,py3.13}-bottle-v0.13.4
@@ -510,13 +510,13 @@ deps =
510510

511511
# ~~~ AI ~~~
512512
anthropic-v0.16.0: anthropic==0.16.0
513-
anthropic-v0.30.1: anthropic==0.30.1
514-
anthropic-v0.44.0: anthropic==0.44.0
515-
anthropic-v0.58.2: anthropic==0.58.2
513+
anthropic-v0.31.2: anthropic==0.31.2
514+
anthropic-v0.46.0: anthropic==0.46.0
515+
anthropic-v0.60.0: anthropic==0.60.0
516516
anthropic: pytest-asyncio
517517
anthropic-v0.16.0: httpx<0.28.0
518-
anthropic-v0.30.1: httpx<0.28.0
519-
anthropic-v0.44.0: httpx<0.28.0
518+
anthropic-v0.31.2: httpx<0.28.0
519+
anthropic-v0.46.0: httpx<0.28.0
520520

521521
cohere-v5.4.0: cohere==5.4.0
522522
cohere-v5.9.4: cohere==5.9.4
@@ -531,7 +531,8 @@ deps =
531531
huggingface_hub-v0.22.2: huggingface_hub==0.22.2
532532
huggingface_hub-v0.26.5: huggingface_hub==0.26.5
533533
huggingface_hub-v0.30.2: huggingface_hub==0.30.2
534-
huggingface_hub-v0.33.4: huggingface_hub==0.33.4
534+
huggingface_hub-v0.34.2: huggingface_hub==0.34.2
535+
huggingface_hub-v0.35.0rc0: huggingface_hub==0.35.0rc0
535536

536537

537538
# ~~~ DBs ~~~
@@ -564,7 +565,7 @@ deps =
564565
statsig-v0.55.3: statsig==0.55.3
565566
statsig-v0.57.3: statsig==0.57.3
566567
statsig-v0.59.1: statsig==0.59.1
567-
statsig-v0.60.0: statsig==0.60.0
568+
statsig-v0.61.0: statsig==0.61.0
568569
statsig: typing_extensions
569570

570571
unleash-v6.0.1: UnleashClient==6.0.1
@@ -608,8 +609,7 @@ deps =
608609
grpc-v1.32.0: grpcio==1.32.0
609610
grpc-v1.46.5: grpcio==1.46.5
610611
grpc-v1.60.2: grpcio==1.60.2
611-
grpc-v1.73.1: grpcio==1.73.1
612-
grpc-v1.74.0rc1: grpcio==1.74.0rc1
612+
grpc-v1.74.0: grpcio==1.74.0
613613
grpc: protobuf
614614
grpc: mypy-protobuf
615615
grpc: types-protobuf
@@ -712,10 +712,10 @@ deps =
712712
aiohttp-v3.4.4: aiohttp==3.4.4
713713
aiohttp-v3.7.4: aiohttp==3.7.4
714714
aiohttp-v3.10.11: aiohttp==3.10.11
715-
aiohttp-v3.12.14: aiohttp==3.12.14
715+
aiohttp-v3.12.15: aiohttp==3.12.15
716716
aiohttp: pytest-aiohttp
717717
aiohttp-v3.10.11: pytest-asyncio
718-
aiohttp-v3.12.14: pytest-asyncio
718+
aiohttp-v3.12.15: pytest-asyncio
719719

720720
bottle-v0.12.25: bottle==0.12.25
721721
bottle-v0.13.4: bottle==0.13.4

0 commit comments

Comments
 (0)