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.4.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.4-cp310-abi3-win_amd64.whl (7.4 MB view details)

Uploaded CPython 3.10+Windows x86-64

formualizer-0.8.4-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.4-cp310-abi3-musllinux_1_2_aarch64.whl (7.7 MB view details)

Uploaded CPython 3.10+musllinux: musl 1.2+ ARM64

formualizer-0.8.4-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (7.9 MB view details)

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

formualizer-0.8.4-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.4-cp310-abi3-macosx_11_0_arm64.whl (7.2 MB view details)

Uploaded CPython 3.10+macOS 11.0+ ARM64

formualizer-0.8.4-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.4.tar.gz.

File metadata

  • Download URL: formualizer-0.8.4.tar.gz
  • Upload date:
  • Size: 2.0 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","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.4.tar.gz
Algorithm Hash digest
SHA256 0fe3aa59afbf93a3dfec9a5034ee8210801ba8a3045a96b6acf3795ca5f7065a
MD5 8e5891a1539a5cd38b76c6aa4db2fa12
BLAKE2b-256 0f5a9b13be108101a3664e82dac0ddd8f2ea69990460c26582405244795e897b

See more details on using hashes here.

File details

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

File metadata

  • Download URL: formualizer-0.8.4-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.5 {"installer":{"name":"uv","version":"0.12.5","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.4-cp310-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 5c7e12719a922f67fde50f9ffec2fbcb1092ec866e5a370eb91505d104aa4be5
MD5 1d0e2a03d54d60f1b7390ec63fbac252
BLAKE2b-256 59fe1e71cdc9e747a66793d4fe9ae93f8898be6b2459d842e5dfb72335044bb4

See more details on using hashes here.

File details

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

File metadata

  • Download URL: formualizer-0.8.4-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.5 {"installer":{"name":"uv","version":"0.12.5","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.4-cp310-abi3-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 632a920c949d5f45e19477c61a8dba2ef4ef2b677bb82be892ad4a3c06996c0b
MD5 bc605ccec87c63b3c7f8f432a37f430a
BLAKE2b-256 83d5557dc9683b36db24b76b126a3fba30008c3de4ddff3d2487d9e4cbf05120

See more details on using hashes here.

File details

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

File metadata

  • Download URL: formualizer-0.8.4-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.5 {"installer":{"name":"uv","version":"0.12.5","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.4-cp310-abi3-musllinux_1_2_aarch64.whl
Algorithm Hash digest
SHA256 04a4b86df34c69567c63d2167f0d9b9d6e58f13a367a4efd1697b898e94374d5
MD5 ac8d808ad0266dc79e00c710505de4fd
BLAKE2b-256 5b06f6f93b5abd767b49f00fc7cd4d6ee7da1e6753548b8494258919a4db10fa

See more details on using hashes here.

File details

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

File metadata

  • Download URL: formualizer-0.8.4-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
  • Upload date:
  • Size: 7.9 MB
  • Tags: CPython 3.10+, manylinux: glibc 2.17+ x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","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.4-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 5f1046e142308b72b7fa12839c2c02dd3699995ee0f81d9b0f946047be689b13
MD5 a219706631c6875774048a6b4a330a56
BLAKE2b-256 e956fa50e8ec2864e74d36a805abbd7d92aeeb00482634c4ddd54e2288853752

See more details on using hashes here.

File details

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

File metadata

  • Download URL: formualizer-0.8.4-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.5 {"installer":{"name":"uv","version":"0.12.5","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.4-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 4c75391086b6f9df7d38d05c7ebe50a87440cb53cb06f555fab6925b9c963483
MD5 65c1fdbd7257b282bf8a1c3ae6bf3959
BLAKE2b-256 094a1979d3cb29888becdd24b74ab33f29564b9f01fdcaafea2a15a7e4d2a107

See more details on using hashes here.

File details

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

File metadata

  • Download URL: formualizer-0.8.4-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.5 {"installer":{"name":"uv","version":"0.12.5","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.4-cp310-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 7e10f2afddc91818fa56def93c63a86a0baa3ed6b7ab74f8b974f57381a010e4
MD5 d5708b8ebe879a288b99c71e7310d42c
BLAKE2b-256 cb1d3b478706bbc74fb495f138dffdd7957802a4c88b1bee0141be09a5dbc0d3

See more details on using hashes here.

File details

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

File metadata

  • Download URL: formualizer-0.8.4-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.5 {"installer":{"name":"uv","version":"0.12.5","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.4-cp310-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 5792bc67507a95f3146ece83a0f223bde31789dc94ee6ac595896183a702167e
MD5 2a66afbd58432ca0f70b5af0edf3bd63
BLAKE2b-256 8f5fe165d64ea67c84995ed1a33b0557fd8d88e167ba66bc666a07f63219e1d5

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.8.4 This release

8 files

0.8.3

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