[py3porters-devel] Porting of apt-listchanges to Python 3

James Lu GLolol1 at hotmail.com
Wed Jun 3 02:05:26 UTC 2015


Hello all,

Lately, there has been an ongoing effort 
<https://wiki.debian.org/Python/Python3Port> to port Python code in 
Debian to Python 3. So, I've decided to take a look at apt-listchanges, 
and modified it so that it supports both Python 2 and 3. The code is 
currently hosted at GitHub, since I don't have access to alioth: 
https://github.com/GLolol/apt-listchanges

I had to make a lot of changes for the code to work properly. For 
starters I migrated the Gtk frontend from the deprecated PyGTK to the 
newer PyGObject: this involved changing Glade signals from destroy-event 
to destroy for window closing, updating imports (gtk.glade->gettext for 
example), and at the same time transitioning the code to GTK 3. For some 
of this, GNOME's pygi-convert.sh and Glade were quite helpful.

I also adjusted many imports using "try, import Y, except ImportError, 
import X as Y" clauses, which preserve compatibility even though many 
libraries have renamed in Python 3. Text strings had to be updated to 
handle the difference between bytes and str, etc. Finally, I changed the 
build system from python-support to dh_python3 in order to build Python 
3 .debs.

So far, I've tested all the various frontends (pager, browser, 
xterm-pager, xterm-browser, text, mail, gtk) from the command line on 
.deb files, which seemed to work fine. The only thing untested is the 
actual APT pipeline support, since I haven't gotten any NEWS files that 
would be displayed there. Anyone, please feel free to comment and review 
the changes, which can be found here: 
https://github.com/GLolol/apt-listchanges/compare/5e72a2cf32c2395f027bfb1c3cf3e4e72dceef4a...debian-sid
(patch form <cid:part4.06010006.07000306 at hotmail.com>).

Best,
James

<https://github.com/GLolol/apt-listchanges/compare/5e72a2cf32c2395f027bfb1c3cf3e4e72dceef4a...debian-sid> 

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.alioth.debian.org/pipermail/py3porters-devel/attachments/20150602/95bb60a1/attachment-0001.html>
-------------- next part --------------
From 642fc81252aef1d624699c0e06ce98f2b158fea9 Mon Sep 17 00:00:00 2001
From: James Lu <GLolol1 at hotmail.com>
Date: Mon, 1 Jun 2015 21:55:08 -0700
Subject: [PATCH 1/4] Begin port to Python 3

- Replacing python-gtk2 and python-glade2 code with python3-gi
- Many syntax / import fixes in a way that should preserve compatibility with both Python 2 and 3
---
 apt-listchanges.py                    |  24 +++---
 apt-listchanges/ALCConfig.py          |  11 ++-
 apt-listchanges/AptListChangesGtk.py  |  58 +++++++------
 apt-listchanges/DebianFiles.py        |  12 +--
 apt-listchanges/apt-listchanges.glade | 152 ----------------------------------
 apt-listchanges/apt-listchanges.ui    | 144 ++++++++++++++++++++++++++++++++
 apt-listchanges/apt_listchanges.py    |  36 ++++----
 7 files changed, 223 insertions(+), 214 deletions(-)
 delete mode 100644 apt-listchanges/apt-listchanges.glade
 create mode 100644 apt-listchanges/apt-listchanges.ui

diff --git a/apt-listchanges.py b/apt-listchanges.py
index d73affb..39a88b8 100755
--- a/apt-listchanges.py
+++ b/apt-listchanges.py
@@ -26,8 +26,12 @@
 
 import sys, os, os.path
 import apt_pkg
-import anydbm
-import commands
+try:
+    import subprocess
+    from dbm import ndbm
+except ImportError:
+    import anydbm as ndbm
+    import commands as subprocess
 
 sys.path += [os.path.dirname(sys.argv[0]) + '/apt-listchanges', '/usr/share/apt-listchanges']
 from ALChacks import *
@@ -83,8 +87,8 @@ def main():
 
     if config.save_seen:
         try:
-            seen = anydbm.open(config.save_seen, 'c')
-            seen.has_key('foo%0')
+            seen = dbm.ndbm.open(config.save_seen, 'c')
+            'foo%0' in seen
         except:
             sys.stderr.write(_("database %s failed to load.\n") % config.save_seen)
             sys.exit(1)
@@ -119,7 +123,7 @@ def main():
         fromversion = None
 
         if not config.show_all:
-            if config.save_seen and seen.has_key(srcpackage):
+            if config.save_seen and srcpakge in seen:
                 fromversion = seen[srcpackage]
             elif config.since:
                 fromversion = config.since
@@ -140,7 +144,7 @@ def main():
         #
         # This is why even if we've seen a package we may miss bits of
         # changelog in some odd cases
-        if found.has_key(srcpackage) and \
+        if srcpackage in found and \
                 apt_pkg.version_compare(srcversion, found[srcpackage]) <= 0:
             continue
 
@@ -186,7 +190,7 @@ def main():
 
             package = rec.package
             header = _('News for %s') % package
-            if len(filter(lambda x: x != package, source_packages[package])) > 0:
+            if len([x for x in source_packages[package] if x != package]) > 0:
                 # Differing source and binary packages
                 header += ' (' + ' '.join(source_packages[package]) + ')'
             news += '--- ' + header + ' ---\n' + rec.changes
@@ -197,7 +201,7 @@ def main():
 
             package = rec.package
             header = _('Changes for %s') % package
-            if len(filter(lambda x: x != package, source_packages[package])) > 0:
+            if len([x for x in source_packages[package] if x != package]) > 0:
                 # Differing source and binary packages
                 header += ' (' + ' '.join(source_packages[package]) + ')'
             changes += '--- ' + header + ' ---\n' + rec.changes
@@ -226,7 +230,7 @@ def main():
                 sys.stderr.write(_("Confirmation failed, don't save seen state")+'.\n')
                 sys.exit(0)
 
-        hostname = commands.getoutput('hostname')
+        hostname = subprocess.getoutput('hostname')
 
         if config.email_address and os.path.exists("/usr/sbin/sendmail"):
             if changes:
@@ -239,7 +243,7 @@ def main():
 
         # Write out seen db
         if config.save_seen:
-            seen = anydbm.open(config.save_seen, 'c')
+            seen = dbm.ndbm.open(config.save_seen, 'c')
             for (key, value) in seen_new.items():
                 seen[key] = value
             seen.close()
diff --git a/apt-listchanges/ALCConfig.py b/apt-listchanges/ALCConfig.py
index 1f490a8..b15e8f8 100644
--- a/apt-listchanges/ALCConfig.py
+++ b/apt-listchanges/ALCConfig.py
@@ -23,7 +23,10 @@
 #   MA 02111-1307 USA
 #
 
-import ConfigParser
+try:
+    import ConfigParser
+except ImportError:
+    import configparser as ConfigParser
 import getopt
 import sys, os
 import re
@@ -130,8 +133,8 @@ def getopt(self, argv):
                 if arg in self.allowed_which:
                     self.which = arg
                 else:
