Closed oliverdain closed 1 week ago
I have the same issue with 0.24.0
. Downgrading to 0.23.8
helped to "solve" the problem.
Thanks for the report, I'll look into this!
Hmm, pytest v8.3.2 explicitly exports the FixtureDef symbol. That means the import statement in pytest-asyncio should be fine. Pytest-asyncio also performs type checking with mypy, which doesn't show any issues.
Do you have any idea what else could break the import? Anything specific to your respective projects?
Is there a chance you can reduce the code to a minimal reproducible example?
@oliverdain @ZivotJeKrasny Do you happen to have any additional information on this?
@oliverdain @ZivotJeKrasny Do you happen to have any additional information on this?
I'm afraid not. When it happened I pinned the version of pytest-asyncio to pytest-asyncio==0.23.8
to avoid the issue and that's about all the debugging I've done.
@seifertm the plugin's metadata still sets pytest 8.2 as the minimum version. I can imagine a situation where resolvelib (the dependency resolver in pip install
) would backtrack and decide to downgrade the version to an incompatible one, because of some other package in their env, but people would still think it's new. After all, nobody provided any proof of what versions their pip list
shows.
Hit this error today on a fresh build off a mamba
environment solve.
Have the environment:
$ conda list | grep pytest
pytest 7.4.4 pypi_0 pypi
pytest-asyncio 0.24.0 pyhd8ed1ab_0 conda-forge
pytest-cov 5.0.0 pyhd8ed1ab_0 conda-forge
pytest-mock 3.14.0 pypi_0 pypi
@eric-tramel this project does not package things for conda (somebody else, external, does), and you seem to have a mixed environment. If this can't be reproduced with a pip install
, I'd suggest asking the downstream packagers for help.
Also, could you post the header of your pytest
invocation output (and the command itself)? It contains the versions of Python, pytest, and all the loaded plugins. This is to make sure that it's not loading something else from some other place on your system.
pytest 7.4.4 pypi_0 pypi
Oh, wait. It's v7. I misread it and thought it was v8. Of course, that version is incompatible. https://github.com/pytest-dev/pytest-asyncio/blob/v0.24.0/setup.cfg#L44 specifically says pytest >= 8.2
, which means that it's on the side of the conda ecosystem.
So https://github.com/pytest-dev/pytest/blob/8.0.0rc1/src/pytest/__init__.py#L25 is the first version that introduced this import. This means that the metadata in pytest-asyncio
is correct. The CI being green also demonstrates that it's fine.
We now know that @eric-tramel's environment is broken, and for all we know the envs of @oliverdain and @ZivotJeKrasny might be just as corrupted.
@seifertm I'd suggest closing this one as all the info provided so far is pointing to PEBCAK.
I've also been running into this issue consistently ever since updating to pytest-asyncio 0.24.0. Downgrading to 0.23.8 has fixed it for now.
Unless you can demonstrate it in a non-broken env, there's nothing for the upstream to do 🤷‍♂️
@webknjaz Here's a reproduction of the issue which, if installed in a fresh devcontainer, barfs with this issue.
The following should reproduce the error easily, if not you may need to use an exact pyproject.toml and corresponding poetry.lock file which I've included below for completeness' sake.
poetry lock
poetry install
(installs happily so far with no conflicts announced)mkdir tests
pytest tests
(results in the error below)0.23.8
then repeating the above does fix the issue[tool.poetry]
name = "server"
[tool.poetry.dependencies]
python = ">3.10,<3.11"
Django = "^4.0"
# abridged for brevity
[tool.poetry.group.dev.dependencies]
# abridged for brevity
pytest-sugar = "^1.0.0" # https://pivotfinland.com/pytest-sugar/
pytest-django = "^4.9.0" # https://github.com/pytest-dev/pytest-django
pytest-asyncio = "^0.24.0" # https://github.com/pytest-dev/pytest-asyncio
pytest-cov = "^5"
pytest = "^8.3.3"
pytest-subtests = "^0.13.0"
pytest-factoryboy = "^2.7.0"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
Traceback (most recent call last):
File "/usr/local/py-utils/bin/pytest", line 8, in <module>
sys.exit(console_main())
File "/usr/local/py-utils/venvs/pytest/lib/python3.10/site-packages/_pytest/config/__init__.py", line 189, in console_main
code = main()
File "/usr/local/py-utils/venvs/pytest/lib/python3.10/site-packages/_pytest/config/__init__.py", line 147, in main
config = _prepareconfig(args, plugins)
File "/usr/local/py-utils/venvs/pytest/lib/python3.10/site-packages/_pytest/config/__init__.py", line 328, in _prepareconfig
config = pluginmanager.hook.pytest_cmdline_parse(
File "/usr/local/py-utils/venvs/pytest/lib/python3.10/site-packages/pluggy/_hooks.py", line 265, in __call__
return self._hookexec(self.name, self.get_hookimpls(), kwargs, firstresult)
File "/usr/local/py-utils/venvs/pytest/lib/python3.10/site-packages/pluggy/_manager.py", line 80, in _hookexec
return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
File "/usr/local/py-utils/venvs/pytest/lib/python3.10/site-packages/pluggy/_callers.py", line 55, in _multicall
gen.send(outcome)
File "/usr/local/py-utils/venvs/pytest/lib/python3.10/site-packages/_pytest/helpconfig.py", line 103, in pytest_cmdline_parse
config: Config = outcome.get_result()
File "/usr/local/py-utils/venvs/pytest/lib/python3.10/site-packages/pluggy/_result.py", line 60, in get_result
raise ex[1].with_traceback(ex[2])
File "/usr/local/py-utils/venvs/pytest/lib/python3.10/site-packages/pluggy/_callers.py", line 39, in _multicall
res = hook_impl.function(*args)
File "/usr/local/py-utils/venvs/pytest/lib/python3.10/site-packages/_pytest/config/__init__.py", line 1067, in pytest_cmdline_parse
self.parse(args)
File "/usr/local/py-utils/venvs/pytest/lib/python3.10/site-packages/_pytest/config/__init__.py", line 1354, in parse
self._preparse(args, addopts=addopts)
File "/usr/local/py-utils/venvs/pytest/lib/python3.10/site-packages/_pytest/config/__init__.py", line 1237, in _preparse
self.pluginmanager.load_setuptools_entrypoints("pytest11")
File "/usr/local/py-utils/venvs/pytest/lib/python3.10/site-packages/pluggy/_manager.py", line 287, in load_setuptools_entrypoints
plugin = ep.load()
File "/usr/local/lib/python3.10/importlib/metadata/__init__.py", line 171, in load
module = import_module(match.group('module'))
File "/usr/local/lib/python3.10/importlib/__init__.py", line 126, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
File "<frozen importlib._bootstrap>", line 1050, in _gcd_import
File "<frozen importlib._bootstrap>", line 1027, in _find_and_load
File "<frozen importlib._bootstrap>", line 992, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
File "<frozen importlib._bootstrap>", line 1050, in _gcd_import
File "<frozen importlib._bootstrap>", line 1027, in _find_and_load
File "<frozen importlib._bootstrap>", line 1006, in _find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 688, in _load_unlocked
File "/usr/local/py-utils/venvs/pytest/lib/python3.10/site-packages/_pytest/assertion/rewrite.py", line 172, in exec_module
exec(co, module.__dict__)
File "/usr/local/lib/python3.10/site-packages/pytest_asyncio/__init__.py", line 4, in <module>
from .plugin import fixture, is_async_test
File "/usr/local/py-utils/venvs/pytest/lib/python3.10/site-packages/_pytest/assertion/rewrite.py", line 172, in exec_module
exec(co, module.__dict__)
File "/usr/local/lib/python3.10/site-packages/pytest_asyncio/plugin.py", line 35, in <module>
from pytest import (
ImportError: cannot import name 'FixtureDef' from 'pytest' (/usr/local/py-utils/venvs/pytest/lib/python3.10/site-packages/pytest/__init__.py)
I had to add the fields authors, description, and version to pyproject.toml for poetry lock
to run. OTher than that, I haven't modified the example. Still, I cannot reproduce the issue in a fresh Python3.10 venv on my machine (see below).
@thclark You mentioned a devcontainer. Can you share the container image you're using? This could help me reproduce the error.
Edit: I also tried the pyproject.toml and poetry.lock from the "exact reproduction" section of your comment, but the result is the same.
Commands:
python3.10 -m venv venv
. venv/bin/activate
cat <<"EOF" >pyproject.toml
[tool.poetry]
name = "server"
authors = []
description = ""
version = "0.1.0"
[tool.poetry.dependencies]
python = ">3.10,<3.11"
Django = "^4.0"
# and other stuff
[tool.poetry.group.dev.dependencies]
codecov = "^2.1.10"
coverage = "^7.6.3" # https://github.com/nedbat/coveragepy
django-coverage-plugin = "^3.1.0" # https://github.com/nedbat/django_coverage_plugin
django-debug-toolbar = "4.4.6" # https://github.com/jazzband/django-debug-toolbar
factory-boy = "^3.3.0"
pytest-sugar = "^1.0.0" # https://pivotfinland.com/pytest-sugar/
pytest-django = "^4.9.0" # https://github.com/pytest-dev/pytest-django
pytest-asyncio = "^0.24.0" # https://github.com/pytest-dev/pytest-asyncio
pytest-cov = "^5"
pytest = "^8.3.3"
pytest-subtests = "^0.13.0"
pytest-factoryboy = "^2.7.0"
ruff = "^0.7.0"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
EOF
poetry lock
poetry install
mkdir tests
pytest tests
Output:
Updating dependencies
Resolving dependencies... (1.2s)
Writing lock file
Installing dependencies from lock file
Package operations: 33 installs, 0 updates, 0 removals
- Installing six (1.16.0)
- Installing python-dateutil (2.9.0.post0)
- Installing typing-extensions (4.12.2)
- Installing asgiref (3.8.1)
- Installing certifi (2024.8.30)
- Installing charset-normalizer (3.4.0)
- Installing exceptiongroup (1.2.2)
- Installing faker (30.6.0)
- Installing idna (3.10)
- Installing iniconfig (2.0.0)
- Installing packaging (24.1)
- Installing pluggy (1.5.0)
- Installing sqlparse (0.5.1)
- Installing tomli (2.0.2)
- Installing urllib3 (2.2.3)
- Installing attrs (24.2.0)
- Installing coverage (7.6.3)
- Installing django (4.2.16)
- Installing factory-boy (3.3.1)
- Installing inflection (0.5.1)
- Installing pytest (8.3.3)
- Installing requests (2.32.3)
- Installing termcolor (2.5.0)
- Installing codecov (2.1.13)
- Installing django-coverage-plugin (3.1.0)
- Installing django-debug-toolbar (4.4.6)
- Installing pytest-asyncio (0.24.0)
- Installing pytest-cov (5.0.0)
- Installing pytest-django (4.9.0)
- Installing pytest-factoryboy (2.7.0)
- Installing pytest-subtests (0.13.1)
- Installing pytest-sugar (1.0.0)
- Installing ruff (0.7.0)
Installing the current project: server (0.1.0)
Warning: The current project could not be installed: No file/folder found for package server
If you do not want to install the current project use --no-root.
If you want to use Poetry only for dependency management but not for packaging, you can disable package mode by setting package-mode = false in your pyproject.toml file.
In a future version of Poetry this warning will become an error!
/tmp/tst2/venv/lib/python3.10/site-packages/pytest_asyncio/plugin.py:208: PytestDeprecationWarning: The configuration option "asyncio_default_fixture_loop_scope" is unset.
The event loop scope for asynchronous fixtures will default to the fixture caching scope. Future versions of pytest-asyncio will default the loop scope for asynchronous fixtures to function scope. Set the default fixture loop scope explicitly in order to avoid unexpected behavior in the future. Valid fixture loop scopes are: "function", "class", "module", "package", "session"
warnings.warn(PytestDeprecationWarning(_DEFAULT_FIXTURE_LOOP_SCOPE_UNSET))
Test session starts (platform: linux, Python 3.10.15, pytest 8.3.3, pytest-sugar 1.0.0)
django: version: 4.2.16
rootdir: /tmp/tst2
configfile: pyproject.toml
plugins: Faker-30.6.0, subtests-0.13.1, sugar-1.0.0, asyncio-0.24.0, factoryboy-2.7.0, cov-5.0.0, django-4.9.0
asyncio: mode=strict, default_loop_scope=None
collected 0 items
Results (0.02s):
@thclark could you make the reproducer smaller with as few dependencies as possible? It's best to have bare minimum: https://sscce.org.
Also, I must note that poetry's “lock file” is not actually a lock file but a bunch of possible resolutions that are getting resolved again on install with a number of quirks. It's been brought up numerous times in PEP 751 discussions. Brett decided to name this method “package locking” (requiring double resolution) as opposed to “per-file locking” (this is when installers can skip resolving and just iterate the list of things). It also uses a different dependency resolver mixology, which implements PubGrub (uv uses this algorithm too), unlike pip's resolvelib. To achieve their “lock files”, Poetry makes a number of assumptions that are very often true except for the corner cases that just can't work like that.
This is all to say that you may be experiencing bugs in your tooling (be it Poetry or Conda). To prove that a problem might be related to pytest-asyncio
, it would be best to avoid examples that go beyond pip install
. Otherwise, I haven't seen any convincing evidence of this so far.
File "/usr/local/py-utils/venvs/pytest/lib/python3.10/site-packages/_pytest/assertion/rewrite.py", line 172, in exec_module exec(co, module.__dict__) File "/usr/local/lib/python3.10/site-packages/pytest_asyncio/__init__.py", line 4, in <module> from .plugin import fixture, is_async_test File "/usr/local/py-utils/venvs/pytest/lib/python3.10/site-packages/_pytest/assertion/rewrite.py", line 172, in exec_module exec(co, module.__dict__) File "/usr/local/lib/python3.10/site-packages/pytest_asyncio/plugin.py", line 35, in <module>
Oh… I think I see what's happening here. If you look closer to the file paths, you'll noticed that pytest
executed from a virtualenv (/usr/local/py-utils/venvs/pytest
) but pytest-asyncio
is not coming from that environment. Instead, it's coming from a global location @ /usr/local/lib/python3.10/site-packages
.
@thclark I think that @seifertm is right, and your problem might be coming from your environment — the devcontainer one. Perhaps, you broke it or maybe, you're misusing some tooling, or it might be a $PYTHONPATH
prioritizing a different location. Or poetry
doesn't actually install it into the venv in some cases. I'm not sure. But I think that if you use python -Im venv some-venv && some-venv/bin/python -Im pip install pytest-asyncio && some-venv/bin/python -Im pytest
, it'll likely just work.
@thclark try running these commands (separately, don't reuse REPL!):
/usr/local/py-utils/venvs/pytest/bin/python -c 'import pytest_asyncio; print(f"{pytest_asyncio.__version__=} {pytest_asyncio.__file__=}")'
/usr/local/py-utils/venvs/pytest/bin/python -c 'import importlib.metadata; pytest_asyncio = importlib.metadata.import_module("pytest_asyncio"); print(f"{pytest_asyncio.__version__=} {pytest_asyncio.__file__=}")'
Just to be on the safe side, I also double-checked that the metadata in the dists uploaded to PyPI is correct (and both have Requires-Dist: pytest<9,>=8.2
set):
I just upgraded from 0.23.8 to 0.24.0 and my tests are now failing with:
This is with pytest 8.3.2. I'm not sure if there's just a but in pytest-asyncio or if it needs to specify that it requires a newer version of pytest.