Python API

noodlelab.verify

Verifiable calculations in plain Python: units, uncertainty, requirements, checks and an audit trail, for people and for AI agents writing code.

This is noodlelab’s science without the editor. Everything here works in any script, notebook or test after pip install noodlelab:

import noodlelab.verify as nv

with nv.record("pendulum") as rec:                      # writes runs/<...>/provenance.json
    L = rec.input("L", "1.000 ± 0.002 m", source="tape measure, lab book p. 12")
    g = rec.input("g", "9.81 ± 0.02 m/s^2", source="local gravity survey")
    T = rec.result("period", nv.const.tau * (L / g) ** 0.5)  # (2.006 ± 0.003) s
    rec.require("PER-001 period <= 2.1 s [Analysis]  # The swing shall take at most 2.1 s")
    rec.verify("PER-001", T)                                # ✓ margin +0.094 s
assert rec.passed

The rules it helps you keep (and audit() checks afterwards):

  1. Every number has a unit. Values are Pint quantities; mixing dimensions raises instead of silently giving nonsense. Dimensionless results say so (unit="1"). Constants are named, never retyped: nv.const.c, rec.constant("g0") (see noodlelab.constants), with their unit, their source and, for measured ones such as G, their CODATA uncertainty.

  2. Every measured input has an uncertainty and a source. Uncertainty is propagated by the GUM’s law (first order, with correlations kept), and budget() says which input contributes most. monte_carlo() checks the linear result where the model is not linear (JCGM 101).

  3. Requirements are written down before they are checked, as text a person can read (COM-001 link_margin >= 3 dB [Analysis]), and each check reports its margin, not just pass or fail. An uncertain result whose margin is no larger than its expanded uncertainty U = 2u is inconclusive, and does not pass: the design may meet the requirement, but the calculation cannot show it.

  4. Every run leaves a record: inputs, results, requirements, checks, the code (its hash and git commit), the files read and written (SHA-256) and the environment (Python, platform, package versions), so someone else can see exactly what was computed and repeat it.

noodlelab verify script.py runs a script, audits every record it wrote and exits non-zero when a check fails or is inconclusive, for CI and for agents checking their own work. Graphs from the editor are verified the same way (noodlelab verify analysis.graph.json).

class noodlelab.verify.Check(passed: 'bool', message: 'str' = '', requirement: 'str' = '', statement: 'str' = '', method: 'str' = '', required: 'str' = '', achieved: 'str' = '', margin: 'float | None' = None, unit: 'str' = '', margin_pct: 'float | None' = None, uncertainty: 'float | None' = None, inconclusive: 'bool' = False)[source]
Parameters:
  • passed (bool)

  • message (str)

  • requirement (str)

  • statement (str)

  • method (str)

  • required (str)

  • achieved (str)

  • margin (float | None)

  • unit (str)

  • margin_pct (float | None)

  • uncertainty (float | None)

  • inconclusive (bool)

passed: bool
message: str = ''
requirement: str = ''
statement: str = ''
method: str = ''
required: str = ''
achieved: str = ''
margin: float | None = None
unit: str = ''
margin_pct: float | None = None
uncertainty: float | None = None
inconclusive: bool = False
property status: str

"passed", "failed" or "inconclusive".

details()[source]

The requirement fields that are set, for events and records. False is left out like an empty field, so a record of an exact check is the same as before inconclusive verdicts existed.

Return type:

dict[str, Any]

class noodlelab.verify.Finding(code, level, message, where='')[source]

Something audit() found in a record.

Parameters:
  • code (str)

  • level (Literal['error', 'warning', 'info'])

  • message (str)

  • where (str)

code: str
level: Literal['error', 'warning', 'info']
message: str
where: str = ''
class noodlelab.verify.MonteCarlo(mean, std, low, high, p, trials, unit, gum, agrees, message)[source]

A Monte Carlo evaluation (JCGM 101) of a model, next to its GUM result.

Parameters:
  • mean (float)

  • std (float)

  • low (float)

  • high (float)

  • p (float)

  • trials (int)

  • unit (str)

  • gum (Any)

  • agrees (bool)

  • message (str)

mean: float
std: float
low: float
high: float
p: float
trials: int
unit: str
gum: Any
agrees: bool
message: str
property inconclusive: bool

Too few trials to tell (the message says how many would).

check(name='')[source]
Parameters:

name (str)

Return type:

Check

class noodlelab.verify.Quantity(value, unit=None)[source]

A Pint quantity as a type hint: Quantity["m/s"] for a speed, Quantity for any quantity. Quantity(9.81, "m/s^2") makes one, and isinstance(x, Quantity) is true for every Pint quantity.

Parameters:
  • value (Any)

  • unit (str | None)

Return type:

Any

class noodlelab.verify.Record(name, *, out=None, write=True, _caller=None)[source]

The audit trail of one calculation. See record().

Parameters:
  • name (str)

  • out (str | Path | None)

  • write (bool)

  • _caller (str | None)

status: str
error: str | None
checks: list[Check]
inputs: dict[str, dict[str, Any]]
results: dict[str, dict[str, Any]]
calls: list[dict[str, Any]]
files: dict[str, list[dict[str, Any]]]
notes: list[str]
path: Path | None
close()[source]

Finish the record and write it (done on leaving the with block).

Return type:

Path | None

input(name, value, *, unit='', source='', note='')[source]

A value the calculation starts from: "9.81 ± 0.02 m/s^2", a measure`d or :func:`q() value, or a plain number with unit. Name its source (instrument, dataset, paper, datasheet): the audit asks for one. Measured inputs are labelled name in uncertainty budgets. Returns the value to compute with.

Parameters:
  • name (str)

  • value (Any)

  • unit (str)

  • source (str)

  • note (str)

Return type:

Any

constant(name, *, label='')[source]

A named constant as an input (rec.constant("g0")), recorded under label (default: its name) with its source, so the audit needs no more. Returns the value to compute with.

Parameters:
  • name (str)

  • label (str)

Return type:

Any

result(name, value, *, unit='', exact=False, note='')[source]

A result of the calculation, recorded with its unit, uncertainty and budget. Converted to unit when given. exact=True says it has no uncertainty on purpose (a count, a definition), which the audit then accepts. Returns the value.

Parameters:
  • name (str)

  • value (Any)

  • unit (str)

  • exact (bool)

  • note (str)

Return type:

Any

require(spec)[source]

Write down requirements (text, one per line, or parsed ones) before checking them; each should be verified before the record closes.

Parameters:

spec (str | Requirement | RequirementSet)

Return type:

RequirementSet

verify(req, value)[source]

Check a result against a requirement: its id (given with require()), a Requirement, or a line of text.

Parameters:
Return type:

Check

expect(condition, message)[source]

A check that is not a requirement: a sanity bound, a conservation law, agreement with a reference. condition is truthy when it holds.

Parameters:
  • condition (Any)

  • message (str)

Return type:

Check

close_to(name, value, reference, *, rtol=0.0, atol=0.0, k=2.0)[source]

Check value agrees with reference (a textbook value, another method, an earlier result): within rtol/atol when given, else within k times their combined standard uncertainty (En ≤ 1 at k = 2).

Quantities are compared in the reference’s unit, so 20 degC and 293.15 K agree. With a temperature in degC or degF, atol is a difference (0.5 K or 0.5 delta_degC; 0.5 degC is read as one too) and rtol is relative to the absolute temperature.

Parameters:
  • name (str)

  • value (Any)

  • reference (Any)

  • rtol (float)

  • atol (Any)

Return type:

Check

read(path)[source]

Note a file the calculation reads (its SHA-256 goes in the record).

Parameters:

path (str | Path)

Return type:

Path

output(name)[source]

A path in this record’s folder to write an output to (a plot, a table); its SHA-256 is recorded when the record closes.

Parameters:

name (str)

Return type:

Path

wrote(path)[source]

Note a file the calculation wrote outside the record’s folder.

Parameters:

path (str | Path)

Return type:

Path

note(text)[source]

A remark for the reader: an assumption, a simplification, a caveat.

Parameters:

text (str)

Return type:

None

property passed: bool

Every check passed, every requirement written down was verified, and the block did not raise.

property unverified: list[str]

Requirements written down but never checked.

to_dict()[source]

The record as JSON-ready data (what write() saves).

Return type:

dict[str, Any]

write()[source]

Save the record as provenance.json in its folder.

Return type:

Path

summary()[source]

A few lines for a person (or an agent’s context): the verdict, each check, requirements left unverified and the audit’s findings.

Return type:

str

class noodlelab.verify.Requirement(id: 'str', quantity: 'str', op: 'Op', limit: 'float', unit: 'str' = '', upper: 'float | None' = None, tolerance: 'float' = 0.0, text: 'str' = '', method: 'str' = 'Analysis', priority: 'str' = '', parent: 'str' = '')[source]
Parameters:
  • id (str)

  • quantity (str)

  • op (Literal['>=', '<=', '>', '<', '==', 'between'])

  • limit (float)

  • unit (str)

  • upper (float | None)

  • tolerance (float)

  • text (str)

  • method (str)

  • priority (str)

  • parent (str)

id: str
quantity: str
op: Literal['>=', '<=', '>', '<', '==', 'between']
limit: float
unit: str = ''
upper: float | None = None
tolerance: float = 0.0
text: str = ''
method: str = 'Analysis'
priority: str = ''
parent: str = ''
property required: str

