File size: 1,597 Bytes
700e896
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
"""Per-phase Parquet checkpoints (K13) β€” persist each phase's output frame for inspection.

Configurable via ``settings.debug.checkpoints`` (default OFF β†’ zero writes, zero overhead). When on, each
phase writes ``<checkpoint_dir>/<NN>_<name>.parquet`` atomically. Any failure is logged and swallowed β€” a
checkpoint must NEVER break a run. This is what makes each phase independently inspectable/testable.
"""

from __future__ import annotations

from pathlib import Path
from typing import TYPE_CHECKING

import polars as pl

from .io import atomic_write_parquet
from .logging import get_logger
from .paths import REPO_ROOT

if TYPE_CHECKING:  # runtime-free import β€” avoids any coupling/cycle with the config module
    from config import Settings


def checkpoint(index: int, name: str, frame: pl.DataFrame, settings: Settings) -> None:
    """Persist ``frame`` as ``<NN>_<name>.parquet`` when checkpoints are enabled; otherwise a no-op."""
    if not settings.debug.checkpoints:
        return
    base = Path(settings.debug.checkpoint_dir)
    if not base.is_absolute():
        base = REPO_ROOT / base  # resolve relative dirs against the repo root, not the cwd
    target = base / f"{index:02d}_{name}.parquet"
    try:
        atomic_write_parquet(frame, target)
        get_logger("checkpoint").info(
            "checkpoint.write", phase=name, rows=frame.height, cols=frame.width, path=str(target)
        )
    except Exception as failure:  # observational only β€” never break the rank
        get_logger("checkpoint").warning("checkpoint.fail", phase=name, error=str(failure))