uncommitted - mozilla-devscripts

Ready changes

Summary

Import uploads missing from VCS:

Diff

diff --git a/Makefile b/Makefile
index 85e2726..d9a0565 100644
--- a/Makefile
+++ b/Makefile
@@ -5,7 +5,7 @@ test:
 	tests/test-moz-version
 
 install:
-	python2 setup.py install --root="$(DESTDIR)" --no-compile --install-layout=deb
+	python3 setup.py install --root="$(DESTDIR)" --no-compile --install-layout=deb
 
 clean:
 	rm -rf build *.pyc
diff --git a/debian/bzr-builddeb.conf b/debian/bzr-builddeb.conf
new file mode 100644
index 0000000..3a08d60
--- /dev/null
+++ b/debian/bzr-builddeb.conf
@@ -0,0 +1,2 @@
+[BUILDDEB]
+native = True
diff --git a/debian/changelog b/debian/changelog
index f7a5e97..2b0030f 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,12 @@
+mozilla-devscripts (0.54.2+nmu1) unstable; urgency=medium
+
+  * Non-maintainer upload.
+
+  [ Håvard F. Aasen ]
+  * Finalize porting to Python 3. (Closes: #937085)
+
+ -- Sebastian Ramacher <sramacher@debian.org>  Mon, 15 Aug 2022 18:37:49 +0200
+
 mozilla-devscripts (0.54.2) unstable; urgency=medium
 
   [ Simon McVittie ]
diff --git a/debian/control b/debian/control
index 047ad61..dbc1ffb 100644
--- a/debian/control
+++ b/debian/control
@@ -5,22 +5,20 @@ Maintainer: Debian Mozilla Extension Maintainers <pkg-mozext-maintainers@lists.a
 Uploaders: Benjamin Drung <bdrung@debian.org>
 Build-Depends: debhelper-compat (= 12),
                dh-python,
-               python-all (>= 2.6.6-3~),
-               python-librdf (>= 1.0.17.1+dfsg-1.4),
-               python3
+               python3,
+               python3-librdf,
 Standards-Version: 4.4.1
 VCS-Browser: https://salsa.debian.org/webext-team/webext-devscripts
 VCS-Git: https://salsa.debian.org/webext-team/webext-devscripts.git
 
 Package: mozilla-devscripts
 Architecture: all
-Depends: python-librdf,
+Depends: python3-librdf,
          unzip,
          zip,
          ${misc:Depends},
          ${perl:Depends},
          ${python3:Depends},
-         ${python:Depends}
 Description: Development scripts used by Mozilla's addons packages
  This package contains mozilla-devscripts, a collection of scripts
  based on Makefile inheritance meant for packaging Firefox's and
diff --git a/debian/rules b/debian/rules
index 7c5e724..25115de 100755
--- a/debian/rules
+++ b/debian/rules
@@ -1,4 +1,4 @@
 #!/usr/bin/make -f
 
 %:
-	dh $@ --with python2,python3
+	dh $@ --with python3
diff --git a/dh_xul-ext b/dh_xul-ext
index 02c16ea..2ab0e3c 100755
--- a/dh_xul-ext
+++ b/dh_xul-ext
@@ -1,4 +1,4 @@
-#!/usr/bin/python2
+#!/usr/bin/python3
 
 # Copyright (c) 2009-2012, Benjamin Drung <bdrung@debian.org>
 #
@@ -25,6 +25,7 @@ import RDF
 
 from moz_version import (compare_versions, convert_moz_to_debian_version,
                          moz_to_next_debian_version)
+from functools import reduce
 
 _VENDOR_ENV = "DH_XUL_EXT_VENDOR"
 # error codes
@@ -161,8 +162,8 @@ def get_xul_apps(script_name, all_distros):
     else:
         csv_filename = os.path.join(data_dir, "xul-app-data.csv." + vendor)
         if not os.path.isfile(csv_filename):
-            print >> sys.stderr, ('%s: Unknown vendor "%s" specified.' %
-                                  (script_name, vendor))
+            print('%s: Unknown vendor "%s" specified.' %
+                                  (script_name, vendor), file=sys.stderr)
             sys.exit(1)
         csv_filenames = [csv_filename]
 
@@ -217,7 +218,7 @@ def _get_id_max_min_triple(script_name, package, install_rdf):
                    "from install.xpi." %
                    (script_name, package, appid, script_name, min_version,
                     max_version, script_name))
