pytest-dev/pytest-asyncio

Regression in 0.25.2 - Event loop is closed

schlamar opened this issue · 7 comments

If there is a user defined fixture which handles loop shutdown and closing the loop on its own, the plugin raises an error.

Expected behavior: If user code closes the event loop, pytest-asyncio should handle this transparently.

Actual behavior: pytest-asyncio raises RuntimeError: Event loop is closed.

..\..\AppData\Local\Programs\Python\Python310\lib\contextlib.py:142: in __exit__
    next(self.gen)
.venv\lib\site-packages\pytest_asyncio\plugin.py:1168: in _provide_event_loop
    loop.run_until_complete(loop.shutdown_asyncgens())
..\..\AppData\Local\Programs\Python\Python310\lib\asyncio\base_events.py:624: in run_until_complete
    self._check_closed()

Example working with 0.25.1, crashes with 0.25.2:

import asyncio

import pytest


@pytest.fixture(autouse=True)
def handle_event_loop(request: pytest.FixtureRequest):
    event_loop = None
    if "event_loop" in request.fixturenames:
        event_loop = request.getfixturevalue("event_loop")

    yield

    if event_loop is None or event_loop.is_closed():
        return
    try:
        asyncio.runners._cancel_all_tasks(event_loop)
        event_loop.run_until_complete(event_loop.shutdown_asyncgens())
        if hasattr(event_loop, "shutdown_default_executor"):  # Python 3.9+
            event_loop.run_until_complete(event_loop.shutdown_default_executor())
    finally:
        event_loop.close()


async def test():
    assert True

Thanks for the detailed report!
A patch release shouldn't introduce a breaking change. We should address this in a new v0.25 release.
It's also quite easy to address in pytest-asyncio.

I am also getting this error with my doctests.

The error occurs already with v0.25.1 (in my case, when using it together with pytest-playwright).

fwiw, another related regression in #1034 is that the shutdown can apparently also fail with CancelledError, which I guess should be suppressed? I'm not sure how/why, but we're seeing that. Might be something weird we are doing, but i think it is in general related to the fact that our own event-loop cleanup code conflicts with pytest-asyncio unconditionally cleaning things up without checking if it can/should.

The error occurs already with v0.25.1 (in my case, when using it together with pytest-playwright).

@medihack This has to be another issue. The problem I'm describing here is the direct cause of a change introduced in 0.25.2. Please try to check if your problem is not a user issue or an issue with pytest-playwright.

If you get a reproducible test case working with 0.25.0 but not with 0.25.1 and 0.25.2, please open a separate issue as this is very likely unrelated and probably requires a different bug fix.

fwiw, another related regression in #1034 is that the shutdown can apparently also fail with CancelledError, which I guess should be suppressed? I'm not sure how/why, but we're seeing that. Might be something weird we are doing, but i think it is in general related to the fact that our own event-loop cleanup code conflicts with pytest-asyncio unconditionally cleaning things up without checking if it can/should.

The asyncio.Runner.close doesn't seem to handle CancelledErrors explicitly, neither in shutdown_asyncgens, nor in _cancel_all_tasks. It does forward any exceptions to the loop's default exception handler, though.

Suppressing these errors seems like a good idea, until #205 is tackled.

Pytest-asyncio v0.25.3 is available, which should address this issue.

Thanks to @minrk for contributing the bug fix!