Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unexpected behavior when defining custom loop policy #591

Closed
schlamar opened this issue Jul 25, 2023 · 5 comments · Fixed by #662
Closed

Unexpected behavior when defining custom loop policy #591

schlamar opened this issue Jul 25, 2023 · 5 comments · Fixed by #662
Labels

Comments

@schlamar
Copy link

When defining a custom loop policy in a fixture, the first test is executed with the loop from the default event loop policy and not from the custom loop policy. I assume pytest-asyncio creates the first event loop before the fixture for the custom loop policy has run.

Simple reproduction test (Windows):

import asyncio
import pytest


@pytest.fixture(autouse=True, scope="module")
def set_loop_policy():
    asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())


@pytest.mark.asyncio
async def test_loop(event_loop):
    assert isinstance(event_loop, asyncio.SelectorEventLoop)


@pytest.mark.asyncio
async def test_loop_second(event_loop):
    assert isinstance(event_loop, asyncio.SelectorEventLoop)

The first test fails, the second passes. I would expect that both tests pass.

Related to #587 and #588

@seifertm
Copy link
Contributor

Thanks for the report and the reproducer!

There seems to be a problem with the order in which fixtures are resolved. The event_loop fixture is evaluated before set_loop_policy, so the policy change only takes effect after the first loop has been created. You can check for yourself by adding the `--setup-show`` option to the pytest call.

As a workaround, you can call asyncio.set_event_loop_policy outside of the fixture (i.e. directly in the module).

As per the pytest docs, wider-scoped fixtures are evaluated earlier. This behavior is probably broken by the event_loop fixture injection performed by pytest-asyncio.

@seifertm seifertm added the bug label Jul 25, 2023
@seifertm
Copy link
Contributor

Changing pytest-asyncio to evaluate the event_loop fixture last rather than first would solve the issue. However, the event_loop fixture technically is an autouse fixture, so it should be evaluated together with other autouse fixtures of the same scope.

@schlamar
Copy link
Author

As a workaround, you can call asyncio.set_event_loop_policy outside of the fixture (i.e. directly in the module).

That doesn't work in certain scenarios. For example if a loop policy should be active only for a specific package or if a loop policy fixture is parametrized (so tests run with multiple event loop implementations).

@seifertm
Copy link
Contributor

My understanding is that only the first test uses the wrong event loop policy. If you really need the policy to be set in a pytest fixture, you can add a dummy test in between the policy fixture and the tests as a workaround:

import asyncio
import pytest

class MyLoop(asyncio.SelectorEventLoop):
    pass

class MyLoopPolicy(asyncio.DefaultEventLoopPolicy):
    def new_event_loop(self):
        return MyLoop()

@pytest.fixture(autouse=True, scope="module")
def set_loop_policy():
    asyncio.set_event_loop_policy(MyLoopPolicy())

def test_workaround(event_loop):
    pass

@pytest.mark.asyncio
async def test_loop(event_loop):
    assert isinstance(event_loop, MyLoop)


@pytest.mark.asyncio
async def test_loop_second(event_loop):
    assert isinstance(event_loop, MyLoop)

This is all I can offer until we actually fix the issue.

@seifertm
Copy link
Contributor

@schlamar pytest-asyncio v0.23.0a0 is available on PyPI and adds an event_loop_policy fixture that allows controlling the policy independently from loop scopes. I'd appreciate your feedback on the pre-release version.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants