Writing a plugin

noodlelab new-plugin thermo     # scaffolds noodlelab-thermo/
cd noodlelab-thermo
git init && git add -A && git commit -m "First version"
uv pip install -e .             # into the environment noodlelab runs in
noodlelab check-plugin .        # the standard: manifest, imports, permissions, nodes, examples

The scaffold is a complete plugin: pyproject.toml (the version comes from git tags), the manifest, two nodes, an example graph, a test, and a release workflow that checks it and publishes signed wheels.

Nodes

A node is a type-hinted function with @node. The hints decide its sockets, units and widgets, and its docstring is the help text people and agents read:

from typing import Annotated

from noodlelab.plugin import Param, Quantity, node


@node(category="Thermodynamics")
def isentropic_efficiency(
    h_in: Quantity["kJ/kg"],
    h_out: Quantity["kJ/kg"],
    h_out_isentropic: Quantity["kJ/kg"],
) -> float:
    """Efficiency of a turbine stage: actual over ideal enthalpy drop."""
    return float((h_in - h_out) / (h_in - h_out_isentropic))

Uncertainty propagates through a node without any code in it. A node never changes its inputs in place: arrays arrive read-only, tables and lists as copies of their own.

The public API

Import noodlelab only through noodlelab.plugin:

Module

What

noodlelab.plugin

node, Param, Quantity, RunContext, FileRef, Probe, Uncertain, Check, warning, error, warn, register_*, require

noodlelab.plugin.units

the unit registry (ureg), is_quantity, magnitude, parse…

noodlelab.plugin.uncertainty

making and reading values with uncertainties

noodlelab.plugin.about

units and descriptions of table columns

noodlelab.plugin.tables

columns as arrays, table attributes

noodlelab.plugin.expr

checking expressions typed into widgets before evaluating them

noodlelab.plugin.constants, .figures, .studies, .math

constants, saving figures, parameter studies, Typst maths

warning and error are what a node’s .check returns before a run; warn("...") is for what a node finds while it runs (a matrix close to singular, a fit the data cannot pin down): the run goes on, and the warning is shown on the node and kept in the run’s provenance. The executor also warns by itself when a node returns a value that is not a number (NaN or infinite). A node that judges its result returns a Check among its outputs (see the Checks pack): it is reported like Expect Value’s, without stopping the run.

Everything else in noodlelab is internal and may change in any release. noodlelab.plugin.API_VERSION is the version of this contract; a plugin states the one it was written for in its manifest, and noodlelab refuses, before running any of its code, a plugin written for a version it does not speak.

To use another plugin’s helpers, import its public (not _-prefixed) modules and declare it in requires. Optional dependencies are checked at import with noodlelab.plugin.require("noodlelab-thermo[steam]", "CoolProp"), which fails with the install command instead of an ImportError deep in a run.

The manifest

src/<package>/noodlelab-plugin.toml, shipped inside the package:

name = "thermo"                    # = the entry point name = the node id prefix
title = "Thermodynamics"
description = "Steam tables, cycles and heat exchangers"
api = 1                            # the noodlelab.plugin API it was written for
requires = { maths = ">=1.2,<2" }  # other plugins, with version ranges ("*": any)
permissions = []                   # see below
examples = ["examples/rankine.graph.json"]
templates = ["reports/datasheet"]  # report templates: folders with template.typ
homepage = "https://github.com/you/noodlelab-thermo"
license = "MIT"

The version is not in the manifest: it comes from the git tag.

Permissions declare what the plugin does besides computing on its inputs and reading and writing files in the workspace: network, subprocess, filesystem:home, filesystem:any, native (ctypes/cffi) and env. check-plugin and the index read the code and refuse a version that does more than it declares, and admins decide whether to grant them.

Versions

Plugins follow semantic versioning:

  • major (2.0.0): a node id, socket, unit or meaning changed, so saved graphs may break;

  • minor (1.3.0): new nodes or options;

  • patch (1.2.1): fixes.

A saved graph records the plugins it uses as >=<saved version>,<next major>. When a node’s results change (a corrected formula), also raise its own @node(version=N): that invalidates cached results computed by the old code.

check-plugin

noodlelab check-plugin . checks everything an index checks, before you publish:

  • the manifest, and that its name is the entry point’s;

  • the plugin API;

  • imports: only noodlelab.plugin, its own package, the plugins it requires and third-party libraries;

  • permissions: nothing it does that it does not declare (and what it hides, like eval or dynamic imports, is pointed out for a reviewer);

  • the nodes, as strict mode judges them;

  • the example graphs, which must run;

  • at a tag, that the package builds that version.