From 2c9955f0590d93968b4959e3622d01610e398700 Mon Sep 17 00:00:00 2001 From: Michael Williamson Date: Sat, 15 Aug 2015 18:11:46 +0100 Subject: Continue with warning style mapping cannot be parsed --- NEWS | 5 +++++ mammoth/__init__.py | 12 +++++++----- mammoth/options.py | 16 ++++++++++------ mammoth/style_reader/__init__.py | 11 +++++++++-- tests/conversion_tests.py | 26 ++++++++++++++++---------- tests/mammoth_tests.py | 12 ++++++++++++ tests/options_tests.py | 14 +++++++------- tests/style_reader_tests.py | 4 +++- 8 files changed, 69 insertions(+), 31 deletions(-) diff --git a/NEWS b/NEWS index 7762793..0337367 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,8 @@ +# 0.3.22 + +* Generate warnings for not-understood style mappings and continue, rather than + stopping with an error. + # 0.3.21 * Ignore w:numPr elements without w:numId or w:ilvl children. diff --git a/mammoth/__init__.py b/mammoth/__init__.py index f67f890..297cf24 100644 --- a/mammoth/__init__.py +++ b/mammoth/__init__.py @@ -15,11 +15,13 @@ def convert_to_markdown(*args, **kwargs): def convert(fileobj, transform_document=None, id_prefix=None, **kwargs): if transform_document is None: transform_document = lambda x: x - return docx.read(fileobj).map(transform_document).bind(lambda document: - conversion.convert_document_element_to_html( - document, - id_prefix=id_prefix, - **options.read_options(kwargs) + return options.read_options(kwargs).bind(lambda convert_options: + docx.read(fileobj).map(transform_document).bind(lambda document: + conversion.convert_document_element_to_html( + document, + id_prefix=id_prefix, + **convert_options + ) ) ) diff --git a/mammoth/options.py b/mammoth/options.py index 9d09146..1909b86 100644 --- a/mammoth/options.py +++ b/mammoth/options.py @@ -1,19 +1,20 @@ -from . import style_reader, lists +from . import style_reader, lists, results def read_options(options): - custom_style_map = _read_style_map(options.get("style_map") or "") + custom_style_map_result = _read_style_map(options.get("style_map") or "") include_default_style_map = options.get("include_default_style_map", True) options["ignore_empty_paragraphs"] = options.get("ignore_empty_paragraphs", True) - options["style_map"] = custom_style_map + \ + options["style_map"] = custom_style_map_result.value + \ (_default_style_map if include_default_style_map else []) - return options + return custom_style_map_result.map(lambda _: options) def _read_style_map(style_text): lines = filter(None, map(_get_line, style_text.split("\n"))) - return lists.map(style_reader.read_style, lines) + return results.combine(lists.map(style_reader.read_style, lines)) \ + .map(lambda style_mappings: filter(None, style_mappings)) def _get_line(line): @@ -24,7 +25,7 @@ def _get_line(line): return line -_default_style_map = _read_style_map(""" +_default_style_map_result = _read_style_map(""" p[style-name='Normal'] => p:fresh p.Heading1 => h1:fresh @@ -68,3 +69,6 @@ p:ordered-list(5) => ul|ol > li > ul|ol > li > ul|ol > li > ul|ol > li > ol > li r[style-name='Hyperlink'] => """) + +assert not _default_style_map_result.messages +_default_style_map = _default_style_map_result.value diff --git a/mammoth/style_reader/__init__.py b/mammoth/style_reader/__init__.py index 28281a8..aca07df 100644 --- a/mammoth/style_reader/__init__.py +++ b/mammoth/style_reader/__init__.py @@ -1,16 +1,23 @@ from parsimonious.grammar import Grammar +from parsimonious.exceptions import ParseError from .html_path_reader import read_html_path, grammar_text as html_path_grammar, read_html_path_node from .document_matcher_reader import read_document_matcher, grammar_text as document_matcher_grammar, read_document_matcher_node from ..styles import Style +from .. import results __all__ = ["read_style", "read_html_path", "read_document_matcher"] def read_style(string): - style_node = _grammar.parse(string) - return _read_style_node(style_node) + try: + style_node = _grammar.parse(string) + except ParseError: + warning = "Did not understand this style mapping, so ignored it: " + string + return results.Result(None, [results.warning(warning)]) + + return results.success(_read_style_node(style_node)) def _read_style_node(style_node): diff --git a/tests/conversion_tests.py b/tests/conversion_tests.py index 23e61d4..a5ae9cb 100644 --- a/tests/conversion_tests.py +++ b/tests/conversion_tests.py @@ -46,7 +46,7 @@ def style_mappings_using_style_ids_can_be_used_to_map_paragraphs(): _run_with_text("Tip") ]), style_map=[ - style_reader.read_style("p.TipsParagraph => p.tip") + _style_mapping("p.TipsParagraph => p.tip") ] ) assert_equal('

