7398963999bfa470e7ffb9ee17822a4a8bc7ddd4
[htsworkflow.git] / htsworkflow / util / ethelp.py
1 """ElementTree helper functions
2 """
3 import logging
4 import os
5 LOGGER = logging.getLogger(__name__)
6
7 import lxml.etree
8 try:
9     XHTML_RDF_DTD = lxml.etree.DTD(external_id='-//W3C//DTD XHTML+RDFa 1.0//EN')
10 except lxml.etree.DTDParseError as e:
11     XHTML_RDF_DTD = None
12     LOGGER.warn("Unable to load XHTML DTD %s" % (str(e),))
13
14 def indent(elem, level=0):
15     """
16     reformat an element tree to be 'pretty' (indented)
17     """
18     i = "\n" + level*"  "
19     if len(elem):
20         if not elem.text or not elem.text.strip():
21             elem.text = i + "  "
22         for child in elem:
23             indent(child, level+1)
24         # we don't want the closing tag indented too far
25         child.tail = i
26         if not elem.tail or not elem.tail.strip():
27             elem.tail = i
28     else:
29         if level and (not elem.tail or not elem.tail.strip()):
30             elem.tail = i
31
32 def flatten(elem, include_tail=0):
33     """
34     Extract the text from an element tree
35     (AKA extract the text that not part of XML tags)
36     """
37     text = elem.text or ""
38     for e in elem:
39         text += flatten(e, 1)
40     if include_tail and elem.tail: text += elem.tail
41     return text
42
43 def validate_xhtml(html, base_url='http://localhost'):
44     """Helper for validating xhtml, mostly intended for test code
45
46     Defaults to assuming XHTML+RDFa
47     Returns None if there was a problem configuring validation
48     Logs messages from lxml.etree using python logging
49     Returns True if it passed validation
50     and False if it fails.
51     """
52     if XHTML_RDF_DTD is None:
53         return None
54
55     try:
56         root = lxml.etree.fromstring(html, base_url=base_url)
57     except lxml.etree.ParseError as e:
58         LOGGER.warn("Unable to parse document: %s" % (str(e),))
59         return False
60
61     if XHTML_RDF_DTD.validate(root):
62         # so unlikely.
63         return True
64
65     isgood = True
66     for msg in XHTML_RDF_DTD.error_log.filter_from_errors():
67         # I have no idea how to suppress this error
68         # but I need the xmlns attributes for of my RDFa 1.0 encoding
69         if 'ERROR:VALID:DTD_UNKNOWN_ATTRIBUTE' in str(msg):
70             continue
71         else:
72             LOGGER.error(msg)
73             isgood = False
74
75     return isgood