view test/html_norm.py @ 8180:d02ce1d14acd

feat: issue2551068 - Provide way to retrieve file/msg data via rest endpoint. Use Allow header to change format of /binary_content endpoint. If Allow header for endpoint is not application/json, it will be matched against the mime type for the file. */*, text/* are supported and will return the native mime type if present. Changes: move */* mime type from static dict of supported types. It was hardcoded to return json only. Now it can return a matching non-json mime type for the /binary_content endpoint. Edited some errors to explicitly add */* mime type. Cleanups to use ', ' separation in lists of valid mime types rather than just space separated. Remove ETag header when sending raw content. See issue 2551375 for background. Doc added to rest.txt. Small format fix up (add dash) in CHANGES.txt. Make passing an unset/None/False accept_mime_type to format_dispatch_output a 500 error. This used to be the fallback to produce a 406 error after all processing had happened. It should no longer be possible to take that code path as all 406 errors (with valid accept_mime_types) are generated before processing takes place. Make format_dispatch_output handle output other than json/xml so it can send back binary_content data. Removed a spurious client.response_code = 400 that seems to not be used. Tests added for all code paths. Database setup for tests msg and file entry. This required a file upload test to change so it doesn't look for file1 as the link returned by the upload. Download the link and verify the data rather than verifying the link. Multiple formatting changes to error messages to make all lists of valid mime types ', ' an not just space separated.
author John Rouillard <rouilj@ieee.org>
date Sun, 08 Dec 2024 17:22:33 -0500
parents 5cadcaa13bed
children
line wrap: on
line source

"""Minimal html parser/normalizer for use in test_templating.

When testing markdown -> html conversion libraries, there are
gratuitous whitespace changes in generated output that break the
tests. Use this to try to normalize the generated HTML into something
that tries to preserve the semantic meaning allowing tests to stop
breaking.

This is not a complete parsing engine. It supports the Roundup issue
tracker unit tests so that no third party libraries are needed to run
the tests. If you find it useful enjoy.

Ideally this would be done by hijacking in some way
lxml.html.usedoctest to get a liberal parser that will ignore
whitespace. But that means the user has to install lxml to run the
tests. Similarly BeautifulSoup could be used to pretty print the html
but again, BeautifulSoup would need to be installed to run the
tests.

"""
try:
    from html.parser import HTMLParser
except ImportError:
    from HTMLParser import HTMLParser  # python2

try:
    from htmlentitydefs import name2codepoint
except ImportError:
    pass  # assume running under python3, name2codepoint predefined


class NormalizingHtmlParser(HTMLParser):
    """Handle start/end tags and normalize whitespace in data.
    Strip doctype, comments when passed in.

    Implements normalize method that takes input html and returns a
    normalized string leaving the instance ready for another call to
    normalize for another string.


    Note that using this rewrites all attributes parsed by HTMLParser
    into attr="value" form even though HTMLParser accepts other
    attribute specification forms.
    """

    debug = False  # set to true to enable more verbose output

    current_normalized_string = ""  # accumulate result string
    preserve_data = False           # if inside pre preserve whitespace

    def handle_starttag(self, tag, attrs):
        """put tag on new line with attributes.
           Note valid attributes according to HTMLParser:
              attrs='single_quote'
              attrs=noquote
              attrs="double_quote"
        """
        if self.debug: print("Start tag:", tag)

        self.current_normalized_string += "\n<%s" % tag

        for attr in attrs:
            if self.debug: print("     attr:", attr)
            self.current_normalized_string += ' %s="%s"' % attr

        self.current_normalized_string += ">\n"

        if tag == 'pre':
            self.preserve_data = True

    def handle_endtag(self, tag):
        if self.debug: print("End tag  :", tag)

        self.current_normalized_string += "\n</%s>" % tag

        if tag == 'pre':
            self.preserve_data = False

    def handle_data(self, data):
        if self.debug: print("Data     :", data)
        if not self.preserve_data:
            # normalize whitespace remove leading/trailing
            data = " ".join(data.strip().split())

        if data:
            self.current_normalized_string += "%s" % data

    def handle_comment(self, data):
        print("Comment  :", data)

    def handle_decl(self, data):
        print("Decl     :", data)

    def reset(self):
        """wrapper around reset with clearing of csef.current_normalized_string
           and reset of self.preserve_data
        """
        HTMLParser.reset(self)
        self.current_normalized_string = ""
        self.preserve_data = False

    def normalize(self, html):
        self.feed(html)
        result = self.current_normalized_string
        self.reset()
        return result


if __name__ == "__main__":
    parser = NormalizingHtmlParser()

    parser.feed('<div class="markup"><p> paragraph   text with whitespace\n  and more space  <pre><span class="f" data-attr="f">text more text</span></pre></div>')
    print("\n\ntest1", parser.current_normalized_string)

    parser.reset()

    parser.feed('''<div class="markup">
       <p> paragraph   text with whitespace\n  and more space  
       <pre><span class="f" data-attr="f">text \n more text</span></pre>
    </div>''')
    print("\n\ntest2", parser.current_normalized_string)
    parser.reset()
    print("\n\nnormalize", parser.normalize('''<div class="markup">
       <p> paragraph   text with whitespace\n  and more space  
       <pre><span class="f" data-attr="f">text \n more text &lt;</span></pre>
    </div>'''))

Roundup Issue Tracker: http://roundup-tracker.org/