-            print >> sys.stderr, msg
+            print(msg, file=sys.stderr)
             failures += 1
     if failures > 0:
         sys.exit(INVALID_VERSION_RANGE)
@@ -229,10 +230,10 @@ def get_supported_apps(script_name, xul_apps, install_rdf, package,
                        verbose=False):
     id_max_min = _get_id_max_min_triple(script_name, package, install_rdf)
     if verbose:
-        print "%s: %s supports %i XUL application(s):" % (script_name, package,
-                                                          len(id_max_min))
+        print("%s: %s supports %i XUL application(s):" % (script_name, package,
+                                                          len(id_max_min)))
         for (appid, max_version, min_version) in id_max_min:
-            print "%s %s to %s" % (appid, min_version, max_version)
+            print("%s %s to %s" % (appid, min_version, max_version))
 
     # find supported apps/packages
     supported_apps = list()
@@ -247,18 +248,18 @@ def get_supported_apps(script_name, xul_apps, install_rdf, package,
                     xul_app.set_max_version(max_version)
                     supported_apps.append(xul_app)
                     if verbose:
-                        print "%s: %s supports %s." % (script_name, package,
-                                                       xul_app.get_package())
+                        print("%s: %s supports %s." % (script_name, package,
+                                                       xul_app.get_package()))
                 elif verbose:
-                    print "%s: %s does not support %s (any more)." % \
-                          (script_name, package, xul_app.get_package())
+                    print("%s: %s does not support %s (any more)." % \
+                          (script_name, package, xul_app.get_package()))
             elif verbose:
-                print "%s: %s does not support %s (yet)." % \
-                      (script_name, package, xul_app.get_package())
+                print("%s: %s does not support %s (yet)." % \
+                      (script_name, package, xul_app.get_package()))
         elif len(supported_app) > 1:
-            print ("%s: Found error in %s. There are multiple entries for "
-                   "application ID %s.") % (script_name, install_rdf,
-                                            xul_app.get_id())
+            print("%s: Found error in %s. There are multiple entries for "
+                   "application ID %s." % (script_name, install_rdf,
+                                            xul_app.get_id()))
 
     return supported_apps
 
@@ -306,7 +307,7 @@ def get_provided_package_names(package, supported_apps):
 
     for xul_app in supported_apps:
         app = xul_app.get_package()
-        for i in xrange(len(app) - 1, -1, -1):
+        for i in range(len(app) - 1, -1, -1):
             if app[i] == '-':
                 app = app[:i]
             elif not app[i].isdigit() and not app[i] == '.':
@@ -342,11 +343,11 @@ def generate_substvars(script_name, xul_apps, package, verbose=False):
                   " does not contain a XUL extension (no install.rdf found).")
         return
     elif len(install_rdfs) > 1:
-        print >> sys.stderr, ("%s: %s contains multiple install.rdf files. "
-                              "That's not supported.") % (script_name, package)
+        print("%s: %s contains multiple install.rdf files. "
+                              "That's not supported." % (script_name, package), file=sys.stderr)
         basepath_len = len(os.path.realpath("debian/" + package))
         rdfs = [x[basepath_len:] for x in install_rdfs]
-        print >> sys.stderr, "\n".join(rdfs)
+        print("\n".join(rdfs), file=sys.stderr)
         sys.exit(MULTIPLE_INSTALL_RDFS)
     install_rdf = install_rdfs.pop()
 
@@ -366,8 +367,8 @@ def generate_substvars(script_name, xul_apps, package, verbose=False):
     packages = [a.get_versioned_package() for a in supported_apps]
     if has_no_xpi_depends():
         # Use xpi:Recommends instead of xpi:Depends for backwards compatibility
-        print ("%s: Warning: Please add ${xpi:Depends} to Depends. Using only "
-               "${xpi:Recommends} is deprecated.") % (script_name)
+        print("%s: Warning: Please add ${xpi:Depends} to Depends. Using only "
+               "${xpi:Recommends} is deprecated." % (script_name))
         lines.append("xpi:Recommends=" + " | ".join(packages) + "\n")
     else:
         lines.append("xpi:Depends=" + " | ".join(packages) + "\n")
