diff --git a/sentry_sdk/integrations/_asgi_common.py b/sentry_sdk/integrations/_asgi_common.py index 525ca4b5b5..3a66a44f4a 100644 --- a/sentry_sdk/integrations/_asgi_common.py +++ b/sentry_sdk/integrations/_asgi_common.py @@ -1,3 +1,5 @@ +import asyncio +import inspect import urllib from sentry_sdk.scope import should_send_default_pii @@ -14,6 +16,14 @@ from sentry_sdk.utils import AnnotatedValue +# Python 3.12 deprecates asyncio.iscoroutinefunction() as an alias for +# inspect.iscoroutinefunction(). Until 3.12 is the minimum supported Python +# version, provide a shim. +if hasattr(inspect, "iscoroutinefunction"): + _iscoroutinefunction = inspect.iscoroutinefunction +else: + _iscoroutinefunction = asyncio.iscoroutinefunction # type: ignore[assignment] + def _get_headers(asgi_scope: "Any") -> "Dict[str, str]": """ diff --git a/sentry_sdk/integrations/asgi.py b/sentry_sdk/integrations/asgi.py index 64dc3cc554..43ebe59b44 100644 --- a/sentry_sdk/integrations/asgi.py +++ b/sentry_sdk/integrations/asgi.py @@ -5,7 +5,6 @@ """ import sys -import asyncio import inspect from copy import deepcopy from functools import partial @@ -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, @@ -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: diff --git a/sentry_sdk/integrations/django/asgi.py b/sentry_sdk/integrations/django/asgi.py index f3aff113d6..78396d7dba 100644 --- a/sentry_sdk/integrations/django/asgi.py +++ b/sentry_sdk/integrations/django/asgi.py @@ -16,6 +16,7 @@ from sentry_sdk.consts import OP from sentry_sdk.integrations.asgi import SentryAsgiMiddleware +from sentry_sdk.integrations._asgi_common import _iscoroutinefunction from sentry_sdk.scope import should_send_default_pii from sentry_sdk.utils import ( capture_internal_exceptions, @@ -41,10 +42,8 @@ # 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 @@ -215,7 +214,7 @@ 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): + if _iscoroutinefunction(self.get_response): markcoroutinefunction(self) def async_route_check(self) -> bool: @@ -223,7 +222,7 @@ 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 diff --git a/sentry_sdk/integrations/django/views.py b/sentry_sdk/integrations/django/views.py index c9e370029e..a2b5b7b87e 100644 --- a/sentry_sdk/integrations/django/views.py +++ b/sentry_sdk/integrations/django/views.py @@ -2,6 +2,7 @@ import sentry_sdk from sentry_sdk.consts import OP +from sentry_sdk.integrations._asgi_common import _iscoroutinefunction from typing import TYPE_CHECKING @@ -9,12 +10,6 @@ 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): @@ -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) diff --git a/sentry_sdk/integrations/fastapi.py b/sentry_sdk/integrations/fastapi.py index 66f73ea4e0..f708dfaa94 100644 --- a/sentry_sdk/integrations/fastapi.py +++ b/sentry_sdk/integrations/fastapi.py @@ -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 @@ -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 diff --git a/sentry_sdk/integrations/quart.py b/sentry_sdk/integrations/quart.py index c1b8fca717..95de5995d7 100644 --- a/sentry_sdk/integrations/quart.py +++ b/sentry_sdk/integrations/quart.py @@ -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 @@ -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) diff --git a/sentry_sdk/integrations/starlette.py b/sentry_sdk/integrations/starlette.py index dac9887e2f..55a2091fa8 100644 --- a/sentry_sdk/integrations/starlette.py +++ b/sentry_sdk/integrations/starlette.py @@ -1,4 +1,3 @@ -import asyncio import functools import warnings from collections.abc import Set @@ -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, @@ -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__) )