Source code for peal.entrypoints.preflight

"""Check run configs before spending GPU time on them.

Usage::

    python preflight.py <config.yaml> [<config.yaml> ...]
    python preflight.py --script reproduction_scripts/reproduce_sce_results.sh
    python preflight.py --script <script> --quiet     # only the failures
    python preflight.py --no-existence configs/**/*.yaml   # CI, no $PEAL_RUNS

Exit status is 0 when everything checked can run, 1 otherwise, so a script can
guard itself with `python preflight.py --script "$0" || exit 1`.
"""

import argparse
import sys

from peal.preflight import check_config, check_script, check_script_executables


[docs] def report(name, problems, notes, quiet=False): """Print one config's verdict and say whether it failed. Parameters ---------- name : str Label for the line, normally the config's file name. problems : list of str Reasons the config cannot run; any entry means failure. notes : list of str Informational remarks, printed only for passing configs. quiet : bool, optional Suppress the output for passing configs. Returns ------- bool ``True`` when ``problems`` is non-empty. """ if problems: print(f"FAIL {name}") for p in problems: print(f" problem: {p}") elif not quiet: print(f"ok {name}") for n in notes: print(f" note: {n}") return bool(problems)
[docs] def main(): """Check the configs named on the command line and/or in ``--script``. Delegates to ``check_config`` and ``check_script`` in ``peal/preflight.py``. Returns ------- int Exit status: 0 when every checked config can run, 1 otherwise. """ parser = argparse.ArgumentParser( description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter ) parser.add_argument("configs", nargs="*") parser.add_argument( "--script", help="check every --config this shell script passes" ) parser.add_argument("--quiet", action="store_true", help="print only the failures") parser.add_argument( "--require-tracked", action="store_true", help="also fail when a file the script invokes exists but is not tracked\n" "by git, i.e. a clone of the repository would not get it. Use this for a\n" "release check; the default only asks whether the script can run here", ) parser.add_argument( "--no-existence", action="store_true", help="skip the on-disk checks; use in a commit hook or CI, where\n" "$PEAL_RUNS is not mounted", ) args = parser.parse_args() failed = 0 checked = 0 if args.script: results = check_script(args.script, not args.no_existence) for cfg, problems, notes in results: checked += 1 failed += report(cfg.split("/")[-1], problems, notes, args.quiet) # A script also *runs* files, and a config-only check cannot see when one # of those is missing or was never committed. if not args.no_existence: problems, notes = check_script_executables( args.script, args.require_tracked ) checked += 1 failed += report( args.script.split("/")[-1] + " (files it runs)", problems, notes, args.quiet, ) for cfg in args.configs: problems, notes = check_config(cfg, not args.no_existence) checked += 1 failed += report(cfg.split("/")[-1], problems, notes, args.quiet) if not checked: parser.error("nothing to check: pass config paths or --script") print(f"\n{checked} config(s) checked, {failed} cannot run as committed") return 1 if failed else 0
if __name__ == "__main__": sys.exit(main())