Skip to content

finprint.apply

module finprint

Apply: run a saved recipe on new inputs of the same shape (more weeks, an extra column), not to check it.

verify asks “does this recipe still make this exact file?” and refuses any input whose hash differs. apply asks “make the same prep on this new data”: it binds each recipe input to a frame or a file, runs the steps through the same machinery as replay (run_step, the allowlist, NamedSteps, the trust store) and checks each step’s shape instead of its hash. Outcomes:

  • applied: every step ran and kept the shape it was recorded with; the output frame is returned;
  • refused with a code: INPUTS_UNBOUND (a recipe input was not given, or its file could not be read), INPUTS_AMBIGUOUS (one key given matches more than one recipe input), DATA_DERIVED_LITERAL (a step uses a value taken out of a computed scalar, so it would reuse the recorded value), ROW_BOUND_STEP (a step addresses rows by position or label, so it cannot carry over to new rows; checked before anything runs), STEP_NOT_REPLAYABLE (a cell edit or a step replay never reruns), CODE_UNTRUSTED (named-step code this machine did not record, without trust_code=True), STEP_SHAPE_CHANGED (a step added or removed other columns than it did when recorded), STEP_FAILED (a step raised, e.g. a missing column).

A dtype change that differs from the recorded one is a warning note (DTYPE_DRIFT), never a stop; row counts are free. A value computed from the data (s.median(), s.max() - 52 weeks, s.iloc[0]) is recorded as an expression and recomputed on the new data; a step using such a value after the analyst took it out of the recorder (float(x), if x > 0) is refused before anything runs (DATA_DERIVED_LITERAL). Columns the new inputs have and the recorded inputs did not are carried and never counted as a shape change. The message holds basenames, column names and exception types only (an exception’s text can quote row values), like verify’s. The frames passed in are copied, never changed.

class

class ApplyResult

Fields

NameTypeDefaultDescription
statusstr
messagestr
framepd.DataFrame | NoneNone
codestr | NoneNone
step_idstr | NoneNone
noteslist[dict[str, str]]field(default_factory=list)
rows_indict[str, int]field(default_factory=dict)
rows_outint | NoneNone
untrustedlist[RecipeStep]field(default_factory=list)
Methods and properties (2)

to_dict method

def to_dict(self) -> dict[str, Any]

Everything but the frame, JSON-safe.

init constructor

ApplyResult(
self,
status: str,
message: str,
frame: pd.DataFrame | None = None,
code: str | None = None,
step_id: str | None = None,
notes: list[dict[str, str]] = list(),
rows_in: dict[str, int] = dict(),
rows_out: int | None = None,
untrusted: list[RecipeStep] = list(),
) -> None

Source: packages/finprint/src/finprint/apply.py, line 51

function

def row_bound_steps(recipe: TransformRecipe) -> list[dict[str, str]]

Steps whose recorded arguments address rows by literal position or label, so they cannot carry over to new data ([{"step_id", "op", "reason"}]). Pure: reads only the recorded op and arguments.

Row-bound: drop(index=...) or row labels on axis 0, a bounded frame slice (df[10:40]), iloc/iat with positions, loc/at with literal labels or a bounded label slice, take, head/tail (a fixed row count cuts the new rows), sample(n=...) (with or without random_state), truncate with row labels that are not dates, xs(label) on the row axis, setting index or reindex/set_axis to fixed labels, and the same addressing inside a column expression (s.iloc[:50], s.loc[5:20], s[:30], s.head()). Not row-bound: a boolean mask or expression, [:, cols], sample(frac=...), truncate on dates, xs(..., axis=1), column-only operations, one cell read inside a computed value (s / s.iloc[0] recomputes: on new data it reads the new first row), named steps (their code is the analyst’s) and cell edits (refused anyway).

Source: packages/finprint/src/finprint/apply.py, line 258

function

def data_derived_steps(recipe: TransformRecipe) -> list[dict[str, str]]

Steps that use a literal the analyst took out of a value computed from the data (float(s.median()), int(s.max()), f"{s.mean():.1f}", np.float64(x), math.log(x)) or that follow an if on such a value. The recorder marks them (args.data_derived: how the value was taken out); on new data they would reuse the recorded value, so apply refuses them. Values kept as they come from pandas are recorded as expressions ({"__scalar__": ...}) and recomputed instead.

Source: packages/finprint/src/finprint/apply.py, line 289

function

def predates_apply(recipe: TransformRecipe) -> bool

The recipe was recorded by a finprint older than APPLY_MIN_VERSION (or its version cannot be read).

Source: packages/finprint/src/finprint/apply.py, line 324

function

def apply(
recipe: TransformRecipe | str | Path,
inputs: Mapping[str, Any],
*,
trust_code: bool = False,
) -> ApplyResult

Run recipe’s steps on new inputs ({input_id | name | file basename: DataFrame | path}).

No hash or fingerprint is compared (that is verify); each step must add and remove the columns it did when recorded. Named-step code this machine did not record runs only with trust_code=True.

Source: packages/finprint/src/finprint/apply.py, line 409

NameTypeValueDescription
APPLY_MIN_VERSION'0.2.0'
ROW_INDEXERS{'loc': 'label', 'at': 'label', 'iloc': 'position', 'iat': 'position'}