Skip to content
Open
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
26 changes: 25 additions & 1 deletion sentry_sdk/_compat.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,44 @@
import sys
import asyncio
import inspect

from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Any, Callable, TypeVar, cast

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

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


# Use a conservative cutoff (Python 3.13+) before switching to
# inspect.iscoroutinefunction. See contributor discussion and
# references to Starlette/Uvicorn behavior for rationale.
PY313 = sys.version_info[0] == 3 and sys.version_info[1] >= 13

# Backwards-compatible version flags expected across the codebase
PY37 = sys.version_info[0] == 3 and sys.version_info[1] >= 7
PY38 = sys.version_info[0] == 3 and sys.version_info[1] >= 8
PY310 = sys.version_info[0] == 3 and sys.version_info[1] >= 10
PY311 = sys.version_info[0] == 3 and sys.version_info[1] >= 11

Copy link
Contributor

Choose a reason for hiding this comment

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

Switching asyncio.iscoroutinefunction for inspect.iscoroutinefunction is tricky because of historical incompatibility. Since we support Python versions 3.6 and up we have to care about this unfortunately.

We should use the most conservative cutoff to not break any users. So while CPython deprecated asyncio.iscoroutinefunction in version 3.12, we should probably mirror Starlette who use 3.13 because of a bug in standard libraries which have not been backported. See Kludex/starlette#2983

Also, since we vendor _is_async_callable, the change below would keep the vendor up to date. We can therefore be more confident that we won't run into edge cases, since users of starlette would have run into them already.

Suggested change
PY313 = sys.version_info[0] == 3 and sys.version_info[1] >= 13
iscoroutinefunction = inspect.iscoroutinefunction if PY313 else asyncio.iscoroutinefunction


# Public shim symbol so other modules can import a stable API. For Python
# 3.13+ prefer inspect.iscoroutinefunction, otherwise fall back to
# asyncio.iscoroutinefunction to preserve historical behavior on older Pythons.
iscoroutinefunction: Callable[[Any], bool] = cast(
Callable[[Any], bool], inspect.iscoroutinefunction if PY313 else asyncio.iscoroutinefunction
)


# We intentionally do not export `markcoroutinefunction` here. The decorator
# is only used by the Django ASGI integration and historically may be applied
# to middleware instances (non-callables). Keeping the marker local to the
# integration reduces import surface and avoids potential circular imports.


def with_metaclass(meta, *bases):
# type: (Any, *Any) -> Any
class MetaClass(type):
Expand Down