Skip to main content

Embeddable spreadsheet engine — parse, evaluate & mutate Excel workbooks at native speed

Project description

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

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}")

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
recalculate_file(path: str, output: str | None = None) -> dict

Core classes

  • Workbook — create, load, evaluate, undo/redo. Supports from_path() and load_path() class methods.
  • 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

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.

Project details


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.5.1.tar.gz (973.3 kB view details)

Uploaded Source

Built Distributions

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

formualizer-0.5.1-cp310-abi3-win_amd64.whl (5.6 MB view details)

Uploaded CPython 3.10+Windows x86-64

formualizer-0.5.1-cp310-abi3-musllinux_1_2_x86_64.whl (6.9 MB view details)

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

formualizer-0.5.1-cp310-abi3-musllinux_1_2_aarch64.whl (6.7 MB view details)

Uploaded CPython 3.10+musllinux: musl 1.2+ ARM64

formualizer-0.5.1-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (6.5 MB view details)

Uploaded CPython 3.10+manylinux: glibc 2.17+ ARM64

formualizer-0.5.1-cp310-abi3-macosx_11_0_arm64.whl (6.0 MB view details)

Uploaded CPython 3.10+macOS 11.0+ ARM64

formualizer-0.5.1-cp310-abi3-macosx_10_12_x86_64.whl (6.1 MB view details)

Uploaded CPython 3.10+macOS 10.12+ x86-64

formualizer-0.5.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (6.7 MB view details)

Uploaded CPython 3.8manylinux: glibc 2.17+ x86-64

File details

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

File metadata

  • Download URL: formualizer-0.5.1.tar.gz
  • Upload date:
  • Size: 973.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.12 {"installer":{"name":"uv","version":"0.10.12","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.5.1.tar.gz
Algorithm Hash digest
SHA256 b3f39e9d51ee4bce3814e70ed9984ef9f1a0756f16ff22403b68dc6476b144d1
MD5 d5b9e960f4fd6c3768902b2d1f1f2bc6
BLAKE2b-256 f7b5651acbaf0dd055fc269d97cdf38d730ab1293d51661ad109c86c5650eeaa

See more details on using hashes here.

File details

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

File metadata

  • Download URL: formualizer-0.5.1-cp310-abi3-win_amd64.whl
  • Upload date:
  • Size: 5.6 MB
  • Tags: CPython 3.10+, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.12 {"installer":{"name":"uv","version":"0.10.12","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.5.1-cp310-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 8275af9bec6e713a6231e4e62423db2118bd17cc2aa99a897b0519dd726429d6
MD5 ec6d00d7d7a1abd09ac42f406a07dc69
BLAKE2b-256 e507836038ff4e6bbc7ba70169e9f94889e44139c2358206114ea3bdd2c0d988

See more details on using hashes here.

File details

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

File metadata

  • Download URL: formualizer-0.5.1-cp310-abi3-musllinux_1_2_x86_64.whl
  • Upload date:
  • Size: 6.9 MB
  • Tags: CPython 3.10+, musllinux: musl 1.2+ x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.12 {"installer":{"name":"uv","version":"0.10.12","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.5.1-cp310-abi3-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 717e4c09f8d91cf316add4f972d235762b9efd5acbf37610901a8573b2a526d6
MD5 5e47b3b16bb2a0eac52fc39be8ff6719
BLAKE2b-256 e0931c3d9264b70080e68c042c7045ebc8afa31d0093a4b84de1ae023380ff0a

See more details on using hashes here.

File details

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

File metadata

  • Download URL: formualizer-0.5.1-cp310-abi3-musllinux_1_2_aarch64.whl
  • Upload date:
  • Size: 6.7 MB
  • Tags: CPython 3.10+, musllinux: musl 1.2+ ARM64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.12 {"installer":{"name":"uv","version":"0.10.12","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.5.1-cp310-abi3-musllinux_1_2_aarch64.whl
Algorithm Hash digest
SHA256 130c8ec12a0902fe1854a7e6d18a3a52b24867d7075c5beda4443fef489b06bb
MD5 a12fb724290dd50004b20701c4952554
BLAKE2b-256 868ce3af3a07f69c0dc3f8c3845062bd881867fd06ab2fc61638206a56333606

See more details on using hashes here.

File details

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

File metadata

  • Download URL: formualizer-0.5.1-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
  • Upload date:
  • Size: 6.5 MB
  • Tags: CPython 3.10+, manylinux: glibc 2.17+ ARM64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.12 {"installer":{"name":"uv","version":"0.10.12","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.5.1-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 396e476c163f5c75a3510ccef16f3a5b63dba9d1b71c28aa1469758a81700553
MD5 40471739f161aaf2188a1f6aac3ee640
BLAKE2b-256 37f3fb48c57c7347731f283af3e43abe484996501c735175d0e46fef92b12e26

See more details on using hashes here.

File details

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

File metadata

  • Download URL: formualizer-0.5.1-cp310-abi3-macosx_11_0_arm64.whl
  • Upload date:
  • Size: 6.0 MB
  • Tags: CPython 3.10+, macOS 11.0+ ARM64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.12 {"installer":{"name":"uv","version":"0.10.12","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.5.1-cp310-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 0bfb54b844172ff0d0dbc6344c4c0a82c0a859c406ead065d12fd7ce59fdb118
MD5 a51f2006502304ced761548c3142bd20
BLAKE2b-256 e81776e5c51822a3c108bc9998b567043173e5398bdd0eb008f90971e4ab50b5

See more details on using hashes here.

File details

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

File metadata

  • Download URL: formualizer-0.5.1-cp310-abi3-macosx_10_12_x86_64.whl
  • Upload date:
  • Size: 6.1 MB
  • Tags: CPython 3.10+, macOS 10.12+ x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.12 {"installer":{"name":"uv","version":"0.10.12","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.5.1-cp310-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 85d679a3eacbd43cf08ed1e84cb6ef55db34be20efdc9a089a815f679280a853
MD5 6ee01a31b19f6fc285578261d21b68df
BLAKE2b-256 2f25405be679c9b36cddbe88ec32e72189444f1c4e8f6dcceb45b470052c1869

See more details on using hashes here.

File details

Details for the file formualizer-0.5.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

  • Download URL: formualizer-0.5.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
  • Upload date:
  • Size: 6.7 MB
  • Tags: CPython 3.8, manylinux: glibc 2.17+ x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.12 {"installer":{"name":"uv","version":"0.10.12","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.5.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 7e405d6e27fd9bd68bba6b23e79a0dd808fbacfa2f159cb49d56434f8be28d83
MD5 a794abdec4709f7d48506f94c35ef251
BLAKE2b-256 c7dbd53df6f7c2127d1b586bd81a03559df8c1b2ca35c7245c45b9c7a03fefd9

See more details on using hashes here.

Supported by

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