Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions src/pytest_codspeed/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import json
import os
import random
import sys
from dataclasses import dataclass, field
from pathlib import Path
from time import time
Expand Down Expand Up @@ -37,6 +38,39 @@
T = TypeVar("T")
P = ParamSpec("P")

# Mirrors `requires-python` and the `Programming Language :: Python` classifiers
# in pyproject.toml, and the CI test matrix. Bumping a version means updating all
# three. Outside this range nothing is validated, so measurements may be wrong.
MIN_SUPPORTED_PYTHON_VERSION = (3, 9)
MAX_SUPPORTED_PYTHON_VERSION = (3, 15)


def get_python_version_warning(version: tuple[int, int]) -> str | None:
"""Warn when the interpreter is outside the supported version range.

GitHub Actions renders a workflow command as an annotation shown outside the
job log, which requires it to start a line. Elsewhere the line is only
coloured, since no annotation mechanism exists.
"""
if MIN_SUPPORTED_PYTHON_VERSION <= version <= MAX_SUPPORTED_PYTHON_VERSION:
return None

supported = " to ".join(
f"{major}.{minor}"
for major, minor in (
MIN_SUPPORTED_PYTHON_VERSION,
MAX_SUPPORTED_PYTHON_VERSION,
)
)
message = (
f"Python {version[0]}.{version[1]} is not officially supported by "
f"pytest-codspeed (supported: {supported}). Support is experimental and "
"untested, measurements may be unreliable."
)
if os.environ.get("GITHUB_ACTIONS") == "true":
return f"::warning title=Unsupported Python version::{message}"
return f"\033[93m{message}\033[0m"


@pytest.hookimpl(trylast=True)
def pytest_addoption(parser: pytest.Parser):
Expand Down Expand Up @@ -185,12 +219,14 @@ def pytest_plugin_registered(plugin, manager: pytest.PytestPluginManager):
def pytest_report_header(config: pytest.Config):
plugin = get_plugin(config)
config_str, warns = plugin.instrument.get_instrument_config_str_and_warns()
python_version_warning = get_python_version_warning(sys.version_info[:2])
out = [
(
f"codspeed: {__version__} ("
f"{'enabled' if plugin.is_codspeed_enabled else 'disabled'}, {config_str}"
")"
),
*([python_version_warning] if python_version_warning else []),
*warns,
]
if len(plugin.disabled_plugins) > 0:
Expand Down
38 changes: 38 additions & 0 deletions tests/test_pytest_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
skip_without_valgrind,
)

from pytest_codspeed import plugin


@pytest.mark.parametrize("mode", [*MeasurementMode])
def test_plugin_enabled_with_kwargs(
Expand Down Expand Up @@ -38,6 +40,42 @@ def test_bench_enabled_header_without_perf(
)


@pytest.mark.parametrize("version", [(3, 9), (3, 14), (3, 15)])
def test_supported_python_version_does_not_warn(version) -> None:
assert plugin.get_python_version_warning(version) is None


@pytest.mark.parametrize("version", [(3, 8), (3, 16), (4, 0)])
def test_unsupported_python_version_warns(version, monkeypatch) -> None:
monkeypatch.delenv("GITHUB_ACTIONS", raising=False)
assert plugin.get_python_version_warning(version) == (
f"\033[93mPython {version[0]}.{version[1]} is not officially supported by "
"pytest-codspeed (supported: 3.9 to 3.15). Support is experimental and "
"untested, measurements may be unreliable.\033[0m"
)


def test_unsupported_python_version_warns_as_annotation(monkeypatch) -> None:
monkeypatch.setenv("GITHUB_ACTIONS", "true")
warning = plugin.get_python_version_warning((3, 16))
assert warning is not None
assert warning.startswith("::warning title=Unsupported Python version::Python 3.16")


def test_python_version_warning_is_reported_in_header(
pytester: pytest.Pytester, monkeypatch
) -> None:
# Faking `sys.version_info` would break pytest's own version-gated
# introspection, so the lookup is stubbed instead. The line has to start the
# output line for GitHub Actions to pick the annotation up.
monkeypatch.setattr(
plugin, "get_python_version_warning", lambda version: "::warning ::too new"
)
pytester.copy_example("tests/examples/test_addition_fixture.py")
result = pytester.runpytest("--codspeed")
result.stdout.fnmatch_lines(["::warning ::too new"])


@skip_without_valgrind
def test_plugin_enabled_by_env(pytester: pytest.Pytester, codspeed_env) -> None:
pytester.copy_example("tests/examples/test_addition_fixture.py")
Expand Down
Loading