From 00bdc92db936eb55258bda5051529d0f6c528972 Mon Sep 17 00:00:00 2001 From: Adam Williamson Date: Jul 26 2021 20:15:58 +0000 Subject: [PATCH 1/3] Considerably simplify html_util for Python 3.10 compatibility (#58) As reported in #58 and RHBZ #1972391, `formatter` was removed from the Python standard library in Python 3.10. This heavily simplifies `html_util.html_to_text()` by using the stdlib `HTMLParser` class, which avoids the use of `formatter`. Signed-off-by: Adam Williamson --- diff --git a/framework/src/setroubleshoot/html_util.py b/framework/src/setroubleshoot/html_util.py index 5c6d07a..095eaeb 100644 --- a/framework/src/setroubleshoot/html_util.py +++ b/framework/src/setroubleshoot/html_util.py @@ -28,110 +28,29 @@ __all__ = [ import syslog import sys +import textwrap if sys.version_info > (3,): import html - import html.parser import html.entities - from io import StringIO + from html.parser import HTMLParser else: import htmllib - from StringIO import StringIO -import formatter as Formatter + from HTMLParser import HTMLParser import string from types import * #------------------------------------------------------------------------------ +class HTMLFilter(HTMLParser): + def __init__(self): + HTMLParser.__init__(self) + self.text = "" -class TextWriter(Formatter.DumbWriter): - - def __init__(self, file=None, maxcol=80, indent_width=4): - Formatter.DumbWriter.__init__(self, file, maxcol) - self.indent_level = 0 - self.indent_width = indent_width - self._set_indent() - - def _set_indent(self): - self.indent_col = self.indent_level * self.indent_width - self.indent = ' ' * self.indent_col - - def new_margin(self, margin, level): - self.indent_level = level - self._set_indent() - - def send_label_data(self, data): - data = data + ' ' - if len(data) > self.indent_col: - self.send_literal_data(data) - else: - offset = self.indent_col - len(data) - self.send_literal_data(' ' * offset + data) - - def send_flowing_data(self, data): - if not data: - return - atbreak = self.atbreak or data[0] in string.whitespace - col = self.col - maxcol = self.maxcol - write = self.file.write - col = self.col - if col == 0: - write(self.indent) - col = self.indent_col - for word in data.split(): - if atbreak: - if col + len(word) >= maxcol: - write('\n' + self.indent) - col = self.indent_col - else: - write(' ') - col = col + 1 - write(word) - col = col + len(word) - atbreak = 1 - self.col = col - self.atbreak = data[-1] in string.whitespace - -if sys.version_info > (3,): - class HTMLParserAnchor(html.parser.HTMLParser): - - def __init__(self, formatter, strict=False, convert_charrefs=False): - super(HTMLParserAnchor, self).__init__() - self.formatter = formatter - self.anchor_href = None - - def handle_starttag(self, tag, attrs): - if tag == 'a': - for key, value in attrs: - if key == 'href': - self.anchor_href = value - - def handle_endtag(self, tag): - if tag == 'a': - if self.anchor_href != None: - self.formatter.writer.send_flowing_data('(' + self.anchor_href + ')') - self.anchor_href = None - - def handle_data(self, data): - self.formatter.writer.send_flowing_data(data) - -else: - class HTMLParserAnchor(htmllib.HTMLParser): - - def __init__(self, formatter, verbose=0): - htmllib.HTMLParser.__init__(self, formatter, verbose) - - def anchor_bgn(self, href, name, type): - self.anchor = href - - def anchor_end(self): - if self.anchor: - self.handle_data(' (%s) ' % self.anchor) - self.anchor = None + def handle_data(self, data): + self.text += data #------------------------------------------------------------------------------ - def escape_html(s): if s is None: return None @@ -161,14 +80,9 @@ def unescape_html(s): def html_to_text(html, maxcol=80): try: - buffer = StringIO() - formatter = Formatter.AbstractFormatter(TextWriter(buffer, maxcol)) - parser = HTMLParserAnchor(formatter) - parser.feed(html) - parser.close() - text = buffer.getvalue() - buffer.close() - return text + filter = HTMLFilter() + filter.feed(html) + return textwrap.fill(filter.text, width=maxcol) except Exception as e: syslog.syslog(syslog.LOG_ERR, 'cannot convert html to text: %s' % e) return None From 5c19694f3beecfe5b9767b6eff6a4d41ed1822cf Mon Sep 17 00:00:00 2001 From: Adam Williamson Date: Jul 26 2021 20:17:50 +0000 Subject: [PATCH 2/3] html_util: drop various unnecessary imports None of these actually seem to be used any more. Signed-off-by: Adam Williamson --- diff --git a/framework/src/setroubleshoot/html_util.py b/framework/src/setroubleshoot/html_util.py index 095eaeb..d24632b 100644 --- a/framework/src/setroubleshoot/html_util.py +++ b/framework/src/setroubleshoot/html_util.py @@ -30,14 +30,9 @@ import syslog import sys import textwrap if sys.version_info > (3,): - import html - import html.entities from html.parser import HTMLParser else: - import htmllib from HTMLParser import HTMLParser -import string -from types import * #------------------------------------------------------------------------------ From 0a76ce3f0eaf7fee81438f7a0082280a3a0cbbc3 Mon Sep 17 00:00:00 2001 From: Adam Williamson Date: Jul 26 2021 20:21:59 +0000 Subject: [PATCH 3/3] html_util: remove html_document I suspect this can safely be removed, because I don't think it has worked for six years. Since dbaaf0f it uses `six.string_types` but does not `import six`; if the function were ever called, it would crash immediately. I suggest this is reasonable proof that it's useless. Nothing in setroubleshoot itself calls it. Signed-off-by: Adam Williamson --- diff --git a/framework/src/setroubleshoot/html_util.py b/framework/src/setroubleshoot/html_util.py index d24632b..d16b789 100644 --- a/framework/src/setroubleshoot/html_util.py +++ b/framework/src/setroubleshoot/html_util.py @@ -22,8 +22,6 @@ __all__ = [ 'escape_html', 'unescape_html', 'html_to_text', - - 'html_document', ] import syslog @@ -81,35 +79,3 @@ def html_to_text(html, maxcol=80): except Exception as e: syslog.syslog(syslog.LOG_ERR, 'cannot convert html to text: %s' % e) return None - - -def html_document(*body_components): - '''Wrap the body components in a HTML document structure with a valid header. - Accepts a variable number of arguments of of which canb be: - * string - * a sequences of strings (tuple or list). - * a callable object taking no parameters and returning a string or sequence of strings. - ''' - head = '\n \n \n \n \n' - tail = '\n \n' - - doc = head - - for body_component in body_components: - if isinstance(body_component, six.string_types): - doc += body_component - elif isinstance(body_component, (tuple, list)): - for item in body_component: - doc += item - elif callable(body_component): - result = body_component() - if isinstance(result, (tuple, list)): - for item in result: - doc += item - else: - doc += result - else: - doc += body_component - - doc += tail - return doc