Skip to content

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 ​

01Read templateBytes from the file system02Workbook.load(bytes)Used in a with block03Set inputsset_number / set_formula04recalc()Evaluates dependentformulas05Validateget_value on the checkedcells06save() or raisePass: write bytes. Fail: log,raise01Read templateBytes from the file system02Workbook.load(bytes)Used in a with block03Set inputsset_number / set_formula04recalc()Evaluates dependent formulas05Validateget_value on the checked cells06save() or raisePass: write bytes. Fail: log, raise
The with block releases the engine handle on every path, including the one that raises.

The with block around the Workbook releases the engine handle on exit, including when the failure branch raises.

Example job ​

python
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 ​

python
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:

sh
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.txt

Running 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 ​

python
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)
    raise

Fit check ​

WorkflowRecommended runtime
Cron-driven nightly reportsPython
Jupyter / Colab notebooksPython
Browser-local recalculationWASM
Node service with large workbook throughputNative Node
Shell-driven CI snapshotsCLI