quality_control.checkpoints.base

Checkpoint framework base class.

The third quality-control stage (after Diagnostics and QC). A Checkpoint’s checkpoint methods READ the 0/1 QC flags written to QUALITY_CONTROL and the product headers, then emit warnings or raise errors – they never write keywords. A method opts in by setting _checkpoint_name on the function object; run() walks all such methods (MRO order) and calls each.

run() also folds in the two upstream read-only stages: it first runs the subclass’s paired Diagnostics and QC classes (DIAGNOSTICS/QC) – which write the metrics and 0/1 flags – then the checkpoint methods. So the recipe drives the whole pipeline order science modules -> Diagnostics -> QC -> Checkpoints through one CheckpointL{n}(obj).run() call.

Two responsibilities, both inherited base checkpoints:
  • unregistered_keywords – structural header validation: any non-structural card not registered for its extension raises ValueError (this logic used to live in QC._validate_headers).

  • qc_flags – read each 0/1 QC flag; a failed (0) flag named in the subclass’s RAISE_FLAGS raises, every other failed flag warns.

Severity policy lives in the per-level subclasses (their RAISE_FLAGS).

class kpfpipe.quality_control.checkpoints.base.Checkpoint(kpf_obj)

Bases: object

Base runner for per-level checkpoint methods.

Parameters:

kpf_obj (KPFDataModel) – Finished data product whose flags/headers are read (never written).

DIAGNOSTICS = None
LEVEL = None
QC = None
RAISE_FLAGS = ()
qc_flags()

Read this level’s 0/1 QC flags; raise a RAISE_FLAGS failure, warn the rest.

Scoped to this level’s own checks (the registry’s qc_flag_keywords_by_level[LEVEL] – the QCL{n} flags), NOT the flags propagated from lower levels: QUALITY_CONTROL accumulates the whole L0->L1->L2->L4 history, but a lower-level flag was already surfaced at its own level’s checkpoint, so re-warning it here would just be noise. A flag absent from the header is skipped (the check did not run); a flag equal to 0 raises if it is in RAISE_FLAGS, else warns.

run()

Run the paired Diagnostics and QC, then every checkpoint method.

Folds in the two upstream read-only stages: Diagnostics writes its metrics, QC writes the 0/1 flags + ISGOOD (captured in self.qc_results for callers that report them), then each checkpoint method warns or raises. A level with no paired DIAGNOSTICS/QC skips that stage. The checkpoint methods themselves never write (no return value).

unregistered_keywords()

Raise on any non-structural card not registered for its extension.

For each registry-governed extension present on the product (PRIMARY only where it is EPRV-standard – L1 onward; the raw WMKO L0 PRIMARY is skipped), a card that is neither structural nor registered (in keyword_registry.allowed[ext]) raises ValueError and names it. This subsumes the old WMKO-native leak check: a raw instrument keyword kept in INSTRUMENT_HEADER is simply unregistered for an EPRV PRIMARY.