From 6d5fe54cd6183d12c0e984d9d0d7e3204817583f Mon Sep 17 00:00:00 2001 From: Michael Williamson Date: Sun, 17 Mar 2019 18:20:17 +0000 Subject: Defer resolution of abstract nums until necessary --- mammoth/docx/__init__.py | 26 +++++++++++++------------- mammoth/docx/numbering_xml.py | 27 ++++++++++++++++++--------- tests/docx/body_xml_tests.py | 15 +++++++++++---- 3 files changed, 42 insertions(+), 26 deletions(-) diff --git a/mammoth/docx/__init__.py b/mammoth/docx/__init__.py index 75511d2..517683d 100644 --- a/mammoth/docx/__init__.py +++ b/mammoth/docx/__init__.py @@ -1,7 +1,7 @@ from functools import partial import os -import cobble +import cobble from .. import results, lists, zips from .document_xml import read_document_xml_element @@ -27,7 +27,7 @@ def read(fileobj): zip_file, part_paths=part_paths, ) - + return results.combine([ _read_notes(read_part_with_body, part_paths), _read_comments(read_part_with_body, part_paths), @@ -49,12 +49,12 @@ class _PartPaths(object): def _find_part_paths(zip_file): package_relationships = _read_relationships(zip_file, "_rels/.rels") document_filename = _find_document_filename(zip_file, package_relationships) - + document_relationships = _read_relationships( zip_file, _find_relationships_path_for(document_filename), ) - + def find(name): return _find_part_path( zip_file=zip_file, @@ -63,7 +63,7 @@ def _find_part_paths(zip_file): fallback_path="word/{0}.xml".format(name), base_path=zips.split_path(document_filename)[0], ) - + return _PartPaths( main_document=document_filename, comments=find("comments"), @@ -111,7 +111,7 @@ def _read_notes(read_part_with_body, part_paths): lambda root, body_reader: read_endnotes_xml_element(root, body_reader=body_reader), default=_empty_result, ) - + return results.combine([footnotes, endnotes]).map(lists.flatten) @@ -122,7 +122,7 @@ def _read_comments(read_part_with_body, part_paths): default=_empty_result, ) - + def _read_document(zip_file, read_part_with_body, notes, comments, part_paths): return read_part_with_body( part_paths.main_document, @@ -146,19 +146,19 @@ def _part_with_body_reader(document_path, zip_file, part_paths): zip_file, part_paths.numbering, read_numbering_xml_element, - default=Numbering({}), + default=Numbering(abstract_nums={}, nums={}), ) - + styles = _try_read_entry_or_default( zip_file, part_paths.styles, read_styles_xml_element, Styles.EMPTY, ) - + def read_part(name, reader, default=_undefined): relationships = _read_relationships(zip_file, _find_relationships_path_for(name)) - + body_reader = body_xml.reader( numbering=numbering, content_types=content_types, @@ -172,7 +172,7 @@ def _part_with_body_reader(document_path, zip_file, part_paths): return _read_entry(zip_file, name, partial(reader, body_reader=body_reader)) else: return _try_read_entry_or_default(zip_file, name, partial(reader, body_reader=body_reader), default=default) - + return read_part @@ -180,7 +180,7 @@ def _part_with_body_reader(document_path, zip_file, part_paths): def _find_relationships_path_for(name): dirname, basename = zips.split_path(name) return zips.join_path(dirname, "_rels", basename + ".rels") - + def _read_relationships(zip_file, name): return _try_read_entry_or_default( diff --git a/mammoth/docx/numbering_xml.py b/mammoth/docx/numbering_xml.py index f7f98e0..31a0d3c 100644 --- a/mammoth/docx/numbering_xml.py +++ b/mammoth/docx/numbering_xml.py @@ -1,10 +1,12 @@ +import cobble + from ..documents import numbering_level def read_numbering_xml_element(element): abstract_nums = _read_abstract_nums(element) - nums = _read_nums(element, abstract_nums) - return Numbering(nums) + nums = _read_nums(element) + return Numbering(abstract_nums=abstract_nums, nums=nums) def _read_abstract_nums(element): @@ -33,27 +35,34 @@ def _read_abstract_num_level(element): return numbering_level(level_index, is_ordered) -def _read_nums(element, abstract_nums): +def _read_nums(element): num_elements = element.find_children("w:num") return dict( - _read_num(num_element, abstract_nums) + _read_num(num_element) for num_element in num_elements ) -def _read_num(element, abstract_nums): +def _read_num(element): num_id = element.attributes.get("w:numId") abstract_num_id = element.find_child_or_null("w:abstractNumId").attributes["w:val"] - return num_id, abstract_nums[abstract_num_id] + return num_id, _Num(abstract_num_id=abstract_num_id) + + +@cobble.data +class _Num(object): + abstract_num_id = cobble.field() class Numbering(object): - def __init__(self, nums): + def __init__(self, abstract_nums, nums): + self._abstract_nums = abstract_nums self._nums = nums - + def find_level(self, num_id, level): num = self._nums.get(num_id) if num is None: return None else: - return num.get(level) + abstract_num = self._abstract_nums[num.abstract_num_id] + return abstract_num.get(level) diff --git a/tests/docx/body_xml_tests.py b/tests/docx/body_xml_tests.py index 7ccd9b4..04ac944 100644 --- a/tests/docx/body_xml_tests.py +++ b/tests/docx/body_xml_tests.py @@ -9,7 +9,6 @@ import funk from mammoth import documents, results from mammoth.docx.xmlparser import element as xml_element, text as xml_text from mammoth.docx import body_xml -from mammoth.docx.numbering_xml import Numbering from mammoth.docx.relationships_xml import Relationships, Relationship from mammoth.docx.styles_xml import Styles, Style from .document_matchers import ( @@ -108,7 +107,7 @@ class ParagraphTests(object): xml_element("w:numId", {"w:val": "42"}), ]) - numbering = Numbering({"42": {"1": documents.numbering_level("1", True)}}) + numbering = _NumberingMap({"42": {"1": documents.numbering_level("1", True)}}) paragraph = _read_and_get_document_xml_element(paragraph_xml, numbering=numbering) assert_equal("1", paragraph.numbering.level_index) @@ -120,7 +119,7 @@ class ParagraphTests(object): xml_element("w:numId", {"w:val": "42"}), ]) - numbering = Numbering({"42": {"1": documents.numbering_level("1", True)}}) + numbering = _NumberingMap({"42": {"1": documents.numbering_level("1", True)}}) paragraph = _read_and_get_document_xml_element(paragraph_xml, numbering=numbering) assert_equal(None, paragraph.numbering) @@ -131,7 +130,7 @@ class ParagraphTests(object): xml_element("w:ilvl", {"w:val": "1"}), ]) - numbering = Numbering({"42": {"1": documents.numbering_level("1", True)}}) + numbering = _NumberingMap({"42": {"1": documents.numbering_level("1", True)}}) paragraph = _read_and_get_document_xml_element(paragraph_xml, numbering=numbering) assert_equal(None, paragraph.numbering) @@ -1291,3 +1290,11 @@ def _image_relationship(relationship_id, target): target=target, type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image", ) + + +class _NumberingMap(object): + def __init__(self, nums): + self._nums = nums + + def find_level(self, num_id, level): + return self._nums[num_id][level] -- cgit v1.2.3