Skip to main content

Formualizer for Python

Arrow Powered PyPI License: MIT/Apache-2.0 Documentation

Formualizer banner


Parse, evaluate, and mutate Excel workbooks at native speed from Python.

A Rust-powered spreadsheet engine with 320+ Excel-compatible functions, exposed through a clean Pythonic API. Tokenize formulas, walk ASTs, evaluate workbooks, and use SheetPort to treat spreadsheets as typed APIs.

Installation

pip install formualizer

Prebuilt wheels are available for Python 3.10-3.13 on Linux, macOS, and Windows. No Rust toolchain required.

Documentation

Full documentation at formualizer.dev:

Quick start

Evaluate a workbook

import formualizer as fz

wb = fz.Workbook()
s = wb.sheet("Sheet1")

s.set_value(1, 1, fz.LiteralValue.number(1000.0))  # A1: principal
s.set_value(2, 1, fz.LiteralValue.number(0.05))  # A2: annual rate
s.set_value(3, 1, fz.LiteralValue.number(12.0))  # A3: periods

s.set_formula(1, 2, "=PMT(A2/12, A3, -A1)")
print(wb.evaluate_cell("Sheet1", 1, 2))  # ~85.61

Load an XLSX and evaluate

import formualizer as fz

wb = fz.load_workbook("financial_model.xlsx", strategy="eager_all")
print(wb.evaluate_cell("Summary", 1, 2))

Load and save XLSX bytes

import formualizer as fz

payload = open("financial_model.xlsx", "rb").read()
wb = fz.load_workbook_bytes(payload)
print(wb.evaluate_cell("Summary", 1, 2))

out = wb.to_xlsx_bytes()

Native Python builds use calamine by default for both path-based and byte-oriented XLSX loading. Pyodide currently defaults to umya, which also remains available explicitly on native builds. XLSX byte export uses umya because Calamine is read-only.

Recalculate XLSX cached values (writeback)

import formualizer as fz

# in-place
summary = fz.recalculate_file("financial_model.xlsx")
print(summary["status"], summary["evaluated"], summary["errors"])

# write to a new file
summary = fz.recalculate_file(
    "financial_model.xlsx", output="financial_model.recalc.xlsx"
)

Formula text is preserved. Cached-value typing follows the active umya-spreadsheet implementation.

Parse and analyze formulas

from formualizer import parse
from formualizer.visitor import collect_references, collect_function_names

ast = parse("=SUMIFS(Revenue,Region,A1,Year,B1)")
print(ast.pretty())  # indented AST tree
print(ast.to_formula())  # canonical Excel string
print(collect_references(ast))  # [Revenue, Region, A1, Year, B1]
print(collect_function_names(ast))  # ['SUMIFS']

Key features

Capability Description
Tokenization Break formulas into structured Token objects with byte spans and operator metadata
Parsing Produce a rich AST with reference normalization, source tracking, and 64-bit structural fingerprints
320+ built-in functions Math, text, lookup (XLOOKUP, VLOOKUP), date/time, financial, statistics, database, engineering
Workbook evaluation Set values and formulas, evaluate cells/ranges, load XLSX/CSV/JSON
XLSX cache writeback recalculate_file(path, output=None) recalculates formulas and writes cached values back
Batch operations set_values_batch / set_formulas_batch for efficient bulk updates
Undo / redo Optional changelog with automatic action grouping — single edits are individually undoable
Evaluation planning Inspect the dependency graph and evaluation schedule before computing
SheetPort Treat spreadsheets as typed functions with YAML manifests, schema validation, and batch scenarios
Deterministic mode Inject clock, timezone, and RNG seed for reproducible evaluation
Visitor utilities walk_ast, collect_references, collect_function_names for ergonomic tree traversal
Rich errors Typed TokenizerError / ParserError / ExcelEvaluationError with position info

Workbook evaluation

import formualizer as fz

wb = fz.Workbook()
s = wb.sheet("Data")

# Set values and formulas
s.set_value(1, 1, fz.LiteralValue.number(100.0))
s.set_value(2, 1, fz.LiteralValue.number(200.0))
s.set_value(3, 1, fz.LiteralValue.number(300.0))
s.set_formula(4, 1, "=SUM(A1:A3)")
s.set_formula(4, 2, "=AVERAGE(A1:A3)")

