aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mammoth/html/__init__.py58
-rw-r--r--mammoth/html/nodes.py32
-rw-r--r--tests/html/__init__.py1
-rw-r--r--tests/html/strip_empty_tests.py65
4 files changed, 156 insertions, 0 deletions
diff --git a/mammoth/html/__init__.py b/mammoth/html/__init__.py
new file mode 100644
index 0000000..905b423
--- /dev/null
+++ b/mammoth/html/__init__.py
@@ -0,0 +1,58 @@
+from ..lists import flat_map
+from .nodes import TextNode, Element, SelfClosingElement, ForceWrite, NodeVisitor
+
+
+def text(value):
+ return TextNode(value)
+
+
+def element(tag_name, attributes=None, children=None):
+ if attributes is None:
+ attributes = {}
+ if children is None:
+ children = []
+
+ return Element([tag_name], attributes, children, collapsible=False)
+
+
+def self_closing_element(tag_name, attributes=None):
+ if attributes is None:
+ attributes = {}
+
+ return SelfClosingElement(tag_name, attributes)
+
+
+force_write = ForceWrite()
+
+
+def strip_empty(nodes):
+ return flat_map(_strip_empty_node, nodes)
+
+
+def _strip_empty_node(node):
+ return StripEmpty().visit(node)
+
+
+class StripEmpty(NodeVisitor):
+ def visit_text_node(self, node):
+ if node.value:
+ return [node]
+ else:
+ return []
+
+ def visit_element(self, element):
+ children = strip_empty(element.children)
+ if len(children) == 0:
+ return []
+ else:
+ return [Element(
+ element.tag_names,
+ element.attributes,
+ children,
+ collapsible=element.collapsible)]
+
+ def visit_self_closing_element(self, element):
+ return [element]
+
+ def visit_force_write(self, node):
+ return [node]
diff --git a/mammoth/html/nodes.py b/mammoth/html/nodes.py
new file mode 100644
index 0000000..aae4888
--- /dev/null
+++ b/mammoth/html/nodes.py
@@ -0,0 +1,32 @@
+import cobble
+
+
+class Node(object):
+ pass
+
+
+@cobble.data
+class TextNode(Node):
+ value = cobble.field()
+
+
+@cobble.data
+class Element(Node):
+ tag_names = cobble.field()
+ attributes = cobble.field()
+ children = cobble.field()
+ collapsible = cobble.field()
+
+
+@cobble.data
+class SelfClosingElement(Node):
+ tag_name = cobble.field()
+ attributes = cobble.field()
+
+
+@cobble.visitable
+class ForceWrite(Node):
+ pass
+
+
+NodeVisitor = cobble.visitor(Node)
diff --git a/tests/html/__init__.py b/tests/html/__init__.py
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/tests/html/__init__.py
@@ -0,0 +1 @@
+
diff --git a/tests/html/strip_empty_tests.py b/tests/html/strip_empty_tests.py
new file mode 100644
index 0000000..eb28f30
--- /dev/null
+++ b/tests/html/strip_empty_tests.py
@@ -0,0 +1,65 @@
+from nose.tools import istest, assert_equal
+
+from mammoth import html
+
+
+@istest
+def text_nodes_with_text_are_not_stripped():
+ assert_equal(
+ [html.text("H")],
+ html.strip_empty([html.text("H")]))
+
+
+@istest
+def empty_text_nodes_are_stripped():
+ assert_equal(
+ [],
+ html.strip_empty([html.text("")]))
+
+
+@istest
+def elements_with_non_empty_children_are_not_stripped():
+ assert_equal(
+ [html.element("p", {}, [html.text("H")])],
+ html.strip_empty([html.element("p", {}, [html.text("H")])]))
+
+
+@istest
+def elements_with_no_children_are_stripped():
+ assert_equal(
+ [],
+ html.strip_empty([html.element("p")]))
+
+
+@istest
+def elements_with_only_empty_children_are_stripped():
+ assert_equal(
+ [],
+ html.strip_empty([html.element("p", {}, [html.text("")])]))
+
+
+@istest
+def empty_children_are_removed():
+ assert_equal(
+ html.strip_empty([html.element("ul", {}, [
+ html.element("li", {}, [html.text("")]),
+ html.element("li", {}, [html.text("H")]),
+ ])]),
+
+ [html.element("ul", {}, [
+ html.element("li", {}, [html.text("H")])
+ ])])
+
+
+@istest
+def self_closing_elements_are_never_empty():
+ assert_equal(
+ [html.self_closing_element("br")],
+ html.strip_empty([html.self_closing_element("br")]))
+
+
+@istest
+def force_writes_are_never_empty():
+ assert_equal(
+ [html.force_write],
+ html.strip_empty([html.force_write]))