“≥ 3 dB”, “-20 to 60 °C”.

Type:

The requirement as a short phrase

property value: Any

a quantity when it has a unit (the lower limit of a range).

Type:

The limit as a value for the graph

magnitude(value)[source]

value as plain numbers in this requirement’s unit.

Parameters:

value (Any)

Return type:

Any

meets(margin)[source]

Whether a margin from margins() meets the requirement: a value exactly at the limit meets >= (margin 0) but not >. Element-wise on arrays.

Parameters:

margin (Any)

Return type:

Any

margins(value)[source]

How far inside the limits each value is, in the requirement’s unit (negative: outside; zero: at the limit, see meets()). Works element-wise on arrays and columns.

Parameters:

value (Any)

Return type:

Any

verify(value)[source]

Compare a result with the requirement. An array passes when every element does; its margin is the smallest (the worst case).

An uncertain value is judged with its expanded uncertainty U = 2u (k = 2, about 95 %): it fails when its nominal value misses, passes when the nominal value is inside the limit by more than U, and is otherwise inconclusive (not passed): the design may meet the requirement, but the calculation cannot show it. An exact value is judged on its value alone, as it always was.

Parameters:

value (Any)

Return type:

Check

to_record()[source]
Return type:

dict[str, Any]

class noodlelab.verify.RequirementSet(items=())[source]

Requirements by id, in the order they were given.

Parameters:

items (tuple[Requirement, ...])

items: tuple[Requirement, ...] = ()
property ids: list[str]
get(rid)[source]
Parameters:

rid (str)

Return type:

Requirement

merged(other)[source]
Parameters:

other (RequirementSet)

Return type:

RequirementSet

rows()[source]
Return type:

list[list[str]]

class noodlelab.verify.Uncertain(value, uncertainty, unit='', name='')[source]

A value with a standard uncertainty, as a type hint: Uncertain for any, Uncertain["m"] for a length. Uncertain(9.81, 0.02, "m/s^2") makes one.

Parameters:
  • value (Any)

  • uncertainty (float)

  • unit (str)

  • name (str)

Return type:

Any

class noodlelab.verify.Verifications(checks=())[source]

Checks of requirements, collected into a compliance matrix (one entry per requirement: verifying it again replaces the earlier entry).

Parameters:

checks (tuple[Check, ...])

checks: tuple[Check, ...] = ()
add(check)[source]
Parameters:

check (Check)

Return type:

Verifications

property passed: bool
rows()[source]
Return type:

list[list[str]]

noodlelab.verify.audit(rec)[source]

Check a record against the rules of a verifiable calculation (see RULES): failed or missing checks, results without units or uncertainty, inputs without a source, code that was not committed or has changed since. Takes a Record, its data, or its file.

Parameters:

rec (Record | dict[str, Any] | str | Path)

Return type:

list[Finding]

noodlelab.verify.budget(value)[source]

[(input, contribution, share of variance), ...], largest first: which named inputs the uncertainty of value comes from.

Parameters:

value (Any)

Return type:

list[tuple[str, float, float]]

noodlelab.verify.constant(name)[source]

A named constant with its unit: constant("c"), constant("g0"). Measured ones (G, m_e…) carry their CODATA uncertainty. The same as nv.const.c; add your own with define_constant().

Parameters:

name (str)

Return type:

Any

noodlelab.verify.current()[source]

The record being written (inside with record(...)), or None.

Return type:

Record | None

noodlelab.verify.define_constant(name, value, unit='', *, uncertainty=0.0, title='', source='', symbol='', override=False)

Add a constant for every graph and script in this process:

define("g_local", 9.8123, "m/s^2", uncertainty=0.0005, source="survey 2024")
define("rho_water", "998.2 kg/m^3", title="density of water at 20 °C")

Replacing a built-in constant warns, unless override=True says it is meant. pi, tau and e cannot be replaced.

Parameters:
  • name (str)

  • value (Any)

  • unit (str)

  • uncertainty (float)

  • title (str)

  • source (str)

  • symbol (str)

  • override (bool)

Return type:

Constant

noodlelab.verify.finished()[source]

Every record finished in this process, oldest first.

Return type:

list[Record]

noodlelab.verify.fmt(value, k=1.0)[source]

A value for people: (2.006 ± 0.003) s (GUM rounding), 3 dB, 0.5. k: coverage factor for an expanded uncertainty.

Parameters:
  • value (Any)

  • k (float)

Return type:

str

noodlelab.verify.load(path)[source]

A record from its provenance.json (or the folder holding it).

Parameters:

path (str | Path)

Return type:

dict[str, Any]