-                    print _('Unknown option %s for --which.  Allowed are: %s.') % \
-                        (arg, ', '.join(self.allowed_which))
+                    print(_('Unknown option %s for --which.  Allowed are: %s.') % \
+                        (arg, ', '.join(self.allowed_which)))
                     sys.exit(1)
             elif opt == '--debug':
                 self.debug = 1
@@ -145,7 +148,7 @@ def getopt(self, argv):
 
         if self.since is not None:
             if len(args) is not 1:
-                print _('--since=<version> expects a only path to a .deb')
+                print(_('--since=<version> expects a only path to a .deb'))
                 sys.exit(1)
             self.save_seen = None
         return args
diff --git a/apt-listchanges/AptListChangesGtk.py b/apt-listchanges/AptListChangesGtk.py
index b7c1fcd..bd0ab24 100644
--- a/apt-listchanges/AptListChangesGtk.py
+++ b/apt-listchanges/AptListChangesGtk.py
@@ -2,48 +2,52 @@
 
 from apt_listchanges import frontend
 
-import pygtk
-pygtk.require('2.0')
-import gtk
-import gobject
-import gtk.glade
+import gi
+gi.require_version('Gtk', '3.0')
+import gettext
+
+from gi.repository import Gtk
+from gi.repository import GObject
 
 from ALChacks import *
 
 # set the i18n dirs
-gtk.glade.bindtextdomain("apt-listchanges", "/usr/share/locale")
-gtk.glade.textdomain("apt-listchanges")
+gettext.bindtextdomain("apt-listchanges", "/usr/share/locale")
+gettext.textdomain("apt-listchanges")
 
 class gtk2(frontend):
     def flush_interface(self):
-        while gtk.events_pending():
-            gtk.main_iteration()
+        while Gtk.events_pending():
+            Gtk.main_iteration()
 
     def cb_close(self, widget):
         if self.button_close.get_property("sensitive") == False:
             # window manager was used to close before the parsing was complete
             sys.exit()
-        gtk.main_quit()
+        Gtk.main_quit()
 
     def __init__(self, packages, config):
         frontend.__init__(self,packages, config)
+        self.builder = Gtk.Builder()
         try:
-            file("apt-listchanges/apt-listchanges.glade").close()
-            self.glade = gtk.glade.XML("apt-listchanges/apt-listchanges.glade")
-        except:
-            self.glade = gtk.glade.XML("/usr/share/apt-listchanges/apt-listchanges.glade")
-        self.window_main = self.glade.get_widget("window_main")
-        self.window_main.connect("destroy", self.cb_close)
-        self.glade.signal_connect("on_button_close_clicked", self.cb_close)
-        self.progressbar_main = self.glade.get_widget("progressbar_main")
-        self.button_close = self.glade.get_widget("button_close")
+            self.builder.add_from_file("apt-listchanges/apt-listchanges.ui")
+        except ValueError:
+            self.builder.add_from_file("/usr/share/apt-listchanges/apt-listchanges.ui")
+        self.window_main = self.builder.get_object("window_main")
+        handlers = {
+            "on_button_close_clicked": self.cb_close,
+            "on_window_main_destroy_event": self.cb_close,
+        }
+        self.progressbar_main = self.builder.get_object("progressbar_main")
+        self.button_close = self.builder.get_object("button_close")
+        self.builder.connect_signals(handlers)
         self.flush_interface()
 
     def display_output(self,text):
         self.button_close.set_sensitive(True)
-        buf = self.glade.get_widget("textview_main").get_buffer()
+        buf = self.builder.get_object("textview_main").get_buffer()
         buf.set_text(self._render(text))
-        gtk.main()
+        Gtk.main()
 
     def update_progress(self):
         if not hasattr(self,'progress'):
@@ -60,12 +64,12 @@ def progress_done(self):
         self.flush_interface()
 
     def confirm(self):
-        m = gtk.MessageDialog(self.window_main,
-                              gtk.DIALOG_MODAL,
-                              gtk.MESSAGE_QUESTION,
-                              gtk.BUTTONS_YES_NO)
-        m.set_default_response(gtk.RESPONSE_YES)
+        m = Gtk.MessageDialog(self.window_main,
+                              Gtk.DialogFlags.MODAL,
+                              Gtk.MessageType.QUESTION,
+                              Gtk.ButtonsType.YES_NO)
+        m.set_default_response(Gtk.ResponseType.YES)
         m.set_markup("<big><b>%s</b></big>\n\n%s" % (_("Continue Installation?"), _("You can abort the installation if you select 'no'.")))
-        if m.run() == gtk.RESPONSE_NO:
+        if m.run() == Gtk.ResponseType.NO:
             return False
         return True
diff --git a/apt-listchanges/DebianFiles.py b/apt-listchanges/DebianFiles.py
index 0efc41e..18818dd 100644
--- a/apt-listchanges/DebianFiles.py
+++ b/apt-listchanges/DebianFiles.py
@@ -23,8 +23,9 @@
 #   MA 02111-1307 USA
 #
 
+from __future__ import print_function
 import re
-import sys, os, os.path
+import sys, os
 import tempfile
 import gzip
 import errno
@@ -35,6 +36,7 @@
 
 import apt_pkg
 from ALChacks import *
+from functools import reduce
 
 # TODO:
 # indexed lookups by package at least, maybe by arbitrary field
@@ -110,8 +112,8 @@ def readdeb(self, deb):
         self.stanzas.append(ControlStanza(fh.read()))
 
     def find(self, field, value):
-        if self.index.has_key(field):
-            if self.index[field].has_key(value):
+        if field in self.index:
+            if value in self.index[field]:
                 return self.index[field][value]
             else:
                 return None
@@ -191,13 +193,13 @@ def read_changelog(self, filename, since_version, reverse=False):
         for filename in filenames:
             try:
                 if os.path.isdir(filename):
-                    print >> sys.stderr, _("Ignoring `%s' (seems to be a directory!)") % filename
+                    print(_("Ignoring `%s' (seems to be a directory!)") % filename, file=sys.stderr)
                 elif filename.endswith('.gz'):
                     fd = gzip.GzipFile(filename)
                 else:
                     fd = open(filename)
                 break
-            except IOError, e:
+            except IOError as e:
                 if e.errno == errno.ENOENT:
                     pass
                 else:
