Ctrl K
ComunidadGuías…

scripts/p5_wave3_autofix_ops.py

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
#!/usr/bin/env python3
"""P5 wave3: generic mypy strict autofix for *_ops.py."""

from __future__ import annotations

import re
import subprocess
import sys
from pathlib import Path

ROOT = Path(__file__).resolve().parents[1]


def ensure_import(src: str, name: str) -> str:
    if name not in src:
        return src
    if re.search(rf"from typing import[^\n]*\b{name}\b", src):
        return src
    if "from typing import" in src:
        return re.sub(
            r"(from typing import [^\n]+)",
            lambda m: m.group(1) + (f", {name}" if name not in m.group(1) else ""),
            src,
            count=1,
        )
    return f"from typing import {name}\n" + src


def generic_fix(src: str) -> str:
    # yaml
    if "import yaml" in src:
        src = re.sub(
            r"import yaml(?!  # type: ignore)",
            "import yaml  # type: ignore[import-untyped]",
            src,
        )
    # remove unused import-untyped ignores (strict may not need)
    src = re.sub(r"  # type: ignore\[import-untyped\](?=\n)", "", src)

    src = re.sub(r": dict\)(?!])", ": dict[str, Any])", src)
    src = re.sub(r": dict \|", ": dict[str, Any] |", src)
    src = re.sub(r": dict,", ": dict[str, Any],", src)
    src = re.sub(r"-> dict:", "-> dict[str, Any]:", src)
    src = re.sub(r"-> dict\)(?!])", "-> dict[str, Any])", src)
    src = re.sub(r": list\)(?!])", ": list[Any])", src)
    src = re.sub(r"-> list:", "-> list[Any]:", src)
    src = re.sub(r"-> list\)(?!])", "-> list[Any])", src)
    src = re.sub(r"list\[dict\]", "list[dict[str, Any]]", src)
    src = re.sub(r"\bdict\[\]", "dict[str, Any]", src)
    src = re.sub(r"\blist\[\]", "list[Any]", src)

    if "dict[str, Any]" in src or "list[Any]" in src:
        src = ensure_import(src, "Any")
    if "cast(" in src:
        src = ensure_import(src, "cast")
    if "Callable" in src and "from collections.abc import Callable" not in src:
        src = ensure_import(src, "Callable")

    # env_truthy
    src = re.sub(r"return env_truthy\(", "return bool(env_truthy(", src)

    # get_butler_home
    if "get_butler_home()" in src:
        src = ensure_import(src, "cast")
        src = re.sub(r"return get_butler_home\(\)", "return cast(Path, get_butler_home())", src)

    return src


def fix_path(path: Path) -> bool:
    orig = path.read_text(encoding="utf-8")
    new = generic_fix(orig)
    if new != orig:
        path.write_text(new, encoding="utf-8")
        return True
    return False


def main() -> int:
    targets: list[Path] = []
    if len(sys.argv) > 1:
        for arg in sys.argv[1:]:
            targets.append(ROOT / arg)
    else:
        strict = {
            ln.strip()
            for ln in (ROOT / "scripts/p5_ops_strict_modules.txt").read_text().splitlines()
            if ln.strip()
        }
        for p in sorted((ROOT / "butler").rglob("*_ops.py")):
            rel = str(p.relative_to(ROOT)).replace("\\", "/")
            if rel not in strict:
                targets.append(p)

    changed = 0
    for p in targets:
        if fix_path(p):
            changed += 1
            print("generic", p.relative_to(ROOT))
    print(f"generic-fixed {changed}/{len(targets)}")
    return 0


if __name__ == "__main__":
    raise SystemExit(main())