72 lines
2.4 KiB
Python
72 lines
2.4 KiB
Python
"""Packaging invariants — version/solver single-sourcing and dependency hygiene.
|
|
|
|
Pure static checks over the repository files. No imports of :mod:`otko`,
|
|
Qt or the solver stack, so this test runs anywhere (including Windows 3.12).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
from pathlib import Path
|
|
|
|
try: # Python 3.11+
|
|
import tomllib
|
|
except ModuleNotFoundError: # pragma: no cover - Python 3.10 backport
|
|
import tomli as tomllib
|
|
|
|
REPO_ROOT = Path(__file__).resolve().parents[2]
|
|
SRC_ROOT = REPO_ROOT / "src" / "otko"
|
|
PYPROJECT = REPO_ROOT / "pyproject.toml"
|
|
|
|
_VERSION_RE = re.compile(r"^__version__\s*=", re.MULTILINE)
|
|
|
|
|
|
def _pyproject() -> dict:
|
|
with PYPROJECT.open("rb") as handle:
|
|
return tomllib.load(handle)
|
|
|
|
|
|
def _src_files() -> list[Path]:
|
|
return sorted(SRC_ROOT.rglob("*.py"))
|
|
|
|
|
|
def _dep_name(requirement: str) -> str:
|
|
"""Return the lowercased distribution name from a PEP 508 requirement."""
|
|
match = re.match(r"^\s*([A-Za-z0-9][A-Za-z0-9._-]*)", requirement)
|
|
return match.group(1).lower().replace("_", "-") if match else ""
|
|
|
|
|
|
def test_console_script_entrypoint() -> None:
|
|
assert _pyproject()["project"]["scripts"]["otko"] == "otko.__main__:main"
|
|
|
|
|
|
def test_project_version_is_dynamic() -> None:
|
|
project = _pyproject()["project"]
|
|
assert "version" not in project
|
|
assert project["dynamic"] == ["version"]
|
|
|
|
|
|
def test_single_version_assignment() -> None:
|
|
hits = [path for path in _src_files() if _VERSION_RE.search(path.read_text(encoding="utf-8"))]
|
|
assert len(hits) == 1, f"expected exactly one __version__ assignment, got {hits}"
|
|
assert hits[0] == SRC_ROOT / "_const.py"
|
|
|
|
|
|
def test_solver_version_literal_only_in_const() -> None:
|
|
hits = [path for path in _src_files() if "3.8.0.0" in path.read_text(encoding="utf-8")]
|
|
assert hits == [SRC_ROOT / "_const.py"], f"unexpected 3.8.0.0 occurrences: {hits}"
|
|
|
|
|
|
def test_ruff_line_length_and_ignore() -> None:
|
|
ruff = _pyproject()["tool"]["ruff"]
|
|
assert ruff["line-length"] == 100
|
|
assert "E501" in ruff["lint"]["ignore"]
|
|
|
|
|
|
def test_no_phantom_scipy_pandas_dependencies() -> None:
|
|
project = _pyproject()["project"]
|
|
groups = [project.get("dependencies", [])]
|
|
groups.extend(project.get("optional-dependencies", {}).values())
|
|
declared = {_dep_name(req) for group in groups for req in group}
|
|
assert "scipy" not in declared
|
|
assert "pandas" not in declared
|