Python Batch Recalculation
Use Python when spreadsheet recalculation is part of a scheduled job, notebook, or data pipeline. The same engine that runs in the browser runs here — only the host language changes.
Keep workbook IO at the edges
Load bytes at the start, mutate and recalculate explicitly, and write bytes at the end. Keeping unrelated data-loading code out of that block makes failures easier to attribute and the job easier to test.
Flow
The with block around the Workbook releases the engine handle on exit, including when the failure branch raises.
Example job
from pathlib import Path
from formulon import Workbook
def recalc_report(input_path: Path, output_path: Path, revenue: float) -> None:
with Workbook.load(input_path.read_bytes()) as wb:
wb.set_excel_profile_id("win-365-en_US")
wb.set_pinned_now(2026, 10, 9, 9, 30, 0)
wb.set_number(0, 3, 1, revenue) # B4 on Sheet1
wb.recalc()
output_path.write_bytes(wb.save())
recalc_report(Path("template.xlsx"), Path("report.xlsx"), 125_000.0)The job sets the profile and the clock on every run because neither is saved in the file. Without them, locale-sensitive and date-dependent results can differ between runs. See Locale profiles.
Mutating many cells
from formulon import Workbook, ValueKind
INPUTS = [
(0, 3, 1, 125_000.0), # B4: revenue
(0, 4, 1, 80_000.0), # B5: COGS
(0, 5, 1, 12_000.0), # B6: marketing
]
with Workbook.load(template_bytes) as wb:
wb.set_excel_profile_id("win-365-en_US")
wb.set_pinned_now(2026, 10, 9, 9, 30, 0)
for sheet, row, col, value in INPUTS:
wb.set_number(sheet, row, col, value)
wb.recalc()
margin = wb.get_value(0, 8, 1) # B9: gross margin
if margin.kind is ValueKind.NUMBER and margin.number < 0:
raise RuntimeError(f"Negative margin: {margin.number}")
output_path.write_bytes(wb.save())set_number / set_text / set_formula are the canonical mutators; coordinates are zero-based (sheet, row, col). See Workbook operations.
Validation pattern
Keep a small set of fixture workbooks in your repository:
python jobs/recalc_reports.py
formulon dump --profile win-365-en_US --now 2026-10-09T09:30:00 --values report.xlsx > report.values.txt
git diff --exit-code report.values.txtRunning the job and then snapshotting its output with dump --values catches changes in both the job code and the workbook.
Volatile inputs need handling
NOW, TODAY, RAND, and RANDBETWEEN are volatile. External-service functions such as WEBSERVICE, CUBE functions, and STOCKHISTORY do not perform network I/O; they return a fixed unavailable Excel error. For golden snapshots, replace volatile inputs with fixed values or move them out of the snapshotted range, and record or exclude unavailable-service cells according to formula coverage.
Error handling
from formulon import Workbook, FormulonError, ValueKind
try:
with Workbook.load(blob) as wb:
wb.recalc()
value = wb.get_value(0, 0, 0)
if value.kind is ValueKind.ERROR:
# Cell-level Excel error — surface as data, not as exception
log.warning("cell error: %s", value.error_code)
except FormulonError as e:
# Host failure — unreadable workbook bytes or another engine status error
log.error("formulon host failure: %s", e)
raiseFit check
| Workflow | Recommended runtime |
|---|---|
| Cron-driven nightly reports | Python |
| Jupyter / Colab notebooks | Python |
| Browser-local recalculation | WASM |
| Node service with large workbook throughput | Native Node |
| Shell-driven CI snapshots | CLI |
Read next
- Python API — top-level surface.
- Workbook lifecycle — engine flow behind the script.
- CI workbook regression — pairing Python with snapshots.