MAINT-21: tests read and patch the Controller through tools/main_files.py
Prepares the split of app/main.py's Controller into files by job, with no app change. tools/main_files.py gives a test the text and the AST of main.py plus the files split from it, and `live`, which sets a global in every file whose functions read it (and raises when none does). Tests that read main.py's source, and tests and replay tools that patch a main global (datetime, compute, resolve, _mono, WRITE_BUDGET_S, ...), go through it, so they pass on this layout and on the split one. test_split_patches.py fails when a test or tool patches an app.main attribute that main.py no longer reads, or that another file also reads. tools/maint21_same_ast.py proves every function, method and module-level assignment of main.py at the base is found unchanged in its new file. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude
parent
56fc41fffa
commit
0d49027daa
@@ -135,6 +135,9 @@ from app.arbiter import (
|
||||
)
|
||||
from app.control import Tuning, compute, peak_at_risk
|
||||
from app.freeze import FreezeWatchdogConfig, looks_frozen, rail_w
|
||||
import os # noqa: E402
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "tools")) # noqa: E402
|
||||
import main_files # noqa: E402
|
||||
from app import main as app_main
|
||||
from app.inverter import Inverter, build_roster
|
||||
from app.main import Controller
|
||||
@@ -1295,7 +1298,7 @@ print(" the wiring's own mutations - a default install, maintenance charging")
|
||||
# `peak_at_risk()` answers False forever. That is the whole point - it is the
|
||||
# configuration on which the old suspension could not fire.
|
||||
MAINT_CHARGING = {"phase": "charge"}
|
||||
_mainsrc = (HERE / "app" / "main.py").read_text(encoding="utf-8")
|
||||
_mainsrc = main_files.source()
|
||||
|
||||
|
||||
def maint_cycle(tag, want_floor_w=LIVE_MAX_W * 0.3, **kw):
|
||||
@@ -2709,7 +2712,7 @@ CLAIM_SOURCES = {"device", "supervised", "failsafe", "safety", "peak",
|
||||
# silent gap in the scan.
|
||||
CLAIM_BUILDERS = {"peak_claim": "peak", "arb_claim": "arbitrage"}
|
||||
|
||||
_main_tree = ast.parse((HERE / "app" / "main.py").read_text(encoding="utf-8"))
|
||||
_main_tree = main_files.tree()
|
||||
_cycle_def = first((
|
||||
f
|
||||
for cls in ast.walk(_main_tree)
|
||||
@@ -2762,7 +2765,7 @@ def cycle_claims(run):
|
||||
call site and nowhere else.
|
||||
"""
|
||||
seen = []
|
||||
real = app_main.resolve
|
||||
real = main_files.live.resolve
|
||||
|
||||
def _spy(claims, **kw):
|
||||
# ⚠️ `**kw` PASSES THE ROSTER THROUGH UNREAD, AND THAT IS THE WHOLE
|
||||
@@ -2776,11 +2779,11 @@ def cycle_claims(run):
|
||||
seen.append(list(claims))
|
||||
return real(claims, **kw)
|
||||
|
||||
app_main.resolve = _spy
|
||||
main_files.live.resolve = _spy
|
||||
try:
|
||||
out = run()
|
||||
finally:
|
||||
app_main.resolve = real
|
||||
main_files.live.resolve = real
|
||||
return out, (seen[-1] if seen else [])
|
||||
|
||||
|
||||
@@ -4977,7 +4980,11 @@ check("⚠️ AND THE SECOND CONSUMER OF THE SAME REDUCTION. `arbitrage.decide()
|
||||
# written, at every roster length, in every configuration, and with no fixture
|
||||
# required.
|
||||
_APP_SRC = {p.stem: p.read_text(encoding="utf-8")
|
||||
for p in sorted((HERE / "app").glob("*.py"))}
|
||||
for p in sorted((HERE / "app").glob("*.py"))
|
||||
if p.name == "main.py" or p.name not in main_files.files()}
|
||||
# MAINT-21: main.py and the files split from it, the mixins read as Controller.
|
||||
_APP_SRC["main"] = re.sub(r"^class \w+Mixin:", "class Controller:",
|
||||
main_files.source(), flags=re.M)
|
||||
_ROSTER_OPTION = "inverters" # the ONE seed. Everything else is closure.
|
||||
_WRAPPERS = {"list", "tuple", "sorted", "enumerate", "zip", "reversed",
|
||||
"filter", "iter", "set", "dict"}
|
||||
|
||||
Reference in New Issue
Block a user