docx/scale-runs-per-paragraph--n100

Stress test that writes a single paragraph containing N bolded runs and verifies the run count round-trips. Parameterised across N = 1, 10, 100, 1000. Also asserts the python-docx 2026.05.3 paragraph-mark <w:rPr> mirror: at N=1 the mirror must fire (the single-run paragraph's <w:b/> propagates to the paragraph mark under <w:pPr><w:rPr>); at N>1 the mirror must not fire.

Library verdicts: python-docx: — docxjs: —

Metadata

Feature iddocx/scale-runs-per-paragraph--n100
Formatdocx
Categoryscale
Familydocx/scale-runs-per-paragraph
Axis valuessize=n100
Spececma-376-5-part-1 § 17.3.2.1 w:r

XPath assertions

ID / partPredicateXPathpython-docxdocxjs
run-count-n100
word/document.xml
equal = 100
The single body paragraph must contain exactly N <w:r> children.
string(count(/w:document/w:body/w:p/w:r))
run-is-bold-n100
word/document.xml
exist
At least the first run must carry <w:rPr><w:b/></w:rPr>, confirming the bold formatting survived serialisation.
//w:r[1]/w:rPr/w:b
paragraph-mark-mirror-n100
word/document.xml
equal = false
Paragraph-mark rPr mirror (python-docx 2026.05.3): at n=1 a single-run paragraph's <w:b/> must mirror onto <w:pPr><w:rPr><w:b/>; at n>1 the mirror must be absent.
string(boolean(/w:document/w:body/w:p/w:pPr/w:rPr/w:b))

Render assertions

No render assertions declared.

Generator source

scripts/gen_scale_runs_per_paragraph.py

#!/usr/bin/env python3
"""Generate ``fixtures/docx/scale-runs-per-paragraph--<n>.docx``.

A single paragraph containing ``--runs`` bolded runs. Designed to
exercise python-docx's paragraph-mark ``<w:rPr>`` mirror logic
introduced in 2026.05.3: the mirror fires only for paragraphs with
exactly one direct ``<w:r>`` child, so the n=1 case should carry a
mirrored ``<w:pPr><w:rPr><w:b/></w:rPr></w:pPr>`` and n>1 cases
should not.

Assertions:
  - run count inside the single paragraph matches ``--runs``
  - when runs == 1, the paragraph mark mirrors ``<w:b/>``
  - when runs > 1, the paragraph mark does not mirror ``<w:b/>``

Usage::

    python scripts/gen_scale_runs_per_paragraph.py \
        --runs 10 \
        --out fixtures/docx/scale-runs-per-paragraph--n10.docx
"""

from __future__ import annotations

import argparse
from pathlib import Path

from docx import Document

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


def _write(runs: int, out: Path) -> None:
    doc = Document()
    paragraph = doc.add_paragraph()
    for i in range(runs):
        run = paragraph.add_run(f"r{i} ")
        run.bold = True
    out.parent.mkdir(parents=True, exist_ok=True)
    doc.save(out, reproducible=True)


def main() -> int:
    parser = argparse.ArgumentParser()
    parser.add_argument("--runs", required=True, type=int, help="Number of runs in the paragraph.")
    parser.add_argument("--out", required=True, help="Output path for the .docx file.")
    args = parser.parse_args()

    out_path = Path(args.out)
    if not out_path.is_absolute():
        out_path = _REPO_ROOT / out_path
    _write(args.runs, out_path)
    print(out_path)
    return 0


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

Manifest (expanded)

{
  "$schema": "../manifest.schema.json",
  "id": "docx/scale-runs-per-paragraph--n100",
  "kind": "literal",
  "title": "Scale: one paragraph, many bolded runs",
  "format": "docx",
  "category": "scale",
  "summary": "Stress test that writes a single paragraph containing N bolded runs and verifies the run count round-trips. Parameterised across N = 1, 10, 100, 1000. Also asserts the python-docx 2026.05.3 paragraph-mark <w:rPr> mirror: at N=1 the mirror must fire (the single-run paragraph's <w:b/> propagates to the paragraph mark under <w:pPr><w:rPr>); at N>1 the mirror must not fire.",
  "spec": {
    "source": "ecma-376-5-part-1",
    "clause": "17.3.2.1",
    "element": "w:r",
    "rnc_reference": "spec/ecma-376-5/part-1/rnc/WordprocessingML.rnc",
    "xsd_reference": "spec/ecma-376-5/part-1/xsd/wml.xsd",
    "notes": "The 2026.05.3 paragraph-mark mirror (commit 7f44cad) propagates a single-run paragraph's <w:rPr> formatting onto the paragraph mark as <w:pPr><w:rPr>. Multi-run paragraphs are intentionally left alone — Word's 'keep typing in bold' convention only applies when there's one unambiguous source. This family exercises both branches by scaling run count through 1 (mirror should fire) and 10/100/1000 (mirror should stay absent). It also forces a single paragraph to carry N runs, which exposes O(N^2) costs in any code path that re-scans the paragraph's child list per-run."
  },
  "fixtures": {
    "machine": "docx/scale-runs-per-paragraph--n100"
  },
  "generator": {
    "python": "scripts/gen_scale_runs_per_paragraph.py",
    "arg_template": "--runs {size.n} --out fixtures/docx/scale-runs-per-paragraph--{size.id}.docx"
  },
  "meta": {
    "performance": {
      "recorded_on": "2026-05-05",
      "notes": "Wall-clock save time. n=1 fires the paragraph-mark mirror; n=10/100/1000 do not. Growth from n=100 to n=1000 is modest (0.22s -> 0.41s).",
      "cases": [
        {
          "id": "n1",
          "save_seconds": 0.22,
          "file_bytes": 19036,
          "pPr_rPr_mirror_fires": true
        },
        {
          "id": "n10",
          "save_seconds": 0.21,
          "file_bytes": 19066,
          "pPr_rPr_mirror_fires": false
        },
        {
          "id": "n100",
          "save_seconds": 0.22,
          "file_bytes": 19342,
          "pPr_rPr_mirror_fires": false
        },
        {
          "id": "n1000",
          "save_seconds": 0.41,
          "file_bytes": 22034,
          "pPr_rPr_mirror_fires": false
        }
      ]
    }
  },
  "_expansion": {
    "parent_id": "docx/scale-runs-per-paragraph",
    "bindings": {
      "size": "n100"
    }
  },
  "assertions": [
    {
      "id": "run-count-n100",
      "part": "word/document.xml",
      "namespaces": {
        "w": "http://schemas.openxmlformats.org/wordprocessingml/2006/main"
      },
      "xpath": "string(count(/w:document/w:body/w:p/w:r))",
      "must": "equal",
      "value": "100",
      "description": "The single body paragraph must contain exactly N <w:r> children."
    },
    {
      "id": "run-is-bold-n100",
      "part": "word/document.xml",
      "namespaces": {
        "w": "http://schemas.openxmlformats.org/wordprocessingml/2006/main"
      },
      "xpath": "//w:r[1]/w:rPr/w:b",
      "must": "exist",
      "description": "At least the first run must carry <w:rPr><w:b/></w:rPr>, confirming the bold formatting survived serialisation."
    },
    {
      "id": "paragraph-mark-mirror-n100",
      "part": "word/document.xml",
      "namespaces": {
        "w": "http://schemas.openxmlformats.org/wordprocessingml/2006/main"
      },
      "xpath": "string(boolean(/w:document/w:body/w:p/w:pPr/w:rPr/w:b))",
      "must": "equal",
      "value": "false",
      "description": "Paragraph-mark rPr mirror (python-docx 2026.05.3): at n=1 a single-run paragraph's <w:b/> must mirror onto <w:pPr><w:rPr><w:b/>; at n>1 the mirror must be absent."
    }
  ]
}

Fixture

Download scale-runs-per-paragraph--n100.docx (18.9 KB)

Reference preview

No rendered reference is available for this case.

Spec notes

The 2026.05.3 paragraph-mark mirror (commit 7f44cad) propagates a single-run paragraph's <w:rPr> formatting onto the paragraph mark as <w:pPr><w:rPr>. Multi-run paragraphs are intentionally left alone — Word's 'keep typing in bold' convention only applies when there's one unambiguous source. This family exercises both branches by scaling run count through 1 (mirror should fire) and 10/100/1000 (mirror should stay absent). It also forces a single paragraph to carry N runs, which exposes O(N^2) costs in any code path that re-scans the paragraph's child list per-run.