Skip to content

Teardown error: "attached to a different loop" with even_loop module scope fixture #162

Closed
@alblasco

Description

@alblasco

I'm working on python/pytest/asyncio since beginning of this year, and when updating pytest-asyncio (from 0.10.0 to 0.12.0) then some previous working tests started to fail.

I have created following minimal example (isolated from a more complex solution) that always fails at teardown.
Can you please let me know if you can reproduce it, and your kind feedback about this potential issue.
Don´t hesitate to ask for any additional information or test on my side.

BR

Angel Luis

Additional info

Versions used to reproduce are:

  • pytest-asyncio 0.12.0. (it fails also with 0.11.0)
  • pytest 5.4.2
  • python 3.8.2
  • setuptools 46.2.0 & pip 20.1
  • windows 10 Pro [Versión 10.0.18362.175]
    Note: Minimal Example works with pytest-asyncio 0.10.0.

Minimal example is the following

import asyncio
import contextlib
import functools
import pytest


@pytest.mark.asyncio
async def test_simple(port):
    assert True

@pytest.fixture(scope="module")
def event_loop():
    """Change event_loop fixture to module level."""
    loop = asyncio.get_event_loop_policy().new_event_loop()
    yield loop
    loop.close()


@pytest.fixture(scope="module")
async def port(request, event_loop):
    def port_finalizer(finalizer):
        async def port_afinalizer():
            await finalizer(None, None, None)
        event_loop.run_until_complete(port_afinalizer())

    context_manager = port_map()
    port = await context_manager.__aenter__()
    request.addfinalizer(functools.partial(port_finalizer, context_manager.__aexit__))
    return port

@contextlib.asynccontextmanager
async def port_map():
    worker = asyncio.create_task(background_worker())
    yield
    try:
        worker.cancel()
        await worker
    except asyncio.CancelledError:
        pass

async def background_worker():
    while True:
        await asyncio.sleep(10.0)

When I run the test I get the following console output:

(venv) C:\git\ATB2\ejemplo_issue>pytest
=================== test session starts =============================
platform win32 -- Python 3.8.2, pytest-5.4.2, py-1.8.1, pluggy-0.13.1
rootdir: C:\git\ATB2\ejemplo_issue
plugins: asyncio-0.12.0
collected 1 item                                                                                                                                                                                                                                                           

test_example.py .E                                                                                                                                                                                                                                                   [100%]

====================== ERRORS =======================================
______________ ERROR at teardown of test_simple _____________________

finalizer = <bound method _AsyncGeneratorContextManager.__aexit__ of <contextlib._AsyncGeneratorContextManager object at 0x0423AC40>>

    def port_finalizer(finalizer):
        async def port_afinalizer():
            await finalizer(None, None, None)
>       event_loop.run_until_complete(port_afinalizer())

test_example.py:24:
 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
C:\Users\ablasco\AppData\Local\Programs\Python\Python38-32\lib\asyncio\base_events.py:616: in run_until_complete
    return future.result()
test_example.py:23: in port_afinalizer
    await finalizer(None, None, None)
C:\Users\ablasco\AppData\Local\Programs\Python\Python38-32\lib\contextlib.py:178: in __aexit__
    await self.gen.__anext__()
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

    @contextlib.asynccontextmanager
    async def port_map():
        worker = asyncio.create_task(background_worker())
        yield
        try:
            worker.cancel()
>           await worker
E           RuntimeError: Task <Task pending name='Task-4' coro=<port.<locals>.port_finalizer.<locals>.port_afinalizer() running at C:\git\ATB2\ejemplo_issue\test_example.py:23> cb=[_run_until_complete_cb() at C:\Users\ablasco\AppData\Local\Programs\Python\Python38-32
\lib\asyncio\base_events.py:184]> got Future <Task pending name='Task-2' coro=<background_worker() running at C:\git\ATB2\ejemplo_issue\test_example.py:43> wait_for=<Future cancelled>> attached to a different loop

test_example.py:37: RuntimeError
============================= short test summary info ==============================
ERROR test_example.py::test_simple - RuntimeError: Task <Task pending name='Task-4' coro=<port.<locals>.port_finalizer.<locals>.port_afinalizer() running at C:\git\ATB2\ejemplo_issue\test_example.py:23> cb=[_run_until_complete_cb() at C:\Users\ablasco\AppData\Local...

=========================== 1 passed, 1 error in 0.13s =============================

Unexpectedly either of the following changes, made the test to pass:

a) Change fixture to a lower level scope (class or function):

@pytest.fixture(scope="class")
def event_loop():
@pytest.fixture(scope="class")
async def port(request, event_loop):

b) Embed the test inside a class:

class TestClass:
    @pytest.mark.asyncio
    async def test_simple(port):
        assert True

c) Or change finalizer to call get_event_loop (instead of using event_loop from fixture)

    def port_finalizer(finalizer):
        async def port_afinalizer():
            await finalizer(None, None, None)
        # event_loop.run_until_complete(port_afinalizer())
        loop = asyncio.get_event_loop()
        loop.run_until_complete(port_afinalizer())

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions