finprint.encoding
module finprint
Tagged JSON for step arguments and series expressions, and the inverse used by replay.
Arguments are stored as JSON-safe values. Values JSON cannot carry are tagged objects:
===================== =====================================================
{"__frame__": id} a tracked frame (or a series a step produced), by id
{"__expr__": node} a tracked series built from frames (see below)
{"__tuple__": []} tuple; __set__ set; __slice__ [start, stop, step]
{"__dict__": []} mapping with non-string keys, as [[key, value], …]
{"__ts__": iso} timestamp; __date__, __td__ (timedelta), __na__ (NA / NaT), __float__ (nan/inf)
{"__dtype__": s} a dtype; {"__callable__": "module:qualname"} a function on the replay allowlist (pure
pandas, numpy, math, statistics, datetime and builtins functions); {"__index__": [...]} a
pandas Index of labels; {"__module__": name} a module a named step uses
{"__scalar__": e} a value computed from tracked data (s.median(), s.max() - 52 weeks, s.iloc[0]):
the expression e, evaluated again on replay, so apply recomputes it on new data
{"__opaque__": s} something that could not be recorded (lambda, untracked frame, array); not replayable
{"__truncated__":} a string longer than 200 characters (prefix, length, sha256); not replayable
===================== =====================================================
Expression nodes: {"__col__": {"frame", "name"}}, {"__ref__": id}, {"__op__": {"self", "name", "args", "kwargs"}}`, `{"__attr__": {"self", "name"}}`, `{"__acc__": {"self", "accessor"}}`, `{"__idx__": {"frame", "kind"}}`, `{"__ufunc__": {"name", "args", "kwargs"}}`, `{"__sop__": {"op", "args"}} (arithmetic, a comparison
or round/abs/math.floor on scalars, from SCALAR_OPS only), and for edits made through a tracked series
{"__setv__": {"self", "indexer", "key", "value"}} (s[k] = v, s.loc[k] = v) and {"__seta__": {"self", "name", "value"}}` (`s.name = v`); an `__op__` with `inplace=True replays on a copy. Nothing is ever pickled.
Replay is data-driven and never runs a string: callables resolve only through the allowlist in resolve_callable,
method and attribute names must be public (or operator dunders) and never file I/O (to_csv, read_*).
Classes
Section titled “Classes”TrackedBase
Section titled “TrackedBase”class
class TrackedBaseBase of the proxies, so the encoder can recognise them without importing tracked.
Source: packages/finprint/src/finprint/encoding.py, line 103
NotReplayable
Section titled “NotReplayable”class
class NotReplayable(Exception)Raised when a recorded value cannot be rebuilt.
Source: packages/finprint/src/finprint/encoding.py, line 115
NotAllowedInReplay
Section titled “NotAllowedInReplay”class
class NotAllowedInReplay(NotReplayable)A recipe names a function, method or attribute that replay never runs (I/O, code execution, private names).
Source: packages/finprint/src/finprint/encoding.py, line 119
Encoder
Section titled “Encoder”class
class EncoderAccumulates whether everything encoded so far is replayable, the frame ids referenced and why not.
Fields
| Name | Type | Default | Description |
|---|---|---|---|
exempt | exempt | ||
replayable | True | ||
refs | list[str] | [] | |
reasons | list[str] | [] |
Methods and properties (6)
init constructor
Encoder(self, *, pandas_call: bool = True, exempt: frozenset[str] | None = None) -> Noneopaque method
def opaque(self, what: str) -> dict[str, Any]text method
def text(self, v: str) -> Anyvalue method
def value(self, v: Any, name: str | None = None, rows: int | None = None) -> AnyA value assigned into a frame: a list of more than a few items is row values and never recorded.
enc method
def enc(self, v: Any, name: str | None = None) -> Anycall method
def call( self, args: tuple[Any, ...] | list[Any], kwargs: Mapping[str, Any], names: list[str | None] | None = None,) -> dict[str, Any]Source: packages/finprint/src/finprint/encoding.py, line 399
Functions
Section titled “Functions”safe_method
Section titled “safe_method”function
def safe_method(name: str) -> boolA DataFrame or Series method name replay may call: public, or an operator dunder; never file I/O.
Source: packages/finprint/src/finprint/encoding.py, line 123
check_method
Section titled “check_method”function
def check_method(name: str) -> strSource: packages/finprint/src/finprint/encoding.py, line 130
unsafe_function_arg
Section titled “unsafe_function_arg”function
def unsafe_function_arg(method: str, args: Any, kwargs: Any) -> str | NoneThe first string a recorded call would run as a method that safe_method refuses, else None.
For apply/agg/aggregate/transform/pipe/map the function slot (the first positional
argument, or func/arg) is checked, and for agg/aggregate every named aggregation too; aggfunc
is checked on any call (pivot_table).
Source: packages/finprint/src/finprint/encoding.py, line 164
check_function_arg
Section titled “check_function_arg”function
def check_function_arg(method: str, args: Any, kwargs: Any) -> NoneSource: packages/finprint/src/finprint/encoding.py, line 187
eval_text_reason
Section titled “eval_text_reason”function
def eval_text_reason(expr: Any, names: set[str] | None) -> str | NoneWhy a query/eval expression is not something replay runs, else None. names are the @ names the
recipe recorded for the call (None: the recorded values could not be read, so no @ name is allowed).
Source: packages/finprint/src/finprint/encoding.py, line 208
eval_kwargs_reason
Section titled “eval_kwargs_reason”function
def eval_kwargs_reason(kwargs: Mapping[str, Any]) -> str | NoneKeyword arguments to query/eval that replay never passes on (scope, engine or parser overrides).
Source: packages/finprint/src/finprint/encoding.py, line 236
unsafe_eval_call
Section titled “unsafe_eval_call”function
def unsafe_eval_call(args: Any, kwargs: Any) -> str | NoneWhy a recorded query/eval call (its encoded arguments) is not something replay runs, else None.
Source: packages/finprint/src/finprint/encoding.py, line 261
call_eval
Section titled “call_eval”function
def call_eval(target: Any, method: str, args: list[Any], kwargs: dict[str, Any]) -> AnyRun target.query/target.eval with decoded arguments, @ limited to the recorded local_dict and an
empty global scope (global_dict={}, so the caller’s module globals are never visible).
Source: packages/finprint/src/finprint/encoding.py, line 292
resolve_callable
Section titled “resolve_callable”function
def resolve_callable(name: str) -> AnyThe function module:qualname names, only when it is on the replay allowlist (pure pandas, numpy, math,
statistics, datetime and builtins functions; never exec, eval, open, getattr or file I/O).
Source: packages/finprint/src/finprint/encoding.py, line 340
callable_name
Section titled “callable_name”function
def callable_name(f: Any) -> str | Nonemodule:qualname when f is a function replay may call (see resolve_callable), else None.
Source: packages/finprint/src/finprint/encoding.py, line 363
resolve_module
Section titled “resolve_module”function
def resolve_module(name: str) -> AnySource: packages/finprint/src/finprint/encoding.py, line 382
exempt_params
Section titled “exempt_params”function
def exempt_params(fn: Any) -> frozenset[str]pandas’ own key/keys parameters of fn (they name columns, so they are not redacted).
Source: packages/finprint/src/finprint/encoding.py, line 388
positional_names
Section titled “positional_names”function
def positional_names( func: Any, args: tuple[Any, ...], kwargs: Mapping[str, Any], *, skip_self: bool,) -> list[str | None]Parameter names for positional arguments (for redaction), best effort.
Source: packages/finprint/src/finprint/encoding.py, line 540
unwrap
Section titled “unwrap”function
def unwrap(v: Any) -> AnyPlain pandas objects for tracked ones, recursively through lists, tuples and dicts.
Source: packages/finprint/src/finprint/encoding.py, line 560
tracked_in
Section titled “tracked_in”function
def tracked_in(v: Any) -> list[Any]Tracked objects inside v (lists, tuples and dict values).
Source: packages/finprint/src/finprint/encoding.py, line 573
decode
Section titled “decode”function
def decode(node: Any, ns: Mapping[str, Any], copy_frames: bool = True) -> AnySource: packages/finprint/src/finprint/encoding.py, line 587
eval_expr
Section titled “eval_expr”function
def eval_expr(node: Any, ns: Mapping[str, Any]) -> AnyEvaluate a series expression node against replayed frames (only public methods and operators run).
Source: packages/finprint/src/finprint/encoding.py, line 636
Constants
Section titled “Constants”| Name | Type | Value | Description |
|---|---|---|---|
ALLOWED_MODULES | {'builtins', 'pandas', 'numpy', 'math', 'statistics', 'datetime'} | ||
STEP_MODULES | ALLOWED_MODULES - {'builtins'} | {'re', 'string', 'itertools', 'functools', '... | ||
PANDAS_KEY_PARAMS | frozenset({'key', 'keys'}) | ||
MAX_ITEMS | 256 | ||
MAX_VALUE_ITEMS | 16 | ||
SAFE_BUILTINS | frozenset({'abs', 'all', 'any', 'bool', 'dict', 'divmod', 'enumerate', 'filte... | ||
BUILTIN_TYPES | frozenset({'str', 'int', 'float', 'bool', 'dict', 'list', 'tuple', 'set', 'fr... | ||
DATETIME_TYPES | frozenset({'date', 'datetime', 'time', 'timedelta', 'timezone'}) | ||
NUMPY_FUNCS | ('where', 'select', 'clip', 'round', 'around', 'sum', 'mean', 'median', 'std'... | ||
PANDAS_FUNCS | ('concat', 'merge', 'merge_asof', 'merge_ordered', 'to_datetime', 'to_numeric... | ||
PANDAS_CLASSES | ('DataFrame', 'Series', 'Index', 'MultiIndex', 'DatetimeIndex', 'Categorical'... | ||
IO_METHODS | frozenset({'to_csv', 'to_excel', 'to_parquet', 'to_pickle', 'to_json', 'to_sq... | ||
SAFE_DUNDERS | frozenset({*(f'__{n}__' for n in ('add', 'sub', 'mul', 'truediv', 'floordiv',... | ||
ACCESSORS | frozenset({'str', 'dt', 'cat', 'sparse', 'list', 'struct'}) | ||
SCALAR_OPS | dict[str, Callable[..., Any]] | {'add': operator.add, 'sub': operator.sub, 'mul': operator.mul, 'truediv': op... | |
FUNC_METHODS | frozenset({'apply', 'agg', 'aggregate', 'transform', 'pipe', 'map', 'applymap'}) | ||
LABEL_MAP_METHODS | frozenset[str] | frozenset({'map', 'applymap'}) | Methods whose mapping argument holds labels (s.map({"a": "to_review"})), so its values are never method names. |
EVAL_METHODS | frozenset({'query', 'eval'}) |