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