Define the interface, without actually doing much of a conversion.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
patchew/logviewer.py | 50 +++++++++++++++++++++++++++++++++++++++++++++++--
tests/test_ansi2html.py | 30 +++++++++++++++++++++++++++++
2 files changed, 78 insertions(+), 2 deletions(-)
create mode 100644 tests/test_ansi2html.py
diff --git a/patchew/logviewer.py b/patchew/logviewer.py
index 53bf238..4438fa4 100644
--- a/patchew/logviewer.py
+++ b/patchew/logviewer.py
@@ -1,10 +1,41 @@
+# Convert ANSI sequences to HTML for Patchew
+#
+# Copyright (C) 2018 Red Hat, Inc.
+#
+# Author: Paolo Bonzini <pbonzini@redhat.com>
+
import abc
+import sys
from django.views import View
from django.http import HttpResponse, StreamingHttpResponse
from django.utils.html import format_html
from django.utils.safestring import mark_safe
+class ANSI2HTMLConverter(object):
+ def __init__(self, white_bg=False):
+ self.prefix = '<pre class="ansi">'
+
+ def _write_prefix(self):
+ if self.prefix != '':
+ yield self.prefix
+ self.prefix = ''
+
+ def convert(self, input):
+ yield from self._write_prefix()
+ yield format_html('{}', input)
+
+ def finish(self):
+ yield from self._write_prefix()
+ yield '</pre>'
+ self.prefix = '<pre class="ansi">'
+
+def ansi2html(input, white_bg=False):
+ c = ANSI2HTMLConverter(white_bg=white_bg)
+ yield from c.convert(input)
+ yield from c.finish()
+
+
class LogView(View, metaclass=abc.ABCMeta):
@abc.abstractmethod
def content(request, **kwargs):
@@ -16,6 +47,9 @@ class LogView(View, metaclass=abc.ABCMeta):
# prolog includes Bootstrap's pre formatting (for consistency with
# the parent window's <pre> tags) and and a script to close the
# colorbox on Esc.
+ # Putting this in a template would be nice, but it would also
+ # consume more memory because we would not be able to just
+ # "yield from" into the StreamingHttpResponse.
HTML_PROLOG = mark_safe("""<!DOCTYPE html><html><head>
<style type="text/css">*{margin:0px;padding:0px;background:#333}
pre { font-size: 13px; line-height: 1.42857143; white-space: pre-wrap; word-wrap: break-word; max-width: 100%; color: #eee}
@@ -31,11 +65,23 @@ if (parent.jQuery && parent.jQuery.colorbox) {
}</script><body>""")
def generate_html(self):
- return format_html('{}<pre>{}</pre>', self.HTML_PROLOG, self.text)
+ yield self.HTML_PROLOG
+ yield from ansi2html(self.text)
def get(self, request, **kwargs):
self.text = self.content(request, **kwargs)
if request.GET.get('html', None) != '1':
return HttpResponse(self.text, content_type='text/plain')
- return HttpResponse(self.generate_html())
+ return StreamingHttpResponse(self.generate_html())
+
+if __name__ == "__main__":
+ import io
+ c = ANSI2HTMLConverter()
+ # Never split lines at \r
+ sys.stdin = io.TextIOWrapper(sys.stdin.buffer, newline='\n')
+ for line in sys.stdin:
+ for output in c.convert(line):
+ sys.stdout.write(output)
+ for output in c.finish():
+ sys.stdout.write(output)
diff --git a/tests/test_ansi2html.py b/tests/test_ansi2html.py
new file mode 100644
index 0000000..5ad993b
--- /dev/null
+++ b/tests/test_ansi2html.py
@@ -0,0 +1,30 @@
+# Test conversion of ANSI sequences into HTML
+#
+# Copyright (C) 2018 Red Hat, Inc.
+#
+# Author: Paolo Bonzini <pbonzini@redhat.com>
+
+import unittest
+
+from patchew.logviewer import ansi2html
+
+class ANSI2HTMLTest(unittest.TestCase):
+ def assertAnsi(self, test, expected, **kwargs):
+ self.assertEqual(''.join(ansi2html(test, **kwargs)),
+ '<pre class="ansi">%s</pre>' % expected,
+ repr(test))
+
+ def assertBlackBg(self, test, expected):
+ self.assertAnsi(test, expected)
+
+ def assertWhiteBg(self, test, expected):
+ self.assertAnsi(test, expected, white_bg=True)
+
+ # basic formatting tests
+ def test_basic(self):
+ self.assertBlackBg('a\nbc', 'a\nbc')
+ self.assertBlackBg('<', '<')
+
+
+if __name__ == '__main__':
+ unittest.main()
--
2.14.3