Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(integrations): Support Django 5.0 #2490

Merged
merged 33 commits into from
Nov 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
33ec70b
Run tests on Django 5.0
sentrivana Nov 7, 2023
4043eb0
fix sync receiver patching
sentrivana Nov 8, 2023
aaaa910
reorganize
sentrivana Nov 8, 2023
06a8a12
Merge branch 'master' into django-5.0
sentrivana Nov 8, 2023
b01a398
also patch async receivers
sentrivana Nov 8, 2023
4c23cae
test 5.0 on 3.12 as well
sentrivana Nov 8, 2023
d357101
Make reading the request body work in Django ASGI apps.
antonpirker Nov 8, 2023
b743eae
Merge branch 'master' into django-5.0
sentrivana Nov 8, 2023
3512518
Merge branch 'master' into antonpirker/django_asgi_read_body
antonpirker Nov 8, 2023
9a75259
Merge branch 'master' into antonpirker/django_asgi_read_body
antonpirker Nov 8, 2023
1656a83
Fixed some tests for Django<3
antonpirker Nov 8, 2023
2088128
Merge branch 'master' into antonpirker/django_asgi_read_body
antonpirker Nov 8, 2023
2b81590
Make mypy happy
antonpirker Nov 8, 2023
4b4f8c1
Merge branch 'antonpirker/django_asgi_read_body' of github.com:getsen…
antonpirker Nov 8, 2023
4add097
Make old Djangos happy
antonpirker Nov 8, 2023
faf73a9
missing deps?
sentrivana Nov 8, 2023
6c0c9e0
Merge branch 'master' into django-5.0
sentrivana Nov 8, 2023
96a4d46
missing deps
sentrivana Nov 8, 2023
5da9af2
Added tests
antonpirker Nov 8, 2023
75cb3c9
Merge branch 'master' into antonpirker/django_asgi_read_body
antonpirker Nov 8, 2023
ed6afc5
Added comment to make it easier to comprehend
antonpirker Nov 8, 2023
3f0aa00
Omit test in old Django versions
antonpirker Nov 8, 2023
619c105
add missing 3.12 tests
sentrivana Nov 10, 2023
8525850
Merge branch 'antonpirker/django_asgi_read_body' into django-5.0
sentrivana Nov 10, 2023
b271884
merge leftovers
sentrivana Nov 10, 2023
c4a9122
Merge branch 'master' into django-5.0
sentrivana Nov 13, 2023
8edee83
new test makes other tests fail, run separately
sentrivana Nov 13, 2023
51dbb40
Merge branch 'master' into django-5.0
sentrivana Nov 13, 2023
7f2871e
stop async tests from influencing each other
sentrivana Nov 13, 2023
2f69be8
do not wrap async receivers
sentrivana Nov 13, 2023
8e3172b
remove unused code
sentrivana Nov 13, 2023
67615f6
remove deprecated types
sentrivana Nov 14, 2023
ada4a8e
new Callable
sentrivana Nov 14, 2023
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
21 changes: 8 additions & 13 deletions sentry_sdk/integrations/django/asgi.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

import asyncio

from django.core.handlers.wsgi import WSGIRequest

Check warning on line 11 in sentry_sdk/integrations/django/asgi.py

View check run for this annotation

Codecov / codecov/patch

sentry_sdk/integrations/django/asgi.py#L11

Added line #L11 was not covered by tests

from sentry_sdk import Hub, _functools
from sentry_sdk._types import TYPE_CHECKING
from sentry_sdk.consts import OP
Expand All @@ -16,26 +18,21 @@
from sentry_sdk.integrations.asgi import SentryAsgiMiddleware
from sentry_sdk.utils import capture_internal_exceptions

from django.core.handlers.wsgi import WSGIRequest


if TYPE_CHECKING:
from typing import Any
from typing import Dict
from typing import Union
from typing import Callable
from collections.abc import Callable
from typing import Any, Union

Check warning on line 24 in sentry_sdk/integrations/django/asgi.py

View check run for this annotation

Codecov / codecov/patch

sentry_sdk/integrations/django/asgi.py#L23-L24

