Repository Public
1"""Static purity check of a strategy file WITHOUT importing or executing it (v7 research jobs).23 python -m spq.lint --strategy <dir>45Prints {"sha256", "bytes", "problems": [...]} and exits 0 when the source passes the static check of spq.strategy6(allow-listed imports, no open/eval/exec/getattr/..., no clock, no file or network helper), 3 when it does not,72 when the file cannot be read. Nothing of the file is executed: it is parsed (ast) and hashed. This is what the8API runs on a submitted strategy before any other laboratory command touches it.9"""10from __future__ import annotations1112import argparse13import hashlib14import json15import sys16from pathlib import Path1718from .strategy import static_check1920MAX_BYTES = 64 * 1024212223def main(argv=None) -> int:24 p = argparse.ArgumentParser(prog="python -m spq.lint")25 p.add_argument("--strategy", required=True, help="strategy version directory (contains strategy.py)")26 a = p.parse_args(argv)27 path = Path(a.strategy) / "strategy.py"28 try:29 if path.is_symlink() or not path.is_file():30 raise OSError("strategy.py is missing or is not a regular file")31 blob = path.read_bytes()32 if len(blob) > MAX_BYTES:33 raise OSError(f"strategy.py is {len(blob)} bytes (limit {MAX_BYTES})")34 source = blob.decode("utf-8")35 except (OSError, UnicodeDecodeError) as e:36 print(f"spq.lint: {e}", file=sys.stderr)37 return 238 problems = static_check(source)39 print(json.dumps({"sha256": hashlib.sha256(blob).hexdigest(), "bytes": len(blob), "problems": problems}))40 return 3 if problems else 0414243if __name__ == "__main__":44 sys.exit(main())45