diff --git a/apt-listchanges/apt-listchanges.glade b/apt-listchanges/apt-listchanges.glade
deleted file mode 100644
index d5998a9..0000000
--- a/apt-listchanges/apt-listchanges.glade
+++ /dev/null
@@ -1,152 +0,0 @@
-<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
-<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
-
-<glade-interface>
-
-<widget class="GtkWindow" id="window_main">
-  <property name="border_width">6</property>
-  <property name="visible">True</property>
-  <property name="title" translatable="yes">List the changes</property>
-  <property name="type">GTK_WINDOW_TOPLEVEL</property>
-  <property name="window_position">GTK_WIN_POS_CENTER</property>
-  <property name="modal">False</property>
-  <property name="default_width">600</property>
-  <property name="default_height">400</property>
-  <property name="resizable">True</property>
-  <property name="destroy_with_parent">False</property>
-  <property name="decorated">True</property>
-  <property name="skip_taskbar_hint">False</property>
-  <property name="skip_pager_hint">False</property>
-  <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
-  <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
-  <property name="focus_on_map">True</property>
-  <signal name="destroy_event" handler="on_window_main_destroy_event" last_modification_time="Sat, 05 Feb 2005 13:12:24 GMT"/>
-
-  <child>
-    <widget class="GtkVBox" id="vbox1">
-      <property name="border_width">6</property>
-      <property name="visible">True</property>
-      <property name="homogeneous">False</property>
-      <property name="spacing">6</property>
-
-      <child>
-	<widget class="GtkLabel" id="label_header">
-	  <property name="visible">True</property>
-	  <property name="label" translatable="yes"><big><b>Changelogs</b></big>
-
-The following changes are found in the packages you are about to install:</property>
-	  <property name="use_underline">True</property>
-	  <property name="use_markup">True</property>
-	  <property name="justify">GTK_JUSTIFY_LEFT</property>
-	  <property name="wrap">False</property>
-	  <property name="selectable">False</property>
-	  <property name="xalign">0</property>
-	  <property name="yalign">0.5</property>
-	  <property name="xpad">0</property>
-	  <property name="ypad">0</property>
-	  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
-	  <property name="width_chars">-1</property>
-	  <property name="single_line_mode">False</property>
-	  <property name="angle">0</property>
-	</widget>
-	<packing>
-	  <property name="padding">0</property>
-	  <property name="expand">False</property>
-	  <property name="fill">False</property>
-	</packing>
-      </child>
-
-      <child>
-	<widget class="GtkScrolledWindow" id="scrolledwindow2">
-	  <property name="visible">True</property>
-	  <property name="can_focus">True</property>
-	  <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
-	  <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
-	  <property name="shadow_type">GTK_SHADOW_IN</property>
-	  <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
-
-	  <child>
-	    <widget class="GtkTextView" id="textview_main">
-	      <property name="visible">True</property>
-	      <property name="can_focus">True</property>
-	      <property name="editable">False</property>
-	      <property name="overwrite">False</property>
-	      <property name="accepts_tab">True</property>
-	      <property name="justification">GTK_JUSTIFY_LEFT</property>
-	      <property name="wrap_mode">GTK_WRAP_NONE</property>
-	      <property name="cursor_visible">False</property>
-	      <property name="pixels_above_lines">0</property>
-	      <property name="pixels_below_lines">0</property>
-	      <property name="pixels_inside_wrap">0</property>
-	      <property name="left_margin">0</property>
-	      <property name="right_margin">0</property>
-	      <property name="indent">0</property>
-	      <property name="text" translatable="yes">Reading changelogs. Please wait.</property>
-	    </widget>
-	  </child>
-	</widget>
-	<packing>
-	  <property name="padding">0</property>
-	  <property name="expand">True</property>
-	  <property name="fill">True</property>
-	</packing>
-      </child>
-
-      <child>
-	<widget class="GtkProgressBar" id="progressbar_main">
-	  <property name="orientation">GTK_PROGRESS_LEFT_TO_RIGHT</property>
-	  <property name="fraction">0</property>
-	  <property name="pulse_step">0.10000000149</property>
-	  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
-	</widget>
-	<packing>
-	  <property name="padding">0</property>
-	  <property name="expand">False</property>
-	  <property name="fill">False</property>
-	</packing>
-      </child>
-
-      <child>
-	<widget class="GtkHBox" id="hbox4">
-	  <property name="visible">True</property>
-	  <property name="homogeneous">False</property>
-	  <property name="spacing">6</property>
-
-	  <child>
-	    <widget class="GtkHButtonBox" id="hbuttonbox1">
-	      <property name="visible">True</property>
-	      <property name="layout_style">GTK_BUTTONBOX_END</property>
-	      <property name="spacing">6</property>
-
-	      <child>
-		<widget class="GtkButton" id="button_close">
-		  <property name="visible">True</property>
-		  <property name="sensitive">False</property>
-		  <property name="can_default">True</property>
-		  <property name="can_focus">True</property>
-		  <property name="label">gtk-close</property>
-		  <property name="use_stock">True</property>
-		  <property name="relief">GTK_RELIEF_NORMAL</property>
-		  <property name="focus_on_click">True</property>
-		  <signal name="clicked" handler="on_button_close_clicked" last_modification_time="Sat, 05 Feb 2005 11:13:29 GMT"/>
-		</widget>
-	      </child>
-	    </widget>
-	    <packing>
-	      <property name="padding">0</property>
-	      <property name="expand">True</property>
-	      <property name="fill">True</property>
-	    </packing>
-	  </child>
-	</widget>
-	<packing>
-	  <property name="padding">0</property>
-	  <property name="expand">False</property>
-	  <property name="fill">False</property>
-	</packing>
-      </child>
-    </widget>
-  </child>
-</widget>
-
-</glade-interface>
diff --git a/apt-listchanges/apt-listchanges.ui b/apt-listchanges/apt-listchanges.ui
new file mode 100644
index 0000000..69eb02b
--- /dev/null
+++ b/apt-listchanges/apt-listchanges.ui
@@ -0,0 +1,144 @@
+<?xml version="1.0"?>
+<!--*- mode: xml -*-->
+<interface>
+  <object class="GtkTextBuffer" id="textbuffer1">
+    <property name="text">Reading changelogs. Please wait.</property>
+  </object>
+  <object class="GtkWindow" id="window_main">
+    <property name="border_width">6</property>
+    <property name="visible">True</property>
+    <property name="title" translatable="yes">List the changes</property>
+    <property name="type">GTK_WINDOW_TOPLEVEL</property>
+    <property name="window_position">GTK_WIN_POS_CENTER</property>
+    <property name="modal">False</property>
+    <property name="default_width">600</property>
+    <property name="default_height">400</property>
+    <property name="resizable">True</property>
+    <property name="destroy_with_parent">False</property>
+    <property name="decorated">True</property>
+    <property name="skip_taskbar_hint">False</property>
+    <property name="skip_pager_hint">False</property>
+    <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
+    <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+    <property name="focus_on_map">True</property>
+    <signal handler="on_window_main_destroy_event" last_modification_time="Sat, 05 Feb 2005 13:12:24 GMT" name="destroy_event"/>
+    <child>
+      <object class="GtkVBox" id="vbox1">
+        <property name="border_width">6</property>
+        <property name="visible">True</property>
+        <property name="homogeneous">False</property>
+        <property name="spacing">6</property>
+        <child>
+          <object class="GtkLabel" id="label_header">
+            <property name="visible">True</property>
+            <property name="label" translatable="yes"><big><b>Changelogs</b></big>
+
+The following changes are found in the packages you are about to install:</property>
+            <property name="use_underline">True</property>
+            <property name="use_markup">True</property>
+            <property name="justify">GTK_JUSTIFY_LEFT</property>
+            <property name="wrap">False</property>
+            <property name="selectable">False</property>
+            <property name="xalign">0</property>
+            <property name="yalign">0.5</property>
+            <property name="xpad">0</property>
+            <property name="ypad">0</property>
+            <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+            <property name="width_chars">-1</property>
+            <property name="single_line_mode">False</property>
+            <property name="angle">0</property>
+          </object>
+          <packing>
+            <property name="padding">0</property>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkScrolledWindow" id="scrolledwindow2">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+            <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+            <property name="shadow_type">GTK_SHADOW_IN</property>
+            <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+            <child>
+              <object class="GtkTextView" id="textview_main">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="editable">False</property>
+                <property name="overwrite">False</property>
+                <property name="accepts_tab">True</property>
+                <property name="justification">GTK_JUSTIFY_LEFT</property>
+                <property name="wrap_mode">GTK_WRAP_NONE</property>
+                <property name="cursor_visible">False</property>
+                <property name="pixels_above_lines">0</property>
+                <property name="pixels_below_lines">0</property>
+                <property name="pixels_inside_wrap">0</property>
+                <property name="left_margin">0</property>
+                <property name="right_margin">0</property>
+                <property name="indent">0</property>
+                <property name="buffer">textbuffer1</property>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="padding">0</property>
+            <property name="expand">True</property>
+            <property name="fill">True</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkProgressBar" id="progressbar_main">
+            <property name="orientation">GTK_PROGRESS_LEFT_TO_RIGHT</property>
+            <property name="fraction">0</property>
+            <property name="pulse_step">0.10000000149</property>
+            <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+          </object>
+          <packing>
+            <property name="padding">0</property>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkHBox" id="hbox4">
+            <property name="visible">True</property>
+            <property name="homogeneous">False</property>
+            <property name="spacing">6</property>
+            <child>
+              <object class="GtkHButtonBox" id="hbuttonbox1">
+                <property name="visible">True</property>
+                <property name="layout_style">GTK_BUTTONBOX_END</property>
+                <property name="spacing">6</property>
+                <child>
+                  <object class="GtkButton" id="button_close">
+                    <property name="visible">True</property>
+                    <property name="sensitive">False</property>
+                    <property name="can_default">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="label">gtk-close</property>
+                    <property name="use_stock">True</property>
+                    <property name="relief">GTK_RELIEF_NORMAL</property>
+                    <property name="focus_on_click">True</property>
+                    <signal handler="on_button_close_clicked" last_modification_time="Sat, 05 Feb 2005 11:13:29 GMT" name="clicked"/>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="padding">0</property>
+                <property name="expand">True</property>
+                <property name="fill">True</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="padding">0</property>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+</interface>
diff --git a/apt-listchanges/apt_listchanges.py b/apt-listchanges/apt_listchanges.py
index cc879e5..a7bc273 100644
--- a/apt-listchanges/apt_listchanges.py
+++ b/apt-listchanges/apt_listchanges.py
@@ -25,16 +25,21 @@
 
 import sys
 import os
