From 12871c2ee68bdb4015847c59829648885927c92c Mon Sep 17 00:00:00 2001
From: Clint Byrum <spamaps@debian.org>
Forwarded: yes
Date: Fri, 13 Sep 2019 18:08:55 -0700
Subject: [PATCH] Port to Python 3

This is able to completely display the examples. A few things were done:

- Use six to retain python 2 compatibility
- Windows-1252 is needed for ANSI art
- Different byte lengths require transitions to be smarter
---
 presentty/image.py      | 18 +++++++++---------
 presentty/presentty.py  | 13 +++++++------
 presentty/rst.py        | 25 +++++++++++++------------
 presentty/server.py     |  6 +++---
 presentty/slide.py      |  2 +-
 presentty/text.py       | 10 ++++++----
 presentty/transition.py | 14 +++++++++-----
 requirements.txt        |  2 +-
 8 files changed, 49 insertions(+), 41 deletions(-)

--- a/presentty/image.py
+++ b/presentty/image.py
@@ -14,14 +14,14 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 import subprocess
-import HTMLParser
+from six.moves import html_parser
 import re
 
 import PIL
 import PIL.ExifTags
 import urwid
 
-import slide
+from . import slide
 
 def nearest_color(x):
     if x < 0x30: return '0'
@@ -36,7 +36,7 @@ class ANSIImage(urwid.Widget):
         super(ANSIImage, self).__init__()
         self.uri = uri
         image = self._loadImage()
-        self.htmlparser = HTMLParser.HTMLParser()
+        self.htmlparser = html_parser.HTMLParser()
         self.ratio = float(image.size[0])/float(image.size[1])
         self.hinter = hinter
         if scale > 1:
@@ -116,7 +116,7 @@ class ANSIImage(urwid.Widget):
                                     stdin=subprocess.PIPE,
                                     stdout=subprocess.PIPE,
                                     stderr=subprocess.PIPE)
-        except OSError, e:
+        except OSError as e:
             if self._prime:
                 if e.errno == 2:
                     print("ERROR: jp2a is used but is not installed.")
@@ -147,7 +147,7 @@ class ANSIImage(urwid.Widget):
             line_list.append(' ' * total_width)
             attr_list.append([(padding_attr, 1)] * total_width)
 
-        for line in data.split('<br/>'):
+        for line in data.split(b'<br/>'):
             if not line:
                 continue
 
@@ -156,15 +156,15 @@ class ANSIImage(urwid.Widget):
             for fake_attr in range(0, left_pad):
                 line_attrs.append((padding_attr, 1))
 
-            for span in line.split('</span>'):
+            for span in line.split(b'</span>'):
 
                 if not span:
                     continue
-                m = spanre.match(span)
+                m = spanre.match(span.decode('utf-8'))
                 fg, bg, char = m.groups()
                 if '&' in char:
                     char = htmlparser.unescape(char)
-                char = char.encode('utf8')
+                char = char
                 line_text += char
                 props = []
                 # TODO: if bold is set, append bold to props
@@ -207,7 +207,7 @@ class ANSIImage(urwid.Widget):
             line_list.append(' ' * total_width)
             attr_list.append([(padding_attr, 1)] * total_width)
 
-        canvas = urwid.TextCanvas(line_list, attr_list)
+        canvas = urwid.TextCanvas([x.encode('utf-8') for x in line_list], attr_list)
         return canvas
 
 def main():
--- a/presentty/presentty.py
+++ b/presentty/presentty.py
@@ -18,12 +18,13 @@ import os
 import sys
 import time
 
+import six
 import urwid
 
-import slide
-import server
-import rst
-import palette
+from . import slide
+from . import server
+from . import rst
+from . import palette
 
 
 class Presenter(object):
@@ -152,11 +153,11 @@ def main():
         plt = palette.DARK_PALETTE
     hinter = slide.ScreenHinter()
     parser = rst.PresentationParser(plt, hinter)
-    program = parser.parse(unicode(open(args.file).read(), 'utf-8'), args.file)
+    program = parser.parse(six.text_type(open(args.file, 'rb').read(), 'utf-8'), args.file)
     if args.warnings:
         w = parser.warnings.getvalue()
         if w:
-            print w
+            print(w)
             sys.exit(1)
     p = Presenter(plt)
     p.setProgram(program)
--- a/presentty/rst.py
+++ b/presentty/rst.py
@@ -19,15 +19,16 @@ import docutils
 import docutils.frontend
 import docutils.parsers.rst
 import docutils.nodes
-import cStringIO as StringIO
+from six.moves import cStringIO as StringIO
 
+import six
 import urwid
 
-import slide
-import transition as transition_mod
-import image
-import ansiparser
-import text
+from . import slide
+from . import transition as transition_mod
+from . import image
+from . import ansiparser
+from . import text
 
 try:
     import PIL