Added lines #L23 - L24 were not covered by tests

from django.core.handlers.asgi import ASGIRequest
from django.http.response import HttpResponse

from sentry_sdk.integrations.django import DjangoIntegration
from sentry_sdk._types import EventProcessor


def _make_asgi_request_event_processor(request, integration):
# type: (ASGIRequest, DjangoIntegration) -> EventProcessor
def _make_asgi_request_event_processor(request):

Check warning on line 32 in sentry_sdk/integrations/django/asgi.py

View check run for this annotation

Codecov / codecov/patch

sentry_sdk/integrations/django/asgi.py#L32

Added line #L32 was not covered by tests
# type: (ASGIRequest) -> EventProcessor
def asgi_request_event_processor(event, hint):
# type: (Dict[str, Any], Dict[str, Any]) -> Dict[str, Any]
# type: (dict[str, Any], dict[str, Any]) -> dict[str, Any]
# if the request is gone we are fine not logging the data from
# it. This might happen if the processor is pushed away to
# another thread.
Expand Down Expand Up @@ -103,9 +100,7 @@
# (otherwise Django closes the body stream and makes it impossible to read it again)
_ = request.body

scope.add_event_processor(
_make_asgi_request_event_processor(request, integration)
)
scope.add_event_processor(_make_asgi_request_event_processor(request))

Check warning on line 103 in sentry_sdk/integrations/django/asgi.py

View check run for this annotation

Codecov / codecov/patch

sentry_sdk/integrations/django/asgi.py#L103

Added line #L103 was not covered by tests

return request, error_response

Expand Down
33 changes: 23 additions & 10 deletions sentry_sdk/integrations/django/signals_handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@
from sentry_sdk._functools import wraps
from sentry_sdk._types import TYPE_CHECKING
from sentry_sdk.consts import OP
from sentry_sdk.integrations.django import DJANGO_VERSION

Check warning on line 10 in sentry_sdk/integrations/django/signals_handlers.py

View check run for this annotation

Codecov / codecov/patch

sentry_sdk/integrations/django/signals_handlers.py#L10

Added line #L10 was not covered by tests


if TYPE_CHECKING:
from typing import Any
from typing import Callable
from typing import List
from collections.abc import Callable
from typing import Any, Union

Check warning on line 15 in sentry_sdk/integrations/django/signals_handlers.py

View check run for this annotation

Codecov / codecov/patch

sentry_sdk/integrations/django/signals_handlers.py#L14-L15

Added lines #L14 - L15 were not covered by tests


def _get_receiver_name(receiver):
Expand Down Expand Up @@ -42,17 +42,27 @@

def patch_signals():
# type: () -> None
"""Patch django signal receivers to create a span"""
"""
Patch django signal receivers to create a span.

This only wraps sync receivers. Django>=5.0 introduced async receivers, but
since we don't create transactions for ASGI Django, we don't wrap them.
"""
from sentry_sdk.integrations.django import DjangoIntegration

old_live_receivers = Signal._live_receivers

def _sentry_live_receivers(self, sender):
# type: (Signal, Any) -> List[Callable[..., Any]]
# type: (Signal, Any) -> Union[tuple[list[Callable[..., Any]], list[Callable[..., Any]]], list[Callable[..., Any]]]
hub = Hub.current
receivers = old_live_receivers(self, sender)

def sentry_receiver_wrapper(receiver):
if DJANGO_VERSION >= (5, 0):
sync_receivers, async_receivers = old_live_receivers(self, sender)

Check warning on line 60 in sentry_sdk/integrations/django/signals_handlers.py

View check run for this annotation

Codecov / codecov/patch

sentry_sdk/integrations/django/signals_handlers.py#L60

Added line #L60 was not covered by tests
else:
sync_receivers = old_live_receivers(self, sender)
async_receivers = []

Check warning on line 63 in sentry_sdk/integrations/django/signals_handlers.py

View check run for this annotation

Codecov / codecov/patch

sentry_sdk/integrations/django/signals_handlers.py#L62-L63

Added lines #L62 - L63 were not covered by tests

def sentry_sync_receiver_wrapper(receiver):