@@ -429,13 +430,13 @@ def main():
         for unknown_option in parser.unknown_options:
             sys.stderr.write("%s: warning: no such option: %s\n" %
                              (script_name, unknown_option))
-        print script_name + ": packages:", ", ".join(options.packages)
+        print(script_name + ": packages:", ", ".join(options.packages))
 
     xul_apps = get_xul_apps(script_name, options.all)
     if options.verbose and xul_apps:
-        print script_name + ": found %i Xul applications:" % (len(xul_apps))
+        print(script_name + ": found %i Xul applications:" % (len(xul_apps)))
         for xul_app in xul_apps:
-            print xul_app
+            print(xul_app)
 
     for package in options.packages:
         generate_substvars(script_name, xul_apps, package, options.verbose)
diff --git a/install-xpi b/install-xpi
index 7fcffdc..c0418f6 100755
--- a/install-xpi
+++ b/install-xpi
@@ -1,4 +1,4 @@
-#!/usr/bin/python2
+#!/usr/bin/python3
 
 # Copyright (c) 2009-2013, Benjamin Drung <bdrung@debian.org>
 #
@@ -14,7 +14,7 @@
 # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
-from __future__ import print_function
+
 
 import csv
 import optparse
@@ -107,7 +107,7 @@ def get_arch(package, debian_directory):
 def get_mode(filename):
     statinfo = os.stat(filename)
     mode = statinfo[stat.ST_MODE]
-    return mode & 0777
+    return mode & 0o777
 
 
 def get_xul_apps():
@@ -169,22 +169,22 @@ def install_xpi(script_name, package, xpi_file, exclude, install_dir, links,
             filename = os.path.join(copy_dir, name)
             if os.path.exists(filename):
                 mode = get_mode(filename)
-                if os.path.isdir(filename) and mode != 0755:
+                if os.path.isdir(filename) and mode != 0o755:
                     print("%s: correct permission from %s to %s of %s" %
-                          (script_name, oct(mode), oct(0755), name))
-                    os.chmod(filename, 0755)
+                          (script_name, oct(mode), oct(0o755), name))
+                    os.chmod(filename, 0o755)
                 elif os.path.isfile(filename):
-                    header = open(filename, "r").read(2)
-                    if header != "#!" and mode != 0644:
+                    header = open(filename, "rb").read(2)
+                    if header != "#!" and mode != 0o644:
                         # file without shebang
                         print("%s: correct permission from %s to %s of %s" %
-                              (script_name, oct(mode), oct(0644), name))
-                        os.chmod(filename, 0644)
-                    elif header == "#!" and mode != 0755:
+                              (script_name, oct(mode), oct(0o644), name))
+                        os.chmod(filename, 0o644)
+                    elif header == "#!" and mode != 0o755:
                         # file with shebang
                         print("%s: correct permission from %s to %s of %s" %
-                              (script_name, oct(mode), oct(0755), name))
-                        os.chmod(filename, 0755)
+                              (script_name, oct(mode), oct(0o755), name))
+                        os.chmod(filename, 0o755)
 
     # create a system preference file in /etc
     if system_prefs:
diff --git a/moz-version b/moz-version
index cb7062f..8ac2b2f 100755
--- a/moz-version
+++ b/moz-version
@@ -1,4 +1,4 @@
-#!/usr/bin/python2
+#!/usr/bin/python3
 
 # Copyright (c) 2009-2011, Benjamin Drung <bdrung@debian.org>
 #