print(wb.evaluate_cell("Data", 4, 1))  # 600.0
print(wb.evaluate_cell("Data", 4, 2))  # 200.0

Custom functions

Register workbook-local callbacks without forking Formualizer:

import formualizer as fz

wb = fz.Workbook(mode=fz.WorkbookMode.Ephemeral)
wb.add_sheet("Sheet1")

wb.register_function(
    "py_add",
    lambda a, b: a + b,
    min_args=2,
    max_args=2,
)

wb.set_formula("Sheet1", 1, 1, "=PY_ADD(20,22)")
print(wb.evaluate_cell("Sheet1", 1, 1))  # 42
print(wb.list_functions())
wb.unregister_function("py_add")

Key semantics:

  • Names are case-insensitive and stored canonically (py_add -> PY_ADD).
  • Custom functions are workbook-local and take precedence over global built-ins.
  • Built-in override is disabled by default; set allow_override_builtin=True to opt in.
  • Args are passed by value; range inputs arrive as nested Python lists.
  • Return Python primitives, datetime/date/time/timedelta, dict error objects, or nested lists for array spill output.
  • Python callback exceptions are sanitized and mapped to #VALUE!.

Runnable example: python bindings/python/examples/custom_function_registration.py

Batch operations

# Bulk-set values (auto-grouped as one undo step when changelog is enabled)
s.set_values_batch(
    1,
    1,
    3,
    2,
    [
        [fz.LiteralValue.number(10.0), fz.LiteralValue.number(20.0)],
        [fz.LiteralValue.number(30.0), fz.LiteralValue.number(40.0)],
        [fz.LiteralValue.number(50.0), fz.LiteralValue.number(60.0)],
    ],
)

Undo / redo

The changelog is opt-in. Once enabled, every edit is tracked:

wb.set_changelog_enabled(True)

s.set_value(1, 1, fz.LiteralValue.number(10.0))
s.set_value(1, 1, fz.LiteralValue.number(20.0))
wb.undo()  # back to 10
wb.redo()  # back to 20

# Batch methods are auto-grouped as one undo step.
# For manual grouping of multiple calls:
wb.begin_action("update prices")
s.set_value(1, 1, fz.LiteralValue.number(100.0))
s.set_value(2, 1, fz.LiteralValue.number(200.0))
wb.end_action()
wb.undo()  # reverts both values at once

Evaluation planning

Inspect what the engine will compute before running:

plan = wb.get_eval_plan([("Sheet1", 1, 2)])
print(f"Vertices to evaluate: {plan.total_vertices_to_evaluate}")
print(f"Parallel layers: {plan.estimated_parallel_layers}")
for layer in plan.layers:
    print(f"  Layer: {layer.vertex_count} vertices, parallel={layer.parallel_eligible}")

# By default this will build deferred workbook graphs if needed.
# Disable that behavior if you want planning to fail instead of mutating workbook state.
wb.get_eval_plan([("Sheet1", 1, 2)], build_graph_if_needed=False)

SheetPort: spreadsheets as typed APIs

Define a YAML manifest to treat a spreadsheet as a typed function with validated inputs/outputs:

from formualizer import SheetPortSession, Workbook

manifest_yaml = """
spec: fio
spec_version: "0.3.0"
manifest:
  id: pricing-model
  name: Pricing Model
  workbook:
    uri: memory://pricing.xlsx
    locale: en-US
    date_system: 1900
ports:
  - id: base_price
    dir: in
    shape: scalar
    location: { a1: Inputs!A1 }
    schema: { type: number }
  - id: final_price
    dir: out
    shape: scalar
    location: { a1: Outputs!A1 }
    schema: { type: number }
"""

wb = Workbook()
wb.add_sheet("Inputs")
wb.add_sheet("Outputs")
wb.set_formula("Outputs", 1, 1, "=Inputs!A1*1.2")

