from __future__ import annotations
import re
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
SUPPORTED = ("3.10", "3.11", "3.12", "3.13", "3.14")
MINIMUM = SUPPORTED[0]
CURRENT_STABLE = SUPPORTED[-1]
PRERELEASE = "3.15"
UNMARKED_DEPENDENCIES = (
"aiohttp>=3.14.3",
"httpx2>=2.7.0, =4.10.0, =1.40.0,={MINIMUM}"],
f"uv.lock requires-python is {uv_requires_python}, expected >={MINIMUM}",
)
classifiers = re.findall(r'"Programming Language :: Python :: (3\.\d+)"', pyproject)
require(tuple(classifiers) == SUPPORTED, f"Unexpected Python classifiers: {classifiers}")
require(f'pythonVersion = "{MINIMUM}"' in pyproject, "Pyright does not target the minimum Python")
require(f'target-version = "py{MINIMUM.replace(".", "")}"' in pyproject, "Ruff does not target the minimum Python")
require(python_version.startswith(f"{MINIMUM}."), f".python-version is not on Python {MINIMUM}: {python_version}")
require(f"Python {MINIMUM}+" in readme, "README introduction does not state the minimum Python")
require(f"Python {MINIMUM} or higher." in readme, "README requirements do not state the minimum Python")
require("PYTHON_VERSION_POLICY.md" in contributing, "CONTRIBUTING does not link the Python policy")
require(
f'# requires-python = ">={MINIMUM}"' in realtime_example,
"Realtime example metadata does not state the minimum Python",
)
pr_versions = matrix_versions(workflow_job(workflow, "test"))
expected_pr_versions = (MINIMUM, CURRENT_STABLE)
require(
pr_versions == expected_pr_versions,
f"PR test matrix is {pr_versions}, expected {expected_pr_versions}",
)
compatibility_job = workflow_job(workflow, "compatibility")
require(
"continue-on-error: ${{ matrix.experimental }}" in compatibility_job,
"Scheduled compatibility failures are not controlled by matrix.experimental",
)
require(
"allow-prereleases: ${{ matrix.experimental }}" in compatibility_job,
"Scheduled compatibility prerelease setup is not controlled by matrix.experimental",
)
compatibility = compatibility_matrix(compatibility_job)
expected_compatibility = tuple((version, False) for version in SUPPORTED) + ((PRERELEASE, True),)
require(
compatibility == expected_compatibility,
f"Scheduled compatibility matrix is {compatibility}, expected {expected_compatibility}",
)
require(
f"Python {MINIMUM} through\n{CURRENT_STABLE}" in policy,
"Policy current-compatibility text does not match the supported matrix",
)
project_metadata = pyproject.split("[dependency-groups]", 1)[0]
for requirement in UNMARKED_DEPENDENCIES:
require(
f'"{requirement}"' in project_metadata,
f"Package metadata does not contain the unmarked requirement {requirement!r}",
)
require(
re.search(rf'"{re.escape(requirement)};\s*python_version\b', project_metadata) is None,
f"Package metadata still contains a Python marker for {requirement!r}",
)
print(
f"Python policy is synchronized: minimum {MINIMUM}, "
f"supported {SUPPORTED[0]}-{SUPPORTED[-1]}, prerelease {PRERELEASE}"
)
if __name__ == "__main__":
main()