question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

0.69 introduces a RuntimeError: Task <...>got Future <Future pending> attached to a different loop

See original GitHub issue

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

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Comments:9 (1 by maintainers)

github_iconTop GitHub Comments

1reaction
spacemanspiff2007commented, Jan 18, 2022

No, but those who are async use the AsyncTestClient. I am using @pytest.mark.asyncio for these functions but I guess .anyio would be more correct. See the first example from the link for the import.

0reactions
fulloniccommented, Jan 17, 2022

@spacemanspiff2007 so, all your tests are async? Do you use pytest for testing? If so, are you doing pytest.mark.anyio to all your tests?

From where do you import AsyncTestClient? Or is it just a renaming of httpx.AsyncClient? Thanks

Read more comments on GitHub >

github_iconTop Results From Across the Web

Task got Future <Future pending> attached to a different loop ...
This method returns the event loop for the current thread, which in your code, is the main thread. Telethon then remembers and uses...
Read more >
tiangolo/fastapi - Gitter
I want to run a background task but one that's not scheduled by a ... My error is always "got Future <Future pending>...
Read more >
Release Notes - FastAPI
You can start with Python Types Intro, it explains what changes between different Python versions, in Python 3.9 and in Python 3.10. All...
Read more >
Apache Tomcat 9 (9.0.70) - Changelog
To aid future additions of new functionality, rather than throw an ... Update ImportHandler optimisation for new classes introduced in Java 19. (markt) ......
Read more >
Data Structures and Algorithm Analysis in C++ - UOITC
introduced in C++11. r. Chapter 8 uses the new union/find analysis by Seidel and Sharir and shows the. O( M α(M, N) )...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found