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
5 changes: 2 additions & 3 deletions sentry_sdk/integrations/django/asgi.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
"""
Instrumentation for Django 3.0
Since this file contains `async def` it is conditionally imported in
`sentry_sdk.integrations.django` (depending on the existence of
`django.core.handlers.asgi`.
Since this file imports `django.core.handlers.asgi` (Django >= 3)
it is conditionally imported in `sentry_sdk.integrations.django`.
"""

import asyncio
Expand Down
16 changes: 6 additions & 10 deletions sentry_sdk/integrations/django/views.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import functools
import sys
from typing import TYPE_CHECKING

import sentry_sdk
Expand All @@ -10,15 +11,15 @@
from typing import Any


try:
if sys.version_info >= (3, 14):
from inspect import iscoroutinefunction
else:
Comment on lines +14 to +16

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Any reason to not re-use the shim we already have in Django?

# Python 3.12 deprecates asyncio.iscoroutinefunction() as an alias for
# inspect.iscoroutinefunction(), whilst also removing the _is_coroutine marker.
# The latter is replaced with the inspect.markcoroutinefunction decorator.
# Until 3.12 is the minimum supported Python version, provide a shim.
# This was copied from https://github.com/django/asgiref/blob/main/asgiref/sync.py
if hasattr(inspect, "markcoroutinefunction"):
iscoroutinefunction = inspect.iscoroutinefunction
markcoroutinefunction = inspect.markcoroutinefunction
else:
iscoroutinefunction = asyncio.iscoroutinefunction
def markcoroutinefunction(func: "_F") -> "_F":
func._is_coroutine = asyncio.coroutines._is_coroutine # type: ignore
return func

@Digenis Digenis Aug 3, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

asgi.py imports django.core.handlers.asgi which doesn't exist in django < 3

sentry supports django ≥ 1.8

views.py wraps the asgi import in a try/except block
suppressing the inner import error
caused by the missing module in django < 3
to keep django ≥ 1.8 compatibility

To rely on the shim
I have to redefine the other half of the conditional iscoroutinefunction definition
inside the try/except block
which defeats the purpose of using the shim.

The "conditional import" phrase in the asgi module docstring
was, and still is, misleading people
into imagining an import within an if block.

Maybe I should rephrase it, since I'm touching this sentence in the chore commit anyway.
Examples:

This module imports django.core.handlers.asgi (Django >= 3), so
sentry_sdk.integrations.django imports it via a guarded import that
handles ImportError on older Django versions.

This module is imported from sentry_sdk.integrations.django using
try/except to maintain compatibility with Django versions that do
not provide django.core.handlers.asgi.

Separate commit or fixup on chore(django): Drop Python < 3.5 compatibility ?

But this alone will not be enough to address the same misunderstanding in the future
so maybe a code comment too in views.py is needed.
It's obvious what the code does but not why it's here and not there.

Duplicated from asgi.py because that module is not importable on Django < 3.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for the thorough reply. We're not actually concerned about the

from django.core.handlers.asgi import ASGIRequest

import because it's in a type checking block. Our typing environment has newer package versions so there's no risk of ImportError for ASGIRequest.

Also, the except: block exists for Python 2 compatibility (see #851). We're not concerned about Python 2 any more, so we can remove the except : 😄

from asyncio import iscoroutinefunction
except ImportError:
iscoroutinefunction = None # type: ignore


try:
from sentry_sdk.integrations.django.asgi import wrap_async_view
except (ImportError, SyntaxError):
except ImportError: # Django < 3.0
wrap_async_view = None # type: ignore


Expand Down Expand Up @@ -63,12 +64,7 @@ def sentry_patched_make_view_atomic(

integration = sentry_sdk.get_client().get_integration(DjangoIntegration)
if integration is not None:
is_async_view = (
iscoroutinefunction is not None
and wrap_async_view is not None
and iscoroutinefunction(callback)
)
if is_async_view:
if wrap_async_view is not None and iscoroutinefunction(callback):
sentry_wrapped_callback = wrap_async_view(callback)
else:
sentry_wrapped_callback = _wrap_sync_view(callback)
Expand Down
Loading