A worksheet carries a <table> (ListObject) over A1:C5 with a header row of column names and four data rows. Four parameter cases toggle headerRowCount, totalsRowCount, tableStyleInfo/@showColumnStripes.
Library verdicts: python-xlsx: — xlsxjs: —
| Feature id | xlsx/table-listobject--plain |
|---|---|
| Format | xlsx |
| Category | workbook-structure |
| Family | xlsx/table-listobject |
| Axis values | case=plain |
| Spec | ecma-376-5-part-1 § 18.5 table |
| ID / part | Predicate | XPath | python-xlsx | xlsxjs |
|---|---|---|---|---|
table-present-plainxl/tables/table1.xml | existxl/tables/table1.xml must carry a <table> whose @displayName equals 'TestTable'. | //x:table[@displayName='TestTable'] | — | — |
table-variant-plainxl/tables/table1.xml | equal = A1:C5The case-specific ListObject attribute must match the expected value. | //x:table[@displayName='TestTable']/@ref | — | — |
No render assertions declared.
scripts/gen_table_listobject.py
#!/usr/bin/env python3
"""Generate ``fixtures/xlsx/table-listobject--<case>.xlsx`` fixtures.
Parameterised generator for the ``xlsx/table-listobject`` manifest.
Four cases drive ``Worksheet.add_table()`` with different toggles:
- ``plain`` — header row only.
- ``with-totals-row`` — ``totals_row={'Price': 'sum'}``.
- ``with-header-row`` — explicit ``header_row=True`` (default; pinned so
the manifest's equality assertion remains honest).
- ``with-filter`` — ``tableStyleInfo/@showColumnStripes=1`` via
``show_col_stripes=True`` and a built-in style.
python-xlsx emits the ListObject at ``xl/tables/table1.xml``.
"""
from __future__ import annotations
import argparse
import datetime as _dt
from pathlib import Path
from xlsx import Workbook
_REPRODUCIBLE_DT = _dt.datetime(2000, 1, 1, 0, 0, 0)
_REPO_ROOT = Path(__file__).resolve().parent.parent
_HEADER = ("Name", "Qty", "Price")
_DATA = (
("Apple", 10, 1.5),
("Pear", 20, 2.5),
("Plum", 30, 3.5),
("Kiwi", 40, 4.5),
)
def _seed(ws) -> None:
"""Write the 5-row data block into A1:C5."""
for j, v in enumerate(_HEADER, 1):
ws.cell(row=1, column=j, value=v)
for i, row in enumerate(_DATA, 2):
for j, v in enumerate(row, 1):
ws.cell(row=i, column=j, value=v)
def _seed_with_totals(ws) -> None:
"""Seed A1:C6 — header + 4 data rows + totals row placeholder."""
_seed(ws)
ws.cell(row=6, column=1, value="Total")
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("--case", required=True,
choices=("plain", "with-totals-row",
"with-header-row", "with-filter"))
parser.add_argument("--out", required=True)
args = parser.parse_args()
out_path = Path(args.out)
if not out_path.is_absolute():
out_path = _REPO_ROOT / out_path
wb = Workbook()
ws = wb.active
ws.title = "Sheet1"
if args.case == "plain":
_seed(ws)
ws.add_table("A1:C5", name="TestTable", display_name="TestTable")
elif args.case == "with-totals-row":
_seed_with_totals(ws)
ws.add_table("A1:C6", name="TestTable", display_name="TestTable",
totals_row={"Price": "sum"})
elif args.case == "with-header-row":
_seed(ws)
ws.add_table("A1:C5", name="TestTable", display_name="TestTable",
header_row=True)
elif args.case == "with-filter":
_seed(ws)
ws.add_table("A1:C5", name="TestTable", display_name="TestTable",
style="TableStyleMedium9", show_col_stripes=True)
out_path.parent.mkdir(parents=True, exist_ok=True)
wb.save(out_path, zip_date_time=_REPRODUCIBLE_DT)
print(out_path)
return 0
if __name__ == "__main__":
raise SystemExit(main())
{
"$schema": "../manifest.schema.json",
"id": "xlsx/table-listobject--plain",
"kind": "literal",
"title": "ListObject (structured table) — 4 toggle variants",
"format": "xlsx",
"category": "workbook-structure",
"summary": "A worksheet carries a <table> (ListObject) over A1:C5 with a header row of column names and four data rows. Four parameter cases toggle headerRowCount, totalsRowCount, tableStyleInfo/@showColumnStripes.",
"spec": {
"source": "ecma-376-5-part-1",
"clause": "18.5",
"element": "table",
"rnc_reference": "spec/ecma-376-5/part-1/rnc/SpreadsheetML.rnc",
"xsd_reference": "spec/ecma-376-5/part-1/xsd/sml.xsd",
"notes": "A <table> (ListObject) part lives at xl/tables/tableN.xml, referenced from a worksheet via a table-relationship. @displayName identifies the table in the UI; @ref is the cell-range the table covers. Four variants exercise the common toggles: 'plain' is a vanilla header-only table; 'with-totals-row' adds @totalsRowCount=1 and a TableColumn/@totalsRowFunction aggregate; 'with-header-row' pins the default @headerRowCount=1 explicitly so the assertion is an equality rather than presence check; 'with-filter' adds a <tableStyleInfo> with @showColumnStripes=1. python-xlsx's Worksheet.add_table() drives all four shapes via the same call signature with different kwargs; the emitted part uses the default SpreadsheetML namespace (XPath binds 'x')."
},
"fixtures": {
"machine": "xlsx/table-listobject--plain"
},
"generator": {
"python": "scripts/gen_table_listobject.py",
"arg_template": "--case {case.id} --out fixtures/xlsx/table-listobject--{case.id}.xlsx"
},
"_expansion": {
"parent_id": "xlsx/table-listobject",
"bindings": {
"case": "plain"
}
},
"assertions": [
{
"id": "table-present-plain",
"part": "xl/tables/table1.xml",
"namespaces": {
"x": "http://schemas.openxmlformats.org/spreadsheetml/2006/main"
},
"xpath": "//x:table[@displayName='TestTable']",
"must": "exist",
"description": "xl/tables/table1.xml must carry a <table> whose @displayName equals 'TestTable'."
},
{
"id": "table-variant-plain",
"part": "xl/tables/table1.xml",
"namespaces": {
"x": "http://schemas.openxmlformats.org/spreadsheetml/2006/main"
},
"xpath": "//x:table[@displayName='TestTable']/@ref",
"must": "equal",
"value": "A1:C5",
"description": "The case-specific ListObject attribute must match the expected value."
}
]
}
No rendered reference is available for this case.