pytests: update fixtures to not use deprecated event_loop

pytest_asyncio dropped `event_loop` fixture in version 1.0.0
This commit is contained in:
tomasmatus 2025-05-28 10:35:45 +02:00 committed by Tomáš Matuš
parent fbdc5a89e7
commit d53b3e8400
7 changed files with 49 additions and 22 deletions

View file

@ -221,7 +221,7 @@ deps =
lint: ruff
lint: vulture
pytest
pytest-asyncio < 1.0
pytest-asyncio
pytest: pytest-cov
pytest: pytest-timeout
pytest: pytest-xdist

View file

@ -2,9 +2,10 @@ import asyncio
import os
import subprocess
import sys
from typing import Iterable
from typing import Any, AsyncGenerator, Iterable
import pytest
import pytest_asyncio
from cockpit import polyfills
from cockpit._vendor.systemd_ctypes import EventLoopPolicy
@ -18,6 +19,14 @@ except (ImportError, AttributeError):
# old versions don't have __version_tuple__ or event_loop_policy
have_event_loop_policy_fixture = False
# pytest fixture polyfill
# pytest_asyncio.fixture() was added in 0.22.0
if not hasattr(pytest_asyncio, 'fixture'):
# mypy is strict here because of slightly different signatures.
# On old versions of pytest_asyncio this is fine to do as `pytest.fixture`
# was always used before `pytest_asyncio.fixture` existed.
pytest_asyncio.fixture = pytest.fixture # type: ignore[assignment]
def any_subprocesses() -> bool:
# Make sure we don't leak subprocesses
@ -67,18 +76,32 @@ else:
return EventLoopPolicy()
@pytest.fixture(autouse=True)
def _check_settled(event_loop) -> Iterable[None]:
def filter_current_task(tasks: 'set[asyncio.Task[Any]]',
current_task: 'asyncio.Task[Any]') -> 'set[asyncio.Task[Any]]':
return {task for task in tasks if task is not current_task}
@pytest_asyncio.fixture(autouse=True)
async def _check_settled() -> AsyncGenerator[None, None]:
yield
# python 3.6 asyncio does not have 'current_task' attribute
if hasattr(asyncio, 'current_task'):
current_task = asyncio.current_task()
else:
current_task = asyncio.Task.current_task() # type: ignore[attr-defined]
assert current_task is not None
event_loop = asyncio.get_running_loop()
# Let all tasks and subprocesses run to completion
for _ in range(200):
if not (asyncio.all_tasks(event_loop) or any_subprocesses()):
all_tasks = filter_current_task(asyncio.all_tasks(loop=event_loop), current_task)
if not (all_tasks or any_subprocesses()):
break
event_loop.run_until_complete(asyncio.sleep(0.005))
await asyncio.sleep(0.005)
# No tasks left
assert asyncio.all_tasks(loop=event_loop) == set()
assert filter_current_task(asyncio.all_tasks(loop=event_loop), current_task) == set()
# No subprocesses left
if any_subprocesses():

View file

@ -92,13 +92,12 @@ class MockTransport(asyncio.Transport):
_, channel, data = data.split(b'\n', 2)
self.queue.put_nowait((channel.decode('ascii'), data))
def stop(self, event_loop: Optional[asyncio.AbstractEventLoop] = None) -> None:
async def stop(self) -> None:
keep_open = self.protocol.eof_received()
if keep_open:
assert event_loop is not None
self.close_future = event_loop.create_future()
self.close_future = asyncio.get_running_loop().create_future()
try:
event_loop.run_until_complete(self.close_future)
await self.close_future
finally:
self.close_future = None

View file

@ -14,9 +14,10 @@ import sys
import unittest.mock
from collections import deque
from pathlib import Path
from typing import Dict, Generator, Iterable, Iterator, Sequence, Union
from typing import AsyncGenerator, Dict, Generator, Iterator, Sequence, Union
import pytest
import pytest_asyncio
from cockpit._vendor.systemd_ctypes import bus
from cockpit.bridge import Bridge
@ -67,13 +68,13 @@ def add_pseudo(bridge: Bridge) -> None:
])
@pytest.fixture
def no_init_transport(event_loop: asyncio.AbstractEventLoop, bridge: Bridge) -> Iterable[MockTransport]:
@pytest_asyncio.fixture()
async def no_init_transport(bridge: Bridge) -> AsyncGenerator[MockTransport, None]:
transport = MockTransport(bridge)
try:
yield transport
finally:
transport.stop(event_loop)
await transport.stop()
@pytest.fixture

View file

@ -1,12 +1,12 @@
import argparse
import asyncio
import datetime
import json
import sys
import time
from typing import Iterable
from typing import AsyncGenerator
import pytest
import pytest_asyncio
# Skip import when PCP is not available (for example in our tox env without system packages)
try:
@ -31,14 +31,14 @@ from cockpit.bridge import Bridge
from .mocktransport import MockTransport
@pytest.fixture
def no_init_transport(event_loop: asyncio.AbstractEventLoop) -> Iterable[MockTransport]:
@pytest_asyncio.fixture()
async def no_init_transport() -> AsyncGenerator[MockTransport, None]:
bridge = Bridge(argparse.Namespace(privileged=False, beipack=False))
transport = MockTransport(bridge)
try:
yield transport
finally:
transport.stop(event_loop)
await transport.stop()
@pytest.fixture

View file

@ -4,6 +4,7 @@ import sys
import time
import pytest
import pytest_asyncio
from cockpit.channel import ChannelError
from cockpit.packages import BridgeConfig
@ -38,8 +39,9 @@ def bridge():
return Bridge()
@pytest.fixture
def transport(bridge):
# requires an event loop for queue in MockTransport
@pytest_asyncio.fixture()
async def transport(bridge): # noqa: RUF029
return MockTransport(bridge)

View file

@ -1,3 +1,5 @@
import pytest
pytest.Module._obj # type: ignore[attr-defined]
pytest.Module._check_settled # type: ignore[attr-defined]