Check warning on line 65 in sentry_sdk/integrations/django/signals_handlers.py

View check run for this annotation

Codecov / codecov/patch

sentry_sdk/integrations/django/signals_handlers.py#L65

Added line #L65 was not covered by tests
# type: (Callable[..., Any]) -> Callable[..., Any]
@wraps(receiver)
def wrapper(*args, **kwargs):
Expand All @@ -69,9 +79,12 @@

integration = hub.get_integration(DjangoIntegration)
if integration and integration.signals_spans:
for idx, receiver in enumerate(receivers):
receivers[idx] = sentry_receiver_wrapper(receiver)
for idx, receiver in enumerate(sync_receivers):
sync_receivers[idx] = sentry_sync_receiver_wrapper(receiver)

Check warning on line 83 in sentry_sdk/integrations/django/signals_handlers.py

View check run for this annotation

Codecov / codecov/patch

sentry_sdk/integrations/django/signals_handlers.py#L83

Added line #L83 was not covered by tests

return receivers
if DJANGO_VERSION >= (5, 0):
return sync_receivers, async_receivers

Check warning on line 86 in sentry_sdk/integrations/django/signals_handlers.py

View check run for this annotation

Codecov / codecov/patch

sentry_sdk/integrations/django/signals_handlers.py#L86

Added line #L86 was not covered by tests
else:
return sync_receivers

Check warning on line 88 in sentry_sdk/integrations/django/signals_handlers.py

View check run for this annotation

Codecov / codecov/patch

sentry_sdk/integrations/django/signals_handlers.py#L88

Added line #L88 was not covered by tests

Signal._live_receivers = _sentry_live_receivers
11 changes: 11 additions & 0 deletions tests/integrations/django/asgi/test_asgi.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

@pytest.mark.parametrize("application", APPS)
@pytest.mark.asyncio
@pytest.mark.forked
async def test_basic(sentry_init, capture_events, application):
sentry_init(integrations=[DjangoIntegration()], send_default_pii=True)

Expand Down Expand Up @@ -58,6 +59,7 @@ async def test_basic(sentry_init, capture_events, application):

@pytest.mark.parametrize("application", APPS)
@pytest.mark.asyncio
@pytest.mark.forked
@pytest.mark.skipif(
django.VERSION < (3, 1), reason="async views have been introduced in Django 3.1"
)
Expand Down Expand Up @@ -85,6 +87,7 @@ async def test_async_views(sentry_init, capture_events, application):
@pytest.mark.parametrize("application", APPS)
@pytest.mark.parametrize("endpoint", ["/sync/thread_ids", "/async/thread_ids"])
@pytest.mark.asyncio
@pytest.mark.forked
@pytest.mark.skipif(
django.VERSION < (3, 1), reason="async views have been introduced in Django 3.1"
)
Expand Down Expand Up @@ -119,6 +122,7 @@ async def test_active_thread_id(sentry_init, capture_envelopes, endpoint, applic


@pytest.mark.asyncio
@pytest.mark.forked
@pytest.mark.skipif(
django.VERSION < (3, 1), reason="async views have been introduced in Django 3.1"
)
Expand Down Expand Up @@ -152,6 +156,7 @@ async def test_async_views_concurrent_execution(sentry_init, settings):


@pytest.mark.asyncio
@pytest.mark.forked
@pytest.mark.skipif(
django.VERSION < (3, 1), reason="async views have been introduced in Django 3.1"
)
Expand Down Expand Up @@ -189,6 +194,7 @@ async def test_async_middleware_that_is_function_concurrent_execution(


@pytest.mark.asyncio
@pytest.mark.forked
@pytest.mark.skipif(
django.VERSION < (3, 1), reason="async views have been introduced in Django 3.1"
)
Expand Down Expand Up @@ -238,6 +244,7 @@ async def test_async_middleware_spans(


@pytest.mark.asyncio
@pytest.mark.forked
@pytest.mark.skipif(
django.VERSION < (3, 1), reason="async views have been introduced in Django 3.1"
)
Expand Down Expand Up @@ -267,6 +274,7 @@ async def test_has_trace_if_performance_enabled(sentry_init, capture_events):


@pytest.mark.asyncio
@pytest.mark.forked
@pytest.mark.skipif(
django.VERSION < (3, 1), reason="async views have been introduced in Django 3.1"
)
Expand All @@ -293,6 +301,7 @@ async def test_has_trace_if_performance_disabled(sentry_init, capture_events):


@pytest.mark.asyncio
@pytest.mark.forked
@pytest.mark.skipif(
django.VERSION < (3, 1), reason="async views have been introduced in Django 3.1"
)
Expand Down Expand Up @@ -328,6 +337,7 @@ async def test_trace_from_headers_if_performance_enabled(sentry_init, capture_ev


@pytest.mark.asyncio
@pytest.mark.forked
@pytest.mark.skipif(
django.VERSION < (3, 1), reason="async views have been introduced in Django 3.1"
)
Expand Down Expand Up @@ -373,6 +383,7 @@ async def test_trace_from_headers_if_performance_disabled(sentry_init, capture_e
],
)
@pytest.mark.asyncio
@pytest.mark.forked
@pytest.mark.skipif(
django.VERSION < (3, 1), reason="async views have been introduced in Django 3.1"
)
Expand Down
17 changes: 10 additions & 7 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ envlist =
{py3.6,py3.7,py3.8,py3.9,py3.10,py3.11}-django-v{3.2}
# - Django 4.x
{py3.8,py3.9,py3.10,py3.11,py3.12}-django-v{4.0,4.1,4.2}
# - Django 5.x
{py3.10,py3.11,py3.12}-django-v{5.0}

# Falcon
{py2.7,py3.5,py3.6,py3.7}-falcon-v{1.4}
Expand Down Expand Up @@ -288,17 +290,16 @@ deps =
django: Werkzeug<2.1.0
django-v{1.11,2.0,2.1,2.2,3.0,3.1,3.2}: djangorestframework>=3.0.0,<4.0.0

{py3.7,py3.8,py3.9,py3.10,py3.11}-django-v{1.11,2.0,2.1,2.2,3.0,3.1,3.2,4.0,4.1,4.2}: pytest-asyncio
{py3.7,py3.8,py3.9,py3.10,py3.11}-django-v{1.11,2.0,2.1,2.2,3.0,3.1,3.2,4.0,4.1,4.2}: channels[daphne]>2
{py3.7,py3.8,py3.9,py3.10,py3.11,py3.12}-django-v{1.11,2.0,2.1,2.2,3.0,3.1,3.2,4.0,4.1,4.2,5.0}: pytest-asyncio
{py3.7,py3.8,py3.9,py3.10,py3.11,py3.12}-django-v{1.11,2.0,2.1,2.2,3.0,3.1,3.2,4.0,4.1,4.2,5.0}: channels[daphne]>2

django-v{1.8,1.9,1.10,1.11,2.0,2.1}: pytest-django<4.0
django-v{2.2,3.0,3.1,3.2}: pytest-django>=4.0
django-v{2.2,3.0,3.1,3.2}: Werkzeug<2.0

django-v{4.0,4.1,4.2}: djangorestframework
django-v{4.0,4.1,4.2}: pytest-asyncio
django-v{4.0,4.1,4.2}: pytest-django
django-v{4.0,4.1,4.2}: Werkzeug
django-v{4.0,4.1,4.2,5.0}: djangorestframework
django-v{4.0,4.1,4.2,5.0}: pytest-asyncio
django-v{4.0,4.1,4.2,5.0}: pytest-django
django-v{4.0,4.1,4.2,5.0}: Werkzeug

django-v1.8: Django>=1.8,<1.9
django-v1.9: Django>=1.9,<1.10
Expand All @@ -313,6 +314,8 @@ deps =
django-v4.0: Django>=4.0,<4.1
django-v4.1: Django>=4.1,<4.2
django-v4.2: Django>=4.2,<4.3
# TODO: change to final when available
django-v5.0: Django==5.0b1

# Falcon
falcon-v1.4: falcon>=1.4,<1.5
Expand Down
Loading