docx/round-trip-unknown-preservation

Inject a Microsoft Word extension element (w14:checkbox) into a run's rPr. python-docx's object model has no proxy for checkbox — the only reason it can survive a round-trip is the xmlchemy descriptor contract that unknown children of known elements are preserved verbatim. This manifest pins that contract.

Library verdicts: python-docx: — docxjs: —

Metadata

Feature iddocx/round-trip-unknown-preservation
Formatdocx
Categoryround-trip
Spececma-376-5-part-1 § annex-A.4 w14:checkbox

XPath assertions

ID / partPredicateXPathpython-docxdocxjs
w14-checkbox-preserved
word/document.xml
exist
The injected <w14:checkbox> element must survive round-trip as a child of the run's rPr.
//w:r[w:t[normalize-space()='Text with unknown extension']]/w:rPr/w14:checkbox
w14-checked-val-preserved
word/document.xml
equal = 1
The nested <w14:checked w14:val='1'/> must survive with its attribute intact.
//w:r[w:t[normalize-space()='Text with unknown extension']]/w:rPr/w14:checkbox/w14:checked/@w14:val
run-text-preserved
word/document.xml
exist
Sanity: the run text itself must survive.
//w:r/w:t[normalize-space()='Text with unknown extension']

Render assertions

No render assertions declared.

Generator source

scripts/gen_round_trip_unknown_preservation.py

#!/usr/bin/env python3
"""Generate round-trip-unknown-preservation twin.

Critical round-trip check: author a document that contains a Microsoft
Word extension element the python-docx object model has no proxy for.
Round-trip it. Assert the unknown element survived.

Mechanism: we inject a `<w14:checkbox>` / `<w14:checked>` pair into a
run's `<w:rPr>` via raw lxml SubElement (bypassing xmlchemy). This is
EXACTLY the shape a Word-authored document would carry when a user
drops a content control into a paragraph. python-docx's descriptor
layer has no `checkbox` property on CT_RPr; the only reason the element
round-trips is the xmlchemy passthrough contract that says "unknown
children of a known element are preserved verbatim". If that contract
ever regresses, this fixture fails.

The twin is committed. The source is ephemeral scratch.
"""

from __future__ import annotations

import argparse
import sys
import tempfile
from pathlib import Path

from docx import Document
from lxml import etree

_REPO_ROOT = Path(__file__).resolve().parent.parent

W14_NS = "http://schemas.microsoft.com/office/word/2010/wordml"


def build_source(source_path: Path) -> None:
    doc = Document()
    p = doc.add_paragraph()
    r = p.add_run("Text with unknown extension")
    rPr = r._element.get_or_add_rPr()  # type: ignore[attr-defined]
    checkbox = etree.SubElement(rPr, f"{{{W14_NS}}}checkbox")
    checked = etree.SubElement(checkbox, f"{{{W14_NS}}}checked")
    checked.set(f"{{{W14_NS}}}val", "1")
    source_path.parent.mkdir(parents=True, exist_ok=True)
    doc.save(str(source_path), reproducible=True)


def round_trip(source_path: Path, twin_path: Path) -> None:
    doc = Document(str(source_path))
    twin_path.parent.mkdir(parents=True, exist_ok=True)
    doc.save(str(twin_path), reproducible=True)


def main(argv: list[str] | None = None) -> int:
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--out", required=True, help="Twin output path (repo-relative).")
    ns = parser.parse_args(argv)

    twin_path = _REPO_ROOT / ns.out
    with tempfile.NamedTemporaryFile(suffix=".docx", delete=False) as tmp:
        source_path = Path(tmp.name)
    try:
        build_source(source_path)
        round_trip(source_path, twin_path)
    finally:
        source_path.unlink(missing_ok=True)

    print(twin_path)
    return 0


if __name__ == "__main__":
    raise SystemExit(main())

Manifest (expanded)

{
  "$schema": "../manifest.schema.json",
  "id": "docx/round-trip-unknown-preservation",
  "title": "Round-trip unknown-element preservation (python-docx)",
  "format": "docx",
  "category": "round-trip",
  "summary": "Inject a Microsoft Word extension element (w14:checkbox) into a run's rPr. python-docx's object model has no proxy for checkbox — the only reason it can survive a round-trip is the xmlchemy descriptor contract that unknown children of known elements are preserved verbatim. This manifest pins that contract.",
  "spec": {
    "source": "ecma-376-5-part-1",
    "clause": "annex-A.4",
    "element": "w14:checkbox",
    "notes": "w14:checkbox is documented under [MS-DOCX] / [MS-OE376] Word 2010 extensions, not the ECMA-376 core. It is the inline content-control that Word renders as a clickable checkbox. python-docx doesn't model it, so a library that treats unknown rPr children as 'discard on read' would silently delete it. The xmlchemy ZeroOrMore / ZeroOrOne descriptors only remove *known* child types; anything else is left on the etree subtree and re-emitted on save. This manifest is the canary for that behaviour."
  },
  "fixtures": {
    "machine": "docx/round-trip-unknown-preservation"
  },
  "round_trip": {
    "library": "python-docx",
    "source_fixture": "docx/round-trip-unknown-preservation",
    "notes": "Mode B (generator-embedded). Source built inline with a raw lxml SubElement under the run's rPr; twin produced by a single python-docx load-save cycle."
  },
  "generator": {
    "python": "scripts/gen_round_trip_unknown_preservation.py",
    "arg_template": "--out fixtures/docx/round-trip-unknown-preservation.docx"
  },
  "assertions": [
    {
      "id": "w14-checkbox-preserved",
      "part": "word/document.xml",
      "namespaces": {
        "w": "http://schemas.openxmlformats.org/wordprocessingml/2006/main",
        "w14": "http://schemas.microsoft.com/office/word/2010/wordml"
      },
      "xpath": "//w:r[w:t[normalize-space()='Text with unknown extension']]/w:rPr/w14:checkbox",
      "must": "exist",
      "description": "The injected <w14:checkbox> element must survive round-trip as a child of the run's rPr."
    },
    {
      "id": "w14-checked-val-preserved",
      "part": "word/document.xml",
      "namespaces": {
        "w": "http://schemas.openxmlformats.org/wordprocessingml/2006/main",
        "w14": "http://schemas.microsoft.com/office/word/2010/wordml"
      },
      "xpath": "//w:r[w:t[normalize-space()='Text with unknown extension']]/w:rPr/w14:checkbox/w14:checked/@w14:val",
      "must": "equal",
      "value": "1",
      "description": "The nested <w14:checked w14:val='1'/> must survive with its attribute intact."
    },
    {
      "id": "run-text-preserved",
      "part": "word/document.xml",
      "namespaces": {
        "w": "http://schemas.openxmlformats.org/wordprocessingml/2006/main"
      },
      "xpath": "//w:r/w:t[normalize-space()='Text with unknown extension']",
      "must": "exist",
      "description": "Sanity: the run text itself must survive."
    }
  ]
}

Fixture

Download round-trip-unknown-preservation.docx (18.6 KB)

Reference preview

No rendered reference is available for this case.

Spec notes

w14:checkbox is documented under [MS-DOCX] / [MS-OE376] Word 2010 extensions, not the ECMA-376 core. It is the inline content-control that Word renders as a clickable checkbox. python-docx doesn't model it, so a library that treats unknown rPr children as 'discard on read' would silently delete it. The xmlchemy ZeroOrMore / ZeroOrOne descriptors only remove *known* child types; anything else is left on the etree subtree and re-emitted on save. This manifest is the canary for that behaviour.