Skip to content

Sanity-check all script inputs up front - #41

Open
ggmarshall wants to merge 5 commits into
mainfrom
input-validation
Open

Sanity-check all script inputs up front#41
ggmarshall wants to merge 5 commits into
mainfrom
input-validation

Conversation

@ggmarshall

Copy link
Copy Markdown
Contributor

Follow-on to #39: every console script now sanity-checks its inputs up front with named errors, so failures surface at startup instead of deep inside processing or after expensive computation.

New helpers (legenddataflowscripts.utils)

  • check_input_files(files, argname) — named FileNotFoundError aggregating every missing path (truncated after five).
  • expand_filelist(..., check_exists=True) — files listed inside filelists are now existence-checked by default. Previously a missing member failed deep inside lh5.read/load_data, often after other inputs were loaded and DSP work had begun.
  • prepare_output_paths(*paths) — output parent dirs are created right after build_log, so an unwritable output location fails before the minutes-long calibration/optimisation, not after it.
  • parse_json_arg(value, argname) — JSON CLI args (--table-map, --alias-table) fail naming the argument. A malformed --alias-table previously failed after the entire tier build.
  • get_rule_config — named error for a nonexistent config directory (dbetto's TextDB raises ValueError("input path is not a valid directory") without the path).

Script changes

  • tier/dsp + tier/hit (both entry points): rule config resolved via get_rule_config (named dir/rule errors instead of the pathless dbetto ValueError and bare KeyErrors); --table-map/--alias-table parsed right after build_log; --input checked; named error when all --pars-file entries are suffix-filtered away.
  • dplms / eopt / nopt: build_log now runs before the first config read, so those failures get ERROR-token logging; --peak-file/--inplots checked and output dirs prepared before the optimisation.
  • evtsel: a wrong --channel on --raw-cal-curve names the channel and argument.
  • svm: --svm-file existence checked — a bad path was silently embedded into the output pars.
  • svm_build: --train-hyperpars required when --train-data is given (previously Props.read_from(None)); output dir prepared (was missing entirely).
  • aoe: --detector required when --override-files is given — omitting it silently disabled the overrides.
  • ecal: named errors for --ctc-dict entries all filtered away and missing ctc_params; fixed the dead mkdir-after-open on --results-path; --save-path dir prepared (had no mkdir at all).
  • qc / pz / lq / filedb: output dirs prepared up front; filedb configures logging before its first file read.

Also

Tests

54 passing (5 new: check_input_files, prepare_output_paths, parse_json_arg, filelist member checking, get_rule_config missing-dir). Existing expand_filelist tests updated for the existence-check default. legend-dataflow's suite (100 tests) verified green against this branch via its editable install.

Behavior note: expand_filelist's member check is on by default — Snakemake-driven runs are unaffected (inputs are DAG-guaranteed), but hand-run invocations with nonexistent paths now fail at startup with the argument named.


Per AI_POLICY.md: this contribution was drafted with AI assistance (Claude Code) and reviewed by the submitter.

🤖 Generated with Claude Code

ggmarshall and others added 2 commits July 13, 2026 13:19
New helpers (utils, all exported):
- check_input_files(files, argname): named FileNotFoundError aggregating
  every missing path (truncated after five).
- expand_filelist(..., check_exists=True): expanded filelist members are
  now existence-checked by default — a missing listed file previously
  surfaced deep inside lh5.read/load_data, often after other inputs were
  loaded and DSP work had begun.
- prepare_output_paths(*paths): create output parent dirs before the
  expensive computation instead of at write time.
- parse_json_arg(value, argname): JSON CLI args fail naming the argument.
- get_rule_config: named error for a nonexistent config directory
  (dbetto's TextDB raises "input path is not a valid directory" without
  the path).

Script changes:
- tier/dsp + tier/hit (both entry points): resolve the rule config via
  get_rule_config; parse --table-map/--alias-table via parse_json_arg
  right after build_log (a malformed --alias-table previously failed
  AFTER the entire tier build); check --input; hoist output dir prep;
  named error when all --pars-file entries are suffix-filtered away.
- dplms/eopt/nopt: build_log now runs before the first config read so
  failures get ERROR-token logging; inputs checked and output dirs
  prepared before the optimisation work.
- evtsel: wrong --channel on --raw-cal-curve now names the channel and
  argument (get_channel_config).
- svm: --svm-file existence checked (a bad path was silently embedded
  into the output pars).
- svm_build: --train-hyperpars required when --train-data is given
  (previously Props.read_from(None)); output dir prepared (was missing
  entirely).
- aoe: --detector required when --override-files is given (the override
  was silently dropped when omitted).
- ecal: named errors for --ctc-dict entries all filtered away and for a
  missing ctc_params key; fixed the dead mkdir-after-open on
  --results-path; output dirs prepared up front (--save-path had no
  mkdir at all).
- qc: build_log return captured; output dirs prepared up front.
- filedb: logging configured before the first file read; --output parent
  prepared up front (was missing).

Tests: check_input_files, prepare_output_paths, parse_json_arg,
expand_filelist member checking, get_rule_config missing-dir.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
TextDB.on(category=...) was adopted in #40 but the dependency pin still
allowed dbetto 1.3.x, which only accepts the deprecated system argument.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR centralizes “fail fast” input validation for console scripts by adding shared helpers in legenddataflowscripts.utils and adopting them across tier-building and parameter-calibration entry points, so invalid inputs are rejected early with named, actionable errors.

Changes:

  • Added input/output/JSON parsing helpers (check_input_files, prepare_output_paths, parse_json_arg) and strengthened expand_filelist by default member existence checking.
  • Updated multiple scripts to validate required inputs and create output directories immediately after logging setup / config resolution.
  • Bumped the dbetto dependency pin to >=1.4 to match usage of TextDB.on(category=...) and avoid test failures on older versions.

Reviewed changes

Copilot reviewed 19 out of 19 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
tests/test_utils_helpers.py Adds/updates unit tests for new helper behaviors and the new default existence-check semantics.
src/legenddataflowscripts/workflow/filedb.py Prepares output directory before reading config / scanning, after logging is configured.
src/legenddataflowscripts/utils/files.py Introduces check_input_files, prepare_output_paths, parse_json_arg; updates expand_filelist to optionally check existence (default on).
src/legenddataflowscripts/utils/cfgtools.py Adds a named FileNotFoundError when rule config directory does not exist.
src/legenddataflowscripts/utils/init.py Re-exports new helper functions from utils.files.
src/legenddataflowscripts/tier/hit.py Uses get_rule_config, parses JSON args early, validates inputs, and prepares output directories up front.
src/legenddataflowscripts/tier/dsp.py Uses get_rule_config, parses JSON args early, validates inputs, prepares outputs, and adds a named error for filtered-out --pars-file.
src/legenddataflowscripts/par/geds/hit/qc.py Initializes logging into log and prepares output paths early.
src/legenddataflowscripts/par/geds/hit/lq.py Prepares output paths up front (plot/pars/results) rather than immediately before writing.
src/legenddataflowscripts/par/geds/hit/ecal.py Prepares output paths early; adds named errors for filtered --ctc-dict and missing required config keys.
src/legenddataflowscripts/par/geds/hit/aoe.py Prepares output paths early and validates --detector when --override-files is provided.
src/legenddataflowscripts/par/geds/dsp/svm.py Adds existence-check for --svm-file to avoid embedding a bad path.
src/legenddataflowscripts/par/geds/dsp/svm_build.py Prepares output path early; adds validation for training inputs before attempting to train.
src/legenddataflowscripts/par/geds/dsp/pz.py Prepares output paths up front and removes late mkdirs.
src/legenddataflowscripts/par/geds/dsp/nopt.py Reorders logging/config read and introduces early checks/output path preparation.
src/legenddataflowscripts/par/geds/dsp/evtsel.py Uses get_channel_config(..., name="--raw-cal-curve") so bad channels are named in errors.
src/legenddataflowscripts/par/geds/dsp/eopt.py Reorders logging/config read and introduces early checks/output path preparation.
src/legenddataflowscripts/par/geds/dsp/dplms.py Reorders logging/config read and introduces early checks/output path preparation.
pyproject.toml Updates dependency pin from dbetto>=1.3.6 to dbetto>=1.4.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/legenddataflowscripts/par/geds/dsp/nopt.py
Comment thread src/legenddataflowscripts/par/geds/dsp/dplms.py
Comment thread src/legenddataflowscripts/par/geds/dsp/svm_build.py
Comment thread src/legenddataflowscripts/par/geds/hit/ecal.py
Calibration parameters load from the pars YAML as python (64-bit)
floats, and Table.eval/numexpr promotes float32-array x float64-scalar
to float64 - making every derived hit column (calibrated energies, PSD
and QC classifiers, 83% of the pht tier's raw bytes, compressing at
only 0.84-0.95) float64 even when the DSP inputs are float32. YAML
cannot express float32, so the cast happens in build-tier-hit after
loading the parameter dictionary, in both the multi-channel and
single-channel entry points (covers the hit and pht tiers).

Ops whose expressions reference genuine float64 columns (timestamp)
still promote to float64, which is correct. Validated on production
p16 pht pars over real data: 48/49 float ops become float32, all
boolean cuts and NaN patterns identical, every difference <= 5e-5 of
the column spread. The lone holdout is AoE_Classifier, whose generated
expression embeds a 10**-99 literal - to be fixed in the A/E
calibration generator upstream.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Cast hit-operation float parameters to float32 at load time
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants