Skip to content

docx_plus.numbering.read

Reading list definitions back out of numbering.xml, including ones Word or another tool wrote.

Like every reader in the library this never fabricates a part: a document with no numbering.xml reads as an empty list rather than gaining one as a side effect of being inspected.

A fresh Document() is not empty

python-docx's bundled template ships nine abstractNum entries and nine num instances backing the built-in List Bullet and List Number styles, so an untouched document already reports nine definitions.

docx_plus.numbering.read

Reading list definitions back out of numbering.xml.

The read side of :mod:docx_plus.numbering.define. It reports what is actually in the part — including definitions Word or another tool wrote, and including the nine abstractNum entries python-docx's bundled template ships in every fresh document.

Like every reader in the library this never fabricates a part: a document with no numbering.xml reads as an empty list rather than gaining one as a side effect of being inspected.

This module imports only from docx_plus.core (SPEC §9.1).

ListDefinition dataclass

ListDefinition(
    num_id: int,
    abstract_id: int | None,
    levels: tuple[ListLevel, ...],
    name: str | None = None,
    style_link: str | None = None,
    num_style_link: str | None = None,
    multi_level_type: str | None = None,
    start_overrides: tuple[tuple[int, int], ...] = (),
)

A <w:num> instance together with the definition behind it.

Attributes:

Name Type Description
num_id int

The w:numId paragraphs reference.

abstract_id int | None

The w:abstractNumId it points at. None if the w:num carries no reference — malformed, but present in the wild.

levels tuple[ListLevel, ...]

The abstract definition's levels, outermost first. Empty if the reference is dangling.

name str | None

The definition's w:name, if any.

style_link str | None

w:styleLink — the style this definition is the numbering for.

num_style_link str | None

w:numStyleLink — the style whose numbering this definition defers to.

multi_level_type str | None

w:multiLevelType.

start_overrides tuple[tuple[int, int], ...]

{level: start} for every w:lvlOverride/w:startOverride on the instance. This is what distinguishes a restarted sequence from the original — see :func:~docx_plus.numbering.restart_list.

ListLevel dataclass

ListLevel(
    level: int,
    fmt: str | None = None,
    text: str | None = None,
    start: int | None = None,
    indent: int | None = None,
    hanging: int | None = None,
    justify: str | None = None,
    suffix: str | None = None,
    restart_after: int | None = None,
    font: str | None = None,
)

One outline level of a definition, as found in the document.

The read-side counterpart of :class:~docx_plus.numbering.LevelDefinition. Every field mirrors an optional child of <w:lvl>, so None means "the element is absent" — which Word reads as its own default, not as zero.

Attributes:

Name Type Description
level int

Zero-based outline depth (w:ilvl).

fmt str | None

w:numFmt value, e.g. "decimal" or "bullet".

text str | None

w:lvlText pattern or literal bullet glyph.

start int | None

w:start value.

indent int | None

Left indent in twips from the level's w:ind.

hanging int | None

Hanging indent in twips from the same.

justify str | None

w:lvlJc value.

suffix str | None

w:suff value. None means the element is absent, which Word treats as "tab".

restart_after int | None

w:lvlRestart value.

font str | None

w:ascii from the level's w:rPr/w:rFonts.

read_list_definitions

read_list_definitions(doc: Document) -> list[ListDefinition]

Return every list definition in doc, in numbering.xml order.

Note

A fresh Document() is not empty here. python-docx's bundled template ships nine abstractNum entries and nine num instances covering the built-in List Bullet and List Number styles, so a document you have not touched already reports nine definitions.

Parameters:

Name Type Description Default
doc Document

The python-docx :class:~docx.document.Document to scan.

required

Returns:

Name Type Description
One list[ListDefinition]

class:ListDefinition per <w:num>. Returns [] if

list[ListDefinition]

the document has no numbering.xml.

Example

from docx import Document from docx_plus.numbering import define_bullet_list, read_list_definitions doc = Document() num = define_bullet_list(doc) mine = [d for d in read_list_definitions(doc) if d.num_id == num] mine[0].levels[0].fmt 'bullet'

Source code in docx_plus/numbering/read.py
def read_list_definitions(doc: Document) -> list[ListDefinition]:
    """Return every list definition in ``doc``, in ``numbering.xml`` order.

    Note:
        A fresh ``Document()`` is **not** empty here. python-docx's
        bundled template ships nine ``abstractNum`` entries and nine
        ``num`` instances covering the built-in ``List Bullet`` and
        ``List Number`` styles, so a document you have not touched
        already reports nine definitions.

    Args:
        doc: The python-docx :class:`~docx.document.Document` to scan.

    Returns:
        One :class:`ListDefinition` per ``<w:num>``. Returns ``[]`` if
        the document has no ``numbering.xml``.

    Example:
        >>> from docx import Document
        >>> from docx_plus.numbering import define_bullet_list, read_list_definitions
        >>> doc = Document()
        >>> num = define_bullet_list(doc)
        >>> mine = [d for d in read_list_definitions(doc) if d.num_id == num]
        >>> mine[0].levels[0].fmt
        'bullet'
    """
    root = _numbering_root(doc)
    if root is None:
        return []

    abstracts = {
        abstract.get(qn("w:abstractNumId")): abstract for abstract in xpath(root, "./w:abstractNum")
    }

    definitions: list[ListDefinition] = []
    for num in xpath(root, "./w:num"):
        num_id = _int_attr(num, "w:numId")
        if num_id is None:
            continue  # a w:num with no id cannot be referenced; skip it
        raw_abstract = _child_val(num, "w:abstractNumId")
        abstract = abstracts.get(raw_abstract) if raw_abstract is not None else None
        definitions.append(
            ListDefinition(
                num_id=num_id,
                abstract_id=_as_int(raw_abstract),
                levels=_read_levels(abstract),
                name=_child_val(abstract, "w:name"),
                style_link=_child_val(abstract, "w:styleLink"),
                num_style_link=_child_val(abstract, "w:numStyleLink"),
                multi_level_type=_child_val(abstract, "w:multiLevelType"),
                start_overrides=_read_start_overrides(num),
            )
        )
    return definitions