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;refusedwith 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, withouttrust_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.
Classes
Section titled “Classes”ApplyResult
Section titled “ApplyResult”class
class ApplyResultFields
| Name | Type | Default | Description |
|---|---|---|---|
status | str | ||
message | str | ||
frame | pd.DataFrame | None | None | |
code | str | None | None | |
step_id | str | None | None | |
notes | list[dict[str, str]] | field(default_factory=list) | |
rows_in | dict[str, int] | field(default_factory=dict) | |
rows_out | int | None | None | |
untrusted | list[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(),) -> NoneSource: packages/finprint/src/finprint/apply.py, line 51
Functions
Section titled “Functions”row_bound_steps
Section titled “row_bound_steps”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
data_derived_steps
Section titled “data_derived_steps”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
predates_apply
Section titled “predates_apply”function
def predates_apply(recipe: TransformRecipe) -> boolThe 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,) -> ApplyResultRun 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
Constants
Section titled “Constants”| Name | Type | Value | Description |
|---|---|---|---|
APPLY_MIN_VERSION | '0.2.0' | ||
ROW_INDEXERS | {'loc': 'label', 'at': 'label', 'iloc': 'position', 'iat': 'position'} |