noodlelab.verify.load_constants(path)

define() every constant in a constants.toml file. Raises ConstantsError listing every problem, since a script should stop on them.

Parameters:

path (str | Path)

Return type:

dict[str, Constant]

noodlelab.verify.measure(value, u=None, unit='', *, name='', distribution='normal', dof=None)[source]

A measured value with its standard uncertainty:

  • measure("9.81 ± 0.02 m/s^2"), measure("9.81(2) m/s^2")

  • measure(9.81, 0.02, "m/s^2")

  • measure(q("9.81 m/s^2"), 0.02)

name labels it in budget(). distribution (and dof for Student’s t) is what monte_carlo() samples; u is always the GUM standard uncertainty (for a rectangular distribution of half-width a, give a/√3; for the mean of n readings with spread s, s/√n with dof = n - 1, which is the scale of the t distribution sampled, JCGM 101 6.4.9). An unknown distribution, or t without dof, is refused. Inside a monte_carlo() model this returns one draw instead.

Parameters:
  • value (Any)

  • u (float | None)

  • unit (str)

  • name (str)

  • distribution (Literal['normal', 'rectangular', 'triangular', 't'])

  • dof (float | None)

Return type:

Any

noodlelab.verify.monte_carlo(model, trials=20000, *, seed=1, p=0.95)[source]

Evaluate model (a function of no arguments that makes its uncertain inputs with measure() and returns a number or quantity) by Monte Carlo, and compare with linear GUM propagation (JCGM 101 8). Use it when the model is not linear in its inputs: agrees says whether the GUM uncertainty can be trusted. The inputs must be made inside the model.

Parameters:
  • model (Callable[[], Any])

  • trials (int)

  • seed (int)

  • p (float)

Return type:

MonteCarlo

noodlelab.verify.nominal(value)[source]

The value without its uncertainty.

Parameters:

value (Any)

Return type:

Any

noodlelab.verify.q(value, unit='')[source]

A quantity: q("9.81 m/s^2"), q(9.81, "m/s^2"), q("3 dB"). Text with an uncertainty ("9.81 ± 0.02 m/s^2") gives an uncertain one.

Parameters:
  • value (Any)

  • unit (str)

Return type:

Any

noodlelab.verify.record(name, *, out=None, write=True)[source]

Start a record of a calculation: with nv.record("link budget") as rec: ....

On leaving the block the record is written to <out>/<time>-<name>-<id>/provenance.json (out defaults to runs next to the script, or $NOODLELAB_RECORDS). write=False keeps it in memory only (tests).

Parameters:
  • name (str)

  • out (str | Path | None)

  • write (bool)

Return type:

Record

noodlelab.verify.requirement(line)[source]

One requirement from a line of text (see requirements()).

Parameters:

line (str)

Return type:

Requirement

noodlelab.verify.requirements(text)[source]

Requirements from text, one per line:

COM-001 link_margin >= 3 dB [Analysis]   # The link shall close with 3 dB to spare
PWR-001 dc_power <= 40 W [Inspection]
TMP-001 temperature between -20 and 60 degC

or from records (rows of a CSV/YAML spec: id, quantity, op, limit, unit…).

Parameters:

text (str | Iterable[dict[str, Any]])

Return type:

RequirementSet

noodlelab.verify.std_dev(value)[source]

The standard uncertainty (0 for an exact value), in the value’s unit.

Parameters:

value (Any)

Return type:

Any

noodlelab.verify.traced(fn=None, *, name=None)[source]

Record every call of a function in the active record: its arguments, result (with units and uncertainty), duration and a hash of its source. Outside a record the function runs as usual, at no cost:

@nv.traced
def drag(rho, v, cd, area):
    return 0.5 * rho * v**2 * cd * area
Parameters:
  • fn (F | None)

  • name (str | None)

Return type:

Any

noodlelab.verify.verify(req, value)[source]

Check value against a requirement (a Requirement or its line of text). The Check says whether it passed, what was achieved and the margin in the requirement’s unit (negative: outside the limit). An array passes when every element does; its margin is the worst case.

Parameters:
Return type:

Check

noodlelab.constants

Named constants for calculations in Python, with their units and, where they were measured, their CODATA uncertainty:

from noodlelab.constants import c, g0, k_B, pi

E = m * c**2                    # a Pint quantity, in kg·m²/s²
W = m * g0                      # standard gravity, 9.80665 m/s², exact
F = G * M * m / r**2            # G is uncertain: 6.67430(15)e-11 m³/(kg·s²)

pi, tau and e are the math module’s plain floats. Exact constants (c, h, k_B, g0…) are quantities, and measured ones (G, m_e, alpha…) are uncertain values named after the constant, so noodlelab.verify.budget() can list them. The full table, with sources, is in noodlelab.core.constants, and noodlelab constants prints it.