Tip

', result.value) @@ -59,7 +59,7 @@ def style_mappings_using_style_names_can_be_used_to_map_paragraphs(): _run_with_text("Tip") ]), style_map=[ - style_reader.read_style("p[style-name='Tips Paragraph'] => p.tip") + _style_mapping("p[style-name='Tips Paragraph'] => p.tip") ] ) assert_equal('

Tip

', result.value) @@ -72,7 +72,7 @@ def style_names_in_style_mappings_are_case_insensitive(): _run_with_text("Tip") ]), style_map=[ - style_reader.read_style("p[style-name='tips paragraph'] => p.tip") + _style_mapping("p[style-name='tips paragraph'] => p.tip") ] ) assert_equal('

Tip

', result.value) @@ -95,7 +95,7 @@ def default_paragraph_style_is_specified_by_mapping_plain_paragraphs(): _run_with_text("Tip") ]), style_map=[ - style_reader.read_style("p => p.tip") + _style_mapping("p => p.tip") ] ) assert_equal('

Tip

', result.value) @@ -128,7 +128,7 @@ def bulleted_paragraphs_are_converted_using_matching_styles(): _run_with_text("Hello") ], numbering=documents.numbering_level(level_index=0, is_ordered=False)), style_map=[ - style_reader.read_style("p:unordered-list(1) => ul > li:fresh") + _style_mapping("p:unordered-list(1) => ul > li:fresh") ] ) assert_equal('', result.value) @@ -141,7 +141,7 @@ def bulleted_styles_dont_match_plain_paragraph(): _run_with_text("Hello") ]), style_map=[ - style_reader.read_style("p:unordered-list(1) => ul > li:fresh") + _style_mapping("p:unordered-list(1) => ul > li:fresh") ] ) assert_equal('

Hello

', result.value) @@ -185,7 +185,7 @@ def underline_runs_can_be_mapped_using_style_mapping(): result = convert_document_element_to_html( documents.run(children=[documents.text("Hello")], is_underline=True), style_map=[ - style_reader.read_style("u => em") + _style_mapping("u => em") ] ) assert_equal("Hello", result.value) @@ -196,7 +196,7 @@ def style_mapping_for_underline_runs_does_not_close_parent_elements(): result = convert_document_element_to_html( documents.run(children=[documents.text("Hello")], is_underline=True, is_bold=True), style_map=[ - style_reader.read_style("u => em") + _style_mapping("u => em") ] ) assert_equal("Hello", result.value) @@ -215,7 +215,7 @@ def strikethrough_runs_can_be_configured_with_style_mapping(): result = convert_document_element_to_html( documents.run(children=[documents.text("Hello")], is_strikethrough=True), style_map=[ - style_reader.read_style("strike => del") + _style_mapping("strike => del") ] ) assert_equal("Hello", result.value) @@ -248,7 +248,7 @@ def runs_are_converted_by_satisfying_matching_paths(): result = convert_document_element_to_html( documents.run(style_id="TipsRun", children=[documents.Text("Tip")]), style_map=[ - style_reader.read_style("r.TipsRun => span.tip") + _style_mapping("r.TipsRun => span.tip") ] ) assert_equal('Tip', result.value) @@ -393,3 +393,9 @@ def _paragraph_with_text(text): def _run_with_text(text): return documents.run(children=[documents.text(text)]) + + +def _style_mapping(text): + result = style_reader.read_style(text) + assert not result.messages + return result.value diff --git a/tests/mammoth_tests.py b/tests/mammoth_tests.py index 88aa445..ddcb9a6 100644 --- a/tests/mammoth_tests.py +++ b/tests/mammoth_tests.py @@ -7,6 +7,7 @@ from nose.tools import istest, assert_equal from .testing import test_path import mammoth +from mammoth import results @istest @@ -40,6 +41,17 @@ def empty_paragraphs_are_preserved_if_ignore_empty_paragraphs_is_false(): assert_equal("

