Five production workbook recipes¶
These recipes start with a copy of a workbook you control. Keep the original immutable, write each result to a new path, and review the compatibility matrix and known limitations before a production rollout.
1. Preserve a macro-enabled template while changing cells¶
Use modify mode when the requested change is narrow and unrelated workbook parts must survive.
from pathlib import Path
from zipfile import ZipFile
from wolfxl import load_workbook
source = Path("template.xlsm")
output = Path("template-updated.xlsm")
with ZipFile(source) as archive:
original_vba = archive.read("xl/vbaProject.bin")
workbook = load_workbook(source, modify=True, keep_vba=True, keep_links=True)
workbook["Inputs"]["B4"] = 42
workbook.save(output)
workbook.close()
with ZipFile(output) as archive:
assert archive.read("xl/vbaProject.bin") == original_vba
assert source.read_bytes() != b""
assert output.read_bytes() != source.read_bytes()
This checks the requested output exists and the VBA project bytes remain identical. Add checks for every drawing, control, link, or package part that forms part of your workbook contract.
Do not use this recipe to author or execute VBA.
2. Modify a large workbook without rebuilding the source file¶
Keep the source immutable and time only the operation your service performs.
from pathlib import Path
from time import perf_counter
from wolfxl import load_workbook
source = Path("large-report.xlsx")
output = Path("large-report-updated.xlsx")
source_digest = source.read_bytes()
started = perf_counter()
workbook = load_workbook(source, modify=True)
workbook["Status"]["B2"] = "Approved"
workbook.save(output)
workbook.close()
elapsed_seconds = perf_counter() - started
assert source.read_bytes() == source_digest
reloaded = load_workbook(output)
assert reloaded["Status"]["B2"].value == "Approved"
reloaded.close()
print({"elapsed_seconds": elapsed_seconds, "output_bytes": output.stat().st_size})
Record the workbook digest, Python and WolfXL versions, operating system, hardware, cold or warm state, and raw timing output before using the result in a capacity claim.
3. Verify formulas, charts, and pivots after saving¶
Formula text, drawing parts, and pivot parts are separate package concerns. Check each one explicitly.
from pathlib import Path
from zipfile import ZipFile
from openpyxl import load_workbook as openpyxl_load_workbook
from wolfxl import load_workbook
source = Path("dashboard.xlsx")
output = Path("dashboard-updated.xlsx")
workbook = load_workbook(source, modify=True)
workbook["Inputs"]["B2"] = 120_000
values = workbook.calculate()
assert "Summary!B12" in values
workbook.save(output)
workbook.close()
cross_read = openpyxl_load_workbook(output, data_only=False, read_only=False)
assert cross_read["Summary"]["B12"].value.startswith("=")
assert len(cross_read["Summary"]._charts) > 0 # openpyxl exposes loaded charts here
cross_read.close()
with ZipFile(output) as archive:
names = set(archive.namelist())
assert any(name.startswith("xl/charts/chart") for name in names)
assert any(name.startswith("xl/pivotTables/pivotTable") for name in names)
assert any(name.startswith("xl/pivotCache/") for name in names)
Replace the broad presence checks with exact expected part names and values for your workbook. A present chart or pivot part does not prove visual parity.
4. Benchmark one real openpyxl pipeline¶
Measure equivalent code paths and validate the same observable result before comparing elapsed time.
from pathlib import Path
from statistics import median
from tempfile import TemporaryDirectory
from time import perf_counter
import openpyxl
import wolfxl
SOURCE = Path("representative.xlsx")
def run(engine, output: Path) -> float:
started = perf_counter()
workbook = engine.load_workbook(SOURCE)
total = sum(
value or 0
for row in workbook["Data"].iter_rows(min_col=4, max_col=4, values_only=True)
for value in row
if isinstance(value, (int, float))
)
workbook.close()
output.write_text(str(total), encoding="utf-8")
return perf_counter() - started
with TemporaryDirectory() as directory:
root = Path(directory)
wolfxl_times = [run(wolfxl, root / f"wolfxl-{index}.txt") for index in range(7)]
openpyxl_times = [run(openpyxl, root / f"openpyxl-{index}.txt") for index in range(7)]
assert (root / "wolfxl-0.txt").read_text() == (root / "openpyxl-0.txt").read_text()
print({
"wolfxl_median_seconds": median(wolfxl_times),
"openpyxl_median_seconds": median(openpyxl_times),
})
Run this in a clean environment and record exact package versions, source digest, hardware, cache policy, iteration count, and raw samples. The order above can favor the second engine through warmed file caches; alternate or isolate engine runs when that matters.
5. Migrate one load_workbook workflow with a rollback path¶
Keep the import decision behind one application-owned module so the old path remains available during qualification.
# excel_engine.py
import os
if os.environ.get("USE_WOLFXL") == "1":
from wolfxl import load_workbook
else:
from openpyxl import load_workbook
# report_job.py
from pathlib import Path
from excel_engine import load_workbook
def update_report(source: Path, output: Path) -> None:
workbook = load_workbook(source)
workbook["Summary"]["B2"] = "Ready"
workbook.save(output)
workbook.close()
Run the same representative fixture twice, once with USE_WOLFXL=0 and once with USE_WOLFXL=1. Compare the intended values and every package part that must remain stable. Promote the WolfXL path only after the accepted outputs match. Keep the environment flag through the observation window, then remove the openpyxl branch when rollback is no longer required.
For an existing template that needs package-preserving edits, use WolfXL modify=True in the WolfXL branch and define the expected difference from openpyxl's rebuild behavior rather than requiring byte-identical engine outputs.