Constants you add with define() (or load() from a constants.toml) can be imported the same way once they are defined.

class noodlelab.constants.Constant(name: 'str', value: 'float', unit: 'str' = '', uncertainty: 'float' = 0.0, symbol: 'str' = '', title: 'str' = '', source: 'str' = '', group: 'str' = '', origin: 'Origin' = 'built-in', override: 'bool' = False)[source]
Parameters:
  • name (str)

  • value (float)

  • unit (str)

  • uncertainty (float)

  • symbol (str)

  • title (str)

  • source (str)

  • group (str)

  • origin (Literal['built-in', 'registered', 'workspace', 'graph'])

  • override (bool)

name: str
value: float
unit: str = ''
uncertainty: float = 0.0
symbol: str = ''
title: str = ''
source: str = ''
group: str = ''
origin: Literal['built-in', 'registered', 'workspace', 'graph'] = 'built-in'
override: bool = False
property exact: bool
value_of()[source]

The constant to compute with. A plain float for exact numbers, and a quantity for exact values with a unit. A measured value is uncertain, named after the constant. It is the same value every time, so results computed from G twice stay correlated. Inside a Monte Carlo trial it is a fresh draw instead.

Return type:

Any

key()[source]

What a cache key needs: the definition, not the description.

Return type:

dict[str, Any]

display()[source]

"9.80665 m/s^2", "6.6743e-11 ± 1.5e-15 m^3/(kg*s^2)".

Return type:

str

citation()[source]

What a record names as the source of an input taken from this constant.

Return type:

str

to_json()[source]
Return type:

dict[str, Any]

exception noodlelab.constants.ConstantsError[source]

A constant that cannot be defined (a bad name, value or unit).

noodlelab.constants.define(name, value, unit='', *, uncertainty=0.0, title='', source='', symbol='', override=False)[source]

Add a constant for every graph and script in this process:

define("g_local", 9.8123, "m/s^2", uncertainty=0.0005, source="survey 2024")
define("rho_water", "998.2 kg/m^3", title="density of water at 20 °C")

Replacing a built-in constant warns, unless override=True says it is meant. pi, tau and e cannot be replaced.

Parameters:
  • name (str)

  • value (Any)

  • unit (str)

  • uncertainty (float)

  • title (str)

  • source (str)

  • symbol (str)

  • override (bool)

Return type:

Constant

noodlelab.constants.load(path)[source]

define() every constant in a constants.toml file. Raises ConstantsError listing every problem, since a script should stop on them.

Parameters:

path (str | Path)

Return type:

dict[str, Constant]

noodlelab.constants.lookup(name)[source]

The constant called name; a KeyError suggests close names otherwise.

Parameters:

name (str)

Return type:

Constant

noodlelab.constants.names()[source]
Return type:

list[str]

Writing node packs

noodlelab: verifiable science for people and AI agents.

For calculations in plain Python (units, uncertainty, requirements, checks and a provenance record of every run), use noodlelab.verify:

import noodlelab.verify as nv

with nv.record("pendulum") as rec:
    L = rec.input("L", "1.000 ± 0.002 m", source="tape measure")
    ...

The public API for node-pack authors lives here:

from noodlelab import Param, Probe, ProbeContext, RunContext, node, register_type
class noodlelab.FileRef(value='')[source]

A reference to a file in the workspace, on the server, or on a mount.

It is a str (so it serialises, hashes and prints as the reference), with methods that work the same for every backend.

Parameters:

value (str | PurePath)

Return type:

FileRef

bind(fs)[source]
Parameters:

fs (FileSystems | None)

Return type:

FileRef

property target: Target
property mount: str | None
property is_local: bool
property name: str
property suffix: str
with_suffix(suffix)[source]

The file next to this one with another extension, on the same storage: dem.asc → dem.prj, for sidecar files.

Parameters:

suffix (str)

Return type:

FileRef

open(mode='rb', **kwargs)[source]

Open for reading ("rb", "r") or, on writable storage, writing.

Parameters:
  • mode (str)

  • kwargs (Any)

Return type:

IO[Any]

read_bytes()[source]
Return type:

bytes

read_text(encoding='utf-8')[source]
Parameters:

encoding (str)

Return type:

str

exists()[source]
Return type:

bool

is_file()[source]
Return type:

bool

info()[source]
Return type:

dict[str, Any]

token()[source]

Changes when the file changes (modification time, size, ETag). Used by probes to revalidate cached previews.

Return type:

tuple[Any, …] | None

local_path()[source]

A local file with this content. Remote files are downloaded once into a cache (keyed by reference, size and modification time).

Return type:

Path