session = SheetPortSession.from_manifest_yaml(manifest_yaml, wb)
session.write_inputs({"base_price": 100.0})
result = session.evaluate_once(freeze_volatile=True)
print(result["final_price"])  # 120.0

API reference

Top-level functions

tokenize(formula: str, dialect: FormulaDialect = None) -> Tokenizer
parse(formula: str, dialect: FormulaDialect = None) -> ASTNode
load_workbook(path: str, strategy: str = None) -> Workbook
load_workbook_bytes(data: bytes, strategy: str = None, backend: str | None = None) -> Workbook
recalculate_file(path: str, output: str | None = None) -> dict

Core classes

  • Workbook — create, load, evaluate, undo/redo. Supports from_path(), from_bytes(), load_path(), and to_xlsx_bytes().
  • Sheet — per-sheet facade for set_value, set_formula, get_cell, batch operations.
  • LiteralValue — typed values: .int(), .number(), .text(), .boolean(), .date(), .empty(), .error(), .array().
  • Tokenizer — iterable token sequence with .render() and .tokens.
  • ASTNode.pretty(), .to_formula(), .fingerprint(), .children(), .walk_refs().
  • CellRef / RangeRef / TableRef / NamedRangeRef — typed references.
  • SheetPortSession — bind manifests to workbooks, read/write typed ports, evaluate.
  • EvaluationConfig — tune parallel evaluation, warmup, range limits, date systems.

Visitor helpers (formualizer.visitor)

walk_ast(node, visitor_fn)  # DFS with VisitControl (CONTINUE/SKIP/STOP)
collect_references(node)  # -> list[ReferenceLike]
collect_function_names(node)  # -> list[str]
collect_nodes_by_type(node, "Function")  # -> list[ASTNode]

Full type stubs are included in the package (.pyi files) for IDE autocompletion and mypy.


Building from source

Requires Rust >= 1.70 and maturin:

pip install maturin
cd bindings/python
maturin develop            # debug build
maturin develop --release  # optimized build

Using in Pyodide (browser / WebAssembly)

formualizer ships a Pyodide-tagged wheel (*-pyodide_<abi>_wasm32.whl) alongside the native wheels on PyPI. Inside a Pyodide runtime:

import micropip

await micropip.install("formualizer")

import formualizer as fz

wb = fz.Workbook()
wb.add_sheet("Sheet1")
wb.set_value("Sheet1", 1, 1, 20)
wb.set_value("Sheet1", 2, 1, 22)
wb.set_formula("Sheet1", 1, 2, "=SUM(A1:A2)")
wb.evaluate_cell("Sheet1", 1, 2)  # -> 42.0

Supported Pyodide versions: 0.29.x (ABI pyodide_2025_0). Later minors may require a new wheel — check the PyPI release matrix for your target Pyodide version.

Pyodide-specific behavior:

  • EvaluationConfig() and Workbook() default enable_parallel = False on sys.platform == "emscripten" (Pyodide has no threads). You can still opt in, but it falls back to single-threaded execution.
  • Native XLSX byte loading (Workbook.from_bytes, load_workbook_bytes) defaults to calamine; Pyodide defaults to umya. XLSX byte export uses umya on all platforms.
  • Python UDFs registered via Workbook.register_function work identically to native; single-cell refs arrive as scalars (Excel-native semantics).

Building a Pyodide wheel from source

For local development or targeting a Pyodide version that isn't on PyPI:

