pytest-asyncio
pytest-asyncio copied to clipboard
0.19.0: Fixture misses attribute
I am working with python3.10
and pytest=7.1.2
version
It seems with the latest version of pytest-asyncio=0.19.0
a regression has been introduced and this bug is back.
Using the same example testcase to reproduce the issue, I observe the same failure
AttributeError: 'async_generator' object has no attribute 'put'
Downgrading pytest-asyncio to version 0.18.3
and the error is gone.
@viralmutant
pytest-asyncio=0.19.0
enforces strict mode. Projects must update their fixture markers as described.
Env: py38
+ purest=7.1.2
+pytest-asyncio=0.19.0
@viralmutant please show the output of the code when you run on python3.10 and pytest-asyncio==0.18.3
@viralmutant please show the output of the code when you run on python3.10 and pytest-asyncio==0.18.3
Here is the output with -s
plugins: forked-1.4.0, cov-3.0.0, asyncio-0.18.3, env-0.6.2, sugar-0.9.5, requests-mock-1.9.3, pyfakefs-4.5.0, xdist-2.5.0
asyncio: mode=legacy
collecting ...
test_bug.py ✓
/CodeBox/turbo-test/lib/python3.10/site-packages/pytest_asyncio/plugin.py:191: DeprecationWarning: The 'asyncio_mode' default value will change to 'strict' in future, please explicitly use 'asyncio_mode=strict' or 'asyncio_mode=auto' in pytest configuration file.
config.issue_config_time_warning(LEGACY_MODE, stacklevel=2)
test_bug.py:12
/CodeBox/turbo/test_bug.py:12: DeprecationWarning: "@coroutine" decorator is deprecated since Python 3.8, use "async def" instead
def release(self):
../turbo-test/lib/python3.10/site-packages/pytest_asyncio/plugin.py:230
.../CodeBox/turbo-test/lib/python3.10/site-packages/pytest_asyncio/plugin.py:230: DeprecationWarning: '@pytest.fixture' is applied to <fixture fake_session, file=/CodeBox/turbo/test_bug.py, line=16> in 'legacy' mode, please replace it with '@pytest_asyncio.fixture' as a preparation for switching to 'strict' mode (or use 'auto' mode to seamlessly handle all these fixtures as asyncio-driven).
warnings.warn(
test_bug.py::test_bug
/CodeBox/turbo/test_bug.py:20: DeprecationWarning: "@coroutine" decorator is deprecated since Python 3.8, use "async def" instead
def _fake_request(method, url, *args, **kwargs):
-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
Results (0.84s):
1 passed
Your test output shows a deprecation warning about asyncio-mode. Starting with pytest-asyncio v0.19 asyncio mode defaults to strict. The error you're seeing is likely caused by wrong fixture decorators. The reproducible example you linked suffers from that exact problem.
As @davidandreoletti already mentioned you need to update your fixture definitions if you intend to use strict mode. Strict mode will no longer evaluate async fixtures decorated witih @pytest.fixture
. You need to use @pytest_asyncio.fixture
.
@viralmutant Please check your fixture definitions and let us know if this fixes your issue.
Yes, after replacing the fixture as @pytest_asyncio.fixture
I didn't observe the issue with v0.19
Thanks
The confusing part is that once the version has been upgraded, it just fails and won't tell why. And while being at the earlier version, when a couple of thousand tests are running in CI pipeline, the warnings are ignored
Hi @seifertm
I am trying to use async_generator with fixture in factory mode:
@pytest_asyncio.fixture
async def async_client_factory():
async def _factory(x):
print(x) # just to simplify case, we need x as parameter for our factory
async with AsyncClient() as ac:
yield ac
return _factory
@pytest.mark.asyncio
async def test_async_generator_factory(async_client_factory):
async_client = async_client_factory(1)
response = await async_client.get("/test")
assert response.status_code == status.HTTP_200_OK
But I get same error:
> response = await async_client.get("/test")
E AttributeError: 'async_generator' object has no attribute 'get'
package versions:
pytest-asyncio = "0.19.0"
httpx = "^0.23.0"
pytest = "^7.1.2"
just a fixture w/o factory works well
@pytest_asyncio.fixture
async def async_client():
async with AsyncClient() as ac:
yield ac
@pytest.mark.asyncio
async def test_async_generator(async_client):
response = await async_client.get("/test")
assert response.status_code == status.HTTP_200_OK
Interesting, thanks for the report. I'll look into it tomorrow.
You can't use a yield inside an httpx.AsyncClient like that, you need the @contextlib.asynccontextmanager
See https://discuss.python.org/t/preventing-yield-inside-certain-context-managers/1091
@graingert it does not help in case with factory
@pytest_asyncio.fixture
async def async_client_factory():
@asynccontextmanager
async def _factory(x):
print(x) # just to simplify case, we need x as parameter for our factory
async with AsyncClient() as ac:
yield ac
return _factory
@pytest.mark.asyncio
async def test_async_generator_factory(async_client_factory):
async_client = async_client_factory(1)
response = await async_client.get("/test")
assert response.status_code == status.HTTP_200_OK
similar error
> response = await async_client.get("/test")
E AttributeError: '_AsyncGeneratorContextManager' object has no attribute 'get'
although it works with fixture w/o factory
@asynccontextmanager
@pytest_asyncio.fixture
async def async_client():
async with AsyncClient() as ac:
yield ac
@pytest.mark.asyncio
async def test_async_generator(async_client):
response = await async_client.get("/test")
assert response.status_code == status.HTTP_200_OK
seems something related to pytest internals
You need to use
async with async_client_factory(1) as async_client:
...
@kiddick I cannot see anything wrong with the behaviour of pytest-asyncio in the example you provided here. Your fixture returns the _factory
async context manager. As Thomas mentioned you need to open the context manager using asnyc with …
, in order to access the yielded AsyncClient
.
From your reaction on the previous comment I assume the issue is resolved for you.
I initially left this issue open, because I intended to add a warning when people forgot to update their fixtures. I don't think there's a reliable way to do it, though, so I'll close this issue.