diff --git a/src/docx/parts/document.py b/src/docx/parts/document.py index 4960264b1..235b48d7d 100644 --- a/src/docx/parts/document.py +++ b/src/docx/parts/document.py @@ -104,7 +104,8 @@ def numbering_part(self) -> NumberingPart: try: return cast(NumberingPart, self.part_related_by(RT.NUMBERING)) except KeyError: - numbering_part = NumberingPart.new() + assert self.package is not None + numbering_part = NumberingPart.new(self.package) self.relate_to(numbering_part, RT.NUMBERING) return numbering_part diff --git a/src/docx/parts/numbering.py b/src/docx/parts/numbering.py index 745c8458a..5a410fb53 100644 --- a/src/docx/parts/numbering.py +++ b/src/docx/parts/numbering.py @@ -1,7 +1,19 @@ """|NumberingPart| and closely related objects.""" -from ..opc.part import XmlPart -from ..shared import lazyproperty +from __future__ import annotations + +import os +from typing import TYPE_CHECKING, cast + +from docx.opc.constants import CONTENT_TYPE as CT +from docx.opc.packuri import PackURI +from docx.opc.part import XmlPart +from docx.oxml.parser import parse_xml +from docx.shared import lazyproperty + +if TYPE_CHECKING: + from docx.oxml.numbering import CT_Numbering + from docx.package import Package class NumberingPart(XmlPart): @@ -9,9 +21,21 @@ class NumberingPart(XmlPart): or glossary.""" @classmethod - def new(cls) -> "NumberingPart": - """Newly created numbering part, containing only the root ```` element.""" - raise NotImplementedError + def new(cls, package: Package) -> "NumberingPart": + """Return a newly created numbering part, containing only the root ```` + element.""" + partname = PackURI("/word/numbering.xml") + content_type = CT.WML_NUMBERING + element = cast("CT_Numbering", parse_xml(cls._default_numbering_xml())) + return cls(partname, content_type, element, package) + + @classmethod + def _default_numbering_xml(cls) -> bytes: + """Return a bytestream containing XML for a default numbering part.""" + path = os.path.join(os.path.split(__file__)[0], "..", "templates", "default-numbering.xml") + with open(path, "rb") as f: + xml_bytes = f.read() + return xml_bytes @lazyproperty def numbering_definitions(self): diff --git a/src/docx/templates/default-numbering.xml b/src/docx/templates/default-numbering.xml new file mode 100644 index 000000000..4ddf3eccb --- /dev/null +++ b/src/docx/templates/default-numbering.xml @@ -0,0 +1,5 @@ + + diff --git a/tests/parts/test_document.py b/tests/parts/test_document.py index c27990baf..8a49b8a82 100644 --- a/tests/parts/test_document.py +++ b/tests/parts/test_document.py @@ -195,7 +195,7 @@ def and_it_creates_a_numbering_part_if_not_present( numbering_part = document_part.numbering_part - NumberingPart_.new.assert_called_once_with() + NumberingPart_.new.assert_called_once_with(package_) relate_to_.assert_called_once_with(document_part, numbering_part_, RT.NUMBERING) assert numbering_part is numbering_part_ diff --git a/tests/parts/test_numbering.py b/tests/parts/test_numbering.py index 1ed0f2a05..92b3d9aba 100644 --- a/tests/parts/test_numbering.py +++ b/tests/parts/test_numbering.py @@ -2,7 +2,9 @@ import pytest +from docx.opc.constants import CONTENT_TYPE as CT from docx.oxml.numbering import CT_Numbering +from docx.package import Package from docx.parts.numbering import NumberingPart, _NumberingDefinitions from ..oxml.unitdata.numbering import a_num, a_numbering @@ -21,6 +23,20 @@ def it_provides_access_to_the_numbering_definitions(self, num_defs_fixture): _NumberingDefinitions_.assert_called_once_with(numbering_elm_) assert numbering_definitions is numbering_definitions_ + def it_constructs_a_default_numbering_part_to_help(self): + package = Package() + + numbering_part = NumberingPart.new(package) + + assert isinstance(numbering_part, NumberingPart) + assert numbering_part.partname == "/word/numbering.xml" + assert numbering_part.content_type == CT.WML_NUMBERING + assert numbering_part.package is package + assert numbering_part.element.tag == ( + "{http://schemas.openxmlformats.org/wordprocessingml/2006/main}numbering" + ) + assert len(numbering_part.element) == 0 + # fixtures ------------------------------------------------------- @pytest.fixture