Skip to content
Open
21 changes: 21 additions & 0 deletions sentry_sdk/integrations/_asgi_common.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import asyncio
import inspect
import urllib

from sentry_sdk.scope import should_send_default_pii
Expand All @@ -9,11 +11,30 @@
from typing import Any
from typing import Dict
from typing import Optional
from typing import TypeVar
from typing import Union
from typing_extensions import Literal

from sentry_sdk.utils import AnnotatedValue

_F = TypeVar("_F")


# Python 3.12 deprecates asyncio.iscoroutinefunction() as an alias for
# inspect.iscoroutinefunction(), whilst also removing the _is_coroutine marker.
# The latter is replaced with inspect.markcoroutinefunction().
# 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 # type: ignore[assignment]

def _markcoroutinefunction(func: "_F") -> "_F":
func._is_coroutine = asyncio.coroutines._is_coroutine # type: ignore[attr-defined]
return func
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.

_markcoroutinefunction is not used outside of the Django integration, so please leave it there.

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.

I've simply moved it back for this PR. Still digesting the other comment



def _get_headers(asgi_scope: "Any") -> "Dict[str, str]":
"""
Expand Down
6 changes: 3 additions & 3 deletions sentry_sdk/integrations/asgi.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
"""

import sys
import asyncio
import inspect
from copy import deepcopy
from functools import partial
Expand All @@ -18,6 +17,7 @@
_get_request_attributes,
_get_request_data,
_get_url,
_iscoroutinefunction,
)
from sentry_sdk.integrations._wsgi_common import (
DEFAULT_HTTP_METHODS_TO_CAPTURE,
Expand Down Expand Up @@ -87,10 +87,10 @@ def _looks_like_asgi3(app: "Any") -> bool:
if inspect.isclass(app):
return hasattr(app, "__await__")
elif inspect.isfunction(app):
return asyncio.iscoroutinefunction(app)
return _iscoroutinefunction(app)
else:
call = getattr(app, "__call__", None) # noqa
return asyncio.iscoroutinefunction(call)
return _iscoroutinefunction(call)


class SentryAsgiMiddleware:
Expand Down
32 changes: 8 additions & 24 deletions sentry_sdk/integrations/django/asgi.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,18 @@
`django.core.handlers.asgi`.
"""

import asyncio
import functools
import inspect

from django.core.handlers.wsgi import WSGIRequest

import sentry_sdk
from sentry_sdk.consts import OP

from sentry_sdk.integrations.asgi import SentryAsgiMiddleware
from sentry_sdk.integrations._asgi_common import (
_iscoroutinefunction,
_markcoroutinefunction,
)
from sentry_sdk.scope import should_send_default_pii
from sentry_sdk.utils import (
capture_internal_exceptions,
Expand All @@ -25,31 +27,13 @@
from typing import TYPE_CHECKING

if TYPE_CHECKING:
from typing import Any, Callable, Union, TypeVar
from typing import Any, Callable, Union

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

from sentry_sdk._types import Event, EventProcessor

_F = TypeVar("_F", bound=Callable[..., Any])


# 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 # type: ignore[assignment]

def markcoroutinefunction(func: "_F") -> "_F":
func._is_coroutine = asyncio.coroutines._is_coroutine # type: ignore
return func


def _make_asgi_request_event_processor(request: "ASGIRequest") -> "EventProcessor":
def asgi_request_event_processor(event: "Event", hint: "dict[str, Any]") -> "Event":
Expand Down Expand Up @@ -215,15 +199,15 @@ def _async_check(self) -> None:
a thread is not consumed during a whole request.
Taken from django.utils.deprecation::MiddlewareMixin._async_check
"""
if iscoroutinefunction(self.get_response):
markcoroutinefunction(self)
if _iscoroutinefunction(self.get_response):
_markcoroutinefunction(self)

def async_route_check(self) -> bool:
"""
Function that checks if we are in async mode,
and if we are forwards the handling of requests to __acall__
"""
return iscoroutinefunction(self.get_response)
return _iscoroutinefunction(self.get_response)

async def __acall__(self, *args: "Any", **kwargs: "Any") -> "Any":
f = self._acall_method
Expand Down
13 changes: 3 additions & 10 deletions sentry_sdk/integrations/django/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,14 @@

import sentry_sdk
from sentry_sdk.consts import OP
from sentry_sdk.integrations._asgi_common import _iscoroutinefunction

from typing import TYPE_CHECKING

if TYPE_CHECKING:
from typing import Any


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


try:
from sentry_sdk.integrations.django.asgi import wrap_async_view
except (ImportError, SyntaxError):
Expand Down Expand Up @@ -48,10 +43,8 @@ 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)
is_async_view = wrap_async_view is not None and _iscoroutinefunction(
callback
)
if is_async_view:
sentry_wrapped_callback = wrap_async_view(callback)
Expand Down
4 changes: 2 additions & 2 deletions sentry_sdk/integrations/fastapi.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import asyncio
from copy import deepcopy
from functools import wraps

import sentry_sdk
from sentry_sdk.integrations import DidNotEnable
from sentry_sdk.integrations._asgi_common import _iscoroutinefunction
from sentry_sdk.scope import should_send_default_pii
from sentry_sdk.tracing import SOURCE_FOR_STYLE, TransactionSource
from sentry_sdk.utils import transaction_from_function
Expand Down Expand Up @@ -73,7 +73,7 @@ def _sentry_get_request_handler(*args: "Any", **kwargs: "Any") -> "Any":
if (
dependant
and dependant.call is not None
and not asyncio.iscoroutinefunction(dependant.call)
and not _iscoroutinefunction(dependant.call)
):
old_call = dependant.call

Expand Down
6 changes: 2 additions & 4 deletions sentry_sdk/integrations/quart.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import asyncio
import inspect
from functools import wraps

import sentry_sdk
from sentry_sdk.integrations import DidNotEnable, Integration
from sentry_sdk.integrations._asgi_common import _iscoroutinefunction
from sentry_sdk.integrations._wsgi_common import _filter_headers
from sentry_sdk.integrations.asgi import SentryAsgiMiddleware
from sentry_sdk.scope import should_send_default_pii
Expand Down Expand Up @@ -106,9 +106,7 @@ def _sentry_route(*args: "Any", **kwargs: "Any") -> "Any":
old_decorator = old_route(*args, **kwargs)

def decorator(old_func: "Any") -> "Any":
if inspect.isfunction(old_func) and not asyncio.iscoroutinefunction(
old_func
):
if inspect.isfunction(old_func) and not _iscoroutinefunction(old_func):

@wraps(old_func)
@ensure_integration_enabled(QuartIntegration, old_func)
Expand Down
6 changes: 3 additions & 3 deletions sentry_sdk/integrations/starlette.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import asyncio
import functools
import warnings
from collections.abc import Set
Expand All @@ -12,6 +11,7 @@
Integration,
_DEFAULT_FAILED_REQUEST_STATUS_CODES,
)
from sentry_sdk.integrations._asgi_common import _iscoroutinefunction
from sentry_sdk.integrations._wsgi_common import (
DEFAULT_HTTP_METHODS_TO_CAPTURE,
HttpCodeRangeContainer,
Expand Down Expand Up @@ -424,8 +424,8 @@ def _is_async_callable(obj: "Any") -> bool:
while isinstance(obj, functools.partial):
obj = obj.func

return asyncio.iscoroutinefunction(obj) or (
callable(obj) and asyncio.iscoroutinefunction(obj.__call__)
return _iscoroutinefunction(obj) or (
callable(obj) and _iscoroutinefunction(obj.__call__)
)


Expand Down
Loading