@@ -35,21 +35,21 @@ def moz_version_compare(version1, comparator, version2, silent=False,
        otherwise false"""
     if comparator not in COMPARATORS:
         if not silent:
-            print >> sys.stderr, "E: The comparator " + comparator + \
+            print("E: The comparator " + comparator + \
                                  " is not valid. It should one of " + \
-                                 ", ".join(COMPARATORS) + "."
+                                 ", ".join(COMPARATORS) + ".", file=sys.stderr)
         sys.exit(INVALID_COMPARATOR)
 
     if version1.strip() == "" or version2.strip() == "":
         if not silent:
-            print >> sys.stderr, "E: At least one version string is empty."
+            print("E: At least one version string is empty.", file=sys.stderr)
         sys.exit(EMPTY_VERSION_STRING)
 
     if verbose:
         symbol = {"lt": "<", "le": "<=", "eq": "=", "ne": "!=",
                   "ge": ">=", "gt": ">"}
-        print "I: Comparing %s %s %s." % \
-              (version1, symbol[comparator], version2)
+        print("I: Comparing %s %s %s." % \
+              (version1, symbol[comparator], version2))
 
     if comparator == "lt":
         return compare_versions(version1, version2, verbose) < 0
@@ -68,7 +68,7 @@ def moz_version_compare(version1, comparator, version2, silent=False,
 
 def usage(output):
     name = os.path.basename(sys.argv[0])
-    print >> output, """Usage: %s [options] action
+    print("""Usage: %s [options] action
 
 Actions:
   -c, --compare version1 comparator version2
@@ -82,16 +82,16 @@ Options:
   -s, --silent             do not print anything and die silent on errors
   -v, --verbose            print more information
 
-See %s(1) for more info.""" % (name, ", ".join(COMPARATORS), name)
+See %s(1) for more info.""" % (name, ", ".join(COMPARATORS), name))
 
 
 def main():
     try:
         long_opts = ["compare", "help", "silent", "to-deb", "to-moz", "verbose"]
         opts, args = getopt.gnu_getopt(sys.argv[1:], "cdhmsv", long_opts)
-    except getopt.GetoptError, e:
+    except getopt.GetoptError as e:
         # print help information and exit:
-        print >> sys.stderr, str(e)
+        print(e, file=sys.stderr)
         usage(sys.stderr)
         sys.exit(COMMAND_LINE_SYNTAX_ERROR)
 
@@ -118,7 +118,7 @@ def main():
 
     if len(actions) != 1:
         if not silent:
-            print >> sys.stderr, "E: You must specify an action."
+            print("E: You must specify an action.", file=sys.stderr)
             usage(sys.stderr)
         sys.exit(COMMAND_LINE_SYNTAX_ERROR)
 
@@ -131,26 +131,26 @@ def main():
             sys.exit(COMMAND_LINE_SYNTAX_ERROR)
         if moz_version_compare(args[0], args[1], args[2], silent, verbose):
             if verbose:
-                print "I: Compare expression true."
+                print("I: Compare expression true.")
             sys.exit(0)
         else:
             if verbose:
-                print "I: Compare expression false."
+                print("I: Compare expression false.")
             sys.exit(1)
     elif action == "to-deb":
         if len(args) != 1:
             if not silent:
-                print >> sys.stderr, "E: The action --to-deb takes exactly " + \
-                                     "one argument."
+                print("E: The action --to-deb takes exactly " + \
+                                     "one argument.", file=sys.stderr)
             sys.exit(COMMAND_LINE_SYNTAX_ERROR)
-        print convert_moz_to_debian_version(args[0], 0, verbose)
+        print(convert_moz_to_debian_version(args[0], 0, verbose))
     elif action == "to-moz":
         if len(args) != 1:
             if not silent:
-                print >> sys.stderr, "E: The action --to-moz takes exactly " + \
-                                     "one argument."
+                print("E: The action --to-moz takes exactly " + \
+                                     "one argument.", file=sys.stderr)
             sys.exit(COMMAND_LINE_SYNTAX_ERROR)
-        print convert_debian_to_moz_version(args[0])
+        print(convert_debian_to_moz_version(args[0]))
 
 if __name__ == "__main__":
     main()
diff --git a/moz_version.py b/moz_version.py
index 86ed727..e95bb6e 100644
--- a/moz_version.py
+++ b/moz_version.py
@@ -14,93 +14,134 @@
 
 # Reference: https://developer.mozilla.org/en/Toolkit_version_format
 
+import functools
 import sys
 
 
-def decode_part(part):
-    """Decodes a version part (like 5pre4) to
-       <number-a><string-b><number-c><string-d>"""
-    subpart = [0, "", 0, ""]
+@functools.total_ordering
+class Part(object):
+    """Mozilla version part in the format <number-a><string-b><number-c><string-d>."""
+    def __init__(self, number_a, string_b, number_c, string_d):
+        self.number_a = number_a
+        self.string_b = string_b
+        self.number_c = number_c
+        self.string_d = string_d
+
+    def __repr__(self):
+        return "%s(%r, %r, %r, %r)" % (self.__class__.__name__, self.number_a, self.string_b,
+                                       self.number_c, self.string_d)
+
+    def __iter__(self):
+        return iter((self.number_a, self.string_b, self.number_c, self.string_d))
+
+
+    def __eq__(self, other):
+        return ((self.number_a, self.string_b, self.number_c, self.string_d)
+                == (other.number_a, other.string_b, other.number_c, other.string_d))
+
+    def __lt__(self, other):
+        # A string-part that exists is always less-then a nonexisting string-part
+        return ((self.number_a, Subpart(self.string_b), self.number_c, Subpart(self.string_d))
+                < (other.number_a, Subpart(other.string_b),
+                    other.number_c, Subpart(other.string_d)))
+
+    @classmethod
+    def from_string(cls, part):
+        """Decodes a version part (like 5pre4) to
+           <number-a><string-b><number-c><string-d>"""
+        number_a = 0
+        string_b = ""
+        number_c = 0
+        string_d = ""
+
+        # Split <number-a>
+        length = 0
+        for i in range(len(part)):
+            if part[i].isdigit() or part[i] == "-":
+                length += 1
+            else:
+                break
+        if length > 0:
+            number_a = int(part[0:length])
+        part = part[length:]
+
+        # Split <string-b>
+        length = 0
+        for i in range(len(part)):
+            if not (part[i].isdigit() or part[i] == "-"):
+                length += 1
+            else:
+                break
+        string_b = part[0:length]
+        part = part[length:]
+
+        # Split <number-c>
+        length = 0
+        for i in range(len(part)):
+            if part[i].isdigit() or part[i] == "-":
+                length += 1
+            else:
+                break
+        if length > 0:
+            number_c = int(part[0:length])
+        string_d = part[length:]
 
-    # Split <number-a>
-    length = 0
-    for i in xrange(len(part)):
-        if part[i].isdigit() or part[i] == "-":
-            length += 1
-        else:
-            break
-    if length > 0:
-        subpart[0] = int(part[0:length])
-    part = part[length:]
-
-    # Split <string-b>
-    length = 0
-    for i in xrange(len(part)):
-        if not (part[i].isdigit() or part[i] == "-"):
-            length += 1
-        else:
-            break
-    subpart[1] = part[0:length]
-    part = part[length:]
-
-    # Split <number-c>
-    length = 0
-    for i in xrange(len(part)):
-        if part[i].isdigit() or part[i] == "-":
-            length += 1
-        else:
-            break
-    if length > 0:
-        subpart[2] = int(part[0:length])
-    subpart[3] = part[length:]
+        # if string-b is a plus sign, number-a is incremented to be compatible with
+        # the Firefox 1.0.x version format: 1.0+ is the same as 1.1pre
+        if string_b == "+":
+            number_a += 1
+            string_b = "pre"
 
-    # if string-b is a plus sign, number-a is incremented to be compatible with
-    # the Firefox 1.0.x version format: 1.0+ is the same as 1.1pre
-    if subpart[1] == "+":
-        subpart[0] += 1
-        subpart[1] = "pre"
+        # if the version part is a single asterisk, it is interpreted as an
+        # infinitely-large number: 1.5.0.* is the same as 1.5.0.(infinity)
+        if string_b == "*":
+            number_a = sys.maxsize
+            string_b = ""
 
-    # if the version part is a single asterisk, it is interpreted as an
-    # infinitely-large number: 1.5.0.* is the same as 1.5.0.(infinity)
-    if subpart[1] == "*":
-        subpart[0] = sys.maxint
-        subpart[1] = ""
+        return cls(number_a, string_b, number_c, string_d)
 
-    return subpart
+    def convert_to_debian(self):
+        """Converts a Mozilla version part (like 5pre4) to a Debian version."""
+        debian_version = ""
+        if self.string_d != "":
+            debian_version = "~" + self.string_d
+        if self.number_c != 0 or self.string_d != "":
+            debian_version = str(self.number_c) + debian_version
+        if self.string_b != "":
+            debian_version = "~" + self.string_b + debian_version
+        debian_version = str(self.number_a) + debian_version
+        return debian_version
 
 
-def decode_version(version, verbose=False):
-    """Decodes a version string like 1.1pre1a"""
-    parts = version.split(".")
-    decoded_parts = map(decode_part, parts)
-    if verbose:
-        print "I: Split %s up into %s." % (version, decoded_parts)
-    return decoded_parts
+@functools.total_ordering
+class Subpart(object):
+    """Represent a sub-part of a Mozilla version (either a number or a string)."""
 
+    def __init__(self, subpart):
+        self.subpart = subpart
 
-def compare_subpart((a, b)):
-    # A string-part that exists is always less-then a nonexisting string-part
-    if a == "":
-        if b == "":
-            return 0
-        else:
-            return 1
-    elif b == "":
-        if a == "":
-            return 0
-        else:
-            return -1
-    else:
-        return cmp(a, b)
+    def __repr__(self):
+        return "%s(%r)" % (self.__class__.__name__, self.subpart)
 
+    def __eq__(self, other):
+        return self.subpart == other.subpart
 
-def compare_part((x, y)):
-    compared_subparts = filter(lambda x: x != 0,
-                               map(compare_subpart, zip(x, y)))
-    if compared_subparts:
-        return compared_subparts[0]
-    else:
-        return 0
+    def __lt__(self, other):
+        # A string-part that exists is always less-then a nonexisting string-part
+        if self.subpart == "":
+            return False
+        if other.subpart == "":
+            return self.subpart != ""
+
+        return self.subpart < other.subpart
+
+
+def decode_version(version, verbose=False):
+    """Decodes a version string like 1.1pre1a"""
+    decoded_parts = [Part.from_string(part) for part in version.split(".")]
+    if verbose:
+        print("I: Split %s up into %s." % (version, decoded_parts))
+    return decoded_parts
 
 
 def compare_versions(version1, version2, verbose=False):
@@ -108,15 +149,15 @@ def compare_versions(version1, version2, verbose=False):
     b = decode_version(version2, verbose)
 
     if len(a) < len(b):
-        a.extend((len(b) - len(a)) * [[0, "", 0, ""]])
+        a += (len(b) - len(a)) * [Part(0, "", 0, "")]
     if len(b) < len(a):
-        b.extend((len(a) - len(b)) * [[0, "", 0, ""]])
+        b += (len(a) - len(b)) * [Part(0, "", 0, "")]
 
-    result = filter(lambda x: x != 0, map(compare_part, zip(a, b)))
-    if result:
-        return result[0]
-    else:
+    if a == b:
         return 0
+    if a < b:
+        return -1
+    return 1
 
 
 def extract_upstream_version(debian_version):
@@ -135,20 +176,6 @@ def extract_upstream_version(debian_version):
     return upstream_version
 
 
-def _convert_part_to_debian(part):
-    """Converts a Mozilla version part (like 5pre4) to a Debian version."""
-    (number_a, string_b, number_c, string_d) = part
-    debian_version = ""
-    if string_d != "":
-        debian_version = "~" + string_d
-    if number_c != 0 or string_d != "":
-        debian_version = str(number_c) + debian_version
-    if string_b != "":
-        debian_version = "~" + string_b + debian_version
-    debian_version = str(number_a) + debian_version
-    return debian_version
-
-
 def convert_debian_to_moz_version(debian_version):
     upstream_version = extract_upstream_version(debian_version)
 
@@ -167,7 +194,7 @@ def convert_debian_to_moz_version(debian_version):
 def convert_moz_to_debian_version(moz_version, epoch=0, verbose=False):
     parts = decode_version(moz_version, verbose)
     # tranform parts
-    parts = [_convert_part_to_debian(p) for p in parts]
+    parts = [p.convert_to_debian() for p in parts]
     debian_version = ".".join(parts)
     if epoch != 0:
         debian_version = str(epoch) + ":" + debian_version
@@ -198,7 +225,7 @@ def moz_to_next_debian_version(moz_version, epoch=0, verbose=False):
         if last_part:
             last_part = str(number_c) + last_part
         else:
-            if number_c == sys.maxint:
+            if number_c == sys.maxsize:
                 last_part = "+"
             else:
                 last_part = str(number_c) + "+"
@@ -210,12 +237,12 @@ def moz_to_next_debian_version(moz_version, epoch=0, verbose=False):
     if last_part:
         last_part = str(number_a) + last_part
     else:
-        if number_a == sys.maxint:
+        if number_a == sys.maxsize:
             last_part = "+"
         else:
             last_part = str(number_a) + "+"
 
-    parts = [_convert_part_to_debian(p) for p in parts[:-1]] + [last_part]
+    parts = [p.convert_to_debian() for p in parts[:-1]] + [last_part]
     debian_version = ".".join(parts)
     if epoch != 0:
         debian_version = str(epoch) + ":" + debian_version
diff --git a/setup.py b/setup.py
index 1d4aa77..ce047f8 100644
--- a/setup.py
+++ b/setup.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python2
+#!/usr/bin/python3
 
 import glob
 import os
@@ -20,28 +20,24 @@ def get_debian_version():
 
 
 SCRIPTS = [
+    'amo-changelog',
+    'dh_webext',
     'dh_xul-ext',
     'install-xpi',
+    'moz-version',
     'xpi-pack',
+    'xpi-repack',
     'xpi-unpack',
-    'moz-version',
 ]
 
-# need to treat these separately, else setuptools fucks with the shebang lines
-PY3_SCRIPTS = [
-    'amo-changelog',
-    'dh_webext',
-    'xpi-repack',
-]
 
 if __name__ == '__main__':
     setup(
         name='mozilla-devscripts',
         version=get_debian_version(),
-        scripts=SCRIPTS,
         py_modules=['moz_version'],
         data_files=[
-            ('bin', PY3_SCRIPTS),
+            ('bin', SCRIPTS),
             ('share/doc/mozilla-devscripts', ['README']),
             ('share/man/man1', glob.glob("man/*.1")),
             ('share/mozilla-devscripts', ['data/xpi.mk'] + glob.glob('data/xul-app-data.csv.*')),
diff --git a/tests/dh_xul-ext/test b/tests/dh_xul-ext/test
index 8dc3e88..244346e 100755
--- a/tests/dh_xul-ext/test
+++ b/tests/dh_xul-ext/test
@@ -1,4 +1,4 @@
-#!/usr/bin/python2
+#!/usr/bin/python3
 
 # Copyright (c) 2011, Benjamin Drung <bdrung@debian.org>
 #
@@ -33,7 +33,7 @@ def escape_arg(arg):
 
 
 def fail(message):
-    print >> sys.stderr, "dh_xul-ext test error: " + message
+    print("dh_xul-ext test error: " + message, file=sys.stderr)
     sys.exit(1)
 
 
@@ -74,10 +74,10 @@ def main():
     for test in TESTS:
         try:
             test_dh_xul_ext(test)
-        except TestError, error:
+        except TestError as error:
             errors += 1
-            print >> sys.stderr, "dh_xul-ext error in " + test + " test: " + \
-                                 str(error)
+            print("dh_xul-ext error in " + test + " test: " + \
+                                 error, file=sys.stderr)
     if errors > 0:
         sys.exit(1)
 
diff --git a/tests/test-moz-version b/tests/test-moz-version
index b6e7e10..e363cbd 100755
--- a/tests/test-moz-version
+++ b/tests/test-moz-version
@@ -1,4 +1,4 @@
-#!/usr/bin/python2
+#!/usr/bin/python3
 
 # Copyright (c) 2010 Benjamin Drung <bdrung@debian.org>
 #
@@ -62,7 +62,7 @@ TEST_PATTERN = (
 
 
 def fail(message):
-    print >> sys.stderr, "E: " + message
+    print("E: " + message, file=sys.stderr)
     sys.exit(TEST_FAILED_ERROR)
 
 
@@ -91,7 +91,7 @@ def main():
         opts = getopt.gnu_getopt(sys.argv[1:], "v", long_opts)[0]
     except getopt.GetoptError as error:
         # print help information and exit:
-        print >> sys.stderr, str(error)
+        print(error, file=sys.stderr)
         sys.exit(COMMAND_LINE_SYNTAX_ERROR)
 
     verbose = False

Run locally

More details

Full run details