@@ -139,7 +140,7 @@ class UrwidTranslator(docutils.nodes.Gen
         pass
 
     def visit_system_message(self, node):
-        #print node.astext()
+        #print(node.astext())
         raise docutils.nodes.SkipNode()
 
     def visit_section(self, node):
@@ -327,7 +328,7 @@ class UrwidTranslator(docutils.nodes.Gen
         for name in node['names']:
             p = ansiparser.ANSIParser()
             fn = os.path.join(self.basedir, name)
-            data = unicode(open(fn).read(), 'utf8')
+            data = six.text_type(open(fn, 'rb').read(), 'utf8')
             text = p.parse(data)
             animation.addFrame(text)
         self.slide.animations.append(animation)
@@ -447,7 +448,7 @@ class PresentationParser(object):
             'cowsay', CowsayDirective)
         docutils.parsers.rst.directives.register_directive(
             'hidetitle', HideTitleDirective)
-        self.warnings = StringIO.StringIO()
+        self.warnings = StringIO()
         self.settings = docutils.frontend.OptionParser(
             components=(docutils.parsers.rst.Parser,),
             defaults=dict(warning_stream=self.warnings)).get_default_values()
@@ -488,12 +489,12 @@ def main():
     slides = [int(x) for x in slides]
 
     if not args.render:
-        print document.pformat()
+        print(document.pformat())
         for i in slides:
-            print '-'*80
+            print('-'*80)
             s = visitor.program[i]
             for line in s.render((80,25)).text:
-                print line
+                print(line)
     else:
         screen = urwid.raw_display.Screen()
         with screen.start():
--- a/presentty/server.py
+++ b/presentty/server.py
@@ -15,9 +15,9 @@
 
 import os
 import threading
-import SocketServer
+from six.moves import socketserver
 
-class ConsoleHandler(SocketServer.StreamRequestHandler):
+class ConsoleHandler(socketserver.StreamRequestHandler):
     def handle(self):
         server = self.server.server
         while True:
@@ -53,7 +53,7 @@ class ConsoleHandler(SocketServer.Stream
                 size = server.size()
                 self.wfile.write('size %s %s\n' % size)
 
-class ThreadedTCPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
+class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
     allow_reuse_address=True
 
 class ConsoleServer(object):
--- a/presentty/slide.py
+++ b/presentty/slide.py
@@ -33,7 +33,7 @@ class SlidePile(urwid.Pile):
 class SlidePadding(urwid.Padding):
     def pack(self, size, focus=False):
         r = self._original_widget.pack(size, focus)
-        width = max(r[0] + self.left + self.right, self.min_width)
+        width = max(r[0] + self.left + self.right, self.min_width or 0)
         width = min(size[0], width)
         return (width, r[1])
 
--- a/presentty/text.py
+++ b/presentty/text.py
@@ -15,6 +15,8 @@
 
 import subprocess
 
+from six.moves import input as raw_input
+
 import urwid
 
 class FigletText(urwid.WidgetWrap):
@@ -34,7 +36,7 @@ class FigletText(urwid.WidgetWrap):
                                  stdin=subprocess.PIPE,
                                  stdout=subprocess.PIPE,
                                  stderr=subprocess.PIPE)
-        except OSError, e:
+        except OSError as e:
             if e.errno == 2:
                 print("ERROR: figlet is used but is not installed.")
             else:
@@ -42,7 +44,7 @@ class FigletText(urwid.WidgetWrap):
             raw_input("Press ENTER to continue.")
             data = "[Unable to run figlet]"
         else:
-            p.stdin.write(self.text)
+            p.stdin.write(self.text.encode('utf-8'))
             p.stdin.close()
             data = p.stdout.read()
             p.stderr.read()
@@ -66,7 +68,7 @@ class CowsayText(urwid.WidgetWrap):
                                  stdin=subprocess.PIPE,
                                  stdout=subprocess.PIPE,
                                  stderr=subprocess.PIPE)
-        except OSError, e:
+        except OSError as e:
             if e.errno == 2:
                 print("ERROR: cowsay is used but is not installed.")
             else:
@@ -74,7 +76,7 @@ class CowsayText(urwid.WidgetWrap):
             raw_input("Press ENTER to continue.")
             data = "[Unable to run cowsay]"
         else:
-            p.stdin.write(self.text)
+            p.stdin.write(self.text.encode('utf-8'))
             p.stdin.close()
             data = p.stdout.read()
             p.stderr.read()
--- a/presentty/transition.py
+++ b/presentty/transition.py
@@ -13,6 +13,7 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
+import six
 import urwid
 
 class Transition(urwid.Widget):
@@ -76,8 +77,12 @@ class DissolveTransition(Transition):
         buf = []
         for line in canvas.content():
             for (attr, cs, text) in line:
-                for char in unicode(text, 'utf8'):
-                    buf.append((attr, cs, char))
+                try:
+                    for char in six.text_type(text, 'utf8'):
+                        buf.append((attr, cs, char))
+                except UnicodeDecodeError:
+                    for char in six.text_type(text, 'windows-1252'):
+                        buf.append((attr, cs, char))
         return buf
 
     def render(self, size, focus=False):
@@ -95,7 +100,7 @@ class DissolveTransition(Transition):
         current_rgb = None
         current_props = None
         background = urwid.AttrSpec('light gray', 'black')
-        for i in range(len(self._oldbuf)):
+        for i in range(min(len(self._oldbuf), len(self._newbuf))):
             oldattr, oldcs, oldchar = self._oldbuf[i]
             newattr, newcs, newchar = self._newbuf[i]
             oldrgb = oldattr.get_rgb_values()
@@ -118,7 +123,6 @@ class DissolveTransition(Transition):
             else:
                 char = oldchar
                 charattr = oldattr
-            char = char.encode('utf8')
             line_text += char
             rgb = []
             props = []
@@ -151,7 +155,7 @@ class DissolveTransition(Transition):
                 line_text = ''
                 attr_list.append(line_attrs)
                 line_attrs = []
-        canvas = urwid.TextCanvas(line_list, attr_list)
+        canvas = urwid.TextCanvas([x.encode('utf-8') for x in line_list], attr_list)
         return canvas
 
 class CutTransition(Transition):
--- a/requirements.txt
+++ b/requirements.txt
@@ -4,4 +4,4 @@ urwid
 docutils>=0.12
 pygments
 Pillow>=2.4.0,<3.0.0 # or PIL.  Optional, only used for images. # MIT
-
+six
