Skip to content

finprint.replay

module finprint

Replay check: rerun a recipe from its inputs and compare fingerprints step by step.

Named steps run from their recorded source, each exec’d in its own namespace holding the globals recorded with it (modules such as pd and np, constants), so two versions of a step with the same name each replay with their own code. Named-step code runs only when this machine recorded it (its hash is in the local trust store, see trust) or the caller passes trust=True after reading it; otherwise replay stops before running anything. Verbs run from the recorded op and arguments through an allowlist: public pandas methods and operators, and pure functions from pandas, numpy, math, statistics, datetime and builtins. Nothing that runs strings as code, imports, reads or writes files is ever called, whatever the recipe says. Every input’s hash is checked first, and the output file beside the recipe is hashed against output.sha256. Outcomes:

  • reproduced: every step and the output (written and read back) match;
  • not_reproduced: the first step whose fingerprint differs, or that raised, is named (unseeded randomness lands here, with a hint); or the output file beside the recipe no longer matches it (RECIPE_OUTPUT_MISMATCH);
  • not_replayable: a step is a cell edit, its arguments could not be recorded, it names something replay never runs, or its code was not recorded on this machine and was not trusted;
  • inputs_missing: an input could not be found, or its data differs from the recorded one.

The message written back into the recipe holds only file basenames and exception types (an exception’s text can quote row values); ReplayResult.detail has the full text for the terminal.

Replay runs recorded code, so it only runs on the analyst’s machine (or a future sandboxed worker). apps/data never executes a recipe.

class

class InputCheck

Fields

NameTypeDefaultDescription
input_idstr
namestr
statusstr
detailstr''
wherestr''
Methods and properties (1)

init constructor

InputCheck(
self,
input_id: str,
name: str,
status: str,
detail: str = '',
where: str = '',
) -> None

Source: packages/finprint/src/finprint/replay.py, line 63

class

class StepCheck

Fields

NameTypeDefaultDescription
step_idstr
opstr
statusstr
expectedstr | NoneNone
gotstr | NoneNone
detailstr''
Methods and properties (1)

init constructor

StepCheck(
self,
step_id: str,
op: str,
status: str,
expected: str | None = None,
got: str | None = None,
detail: str = '',
) -> None

Source: packages/finprint/src/finprint/replay.py, line 72

class

class ReplayResult

Fields

NameTypeDefaultDescription
statusstr
messagestr
first_divergent_stepstr | NoneNone
inputslist[InputCheck]field(default_factory=list)
stepslist[StepCheck]field(default_factory=list)
output_fingerprintstr | NoneNone
detailstr''
untrustedlist[RecipeStep]field(default_factory=list)
output_mismatchboolFalse
Methods and properties (2)

verification method

def verification(self) -> Verification

init constructor

ReplayResult(
self,
status: str,
message: str,
first_divergent_step: str | None = None,
inputs: list[InputCheck] = list(),
steps: list[StepCheck] = list(),
output_fingerprint: str | None = None,
detail: str = '',
untrusted: list[RecipeStep] = list(),
output_mismatch: bool = False,
) -> None

Source: packages/finprint/src/finprint/replay.py, line 82

class

class NamedSteps

Each named step’s function, from its own code and recorded globals in a namespace of its own.

Methods and properties (2)

init constructor

NamedSteps(self) -> None

get method

def get(self, step: RecipeStep) -> Any

Source: packages/finprint/src/finprint/replay.py, line 160

function

def unsafe_step(recipe: TransformRecipe) -> tuple[RecipeStep, str] | None

The first step that passes a string as the function to apply/agg/transform/pipe/map (or an aggfunc) that the method allowlist refuses ("to_pickle", "eval", a read_/to_ name, a private name), with the reason. Checked before anything runs; run_step checks again as it runs.

Source: packages/finprint/src/finprint/replay.py, line 227

function

def run_step(step: RecipeStep, ns: dict[str, Any], named: NamedSteps) -> Any

Source: packages/finprint/src/finprint/replay.py, line 250

function

def blocked_step(recipe: TransformRecipe) -> RecipeStep | None

The first step replay never reruns: a cell edit, or a step whose arguments were not recorded replayably.

Source: packages/finprint/src/finprint/replay.py, line 341

function

def untrusted_steps(recipe: TransformRecipe) -> list[RecipeStep]

Named steps whose code this machine did not record (see trust).

Source: packages/finprint/src/finprint/replay.py, line 346

function

def replay(
recipe: TransformRecipe | str | Path,
*,
inputs_dir: str | Path | None = None,
recipe_dir: str | Path | None = None,
url: str | None = None,
principal: str | None = None,
client: httpx.Client | None = None,
trust_code: bool = False,
) -> ReplayResult

Rerun recipe and report the outcome (nothing is written; verify writes the result back).

Named-step code that this machine did not record runs only with trust_code=True.

Source: packages/finprint/src/finprint/replay.py, line 361

function

def apply_verification(recipe: TransformRecipe, result: ReplayResult) -> TransformRecipe

Source: packages/finprint/src/finprint/replay.py, line 445

function

def verify(
path: str | Path,
*,
inputs_dir: str | Path | None = None,
dry_run: bool = False,
url: str | None = None,
principal: str | None = None,
client: httpx.Client | None = None,
trust_code: bool = False,
) -> ReplayResult

Replay the recipe at path and, unless dry_run, write the verification back into the file.

A refusal to run untrusted named-step code is not written back (nothing was checked).

Source: packages/finprint/src/finprint/replay.py, line 459

NameTypeValueDescription
READERS{'read_csv': pd.read_csv, 'read_excel': pd.read_excel, 'read_parquet': pd.rea...