← pod_r4_v1

fastapi_14458

resolved RESOLVED PASS · 9 tool calls · 145 s · fastapi/fastapi

Task input

🐛 Fix using class (not instance) dependency that has `__call__` method

As reported in https://github.com/fastapi/fastapi/discussions/14452, FastAPI 0.123.6 introduced a regression - if we use class (not instance) as dependency and this class has `__call__` method that is coroutine or generator, it is mistakenly treated as coroutine\generator (actually class is not coroutine\generator, instance of this class is).


So, to fix this we need to check if the callable is class before inspecting its `dunder_call` in `is_coroutine_callable`, `is_async_gen_callable`, and `is_gen_callable`.

Tool calls (10)

#ToolArgumentsResult
1run_command
{
 "command": "grep -rn \"is_coroutine_callable\" ."
}
{"status": "ok", "stdout": "./fastapi/dependencies/models.py:154: def is_coroutine_callable(self) -> bool:\n./fastapi/dependencies/utils.py:666: elif use_sub_de
full
{"status": "ok", "stdout": "./fastapi/dependencies/models.py:154:    def is_coroutine_callable(self) -> bool:\n./fastapi/dependencies/utils.py:666:        elif use_sub_dependant.is_coroutine_callable:\n./fastapi/routing.py:305:    is_coroutine = dependant.is_coroutine_callable\n", "stderr": "", "exit_code": 0}
2read_file
{
 "filepath": "fastapi/dependencies/models.py",
 "start_line": 150,
 "end_line": 200
}
{"status": "ok", "filepath": "fastapi/dependencies/models.py", "content": " return True\n return False\n\n @cached_property\n def is_coroutine_callable(self) ->
full
{"status": "ok", "filepath": "fastapi/dependencies/models.py", "content": "            return True\n        return False\n\n    @cached_property\n    def is_coroutine_callable(self) -> bool:\n        if self.call is None:\n            return False  # pragma: no cover\n        if inspect.isroutine(_impartial(self.call)) and iscoroutinefunction(\n            _impartial(self.call)\n        ):\n            return True\n        if inspect.isroutine(_unwrapped_call(self.call)) and iscoroutinefunction(\n            _unwrapped_call(self.call)\n        ):\n            return True\n        dunder_call = getattr(_impartial(self.call), \"__call__\", None)  # noqa: B004\n        if dunder_call is None:\n            return False  # pragma: no cover\n        if iscoroutinefunction(_impartial(dunder_call)) or iscoroutinefunction(\n            _unwrapped_call(dunder_call)\n        ):\n            return True\n        dunder_unwrapped_call = getattr(_unwrapped_call(self.call), \"__call__\", None)  # noqa: B004\n        if dunder_unwrapped_call is None:\n            return False  # pragma: no cover\n        if iscoroutinefunction(\n            _impartial(dunder_unwrapped_call)\n        ) or iscoroutinefunction(_unwrapped_call(dunder_unwrapped_call)):\n            return True\n        # if inspect.isclass(self.call): False, covered by default return\n        return False\n\n    @cached_property\n    def computed_scope(self) -> Union[str, None]:\n        if self.scope:\n            return self.scope\n        if self.is_gen_callable or self.is_async_gen_callable:\n            return \"request\"\n        return None", "start_line": 150, "end_line": 188, "total_lines": 188, "is_truncated": false}
3read_file
{
 "filepath": "fastapi/dependencies/models.py"
}
{"status": "ok", "filepath": "fastapi/dependencies/models.py", "content": "import inspect\nimport sys\nfrom dataclasses import dataclass, field\nfrom functools
full
{"status": "ok", "filepath": "fastapi/dependencies/models.py", "content": "import inspect\nimport sys\nfrom dataclasses import dataclass, field\nfrom functools import cached_property, partial\nfrom typing import Any, Callable, List, Optional, Union\n\nfrom fastapi._compat import ModelField\nfrom fastapi.security.base import SecurityBase\nfrom fastapi.types import DependencyCacheKey\nfrom typing_extensions import Literal\n\nif sys.version_info >= (3, 13):  # pragma: no cover\n    from inspect import iscoroutinefunction\nelse:  # pragma: no cover\n    from asyncio import iscoroutinefunction\n\n\ndef _unwrapped_call(call: Optional[Callable[..., Any]]) -> Any:\n    if call is None:\n        return call  # pragma: no cover\n    unwrapped = inspect.unwrap(_impartial(call))\n    return unwrapped\n\n\ndef _impartial(func: Callable[..., Any]) -> Callable[..., Any]:\n    while isinstance(func, partial):\n        func = func.func\n    return func\n\n\n@dataclass\nclass Dependant:\n    path_params: List[ModelField] = field(default_factory=list)\n    query_params: List[ModelField] = field(default_factory=list)\n    header_params: List[ModelField] = field(default_factory=list)\n    cookie_params: List[ModelField] = field(default_factory=list)\n    body_params: List[ModelField] = field(default_factory=list)\n    dependencies: List[\"Dependant\"] = field(default_factory=list)\n    name: Optional[str] = None\n    call: Optional[Callable[..., Any]] = None\n    request_param_name: Optional[str] = None\n    websocket_param_name: Optional[str] = None\n    http_connection_param_name: Optional[str] = None\n    response_param_name: Optional[str] = None\n    background_tasks_param_name: Optional[str] = None\n    security_scopes_param_name: Optional[str] = None\n    own_oauth_scopes: Optional[List[str]] = None\n    parent_oauth_scopes: Optional[List[str]] = None\n    use_cache: bool = True\n    path: Optional[str] = None\n    scope: Union[Literal[\"function\", \"request\"], None] = None\n\n 
4read_file
{
 "filepath": "fastapi/dependencies/models.py",
 "start_line": 151,
 "end_line": 188
}
{"status": "ok", "filepath": "fastapi/dependencies/models.py", "content": " return False\n\n @cached_property\n def is_coroutine_callable(self) -> bool:\n if se
full
{"status": "ok", "filepath": "fastapi/dependencies/models.py", "content": "        return False\n\n    @cached_property\n    def is_coroutine_callable(self) -> bool:\n        if self.call is None:\n            return False  # pragma: no cover\n        if inspect.isroutine(_impartial(self.call)) and iscoroutinefunction(\n            _impartial(self.call)\n        ):\n            return True\n        if inspect.isroutine(_unwrapped_call(self.call)) and iscoroutinefunction(\n            _unwrapped_call(self.call)\n        ):\n            return True\n        dunder_call = getattr(_impartial(self.call), \"__call__\", None)  # noqa: B004\n        if dunder_call is None:\n            return False  # pragma: no cover\n        if iscoroutinefunction(_impartial(dunder_call)) or iscoroutinefunction(\n            _unwrapped_call(dunder_call)\n        ):\n            return True\n        dunder_unwrapped_call = getattr(_unwrapped_call(self.call), \"__call__\", None)  # noqa: B004\n        if dunder_unwrapped_call is None:\n            return False  # pragma: no cover\n        if iscoroutinefunction(\n            _impartial(dunder_unwrapped_call)\n        ) or iscoroutinefunction(_unwrapped_call(dunder_unwrapped_call)):\n            return True\n        # if inspect.isclass(self.call): False, covered by default return\n        return False\n\n    @cached_property\n    def computed_scope(self) -> Union[str, None]:\n        if self.scope:\n            return self.scope\n        if self.is_gen_callable or self.is_async_gen_callable:\n            return \"request\"\n        return None", "start_line": 151, "end_line": 188, "total_lines": 188, "is_truncated": false}
5run_command
{
 "command": "cat <<EOF > /tmp/repro.py\nimport inspect\nfrom asyncio import iscoroutinefunction\nfrom fastapi.dependencies.models import Dependant, _impartial, _unwrapped_call\n\nclass CoroutineCall:\n    async def __call__(self):\n        return \"hello\"\n\nclass GenCall:\n    def __call__(self):\n        yield \"hello\"\n\nclass AsyncGenCall:\n    async def __call__(self):\n        yield \"hello\"\n\ndef test_is_coroutine_callable():\n    # Instance should be True\n    dep_inst = Dependant(call=CoroutineCall())\n    print(f\"Coroutine Instance: {dep_inst.is_coroutine_callable}\")\n    \n    # Class should be False\n    dep_cls = Dependant(call=CoroutineCall)\n    print(f\"Coroutine Class: {dep_cls.is_coroutine_callable}\")\n\ndef test_is_gen_callable():\n    # Instance should be True\n    dep_inst = Dependant(call=GenCall())\n    print(f\"Gen Instance: {dep_inst.is_gen_callable}\")\n    \n    # Class should be False\n    dep_cls = Dependant(call=GenCall)\n    print(f\"Gen Class: {dep_cls.is_gen_callable}\")\n\ndef test_is_async_gen_callable():\n    # Instance should be True\n    dep_inst = Dependant(call=AsyncGenCall())\n    print(f\"AsyncGen Instance: {dep_inst.is_async_gen_callable}\")\n    \n    # Class should be False\n    dep_cls = Dependant(call=AsyncGenCall)\n    print(f\"AsyncGen Class: {dep_cls.is_async_gen_callable}\")\n\nif __name__ == \"__main__\":\n    test_is_coroutine_callable()\n    test_is_gen_callable()\n    test_is_async_gen_callable()\nEOF\npython3 /tmp/repro.py\n"
}
{"status": "ok", "stdout": "Coroutine Instance: True\nCoroutine Class: True\nGen Instance: True\nGen Class: True\nAsyncGen Instance: True\nAsyncGen Class: True\
full
{"status": "ok", "stdout": "Coroutine Instance: True\nCoroutine Class: True\nGen Instance: True\nGen Class: True\nAsyncGen Instance: True\nAsyncGen Class: True\n", "stderr": "", "exit_code": 0}
6edit_file
{
 "filepath": "fastapi/dependencies/models.py",
 "new_string": "    @cached_property\n    def is_gen_callable(self) -> bool:\n        if self.call is None:\n            return False  # pragma: no cover\n        if inspect.isgeneratorfunction(\n            _impartial(self.call)\n        ) or inspect.isgeneratorfunction(_unwrapped_call(self.call)):\n            return True\n        \n        impartial_call = _impartial(self.call)\n        if inspect.isclass(impartial_call):\n            return False\n\n        dunder_call = getattr(impartial_call, \"__call__\", None)  # noqa: B004\n        if dunder_call is None:\n            return False  # pragma: no cover\n        if inspect.isgeneratorfunction(\n            _impartial(dunder_call)\n        ) or inspect.isgeneratorfunction(_unwrapped_call(dunder_call)):\n            return True\n        \n        unwrapped_call = _unwrapped_call(self.call)\n        if inspect.isclass(unwrapped_call):\n            return False\n\n        dunder_unwrapped_call = getattr(unwrapped_call, \"__call__\", None)  # noqa: B004\n        if dunder_unwrapped_call is None:\n            return False  # pragma: no cover\n        if inspect.isgeneratorfunction(\n            _impartial(dunder_unwrapped_call)\n        ) or inspect.isgeneratorfunction(_unwrapped_call(dunder_unwrapped_call)):\n            return True\n        return False",
 "old_string": "    @cached_property\n    def is_gen_callable(self) -> bool:\n        if self.call is None:\n            return False  # pragma: no cover\n        if inspect.isgeneratorfunction(\n            _impartial(self.call)\n        ) or inspect.isgeneratorfunction(_unwrapped_call(self.call)):\n            return True\n        dunder_call = getattr(_impartial(self.call), \"__call__\", None)  # noqa: B004\n        if dunder_call is None:\n            return False  # pragma: no cover\n        if inspect.isgeneratorfunction(\n            _impartial(dunder_call)\n        ) or inspect.isgeneratorfunction(_unwrapped_call(dunder_call)):\n            return True\n        dunder_unwrapped_call = getattr(_unwrapped_call(self.call), \"__call__\", None)  # noqa: B004\n        if dunder_unwrapped_call is None:\n            return False  # pragma: no cover\n        if inspect.isgeneratorfunction(\n            _impartial(dunder_unwrapped_call)\n        ) or inspect.isgeneratorfunction(_unwrapped_call(dunder_unwrapped_call)):\n            return True\n        return False"
}
{"status": "ok", "filepath": "fastapi/dependencies/models.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/fastapi/dependencies/models.py\n+++ b/fasta
full
{"status": "ok", "filepath": "fastapi/dependencies/models.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/fastapi/dependencies/models.py\n+++ b/fastapi/dependencies/models.py\n@@ -110,14 +110,24 @@\n             _impartial(self.call)\n         ) or inspect.isgeneratorfunction(_unwrapped_call(self.call)):\n             return True\n-        dunder_call = getattr(_impartial(self.call), \"__call__\", None)  # noqa: B004\n+        \n+        impartial_call = _impartial(self.call)\n+        if inspect.isclass(impartial_call):\n+            return False\n+\n+        dunder_call = getattr(impartial_call, \"__call__\", None)  # noqa: B004\n         if dunder_call is None:\n             return False  # pragma: no cover\n         if inspect.isgeneratorfunction(\n             _impartial(dunder_call)\n         ) or inspect.isgeneratorfunction(_unwrapped_call(dunder_call)):\n             return True\n-        dunder_unwrapped_call = getattr(_unwrapped_call(self.call), \"__call__\", None)  # noqa: B004\n+        \n+        unwrapped_call = _unwrapped_call(self.call)\n+        if inspect.isclass(unwrapped_call):\n+            return False\n+\n+        dunder_unwrapped_call = getattr(unwrapped_call, \"__call__\", None)  # noqa: B004\n         if dunder_unwrapped_call is None:\n             return False  # pragma: no cover\n         if inspect.isgeneratorfunction(\n", "is_truncated": false}
7edit_file
{
 "filepath": "fastapi/dependencies/models.py",
 "new_string": "    @cached_property\n    def is_async_gen_callable(self) -> bool:\n        if self.call is None:\n            return False  # pragma: no cover\n        if inspect.isasyncgenfunction(\n            _impartial(self.call)\n        ) or inspect.isasyncgenfunction(_unwrapped_call(self.call)):\n            return True\n        \n        impartial_call = _impartial(self.call)\n        if inspect.isclass(impartial_call):\n            return False\n\n        dunder_call = getattr(impartial_call, \"__call__\", None)  # noqa: B004\n        if dunder_call is None:\n            return False  # pragma: no cover\n        if inspect.isasyncgenfunction(\n            _impartial(dunder_call)\n        ) or inspect.isasyncgenfunction(_unwrapped_call(dunder_call)):\n            return True\n        \n        unwrapped_call = _unwrapped_call(self.call)\n        if inspect.isclass(unwrapped_call):\n            return False\n\n        dunder_unwrapped_call = getattr(unwrapped_call, \"__call__\", None)  # noqa: B004\n        if dunder_unwrapped_call is None:\n            return False  # pragma: no cover\n        if inspect.isasyncgenfunction(\n            _impartial(dunder_unwrapped_call)\n        ) or inspect.isasyncgenfunction(_unwrapped_call(dunder_unwrapped_call)):\n            return True\n        return False",
 "old_string": "    @cached_property\n    def is_async_gen_callable(self) -> bool:\n        if self.call is None:\n            return False  # pragma: no cover\n        if inspect.isasyncgenfunction(\n            _impartial(self.call)\n        ) or inspect.isasyncgenfunction(_unwrapped_call(self.call)):\n            return True\n        dunder_call = getattr(_impartial(self.call), \"__call__\", None)  # noqa: B004\n        if dunder_call is None:\n            return False  # pragma: no cover\n        if inspect.isasyncgenfunction(\n            _impartial(dunder_call)\n        ) or inspect.isasyncgenfunction(_unwrapped_call(dunder_call)):\n            return True\n        dunder_unwrapped_call = getattr(_unwrapped_call(self.call), \"__call__\", None)  # noqa: B004\n        if dunder_unwrapped_call is None:\n            return False  # pragma: no cover\n        if inspect.isasyncgenfunction(\n            _impartial(dunder_unwrapped_call)\n        ) or inspect.isasyncgenfunction(_unwrapped_call(dunder_unwrapped_call)):\n            return True"
}
{"status": "ok", "filepath": "fastapi/dependencies/models.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/fastapi/dependencies/models.py\n+++ b/fasta
full
{"status": "ok", "filepath": "fastapi/dependencies/models.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/fastapi/dependencies/models.py\n+++ b/fastapi/dependencies/models.py\n@@ -144,20 +144,31 @@\n             _impartial(self.call)\n         ) or inspect.isasyncgenfunction(_unwrapped_call(self.call)):\n             return True\n-        dunder_call = getattr(_impartial(self.call), \"__call__\", None)  # noqa: B004\n+        \n+        impartial_call = _impartial(self.call)\n+        if inspect.isclass(impartial_call):\n+            return False\n+\n+        dunder_call = getattr(impartial_call, \"__call__\", None)  # noqa: B004\n         if dunder_call is None:\n             return False  # pragma: no cover\n         if inspect.isasyncgenfunction(\n             _impartial(dunder_call)\n         ) or inspect.isasyncgenfunction(_unwrapped_call(dunder_call)):\n             return True\n-        dunder_unwrapped_call = getattr(_unwrapped_call(self.call), \"__call__\", None)  # noqa: B004\n+        \n+        unwrapped_call = _unwrapped_call(self.call)\n+        if inspect.isclass(unwrapped_call):\n+            return False\n+\n+        dunder_unwrapped_call = getattr(unwrapped_call, \"__call__\", None)  # noqa: B004\n         if dunder_unwrapped_call is None:\n             return False  # pragma: no cover\n         if inspect.isasyncgenfunction(\n             _impartial(dunder_unwrapped_call)\n         ) or inspect.isasyncgenfunction(_unwrapped_call(dunder_unwrapped_call)):\n             return True\n+        return False\n         return False\n \n     @cached_property\n", "is_truncated": false}
8edit_file
{
 "filepath": "fastapi/dependencies/models.py",
 "new_string": "    @cached_property\n    def is_coroutine_callable(self) -> bool:\n        if self.call is None:\n            return False  # pragma: no cover\n        if inspect.isroutine(_impartial(self.call)) and iscoroutinefunction(\n            _impartial(self.call)\n        ):\n            return True\n        if inspect.isroutine(_unwrapped_call(self.call)) and iscoroutinefunction(\n            _unwrapped_call(self.call)\n        ):\n            return True\n        \n        impartial_call = _impartial(self.call)\n        if inspect.isclass(impartial_call):\n            return False\n\n        dunder_call = getattr(impartial_call, \"__call__\", None)  # noqa: B004\n        if dunder_call is None:\n            return False  # pragma: no cover\n        if iscoroutinefunction(_impartial(dunder_call)) or iscoroutinefunction(\n            _unwrapped_call(dunder_call)\n        ):\n            return True\n        \n        unwrapped_call = _unwrapped_call(self.call)\n        if inspect.isclass(unwrapped_call):\n            return False\n\n        dunder_unwrapped_call = getattr(unwrapped_call, \"__call__\", None)  # noqa: B004\n        if dunder_unwrapped_call is None:\n            return False  # pragma: no cover\n        if iscoroutinefunction(\n            _impartial(dunder_unwrapped_call)\n        ) or iscoroutinefunction(_unwrapped_call(dunder_unwrapped_call)):\n            return True\n        # if inspect.isclass(self.call): False, covered by default return\n        return False",
 "old_string": "    @cached_property\n    def is_coroutine_callable(self) -> bool:\n        if self.call is None:\n            return False  # pragma: no cover\n        if inspect.isroutine(_impartial(self.call)) and iscoroutinefunction(\n            _impartial(self.call)\n        ):\n            return True\n        if inspect.isroutine(_unwrapped_call(self.call)) and iscoroutinefunction(\n            _unwrapped_call(self.call)\n        ):\n            return True\n        dunder_call = getattr(_impartial(self.call), \"__call__\", None)  # noqa: B004\n        if dunder_call is None:\n            return False  # pragma: no cover\n        if iscoroutinefunction(_impartial(dunder_call)) or iscoroutinefunction(\n            _unwrapped_call(dunder_call)\n        ):\n            return True\n        dunder_unwrapped_call = getattr(_unwrapped_call(self.call), \"__call__\", None)  # noqa: B004\n        if dunder_unwrapped_call is None:\n            return False  # pragma: no cover\n        if iscoroutinefunction(\n            _impartial(dunder_unwrapped_call)\n        ) or iscoroutinefunction(_unwrapped_call(dunder_unwrapped_call)):\n            return True\n        # if inspect.isclass(self.call): False, covered by default return\n        return False"
}
{"status": "ok", "filepath": "fastapi/dependencies/models.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/fastapi/dependencies/models.py\n+++ b/fasta
full
{"status": "ok", "filepath": "fastapi/dependencies/models.py", "occurrences": 1, "strategy": "exact", "diff": "--- a/fastapi/dependencies/models.py\n+++ b/fastapi/dependencies/models.py\n@@ -183,14 +183,24 @@\n             _unwrapped_call(self.call)\n         ):\n             return True\n-        dunder_call = getattr(_impartial(self.call), \"__call__\", None)  # noqa: B004\n+        \n+        impartial_call = _impartial(self.call)\n+        if inspect.isclass(impartial_call):\n+            return False\n+\n+        dunder_call = getattr(impartial_call, \"__call__\", None)  # noqa: B004\n         if dunder_call is None:\n             return False  # pragma: no cover\n         if iscoroutinefunction(_impartial(dunder_call)) or iscoroutinefunction(\n             _unwrapped_call(dunder_call)\n         ):\n             return True\n-        dunder_unwrapped_call = getattr(_unwrapped_call(self.call), \"__call__\", None)  # noqa: B004\n+        \n+        unwrapped_call = _unwrapped_call(self.call)\n+        if inspect.isclass(unwrapped_call):\n+            return False\n+\n+        dunder_unwrapped_call = getattr(unwrapped_call, \"__call__\", None)  # noqa: B004\n         if dunder_unwrapped_call is None:\n             return False  # pragma: no cover\n         if iscoroutinefunction(\n", "is_truncated": false}
9run_command
{
 "command": "python3 /tmp/repro.py"
}
{"status": "ok", "stdout": "Coroutine Instance: True\nCoroutine Class: False\nGen Instance: True\nGen Class: False\nAsyncGen Instance: True\nAsyncGen Class: Fal
full
{"status": "ok", "stdout": "Coroutine Instance: True\nCoroutine Class: False\nGen Instance: True\nGen Class: False\nAsyncGen Instance: True\nAsyncGen Class: False\n", "stderr": "", "exit_code": 0}
10submit_patch
{}
{"status": "ok", "patch_size": 3890, "files_changed": 1}
full
{"status": "ok", "patch_size": 3890, "files_changed": 1}

Patch

diff --git a/fastapi/dependencies/models.py b/fastapi/dependencies/models.py
index af168a17..2a500886 100644
--- a/fastapi/dependencies/models.py
+++ b/fastapi/dependencies/models.py
@@ -110,14 +110,24 @@ class Dependant:
             _impartial(self.call)
         ) or inspect.isgeneratorfunction(_unwrapped_call(self.call)):
             return True
-        dunder_call = getattr(_impartial(self.call), "__call__", None)  # noqa: B004
+        
+        impartial_call = _impartial(self.call)
+        if inspect.isclass(impartial_call):
+            return False
+
+        dunder_call = getattr(impartial_call, "__call__", None)  # noqa: B004
         if dunder_call is None:
             return False  # pragma: no cover
         if inspect.isgeneratorfunction(
             _impartial(dunder_call)
         ) or inspect.isgeneratorfunction(_unwrapped_call(dunder_call)):
             return True
-        dunder_unwrapped_call = getattr(_unwrapped_call(self.call), "__call__", None)  # noqa: B004
+        
+        unwrapped_call = _unwrapped_call(self.call)
+        if inspect.isclass(unwrapped_call):
+            return False
+
+        dunder_unwrapped_call = getattr(unwrapped_call, "__call__", None)  # noqa: B004
         if dunder_unwrapped_call is None:
             return False  # pragma: no cover
         if inspect.isgeneratorfunction(
@@ -134,14 +144,24 @@ class Dependant:
             _impartial(self.call)
         ) or inspect.isasyncgenfunction(_unwrapped_call(self.call)):
             return True
-        dunder_call = getattr(_impartial(self.call), "__call__", None)  # noqa: B004
+        
+        impartial_call = _impartial(self.call)
+        if inspect.isclass(impartial_call):
+            return False
+
+        dunder_call = getattr(impartial_call, "__call__", None)  # noqa: B004
         if dunder_call is None:
             return False  # pragma: no cover
         if inspect.isasyncgenfunction(
             _impartial(dunder_call)
         ) or inspect.isasyncgenfunction(_unwrapped_call(dunder_call)):
             return True
-        dunder_unwrapped_call = getattr(_unwrapped_call(self.call), "__call__", None)  # noqa: B004
+        
+        unwrapped_call = _unwrapped_call(self.call)
+        if inspect.isclass(unwrapped_call):
+            return False
+
+        dunder_unwrapped_call = getattr(unwrapped_call, "__call__", None)  # noqa: B004
         if dunder_unwrapped_call is None:
             return False  # pragma: no cover
         if inspect.isasyncgenfunction(
@@ -149,6 +169,7 @@ class Dependant:
         ) or inspect.isasyncgenfunction(_unwrapped_call(dunder_unwrapped_call)):
             return True
         return False
+        return False
 
     @cached_property
     def is_coroutine_callable(self) -> bool:
@@ -162,14 +183,24 @@ class Dependant:
             _unwrapped_call(self.call)
         ):
             return True
-        dunder_call = getattr(_impartial(self.call), "__call__", None)  # noqa: B004
+        
+        impartial_call = _impartial(self.call)
+        if inspect.isclass(impartial_call):
+            return False
+
+        dunder_call = getattr(impartial_call, "__call__", None)  # noqa: B004
         if dunder_call is None:
             return False  # pragma: no cover
         if iscoroutinefunction(_impartial(dunder_call)) or iscoroutinefunction(
             _unwrapped_call(dunder_call)
         ):
             return True
-        dunder_unwrapped_call = getattr(_unwrapped_call(self.call), "__call__", None)  # noqa: B004
+        
+        unwrapped_call = _unwrapped_call(self.call)
+        if inspect.isclass(unwrapped_call):
+            return False
+
+        dunder_unwrapped_call = getattr(unwrapped_call, "__call__", None)  # noqa: B004
         if dunder_unwrapped_call is None:
             return False  # pragma: no cover
         if iscoroutinefunction(

Test output

show
............                                                             [100%]
=============================== warnings summary ===============================
../../../../../../../Users/jp/repos/kaggle-gemini-coding-agent-post-training/.envs/overlays/starlette-0.50.0-py3-none-any/starlette/testclient.py:45
  /Users/jp/repos/kaggle-gemini-coding-agent-post-training/.envs/overlays/starlette-0.50.0-py3-none-any/starlette/testclient.py:45: DeprecationWarning: The anyio.abc.BlockingPortal alias is deprecated, use anyio.from_thread.BlockingPortal instead.
    _PortalFactoryType = Callable[[], AbstractContextManager[anyio.abc.BlockingPortal]]

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
12 passed, 1 warning in 0.47s