-import os.path
 import re
 import locale
 import email.Message
 import email.Header
 import email.Charset
-import cStringIO
+try:
+    import io
+except ImportError:
+    import cStringIO as io
 import tempfile
 from ALChacks import *
 
+if sys.version_info[0] >= 3:
+    unicode = str
+
 # TODO:
 # newt-like frontend, or maybe some GUI bit
 # keep track of tar/dpkg-deb errors like in pre-2.0
@@ -107,7 +112,7 @@ def read_apt_pipeline(config):
     return ordered_filenames
 
 def mail_changes(address, changes, subject):
-    print "apt-listchanges: " + _("Mailing %s: %s") % (address, subject)
+    print("apt-listchanges: " + _("Mailing %s: %s") % (address, subject))
 
     charset = email.Charset.Charset('utf-8')
     charset.body_encoding = '8bit'
@@ -145,13 +150,12 @@ def make_frontend(name, packages, config):
     # import from that. that would mean a uniform mechanism for all
     # frontends (that would become small files inside
     if name == "gtk":
-        if os.environ.has_key("DISPLAY"):
+        if "DISPLAY" in os.environ:
             try:
                 gtk = __import__("AptListChangesGtk")
                 frontends[name] = gtk.gtk2
-            except ImportError, e:
-                sys.stderr.write(_("The gtk frontend needs a working python-gtk2 "
-                                   "and python-glade2.\n"
+            except ImportError as e:
+                sys.stderr.write(_("The gtk frontend needs a working python-gi.\n"
                                    "Those imports can not be found. Falling back "
                                    "to pager.\n"
                                    "The error is: %s\n") % e)
@@ -159,7 +163,7 @@ def make_frontend(name, packages, config):
         else:
             name = 'pager'
 
-    if not frontends.has_key(name):
+    if name not in frontends:
         return None
     return frontends[name](packages, config)
 
@@ -212,7 +216,7 @@ def display_output(self, text):
         sock.close()
         db = dc.Debconf(read=dcfd, write=dcfd)
         tmp = tempfile.NamedTemporaryFile(prefix="apt-listchanges-tmp")
-        os.fchmod(tmp.fileno(), 0644)
+        os.fchmod(tmp.fileno(), 0o644)
         tmp.write('''Template: apt-listchanges/info
 Type: title
 Description: NEWS
@@ -245,7 +249,7 @@ class ttyconfirm:
     def confirm(self):
         try:
             tty = open('/dev/tty', 'r+')
-        except IOError, e:
+        except IOError as e:
             return -1
         tty.write('apt-listchanges: ' + _('Do you want to continue? [Y/n] '))
         tty.flush()
@@ -320,12 +324,12 @@ class pager(runcommand, ttyconfirm, fancyprogress, frontend):
     def __init__(self, *args):
         if not 'LESS' in os.environ:
             os.environ['LESS'] = "-P?e(q to quit)"
-        apply(frontend.__init__, [self] + list(args))
+        frontend.__init__(*[self] + list(args))
         self.command = self.config.get('pager', 'sensible-pager')
 
 class xterm(runcommand, ttyconfirm, fancyprogress, frontend):
     def __init__(self, *args):
-        apply(frontend.__init__, [self] + list(args))
+        frontend.__init__(*[self] + list(args))
         self.mode = os.P_NOWAIT
         self.xterm = self.config.get('xterm', 'x-terminal-emulator')
 
@@ -334,7 +338,7 @@ def get_command(self):
 
 class xterm_pager(xterm):
     def __init__(self, *args):
-        apply(xterm.__init__, [self] + list(args))
+        xterm.__init__(*[self] + list(args))
         self.xterm_command = self.config.get('pager', 'sensible-pager')
 
 class html:
@@ -354,7 +358,7 @@ class html:
     title = '''apt-listchanges output'''
 
     def _render(self, text):
-        htmltext = cStringIO.StringIO()
+        htmltext = io.StringIO()
         htmltext.write('''<html>
         <head>
         <title>''')
@@ -388,13 +392,13 @@ def _render(self, text):
 
 class browser(html, pager):
     def __init__(self, *args):
-        apply(pager.__init__, [self] + list(args))
+        pager.__init__(*[self] + list(args))
         self.command = self.config.get('browser', 'sensible-browser')
     def set_title(self, text):
         self.title = text
 
 class xterm_browser(html, xterm):
     def __init__(self, *args):
-        apply(xterm.__init__, [self] + list(args))
+        xterm.__init__(*[self] + list(args))
         self.xterm_command = self.config.get('browser', 'sensible-browser')
 

From 7ce945e328434fd6fbb1d7598b7cb85a0107e369 Mon Sep 17 00:00:00 2001
From: James Lu <GLolol1 at hotmail.com>
Date: Tue, 2 Jun 2015 17:41:02 -0700
Subject: [PATCH 2/4] fix browser and gtk frontends

- In Glade, migrate the signal from destroy-event to destroy. Otherwise, the window doesn't get the event to close properly.
---
 apt-listchanges/AptListChangesGtk.py |  7 ++-
 apt-listchanges/apt-listchanges.ui   | 88 +++++++++++++-----------------------
 apt-listchanges/apt_listchanges.py   |  6 +--
 3 files changed, 37 insertions(+), 64 deletions(-)

diff --git a/apt-listchanges/AptListChangesGtk.py b/apt-listchanges/AptListChangesGtk.py
index bd0ab24..c00789f 100644
--- a/apt-listchanges/AptListChangesGtk.py
+++ b/apt-listchanges/AptListChangesGtk.py
@@ -8,6 +8,7 @@
 
 from gi.repository import Gtk
 from gi.repository import GObject
+import sys
 
 from ALChacks import *
 
@@ -21,10 +22,8 @@ def flush_interface(self):
             Gtk.main_iteration()
 
     def cb_close(self, widget):
-        if self.button_close.get_property("sensitive") == False:
-            # window manager was used to close before the parsing was complete
-            sys.exit()
         Gtk.main_quit()
+        sys.exit()
 
     def __init__(self, packages, config):
         frontend.__init__(self,packages, config)
@@ -36,7 +35,7 @@ def __init__(self, packages, config):
         self.window_main = self.builder.get_object("window_main")
         handlers = {
             "on_button_close_clicked": self.cb_close,
-            "on_window_main_destroy_event": self.cb_close,
+            "on_window_main_destroy": self.cb_close,
         }
         self.progressbar_main = self.builder.get_object("progressbar_main")
         self.button_close = self.builder.get_object("button_close")
diff --git a/apt-listchanges/apt-listchanges.ui b/apt-listchanges/apt-listchanges.ui
index 69eb02b..9963b97 100644
--- a/apt-listchanges/apt-listchanges.ui
+++ b/apt-listchanges/apt-listchanges.ui
@@ -1,141 +1,115 @@
-<?xml version="1.0"?>
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.18.3 -->
 <!--*- mode: xml -*-->
 <interface>
+  <requires lib="gtk+" version="3.0"/>
   <object class="GtkTextBuffer" id="textbuffer1">
     <property name="text">Reading changelogs. Please wait.</property>
   </object>
   <object class="GtkWindow" id="window_main">
-    <property name="border_width">6</property>
     <property name="visible">True</property>
+    <property name="can_focus">False</property>
+    <property name="border_width">6</property>
     <property name="title" translatable="yes">List the changes</property>
-    <property name="type">GTK_WINDOW_TOPLEVEL</property>
-    <property name="window_position">GTK_WIN_POS_CENTER</property>
-    <property name="modal">False</property>
+    <property name="window_position">center</property>
     <property name="default_width">600</property>
     <property name="default_height">400</property>
-    <property name="resizable">True</property>
-    <property name="destroy_with_parent">False</property>
-    <property name="decorated">True</property>
-    <property name="skip_taskbar_hint">False</property>
-    <property name="skip_pager_hint">False</property>
-    <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
-    <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
-    <property name="focus_on_map">True</property>
-    <signal handler="on_window_main_destroy_event" last_modification_time="Sat, 05 Feb 2005 13:12:24 GMT" name="destroy_event"/>
+    <signal name="destroy" handler="on_window_main_destroy" swapped="no"/>
     <child>
       <object class="GtkVBox" id="vbox1">
-        <property name="border_width">6</property>
         <property name="visible">True</property>
-        <property name="homogeneous">False</property>
+        <property name="can_focus">False</property>
+        <property name="border_width">6</property>
         <property name="spacing">6</property>
         <child>
           <object class="GtkLabel" id="label_header">
             <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="xalign">0</property>
             <property name="label" translatable="yes"><big><b>Changelogs</b></big>
 
 The following changes are found in the packages you are about to install:</property>
-            <property name="use_underline">True</property>
             <property name="use_markup">True</property>
-            <property name="justify">GTK_JUSTIFY_LEFT</property>
-            <property name="wrap">False</property>
-            <property name="selectable">False</property>
-            <property name="xalign">0</property>
-            <property name="yalign">0.5</property>
-            <property name="xpad">0</property>
-            <property name="ypad">0</property>
-            <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
-            <property name="width_chars">-1</property>
-            <property name="single_line_mode">False</property>
-            <property name="angle">0</property>
+            <property name="use_underline">True</property>
           </object>
           <packing>
-            <property name="padding">0</property>
             <property name="expand">False</property>
             <property name="fill">False</property>
+            <property name="position">0</property>
           </packing>
         </child>
         <child>
           <object class="GtkScrolledWindow" id="scrolledwindow2">
             <property name="visible">True</property>
             <property name="can_focus">True</property>
-            <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
-            <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
-            <property name="shadow_type">GTK_SHADOW_IN</property>
-            <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+            <property name="shadow_type">in</property>
             <child>
               <object class="GtkTextView" id="textview_main">
                 <property name="visible">True</property>
                 <property name="can_focus">True</property>
                 <property name="editable">False</property>
-                <property name="overwrite">False</property>
-                <property name="accepts_tab">True</property>
-                <property name="justification">GTK_JUSTIFY_LEFT</property>
-                <property name="wrap_mode">GTK_WRAP_NONE</property>
                 <property name="cursor_visible">False</property>
-                <property name="pixels_above_lines">0</property>
-                <property name="pixels_below_lines">0</property>
-                <property name="pixels_inside_wrap">0</property>
-                <property name="left_margin">0</property>
-                <property name="right_margin">0</property>
-                <property name="indent">0</property>
                 <property name="buffer">textbuffer1</property>
               </object>
             </child>
           </object>
           <packing>
-            <property name="padding">0</property>
             <property name="expand">True</property>
             <property name="fill">True</property>
+            <property name="position">1</property>
           </packing>
         </child>
         <child>
           <object class="GtkProgressBar" id="progressbar_main">
-            <property name="orientation">GTK_PROGRESS_LEFT_TO_RIGHT</property>
-            <property name="fraction">0</property>
+            <property name="can_focus">False</property>
             <property name="pulse_step">0.10000000149</property>
-            <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
           </object>
           <packing>
-            <property name="padding">0</property>
             <property name="expand">False</property>
             <property name="fill">False</property>
+            <property name="position">2</property>
           </packing>
         </child>
         <child>
           <object class="GtkHBox" id="hbox4">
             <property name="visible">True</property>
-            <property name="homogeneous">False</property>
+            <property name="can_focus">False</property>
             <property name="spacing">6</property>
             <child>
               <object class="GtkHButtonBox" id="hbuttonbox1">
                 <property name="visible">True</property>
-                <property name="layout_style">GTK_BUTTONBOX_END</property>
+                <property name="can_focus">False</property>
                 <property name="spacing">6</property>
+                <property name="layout_style">end</property>
                 <child>
                   <object class="GtkButton" id="button_close">
+                    <property name="label">gtk-close</property>
                     <property name="visible">True</property>
                     <property name="sensitive">False</property>
-                    <property name="can_default">True</property>
                     <property name="can_focus">True</property>
-                    <property name="label">gtk-close</property>
+                    <property name="can_default">True</property>
+                    <property name="receives_default">False</property>
                     <property name="use_stock">True</property>
-                    <property name="relief">GTK_RELIEF_NORMAL</property>
-                    <property name="focus_on_click">True</property>
-                    <signal handler="on_button_close_clicked" last_modification_time="Sat, 05 Feb 2005 11:13:29 GMT" name="clicked"/>
+                    <signal name="clicked" handler="on_button_close_clicked" swapped="no"/>
                   </object>
+                  <packing>
+                    <property name="expand">True</property>
+                    <property name="fill">True</property>
+                    <property name="position">0</property>
+                  </packing>
                 </child>
               </object>
               <packing>
-                <property name="padding">0</property>
                 <property name="expand">True</property>
                 <property name="fill">True</property>
+                <property name="position">0</property>
               </packing>
             </child>
           </object>
           <packing>
-            <property name="padding">0</property>
             <property name="expand">False</property>
             <property name="fill">False</property>
+            <property name="position">3</property>
           </packing>
         </child>
       </object>
diff --git a/apt-listchanges/apt_listchanges.py b/apt-listchanges/apt_listchanges.py
index a7bc273..f9042c5 100644
--- a/apt-listchanges/apt_listchanges.py
+++ b/apt-listchanges/apt_listchanges.py
@@ -27,9 +27,9 @@
 import os
 import re
 import locale
-import email.Message
-import email.Header
-import email.Charset
+import email.message
+import email.header
+import email.charset
 try:
     import io
 except ImportError:

From 0f65a2fcc53a3dca18c1d48dd5cc88738fb6205b Mon Sep 17 00:00:00 2001
From: James Lu <GLolol1 at hotmail.com>
Date: Tue, 2 Jun 2015 18:14:30 -0700
Subject: [PATCH 3/4] More Python 3-specific fixes

---
 apt-listchanges.py                 |  9 ++++-----
 apt-listchanges/DebianFiles.py     | 14 +++++++++-----
 apt-listchanges/apt_listchanges.py | 29 +++++++++++++++++++----------
 3 files changed, 32 insertions(+), 20 deletions(-)

diff --git a/apt-listchanges.py b/apt-listchanges.py
index 39a88b8..2c9f190 100755
--- a/apt-listchanges.py
+++ b/apt-listchanges.py
@@ -171,12 +171,11 @@ def main():
     if config.save_seen:
         seen.close()
 
-    all_news = all_news.values()
-    all_changelogs = all_changelogs.values()
-    all_binnmus = all_binnmus.values()
+    all_news = list(all_news.values())
+    all_changelogs = list(all_changelogs.values())
+    all_binnmus = list(all_binnmus.values())
     for batch in (all_news, all_changelogs, all_binnmus):
-        batch.sort(lambda a, b: -cmp(a.urgency, b.urgency) or
-                   cmp(a.package, b.package))
+        batch.sort(key=lambda x: x.urgency or x.package)
 
     # FIXME: two headers with -h
     all_changelogs = all_binnmus + all_changelogs
diff --git a/apt-listchanges/DebianFiles.py b/apt-listchanges/DebianFiles.py
index 18818dd..d3f9f0c 100644
--- a/apt-listchanges/DebianFiles.py
+++ b/apt-listchanges/DebianFiles.py
@@ -23,7 +23,7 @@
 #   MA 02111-1307 USA
 #
 
-from __future__ import print_function
+from __future__ import print_function, unicode_literals
 import re
 import sys, os
 import tempfile
@@ -54,10 +54,10 @@ def numeric_urgency(u):
 class ControlStanza:
     source_version_re = re.compile('^\S+ \((?P<version>.*)\).*')
 
-    def __init__(self, str):
+    def __init__(self, s):
         field = None
 
-        for line in str.split('\n'):
+        for line in s.split('\n'):
             if not line:
                 break
             if line[0] in (' ', '\t'):
@@ -86,7 +86,7 @@ def version(self):
         """
         v = self.Version
         if hasattr(self, 'Source'):
-            match = self.source_version_re.match(self.Source)
+            match = self.source_version_re.match(self.Source.decode('utf-8'))
             if match:
                 sv = match.group('version')
                 if not v.startswith(sv):
@@ -105,7 +105,10 @@ def makeindex(self, field):
             self.index[field][getattr(stanza, field)] = stanza
 
     def readfile(self, file):
-        self.stanzas += [ControlStanza(x) for x in open(file, 'r').read().split('\n\n') if x]
+        try:
+            self.stanzas += [ControlStanza(x) for x in open(file, 'r').read().split('\n\n') if x]
+        except UnicodeDecodeError:
+            self.stanzas += [ControlStanza(x) for x in open(file, 'r').read().decode('utf-8').split('\n\n') if x]
 
     def readdeb(self, deb):
         fh = os.popen('dpkg-deb -f %s' % deb)
@@ -213,6 +216,7 @@ def read_changelog(self, filename, since_version, reverse=False):
         entries = []
         is_debian_changelog = 0
         for line in fd.readlines():
+            line = line.decode('utf-8')
             match = self.changelog_header.match(line)
             if match:
                 entries += [entry]
diff --git a/apt-listchanges/apt_listchanges.py b/apt-listchanges/apt_listchanges.py
index f9042c5..5ee4617 100644
--- a/apt-listchanges/apt_listchanges.py
+++ b/apt-listchanges/apt_listchanges.py
@@ -23,6 +23,7 @@
 #   MA 02111-1307 USA
 #
 
+# Implicitly use Unicode for all strings
 import sys
 import os
 import re
@@ -31,9 +32,9 @@
 import email.header
 import email.charset
 try:
-    import io
+    import StringIO as io
 except ImportError:
-    import cStringIO as io
+    import io
 import tempfile
 from ALChacks import *
 
@@ -186,13 +187,16 @@ def _render(self, text):
         for line in text.split('\n'):
             try:
                 # changelogs are supposed to be in UTF-8
-                uline = line.decode('utf-8')
+                if sys.version_info[0] < 3:
+                    uline = line.decode('utf-8')
+                else:
+                    uline = line
             except UnicodeError:
                 # ... but handle gracefully if they aren't.
                 # (That's also the reason we do it line by line.)
                 # This is possibly wrong, but our best guess.
                 uline = line.decode('iso8859-1')
-            newtext.append(uline.encode(locale.getpreferredencoding() or 'ascii', 'replace'))
+            newtext.append(uline)
         return '\n'.join(newtext)
 
     def confirm(self):
@@ -217,7 +221,7 @@ def display_output(self, text):
         db = dc.Debconf(read=dcfd, write=dcfd)
         tmp = tempfile.NamedTemporaryFile(prefix="apt-listchanges-tmp")
         os.fchmod(tmp.fileno(), 0o644)
-        tmp.write('''Template: apt-listchanges/info
+        tmp.write(b'''Template: apt-listchanges/info
 Type: title
 Description: NEWS
 
@@ -305,7 +309,7 @@ def display_output(self, text):
                 return
 
         tmp = tempfile.NamedTemporaryFile(prefix="apt-listchanges-tmp", suffix=self.suffix)
-        tmp.write(self._render(text))
+        tmp.write(self._render(text).encode('utf-8'))
         tmp.flush()
         shellcommand = self.get_command() + ' ' + tmp.name
 
@@ -373,15 +377,20 @@ def _render(self, text):
         for line in text.split('\n'):
             try:
                 # changelogs are supposed to be in UTF-8
-                uline = line.decode('utf-8')
+                if sys.version_info[0] < 3:
+                    uline = line.decode('utf-8')
+                else:
+                    uline = line
             except UnicodeError:
                 # ... but handle gracefully if they aren't.
                 # This is possibly wrong, but our best guess.
                 uline = line.decode('iso8859-1')
             line = uline.encode('utf-8').replace(
-                '&', '&').replace(
-                '<', '<').replace(
-                '>', '>')
+                b'&', b'&').replace(
+                b'<', b'<').replace(
+                b'>', b'>')
+            if sys.version_info[0] >= 3:
+                line = line.decode('utf-8')
             line = self.lp_bug_stanza_re.sub(lambda m: self.lp_bug_re.sub(self.lp_bug_fmt, m.group(0)), line)
             line = self.bug_stanza_re.sub(lambda m: self.bug_re.sub(self.bug_fmt, m.group(0)), line)
             line = self.email_re.sub(r'<a href="mailto:\g<0>">\g<0></a>', line)

From be624db8408436bc396c5f236733e588e7b99b9a Mon Sep 17 00:00:00 2001
From: James Lu <GLolol1 at hotmail.com>
Date: Tue, 2 Jun 2015 18:23:13 -0700
Subject: [PATCH 4/4] Migrate debian/ to dh_python3 and fix po/Makefile

---
 debian/control         |   9 ++--
 debian/pyversions      |   1 -
 debian/rules           |   4 +-
 po/Makefile            |   2 +-
 po/apt-listchanges.pot | 139 +++++++++++++++++++++++++++----------------------
 5 files changed, 86 insertions(+), 69 deletions(-)
 delete mode 100644 debian/pyversions

diff --git a/debian/control b/debian/control
index 9b787d9..d42cf4d 100644
--- a/debian/control
+++ b/debian/control
@@ -3,18 +3,19 @@ Section: utils
 Priority: standard
 Maintainer: Sandro Tosi <morph at debian.org>
 Uploaders: Thadeu Lima de Souza Cascardo <cascardo at minaslivre.org>
-Standards-Version: 3.9.5
+Standards-Version: 3.9.6
 Vcs-Browser: http://anonscm.debian.org/gitweb/?p=collab-maint/apt-listchanges.git;a=summary
 Vcs-Git: git://anonscm.debian.org/collab-maint/apt-listchanges.git
 Build-Depends: debhelper (>= 5)
 Build-Depends-Indep: docbook-to-man, gettext, po-debconf, libexpat1-dev,
- python-support (>= 0.4.0)
+ python3:any
+X-Python-Version: >= 3.4
 
 Package: apt-listchanges
 Architecture: all
-Depends: ${python:Depends}, apt (>= 0.5.3), python-apt (>= 0.7.93),
+Depends: ${python3:Depends}, apt (>= 0.5.3), python3-apt (>= 0.7.93),
  ucf (>= 0.28), debianutils (>= 2.0.2), ${misc:Depends}
-Suggests: x-terminal-emulator, www-browser, python-glade2, python-gtk2,
+Suggests: x-terminal-emulator, www-browser, python3-gi,
  default-mta | mail-transport-agent
 Description: package change history notification tool
  The tool apt-listchanges can compare a new version of a
diff --git a/debian/pyversions b/debian/pyversions
deleted file mode 100644
index 8b253bc..0000000
--- a/debian/pyversions
+++ /dev/null
@@ -1 +0,0 @@
-2.4-
diff --git a/debian/rules b/debian/rules
index a66feed..d128bca 100755
--- a/debian/rules
+++ b/debian/rules
@@ -15,7 +15,7 @@ clean:
 	dh_testroot
 	rm -f build-stamp
 
-	rm -f *.py[co] *~
+	rm -f *.py[co] *~ __pycache__/ */__pycache__
 	$(MAKE) clean
 
 	dh_clean
@@ -40,7 +40,7 @@ binary-indep: build install
 	dh_installchangelogs 
 #	dh_link
 #	dh_strip
-	dh_pysupport
+	dh_python3
 	dh_compress
 	dh_fixperms
 	dh_installdeb
diff --git a/po/Makefile b/po/Makefile
index 5dcca6b..2517080 100644
--- a/po/Makefile
+++ b/po/Makefile
@@ -25,7 +25,7 @@ POFILES =  $(wildcard *.po)
 MOFILES =  $(patsubst %.po,%.mo,$(POFILES))
 
 SRCFILES = ../apt-listchanges.py $(wildcard ../apt-listchanges/*.py) \
-	../apt-listchanges/apt-listchanges.glade
+	../apt-listchanges/apt-listchanges.ui
 
 all: $(PACKAGE).pot $(MOFILES)
 
diff --git a/po/apt-listchanges.pot b/po/apt-listchanges.pot
index 6d66434..7cd5c68 100644
--- a/po/apt-listchanges.pot
+++ b/po/apt-listchanges.pot
@@ -1,144 +1,161 @@
 # SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR ORGANIZATION
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
 # FIRST AUTHOR <EMAIL at ADDRESS>, YEAR.
 #
+#, fuzzy
 msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
-"POT-Creation-Date: 2010-07-09 15:16+CEST\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2015-06-02 18:20-0700\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL at ADDRESS>\n"
 "Language-Team: LANGUAGE <LL at li.org>\n"
+"Language: \n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=CHARSET\n"
-"Content-Transfer-Encoding: ENCODING\n"
-"Generated-By: pygettext.py 1.5\n"
+"Content-Transfer-Encoding: 8bit\n"
 
-
-#: ../apt-listchanges.py:80
-msgid ""
-"database %s failed to load.\n"
+#: ../apt-listchanges.py:93
+#, python-format
+msgid "database %s failed to load.\n"
 msgstr ""
 
-#: ../apt-listchanges.py:97
-msgid ""
-"Unknown frontend: %s\n"
+#: ../apt-listchanges.py:111
+#, python-format
+msgid "Unknown frontend: %s\n"
 msgstr ""
 
-#: ../apt-listchanges.py:122
+#: ../apt-listchanges.py:136
+#, python-format
 msgid "%s: will be newly installed"
 msgstr ""
 
-#: ../apt-listchanges.py:138
+#: ../apt-listchanges.py:152
+#, python-format
 msgid "%s: Version %s has already been seen"
 msgstr ""
 
-#: ../apt-listchanges.py:172
+#: ../apt-listchanges.py:191
+#, python-format
 msgid "News for %s"
 msgstr ""
 
-#: ../apt-listchanges.py:183
+#: ../apt-listchanges.py:202
+#, python-format
 msgid "Changes for %s"
 msgstr ""
 
-#: ../apt-listchanges.py:193
+#: ../apt-listchanges.py:212
 msgid "Informational notes"
 msgstr ""
 
-#: ../apt-listchanges.py:196
+#: ../apt-listchanges.py:215
 msgid "apt-listchanges: News"
 msgstr ""
 
-#: ../apt-listchanges.py:200
+#: ../apt-listchanges.py:219
 msgid "apt-listchanges: Changelogs"
 msgstr ""
 
-#: ../apt-listchanges.py:207
+#: ../apt-listchanges.py:226
 msgid "Aborting"
 msgstr ""
 
-#: ../apt-listchanges.py:210
+#: ../apt-listchanges.py:229
 msgid "Confirmation failed, don't save seen state"
 msgstr ""
 
-#: ../apt-listchanges.py:216
+#: ../apt-listchanges.py:236
+#, python-format
 msgid "apt-listchanges: changelogs for %s"
 msgstr ""
 
-#: ../apt-listchanges.py:220
+#: ../apt-listchanges.py:240
+#, python-format
 msgid "apt-listchanges: news for %s"
 msgstr ""
 
-#: ../apt-listchanges.py:232
+#: ../apt-listchanges.py:252
 msgid "didn't find any valid .deb archives"
 msgstr ""
 
-#: ../apt-listchanges/ALCConfig.py:76
+#: ../apt-listchanges/apt_listchanges.py:55
 msgid ""
-"Usage: apt-listchanges [options] {--apt | filename.deb ...}\n"
+"Wrong or missing VERSION from apt pipeline\n"
+"(is Dpkg::Tools::Options::/usr/bin/apt-listchanges::Version set to 2?)\n"
 msgstr ""
 
-#: ../apt-listchanges/ALCConfig.py:133
-msgid "Unknown option %s for --which.  Allowed are: %s."
+#: ../apt-listchanges/apt_listchanges.py:116
+#, python-format
+msgid "Mailing %s: %s"
 msgstr ""
 
-#: ../apt-listchanges/ALCConfig.py:148
-msgid "--since=<version> expects a only path to a .deb"
+#: ../apt-listchanges/apt_listchanges.py:143
+#, python-format
+msgid "The %s frontend is deprecated, using pager"
 msgstr ""
 
-#: ../apt-listchanges/ALChacks.py:32
-msgid ""
-"Can't set locale; make sure $LC_* and $LANG are correct!\n"
+#: ../apt-listchanges/apt_listchanges.py:147
+msgid "The mail frontend needs a installed 'sendmail', using pager"
 msgstr ""
 
-#: ../apt-listchanges/AptListChangesGtk.py:68
-msgid "Continue Installation?"
+#: ../apt-listchanges/apt_listchanges.py:159
+#, python-format
+msgid ""
+"The gtk frontend needs a working python-gi.\n"
+"Those imports can not be found. Falling back to pager.\n"
+"The error is: %s\n"
 msgstr ""
 
-#: ../apt-listchanges/AptListChangesGtk.py:68
-msgid "You can abort the installation if you select 'no'."
+#: ../apt-listchanges/apt_listchanges.py:258
+msgid "Do you want to continue? [Y/n] "
 msgstr ""
 
-#: ../apt-listchanges/DebianFiles.py:171
-msgid "Ignoring `%s' (seems to be a directory!)"
+#: ../apt-listchanges/apt_listchanges.py:271
+#: ../apt-listchanges/apt_listchanges.py:291
+#: ../apt-listchanges/apt_listchanges.py:299
+msgid "Reading changelogs"
 msgstr ""
 
-#: ../apt-listchanges/apt_listchanges.py:45
-msgid ""
-"Wrong or missing VERSION from apt pipeline\n"
-"(is Dpkg::Tools::Options::/usr/bin/apt-listchanges::Version set to 2?)\n"
+#: ../apt-listchanges/apt_listchanges.py:299
+msgid "Done"
 msgstr ""
 
-#: ../apt-listchanges/apt_listchanges.py:86
-msgid "Mailing %s: %s"
+#: ../apt-listchanges/ALCConfig.py:79
+msgid "Usage: apt-listchanges [options] {--apt | filename.deb ...}\n"
 msgstr ""
 
-#: ../apt-listchanges/apt_listchanges.py:111
-msgid "The %s frontend is deprecated, using pager"
+#: ../apt-listchanges/ALCConfig.py:136
+#, python-format
+msgid "Unknown option %s for --which.  Allowed are: %s."
 msgstr ""
 
-#: ../apt-listchanges/apt_listchanges.py:115
-msgid "The mail frontend needs a installed 'sendmail', using pager"
+#: ../apt-listchanges/ALCConfig.py:151
+msgid "--since=<version> expects a only path to a .deb"
 msgstr ""
 
-#: ../apt-listchanges/apt_listchanges.py:127
-msgid ""
-"The gtk frontend needs a working python-gtk2 and python-glade2.\n"
-"Those imports can not be found. Falling back to pager.\n"
-"The error is: %s\n"
+#: ../apt-listchanges/DebianFiles.py:199
+#, python-format
+msgid "Ignoring `%s' (seems to be a directory!)"
 msgstr ""
 
-#: ../apt-listchanges/apt_listchanges.py:180
-msgid "Do you want to continue? [Y/n] "
+#: ../apt-listchanges/AptListChangesGtk.py:71
+msgid "Continue Installation?"
 msgstr ""
 
-#: ../apt-listchanges/apt_listchanges.py:193
-#: ../apt-listchanges/apt_listchanges.py:213
-#: ../apt-listchanges/apt_listchanges.py:221
-msgid "Reading changelogs"
+#: ../apt-listchanges/AptListChangesGtk.py:71
+msgid "You can abort the installation if you select 'no'."
 msgstr ""
 
-#: ../apt-listchanges/apt_listchanges.py:221
-msgid "Done"
+#: ../apt-listchanges/apt-listchanges.ui:13
+msgid "List the changes"
 msgstr ""
 
+#: ../apt-listchanges/apt-listchanges.ui:29
+msgid ""
+"<big><b>Changelogs</b></big>\n"
+"\n"
+"The following changes are found in the packages you are about to install:"
+msgstr ""


More information about the py3porters-devel mailing list