scripts/lint_backend_python_floor.py
Rama main · réplica de lectura del repositorio original.
#!/usr/bin/env python3
# SPDX-License-Identifier: AGPL-3.0-only
# Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. See /studio/LICENSE.AGPL-3.0
"""Fails on stdlib APIs newer than the oldest CI interpreter (via vermin); syntax checks miss these."""
from __future__ import annotations
import json
import re
import shutil
import subprocess
import sys
from pathlib import Path
REPO = Path(__file__).resolve().parents[1]
WORKFLOW = REPO / ".github" / "workflows" / "studio-backend-ci.yml"
# Both trees the CI matrix legs execute: studio-backend-ci also runs `pytest unsloth_cli/tests`.
ROOTS = (
REPO / "studio" / "backend",
REPO / "unsloth_cli",
)
# Scan everything under studio/backend, tests included: the CI legs execute all of it.
EXCLUDE_PARTS = ("vendor", "node_modules", "__pycache__", ".venv")
# An above-floor symbol reached deliberately is suppressed AT THE SITE, with `# novermin` and a comment saying why, not by dropping its file from the scan. The one live case is locale.getencoding() in the data-designer plugin's state_store, inside a try/except AttributeError with a pre-3.11 fallback.
# Floor is declared in the workflow; the matrix and pyproject (>= 3.9) cannot supply the real floor.
FLOOR_KEY = "PYTHON_FLOOR"
def declared_floor() -> tuple[int, int]:
"""The floor the workflow declares, as (major, minor)."""
text = WORKFLOW.read_text(encoding = "utf-8")
found = re.search(rf"^\s*{FLOOR_KEY}:\s*['\"]?(\d+)\.(\d+)['\"]?\s*$", text, re.M)
if not found:
raise SystemExit(
f"{WORKFLOW.name} declares no {FLOOR_KEY}, so this lint has no target. It is "
f"declared there rather than here so that the number lives with the CI that "
f"used to test it."
)
return int(found.group(1)), int(found.group(2))
def targets() -> list[str]:
"""Every .py the matrix legs ship or execute, found rather than listed."""
found = []
for root in ROOTS:
if not root.is_dir():
raise SystemExit(f"{root} is gone; the scan would silently stop covering it")
found.extend(
str(path)
for path in sorted(root.rglob("*.py"))
if not any(part in EXCLUDE_PARTS for part in path.relative_to(root).parts)
)
if not found:
raise SystemExit(f"no python files found under {ROOTS}; the scan would pass on nothing")
return found
def main() -> int:
floor = declared_floor()
target = f"{floor[0]}.{floor[1]}"
# The console script: vermin has no __main__, so `python -m vermin` always fails.
vermin = shutil.which("vermin")
if vermin is None:
raise SystemExit(
"vermin is not installed, so the backend floor is unchecked. Install it in "
"the job that runs this, rather than letting the check quietly pass."
)
files = targets()
print(f"[floor] {len(files)} files must run on Python {target}, " f"the declared floor")
command = [
vermin,
"--no-tips",
"--violations",
f"-t={target}",
*files,
]
result = subprocess.run(command, capture_output = True, text = True)
sys.stdout.write(result.stdout)
sys.stderr.write(result.stderr)
if result.returncode == 0:
print(f"[floor] OK: nothing needs more than {target}")
return 0
print(
f"::error title=Backend needs a newer Python than the matrix floor::"
f"something under studio/backend or unsloth_cli requires more than Python {target}, "
f"which is the "
f"floor studio-backend-ci declares. Nothing runs that interpreter any more, so "
f"this check is the only thing standing between an above-floor symbol and a user "
f"on that version. Either guard the usage behind a sys.version_info check, or "
f"raise {FLOOR_KEY} in the workflow and say why."
)
return 1
if __name__ == "__main__":
raise SystemExit(main())