Context
src/hapi used assert for input validation in 53 places. assert is stripped under
python -O, so none of those were doing real validation -- an invalid input sailed past the
guard and failed further in, on a shape mismatch or a None. PR #214 converted every one. This
issue documents that change and the defects it surfaced along the way.
What Changed
- Every
assert in src/hapi doing input checking now raises TypeError (wrong type) or
ValueError (wrong value/length) instead. src/hapi now contains no assert.
- Repeated checks were pulled into helpers instead of becoming dozens of near-identical
if
blocks: _check_parameters_cover_grid and _check_lake_meteo in run.py,
_check_optimization_args in calibration.py.
- Three messages that were already wrong, independent of the
-O issue, are corrected rather
than carried over verbatim (the store_history/history_fname messages in
run_calibration/FW1Calibration, lumpedCalibration's basic_inputs message, and the
four maxbas checks' wording).
Defects found while doing this
Catchment.__init__ now canonicalises and rejects routing_method. It used to store
the string verbatim, so a lower-case "muskingum" silently routed every cell down the
MAXBAS branch (distrrm.SpatialRouting compares != "Muskingum" case-sensitively) and
raised TypeError on bankfull_depth, which is None outside the flood model. Breaking
change: landed as refactor(catchment)! with a BREAKING CHANGE: footer.
read_discharge_gauges filled QGauges by the wrong key -- labelled from
gauges.column but filled by int(id), so any column != "id" produced the requested
columns entirely NaN plus a second, id-named set beside them, silently.
Routing.calculate_weights accepted a MAXBAS below one -- 0.5 produced an all-zero
hydrograph with nothing raised. Fixing it exposed a real defect in the lake test fixtures,
which had been routing on the wrong parameter set and asserting against empty output.
save_results concatenated its output directory instead of joining it, so some/dir
without a trailing separator wrote some/dirResult_2009-01-01.tif beside the directory
rather than inside it.
- Three methods advertised
str | dt.datetime but called strptime unconditionally
(plot_hydrograph, read_discharge_gauges's split=True path, save_results); each now
branches on isinstance(..., str).
Affected locations
| File |
Notes |
src/hapi/catchment.py |
routing_method canonicalisation, QGauges fix, save_results fix, datetime branches |
src/hapi/routing.py |
calculate_weights guard |
src/hapi/calibration.py |
_check_optimization_args, two Sonar-flagged guards |
src/hapi/run.py |
_check_parameters_cover_grid, _check_lake_meteo |
src/hapi/inputs.py, src/hapi/rrm/*.py |
remaining assert -> raise conversions |
Verification
Implemented in #214.
Context
src/hapiusedassertfor input validation in 53 places.assertis stripped underpython -O, so none of those were doing real validation -- an invalid input sailed past theguard and failed further in, on a shape mismatch or a
None. PR #214 converted every one. Thisissue documents that change and the defects it surfaced along the way.
What Changed
assertinsrc/hapidoing input checking now raisesTypeError(wrong type) orValueError(wrong value/length) instead.src/hapinow contains noassert.ifblocks:
_check_parameters_cover_gridand_check_lake_meteoinrun.py,_check_optimization_argsincalibration.py.-Oissue, are corrected ratherthan carried over verbatim (the
store_history/history_fnamemessages inrun_calibration/FW1Calibration,lumpedCalibration'sbasic_inputsmessage, and thefour
maxbaschecks' wording).Defects found while doing this
Catchment.__init__now canonicalises and rejectsrouting_method. It used to storethe string verbatim, so a lower-case
"muskingum"silently routed every cell down theMAXBAS branch (
distrrm.SpatialRoutingcompares!= "Muskingum"case-sensitively) andraised
TypeErroronbankfull_depth, which isNoneoutside the flood model. Breakingchange: landed as
refactor(catchment)!with aBREAKING CHANGE:footer.read_discharge_gaugesfilledQGaugesby the wrong key -- labelled fromgauges.columnbut filled byint(id), so anycolumn != "id"produced the requestedcolumns entirely NaN plus a second, id-named set beside them, silently.
Routing.calculate_weightsaccepted a MAXBAS below one --0.5produced an all-zerohydrograph with nothing raised. Fixing it exposed a real defect in the lake test fixtures,
which had been routing on the wrong parameter set and asserting against empty output.
save_resultsconcatenated its output directory instead of joining it, sosome/dirwithout a trailing separator wrote
some/dirResult_2009-01-01.tifbeside the directoryrather than inside it.
str | dt.datetimebut calledstrptimeunconditionally(
plot_hydrograph,read_discharge_gauges'ssplit=Truepath,save_results); each nowbranches on
isinstance(..., str).Affected locations
src/hapi/catchment.pysrc/hapi/routing.pycalculate_weightsguardsrc/hapi/calibration.py_check_optimization_args, two Sonar-flagged guardssrc/hapi/run.py_check_parameters_cover_grid,_check_lake_meteosrc/hapi/inputs.py,src/hapi/rrm/*.pyVerification
pythonandpython -Ogauges.columncorruption, the all-zero MAXBAS hydrograph and bothstrptimebugs, then confirmed each fixed/review-roundspasses and a SonarCloud sweep (23 open issues down to 6)Implemented in #214.