-
-
Notifications
You must be signed in to change notification settings - Fork 9k
0.69 introduces a RuntimeError: Task <...>got Future <Future pending> attached to a different loop #4100
Copy link
Copy link
Closed
Labels
Description
First Check
- I added a very descriptive title to this issue.
- I used the GitHub search to find a similar issue and didn't find it.
- I searched the FastAPI documentation, with the integrated search.
- I already searched in Google "How to X in FastAPI" and didn't find any information.
- I already read and followed all the tutorial in the docs and didn't find an answer.
- I already checked if it is not related to FastAPI but to Pydantic.
- I already checked if it is not related to FastAPI but to Swagger UI.
- I already checked if it is not related to FastAPI but to ReDoc.
Commit to Help
- I commit to help with one of those options 👆
Example Code
Create a venv with the following versions:
fastapi==0.70.0
tortoise-orm==0.17.8
pytest==6.2.5
save this code as main.py (name is important!)
from typing import Dict
import pytest
from fastapi import FastAPI
from fastapi import status
from fastapi.testclient import TestClient
from tortoise import Model
from tortoise import Tortoise
from tortoise.fields import CharField, ManyToManyRelation, ManyToManyField
class DbObj(Model):
name: str = CharField(max_length=255, pk=True)
my_rels1: ManyToManyRelation['DbObjRelation1']
my_rels2: ManyToManyRelation['DbObjRelation2']
class DbObjRelation1(Model):
name: str = CharField(max_length=255, pk=True)
my_objs: ManyToManyRelation['DbObj'] = ManyToManyField('models.DbObj', related_name='my_rels1')
class DbObjRelation2(Model):
name: str = CharField(max_length=255, pk=True)
my_objs: ManyToManyRelation['DbObj'] = ManyToManyField('models.DbObj', related_name='my_rels2')
app = FastAPI(title='Server')
@app.get(
path="/obj",
response_model=Dict[str, str],
status_code=status.HTTP_200_OK,
)
async def obj_get():
ct = await DbObj.all().prefetch_related('my_rels1', 'my_rels2')
ret = {}
for obj in ct:
ret[obj.name] = ', '.join(list(r.name for r in obj.my_rels1))
return ret
@app.put(
path="/obj/{name}",
response_model=Dict[str, str],
status_code=status.HTTP_200_OK,
)
async def obj_create(name: str):
obj = await DbObj.create(name=name)
rel = await DbObjRelation1.create(name=f'{name}_rel')
await obj.my_rels1.add(rel)
return None
test_client = TestClient(app)
@pytest.fixture(scope="function", autouse=True)
@pytest.mark.asyncio
async def db(monkeypatch):
await Tortoise.init(db_url='sqlite://:memory:', modules={'models': ['main']})
await Tortoise.generate_schemas()
yield
await Tortoise.close_connections()
def test_read_main():
# create one object
response = test_client.put("/obj/name1")
assert response.status_code == 200
# read it back
response = test_client.get("/obj")
assert response.status_code == 200
assert response.json() == {'name1': 'name1_rel'}Description
FastAPI 0.68.2 runs without issues.
From FastAPI 0.69.0 on tests fail:
run
pytest main.py
results in
Testing started at 14:43 ...
Launching pytest with arguments C:/MyTestVenv/src/main.py --no-header --no-summary -q in C:/MyTestVenv/src
============================= test session starts =============================
collecting ... collected 1 item
main.py::test_read_main FAILED [100%]
main.py:69 (test_read_main)
def test_read_main():
# create one object
response = test_client.put("/obj/name1")
assert response.status_code == 200
# read it back
> response = test_client.get("/obj")
main.py:76:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
..\venv\lib\site-packages\requests\sessions.py:555: in get
return self.request('GET', url, **kwargs)
..\venv\lib\site-packages\starlette\testclient.py:468: in request
return super().request(
..\venv\lib\site-packages\requests\sessions.py:542: in request
resp = self.send(prep, **send_kwargs)
..\venv\lib\site-packages\requests\sessions.py:655: in send
r = adapter.send(request, **kwargs)
..\venv\lib\site-packages\starlette\testclient.py:266: in send
raise exc
..\venv\lib\site-packages\starlette\testclient.py:263: in send
portal.call(self.app, scope, receive, send)
..\venv\lib\site-packages\anyio\from_thread.py:230: in call
return cast(T_Retval, self.start_task_soon(func, *args).result())
C:\Program Files\Python38\lib\concurrent\futures\_base.py:439: in result
return self.__get_result()
C:\Program Files\Python38\lib\concurrent\futures\_base.py:388: in __get_result
raise self._exception
..\venv\lib\site-packages\anyio\from_thread.py:177: in _call_func
retval = await retval
..\venv\lib\site-packages\fastapi\applications.py:208: in __call__
await super().__call__(scope, receive, send)
..\venv\lib\site-packages\starlette\applications.py:112: in __call__
await self.middleware_stack(scope, receive, send)
..\venv\lib\site-packages\starlette\middleware\errors.py:181: in __call__
raise exc
..\venv\lib\site-packages\starlette\middleware\errors.py:159: in __call__
await self.app(scope, receive, _send)
..\venv\lib\site-packages\starlette\exceptions.py:82: in __call__
raise exc
..\venv\lib\site-packages\starlette\exceptions.py:71: in __call__
await self.app(scope, receive, sender)
..\venv\lib\site-packages\starlette\routing.py:656: in __call__
await route.handle(scope, receive, send)
..\venv\lib\site-packages\starlette\routing.py:259: in handle
await self.app(scope, receive, send)
..\venv\lib\site-packages\starlette\routing.py:61: in app
response = await func(request)
..\venv\lib\site-packages\fastapi\routing.py:226: in app
raw_response = await run_endpoint_function(
..\venv\lib\site-packages\fastapi\routing.py:159: in run_endpoint_function
return await dependant.call(**values)
main.py:37: in obj_get
ct = await DbObj.all().prefetch_related('my_rels1', 'my_rels2')
..\venv\lib\site-packages\tortoise\queryset.py:966: in _execute
instance_list = await self._db.executor_class(
..\venv\lib\site-packages\tortoise\backends\base\executor.py:176: in execute_select
await self._execute_prefetch_queries(instance_list)
..\venv\lib\site-packages\tortoise\backends\base\executor.py:567: in _execute_prefetch_queries
await asyncio.gather(*prefetch_tasks)
..\venv\lib\site-packages\tortoise\backends\base\executor.py:555: in _do_prefetch
return await self._prefetch_m2m_relation(instance_id_list, field, related_query)
..\venv\lib\site-packages\tortoise\backends\base\executor.py:470: in _prefetch_m2m_relation
_, raw_results = await self.db.execute_query(query.get_sql())
..\venv\lib\site-packages\tortoise\backends\sqlite\client.py:30: in translate_exceptions_
return await func(self, query, *args)
..\venv\lib\site-packages\tortoise\backends\sqlite\client.py:133: in execute_query
async with self.acquire_connection() as connection:
..\venv\lib\site-packages\tortoise\backends\base\client.py:213: in __aenter__
await self.lock.acquire()
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <asyncio.locks.Lock object at 0x0000023238D27F10 [unlocked]>
async def acquire(self):
"""Acquire a lock.
This method blocks until the lock is unlocked, then sets it to
locked and returns True.
"""
if (not self._locked and (self._waiters is None or
all(w.cancelled() for w in self._waiters))):
self._locked = True
return True
if self._waiters is None:
self._waiters = collections.deque()
fut = self._loop.create_future()
self._waiters.append(fut)
# Finally block should be called before the CancelledError
# handling as we don't want CancelledError to call
# _wake_up_first() and attempt to wake up itself.
try:
try:
> await fut
E RuntimeError: Task <Task pending name='Task-11' coro=<BaseExecutor._do_prefetch() running at C:\MyTestVenv\venv\lib\site-packages\tortoise\backends\base\executor.py:555> cb=[gather.<locals>._done_callback() at C:\Program Files\Python38\lib\asyncio\tasks.py:769]> got Future <Future pending> attached to a different loop
C:\Program Files\Python38\lib\asyncio\locks.py:203: RuntimeError
============================== 1 failed in 0.63s ==============================
Process finished with exit code 1
Operating System
Windows
Operating System Details
No response
FastAPI Version
0.70.0
Python Version
Python 3.8.8
Additional Context
No response
Reactions are currently unavailable