mirror of
https://github.com/cockpit-project/cockpit.git
synced 2026-11-03 21:49:50 -09:00
tox: eighty-six 3.6
When we landedd53b3e8400("pytests: update fixtures to not use deprecated event_loop") we promised ourselves "this is the last time". The intensity of hacks required to keep up with being able to run the same set of tests under modern Python versions and a long-dead version which isn't getting any new releases of pytest or its plugins is just too much. And here we are again. Sincefb319e4f43("Pin pytest-asyncio version to unbreak main") we've been avoiding taking a new pytest-asyncio because it breaks our fragile pile of hacks yet again. It's time. Drop support for running the pytest unit tests tests under Python 3.6 and remove the massive pile of hacks we had for that. Without any further effort this has already unbroken the latest pytest-asyncio version, so we can unpin it. We still have full coverage of the bridge code on Python 3.6 via integration testing (beiboot vs container-ws on RHEL 8) and the fact that we're not as actively hacking on the bridge code anymore means that any time that was once saved by getting "quick fails" from unit tests is no longer worth the large and growing amount of effort required to keep those tests from constantly breaking.
This commit is contained in:
parent
289b449e3a
commit
5acce3c6da
2 changed files with 31 additions and 60 deletions
|
|
@ -213,7 +213,7 @@ envlist = lint,pytest
|
|||
isolated_build = True
|
||||
requires = virtualenv<20.22.0
|
||||
labels =
|
||||
venv = py3{6,9,10,11,12,13}-pytest
|
||||
venv = py3{9,10,11,12,13}-pytest
|
||||
|
||||
# The default test environments use system packages and never PyPI.
|
||||
[testenv:{lint,pytest}]
|
||||
|
|
@ -221,7 +221,7 @@ sitepackages = True
|
|||
install_command = python3 -m pip install --no-index --no-build-isolation {opts} {packages}
|
||||
wheel_build_env = pkg
|
||||
|
||||
# All other environments (names like py311-lint, py36-pytest, etc) are isolated
|
||||
# All other environments (names like py311-lint, py39-pytest, etc) are isolated
|
||||
# from the system and get their packages from PyPI, according to the specific
|
||||
# test environment being requested. We build the wheel in a common environment.
|
||||
# These tests will not run the PCP tests as there is no wheel available.
|
||||
|
|
@ -235,7 +235,7 @@ deps =
|
|||
lint: ruff
|
||||
lint: vulture
|
||||
pytest
|
||||
pytest-asyncio < 1.1.1
|
||||
pytest-asyncio
|
||||
pytest: pytest-cov
|
||||
pytest: pytest-timeout
|
||||
pytest: pytest-xdist
|
||||
|
|
@ -243,6 +243,6 @@ allowlist_externals = test/common/static-code
|
|||
setenv =
|
||||
NO_QUNIT=1
|
||||
commands =
|
||||
pytest: python3 -m pytest -opythonpath= {posargs}
|
||||
pytest: python3 -m pytest -opythonpath=test/common {posargs}
|
||||
lint: test/common/static-code --tap
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -1,32 +1,16 @@
|
|||
import asyncio
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from typing import Any, AsyncGenerator, Iterable
|
||||
from importlib.metadata import version
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING, AsyncGenerator
|
||||
|
||||
import pytest
|
||||
import pytest_asyncio
|
||||
from packaging.specifiers import SpecifierSet
|
||||
|
||||
from cockpit import polyfills
|
||||
from cockpit._vendor.systemd_ctypes import EventLoopPolicy
|
||||
|
||||
polyfills.install()
|
||||
|
||||
try:
|
||||
import pytest_asyncio._version
|
||||
have_event_loop_policy_fixture = pytest_asyncio._version.__version_tuple__ >= (0, 22)
|
||||
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
|
||||
|
|
@ -42,66 +26,53 @@ def any_subprocesses() -> bool:
|
|||
# - even sourcing this will break old python versions in ways we don't care to fix
|
||||
# - even sourcing this requires aiohttp, which isn't available everywhere
|
||||
# - there are general issues running under tox which need investigation
|
||||
# - this also requires a bots/ checkout which we don't have in distros
|
||||
if 'NO_QUNIT' in os.environ:
|
||||
@pytest.hookimpl
|
||||
def pytest_ignore_collect(path) -> 'bool | None':
|
||||
return path.basename == 'test_browser.py' or None
|
||||
def pytest_ignore_collect(collection_path: Path) -> bool:
|
||||
return collection_path.name == "test_browser.py"
|
||||
|
||||
# if m.image in ['ubuntu-2204', '🙃']:
|
||||
if not TYPE_CHECKING and version("pytest") not in SpecifierSet(">=7.0"):
|
||||
@pytest.hookimpl
|
||||
def pytest_ignore_collect(path: object) -> bool:
|
||||
return Path(str(path)).name == "test_browser.py"
|
||||
|
||||
|
||||
else:
|
||||
pytest_plugins = "js_coverage"
|
||||
|
||||
|
||||
if not have_event_loop_policy_fixture:
|
||||
@pytest.fixture
|
||||
def event_loop_policy() -> asyncio.AbstractEventLoopPolicy:
|
||||
return EventLoopPolicy()
|
||||
|
||||
|
||||
# if m.image in ['ubuntu-2204', 'ubuntu-2404', '🙃']:
|
||||
if version("pytest-asyncio") not in SpecifierSet(">=0.22"):
|
||||
# Compatibility with pre-`event_loop_policy` versions of pytest-asyncio
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def event_loop(monkeypatch) -> Iterable[asyncio.AbstractEventLoop]:
|
||||
loop = EventLoopPolicy().new_event_loop()
|
||||
|
||||
# Polyfills for Python 3.6
|
||||
if sys.version_info < (3, 7, 0):
|
||||
def all_tasks(loop=loop):
|
||||
return {t for t in asyncio.Task.all_tasks(loop=loop) if not t.done()}
|
||||
|
||||
monkeypatch.setattr(asyncio, 'get_running_loop', lambda: loop, raising=False)
|
||||
monkeypatch.setattr(asyncio, 'create_task', loop.create_task, raising=False)
|
||||
monkeypatch.setattr(asyncio, 'all_tasks', all_tasks, raising=False)
|
||||
|
||||
yield loop
|
||||
|
||||
loop.close()
|
||||
|
||||
else:
|
||||
@pytest.fixture
|
||||
def event_loop_policy() -> asyncio.AbstractEventLoopPolicy:
|
||||
return EventLoopPolicy()
|
||||
|
||||
|
||||
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}
|
||||
def event_loop() -> asyncio.AbstractEventLoop:
|
||||
return EventLoopPolicy().new_event_loop()
|
||||
|
||||
|
||||
@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]
|
||||
current_task = asyncio.current_task()
|
||||
assert current_task is not None
|
||||
event_loop = asyncio.get_running_loop()
|
||||
|
||||
# Let all tasks and subprocesses run to completion
|
||||
for _ in range(200):
|
||||
all_tasks = filter_current_task(asyncio.all_tasks(loop=event_loop), current_task)
|
||||
all_tasks = asyncio.all_tasks(loop=event_loop) - {current_task}
|
||||
if not (all_tasks or any_subprocesses()):
|
||||
break
|
||||
await asyncio.sleep(0.005)
|
||||
|
||||
# No tasks left
|
||||
assert filter_current_task(asyncio.all_tasks(loop=event_loop), current_task) == set()
|
||||
assert asyncio.all_tasks(loop=event_loop) == {current_task}
|
||||
|
||||
# No subprocesses left
|
||||
if any_subprocesses():
|
||||
|
|
|
|||
Loading…
Reference in a new issue