pytest-dev / pytest-asyncio

Asyncio support for pytest
https://pytest-asyncio.readthedocs.io
Apache License 2.0
1.43k stars 152 forks source link

setup-plan option make event_loop become tuple #630

Open tsangwpx opened 1 year ago

tsangwpx commented 1 year ago

Hello, I run: pytest --setup-plan -sv tests/test.py in container image python:3.11 and received the following error:

==================================== test session starts ====================================
platform linux -- Python 3.11.6, pytest-7.4.2, pluggy-1.3.0 -- /usr/local/bin/python
cachedir: .pytest_cache
rootdir: /root
plugins: asyncio-0.21.1
asyncio: mode=Mode.STRICT
collected 1 item

tests/test.py::test_hello
        SETUP    F event_loopERROR
        TEARDOWN F event_loop

========================================== ERRORS ===========================================
_______________________________ ERROR at setup of test_hello ________________________________

fixturedef = <FixtureDef argname='event_loop' scope='function' baseid=''>
request = <SubRequest 'event_loop' for <Function test_hello>>

    @pytest.hookimpl(hookwrapper=True)
    def pytest_fixture_setup(
        fixturedef: FixtureDef, request: SubRequest
    ) -> Optional[object]:
        """Adjust the event loop policy when an event loop is produced."""
        if fixturedef.argname == "event_loop":
            # The use of a fixture finalizer is preferred over the
            # pytest_fixture_post_finalizer hook. The fixture finalizer is invoked once
            # for each fixture, whereas the hook may be invoked multiple times for
            # any specific fixture.
            # see https://github.com/pytest-dev/pytest/issues/5848
            _add_finalizers(
                fixturedef,
                _close_event_loop,
                _provide_clean_event_loop,
            )
            outcome = yield
            loop = outcome.get_result()
            policy = asyncio.get_event_loop_policy()
            try:
                with warnings.catch_warnings():
                    warnings.simplefilter("ignore", DeprecationWarning)
                    old_loop = policy.get_event_loop()
                if old_loop is not loop:
                    old_loop.close()
            except RuntimeError:
                # Either the current event loop has been set to None
                # or the loop policy doesn't specify to create new loops
                # or we're not in the main thread
                pass
>           policy.set_event_loop(loop)

/usr/local/lib/python3.11/site-packages/pytest_asyncio/plugin.py:410:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
/usr/local/lib/python3.11/asyncio/unix_events.py:1457: in set_event_loop
    super().set_event_loop(loop)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <asyncio.unix_events._UnixDefaultEventLoopPolicy object at 0x7f5144eb8c10>
loop = (None, 0, None)

    def set_event_loop(self, loop):
        """Set the event loop."""
        self._local._set_called = True
        if loop is not None and not isinstance(loop, AbstractEventLoop):
>           raise TypeError(f"loop must be an instance of AbstractEventLoop or None, not '{type(loop).__name__}'")
E           TypeError: loop must be an instance of AbstractEventLoop or None, not 'tuple'

/usr/local/lib/python3.11/asyncio/events.py:686: TypeError
================================== short test summary info ==================================
ERROR tests/test.py::test_hello - TypeError: loop must be an instance of AbstractEventLoop or None, not 'tuple'
===================================== 1 error in 0.04s ======================================

It seems that --setup-plan caused loop value became (None, 0, None) instead of event loop instance.

Here is tests/test.py:

import pytest

@pytest.mark.asyncio
async def test_hello():
    print("Hello")

Python version: 3.11.6 pip freeze:

iniconfig==2.0.0
packaging==23.2
pluggy==1.3.0
pytest==7.4.2
pytest-asyncio==0.21.1
seifertm commented 1 year ago

Thanks for the report and for the great reproducer! I can reproduce the issue.

This is a bit of a head-scratcher to me. Pytest-asyncio reimplements a bunch of pytest functionality related to fixture evaluation, in order to support async def fixture functions. Something must go wrong there.

I hope that we can use more of the standard pytest functionality in the future, especially related to resolving fixtures requested by test functions.

Until this issue is fixed, the only workaround that comes to my mind is using --setup-show, instead of --setup-plan, if that's possible in your case.