From 3d7430a0aa7fb45d8e6a6ef19c9ffa675f669335 Mon Sep 17 00:00:00 2001 From: Martijn Pieters Date: Sun, 24 Aug 2025 08:01:30 +0100 Subject: [PATCH] Use `asyncio.iscoroutinefunction` for Python 3.12 and older (#2984) --- starlette/_utils.py | 6 ++++-- tests/test__utils.py | 8 ++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/starlette/_utils.py b/starlette/_utils.py index 5ac985de..b0f01d31 100644 --- a/starlette/_utils.py +++ b/starlette/_utils.py @@ -1,7 +1,6 @@ from __future__ import annotations import functools -import inspect import sys from collections.abc import Awaitable, Generator from contextlib import AbstractAsyncContextManager, contextmanager @@ -10,8 +9,11 @@ from typing import Any, Callable, Generic, Protocol, TypeVar, overload from starlette.types import Scope if sys.version_info >= (3, 13): # pragma: no cover + from inspect import iscoroutinefunction from typing import TypeIs else: # pragma: no cover + from asyncio import iscoroutinefunction + from typing_extensions import TypeIs has_exceptiongroups = True @@ -37,7 +39,7 @@ def is_async_callable(obj: Any) -> Any: while isinstance(obj, functools.partial): obj = obj.func - return inspect.iscoroutinefunction(obj) or (callable(obj) and inspect.iscoroutinefunction(obj.__call__)) + return iscoroutinefunction(obj) or (callable(obj) and iscoroutinefunction(obj.__call__)) T_co = TypeVar("T_co", covariant=True) diff --git a/tests/test__utils.py b/tests/test__utils.py index 916f460d..42372ce3 100644 --- a/tests/test__utils.py +++ b/tests/test__utils.py @@ -1,5 +1,6 @@ import functools from typing import Any +from unittest.mock import create_autospec import pytest @@ -83,6 +84,13 @@ def test_async_nested_partial() -> None: assert is_async_callable(nested_partial) +def test_async_mocked_async_function() -> None: + async def async_func() -> None: ... # pragma: no cover + + mock = create_autospec(async_func) + assert is_async_callable(mock) + + @pytest.mark.parametrize( "scope, expected_result", [ -- 2.47.3