pytest-dev/pytest-asyncio

Finish deprecation of unset asyncio_default_fixture_loop_scope

seifertm opened this issue ยท 16 comments

Pytest-asyncio v0.24 introduced the configuration option asyncio_default_fixture_loop_scope which controls the event loop in which async fixtures are run. In order to allow partial migration of test suites, users have the option to leave the option unset. In this case, the event loop scope of async fixtures defaults to the fixture caching scope, unless explicitly specified. However, when asyncio_default_fixture_loop_scope is unset, pytest-asyncio will emit a deprecation warning.

The goal of this issue is to remove the option to have asyncio_default_fixture_loop_scope unset. The default value should be funciton. The pytest-asyncio test suite should be adjusted accordingly, so that asyncio_default_fixture_loop_scope is not set explicitly, unless necessary.

In the meantime, how can one suppress the PytestDeprecationWarning that pops up? I am trying to configure a filterwarnings entry in pyproject.toml's [tool.pytest.ini_options], but I can't figure out the syntax. Here is what I am trying, but it isn't working:

[tool.pytest.ini_options]
filterwarnings = [
    'ignore:configuration option "asyncio_default_fixture_loop_scope" is unset',
]

I also don't want to use pytest -p no:warnings.

In the meantime, how can one suppress the PytestDeprecationWarning that pops up? I am trying to configure a filterwarnings entry in pyproject.toml's [tool.pytest.ini_options], but I can't figure out the syntax. Here is what I am trying, but it isn't working:

[tool.pytest.ini_options]
filterwarnings = [
    'ignore:configuration option "asyncio_default_fixture_loop_scope" is unset',
]

I also don't want to use pytest -p no:warnings.

Have the same issue.
You can try to make pyproject.toml file in the root of your project which contains:

[tool.pytest.ini_options]
asyncio_default_fixture_loop_scope = "function"

And It worked for me.
But I dont like this approach.
May be there is a way to add something in tests subdir?

Yeah I also don't want to add that asyncio_default_fixture_loop_scope value to all my pyproject.toml. I would rather just silence the warning.

I played around a bunch more today, and still couldn't get filterwarnings to work. I think the issue is that the deprecation warning comes from a pytest plugin, which gets loaded before filterwarnings takes effect. I thus opened pytest-dev/pytest#12756 to have this fixed upstream in pytest, then I think the filterwarnings route can work ๐Ÿ‘Œ

You can also set it in your pytest.ini to silence warnings, but agreed would be nice to properly default without warning.

[pytest]
asyncio_mode=auto
asyncio_default_fixture_loop_scope="function"
nierob commented

In our project we want to use one event loop for all the tests. The tests are written in an "event loop safe" way. Nothing should leak from a test to a test and if it does it hints a much bigger problem, than just a test isolation issue. That also means that there is some shared state, like db connection, but it is fine and expected. To achieve that we set:

@pytest.fixture(scope="session")
def event_loop(request) -> typing.Generator:
    loop = asyncio.get_event_loop()
    yield loop
    loop.close()

Now we hit the warning about unset asyncio_default_fixture_loop_scope, it looks like a config that could replace our custom fixture. So we added pyproject.toml config:

[tool.pytest.ini_options]
testpaths = [
    "tests/",
]
addopts = "--durations=12  --disable-socket --allow-hosts=localhost,127.0.0.1,::1 --strict-markers"
...
asyncio_default_fixture_loop_scope = "session"
asyncio_mode = "auto"  # See pytest-asyncio plug-in

The operation replaced the warning with:

Replacing the event_loop fixture with a custom implementation is deprecated
  and will lead to errors in the future.
  If you want to request an asyncio event loop with a scope other than function
  scope, use the "scope" argument to the asyncio mark when marking the tests.
  If you want to return different types of event loops, use the event_loop_policy
  fixture.

Great so far, but now removing our fixtures causes problems:

RuntimeError('Event loop is closed')

So something is off, the event loop should be closed at the process exit only, unless I misunderstood the feature. The error goes away if I mark the tests with an explicit:

@pytest.mark.asyncio(loop_scope="session")

but then what is the point of asyncio_default_fixture_loop_scope ?

