From 9552d7e8c1e3ca5fca565a5830200fc1e2611e58 Mon Sep 17 00:00:00 2001 From: Sean Doherty Date: Sat, 16 May 2026 17:08:13 -0500 Subject: [PATCH] Improve numbering numId lookup --- src/docx/oxml/numbering.py | 2 +- tests/oxml/test_numbering.py | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 tests/oxml/test_numbering.py diff --git a/src/docx/oxml/numbering.py b/src/docx/oxml/numbering.py index 3512de655..65abe4c31 100644 --- a/src/docx/oxml/numbering.py +++ b/src/docx/oxml/numbering.py @@ -102,7 +102,7 @@ def _next_numId(self): """The first ``numId`` unused by a ```` element, starting at 1 and filling any gaps in numbering between existing ```` elements.""" numId_strs = self.xpath("./w:num/@w:numId") - num_ids = [int(numId_str) for numId_str in numId_strs] + num_ids = {int(numId_str) for numId_str in numId_strs} for num in range(1, len(num_ids) + 2): if num not in num_ids: break diff --git a/tests/oxml/test_numbering.py b/tests/oxml/test_numbering.py new file mode 100644 index 000000000..66b904863 --- /dev/null +++ b/tests/oxml/test_numbering.py @@ -0,0 +1,26 @@ +"""Test suite for the docx.oxml.numbering module.""" + +from typing import cast + +import pytest + +from docx.oxml.numbering import CT_Numbering + +from ..unitutil.cxml import element + + +class DescribeCT_Numbering: + @pytest.mark.parametrize( + ("num_ids", "expected_value"), + [ + ([], 1), + ([1, 2, 3], 4), + ([1, 3, 4], 2), + ], + ) + def it_knows_the_next_unused_numId(self, num_ids: list[int], expected_value: int): + children = ",".join(f"w:num{{w:numId={num_id}}}" for num_id in num_ids) + cxml = "w:numbering" if not children else f"w:numbering/({children})" + numbering = cast(CT_Numbering, element(cxml)) + + assert numbering._next_numId == expected_value