class noodlelab.Param(label=None, description='', widget=None, min=None, max=None, step=None, precision=None, choices=None, accept=None, directory=False, exists=True, options_from=None, empty=None, type=None, multiline=False)[source]

Extra metadata for a parameter, used via Annotated[type, Param(...)].

Parameters:
  • label (str | None)

  • description (str)

  • widget (Literal['number', 'slider', 'combo', 'text', 'toggle', 'none'] | None)

  • min (float | None)

  • max (float | None)

  • step (float | None)

  • precision (int | None)

  • choices (Sequence[Any] | None)

  • accept (Sequence[str] | None)

  • directory (bool)

  • exists (bool)

  • options_from (str | None)

  • empty (str | None)

  • type (Any)

  • multiline (bool)

label: str | None = None
description: str = ''
widget: Literal['number', 'slider', 'combo', 'text', 'toggle', 'none'] | None = None
min: float | None = None
max: float | None = None
step: float | None = None
precision: int | None = None
choices: Sequence[Any] | None = None
accept: Sequence[str] | None = None
directory: bool = False
exists: bool = True
options_from: str | None = None
empty: str | None = None
type: Any = None
multiline: bool = False
class noodlelab.Preview(*, kind, summary, text=None, image=None, table=None, url=None)[source]
Parameters:
  • kind (Literal['value', 'text', 'image', 'table', 'file', 'math'])

  • summary (str)

  • text (str | None)

  • image (str | None)

  • table (TablePreview | None)

  • url (str | None)

kind: Literal['value', 'text', 'image', 'table', 'file', 'math']
summary: str
text: str | None
image: str | None
table: TablePreview | None
url: str | None
model_config = {}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

class noodlelab.Probe(preview=None, meta=None, output_meta=None, summary=None, sample=None, output_samples=None)[source]

What a probe returns (or yields).

preview: any value, rendered with the registered previewers (a DataFrame becomes a table, a Figure an image), or a ready Preview such as one from image_preview(). meta: small JSON describing the node’s outputs, readable downstream with ProbeContext.meta(); it applies to every output unless output_meta gives one per output name. summary overrides the preview’s one-line summary.

sample (or output_samples per output): a small value of the output’s type that downstream nodes are evaluated on while editing (see sample). When left out, a preview value of the output’s type is used, so returning df.head(20) is enough.

Parameters:
  • preview (Any)

  • meta (dict[str, Any] | None)

  • output_meta (dict[str, dict[str, Any]] | None)

  • summary (str | None)

  • sample (Any)

  • output_samples (dict[str, Any] | None)

preview: Any = None
meta: dict[str, Any] | None = None
output_meta: dict[str, dict[str, Any]] | None = None
summary: str | None = None
sample: Any = None
output_samples: dict[str, Any] | None = None
class noodlelab.ProbeContext(node_id, root, timeout, _upstream=<factory>, _linked=<factory>, _cancel=<factory>, _started=<factory>, watched=<factory>, logs=<factory>)[source]

Passed to probes and options functions that ask for it (by annotation or by naming a parameter ctx).

Parameters:
  • node_id (str)

  • root (Path)

  • timeout (float)

  • _upstream (dict[str, dict[str, Any] | None])

  • _linked (set[str])

  • _cancel (Event)

  • _started (float)

  • watched (dict[str, tuple[Callable[[], Any], Any]])

  • logs (list[str])

node_id: str
root: Path
timeout: float
watched: dict[str, tuple[Callable[[], Any], Any]]
logs: list[str]
meta(input)[source]

Meta of the output linked into input, or None (not linked, or the upstream node has no probe).

Parameters:

input (str)

Return type:

dict[str, Any] | None

linked(input)[source]
Parameters:

input (str)

Return type:

bool

watch(path)[source]

Mark a file the probe depends on: the cached result is dropped when it changes (modification time, size, or ETag for remote files). Accepts a Path (resolved against the workspace) or a FileRef; returns it.

Parameters:

path (str | PurePath)

Return type:

Any

property cancelled: bool

True once the node’s values changed and a newer probe started. Long probes should check this between steps (or call check_cancelled()).

check_cancelled()[source]
Return type:

None

property remaining: float

Seconds left in the time budget (may be negative).

log(message)[source]
Parameters:

message (str)

Return type:

None

class noodlelab.Problem(severity, message)[source]
Parameters:
  • severity (Literal['error', 'warning'])

  • message (str)

severity: Literal['error', 'warning']

Alias for field number 0

message: str

Alias for field number 1

