-
Notifications
You must be signed in to change notification settings - Fork 149
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
More difficulties in 0.23 with async fixtures #718
Comments
I forgot to include the pytest.ini: [pytest]
asyncio_mode = auto |
pytest-asyncio 0.23 changed the way event loops work and caused a number of issues, including pytest-dev/asyncio#705 and pytest-dev/pytest-asyncio#718. I wasn't able to find an easy way around these for the qualification test, so for now I've pinned pytest-asyncio to <0.22 (the 0.22 release was a half-way point to 0.23 and has been yanked).
Thanks for the reproducer. Good call for opening a separate issue! |
Let me know if this isn't the best place to post this, but I have a gut feeling it's very closely related to the sloppiness of fixture scopes and event loop initialization. I was busting my head all day trying to refactor my code to get rid of the For anyone who happens upon this, the workaround for now is as follows: # This allows all fixtures to access the session-scoped event loop:
@pytest_asyncio.fixture(scope="session")
async def global_event_loop():
return asyncio.get_running_loop()
# The function of the async fixture you want to create, but without the marker:
async def clear_db():
async with Transaction() as tx:
await tx.execute(sql.text("DELETE FROM some_table"))
# Use this non-async fixture to mimic the behavior of the async fixture you need.
# Run the async code using global_event_loop.
@pytest.fixture(autouse=True, scope="function")
def clear_db_wrapper(global_event_loop):
yield
global_event_loop.run_until_complete(clear_db()) (Edit: Fixed and clarified code.) |
@nzeid I'm sorry that you had to go through so much trouble. Pytest-asyncio v0.23 supports running tests in different event loops. There's a loop for each level of the test suite (session, package, module, class, function). However, pytest-asyncio wrongly assumes that the scope of an async fixture is the same as the scope of the event loop in which the fixture runs. This essentially makes it impossible to have a session-scoped fixture that runs in the same loop as a function-scoped test. See #706. |
I'm experiencing the similar issue, which I believe is another manifestation of #706. Namely, the error message |
@seifertm Hi, I'm trying to use the async fixtures at @pytest_asyncio.fixture(scope="class")
async def grpc_server() -> AsyncGenerator[(grpc.aio.Server, int), None]:
global _KEYSTORE, _API_KEY_MANAGER_SERVICER
server = grpc.aio.server()
# add service
port = server.add_insecure_port("[::]:0")
await server.start()
yield (server, port)
await server.stop(None)
# more clean up actions
@pytest_asyncio.fixture(scope="class")
async def grpc_stub(
grpc_server: tuple[grpc.aio.Server, int],
):
server, port = grpc_server
channel = grpc.aio.insecure_channel(f"localhost:{port}")
await channel.channel_ready()
stub = api_key_manager_pb2_grpc.ApiKeyManagerStub(channel)
yield stub
await channel.close()
class TestExportServiceConfig: # non async test class
_expected_json_str = """{
"free_tier_daily_usage_limit": 1,
"premium_tier_daily_usage_limit": -1
}"""
@pytest.fixture()
def json_path(self, tmp_path: pathlib.Path) -> str:
return tmp_path / "service-config.json"
def test_export_success(self, json_path: pathlib.Path):
with does_not_raise():
_API_KEY_MANAGER_SERVICER.export_service_config_to_json(json_path)
with open(json_path, "r") as file:
assert file.read() == self._expected_json_str
def test_export_fail(self, tmp_path: pathlib.Path):
with pytest.raises(OSError):
_API_KEY_MANAGER_SERVICER.export_service_config_to_json(
tmp_path / "nonexistent" / "file.json"
)
@pytest.mark.asyncio(scope="class")
class TestGetServiceConfig: # async test class
async def test_get_service_config(
self, grpc_stub: api_key_manager_pb2_grpc.ApiKeyManagerStub
):
expected_response = api_key_manager_pb2.GetServiceConfigResponse(
config=api_key_manager_pb2.ApiKeyManagerServiceConfig(
free_tier_daily_usage_limit=_TEST_API_KEY_MANAGER_SERVICE_CONFIG[
"free_tier_daily_usage_limit"
],
premium_tier_daily_usage_limit=_TEST_API_KEY_MANAGER_SERVICE_CONFIG[
"premium_tier_daily_usage_limit"
],
)
)
call: grpc.aio.Call = grpc_stub.GetServiceConfig(empty_pb2.Empty())
response = await call
assert response == expected_response
assert await call.code() == grpc.StatusCode.OK
... It works when I run pytest with just one class/specify a class to test, but when I run it as a module where there're multiple classes, async or not, I get the error of I'm not entirely sure if my issue is related to this thread but it seems to be the case. Tried downgrade to 0.21 directly without changing the code but that doesn't work for me. I guess the Is there any workaround I can implement to make my use case workable? Right now I can run the test on individual class but it makes it not possible for the test module to pass the CI/CD pipeline I've setup. Thanks! Edit: Had a quick look through on the open issues and seems like my issue if also related to #829. Will come back and see if it works after I tried putting those class scope fixtures in the classes. |
Ended up commented out the |
The issue title is very general. I think different issues have come in the discussion:
Given the generic nature of this issue and the fact that all comments are already tracked or accounted for, I'll close this issue. |
pytest-asyncio 0.22, 0.23 and 0.24 are broken in various ways, cf. * pytest-dev/pytest-asyncio#670 * pytest-dev/pytest-asyncio#718 * pytest-dev/pytest-asyncio#587 Additionally, pytest 8 cannot work with pytest-asyncio 0.21, so we need to pin pytest to 7.x too. Perhaps there is a way to make this work, but pin it to earlier versions to do this deliberately at some point.
This might have the same underlying cause as #705 / #706, but I didn't see the specific error message I'm running into so I thought I should open a separate bug.
To reproduce:
conftest.py (copied verbatim from https://pytest-asyncio.readthedocs.io/en/latest/how-to-guides/run_session_tests_in_same_loop.html):
foo/__init__.py: empty
foo/test_foo.py:
Gives this error:
Python 3.10.12, pytest-asyncio 0.23.2.
The text was updated successfully, but these errors were encountered: