formulon
Recalculate Excel workbooks and formulas from Python, with results checked against real Excel 365.
No Excel install, no Windows, no COM automation: the wheel is pure Python around the engine compiled to WebAssembly, so one py3-none-any wheel runs anywhere wasmtime does.
Known differences from Excel are listed case by case in tests/divergence.yaml, each with a reason and the Excel build it was last verified on.
📖 Documentation · Python guide · API · Batch recalculation
What's inside
- Formula engine — 526 Excel function names, 511 implemented locally; dynamic arrays,
LET/LAMBDA, andREGEX*. Coverage - Workbook I/O — read, recalculate and write
.xlsxand.xlsb, including styles, conditional formatting, tables, pivot tables and print layout. File formats - Workbook editing — cells, sheets, rows and columns, styles, merges, comments, validations, pivot tables, print settings and furigana, through a typed
WorkbookAPI. API - Locale profiles — 28 Excel behavior profiles across fourteen locales on Mac and Windows hosts. Locale profiles
- Excel oracle — formula results are compared bit for bit against goldens captured from Mac Excel 365 in fourteen locales. Oracle testing
Installation
pip install formulon # Python 3.9+
wasmtime is the only runtime dependency; pip picks its platform build. Recalculation is serial in this package, with results identical to the threaded surfaces.
Quick start
import formulon
print(formulon.eval_formula("=SUM(1,2,3)").to_python()) # 6.0
v = formulon.eval_formula("=1/0")
print(v.kind.name, v.error_code) # ERROR 1
Excel errors come back as values; FormulonError is raised only for host-side failures. See Errors.
Recalculate a workbook
from formulon import Workbook
with open("input.xlsx", "rb") as f:
data = f.read()
with Workbook.load(data) as wb:
wb.set_number(0, 0, 0, 42.0) # Sheet1!A1
wb.set_formula(0, 1, 0, "=A1*2") # Sheet1!A2
wb.recalc()
print(wb.get_value(0, 1, 0).to_python()) # 84.0
with open("output.xlsx", "wb") as f:
f.write(wb.save())
Switching locale profiles
New workbooks use win-365-en_US. Formulas are always written with English function names and the stored separators; the profile decides how text is parsed and how results are rendered. Switch it per workbook and recalculate:
with formulon.Workbook.create_default() as wb:
wb.set_formula(0, 0, 0, '=LENB("日本")')
wb.set_formula(0, 1, 0, '=VALUE("1,5")')
wb.set_formula(0, 2, 0, "=ISEVEN(2)")
for profile in ("mac-365-en_US", "mac-365-ja_JP", "mac-365-de_DE"):
wb.set_excel_profile_id(profile)
wb.recalc()
print(profile, [wb.get_display_text(0, row, 0)[0] for row in range(3)])
# mac-365-en_US ['2', '#VALUE!', 'TRUE']
# mac-365-ja_JP ['4', '#VALUE!', 'TRUE']
# mac-365-de_DE ['2', '1,5', 'WAHR']
The ids are {mac,win}-365-{ja_JP,en_US,de_DE,fr_FR,zh_CN,ko_KR,th_TH,es_ES,es_MX,pt_BR,ru_RU,zh_TW,it_IT,nl_NL}. Every mac-* profile and win-365-ja_JP is measured against Excel; the other win-* profiles are estimated from the Mac measurements. On a mac-* profile, text comparison, sorting, lookups and grouping follow that locale's measured Mac Excel collation; win-* profiles add the Windows word-sort rules measured on Windows Excel 365 ja-JP to that locale's collation. The profile is not saved into the file, so store the id your application targets and apply it again after loading.
For a new workbook that matches what Excel creates in a locale, use Workbook.create_for_profile(id) instead of create_default: it selects the profile and also sets the body and phonetic fonts, the localized Normal style and first sheet names, and the page margins Excel uses there.
Non-goals
VBA execution, legacy .xls, chart rendering, Power Query / DAX, pivot cache refresh from source data, live external connections, and a spreadsheet UI are permanently out of scope. See Non-goals.
License
Apache License 2.0. See also NOTICE.
Metadata
Release files for formulon 0.15.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| formulon-0.15.2-py3-none-any.whl | Python 3 | none | any | Details |
Release files / formulon-0.15.2-py3-none-any.whl
| Download URL | formulon-0.15.2-py3-none-any.whl |
|---|---|
| Size | 1.4 MB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
06e72dfe6e4b08ab379719baa0314340566b7f47845516c96645e654e68f0d0c
|
|
BLAKE2b-256 checksum How to use checksums |
d01a423282439974ef7e169aef44c595370651dc7212021d3023e14b40c7790b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 13, 2026.
Transparency log