./scripts/build-pyodide-wheel.sh
./scripts/smoke-pyodide-wheel.sh dist/pyodide/*-pyodide_*_wasm32.whl

The build script derives Python, ABI, Emscripten, and Rust toolchain from pyodide config (no hardcoded versions), installs Pyodide's custom wasm-EH Rust sysroot over the stock rustup target, and retags the output wheel to the platform tag Pyodide's micropip expects.

Testing

pip install formualizer[dev]
pytest bindings/python/tests
ruff check bindings/python
mypy bindings/python/formualizer

Workspace layout

formualizer/
  crates/                    # Rust core (parse, eval, workbook, sheetport)
  bindings/python/
    formualizer/             # Python package (helpers, visitor, type stubs)
    src/                     # PyO3 bridge (Rust -> Python)

The Python wheel links directly against the Rust crates — there is no runtime FFI overhead beyond the initial C-to-Rust boundary.

License

Dual-licensed under MIT or Apache-2.0, at your option.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

formualizer-0.8.3.tar.gz (2.0 MB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

formualizer-0.8.3-cp310-abi3-win_amd64.whl (7.4 MB view details)

Uploaded CPython 3.10+Windows x86-64

formualizer-0.8.3-cp310-abi3-musllinux_1_2_x86_64.whl (8.1 MB view details)

Uploaded CPython 3.10+musllinux: musl 1.2+ x86-64

formualizer-0.8.3-cp310-abi3-musllinux_1_2_aarch64.whl (7.7 MB view details)

Uploaded CPython 3.10+musllinux: musl 1.2+ ARM64

formualizer-0.8.3-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (7.8 MB view details)

Uploaded CPython 3.10+manylinux: glibc 2.17+ x86-64

formualizer-0.8.3-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (7.5 MB view details)

Uploaded CPython 3.10+manylinux: glibc 2.17+ ARM64

formualizer-0.8.3-cp310-abi3-macosx_11_0_arm64.whl (7.2 MB view details)

Uploaded CPython 3.10+macOS 11.0+ ARM64

formualizer-0.8.3-cp310-abi3-macosx_10_12_x86_64.whl (7.5 MB view details)

Uploaded CPython 3.10+macOS 10.12+ x86-64

File details

Details for the file formualizer-0.8.3.tar.gz.

File metadata

  • Download URL: formualizer-0.8.3.tar.gz
  • Upload date:
  • Size: 2.0 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.12.4 {"installer":{"name":"uv","version":"0.12.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for formualizer-0.8.3.tar.gz
Algorithm Hash digest
SHA256 13dc58e1c1d55a389d6929106ef3d5ec3404738b8698c49d030d129bbfe1cfdb
MD5 8a46ff81fdbd9e884551869041129898
BLAKE2b-256 33abeeda4979a2f07171616a0fe65c768814d8bc6618178cdc4621ad72500ce1

See more details on using hashes here.

File details

Details for the file formualizer-0.8.3-cp310-abi3-win_amd64.whl.

File metadata

  • Download URL: formualizer-0.8.3-cp310-abi3-win_amd64.whl
  • Upload date:
  • Size: 7.4 MB
  • Tags: CPython 3.10+, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.12.4 {"installer":{"name":"uv","version":"0.12.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for formualizer-0.8.3-cp310-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 ec1763ee72a64d83b69c83843b94d460211f37e1d863df899912cb95621c4268
MD5 e3f58b6c3da19c471333c78420b8beb0
BLAKE2b-256 11d1c14a1d1b5dd5bb588a2c75828a398db2e4251a12ea8a6fb9ee63129c6587

See more details on using hashes here.

File details

Details for the file formualizer-0.8.3-cp310-abi3-musllinux_1_2_x86_64.whl.

File metadata

  • Download URL: formualizer-0.8.3-cp310-abi3-musllinux_1_2_x86_64.whl
  • Upload date:
  • Size: 8.1 MB
  • Tags: CPython 3.10+, musllinux: musl 1.2+ x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.12.4 {"installer":{"name":"uv","version":"0.12.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for formualizer-0.8.3-cp310-abi3-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 f9282f7521e7e4e53550cd9fcdc4390779267d393ed6b437982af2df1408b711
MD5 d01c4f996c151f40fbdeba5d45c1f873
BLAKE2b-256 a621b00efa24842bfab3fa826680a4cf3128beebc440cade083f1d643925bcc4

See more details on using hashes here.

File details

Details for the file formualizer-0.8.3-cp310-abi3-musllinux_1_2_aarch64.whl.

File metadata

  • Download URL: formualizer-0.8.3-cp310-abi3-musllinux_1_2_aarch64.whl
  • Upload date:
  • Size: 7.7 MB
  • Tags: CPython 3.10+, musllinux: musl 1.2+ ARM64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.12.4 {"installer":{"name":"uv","version":"0.12.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for formualizer-0.8.3-cp310-abi3-musllinux_1_2_aarch64.whl
Algorithm Hash digest
SHA256 0ce0a18152e7ecd0528b95c62f55bbda893670d2bc5f9d4241e22a85cedbaaf8
MD5 e954de4a33cda527718f6af518ad84b0
BLAKE2b-256 5d215be57dc741fa9912885cec97896fcfe7160164d71be330c75caee300b002

See more details on using hashes here.

File details

Details for the file formualizer-0.8.3-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

  • Download URL: formualizer-0.8.3-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
  • Upload date:
  • Size: 7.8 MB
  • Tags: CPython 3.10+, manylinux: glibc 2.17+ x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.12.4 {"installer":{"name":"uv","version":"0.12.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for formualizer-0.8.3-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 1aa1bc4f9a9e7ff295e8f7e199eff20621769a3e5edb681d9de6b5e2e7ab4b3a
MD5 c308011d42a6be5086dccb3f9732c7a4
BLAKE2b-256 250f59eddf0f5bd14952bcf32948eccff8303b7c3d54fc346d3503254ccd43fc

See more details on using hashes here.

File details

Details for the file formualizer-0.8.3-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

  • Download URL: formualizer-0.8.3-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
  • Upload date:
  • Size: 7.5 MB
  • Tags: CPython 3.10+, manylinux: glibc 2.17+ ARM64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.12.4 {"installer":{"name":"uv","version":"0.12.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for formualizer-0.8.3-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 44c5fa3ff03b4e7710da422fb2bbab6d6a44a57a432e8e1f6a7be9a9004d4e72
MD5 f319f233dc2ac4c05552f91353fe91bb
BLAKE2b-256 8185cd5c9cb2f4b927d511a1d7a3897e818d90d978c48b1decb43d1776949cb0

See more details on using hashes here.

File details

Details for the file formualizer-0.8.3-cp310-abi3-macosx_11_0_arm64.whl.

File metadata

  • Download URL: formualizer-0.8.3-cp310-abi3-macosx_11_0_arm64.whl
  • Upload date:
  • Size: 7.2 MB
  • Tags: CPython 3.10+, macOS 11.0+ ARM64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.12.4 {"installer":{"name":"uv","version":"0.12.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for formualizer-0.8.3-cp310-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 7ae9e94b17c223eb14aef1c9e5d17fbc97b7013a99e8f9f13853a2b6b66a3a24
MD5 6f54f94095c2e351af2bc5032da43e1e
BLAKE2b-256 7d5376988fb1efba79da586ffa056b3e611caec77a589fed77433010b0f43e8e

See more details on using hashes here.

File details

Details for the file formualizer-0.8.3-cp310-abi3-macosx_10_12_x86_64.whl.

File metadata

  • Download URL: formualizer-0.8.3-cp310-abi3-macosx_10_12_x86_64.whl
  • Upload date:
  • Size: 7.5 MB
  • Tags: CPython 3.10+, macOS 10.12+ x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.12.4 {"installer":{"name":"uv","version":"0.12.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for formualizer-0.8.3-cp310-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 e0b62b65d9c8bea7d20320591f5501bdff5db6337167f3a1f0526d79d806602a
MD5 adfb633b16e43a23ef6333dff743b750
BLAKE2b-256 e4f2b508b9c8e210672c3065142fc3e4c7a0905933cc178696e1d916ffb68bb4

See more details on using hashes here.

Release history Release notifications | RSS feed

0.8.4

8 files

This release

0.8.3 This release

8 files

0.8.2

8 files

0.8.1

8 files

0.8.0

8 files

0.7.1

8 files

0.7.0

8 files

0.6.0

8 files

0.5.9

8 files

0.5.8

8 files

0.5.7

8 files

0.5.6

8 files

0.5.5

8 files

0.5.4

8 files

0.5.3

8 files

0.5.2

8 files

0.5.1

8 files

0.5.0

8 files

0.4.3

8 files

0.4.2

8 files

0.4.1

8 files

0.4.0

8 files

0.3.5

8 files

0.3.4

8 files

0.3.3

8 files

0.3.1

2 files

0.1.2

2 files

0.1.1

2 files

0.1.0

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page