← pod_r5_v1i

fastapi_14458

resolved RESOLVED PASS · 14 tool calls · 130 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 (15)

#ToolArgumentsResult
1run_command
{
 "command": "( grep -rn 'is_coroutine_callable' --include=*.py . || echo '<IMPORTANT> NO MATCH FOUND. DO NOT TRY SAME COMMAND.' ) | head -40"
}
{"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}
2run_command
{
 "command": "sed -n '150,170p' fastapi/dependencies/models.py"
}
{"status": "ok", "stdout": " 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 #
full
{"status": "ok", "stdout": "            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", "stderr": "", "exit_code": 0}
3run_command
{
 "command": "sed -n '150,200p' fastapi/dependencies/models.py"
}
{"status": "ok", "stdout": " 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 #
full
{"status": "ok", "stdout": "            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\n", "stderr": "", "exit_code": 0}
4run_command
{
 "command": "grep -nE 'is_gen_callable|is_async_gen_callable' fastapi/dependencies/models.py"
}
{"status": "ok", "stdout": "106: def is_gen_callable(self) -> bool:\n130: def is_async_gen_callable(self) -> bool:\n186: if self.is_gen_callable or self.is_asyn
full
{"status": "ok", "stdout": "106:    def is_gen_callable(self) -> bool:\n130:    def is_async_gen_callable(self) -> bool:\n186:        if self.is_gen_callable or self.is_async_gen_callable:\n", "stderr": "", "exit_code": 0}
5run_command
{
 "command": "sed -n '100,130p' fastapi/dependencies/models.py"
}
{"status": "ok", "stdout": " @cached_property\n def _security_dependencies(self) -> List[\"Dependant\"]:\n security_deps = [dep for dep in self.dependencies if
full
{"status": "ok", "stdout": "    @cached_property\n    def _security_dependencies(self) -> List[\"Dependant\"]:\n        security_deps = [dep for dep in self.dependencies if dep._is_security_scheme]\n        return security_deps\n\n    @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\n\n    @cached_property\n    def is_async_gen_callable(self) -> bool:\n", "stderr": "", "exit_code": 0}
6run_command
{
 "command": "sed -n '130,150p' fastapi/dependencies/models.py"
}
{"status": "ok", "stdout": " def is_async_gen_callable(self) -> bool:\n if self.call is None:\n return False # pragma: no cover\n if inspect.isasyncgenfunction(
full
{"status": "ok", "stdout": "    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\n", "stderr": "", "exit_code": 0}
7run_command
{
 "command": "cat > /tmp/repro.py <<'PY'\nimport inspect\nfrom fastapi.dependencies.models import Dependant\nfrom fastapi import FastAPI\nfrom fastapi.dependencies.utils import solve_dependencies\nfrom starlette.requests import Request\nimport asyncio\n\nasync def iscoroutinefunction(func):\n    return inspect.iscoroutinefunction(func)\n\nclass MyDependency:\n    async def __call__(self):\n        return \"hello\"\n\ndef test_class_dependency():\n    # The problematic case: using the class itself as a dependency\n    # The class has a __call__ method that is a coroutine.\n    # FastAPI should treat the class as a normal callable (instantiating it first)\n    # but it's currently treating it as if it's already a coroutine function\n    # because it checks the __call__ method of the class.\n    \n    # In FastAPI, a class used as a dependency is instantiated.\n    # If it has a __call__ method, the instance is called.\n    \n    dep = Dependant(call=MyDependency)\n    print(f\"is_coroutine_callable: {dep.is_coroutine_callable}\")\n    \n    # It should be False because the class itself is not a coroutine function.\n    # The INSTANCE of the class would be a coroutine callable.\n    assert dep.is_coroutine_callable is False\n\nif __name__ == \"__main__\":\n    try:\n        test_class_dependency()\n        print(\"Test passed!\")\n    except AssertionError as e:\n        print(\"Test failed!\")\n        raise e\nPY\nrun_command: python3 /tmp/repro.py\n"
}
{"status": "error", "error_type": "CommandError", "error_message": "/bin/bash: line 40: run_command:: command not found\n", "details": {"stdout": "", "stderr":
full
{"status": "error", "error_type": "CommandError", "error_message": "/bin/bash: line 40: run_command:: command not found\n", "details": {"stdout": "", "stderr": "/bin/bash: line 40: run_command:: command not found\n", "exit_code": 127}}
8run_command
{
 "command": "python3 /tmp/repro.py"
}
{"status": "error", "error_type": "CommandError", "error_message": "Traceback (most recent call last):\n File \"/tmp/repro.py\", line 38, in <module>\n raise e\
full
{"status": "error", "error_type": "CommandError", "error_message": "Traceback (most recent call last):\n  File \"/tmp/repro.py\", line 38, in <module>\n    raise e\n  File \"/tmp/repro.py\", line 34, in <module>\n    test_class_dependency()\n    ~~~~~~~~~~~~~~~~~~~~~^^\n  File \"/tmp/repro.py\", line 30, in test_class_dependency\n    assert dep.is_coroutine_callable is False\n           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nAssertionError\n", "details": {"stdout": "is_coroutine_callable: True\nTest failed!\n", "stderr": "Traceback (most recent call last):\n  File \"/tmp/repro.py\", line 38, in <module>\n    raise e\n  File \"/tmp/repro.py\", line 34, in <module>\n    test_class_dependency()\n    ~~~~~~~~~~~~~~~~~~~~~^^\n  File \"/tmp/repro.py\", line 30, in test_class_dependency\n    assert dep.is_coroutine_callable is False\n           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\nAssertionError\n", "exit_code": 1}}
9run_command
{
 "command": "cd /workspace && python3 - <<'PY'\nfrom pathlib import Path\np = Path(\"fastapi/dependencies/models.py\")\ns = p.read_text()\n\n# Fix is_gen_callable\nold_gen = \"\"\"        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\nnew_gen = \"\"\"        dunder_call = getattr(_impartial(self.call), \"__call__\", None)  # noqa: B004\n        if dunder_call is None or inspect.isclass(_impartial(self.call)):\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 or inspect.isclass(_unwrapped_call(self.call)):\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\n# Fix is_async_gen_callable\nold_async_gen = \"\"\"        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\"\"\"\n\nnew_async_gen = \"\"\"        dunder_call = getattr(_impartial(self.call), \"__call__\", None)  # noqa: B004\n        if dunder_call is None or inspect.isclass(_impartial(self.call)):\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 or inspect.isclass(_unwrapped_call(self.call)):\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\n# Fix is_coroutine_callable\nold_coro = \"\"\"        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     
{"status": "ok", "stdout": "", "stderr": "", "exit_code": 0}
full
{"status": "ok", "stdout": "", "stderr": "", "exit_code": 0}
10run_command
{
 "command": "python3 /tmp/repro.py"
}
{"status": "ok", "stdout": "is_coroutine_callable: False\nTest passed!\n", "stderr": "", "exit_code": 0}
full
{"status": "ok", "stdout": "is_coroutine_callable: False\nTest passed!\n", "stderr": "", "exit_code": 0}
11run_command
{
 "command": "cat > /tmp/repro_gen.py <<'PY'\nimport inspect\nfrom fastapi.dependencies.models import Dependant\n\nclass MyGenDependency:\n    def __call__(self):\n        yield \"hello\"\n\nclass MyAsyncGenDependency:\n    async def __call__(self):\n        yield \"hello\"\n\ndef test_gen_class_dependency():\n    dep_gen = Dependant(call=MyGenDependency)\n    print(f\"is_gen_callable: {dep_gen.is_gen_callable}\")\n    assert dep_gen.is_gen_callable is False\n\n    dep_async_gen = Dependant(call=MyAsyncGenDependency)\n    print(f\"is_async_gen_callable: {dep_async_gen.is_async_gen_callable}\")\n    assert dep_async_gen.is_async_gen_callable is False\n\nif __name__ == \"__main__\":\n    try:\n        test_gen_class_dependency()\n        print(\"Tests passed!\")\n    except AssertionError as e:\n        print(\"Tests failed!\")\n        raise e\nPY\npython3 /tmp/repro_gen.py\n"
}
{"status": "ok", "stdout": "is_gen_callable: False\nis_async_gen_callable: False\nTests passed!\n", "stderr": "", "exit_code": 0}
full
{"status": "ok", "stdout": "is_gen_callable: False\nis_async_gen_callable: False\nTests passed!\n", "stderr": "", "exit_code": 0}
12run_command
{
 "command": "cat > /tmp/repro_instance.py <<'PY'\nimport inspect\nfrom fastapi.dependencies.models import Dependant\n\nclass MyCoroutineDependency:\n    async def __call__(self):\n        return \"hello\"\n\nclass MyGenDependency:\n    def __call__(self):\n        yield \"hello\"\n\nclass MyAsyncGenDependency:\n    async def __call__(self):\n        yield \"hello\"\n\ndef test_instance_dependency():\n    # Instance of class with __call__ SHOULD still be detected\n    dep_coro = Dependant(call=MyCoroutineDependency())\n    print(f\"is_coroutine_callable: {dep_coro.is_coroutine_callable}\")\n    assert dep_coro.is_coroutine_callable is True\n\n    dep_gen = Dependant(call=MyGenDependency())\n    print(f\"is_gen_callable: {dep_gen.is_gen_callable}\")\n    assert dep_gen.is_gen_callable is True\n\n    dep_async_gen = Dependant(call=MyAsyncGenDependency())\n    print(f\"is_async_gen_callable: {dep_async_gen.is_async_gen_callable}\")\n    assert dep_async_gen.is_async_gen_callable is True\n\nif __name__ == \"__main__\":\n    try:\n        test_instance_dependency()\n        print(\"Tests passed!\")\n    except AssertionError as e:\n        print(\"Tests failed!\")\n        raise e\nPY\npython3 /tmp/repro_instance.py\n"
}
{"status": "ok", "stdout": "is_coroutine_callable: True\nis_gen_callable: True\nis_async_gen_callable: True\nTests passed!\n", "stderr": "", "exit_code": 0}
full
{"status": "ok", "stdout": "is_coroutine_callable: True\nis_gen_callable: True\nis_async_gen_callable: True\nTests passed!\n", "stderr": "", "exit_code": 0}
13run_command
{
 "command": "git diff"
}
{"status": "ok", "stdout": "diff --git a/fastapi/dependencies/models.py b/fastapi/dependencies/models.py\nindex af168a17..625fb24c 100644\n--- a/fastapi/depende
full
{"status": "ok", "stdout": "diff --git a/fastapi/dependencies/models.py b/fastapi/dependencies/models.py\nindex af168a17..625fb24c 100644\n--- a/fastapi/dependencies/models.py\n+++ b/fastapi/dependencies/models.py\n@@ -111,14 +111,14 @@ class Dependant:\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+        if dunder_call is None or inspect.isclass(_impartial(self.call)):\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+        if dunder_unwrapped_call is None or inspect.isclass(_unwrapped_call(self.call)):\n             return False  # pragma: no cover\n         if inspect.isgeneratorfunction(\n             _impartial(dunder_unwrapped_call)\n@@ -135,14 +135,14 @@ class Dependant:\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+        if dunder_call is None or inspect.isclass(_impartial(self.call)):\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+        if dunder_unwrapped_call is None or inspect.isclass(_unwrapped_call(self.call)):\n             return False  # pragma: no cover
14run_command
{
 "command": "submit_patch"
}
{"status": "error", "error_type": "CommandError", "error_message": "/bin/bash: submit_patch: command not found\n", "details": {"stdout": "", "stderr": "/bin/bas
full
{"status": "error", "error_type": "CommandError", "error_message": "/bin/bash: submit_patch: command not found\n", "details": {"stdout": "", "stderr": "/bin/bash: submit_patch: command not found\n", "exit_code": 127}}
15submit_patch
{}
{"status": "ok", "patch_size": 2853, "files_changed": 1}
full
{"status": "ok", "patch_size": 2853, "files_changed": 1}

Patch

diff --git a/fastapi/dependencies/models.py b/fastapi/dependencies/models.py
index af168a17..625fb24c 100644
--- a/fastapi/dependencies/models.py
+++ b/fastapi/dependencies/models.py
@@ -111,14 +111,14 @@ class Dependant:
         ) or inspect.isgeneratorfunction(_unwrapped_call(self.call)):
             return True
         dunder_call = getattr(_impartial(self.call), "__call__", None)  # noqa: B004
-        if dunder_call is None:
+        if dunder_call is None or inspect.isclass(_impartial(self.call)):
             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
-        if dunder_unwrapped_call is None:
+        if dunder_unwrapped_call is None or inspect.isclass(_unwrapped_call(self.call)):
             return False  # pragma: no cover
         if inspect.isgeneratorfunction(
             _impartial(dunder_unwrapped_call)
@@ -135,14 +135,14 @@ class Dependant:
         ) or inspect.isasyncgenfunction(_unwrapped_call(self.call)):
             return True
         dunder_call = getattr(_impartial(self.call), "__call__", None)  # noqa: B004
-        if dunder_call is None:
+        if dunder_call is None or inspect.isclass(_impartial(self.call)):
             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
-        if dunder_unwrapped_call is None:
+        if dunder_unwrapped_call is None or inspect.isclass(_unwrapped_call(self.call)):
             return False  # pragma: no cover
         if inspect.isasyncgenfunction(
             _impartial(dunder_unwrapped_call)
@@ -163,14 +163,14 @@ class Dependant:
         ):
             return True
         dunder_call = getattr(_impartial(self.call), "__call__", None)  # noqa: B004
-        if dunder_call is None:
+        if dunder_call is None or inspect.isclass(_impartial(self.call)):
             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
-        if dunder_unwrapped_call is None:
+        if dunder_unwrapped_call is None or inspect.isclass(_unwrapped_call(self.call)):
             return False  # pragma: no cover
         if iscoroutinefunction(
             _impartial(dunder_unwrapped_call)

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.54s