Notebook Interface
The wrapper facade API gives generated models analyst-facing names, tables, reports, and scenarios.
The modelwright.notebooks module adds the next layer: pandas-backed helpers that display those
facade views naturally in a live Jupyter kernel.
Install the optional notebook dependency when using these helpers:
python -m pip install 'modelwright[notebook]'
The core package and modelwright.wrappers do not require pandas.
The tracked examples also include real notebook files under examples/notebooks/. They are the
best starting point for users who want a literate, open-and-run walkthrough rather than copying code
from this guide.
When using the tracked source-tree examples from a notebook stored under tmp/notebooks/, add the
repository root to sys.path before importing from examples:
from pathlib import Path
import sys
repo_root = Path.cwd().resolve()
while repo_root != repo_root.parent and not (repo_root / "pyproject.toml").exists():
repo_root = repo_root.parent
if not (repo_root / "pyproject.toml").exists():
raise RuntimeError("Could not find the Modelwright repository root.")
sys.path.insert(0, str(repo_root))
Boundary
Notebook helpers do not edit generated source code and do not recreate a spreadsheet UI. They convert
declared wrapper views into DataFrames so an analyst can inspect model structure, change scenario
inputs, recalculate, and compare results without manually reading raw Sheet!A1 dictionaries.
Minimal Example
Assume a generated model module exposes calculate(inputs=None):
def calculate(inputs=None):
inputs = inputs or {}
base = inputs.get("Inputs!B2", 100)
growth = inputs.get("Inputs!B3", 0.1)
return {
"Summary!B2": base * (1 + growth),
"Summary!C2": base * 2,
"Summary!B3": "ok",
"Summary!C3": base + 5,
}
Declare a facade around the generated model:
from modelwright.wrappers import ModelFacade, cell, report, table
facade = ModelFacade(
generated_model,
cells=[
cell("Inputs!B2", name="base", label="Base volume", role="input", unit="t"),
cell("Inputs!B3", name="growth", label="Growth rate", role="input", unit="fraction"),
cell("Summary!B2", name="projected", label="Projected volume", role="output", unit="t"),
cell("Summary!B3", name="status", label="Status", role="output"),
],
tables=[
table(
"summary_grid",
sheet="Summary",
range_ref="B2:C3",
row_labels=["volume", "status"],
column_labels=["primary", "secondary"],
)
],
reports=[
report("summary", cells=["base", "projected", "status"], tables=["summary_grid"]),
],
)
Render declared inputs and outputs:
from modelwright.notebooks import inputs_frame, outputs_frame
scenario = facade.scenario(name="shock", inputs={"Inputs!B2": 50}).with_input("Inputs!B3", 0.2)
inputs_frame(facade, scenario)
outputs_frame(facade, scenario)
In Jupyter, those calls display tidy DataFrames with names, labels, cell references, roles, units, values, and value-presence flags.
Render Tables
Declared rectangular tables become DataFrames whose visible row and column labels come from the wrapper declaration:
from modelwright.notebooks import table_frame
summary = table_frame(facade, "summary_grid", scenario)
summary
Workbook provenance stays attached to the DataFrame:
summary.attrs["sheet"]
summary.attrs["range_ref"]
summary.attrs["cell_refs"]
Render Reports
Reports return a small mapping with a cell DataFrame and named table DataFrames:
from modelwright.notebooks import report_frames
frames = report_frames(facade, "summary", scenario)
frames["cells"]
frames["tables"]["summary_grid"]
Compare Scenarios
Scenario comparison is the core notebook loop:
from modelwright.notebooks import compare_scenarios_frame
baseline = facade.scenario(name="baseline", inputs={"Inputs!B2": 100, "Inputs!B3": 0.1})
shock = baseline.with_input("Inputs!B2", 120)
compare_scenarios_frame(facade, baseline, shock)
The comparison DataFrame includes declared name, label, cell reference, baseline value, scenario value, absolute change, percent change where numeric and meaningful, unit, role, and description. Text comparisons and zero-baseline percent changes remain explicit rather than raising.
Scenario Inputs
Use scenario_frame to inspect the exact overrides being sent to the generated model:
from modelwright.notebooks import scenario_frame
scenario_frame(shock)
Alpha Limits
The notebook helpers are provisional. They are intended to make wrapped generated models usable in a live Python kernel, not to promise a complete end-user application.
Current non-goals:
full spreadsheet UI recreation;
dashboard server or widget framework;
automatic recovery of workbook semantic names or table meanings;
visual formatting or workbook editing;
Excel-backed recalculation equivalence;
stable public API compatibility before real notebook workflows harden the design.