Cant get rid of asyncio warning.
Iv removed pytest.ini, pyproject.toml etc.
Added conftest.py with content nierob shown,
Marked async tests explicitly with

@pytest.mark.asyncio(loop_scope="session")

And nothing changed - still getting pytest-asyncio warning.

Should v0.24.0 fix that? I dont get it...

@jamesbraza Thanks for pointing to the pytest issue! I wasn't aware of that.

@nierob The asyncio_default_fixture_loop_scope setting only affects async fixtures, not async tests. A similar configuration option for async tests is very much desired and tracked in #793. That means the asyncio_default_fixture_loop_scope setting and the @pytest.mark.asyncio(loop_scope="โ€ฆ") are independent. Other than that, your understanding of the feature is very much correct and removing the event_loop fixture implementation is the correct thing to do.

If it's any help, I'd like to refer you to one of the migration guides and to a how-to about running all tests in a session-wide loop

@ixenion Let me know if these how-tos help you solve the DeprecationWarning.

nierob commented

Thank you for the explanation! I did not know that fixtures follows a different configuration then tests.

In addition to asyncio_default_fixture_loop_scope I implemented pytest_collection_modifyitems to mark all tests with scoped event loop, that allowed me to remove my own loop fixture and all warnings are gone. I'm looking forward to #793 to remove pytest_collection_modifyitems call in future too ๐Ÿค— !

I use async fixture with config asyncio_default_fixture_loop_scope="function",
and has error

    def _retrieve_scope_root(item: Union[Collector, Item], scope: str) -> Collector:
        node_type_by_scope = {
            "class": Class,
            "module": Module,
            "package": Package,
            "session": Session,
        }
>       scope_root_type = node_type_by_scope[scope]
E       KeyError: '"function"'

Help to remove quotes this way, (if use async fixtures):

You can also set it in your pytest.ini to silence warnings, but agreed would be nice to properly default without warning.

[pytest]
asyncio_mode=auto
asyncio_default_fixture_loop_scope="function"
 asyncio_default_fixture_loop_scope=function

Any chance the verbose log can use correct parameter name:
configfile: pytest.ini
plugins: asyncio-0.24.0, cov-6.0.0, mock-3.14.0
asyncio: mode=Mode.AUTO, default_loop_scope=module

loop scope set by param:
asyncio_default_fixture_loop_scope = module

@MarkusBiggus Good catch!

I'd absolutely support a PR addressing the wrong param name in the log alongside a small remark in the changelog.

@rysev-a My understanding is that you're proposing that pytest-asyncio removes extraneous quotes from the config param asyncio_default_fixture_loop_scope. To be honest, I don't really support this idea, because all the config parsing happens in pytest, not pytest-asyncio. I think pytest-asyncio should mess as little as possible with the default pytest functionalities.

Adding this to my pyproject.toml removed the warning for me:

[tool.pytest.ini_options]
# ... other options here
filterwarnings = [
    "ignore:Unused async fixture loop scope:pytest.PytestWarning"
]

I could not get the warning to go away by adding the asyncio_default_fixture_loop_scope to either my pyproject.toml or a new pytest.ini file. So this'll have to do for now.

I tried setting the scope in conftest using:

def pytest_configure(config):
    config.addinivalue_line("asyncio_default_fixture_loop_scope", "function")

But I get an assert isinstance(x, list) error, like pytest-dev/pytest#12174. Am I doing this wrong?

tony commented

There seems to be nothing the suppresses this warnings. I've tried everything mentioned in the thread.

  • Python: 3.13.2
  • pytest-asyncio: 0.25.3

Can anyone claim a successful way to suppress this warning? (either via filterwarnings or via setting options themselves).

I tried setting the scope in conftest using:

def pytest_configure(config):
    config.addinivalue_line("asyncio_default_fixture_loop_scope", "function")

But I get an assert isinstance(x, list) error, like pytest-dev/pytest#12174. Am I doing this wrong?

@jataca The idea is to set asyncio_default_fixture_loop_scope = "function" in the [tool.pytest.ini_options] section of pyproject.toml. Your approach should work though.

I think we should simply follow through with the deprecation and set the default value to "function", instead of trying to hunt down bugs related to warnings.