from utils.config_validate import ConfigError, require, require_schema from components.sud import Sud # The only part of a sud.json document's shape that's fixed regardless of # content - same declarative mechanism validate_temp_ctrl() uses for # config.json's TempCtrl section (see config_validate.py's require_schema()). # Everything else about a schedule (how many steps, which fields each one # overrides vs. inherits from default.step, whether a step needs ramp.rate # at all) depends on the document's own content, not just its shape, so it # can't be expressed as a static schema - handled by the step loop below # instead, still built on the same require()/ConfigError primitives. SUD_SCHEMA = { 'steps': list, } def validate_sud(data): """Fail-fast structural check for a sud.json document - same rationale as config_validate.py's validate_temp_ctrl(): a malformed field here doesn't surface until deep inside a run, or worse, during SudForecastEstimator's very first Load-time simulation (tasks/sud.py, components/sud_forecast.py and scripts/demos/sud/demo_sud.py all read ramp['rate'] directly, no .get() fallback, the moment any step actually sets its own 'temperature') - as a bare KeyError with no indication of which schedule step or field is wrong. Reuses Sud._parse_data() itself (rather than re-implementing default- merging/step-building) so what's checked here can never drift from what actually gets parsed at runtime.""" require_schema(data, SUD_SCHEMA) if not data['steps']: raise ConfigError("'steps' must be a non-empty list") try: schedule = Sud._parse_data(data)[2] except (KeyError, TypeError, AttributeError) as e: raise ConfigError("malformed sud document: {}".format(e)) for step in schedule: if step['temperature'] is None: continue try: require(step, 'ramp.rate', type=(int, float)) except ConfigError as e: raise ConfigError("step {} ({}): {}".format(step['number'], step.get('descr'), e))