Skip to content

Commit dfa422c

Browse files
committed
Merge remote-tracking branch 'origin/master' into potel-base
2 parents f9ab68a + d71b953 commit dfa422c

File tree

5 files changed

+80
-25
lines changed

5 files changed

+80
-25
lines changed

sentry_sdk/integrations/django/__init__.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import sentry_sdk
1010
from sentry_sdk.consts import OP, SPANDATA, SOURCE_FOR_STYLE, TransactionSource
1111
from sentry_sdk.scope import add_global_event_processor, should_send_default_pii
12-
from sentry_sdk.serializer import add_global_repr_processor
12+
from sentry_sdk.serializer import add_global_repr_processor, add_repr_sequence_type
1313
from sentry_sdk.tracing_utils import add_query_source, record_sql_queries
1414
from sentry_sdk.utils import (
1515
AnnotatedValue,
@@ -254,6 +254,7 @@ def _django_queryset_repr(
254254
patch_views()
255255
patch_templates()
256256
patch_signals()
257+
add_template_context_repr_sequence()
257258

258259
if patch_caching is not None:
259260
patch_caching()
@@ -709,3 +710,13 @@ def _set_db_data(span: Span, cursor_or_db: Any) -> None:
709710
server_socket_address = connection_params.get("unix_socket")
710711
if server_socket_address is not None:
711712
span.set_attribute(SPANDATA.SERVER_SOCKET_ADDRESS, server_socket_address)
713+
714+
715+
def add_template_context_repr_sequence():
716+
# type: () -> None
717+
try:
718+
from django.template.context import BaseContext
719+
720+
add_repr_sequence_type(BaseContext)
721+
except Exception:
722+
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: ReprProcessor) -> None:
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

@@ -318,7 +326,7 @@ def _serialize_node_impl(
318326
return rv_dict
319327

320328
elif not isinstance(obj, serializable_str_types) and isinstance(
321-
obj, (Set, Sequence)
329+
obj, tuple(sequence_types)
322330
):
323331
rv_list = []
324332

tests/integrations/celery/test_celery.py

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

251251

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

259-
results = []
260-
261261
@celery.task(name="dummy_task")
262262
def dummy_task():
263-
sentry_sdk.get_isolation_scope().set_tag("foo", "bar")
264-
results.append(42)
263+
return 42
265264

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

269-
assert results == [42] * 10000
270-
assert not sentry_sdk.get_isolation_scope()._tags
275+
# Second invocation should not re-patch
276+
result2 = dummy_task.delay()
277+
assert result2.get() == 42
278+
assert dummy_task.run is patched_run
279+
assert getattr(dummy_task, "_sentry_is_patched", False) is True
271280

272281

273282
def test_simple_no_propagation(capture_events, init_celery):

tests/integrations/django/test_basic.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from django.db.utils import OperationalError, ProgrammingError, DataError
1515
from django.http.request import RawPostDataException
1616
from django.utils.functional import SimpleLazyObject
17+
from django.template.context import make_context
1718

1819
try:
1920
from django.urls import reverse
@@ -312,6 +313,30 @@ def test_queryset_repr(sentry_init, capture_events):
312313
)
313314

314315

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

tests/integrations/socket/test_socket.py

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

33
from sentry_sdk import start_span
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_span(name="socket"):
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_span(name="socket"):
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.host": "example.com",
46-
"address.port": 443,
47+
"address.host": "localhost",
48+
"address.port": PORT,
4749
"timeout": timeout,
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_span(name="foo"):
69-
socket.create_connection(("example.com", 443), 1, None)
71+
socket.create_connection(("localhost", PORT), 1, None)
7072

7173
(event,) = events
7274

0 commit comments

Comments
 (0)