", result.value) assert_equal([], result.messages) +@istest +def warning_if_style_mapping_is_not_understood(): + style_map = """ +!!!! +p => h1""" + with open(test_path("single-paragraph.docx"), "rb") as fileobj: + result = mammoth.convert_to_html(fileobj=fileobj, style_map=style_map) + assert_equal("

Walking on imported air

", result.value) + warning = "Did not understand this style mapping, so ignored it: !!!!" + assert_equal([results.warning(warning)], result.messages) + @istest def inline_images_are_included_in_output(): diff --git a/tests/options_tests.py b/tests/options_tests.py index 72c75bf..f0b0beb 100644 --- a/tests/options_tests.py +++ b/tests/options_tests.py @@ -6,15 +6,15 @@ from mammoth.options import read_options, _default_style_map @istest def default_style_map_is_used_if_style_map_is_not_set(): - assert_equal(_default_style_map, read_options({})["style_map"]) + assert_equal(_default_style_map, read_options({}).value["style_map"]) @istest def custom_style_mappings_are_prepended_to_default_style_mappings(): style_map = read_options({ "style_map": "p.SectionTitle => h2" - })["style_map"] - assert_equal(style_reader.read_style("p.SectionTitle => h2"), style_map[0]) + }).value["style_map"] + assert_equal(style_reader.read_style("p.SectionTitle => h2").value, style_map[0]) assert_equal(_default_style_map, style_map[1:]) @@ -23,8 +23,8 @@ def default_style_mappings_are_ignored_if_include_default_style_map_is_false(): style_map = read_options({ "style_map": "p.SectionTitle => h2", "include_default_style_map": False - })["style_map"] - assert_equal([style_reader.read_style("p.SectionTitle => h2")], style_map) + }).value["style_map"] + assert_equal([style_reader.read_style("p.SectionTitle => h2").value], style_map) @istest @@ -32,5 +32,5 @@ def lines_starting_with_hash_in_custom_style_map_are_ignored(): style_map = read_options({ "style_map": "#p.SectionTitle => h3\np.SectionTitle => h2", "include_default_style_map": False - })["style_map"] - assert_equal([style_reader.read_style("p.SectionTitle => h2")], style_map) + }).value["style_map"] + assert_equal([style_reader.read_style("p.SectionTitle => h2").value], style_map) diff --git a/tests/style_reader_tests.py b/tests/style_reader_tests.py index 52d66fd..776e2dc 100644 --- a/tests/style_reader_tests.py +++ b/tests/style_reader_tests.py @@ -151,7 +151,9 @@ class ReadDocumentMatcherTests(object): @istest def document_matcher_is_mapped_to_html_path_using_fat_arrow(): + style_result = read_style("p => h1") assert_equal( styles.style(document_matchers.paragraph(), html_paths.path([html_paths.element(["h1"])])), - read_style("p => h1") + style_result.value ) + assert_equal([], style_result.messages) -- cgit v1.2.3