class noodlelab.RunContext(run_id: 'str', node_id: 'str', workdir: 'Path', scratch: 'Path | None' = None, _emit: 'Callable[[str, dict[str, Any]], None]' = <function RunContext.<lambda> at 0x7f90a51e2840>, _provenance: 'Any' = None, _about_in: 'dict[str, Any]' = <factory>, _declared: 'dict[str, Any]' = <factory>, _samples: 'Any' = None)[source]
Parameters:
  • run_id (str)

  • node_id (str)

  • workdir (Path)

  • scratch (Path | None)

  • _emit (Callable[[str, dict[str, Any]], None])

  • _provenance (Any)

  • _about_in (dict[str, Any])

  • _declared (dict[str, Any])

  • _samples (Any)

run_id: str
node_id: str
workdir: Path
scratch: Path | None = None
monte_carlo_samples()[source]

The trials the executor ran for a Monte Carlo node (a Samples), or None.

Return type:

Any

describe(output='result', **fields)[source]

Say what an output is: title, description, sources, licences, citations or flags (lists of text). It travels downstream with the value (see noodlelab.core.about).

Parameters:
  • output (str)

  • fields (Any)

Return type:

None

input_about(name)[source]

What is known about input name: an About, or None.

Parameters:

name (str)

Return type:

Any

log(message)[source]

Send a log line to the editor (shown in the node inspector).

Parameters:

message (str)

Return type:

None

progress(fraction, message='')[source]

Report progress between 0 and 1; drawn as a bar on the node.

Parameters:
  • fraction (float)

  • message (str)

Return type:

None

path(name)[source]

A path inside this run’s output directory (parent dirs are created).

Parameters:

name (str)

Return type:

Path

temp(name)[source]

A path in the run’s scratch folder, for large temporary files.

Parameters:

name (str)

Return type:

Path

run_info()[source]

What is known about this run so far, as plain JSON: its id, start time and user, the environment (noodlelab, Python, platform, key library versions), and every node that finished before this one, with its settings and fingerprinted input files (see noodlelab.core.provenance). Outside the editor and noodlelab run (e.g. in an exported script) only the environment is known.

Return type:

dict[str, Any]

classmethod standalone(workdir='noodlelab-output')[source]

A context for calling nodes from plain Python (e.g. exported scripts).

Parameters:

workdir (str | Path)

Return type:

RunContext

noodlelab.as_file(value)[source]

A FileRef from a string or path (for node code also called from scripts).

Parameters:

value (str | PurePath | FileRef)

Return type:

FileRef

noodlelab.define_constant(name, value, unit='', *, uncertainty=0.0, title='', source='', symbol='', override=False)

Add a constant for every graph and script in this process:

define("g_local", 9.8123, "m/s^2", uncertainty=0.0005, source="survey 2024")
define("rho_water", "998.2 kg/m^3", title="density of water at 20 °C")

Replacing a built-in constant warns, unless override=True says it is meant. pi, tau and e cannot be replaced.

Parameters:
  • name (str)

  • value (Any)

  • unit (str)

  • uncertainty (float)

  • title (str)

  • source (str)

  • symbol (str)

  • override (bool)

Return type:

Constant

noodlelab.downsample(array, max_side=256)[source]

Every n-th pixel so that neither of the first two axes exceeds max_side. Cheap and shape-preserving; for a real overview read less data instead (e.g. rasterio’s out_shape).

Parameters:
  • array (Any)

  • max_side (int)

Return type:

Any

noodlelab.error(message)[source]

Return from a .check function to report an error (blocks running).

Parameters:

message (str)

Return type:

Problem

noodlelab.image_preview(array, *, max_side=256, summary=None)[source]

A PNG thumbnail from a NumPy array, with no imaging dependencies.

Accepts (H, W) grayscale, (H, W, 3|4) RGB(A), and band-first (3|4, H, W) as rasterio returns it. Non-uint8 data is stretched between the 2nd and 98th percentiles; NaN and inf become black.

Parameters:
  • array (Any)

  • max_side (int)

  • summary (str | None)

Return type:

Preview

noodlelab.node(func: F) → F[source]
noodlelab.node(*, id: str | None = None, title: str | None = None, category: str = 'Utility', description: str | None = None, color: str | None = None, outputs: Sequence[str] | None = None, version: int = 1, cacheable: bool = True, checkpoint: bool = True, fold: bool = False, sample: bool | None = None, converter: bool | Literal['implicit'] = False, cost: float = 1.0, vectorized: bool = False, views: Sequence[Literal['processing', 'report']] | None = None) → Callable[[F], F]

Mark a function as a node. Usable bare (@node) or with options.

