xlsx/round-trip-formulas-preserved--shared-formula

Formulas and chart extensions are the highest-risk areas for xlsx round-trip drift. A shared formula group (f@t='shared') can be expanded into N independent f elements on save; an array formula (f@t='array') can be rewritten as a plain scalar; a sparklineGroup lives inside worksheet extLst and is easy to drop. This family asserts each class survives a python-xlsx load/save cycle. The shared-formula case is expected to surface a KNOWN round-trip regression where python-xlsx loses the f@t='shared' group structure on save.

Library verdicts: python-xlsx: — xlsxjs: —

Metadata

Feature idxlsx/round-trip-formulas-preserved--shared-formula
Formatxlsx
Categoryround-trip
Familyxlsx/round-trip-formulas-preserved
Axis valuescase=shared-formula
Spececma-376-5-part-1 § 18.3.1.40 f

XPath assertions

ID / partPredicateXPathpython-xlsxxlsxjs
workbook-parses
xl/workbook.xml
exist
Baseline: round-tripped workbook.xml parses.
//x:workbook
formula-or-sparkline-preserved
xl/worksheets/sheet1.xml
exist
The feature-specific marker (shared-formula anchor, array-formula f@t='array', or sparkline in extLst) must survive a python-xlsx load/save round-trip.
//x:c[@r='B1']/x:f[@t='shared' and @ref='B1:B5' and @si='0']

Render assertions

No render assertions declared.

Generator source

scripts/gen_round_trip_formulas_xlsx.py

#!/usr/bin/env python3
"""Generate an xlsx formulas-preserved round-trip fixture.

Identical body to :mod:`gen_round_trip_identity_xlsx` — the
distinguishing behaviour lives in the feature manifest's XPath
probes, not in the script.
"""

from __future__ import annotations

import argparse
import zipfile
from pathlib import Path

import xlsx  # type: ignore[import-not-found]


def _rezip_with_epoch_timestamps(path: Path) -> None:
    tmp = path.with_suffix(path.suffix + ".tmp")
    with zipfile.ZipFile(path, "r") as src, zipfile.ZipFile(
        tmp, "w", zipfile.ZIP_DEFLATED
    ) as dst:
        for info in src.infolist():
            data = src.read(info.filename)
            info.date_time = (1980, 1, 1, 0, 0, 0)
            dst.writestr(info, data)
    tmp.replace(path)


def main() -> None:
    ap = argparse.ArgumentParser()
    ap.add_argument("--source", required=True)
    ap.add_argument("--out", required=True)
    args = ap.parse_args()

    out = Path(args.out)
    out.parent.mkdir(parents=True, exist_ok=True)
    xlsx.load_workbook(args.source).save(str(out))
    _rezip_with_epoch_timestamps(out)


if __name__ == "__main__":
    main()

Manifest (expanded)

{
  "$schema": "../manifest.schema.json",
  "id": "xlsx/round-trip-formulas-preserved--shared-formula",
  "kind": "literal",
  "title": "Round-trip preserves shared/array formulas and sparklines",
  "format": "xlsx",
  "category": "round-trip",
  "summary": "Formulas and chart extensions are the highest-risk areas for xlsx round-trip drift. A shared formula group (f@t='shared') can be expanded into N independent f elements on save; an array formula (f@t='array') can be rewritten as a plain scalar; a sparklineGroup lives inside worksheet extLst and is easy to drop. This family asserts each class survives a python-xlsx load/save cycle. The shared-formula case is expected to surface a KNOWN round-trip regression where python-xlsx loses the f@t='shared' group structure on save.",
  "spec": {
    "source": "ecma-376-5-part-1",
    "clause": "18.3.1.40",
    "element": "f",
    "xsd_reference": "spec/ecma-376-5/part-1/xsd/sml.xsd",
    "notes": "Each parameter case pairs a committed fixture (from W2-F shared-formula and W2-C array-formula / sparkline families) with an XPath that must still hold after load+save. Shared-formula assertion targets the canonical anchor cell carrying @t='shared' @ref='B1:B5' — if it fails, python-xlsx has expanded the group into scalar f cells (a silent but material correctness regression: the saved file opens in Excel as scalar formulas, not a shared group, which Excel will rewrite back but third-party consumers may reject)."
  },
  "fixtures": {
    "machine": "xlsx/round-trip-formulas-preserved--shared-formula"
  },
  "round_trip": {
    "library": "python-xlsx",
    "source_fixture": "xlsx/shared-formula"
  },
  "generator": {
    "python": "scripts/gen_round_trip_formulas_xlsx.py",
    "arg_template": "--source fixtures/{case.source}.xlsx --out fixtures/xlsx/round-trip-formulas-preserved--{case.id}.xlsx"
  },
  "_expansion": {
    "parent_id": "xlsx/round-trip-formulas-preserved",
    "bindings": {
      "case": "shared-formula"
    }
  },
  "assertions": [
    {
      "id": "workbook-parses",
      "part": "xl/workbook.xml",
      "namespaces": {
        "x": "http://schemas.openxmlformats.org/spreadsheetml/2006/main"
      },
      "xpath": "//x:workbook",
      "must": "exist",
      "description": "Baseline: round-tripped workbook.xml parses."
    },
    {
      "id": "formula-or-sparkline-preserved",
      "part": "xl/worksheets/sheet1.xml",
      "namespaces": {
        "x": "http://schemas.openxmlformats.org/spreadsheetml/2006/main"
      },
      "xpath": "//x:c[@r='B1']/x:f[@t='shared' and @ref='B1:B5' and @si='0']",
      "must": "exist",
      "description": "The feature-specific marker (shared-formula anchor, array-formula f@t='array', or sparkline in extLst) must survive a python-xlsx load/save round-trip."
    }
  ]
}

Fixture

Fixture file not found on disk.

Reference preview

No rendered reference is available for this case.

Spec notes

Each parameter case pairs a committed fixture (from W2-F shared-formula and W2-C array-formula / sparkline families) with an XPath that must still hold after load+save. Shared-formula assertion targets the canonical anchor cell carrying @t='shared' @ref='B1:B5' — if it fails, python-xlsx has expanded the group into scalar f cells (a silent but material correctness regression: the saved file opens in Excel as scalar formulas, not a shared group, which Excel will rewrite back but third-party consumers may reject).