aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS5
-rw-r--r--mammoth/__init__.py12
-rw-r--r--mammoth/options.py16
-rw-r--r--mammoth/style_reader/__init__.py11
-rw-r--r--tests/conversion_tests.py26
-rw-r--r--tests/mammoth_tests.py12
-rw-r--r--tests/options_tests.py14
-rw-r--r--tests/style_reader_tests.py4
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('<p class="tip">Tip</p>', 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('<p class="tip">Tip</p>', 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('<p class="tip">Tip</p>', 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('<p class="tip">Tip</p>', 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('<ul><li>Hello</li></ul>', 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('<p>Hello</p>', 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("<em>Hello</em>", 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("<strong><em>Hello</em></strong>", 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("<del>Hello</del>", 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('<span class="tip">Tip</span>', 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("<p></p>", 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("<h1>Walking on imported air</h1>", 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)