version: bump it to invalidate cached results by hand (editing the function’s source already does this automatically). cacheable: set False when the output can change for the same inputs (unseeded randomness, clocks, hardware, files the node finds by itself). Files named by Path/FileRef inputs need no flag: their size and modification time are part of the cache key. checkpoint: set False to keep results in memory only, e.g. for huge intermediates that are quick to recompute. fold: the node is cheap and pure (a constant such as “File Path” or “Number”), so it is also evaluated while the graph is edited and downstream checks and probes receive its value as if it were typed in. sample: set False to never run the node on samples while editing (expensive work, side effects, statistics a sample would misstate). By default cacheable nodes without a RunContext preview on samples, except nodes that read files (give those a probe); True includes them anyway. converter: True makes the node a way to turn its first input’s type into its output’s type; the editor inserts it when such a link is drawn. "implicit" (lossless, no settings) applies it on links, without a node. cost ranks converter chains (lower is preferred). vectorized: the function also works on many Monte Carlo trials at once: given arrays with one element per trial where it takes a number, it returns one element per trial, element by element (no if on a value), without side effects, and raises where it would raise for some trial alone. A Monte Carlo evaluation whose nodes are all vectorized runs its trials in batches; otherwise, or if a batch disagrees with trials run one at a time, trial by trial. views: the canvases whose add-node menus offer the node, “processing” and/or “report”; by default Report nodes are offered on the Reporting tab, Text nodes on both and all others on the Processing tab. Any node can be placed on either tab (the editor can be set to offer them all).

The returned function gets decorators for edit-time helpers: .check (live health checks), .probe (a quick preview and metadata, e.g. the first rows of a file), .options(input) (dynamic dropdown choices), .uses_constants (the named constants it reads, for its cache key) and, for converters, .accepts (which values the conversion fits).

Parameters:
  • func (Any)

  • options (Any)

Return type:

Any

noodlelab.register_codec(py_type, name, *, save, load, suffix='.bin')[source]

Store values of a type (or a qualified class name, for optional dependencies) in checkpoints with save/load instead of pickle. The name is written into every checkpoint, so keep it stable.

Parameters:
  • py_type (type | str)

  • name (str)

  • save (Callable[[Any, IO[bytes]], dict[str, Any] | None])

  • load (Callable[[IO[bytes], dict[str, Any]], Any])

  • suffix (str)

Return type:

Codec

noodlelab.register_fields(py_type, *, names, type, get)[source]

Describe the fields of a type (or a qualified class name) for split outputs.

names(ref, meta): the field names, usually from the output’s meta (meta may be None). type(ref, name, meta): a field’s type hint, also asked without meta when a saved link is checked; return Any when it cannot be known then. get(value, name): read a field of a real value.

Parameters:
  • py_type (type | str)

  • names (Callable[[TypeRef, dict[str, Any] | None], list[str]])

  • type (Callable[[TypeRef, str, dict[str, Any] | None], Any])

  • get (Callable[[Any, str], Any])

Return type:

None

noodlelab.register_meta(py_type)[source]

Register a function describing values of a type (or a qualified class name, for optional dependencies) as a small JSON dict.

Parameters:

py_type (type | str)

Return type:

Callable[[Callable[[Any], dict[str, Any] | None]], Callable[[Any], dict[str, Any] | None]]

noodlelab.register_preview(py_type)[source]
Parameters:

py_type (type | str)

Return type:

Callable[[Callable[[Any, PreviewContext], Preview]], Callable[[Any, PreviewContext], Preview]]

noodlelab.register_sampler(py_type)[source]

Register fn(value, size) -> smaller value of the same type for a type (or a qualified class name). size is roughly the number of rows or elements along each axis to keep.

Parameters:

py_type (type | str)

Return type:

Callable[[Callable[[Any, int], Any]], Callable[[Any, int], Any]]

noodlelab.register_type(py_type, name, color=None, description='')[source]

Map a Python type (or its qualified name) to a socket type.

Parameters:
  • py_type (type | str)

  • name (str)

  • color (str | None)

  • description (str)

Return type:

str

noodlelab.register_unit(newtype, unit)[source]

Give a NewType a unit: its sockets then connect to every type of the same dimension, converted on the link. Returns the NewType.

Parameters:
  • newtype (Any)

  • unit (str)

Return type:

Any

noodlelab.table_preview(columns, rows, *, total_rows=None, summary=None)[source]

A table preview without pandas, e.g. from csv.reader or a database cursor.

Parameters:
  • columns (list[Any])

  • rows (list[list[Any]])

  • total_rows (int | None)

  • summary (str | None)

Return type:

Preview

noodlelab.unit_type(name, unit, base=<class 'float'>)[source]

NewType(name, base) with a unit: Metres = unit_type("Metres", "m").

Parameters:
  • name (str)

  • unit (str)

  • base (Any)

Return type:

Any

noodlelab.warning(message)[source]

Return from a .check function to report a warning (does not block).

Parameters:

message (str)

Return type:

Problem