From ca02e019f84181c19d27be9148591a69258d09e8 Mon Sep 17 00:00:00 2001
From: "A. Wilcox" <AWilcox@Wilcox-Tech.com>
Date: Thu, 26 Jul 2018 23:37:11 -0500
Subject: [PATCH] user/thunderbird: bump to 52.9.0, the death of sanity

---
 user/thunderbird/APKBUILD      |    34 +-
 user/thunderbird/python3.patch | 10079 +++++++++++++++++++++++++++++++
 2 files changed, 10110 insertions(+), 3 deletions(-)
 create mode 100644 user/thunderbird/python3.patch

diff --git a/user/thunderbird/APKBUILD b/user/thunderbird/APKBUILD
index f516c34be6..6b5c8b8fd7 100644
--- a/user/thunderbird/APKBUILD
+++ b/user/thunderbird/APKBUILD
@@ -1,7 +1,7 @@
 # Contributor: A. Wilcox <awilfox@adelielinux.org>
 # Maintainer: A. Wilcox <awilfox@adelielinux.org>
 pkgname=thunderbird
-pkgver=52.6.0
+pkgver=52.9.0
 pkgrel=0
 pkgdesc="Email client from Mozilla"
 url="https://www.mozilla.org/thunderbird/"
@@ -13,7 +13,7 @@ depends=""
 # system-libs
 # actual deps
 makedepends="
-	autoconf2.13 debianutils-which perl python2
+	autoconf2.13 ncurses-dev perl cmd:which
 
 	alsa-lib-dev bzip2-dev icu-dev libevent-dev libffi-dev libpng-dev
 	libjpeg-turbo-dev libvpx-dev nspr-dev nss-dev pulseaudio-dev zlib-dev
@@ -41,6 +41,28 @@ somask="liblgpllibs.so
 	libxul.so"
 _tbirddir=/usr/lib/${pkgname}-${pkgver}
 
+unpack() {
+	default_unpack
+	# just ripped from Firefox's APKBUILD...
+	[ -z $SKIP_PYTHON ] || return 0
+	msg "Killing all remaining hope for humanity and building Python 2..."
+	cd "$srcdir"
+	[ -d python ] && rm -r python
+	mkdir python
+	cd python
+	# 19:39 <+solar> just make the firefox build process build its own py2 copy
+	curl -O https://www.python.org/ftp/python/2.7.15/Python-2.7.15.tar.xz
+	tar xJf Python-2.7.15.tar.xz
+	cd Python-2.7.15
+	# 20:03 <calvin> TheWilfox: there's always violence
+	./configure --prefix="$srcdir/python"
+	make -j $JOBS
+	# 6 tests failed:
+	#    test__locale test_os test_posix test_re test_strptime test_time
+	# make test
+	make -j $JOBS install
+}
+
 prepare() {
 	default_prepare
 	cp "$srcdir"/stab.h "$builddir"/mozilla/toolkit/crashreporter/google-breakpad/src/
@@ -48,6 +70,10 @@ prepare() {
 	echo "ac_add_options --enable-optimize=\"$CFLAGS\"" >> "$builddir"/mozconfig
 	echo "ac_add_options --host=\"$CHOST\"" >> "$builddir"/mozconfig
 	echo "ac_add_options --target=\"$CTARGET\"" >> "$builddir"/mozconfig
+	# too much memory
+	if [ -z "$JOBS" -o $JOBS -gt 32 ]; then
+		echo "mk_add_options MOZ_MAKE_FLAGS=\"-j32\"" >> "$builddir"/mozconfig
+	fi
 }
 
 build() {
@@ -59,11 +85,13 @@ build() {
 	export LDFLAGS="$LDFLAGS -Wl,-rpath,${_tbirddir}"
 	export USE_SHORT_LIBNAME=1
 
+	export PATH="$srcdir/python/bin:$PATH"
 	./mozilla/mach build
 }
 
 package() {
 	cd "$builddir"
+	export PATH="$srcdir/python/bin:$PATH"
 	DESTDIR="$pkgdir" ./mozilla/mach install
 	install -D -m644 "$srcdir"/thunderbird.desktop \
 		"$pkgdir"/usr/share/applications/thunderbird.desktop
@@ -80,7 +108,7 @@ dev() {
 	mv "$pkgdir"/usr/share/idl "$subpkgdir"/usr/share
 }
 
-sha512sums="80742c95ed61d1cb2e72b71bb23bdd211a40240ab4393e9f028a38f902547372084a8f56445e2394484be088a7b9801405f3d6618fb2742601cc968bf34427f0  thunderbird-52.6.0.source.tar.xz
+sha512sums="2142ba7cc04f48a9ffa17ad8f3a0d761f90416c9e6a7066be662a09b19846f13f0fb9669356ccbbf576744a83143cd659c28ce034679c4d6377004f428932dc1  thunderbird-52.9.0.source.tar.xz
 000b4403bfac4a6192ebe36a734ef3e464f3bdd3bc797e87bc487b4d9d93cd4b41137d82726617205e39f7aedf8bf2dfb11645db24a8b0b0137a141c9133f151  mozconfig
 9b11ba43f1f3fe9cda69b6b92e2073ea5165a47e30084537f396ceb8fb63573c4eb057251644837504aa4546183dc8f77fbb24f1450b6a15a1386f29180deefc  bad-google-code.patch
 2f52fcd7c42f8e12c955e05aa12449aa486c5347d2a7406ff0dada66f64079152b18c3f65c43410df372e871488f17889bc337ced37d0b76305afdbcb55cb580  fix-seccomp-bpf.patch
diff --git a/user/thunderbird/python3.patch b/user/thunderbird/python3.patch
new file mode 100644
index 0000000000..2a4835fb79
--- /dev/null
+++ b/user/thunderbird/python3.patch
@@ -0,0 +1,10079 @@
+--- thunderbird-52.9.0/mozilla/python/mozbuild/dumbmake/dumbmake.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/dumbmake/dumbmake.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import absolute_import, unicode_literals
++
+ 
+ from collections import OrderedDict
+ from itertools import groupby
+@@ -36,7 +36,7 @@
+     deps = {}
+ 
+     for i, (indent, target) in enumerate(pairs):
+-        if not deps.has_key(target):
++        if target not in deps:
+             deps[target] = []
+ 
+         for j in range(i+1, len(pairs)):
+@@ -68,7 +68,7 @@
+                     del all_targets[dependency]
+                 all_targets[dependency] = True
+ 
+-    return all_targets.keys()
++    return list(all_targets.keys())
+ 
+ def get_components(path):
+     """Take a path and return all the components of the path."""
+@@ -114,7 +114,7 @@
+                 make_dirs[make_dir] = True
+ 
+     all_components = []
+-    for make_dir in make_dirs.iterkeys():
++    for make_dir in make_dirs.keys():
+         all_components.extend(get_components(make_dir))
+ 
+     for i in all_dependencies(*all_components, dependency_map=dependency_map):
+--- thunderbird-52.9.0/mozilla/python/mozbuild/dumbmake/test/test_dumbmake.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/dumbmake/test/test_dumbmake.py	(refactored)
+@@ -1,7 +1,7 @@
+ # This Source Code Form is subject to the terms of the Mozilla Public
+ # License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ # You can obtain one at http://mozilla.org/MPL/2.0/.
+-from __future__ import unicode_literals
++
+ 
+ import unittest
+ 
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/android_version_code.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/android_version_code.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import absolute_import, print_function
++
+ 
+ import argparse
+ import math
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/artifacts.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/artifacts.py	(refactored)
+@@ -40,7 +40,7 @@
+ '''
+ 
+ 
+-from __future__ import absolute_import, print_function, unicode_literals
++
+ 
+ import collections
+ import functools
+@@ -57,7 +57,7 @@
+ import subprocess
+ import tarfile
+ import tempfile
+-import urlparse
++import urllib.parse
+ import zipfile
+ 
+ import pylru
+@@ -183,7 +183,7 @@
+ 
+         with JarWriter(file=processed_filename, optimize=False, compress_level=5) as writer:
+             reader = JarReader(filename)
+-            for filename, entry in reader.entries.iteritems():
++            for filename, entry in reader.entries.items():
+                 for pattern, (src_prefix, dest_prefix) in self.test_artifact_patterns:
+                     if not mozpath.match(filename, pattern):
+                         continue
+@@ -552,7 +552,7 @@
+ 
+     def print_cache(self):
+         with self:
+-            for item in self._cache.items():
++            for item in list(self._cache.items()):
+                 self.log(logging.INFO, 'artifact',
+                     {'item': item},
+                     '{item}')
+@@ -565,7 +565,7 @@
+         # We use the persisted LRU caches to our advantage.  The first item is
+         # most recent.
+         with self:
+-            item = next(self._cache.items(), None)
++            item = next(list(self._cache.items()), None)
+             if item is not None:
+                 (name, args, sorted_kwargs), result = item
+                 self.print_last_item(args, sorted_kwargs, result)
+@@ -593,10 +593,10 @@
+                          'changeset={changeset}&version=2&tipsonly=1')
+         req = requests.get(cset_url_tmpl.format(tree=tree, changeset=revision),
+                            headers={'Accept': 'application/json'})
+-        if req.status_code not in range(200, 300):
++        if req.status_code not in list(range(200, 300)):
+             raise ValueError
+         result = req.json()
+-        [found_pushid] = result['pushes'].keys()
++        [found_pushid] = list(result['pushes'].keys())
+         return int(found_pushid)
+ 
+     @cachedmethod(operator.attrgetter('_cache'))
+@@ -609,7 +609,7 @@
+                            headers={'Accept': 'application/json'})
+         result = req.json()
+         return [
+-            p['changesets'][-1] for p in result['pushes'].values()
++            p['changesets'][-1] for p in list(result['pushes'].values())
+         ]
+ 
+ class TaskCache(CacheManager):
+@@ -847,7 +847,7 @@
+ 
+             candidate_pushheads = collections.defaultdict(list)
+ 
+-            for tree, pushid in found_pushids.iteritems():
++            for tree, pushid in found_pushids.items():
+                 end = pushid
+                 start = pushid - NUM_PUSHHEADS_TO_QUERY_PER_PARENT
+ 
+@@ -1052,7 +1052,7 @@
+         """
+         if source and os.path.isfile(source):
+             return self.install_from_file(source, distdir)
+-        elif source and urlparse.urlparse(source).scheme:
++        elif source and urllib.parse.urlparse(source).scheme:
+             return self.install_from_url(source, distdir)
+         else:
+             if source is None and 'MOZ_ARTIFACT_REVISION' in os.environ:
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/base.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/base.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import absolute_import, print_function, unicode_literals
++
+ 
+ import json
+ import logging
+@@ -482,7 +482,7 @@
+             for flag in flags:
+                 if flag == '-j':
+                     try:
+-                        flag = flags.next()
++                        flag = next(flags)
+                     except StopIteration:
+                         break
+                     try:
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/config_status.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/config_status.py	(refactored)
+@@ -6,7 +6,7 @@
+ # drop-in replacement for autoconf 2.13's config.status, with features
+ # borrowed from autoconf > 2.5, and additional features.
+ 
+-from __future__ import absolute_import, print_function
++
+ 
+ import logging
+ import os
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/doctor.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/doctor.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, # You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import absolute_import
++
+ 
+ import os
+ import subprocess
+@@ -83,7 +83,7 @@
+         valid = False
+         while not valid and limit > 0:
+             try:
+-                choice = strtobool(raw_input(prompt + '[Y/N]\n'))
++                choice = strtobool(input(prompt + '[Y/N]\n'))
+                 valid = True
+             except ValueError:
+                 print("ERROR! Please enter a valid option!")
+@@ -103,11 +103,11 @@
+             if status == 'SKIPPED':
+                 continue
+             self.results.append(result)
+-            print('%s...\t%s\n' % (
++            print(('%s...\t%s\n' % (
+                    result.get('desc', ''),
+                    status
+                 )
+-            ).expandtabs(40)
++            ).expandtabs(40))
+ 
+     @property
+     def platform(self):
+@@ -212,7 +212,7 @@
+                         fsutil_output = subprocess.check_output(command)
+                         status = 'GOOD, FIXED'
+                         desc = 'lastaccess disabled systemwide'
+-                    except subprocess.CalledProcessError, e:
++                    except subprocess.CalledProcessError as e:
+                         desc = 'lastaccess enabled systemwide'
+                         if e.output.find('denied') != -1:
+                             status = 'BAD, FIX DENIED'
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/dotproperties.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/dotproperties.py	(refactored)
+@@ -5,7 +5,7 @@
+ # This file contains utility functions for reading .properties files, like
+ # region.properties.
+ 
+-from __future__ import absolute_import, unicode_literals
++
+ 
+ import codecs
+ import re
+@@ -14,7 +14,7 @@
+ if sys.version_info[0] == 3:
+     str_type = str
+ else:
+-    str_type = basestring
++    str_type = str
+ 
+ class DotProperties:
+     r'''A thin representation of a key=value .properties file.'''
+@@ -52,7 +52,7 @@
+         if not prefix.endswith('.'):
+             prefix = prefix + '.'
+         indexes = []
+-        for k, v in self._properties.iteritems():
++        for k, v in self._properties.items():
+             if not k.startswith(prefix):
+                 continue
+             key = k[len(prefix):]
+@@ -73,7 +73,7 @@
+         if not prefix.endswith('.'):
+             prefix = prefix + '.'
+ 
+-        D = dict((k[len(prefix):], v) for k, v in self._properties.iteritems()
++        D = dict((k[len(prefix):], v) for k, v in self._properties.items()
+                  if k.startswith(prefix) and '.' not in k[len(prefix):])
+ 
+         for required_key in required_keys:
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/html_build_viewer.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/html_build_viewer.py	(refactored)
+@@ -4,16 +4,16 @@
+ 
+ # This module contains code for running an HTTP server to view build info.
+ 
+-from __future__ import absolute_import, unicode_literals
+ 
+-import BaseHTTPServer
++
++import http.server
+ import json
+ import os
+ 
+ import requests
+ 
+ 
+-class HTTPHandler(BaseHTTPServer.BaseHTTPRequestHandler):
++class HTTPHandler(http.server.BaseHTTPRequestHandler):
+     def do_GET(self):
+         s = self.server.wrapper
+         p = self.path
+@@ -92,7 +92,7 @@
+         self.doc_root = doc_root
+         self.json_files = {}
+ 
+-        self.server = BaseHTTPServer.HTTPServer((address, port), HTTPHandler)
++        self.server = http.server.HTTPServer((address, port), HTTPHandler)
+         self.server.wrapper = self
+         self.do_shutdown = False
+ 
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/jar.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/jar.py	(refactored)
+@@ -8,7 +8,7 @@
+ See the documentation for jar.mn on MDC for further details on the format.
+ '''
+ 
+-from __future__ import absolute_import
++
+ 
+ import sys
+ import os
+@@ -17,7 +17,7 @@
+ import logging
+ from time import localtime
+ from MozZipFile import ZipFile
+-from cStringIO import StringIO
++from io import StringIO
+ from collections import defaultdict
+ 
+ from mozbuild.preprocessor import Preprocessor
+@@ -302,9 +302,9 @@
+         '''updateManifest replaces the % in the chrome registration entries
+         with the given chrome base path, and updates the given manifest file.
+         '''
+-        myregister = dict.fromkeys(map(lambda s: s.replace('%',
+-            chromebasepath), register))
+-        addEntriesToListFile(manifestPath, myregister.iterkeys())
++        myregister = dict.fromkeys([s.replace('%',
++            chromebasepath) for s in register])
++        addEntriesToListFile(manifestPath, iter(myregister.keys()))
+ 
+     def makeJar(self, infile, jardir):
+         '''makeJar is the main entry point to JarMaker.
+@@ -322,7 +322,7 @@
+         elif self.relativesrcdir:
+             self.localedirs = \
+                 self.generateLocaleDirs(self.relativesrcdir)
+-        if isinstance(infile, basestring):
++        if isinstance(infile, str):
+             logging.info('processing ' + infile)
+             self.sourcedirs.append(_normpath(os.path.dirname(infile)))
+         pp = self.pp.clone()
+@@ -372,7 +372,7 @@
+             jarfilepath = jarfile + '.jar'
+             try:
+                 os.makedirs(os.path.dirname(jarfilepath))
+-            except OSError, error:
++            except OSError as error:
+                 if error.errno != errno.EEXIST:
+                     raise
+             jf = ZipFile(jarfilepath, 'a', lock=True)
+@@ -514,7 +514,7 @@
+             # remove previous link or file
+             try:
+                 os.remove(out)
+-            except OSError, e:
++            except OSError as e:
+                 if e.errno != errno.ENOENT:
+                     raise
+             return open(out, 'wb')
+@@ -525,7 +525,7 @@
+             if not os.path.isdir(outdir):
+                 try:
+                     os.makedirs(outdir)
+-                except OSError, error:
++                except OSError as error:
+                     if error.errno != errno.EEXIST:
+                         raise
+             return out
+@@ -541,7 +541,7 @@
+             # remove previous link or file
+             try:
+                 os.remove(out)
+-            except OSError, e:
++            except OSError as e:
+                 if e.errno != errno.ENOENT:
+                     raise
+             if sys.platform != 'win32':
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/mach_commands.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/mach_commands.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, # You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import absolute_import, print_function, unicode_literals
++
+ 
+ import argparse
+ import errno
+@@ -136,7 +136,7 @@
+         # terminal is a blessings.Terminal.
+         self._t = terminal
+         self._fh = sys.stdout
+-        self.tiers = monitor.tiers.tier_status.viewitems()
++        self.tiers = monitor.tiers.tier_status.items()
+ 
+     def clear(self):
+         """Removes the footer from the current terminal."""
+@@ -802,7 +802,7 @@
+             dirpath = None
+ 
+         type_counts = database.type_counts(dirpath)
+-        sorted_counts = sorted(type_counts.iteritems(),
++        sorted_counts = sorted(iter(type_counts.items()),
+             key=operator.itemgetter(1))
+ 
+         total = 0
+@@ -957,7 +957,7 @@
+             processes[i].run()
+ 
+         exit_code = 0
+-        for process in processes.values():
++        for process in list(processes.values()):
+             status = process.wait()
+             if status:
+                 exit_code = status
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/makeutil.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/makeutil.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import absolute_import
++
+ 
+ import os
+ import re
+@@ -66,7 +66,7 @@
+         self._list = []
+         self._set = set()
+ 
+-    def __nonzero__(self):
++    def __bool__(self):
+         return bool(self._set)
+ 
+     def __iter__(self):
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/milestone.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/milestone.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import absolute_import, print_function, unicode_literals
++
+ 
+ import argparse
+ import os
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/mozconfig.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/mozconfig.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import absolute_import, unicode_literals
++
+ 
+ import filecmp
+ import os
+@@ -306,7 +306,7 @@
+ 
+         # Environment variables also appear as shell variables, but that's
+         # uninteresting duplication of information. Filter them out.
+-        filt = lambda x, y: {k: v for k, v in x.items() if k not in y}
++        filt = lambda x, y: {k: v for k, v in list(x.items()) if k not in y}
+         result['vars'] = diff_vars(
+             filt(parsed['vars_before'], parsed['env_before']),
+             filt(parsed['vars_after'], parsed['env_after'])
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/mozinfo.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/mozinfo.py	(refactored)
+@@ -5,7 +5,7 @@
+ # This module produces a JSON file that provides basic build info and
+ # configuration metadata.
+ 
+-from __future__ import absolute_import
++
+ 
+ import os
+ import re
+@@ -154,7 +154,7 @@
+     and what keys are produced.
+     """
+     build_conf = build_dict(config, env)
+-    if isinstance(file, basestring):
++    if isinstance(file, str):
+         file = open(file, 'wb')
+ 
+     json.dump(build_conf, file, sort_keys=True, indent=4)
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/preprocessor.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/preprocessor.py	(refactored)
+@@ -27,7 +27,8 @@
+ import re
+ from optparse import OptionParser
+ import errno
+-from makeutil import Makefile
++from .makeutil import Makefile
++from functools import reduce
+ 
+ # hack around win32 mangling our line endings
+ # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/65443
+@@ -56,7 +57,7 @@
+         self.__ignore_whitespace()
+         self.e = self.__get_logical_or()
+         if self.content:
+-            raise Expression.ParseError, self
++            raise Expression.ParseError(self)
+ 
+     def __get_logical_or(self):
+         """
+@@ -157,7 +158,7 @@
+                 if word_len:
+                     rv = Expression.__ASTLeaf('string', self.content[:word_len])
+                 else:
+-                    raise Expression.ParseError, self
++                    raise Expression.ParseError(self)
+         self.__strip(word_len)
+         self.__ignore_whitespace()
+         return rv
+@@ -196,7 +197,7 @@
+                 return left and right
+             elif tok[1].value == '||':
+                 return left or right
+-            raise Expression.ParseError, self
++            raise Expression.ParseError(self)
+ 
+         # Mapping from token types to evaluator functions
+         # Apart from (non-)equality, all these can be simple lambda forms.
+@@ -230,7 +231,7 @@
+         def __repr__(self):
+             return self.value.__repr__()
+ 
+-    class ParseError(StandardError):
++    class ParseError(Exception):
+         """
+         Error raised when parsing fails.
+         It has two members, offset and content, which give the offset of the
+@@ -278,7 +279,7 @@
+         self.context = Context()
+         for k,v in {'FILE': '',
+                     'LINE': 0,
+-                    'DIRECTORY': os.path.abspath('.')}.iteritems():
++                    'DIRECTORY': os.path.abspath('.')}.items():
+             self.context[k] = v
+         self.actionLevel = 0
+         self.disableLevel = 0
+@@ -292,21 +293,21 @@
+         self.cmds = {}
+         for cmd, level in {'define': 0,
+                            'undef': 0,
+-                           'if': sys.maxint,
+-                           'ifdef': sys.maxint,
+-                           'ifndef': sys.maxint,
++                           'if': sys.maxsize,
++                           'ifdef': sys.maxsize,
++                           'ifndef': sys.maxsize,
+                            'else': 1,
+                            'elif': 1,
+                            'elifdef': 1,
+                            'elifndef': 1,
+-                           'endif': sys.maxint,
++                           'endif': sys.maxsize,
+                            'expand': 0,
+                            'literal': 0,
+                            'filter': 0,
+                            'unfilter': 0,
+                            'include': 0,
+                            'includesubst': 0,
+-                           'error': 0}.iteritems():
++                           'error': 0}.items():
+             self.cmds[cmd] = (level, getattr(self, 'do_' + cmd))
+         self.out = sys.stdout
+         self.setMarker(marker)
+@@ -469,7 +470,7 @@
+                 raise Preprocessor.Error(self, "--depend doesn't work with stdout",
+                                          None)
+             try:
+-                from makeutil import Makefile
++                from .makeutil import Makefile
+             except:
+                 raise Preprocessor.Error(self, "--depend requires the "
+                                                "mozbuild.makeutil module", None)
+@@ -684,7 +685,7 @@
+         current = dict(self.filters)
+         for f in filters:
+             current[f] = getattr(self, 'filter_' + f)
+-        filterNames = current.keys()
++        filterNames = list(current.keys())
+         filterNames.sort()
+         self.filters = [(fn, current[fn]) for fn in filterNames]
+         return
+@@ -694,7 +695,7 @@
+         for f in filters:
+             if f in current:
+                 del current[f]
+-        filterNames = current.keys()
++        filterNames = list(current.keys())
+         filterNames.sort()
+         self.filters = [(fn, current[fn]) for fn in filterNames]
+         return
+@@ -739,7 +740,7 @@
+         args can either be a file name, or a file-like object.
+         Files should be opened, and will be closed after processing.
+         """
+-        isName = type(args) == str or type(args) == unicode
++        isName = type(args) == str or type(args) == str
+         oldCheckLineNumbers = self.checkLineNumbers
+         self.checkLineNumbers = False
+         if isName:
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/pythonutil.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/pythonutil.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import absolute_import
++
+ 
+ import os
+ import sys
+@@ -11,7 +11,7 @@
+ def iter_modules_in_path(*paths):
+     paths = [os.path.abspath(os.path.normcase(p)) + os.sep
+              for p in paths]
+-    for name, module in sys.modules.items():
++    for name, module in list(sys.modules.items()):
+         if not hasattr(module, '__file__'):
+             continue
+ 
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/shellutil.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/shellutil.py	(refactored)
+@@ -15,7 +15,7 @@
+     # which matches the pattern and captures it in a named match group.
+     # The group names and patterns are given as arguments.
+     all_tokens = '|'.join('(?P<%s>%s)' % (name, value)
+-                          for name, value in tokens.iteritems())
++                          for name, value in tokens.items())
+     nonescaped = r'(?<!\\)(?:%s)' % all_tokens
+ 
+     # The final pattern matches either the above pattern, or an escaped
+@@ -96,7 +96,7 @@
+             self.cline = self.cline[m.end():]
+ 
+             match = {name: value
+-                     for name, value in m.groupdict().items() if value}
++                     for name, value in list(m.groupdict().items()) if value}
+             if 'quote' in match:
+                 # " or ' start a quoted string
+                 if match['quote'] == '"':
+@@ -144,7 +144,7 @@
+             self._push(self.cline[:m.start()])
+             self.cline = self.cline[m.end():]
+             match = {name: value
+-                     for name, value in m.groupdict().items() if value}
++                     for name, value in list(m.groupdict().items()) if value}
+             if 'quote' in match:
+                 # a double quote ends the quoted string, so go back to
+                 # unquoted parsing
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/sphinx.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/sphinx.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import absolute_import
++
+ 
+ import importlib
+ import os
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/testing.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/testing.py	(refactored)
+@@ -2,9 +2,9 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import absolute_import, unicode_literals
+-
+-import cPickle as pickle
++
++
++import pickle as pickle
+ import os
+ import sys
+ 
+@@ -62,7 +62,7 @@
+         if test_defaults:
+             with open(test_defaults, 'rb') as fh:
+                 defaults = pickle.load(fh)
+-        for path, tests in test_data.items():
++        for path, tests in list(test_data.items()):
+             for metadata in tests:
+                 if defaults:
+                     manifest = metadata['manifest']
+@@ -303,7 +303,7 @@
+ WEB_PLATFORM_TESTS_FLAVORS = ('web-platform-tests',)
+ 
+ def all_test_flavors():
+-    return ([v[0] for v in TEST_MANIFESTS.values()] +
++    return ([v[0] for v in list(TEST_MANIFESTS.values())] +
+             list(REFTEST_FLAVORS) +
+             list(WEB_PLATFORM_TESTS_FLAVORS) +
+             ['python'])
+@@ -445,7 +445,7 @@
+     only a few tests need to be run.
+     """
+     flavor_info = {flavor: (root, prefix, install)
+-                   for (flavor, root, prefix, install) in TEST_MANIFESTS.values()}
++                   for (flavor, root, prefix, install) in list(TEST_MANIFESTS.values())}
+     objdir_dest = mozpath.join(topobjdir, tests_root)
+ 
+     converter = SupportFilesConverter()
+@@ -527,7 +527,7 @@
+         paths_file = os.path.join(context.config.topsrcdir, "testing",
+                                   "web-platform", "tests", "tools", "localpaths.py")
+         _globals = {"__file__": paths_file}
+-        execfile(paths_file, _globals)
++        exec(compile(open(paths_file).read(), paths_file, 'exec'), _globals)
+         import manifest as wptmanifest
+     finally:
+         sys.path = old_path
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/util.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/util.py	(refactored)
+@@ -5,7 +5,7 @@
+ # This file contains miscellaneous utility functions that don't belong anywhere
+ # in particular.
+ 
+-from __future__ import absolute_import, unicode_literals, print_function
++
+ 
+ import argparse
+ import collections
+@@ -36,7 +36,7 @@
+ if sys.version_info[0] == 3:
+     str_type = str
+ else:
+-    str_type = basestring
++    str_type = str
+ 
+ if sys.platform == 'win32':
+     _kernel32 = ctypes.windll.kernel32
+@@ -78,7 +78,7 @@
+     return h.hexdigest()
+ 
+ 
+-class EmptyValue(unicode):
++class EmptyValue(str):
+     """A dummy type that behaves like an empty string and sequence.
+ 
+     This type exists in order to support
+@@ -92,7 +92,7 @@
+ class ReadOnlyNamespace(object):
+     """A class for objects with immutable attributes set at initialization."""
+     def __init__(self, **kwargs):
+-        for k, v in kwargs.iteritems():
++        for k, v in kwargs.items():
+             super(ReadOnlyNamespace, self).__setattr__(k, v)
+ 
+     def __delattr__(self, key):
+@@ -152,7 +152,7 @@
+     if d and not os.path.exists(path):
+         try:
+             os.makedirs(d)
+-        except OSError, error:
++        except OSError as error:
+             if error.errno != errno.EEXIST:
+                 raise
+ 
+@@ -224,7 +224,7 @@
+         self.mode = mode
+ 
+     def write(self, buf):
+-        if isinstance(buf, unicode):
++        if isinstance(buf, str):
+             buf = buf.encode('utf-8')
+         BytesIO.write(self, buf)
+ 
+@@ -381,7 +381,7 @@
+     def __add__(self, other):
+         # Allow None and EmptyValue is a special case because it makes undefined
+         # variable references in moz.build behave better.
+-        other = [] if isinstance(other, (types.NoneType, EmptyValue)) else other
++        other = [] if isinstance(other, (type(None), EmptyValue)) else other
+         if not isinstance(other, list):
+             raise ValueError('Only lists can be appended to lists.')
+ 
+@@ -390,7 +390,7 @@
+         return new_list
+ 
+     def __iadd__(self, other):
+-        other = [] if isinstance(other, (types.NoneType, EmptyValue)) else other
++        other = [] if isinstance(other, (type(None), EmptyValue)) else other
+         if not isinstance(other, list):
+             raise ValueError('Only lists can be appended to lists.')
+ 
+@@ -542,14 +542,14 @@
+     functions below.
+     """
+     assert isinstance(flags, dict)
+-    assert all(isinstance(v, type) for v in flags.values())
++    assert all(isinstance(v, type) for v in list(flags.values()))
+ 
+     class Flags(object):
+-        __slots__ = flags.keys()
++        __slots__ = list(flags.keys())
+         _flags = flags
+ 
+         def update(self, **kwargs):
+-            for k, v in kwargs.iteritems():
++            for k, v in kwargs.items():
+                 setattr(self, k, v)
+ 
+         def __getattr__(self, name):
+@@ -1080,14 +1080,14 @@
+     # issue.  So we do a little dance to filter it out ourselves.
+     dummy_fill_value = ("dummy",)
+     def filter_out_dummy(iterable):
+-        return itertools.ifilter(lambda x: x != dummy_fill_value,
++        return filter(lambda x: x != dummy_fill_value,
+                                  iterable)
+ 
+     # From the itertools documentation, slightly modified:
+     def grouper(n, iterable):
+         "grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx"
+         args = [iter(iterable)] * n
+-        return itertools.izip_longest(fillvalue=dummy_fill_value, *args)
++        return itertools.zip_longest(fillvalue=dummy_fill_value, *args)
+ 
+     for i, unified_group in enumerate(grouper(files_per_unified_file,
+                                               files)):
+@@ -1104,7 +1104,7 @@
+         [(1,2), (3,4), (5,6)]
+     '''
+     i = iter(iterable)
+-    return itertools.izip_longest(i, i)
++    return itertools.zip_longest(i, i)
+ 
+ 
+ VARIABLES_RE = re.compile('\$\((\w+)\)')
+@@ -1122,7 +1122,7 @@
+         value = variables.get(name)
+         if not value:
+             continue
+-        if not isinstance(value, types.StringTypes):
++        if not isinstance(value, (str,)):
+             value = ' '.join(value)
+         result += value
+     return result
+@@ -1149,7 +1149,7 @@
+     pass
+ 
+ 
+-class EnumString(unicode):
++class EnumString(str):
+     '''A string type that only can have a limited set of values, similarly to
+     an Enum, and can only be compared against that set of values.
+ 
+@@ -1185,19 +1185,18 @@
+     # quoting could be done with either ' or ".
+     if c == "'":
+         return "\\'"
+-    return unicode(c.encode('unicode_escape'))
++    return str(c.encode('unicode_escape'))
+ 
+ # Mapping table between raw characters below \x80 and their escaped
+ # counterpart, when they differ
+ _INDENTED_REPR_TABLE = {
+     c: e
+-    for c, e in map(lambda x: (x, _escape_char(x)),
+-                    map(unichr, range(128)))
++    for c, e in [(x, _escape_char(x)) for x in list(map(chr, list(range(128))))]
+     if c != e
+ }
+ # Regexp matching all characters to escape.
+ _INDENTED_REPR_RE = re.compile(
+-    '([' + ''.join(_INDENTED_REPR_TABLE.values()) + ']+)')
++    '([' + ''.join(list(_INDENTED_REPR_TABLE.values())) + ']+)')
+ 
+ 
+ def indented_repr(o, indent=4):
+@@ -1223,7 +1222,7 @@
+         elif isinstance(o, bytes):
+             yield 'b'
+             yield repr(o)
+-        elif isinstance(o, unicode):
++        elif isinstance(o, str):
+             yield "'"
+             # We want a readable string (non escaped unicode), but some
+             # special characters need escaping (e.g. \n, \t, etc.)
+@@ -1253,11 +1252,11 @@
+     if isinstance(obj, dict):
+         return {
+             encode(k, encoding): encode(v, encoding)
+-            for k, v in obj.iteritems()
++            for k, v in obj.items()
+         }
+     if isinstance(obj, bytes):
+         return obj
+-    if isinstance(obj, unicode):
++    if isinstance(obj, str):
+         return obj.encode(encoding)
+     if isinstance(obj, Iterable):
+         return [encode(i, encoding) for i in obj]
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/vendor_rust.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/vendor_rust.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, # You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import absolute_import, print_function, unicode_literals
++
+ 
+ from distutils.version import LooseVersion
+ import logging
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/virtualenv.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/virtualenv.py	(refactored)
+@@ -5,7 +5,7 @@
+ # This file contains code for populating the virtualenv environment for
+ # Mozilla's build system. It is typically called as part of configure.
+ 
+-from __future__ import absolute_import, print_function, unicode_literals
++
+ 
+ import distutils.sysconfig
+ import os
+@@ -461,8 +461,8 @@
+         and call .ensure() and .activate() to make the virtualenv active.
+         """
+ 
+-        execfile(self.activate_path, dict(__file__=self.activate_path))
+-        if isinstance(os.environ['PATH'], unicode):
++        exec(compile(open(self.activate_path).read(), self.activate_path, 'exec'), dict(__file__=self.activate_path))
++        if isinstance(os.environ['PATH'], str):
+             os.environ['PATH'] = os.environ['PATH'].encode('utf-8')
+ 
+     def install_pip_package(self, package):
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/action/buildlist.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/action/buildlist.py	(refactored)
+@@ -7,7 +7,7 @@
+ 
+ Usage: buildlist.py <filename> <entry> [<entry> ...]
+ '''
+-from __future__ import absolute_import, print_function
++
+ 
+ import sys
+ import os
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/action/cl.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/action/cl.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import absolute_import
++
+ 
+ import ctypes
+ import os
+@@ -55,7 +55,7 @@
+             break
+ 
+     if target is None:
+-        print >>sys.stderr, "No target set"
++        print("No target set", file=sys.stderr)
+         return 1
+ 
+     # Assume the source file is the last argument
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/action/dump_env.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/action/dump_env.py	(refactored)
+@@ -6,5 +6,5 @@
+ # native paths printed on Windows so that these paths can be incorporated
+ # into Python configure's environment.
+ import os
+-for key, value in os.environ.items():
+-    print('%s=%s' % (key, value))
++for key, value in list(os.environ.items()):
++    print(('%s=%s' % (key, value)))
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/action/explode_aar.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/action/explode_aar.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import absolute_import, print_function, unicode_literals
++
+ 
+ import argparse
+ import errno
+@@ -44,7 +44,7 @@
+     assets = mozpath.join(destdir, 'assets')
+     try:
+         os.rmdir(assets)
+-    except OSError, e:
++    except OSError as e:
+         if e.errno in (errno.ENOTEMPTY, errno.ENOENT):
+             pass
+         else:
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/action/file_generate.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/action/file_generate.py	(refactored)
+@@ -6,7 +6,7 @@
+ # the arguments that can be used to generate the output file, call the
+ # script's |main| method with appropriate arguments.
+ 
+-from __future__ import absolute_import, print_function
++
+ 
+ import argparse
+ import imp
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/action/generate_suggestedsites.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/action/generate_suggestedsites.py	(refactored)
+@@ -26,7 +26,7 @@
+ directory e.g. raw/suggestedsites.json, raw-pt-rBR/suggestedsites.json.
+ '''
+ 
+-from __future__ import absolute_import, print_function
++
+ 
+ import argparse
+ import copy
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/action/generate_symbols_file.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/action/generate_symbols_file.py	(refactored)
+@@ -2,12 +2,12 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import absolute_import, print_function, unicode_literals
++
+ 
+ import argparse
+ import buildconfig
+ import os
+-from StringIO import StringIO
++from io import StringIO
+ from mozbuild.preprocessor import Preprocessor
+ from mozbuild.util import DefinesAction
+ 
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/action/jar_maker.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/action/jar_maker.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import absolute_import
++
+ 
+ import sys
+ 
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/action/make_dmg.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/action/make_dmg.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import print_function
++
+ 
+ from mozbuild.base import MozbuildObject
+ from mozpack import dmg
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/action/output_searchplugins_list.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/action/output_searchplugins_list.py	(refactored)
+@@ -18,4 +18,4 @@
+ else:
+   engines = searchinfo["default"]["visibleDefaultEngines"]
+ 
+-print '\n'.join(engines)
++print('\n'.join(engines))
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/action/package_fennec_apk.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/action/package_fennec_apk.py	(refactored)
+@@ -6,7 +6,7 @@
+ Script to produce an Android package (.apk) for Fennec.
+ '''
+ 
+-from __future__ import absolute_import, print_function
++
+ 
+ import argparse
+ import buildconfig
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/action/preprocessor.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/action/preprocessor.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import absolute_import
++
+ 
+ import sys
+ 
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/action/process_define_files.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/action/process_define_files.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import absolute_import, print_function, unicode_literals
++
+ 
+ import argparse
+ import os
+@@ -55,7 +55,7 @@
+                                 'CONFIGURE_DEFINE_FILE')
+                         defines = '\n'.join(sorted(
+                             '#define %s %s' % (name, val)
+-                            for name, val in config.defines.iteritems()
++                            for name, val in config.defines.items()
+                             if name not in config.non_global_defines))
+                         l = l[:m.start('cmd') - 1] \
+                             + defines + l[m.end('name'):]
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/action/process_install_manifest.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/action/process_install_manifest.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import absolute_import, print_function, unicode_literals
++
+ 
+ import argparse
+ import os
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/action/test_archive.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/action/test_archive.py	(refactored)
+@@ -8,7 +8,7 @@
+ # It is defined inline because this was easiest to make test archive
+ # generation faster.
+ 
+-from __future__ import absolute_import, print_function, unicode_literals
++
+ 
+ import argparse
+ import itertools
+@@ -433,7 +433,7 @@
+ # "common" is our catch all archive and it ignores things from other archives.
+ # Verify nothing sneaks into ARCHIVE_FILES without a corresponding exclusion
+ # rule in the "common" archive.
+-for k, v in ARCHIVE_FILES.items():
++for k, v in list(ARCHIVE_FILES.items()):
+     # Skip mozharness because it isn't staged.
+     if k in ('common', 'mozharness'):
+         continue
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/action/webidl.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/action/webidl.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import absolute_import
++
+ 
+ import sys
+ 
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/action/xpccheck.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/action/xpccheck.py	(refactored)
+@@ -8,7 +8,7 @@
+ Usage: xpccheck.py <directory> [<directory> ...]
+ '''
+ 
+-from __future__ import absolute_import
++
+ 
+ import sys
+ import os
+@@ -40,7 +40,7 @@
+         break
+    
+     if not found:
+-      print >>sys.stderr, "TEST-UNEXPECTED-FAIL | xpccheck | test %s is missing from test manifest %s!" % (name, os.path.join(directory, 'xpcshell.ini'))
++      print("TEST-UNEXPECTED-FAIL | xpccheck | test %s is missing from test manifest %s!" % (name, os.path.join(directory, 'xpcshell.ini')), file=sys.stderr)
+       sys.exit(1)
+ 
+ def verifyIniFile(initests, directory):
+@@ -60,12 +60,12 @@
+         break
+ 
+     if not found:
+-      print >>sys.stderr, "TEST-UNEXPECTED-FAIL | xpccheck | found %s in xpcshell.ini and not in directory '%s'" % (name, directory)
++      print("TEST-UNEXPECTED-FAIL | xpccheck | found %s in xpcshell.ini and not in directory '%s'" % (name, directory), file=sys.stderr)
+       sys.exit(1)
+ 
+ def main(argv):
+   if len(argv) < 2:
+-    print >>sys.stderr, "Usage: xpccheck.py <topsrcdir> <directory> [<directory> ...]"
++    print("Usage: xpccheck.py <topsrcdir> <directory> [<directory> ...]", file=sys.stderr)
+     sys.exit(1)
+ 
+   topsrcdir = argv[0]
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/action/xpidl-process.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/action/xpidl-process.py	(refactored)
+@@ -7,7 +7,7 @@
+ # input IDL file(s). It's purpose is to directly support the build
+ # system. The API will change to meet the needs of the build system.
+ 
+-from __future__ import absolute_import
++
+ 
+ import argparse
+ import os
+@@ -58,7 +58,7 @@
+ 
+     # TODO use FileAvoidWrite once it supports binary mode.
+     xpt_path = os.path.join(xpt_dir, '%s.xpt' % module)
+-    xpt_link(xpts.values()).write(xpt_path)
++    xpt_link(list(xpts.values())).write(xpt_path)
+ 
+     rule.add_targets([xpt_path])
+     if deps_dir:
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/action/zip.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/action/zip.py	(refactored)
+@@ -5,7 +5,7 @@
+ # This script creates a zip file, but will also strip any binaries
+ # it finds before adding them to the zip.
+ 
+-from __future__ import absolute_import
++
+ 
+ from mozpack.files import FileFinder
+ from mozpack.copier import Jarrer
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/backend/android_eclipse.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/backend/android_eclipse.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import absolute_import, unicode_literals
++
+ 
+ import itertools
+ import os
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/backend/base.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/backend/base.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import absolute_import, unicode_literals
++
+ 
+ from abc import (
+     ABCMeta,
+@@ -30,15 +30,13 @@
+ from mozbuild.base import ExecutionSummary
+ 
+ 
+-class BuildBackend(LoggingMixin):
++class BuildBackend(LoggingMixin, metaclass=ABCMeta):
+     """Abstract base class for build backends.
+ 
+     A build backend is merely a consumer of the build configuration (the output
+     of the frontend processing). It does something with said data. What exactly
+     is the discretion of the specific implementation.
+     """
+-
+-    __metaclass__ = ABCMeta
+ 
+     def __init__(self, environment):
+         assert isinstance(environment, ConfigEnvironment)
+@@ -252,7 +250,7 @@
+         srcdir = mozpath.dirname(obj.input_path)
+         pp.context.update({
+             k: ' '.join(v) if isinstance(v, list) else v
+-            for k, v in obj.config.substs.iteritems()
++            for k, v in obj.config.substs.items()
+         })
+         pp.context.update(
+             top_srcdir=obj.topsrcdir,
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/backend/common.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/backend/common.py	(refactored)
+@@ -2,9 +2,9 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import absolute_import, unicode_literals
+-
+-import cPickle as pickle
++
++
++import pickle as pickle
+ import itertools
+ import json
+ import os
+@@ -194,11 +194,11 @@
+     def add_defaults(self, manifest):
+         if not hasattr(manifest, 'manifest_defaults'):
+             return
+-        for sub_manifest, defaults in manifest.manifest_defaults.items():
++        for sub_manifest, defaults in list(manifest.manifest_defaults.items()):
+             self.manifest_defaults[sub_manifest] = defaults
+ 
+     def add_installs(self, obj, topsrcdir):
+-        for src, (dest, _) in obj.installs.iteritems():
++        for src, (dest, _) in obj.installs.items():
+             key = src[len(topsrcdir)+1:]
+             self.installs_by_path[key].append((src, dest))
+         for src, pat, dest in obj.pattern_installs:
+@@ -379,7 +379,7 @@
+ 
+         path = mozpath.join(self.environment.topobjdir, 'test-installs.pkl')
+         with self._write_file(path, mode='rb') as fh:
+-            pickle.dump({k: v for k, v in self._test_manager.installs_by_path.items()
++            pickle.dump({k: v for k, v in list(self._test_manager.installs_by_path.items())
+                          if k in self._test_manager.deferred_installs},
+                         fh,
+                         protocol=2)
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/backend/configenvironment.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/backend/configenvironment.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import absolute_import
++
+ 
+ import os
+ import sys
+@@ -17,7 +17,7 @@
+ 
+ 
+ if sys.version_info.major == 2:
+-    text_type = unicode
++    text_type = str
+ else:
+     text_type = str
+ 
+@@ -176,7 +176,7 @@
+                 except UnicodeDecodeError:
+                     return v.decode('utf-8', 'replace')
+ 
+-        for k, v in self.substs.items():
++        for k, v in list(self.substs.items()):
+             if not isinstance(v, StringTypes):
+                 if isinstance(v, Iterable):
+                     type(v)(decode(i) for i in v)
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/backend/cpp_eclipse.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/backend/cpp_eclipse.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import absolute_import
++
+ 
+ import errno
+ import random
+@@ -218,12 +218,12 @@
+         cproject_header = cproject_header.replace('@MACH_COMMAND@', os.path.join(self.environment.topsrcdir, 'mach'))
+         fh.write(cproject_header)
+ 
+-        for path, defines in self._paths_to_defines.items():
++        for path, defines in list(self._paths_to_defines.items()):
+             folderinfo = CPROJECT_TEMPLATE_FOLDER_INFO_HEADER
+             folderinfo = folderinfo.replace('@FOLDER_ID@', str(random.randint(1000000, 99999999999)))
+             folderinfo = folderinfo.replace('@FOLDER_NAME@', 'tree/' + path)
+             fh.write(folderinfo)
+-            for k, v in defines.items():
++            for k, v in list(defines.items()):
+                 define = ET.Element('listOptionValue')
+                 define.set('builtIn', 'false')
+                 define.set('value', str(k) + "=" + str(v))
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/backend/fastermake.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/backend/fastermake.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import absolute_import, unicode_literals, print_function
++
+ 
+ from mozbuild.backend.base import PartialBackend
+ from mozbuild.backend.common import CommonBackend
+@@ -130,12 +130,12 @@
+             if value is not None:
+                 mk.add_statement('%s = %s' % (var, value))
+ 
+-        install_manifests_bases = self._install_manifests.keys()
++        install_manifests_bases = list(self._install_manifests.keys())
+ 
+         # Add information for chrome manifest generation
+         manifest_targets = []
+ 
+-        for target, entries in self._manifest_entries.iteritems():
++        for target, entries in self._manifest_entries.items():
+             manifest_targets.append(target)
+             install_target = mozpath.basedir(target, install_manifests_bases)
+             self._install_manifests[install_target].add_content(
+@@ -144,16 +144,16 @@
+ 
+         # Add information for install manifests.
+         mk.add_statement('INSTALL_MANIFESTS = %s'
+-                         % ' '.join(self._install_manifests.keys()))
++                         % ' '.join(list(self._install_manifests.keys())))
+ 
+         # Add dependencies we infered:
+-        for target, deps in self._dependencies.iteritems():
++        for target, deps in self._dependencies.items():
+             mk.create_rule([target]).add_dependencies(
+                 '$(TOPOBJDIR)/%s' % d for d in deps)
+ 
+         mk.add_statement('include $(TOPSRCDIR)/config/faster/rules.mk')
+ 
+-        for base, install_manifest in self._install_manifests.iteritems():
++        for base, install_manifest in self._install_manifests.items():
+             with self._write_file(
+                     mozpath.join(self.environment.topobjdir, 'faster',
+                                  'install_%s' % base.replace('/', '_'))) as fh:
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/backend/mach_commands.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/backend/mach_commands.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import absolute_import, print_function, unicode_literals
++
+ 
+ import argparse
+ import os
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/backend/recursivemake.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/backend/recursivemake.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import absolute_import, unicode_literals
++
+ 
+ import logging
+ import os
+@@ -12,7 +12,7 @@
+     defaultdict,
+     namedtuple,
+ )
+-from StringIO import StringIO
++from io import StringIO
+ from itertools import chain
+ 
+ from mozpack.manifests import (
+@@ -74,6 +74,7 @@
+ )
+ from ..makeutil import Makefile
+ from mozbuild.shellutil import quote as shell_quote
++from functools import reduce
+ 
+ MOZBUILD_VARIABLES = [
+     b'ANDROID_APK_NAME',
+@@ -214,7 +215,7 @@
+         self.fh.write(buf)
+ 
+     def write_once(self, buf):
+-        if isinstance(buf, unicode):
++        if isinstance(buf, str):
+             buf = buf.encode('utf-8')
+         if b'\n' + buf not in self.fh.getvalue():
+             self.write(buf)
+@@ -283,8 +284,8 @@
+         Helper function to call a filter from compute_dependencies and
+         traverse.
+         """
+-        return filter(current, self._traversal.get(current,
+-            self.SubDirectories()))
++        return list(filter(current, self._traversal.get(current,
++            self.SubDirectories())))
+ 
+     def compute_dependencies(self, filter=None):
+         """
+@@ -638,7 +639,7 @@
+         convenience variables, and the other dependency definitions for a
+         hopefully proper directory traversal.
+         """
+-        for tier, no_skip in self._no_skip.items():
++        for tier, no_skip in list(self._no_skip.items()):
+             self.log(logging.DEBUG, 'fill_root_mk', {
+                 'number': len(no_skip), 'tier': tier
+                 }, 'Using {number} directories during {tier}')
+@@ -684,7 +685,7 @@
+         for tier, filter in filters:
+             main, all_deps = \
+                 self._traversal.compute_dependencies(filter)
+-            for dir, deps in all_deps.items():
++            for dir, deps in list(all_deps.items()):
+                 if deps is not None or (dir in self._idl_dirs \
+                                         and tier == 'export'):
+                     rule = root_deps_mk.create_rule(['%s/%s' % (dir, tier)])
+@@ -697,7 +698,7 @@
+                 rule.add_dependencies('%s/%s' % (d, tier) for d in main)
+ 
+         all_compile_deps = reduce(lambda x,y: x|y,
+-            self._compile_graph.values()) if self._compile_graph else set()
++            list(self._compile_graph.values())) if self._compile_graph else set()
+         compile_roots = set(self._compile_graph.keys()) - all_compile_deps
+ 
+         rule = root_deps_mk.create_rule(['recurse_compile'])
+@@ -839,7 +840,7 @@
+         self._fill_root_mk()
+ 
+         # Make the master test manifest files.
+-        for flavor, t in self._test_manifests.items():
++        for flavor, t in list(self._test_manifests.items()):
+             install_prefix, manifests = t
+             manifest_stem = mozpath.join(install_prefix, '%s.ini' % flavor)
+             self._write_master_test_manifest(mozpath.join(
+@@ -945,7 +946,7 @@
+         for p in ('Makefile', 'backend.mk', '.deps/.mkdir.done'):
+             build_files.add_optional_exists(p)
+ 
+-        for idl in manager.idls.values():
++        for idl in list(manager.idls.values()):
+             self._install_manifests['dist_idl'].add_symlink(idl['source'],
+                 idl['basename'])
+             self._install_manifests['dist_include'].add_optional_exists('%s.h'
+@@ -992,7 +993,7 @@
+ 
+         interfaces_manifests = []
+         dist_dir = mozpath.join(self.environment.topobjdir, 'dist')
+-        for manifest, entries in manager.interface_manifests.items():
++        for manifest, entries in list(manager.interface_manifests.items()):
+             interfaces_manifests.append(mozpath.join('$(DEPTH)', manifest))
+             for xpt in sorted(entries):
+                 registered_xpt_files.add(mozpath.join(
+@@ -1052,7 +1053,7 @@
+         # Don't allow files to be defined multiple times unless it is allowed.
+         # We currently allow duplicates for non-test files or test files if
+         # the manifest is listed as a duplicate.
+-        for source, (dest, is_test) in obj.installs.items():
++        for source, (dest, is_test) in list(obj.installs.items()):
+             try:
+                 self._install_manifests['_test_files'].add_symlink(source, dest)
+             except ValueError:
+@@ -1386,7 +1387,7 @@
+         man_dir = mozpath.join(self.environment.topobjdir, '_build_manifests',
+             dest)
+ 
+-        for k, manifest in manifests.items():
++        for k, manifest in list(manifests.items()):
+             with self._write_file(mozpath.join(man_dir, k)) as fh:
+                 manifest.write(fileobj=fh)
+ 
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/backend/tup.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/backend/tup.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import absolute_import, unicode_literals
++
+ 
+ import os
+ 
+@@ -253,7 +253,7 @@
+         backend_file = self._get_backend_file('xpcom/xpidl')
+         backend_file.export_shell()
+ 
+-        for module, data in sorted(manager.modules.iteritems()):
++        for module, data in sorted(manager.modules.items()):
+             dest, idls = data
+             cmd = [
+                 '$(PYTHON_PATH)',
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/backend/visualstudio.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/backend/visualstudio.py	(refactored)
+@@ -5,7 +5,7 @@
+ # This file contains a build backend for generating Visual Studio project
+ # files.
+ 
+-from __future__ import absolute_import, unicode_literals
++
+ 
+ import errno
+ import os
+@@ -218,7 +218,7 @@
+             includes = [os.path.normpath(i) for i in includes]
+ 
+             defines = []
+-            for k, v in self._paths_to_defines.get(path, {}).items():
++            for k, v in list(self._paths_to_defines.get(path, {}).items()):
+                 if v is True:
+                     defines.append(k)
+                 else:
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/codecoverage/chrome_map.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/codecoverage/chrome_map.py	(refactored)
+@@ -5,7 +5,7 @@
+ from collections import defaultdict
+ import json
+ import os
+-import urlparse
++import urllib.parse
+ 
+ from mach.config import ConfigSettings
+ from mach.logging import LoggingManager
+@@ -42,7 +42,7 @@
+         if isinstance(entry, (ManifestChrome, ManifestResource)):
+             if isinstance(entry, ManifestResource):
+                 dest = entry.target
+-                url = urlparse.urlparse(dest)
++                url = urllib.parse.urlparse(dest)
+                 if not url.scheme:
+                     dest = mozpath.normpath(mozpath.join(entry.base, dest))
+                 if url.scheme == 'file':
+@@ -99,7 +99,7 @@
+             chrome_mapping = self.manifest_handler.chrome_mapping
+             overrides = self.manifest_handler.overrides
+             json.dump([
+-                {k: list(v) for k, v in chrome_mapping.iteritems()},
++                {k: list(v) for k, v in chrome_mapping.items()},
+                 overrides,
+                 self._install_mapping,
+             ], fh, sort_keys=True, indent=2)
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/codecoverage/packager.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/codecoverage/packager.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import absolute_import, print_function
++
+ 
+ import argparse
+ import sys
+@@ -12,7 +12,7 @@
+ 
+ def package_gcno_tree(root, output_file):
+     # XXX JarWriter doesn't support unicode strings, see bug 1056859
+-    if isinstance(root, unicode):
++    if isinstance(root, str):
+         root = root.encode('utf-8')
+ 
+     finder = FileFinder(root)
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/compilation/codecomplete.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/compilation/codecomplete.py	(refactored)
+@@ -4,7 +4,7 @@
+ 
+ # This modules provides functionality for dealing with code completion.
+ 
+-from __future__ import absolute_import
++
+ 
+ import os
+ 
+@@ -59,5 +59,5 @@
+         # Drop the first flag since that is the pathname of the compiler.
+         flags = (shell_split(build_vars[cc]) + shell_split(build_vars[name]))[1:]
+ 
+-        print(' '.join(shell_quote(arg)
+-                       for arg in util.sanitize_cflags(flags)))
++        print((' '.join(shell_quote(arg)
++                       for arg in util.sanitize_cflags(flags))))
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/compilation/database.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/compilation/database.py	(refactored)
+@@ -118,7 +118,7 @@
+ 
+         db = []
+ 
+-        for (directory, filename), cmd in self._db.iteritems():
++        for (directory, filename), cmd in self._db.items():
+             env = self._envs[directory]
+             cmd = list(cmd)
+             cmd.append(filename)
+@@ -151,7 +151,7 @@
+                 a = expand_variables(a, variables).split()
+                 if not a:
+                     continue
+-                if isinstance(a, types.StringTypes):
++                if isinstance(a, (str,)):
+                     c.append(a)
+                 else:
+                     c.extend(a)
+@@ -216,7 +216,7 @@
+             value = cenv.substs.get(name)
+             if not value:
+                 return
+-            if isinstance(value, types.StringTypes):
++            if isinstance(value, (str,)):
+                 value = value.split()
+             db.extend(value)
+ 
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/compilation/warnings.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/compilation/warnings.py	(refactored)
+@@ -4,7 +4,7 @@
+ 
+ # This modules provides functionality for dealing with compiler warnings.
+ 
+-from __future__ import absolute_import, unicode_literals
++
+ 
+ import errno
+ import json
+@@ -123,18 +123,18 @@
+ 
+     def __len__(self):
+         i = 0
+-        for value in self._files.values():
++        for value in list(self._files.values()):
+             i += len(value['warnings'])
+ 
+         return i
+ 
+     def __iter__(self):
+-        for value in self._files.values():
++        for value in list(self._files.values()):
+             for warning in value['warnings']:
+                 yield warning
+ 
+     def __contains__(self, item):
+-        for value in self._files.values():
++        for value in list(self._files.values()):
+             for warning in value['warnings']:
+                 if warning == item:
+                     return True
+@@ -144,7 +144,7 @@
+     @property
+     def warnings(self):
+         """All the CompilerWarning instances in this database."""
+-        for value in self._files.values():
++        for value in list(self._files.values()):
+             for w in value['warnings']:
+                 yield w
+ 
+@@ -152,7 +152,7 @@
+         """Returns a mapping of warning types to their counts."""
+ 
+         types = {}
+-        for value in self._files.values():
++        for value in list(self._files.values()):
+             for warning in value['warnings']:
+                 if dirpath and not mozpath.normsep(warning['filename']).startswith(dirpath):
+                     continue
+@@ -210,7 +210,7 @@
+         """
+ 
+         # Need to calculate up front since we are mutating original object.
+-        filenames = self._files.keys()
++        filenames = list(self._files.keys())
+         for filename in filenames:
+             if not os.path.exists(filename):
+                 del self._files[filename]
+@@ -229,10 +229,10 @@
+         obj = {'files': {}}
+ 
+         # All this hackery because JSON can't handle sets.
+-        for k, v in self._files.iteritems():
++        for k, v in self._files.items():
+             obj['files'][k] = {}
+ 
+-            for k2, v2 in v.iteritems():
++            for k2, v2 in v.items():
+                 normalized = v2
+ 
+                 if k2 == 'warnings':
+@@ -249,8 +249,8 @@
+         self._files = obj['files']
+ 
+         # Normalize data types.
+-        for filename, value in self._files.iteritems():
+-            for k, v in value.iteritems():
++        for filename, value in self._files.items():
++            for k, v in value.items():
+                 if k != 'warnings':
+                     continue
+ 
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/configure/__init__.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/configure/__init__.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import absolute_import, print_function, unicode_literals
++
+ 
+ import inspect
+ import logging
+@@ -193,7 +193,7 @@
+         for b in ('None', 'False', 'True', 'int', 'bool', 'any', 'all', 'len',
+                   'list', 'tuple', 'set', 'dict', 'isinstance', 'getattr',
+                   'hasattr', 'enumerate', 'range', 'zip')
+-    }, __import__=forbidden_import, str=unicode)
++    }, __import__=forbidden_import, str=str)
+ 
+     # Expose a limited set of functions from os.path
+     OS = ReadOnlyNamespace(path=ReadOnlyNamespace(**{
+@@ -333,7 +333,7 @@
+         if path:
+             self.include_file(path)
+ 
+-        for option in self._options.itervalues():
++        for option in self._options.values():
+             # All options must be referenced by some @depends function
+             if option not in self._seen:
+                 raise ConfigureError(
+@@ -437,7 +437,7 @@
+                     value = PositiveOptionValue()
+                 elif value is False or value == ():
+                     value = NegativeOptionValue()
+-                elif isinstance(value, types.StringTypes):
++                elif isinstance(value, (str,)):
+                     value = PositiveOptionValue((value,))
+                 elif isinstance(value, tuple):
+                     value = PositiveOptionValue(value)
+@@ -477,7 +477,7 @@
+         return value
+ 
+     def _dependency(self, arg, callee_name, arg_name=None):
+-        if isinstance(arg, types.StringTypes):
++        if isinstance(arg, (str,)):
+             prefix, name, values = Option.split_option(arg)
+             if values != ():
+                 raise ConfigureError("Option must not contain an '='")
+@@ -541,7 +541,7 @@
+         '''
+         when = self._normalize_when(kwargs.get('when'), 'option')
+         args = [self._resolve(arg) for arg in args]
+-        kwargs = {k: self._resolve(v) for k, v in kwargs.iteritems()
++        kwargs = {k: self._resolve(v) for k, v in kwargs.items()
+                                       if k != 'when'}
+         option = Option(*args, **kwargs)
+         if when:
+@@ -622,7 +622,7 @@
+         with self.only_when_impl(when):
+             what = self._resolve(what)
+             if what:
+-                if not isinstance(what, types.StringTypes):
++                if not isinstance(what, (str,)):
+                     raise TypeError("Unexpected type: '%s'" % type(what).__name__)
+                 self.include_file(what)
+ 
+@@ -640,7 +640,7 @@
+             (k[:-len('_impl')], getattr(self, k))
+             for k in dir(self) if k.endswith('_impl') and k != 'template_impl'
+         )
+-        glob.update((k, v) for k, v in self.iteritems() if k not in glob)
++        glob.update((k, v) for k, v in self.items() if k not in glob)
+ 
+         # Any function argument to the template must be prepared to be sandboxed.
+         # If the template itself returns a function (in which case, it's very
+@@ -664,7 +664,7 @@
+             def wrapper(*args, **kwargs):
+                 args = [maybe_prepare_function(arg) for arg in args]
+                 kwargs = {k: maybe_prepare_function(v)
+-                          for k, v in kwargs.iteritems()}
++                          for k, v in kwargs.items()}
+                 ret = template(*args, **kwargs)
+                 if isfunction(ret):
+                     # We can't expect the sandboxed code to think about all the
+@@ -696,7 +696,7 @@
+         for value, required in (
+                 (_import, True), (_from, False), (_as, False)):
+ 
+-            if not isinstance(value, types.StringTypes) and (
++            if not isinstance(value, (str,)) and (
+                     required or value is not None):
+                 raise TypeError("Unexpected type: '%s'" % type(value).__name__)
+             if value is not None and not self.RE_MODULE.match(value):
+@@ -759,7 +759,7 @@
+         name = self._resolve(name, need_help_dependency=False)
+         if name is None:
+             return
+-        if not isinstance(name, types.StringTypes):
++        if not isinstance(name, (str,)):
+             raise TypeError("Unexpected type: '%s'" % type(name).__name__)
+         if name in data:
+             raise ConfigureError(
+@@ -849,7 +849,7 @@
+                 if isinstance(possible_reasons[0], Option):
+                     reason = possible_reasons[0]
+         if not reason and (isinstance(value, (bool, tuple)) or
+-                           isinstance(value, types.StringTypes)):
++                           isinstance(value, (str,))):
+             # A reason can be provided automatically when imply_option
+             # is called with an immediate value.
+             _, filename, line, _, _, _ = inspect.stack()[1]
+@@ -884,10 +884,10 @@
+         if not inspect.isfunction(func):
+             raise TypeError("Unexpected type: '%s'" % type(func).__name__)
+         if func in self._prepared_functions:
+-            return func, func.func_globals
++            return func, func.__globals__
+ 
+         glob = SandboxedGlobal(
+-            (k, v) for k, v in func.func_globals.iteritems()
++            (k, v) for k, v in func.__globals__.items()
+             if (inspect.isfunction(v) and v not in self._templates) or (
+                 inspect.isclass(v) and issubclass(v, Exception))
+         )
+@@ -908,20 +908,20 @@
+         # Note this is not entirely bullet proof (if the value is e.g. a list,
+         # the list contents could have changed), but covers the bases.
+         closure = None
+-        if func.func_closure:
++        if func.__closure__:
+             def makecell(content):
+                 def f():
+                     content
+-                return f.func_closure[0]
++                return f.__closure__[0]
+ 
+             closure = tuple(makecell(cell.cell_contents)
+-                            for cell in func.func_closure)
++                            for cell in func.__closure__)
+ 
+         new_func = wraps(func)(types.FunctionType(
+-            func.func_code,
++            func.__code__,
+             glob,
+             func.__name__,
+-            func.func_defaults,
++            func.__defaults__,
+             closure
+         ))
+         @wraps(new_func)
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/configure/check_debug_ranges.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/configure/check_debug_ranges.py	(refactored)
+@@ -6,7 +6,7 @@
+ # to a given compilation unit. This is used as a helper to find a bug in some
+ # versions of GNU ld.
+ 
+-from __future__ import absolute_import
++
+ 
+ import subprocess
+ import sys
+@@ -59,4 +59,4 @@
+ 
+ 
+ if __name__ == '__main__':
+-    print main(*sys.argv[1:])
++    print(main(*sys.argv[1:]))
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/configure/constants.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/configure/constants.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import absolute_import, print_function, unicode_literals
++
+ 
+ from mozbuild.util import EnumString
+ from collections import OrderedDict
+@@ -56,7 +56,7 @@
+     'x86_64': 64,
+ }
+ 
+-CPU = EnumString.subclass(*CPU_bitness.keys())
++CPU = EnumString.subclass(*list(CPU_bitness.keys()))
+ 
+ Endianness = EnumString.subclass(
+     'big',
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/configure/help.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/configure/help.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import absolute_import, print_function, unicode_literals
++
+ 
+ import os
+ from mozbuild.configure.options import Option
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/configure/libstdcxx.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/configure/libstdcxx.py	(refactored)
+@@ -13,7 +13,7 @@
+ # will be used from shell, we just print the two assignments and evaluate
+ # them from shell.
+ 
+-from __future__ import absolute_import
++
+ 
+ import os
+ import subprocess
+@@ -76,6 +76,6 @@
+ 
+ if __name__ == '__main__':
+     cxx_env = os.environ['CXX']
+-    print 'MOZ_LIBSTDCXX_TARGET_VERSION=%s' % find_version(cxx_env)
++    print('MOZ_LIBSTDCXX_TARGET_VERSION=%s' % find_version(cxx_env))
+     host_cxx_env = os.environ.get('HOST_CXX', cxx_env)
+-    print 'MOZ_LIBSTDCXX_HOST_VERSION=%s' % find_version(host_cxx_env)
++    print('MOZ_LIBSTDCXX_HOST_VERSION=%s' % find_version(host_cxx_env))
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/configure/lint.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/configure/lint.py	(refactored)
+@@ -2,9 +2,9 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import absolute_import, print_function, unicode_literals
+ 
+-from StringIO import StringIO
++
++from io import StringIO
+ from . import (
+     CombinedDependsFunction,
+     ConfigureError,
+@@ -42,7 +42,7 @@
+             # - don't use @imports
+             # - don't have a closure
+             # - don't use global variables
+-            if func in self._imports or func.func_closure:
++            if func in self._imports or func.__closure__:
+                 return True
+             for op, arg in disassemble_as_iter(func):
+                 if op in ('LOAD_GLOBAL', 'STORE_GLOBAL'):
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/configure/lint_util.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/configure/lint_util.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import absolute_import, print_function, unicode_literals
++
+ 
+ import dis
+ import inspect
+@@ -12,9 +12,9 @@
+ # returns an iterator.
+ def disassemble_as_iter(co):
+     if inspect.ismethod(co):
+-        co = co.im_func
++        co = co.__func__
+     if inspect.isfunction(co):
+-        co = co.func_code
++        co = co.__code__
+     code = co.co_code
+     n = len(code)
+     i = 0
+@@ -30,7 +30,7 @@
+             extended_arg = 0
+             i += 2
+             if op == dis.EXTENDED_ARG:
+-                extended_arg = arg * 65536L
++                extended_arg = arg * 65536
+                 continue
+             if op in dis.hasconst:
+                 yield opname, co.co_consts[arg]
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/configure/options.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/configure/options.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import absolute_import, print_function, unicode_literals
++
+ 
+ import os
+ import sys
+@@ -12,7 +12,7 @@
+ 
+ def istupleofstrings(obj):
+     return isinstance(obj, tuple) and len(obj) and all(
+-        isinstance(o, types.StringTypes) for o in obj)
++        isinstance(o, (str,)) for o in obj)
+ 
+ 
+ class OptionValue(tuple):
+@@ -71,7 +71,7 @@
+     in the form of a tuple for when values are given to the option (in the form
+     --option=value[,value2...].
+     '''
+-    def __nonzero__(self):
++    def __bool__(self):
+         return True
+ 
+ 
+@@ -96,7 +96,7 @@
+         if format_data:
+             message = message.format(**format_data)
+         super(ConflictingOptionError, self).__init__(message)
+-        for k, v in format_data.iteritems():
++        for k, v in format_data.items():
+             setattr(self, k, v)
+ 
+ 
+@@ -132,7 +132,7 @@
+                 'At least an option name or an environment variable name must '
+                 'be given')
+         if name:
+-            if not isinstance(name, types.StringTypes):
++            if not isinstance(name, (str,)):
+                 raise InvalidOptionError('Option must be a string')
+             if not name.startswith('--'):
+                 raise InvalidOptionError('Option must start with `--`')
+@@ -141,7 +141,7 @@
+             if not name.islower():
+                 raise InvalidOptionError('Option must be all lowercase')
+         if env:
+-            if not isinstance(env, types.StringTypes):
++            if not isinstance(env, (str,)):
+                 raise InvalidOptionError(
+                     'Environment variable name must be a string')
+             if not env.isupper():
+@@ -151,8 +151,8 @@
+                 isinstance(nargs, int) and nargs >= 0):
+             raise InvalidOptionError(
+                 "nargs must be a positive integer, '?', '*' or '+'")
+-        if (not isinstance(default, types.StringTypes) and
+-                not isinstance(default, (bool, types.NoneType)) and
++        if (not isinstance(default, (str,)) and
++                not isinstance(default, (bool, type(None))) and
+                 not istupleofstrings(default)):
+             raise InvalidOptionError(
+                 'default must be a bool, a string or a tuple of strings')
+@@ -224,7 +224,7 @@
+                     ', '.join("'%s'" % c for c in choices))
+         elif has_choices:
+             maxargs = self.maxargs
+-            if len(choices) < maxargs and maxargs != sys.maxint:
++            if len(choices) < maxargs and maxargs != sys.maxsize:
+                 raise InvalidOptionError('Not enough `choices` for `nargs`')
+         self.choices = choices
+         self.help = help
+@@ -238,7 +238,7 @@
+         where prefix is one of 'with', 'without', 'enable' or 'disable'.
+         The '=values' part is optional. Values are separated with commas.
+         '''
+-        if not isinstance(option, types.StringTypes):
++        if not isinstance(option, (str,)):
+             raise InvalidOptionError('Option must be a string')
+ 
+         elements = option.split('=', 1)
+@@ -291,7 +291,7 @@
+     def maxargs(self):
+         if isinstance(self.nargs, int):
+             return self.nargs
+-        return 1 if self.nargs == '?' else sys.maxint
++        return 1 if self.nargs == '?' else sys.maxsize
+ 
+     def _validate_nargs(self, num):
+         minargs, maxargs = self.minargs, self.maxargs
+@@ -485,5 +485,5 @@
+ 
+     def __iter__(self):
+         for d in (self._args, self._extra_args):
+-            for arg, pos in d.itervalues():
++            for arg, pos in d.values():
+                 yield arg
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/configure/util.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/configure/util.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import absolute_import, print_function, unicode_literals
++
+ 
+ import codecs
+ import itertools
+@@ -52,7 +52,7 @@
+ 
+     def __cmp__(self, other):
+         # LooseVersion checks isinstance(StringType), so work around it.
+-        if isinstance(other, unicode):
++        if isinstance(other, str):
+             other = other.encode('ascii')
+         return LooseVersion.__cmp__(self, other)
+ 
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/controller/building.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/controller/building.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import absolute_import, unicode_literals
++
+ 
+ import getpass
+ import json
+@@ -108,7 +108,7 @@
+         """
+         o = []
+ 
+-        for tier, state in self.tiers.items():
++        for tier, state in list(self.tiers.items()):
+             t_entry = dict(
+                 name=tier,
+                 start=state['begin_time'],
+@@ -643,8 +643,8 @@
+ 
+         return '\n'.join(lines)
+ 
+-    def __nonzero__(self):
+-        relative_values = [v for k, v in self._values.items()
++    def __bool__(self):
++        relative_values = [v for k, v in list(self._values.items())
+                            if k not in self.ABSOLUTE_KEYS]
+         return (all(v >= 0 for v in relative_values) and
+                 any(v > 0 for v in relative_values))
+@@ -666,8 +666,8 @@
+         """Install test files."""
+ 
+         if self.is_clobber_needed():
+-            print(INSTALL_TESTS_CLOBBER.format(
+-                  clobber_file=os.path.join(self.topobjdir, 'CLOBBER')))
++            print((INSTALL_TESTS_CLOBBER.format(
++                  clobber_file=os.path.join(self.topobjdir, 'CLOBBER'))))
+             sys.exit(1)
+ 
+         if not test_objs:
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/controller/clobber.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/controller/clobber.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import absolute_import, print_function
++
+ 
+ r'''This module contains code for managing clobbering of the tree.'''
+ 
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/frontend/context.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/frontend/context.py	(refactored)
+@@ -14,7 +14,7 @@
+ contain, you've come to the right place.
+ """
+ 
+-from __future__ import absolute_import, unicode_literals
++
+ 
+ import os
+ 
+@@ -234,15 +234,15 @@
+         This function is transactional: if setitem fails for one of the values,
+         the context is not updated at all."""
+         if isinstance(iterable, dict):
+-            iterable = iterable.items()
++            iterable = list(iterable.items())
+ 
+         update = {}
+-        for key, value in itertools.chain(iterable, kwargs.items()):
++        for key, value in itertools.chain(iterable, list(kwargs.items())):
+             stored_type = self._validate(key, value)
+             # Don't create an instance of stored_type if coercion is needed,
+             # until all values are validated.
+             update[key] = (value, stored_type)
+-        for key, (value, stored_type) in update.items():
++        for key, (value, stored_type) in list(update.items()):
+             if not isinstance(value, stored_type):
+                 update[key] = stored_type(value)
+             else:
+@@ -297,7 +297,7 @@
+             self.update(value)
+ 
+ 
+-class FinalTargetValue(ContextDerivedValue, unicode):
++class FinalTargetValue(ContextDerivedValue, str):
+     def __new__(cls, context, value=""):
+         if not value:
+             value = 'dist/'
+@@ -307,7 +307,7 @@
+                 value += 'bin'
+             if context['DIST_SUBDIR']:
+                 value += '/' + context['DIST_SUBDIR']
+-        return unicode.__new__(cls, value)
++        return str.__new__(cls, value)
+ 
+ 
+ def Enum(*values):
+@@ -355,7 +355,7 @@
+                 cls = SourcePath
+         return super(PathMeta, cls).__call__(context, value)
+ 
+-class Path(ContextDerivedValue, unicode):
++class Path(ContextDerivedValue, str, metaclass=PathMeta):
+     """Stores and resolves a source path relative to a given context
+ 
+     This class is used as a backing type for some of the sandbox variables.
+@@ -366,7 +366,6 @@
+       - '!objdir/relative/paths'
+       - '%/filesystem/absolute/paths'
+     """
+-    __metaclass__ = PathMeta
+ 
+     def __new__(cls, context, value=None):
+         return super(Path, cls).__new__(cls, value)
+@@ -386,7 +385,7 @@
+     def __cmp__(self, other):
+         if isinstance(other, Path) and self.srcdir != other.srcdir:
+             return cmp(self.full_path, other.full_path)
+-        return cmp(unicode(self), other)
++        return cmp(str(self), other)
+ 
+     # __cmp__ is not enough because unicode has __eq__, __ne__, etc. defined
+     # and __cmp__ is only used for those when they don't exist.
+@@ -544,7 +543,7 @@
+         __slots__ = tuple([name for name, _ in fields])
+ 
+         def __init__(self, context):
+-            for fname, ftype in self._fields.items():
++            for fname, ftype in list(self._fields.items()):
+                 if issubclass(ftype, ContextDerivedValue):
+                     setattr(self, fname, self._fields[fname](context))
+                 else:
+@@ -614,8 +613,8 @@
+     return _TypedListWithAction
+ 
+ WebPlatformTestManifest = TypedNamedTuple("WebPlatformTestManifest",
+-                                          [("manifest_path", unicode),
+-                                           ("test_root", unicode)])
++                                          [("manifest_path", str),
++                                           ("test_root", str)])
+ ManifestparserManifestList = OrderedListWithAction(read_manifestparser_manifest)
+ ReftestManifestList = OrderedListWithAction(read_reftest_manifest)
+ WptManifestList = TypedListWithAction(WebPlatformTestManifest, read_wpt_manifest)
+@@ -623,12 +622,12 @@
+ OrderedSourceList = ContextDerivedTypedList(SourcePath, StrictOrderingOnAppendList)
+ OrderedTestFlavorList = TypedList(Enum(*all_test_flavors()),
+                                   StrictOrderingOnAppendList)
+-OrderedStringList = TypedList(unicode, StrictOrderingOnAppendList)
++OrderedStringList = TypedList(str, StrictOrderingOnAppendList)
+ DependentTestsEntry = ContextDerivedTypedRecord(('files', OrderedSourceList),
+                                                 ('tags', OrderedStringList),
+                                                 ('flavors', OrderedTestFlavorList))
+ BugzillaComponent = TypedNamedTuple('BugzillaComponent',
+-                        [('product', unicode), ('component', unicode)])
++                        [('product', str), ('component', str)])
+ 
+ 
+ class Files(SubContext):
+@@ -764,7 +763,7 @@
+         self.test_tags |= other.test_tags
+         self.test_flavors |= other.test_flavors
+ 
+-        for k, v in other.items():
++        for k, v in list(other.items()):
+             if k == 'IMPACTED_TESTS':
+                 self.test_files |= set(mozpath.relpath(e.full_path, e.context.config.topsrcdir)
+                                        for e in v.files)
+@@ -818,7 +817,7 @@
+ 
+         bug_components = Counter()
+ 
+-        for f in files.values():
++        for f in list(files.values()):
+             bug_component = f.get('BUG_COMPONENT')
+             if bug_component:
+                 bug_components[bug_component] += 1
+@@ -889,11 +888,11 @@
+         file.
+         """),
+ 
+-    'ANDROID_APK_NAME': (unicode, unicode,
++    'ANDROID_APK_NAME': (str, str,
+         """The name of an Android APK file to generate.
+         """),
+ 
+-    'ANDROID_APK_PACKAGE': (unicode, unicode,
++    'ANDROID_APK_PACKAGE': (str, str,
+         """The name of the Android package to generate R.java for, like org.mozilla.gecko.
+         """),
+ 
+@@ -962,7 +961,7 @@
+         """),
+ 
+     'GENERATED_FILES': (StrictOrderingOnAppendListWithFlagsFactory({
+-                'script': unicode,
++                'script': str,
+                 'inputs': list }), list,
+         """Generic generated files.
+ 
+@@ -1107,7 +1106,7 @@
+         """Like ``OBJDIR_FILES``, with preprocessing. Use sparingly.
+         """),
+ 
+-    'FINAL_LIBRARY': (unicode, unicode,
++    'FINAL_LIBRARY': (str, str,
+         """Library in which the objects of the current directory will be linked.
+ 
+         This variable contains the name of a library, defined elsewhere with
+@@ -1158,7 +1157,7 @@
+         """A list of python unit tests.
+         """),
+ 
+-    'HOST_LIBRARY_NAME': (unicode, unicode,
++    'HOST_LIBRARY_NAME': (str, str,
+         """Name of target library generated when cross compiling.
+         """),
+ 
+@@ -1176,7 +1175,7 @@
+         libraries that link into this library via FINAL_LIBRARY.
+         """),
+ 
+-    'LIBRARY_NAME': (unicode, unicode,
++    'LIBRARY_NAME': (str, str,
+         """The code name of the library generated for a directory.
+ 
+         By default STATIC_LIBRARY_NAME and SHARED_LIBRARY_NAME take this name.
+@@ -1188,7 +1187,7 @@
+         ``example/components/xpcomsample.lib`` on Windows.
+         """),
+ 
+-    'SHARED_LIBRARY_NAME': (unicode, unicode,
++    'SHARED_LIBRARY_NAME': (str, str,
+         """The name of the static library generated for a directory, if it needs to
+         differ from the library code name.
+ 
+@@ -1202,7 +1201,7 @@
+         Implies FORCE_SHARED_LIB.
+         """),
+ 
+-    'STATIC_LIBRARY_NAME': (unicode, unicode,
++    'STATIC_LIBRARY_NAME': (str, str,
+         """The name of the static library generated for a directory, if it needs to
+         differ from the library code name.
+ 
+@@ -1238,37 +1237,37 @@
+ 
+         This variable contains a list of system libaries to link against.
+         """),
+-    'RCFILE': (unicode, unicode,
++    'RCFILE': (str, str,
+         """The program .rc file.
+ 
+         This variable can only be used on Windows.
+         """),
+ 
+-    'RESFILE': (unicode, unicode,
++    'RESFILE': (str, str,
+         """The program .res file.
+ 
+         This variable can only be used on Windows.
+         """),
+ 
+-    'RCINCLUDE': (unicode, unicode,
++    'RCINCLUDE': (str, str,
+         """The resource script file to be included in the default .res file.
+ 
+         This variable can only be used on Windows.
+         """),
+ 
+-    'DEFFILE': (unicode, unicode,
++    'DEFFILE': (str, str,
+         """The program .def (module definition) file.
+ 
+         This variable can only be used on Windows.
+         """),
+ 
+-    'LD_VERSION_SCRIPT': (unicode, unicode,
++    'LD_VERSION_SCRIPT': (str, str,
+         """The linker version script for shared libraries.
+ 
+         This variable can only be used on Linux.
+         """),
+ 
+-    'SYMBOLS_FILE': (Path, unicode,
++    'SYMBOLS_FILE': (Path, str,
+         """A file containing a list of symbols to export from a shared library.
+ 
+         The given file contains a list of symbols to be exported, and is
+@@ -1323,7 +1322,7 @@
+         ``BIN_SUFFIX``, the name will remain unchanged.
+         """),
+ 
+-    'SONAME': (unicode, unicode,
++    'SONAME': (str, str,
+         """The soname of the shared object currently being linked
+ 
+         soname is the "logical name" of a shared object, often used to provide
+@@ -1379,7 +1378,7 @@
+         ``GENERATED_FILES``.
+         """),
+ 
+-    'PROGRAM' : (unicode, unicode,
++    'PROGRAM' : (str, str,
+         """Compiled executable name.
+ 
+         If the configuration token ``BIN_SUFFIX`` is set, its value will be
+@@ -1387,7 +1386,7 @@
+         ``BIN_SUFFIX``, ``PROGRAM`` will remain unchanged.
+         """),
+ 
+-    'HOST_PROGRAM' : (unicode, unicode,
++    'HOST_PROGRAM' : (str, str,
+         """Compiled host executable name.
+ 
+         If the configuration token ``HOST_BIN_SUFFIX`` is set, its value will be
+@@ -1425,7 +1424,7 @@
+         files.
+         """),
+ 
+-    'XPIDL_MODULE': (unicode, unicode,
++    'XPIDL_MODULE': (str, str,
+         """XPCOM Interface Definition Module Name.
+ 
+         This is the name of the ``.xpt`` file that is created by linking
+@@ -1576,14 +1575,14 @@
+         """),
+ 
+     # The following variables are used to control the target of installed files.
+-    'XPI_NAME': (unicode, unicode,
++    'XPI_NAME': (str, str,
+         """The name of an extension XPI to generate.
+ 
+         When this variable is present, the results of this directory will end up
+         being packaged into an extension instead of the main dist/bin results.
+         """),
+ 
+-    'DIST_SUBDIR': (unicode, unicode,
++    'DIST_SUBDIR': (str, str,
+         """The name of an alternate directory to install files to.
+ 
+         When this variable is present, the results of this directory will end up
+@@ -1591,7 +1590,7 @@
+         otherwise be placed.
+         """),
+ 
+-    'FINAL_TARGET': (FinalTargetValue, unicode,
++    'FINAL_TARGET': (FinalTargetValue, str,
+         """The name of the directory to install targets to.
+ 
+         The directory is relative to the top of the object directory. The
+@@ -1622,7 +1621,7 @@
+ 
+     'GYP_DIRS': (StrictOrderingOnAppendListWithFlagsFactory({
+             'variables': dict,
+-            'input': unicode,
++            'input': str,
+             'sandbox_vars': dict,
+             'non_unified_sources': StrictOrderingOnAppendList,
+         }), list,
+@@ -1799,7 +1798,7 @@
+ }
+ 
+ # Sanity check: we don't want any variable above to have a list as storage type.
+-for name, (storage_type, input_types, docs) in VARIABLES.items():
++for name, (storage_type, input_types, docs) in list(VARIABLES.items()):
+     if storage_type == list:
+         raise RuntimeError('%s has a "list" storage type. Use "List" instead.'
+             % name)
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/frontend/data.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/frontend/data.py	(refactored)
+@@ -15,7 +15,7 @@
+ structures.
+ """
+ 
+-from __future__ import absolute_import, unicode_literals
++
+ 
+ from mozbuild.util import StrictOrderingOnAppendList
+ from mozpack.chrome.manifest import ManifestEntry
+@@ -186,7 +186,7 @@
+         self.defines = defines
+ 
+     def get_defines(self):
+-        for define, value in self.defines.iteritems():
++        for define, value in self.defines.items():
+             if value is True:
+                 yield('-D%s' % define)
+             elif value is False:
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/frontend/emitter.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/frontend/emitter.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import absolute_import, unicode_literals
++
+ 
+ import itertools
+ import logging
+@@ -120,8 +120,8 @@
+         # arguments. This gross hack works around the problem until we
+         # rid ourselves of 2.6.
+         self.info = {}
+-        for k, v in mozinfo.info.items():
+-            if isinstance(k, unicode):
++        for k, v in list(mozinfo.info.items()):
++            if isinstance(k, str):
+                 k = k.encode('ascii')
+             self.info[k] = v
+ 
+@@ -197,7 +197,7 @@
+ 
+     def _emit_libs_derived(self, contexts):
+         # First do FINAL_LIBRARY linkage.
+-        for lib in (l for libs in self._libs.values() for l in libs):
++        for lib in (l for libs in list(self._libs.values()) for l in libs):
+             if not isinstance(lib, (StaticLibrary, RustLibrary)) or not lib.link_into:
+                 continue
+             if lib.link_into not in self._libs:
+@@ -257,12 +257,12 @@
+                         lib.link_into == outerlib.basename):
+                     propagate_defines(lib, defines)
+ 
+-        for lib in (l for libs in self._libs.values() for l in libs):
++        for lib in (l for libs in list(self._libs.values()) for l in libs):
+             if isinstance(lib, Library):
+                 propagate_defines(lib, lib.lib_defines)
+             yield lib
+ 
+-        for obj in self._binaries.values():
++        for obj in list(self._binaries.values()):
+             yield obj
+ 
+     LIBRARY_NAME_VAR = {
+@@ -321,7 +321,7 @@
+                             libs[key] = l
+                         if key not in libs:
+                             libs[key] = l
+-                candidates = libs.values()
++                candidates = list(libs.values())
+                 if force_static and not candidates:
+                     if dir:
+                         raise SandboxValidationError(
+@@ -383,9 +383,9 @@
+ 
+     def _verify_deps(self, context, crate_dir, crate_name, dependencies, description='Dependency'):
+         """Verify that a crate's dependencies all specify local paths."""
+-        for dep_crate_name, values in dependencies.iteritems():
++        for dep_crate_name, values in dependencies.items():
+             # A simple version number.
+-            if isinstance(values, (str, unicode)):
++            if isinstance(values, str):
+                 raise SandboxValidationError(
+                     '%s %s of crate %s does not list a path' % (description, dep_crate_name, crate_name),
+                     context)
+@@ -463,7 +463,7 @@
+                      ' in [profile.%s] section') % (libname, profile_name),
+                     context)
+ 
+-        dependencies = set(config.get('dependencies', {}).iterkeys())
++        dependencies = set(config.get('dependencies', {}).keys())
+ 
+         return RustLibrary(context, libname, cargo_file, crate_type,
+                            dependencies, **static_args)
+@@ -738,7 +738,7 @@
+         assert not gen_sources['UNIFIED_SOURCES']
+ 
+         no_pgo = context.get('NO_PGO')
+-        no_pgo_sources = [f for f, flags in all_flags.iteritems()
++        no_pgo_sources = [f for f, flags in all_flags.items()
+                           if flags.no_pgo]
+         if no_pgo:
+             if no_pgo_sources:
+@@ -765,7 +765,7 @@
+ 
+         # The inverse of the above, mapping suffixes to their canonical suffix.
+         canonicalized_suffix_map = {}
+-        for suffix, alternatives in suffix_map.iteritems():
++        for suffix, alternatives in suffix_map.items():
+             alternatives.add(suffix)
+             for a in alternatives:
+                 canonicalized_suffix_map[a] = suffix
+@@ -786,7 +786,7 @@
+         # a directory with mixed C and C++ source, but it's not that important.
+         cxx_sources = defaultdict(bool)
+ 
+-        for variable, (klass, gen_klass, suffixes) in varmap.items():
++        for variable, (klass, gen_klass, suffixes) in list(varmap.items()):
+             allowed_suffixes = set().union(*[suffix_map[s] for s in suffixes])
+ 
+             # First ensure that we haven't been given filetypes that we don't
+@@ -812,7 +812,7 @@
+                     obj = cls(*arglist)
+                     yield obj
+ 
+-        for f, flags in all_flags.iteritems():
++        for f, flags in all_flags.items():
+             if flags.flags:
+                 ext = mozpath.splitext(f)[1]
+                 yield PerSourceFlag(context, f, flags.flags)
+@@ -958,7 +958,7 @@
+         for obj in self._handle_linkables(context, passthru, generated_files):
+             yield obj
+ 
+-        generated_files.update(['%s%s' % (k, self.config.substs.get('BIN_SUFFIX', '')) for k in self._binaries.keys()])
++        generated_files.update(['%s%s' % (k, self.config.substs.get('BIN_SUFFIX', '')) for k in list(self._binaries.keys())])
+ 
+         components = []
+         for var, cls in (
+@@ -1055,10 +1055,10 @@
+         for obj in self._process_jar_manifests(context):
+             yield obj
+ 
+-        for name, jar in context.get('JAVA_JAR_TARGETS', {}).items():
++        for name, jar in list(context.get('JAVA_JAR_TARGETS', {}).items()):
+             yield ContextWrapped(context, jar)
+ 
+-        for name, data in context.get('ANDROID_ECLIPSE_PROJECT_TARGETS', {}).items():
++        for name, data in list(context.get('ANDROID_ECLIPSE_PROJECT_TARGETS', {}).items()):
+             yield ContextWrapped(context, data)
+ 
+         if context.get('USE_YASM') is True:
+@@ -1127,7 +1127,7 @@
+             script = mozpath.join(mozpath.dirname(mozpath.dirname(__file__)),
+                                   'action', 'process_define_files.py')
+             yield GeneratedFile(context, script, 'process_define_file',
+-                                unicode(path),
++                                str(path),
+                                 [Path(context, path + '.in')])
+ 
+         generated_files = context.get('GENERATED_FILES')
+@@ -1170,7 +1170,7 @@
+             yield GeneratedFile(context, script, method, outputs, inputs)
+ 
+     def _process_test_manifests(self, context):
+-        for prefix, info in TEST_MANIFESTS.items():
++        for prefix, info in list(TEST_MANIFESTS.items()):
+             for path, manifest in context.get('%s_MANIFESTS' % prefix, []):
+                 for obj in self._process_test_manifest(context, info, path, manifest):
+                     yield obj
+@@ -1261,7 +1261,7 @@
+ 
+                 process_support_files(test)
+ 
+-            for path, m_defaults in mpmanifest.manifest_defaults.items():
++            for path, m_defaults in list(mpmanifest.manifest_defaults.items()):
+                 process_support_files(m_defaults)
+ 
+             # We also copy manifests into the output directory,
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/frontend/gyp_reader.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/frontend/gyp_reader.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import absolute_import, unicode_literals
++
+ 
+ import gyp
+ import sys
+@@ -69,7 +69,7 @@
+ 
+ 
+ def encode(value):
+-    if isinstance(value, unicode):
++    if isinstance(value, str):
+         return value.encode('utf-8')
+     return value
+ 
+@@ -87,7 +87,7 @@
+     # gyp expects plain str instead of unicode. The frontend code gives us
+     # unicode strings, so convert them.
+     path = encode(path)
+-    str_vars = dict((name, encode(value)) for name, value in vars.items())
++    str_vars = dict((name, encode(value)) for name, value in list(vars.items()))
+ 
+     params = {
+         b'parallel': False,
+@@ -222,7 +222,7 @@
+                         if not f:
+                             continue
+                         # the result may be a string or a list.
+-                        if isinstance(f, types.StringTypes):
++                        if isinstance(f, (str,)):
+                             context[var].append(f)
+                         else:
+                             context[var].extend(f)
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/frontend/mach_commands.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/frontend/mach_commands.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import absolute_import, print_function, unicode_literals
++
+ 
+ from collections import defaultdict
+ import os
+@@ -105,13 +105,13 @@
+         """
+         components = defaultdict(set)
+         try:
+-            for p, m in self._get_files_info(paths, rev=rev).items():
++            for p, m in list(self._get_files_info(paths, rev=rev).items()):
+                 components[m.get('BUG_COMPONENT')].add(p)
+         except InvalidPathException as e:
+             print(e.message)
+             return 1
+ 
+-        for component, files in sorted(components.items(), key=lambda x: (x is None, x)):
++        for component, files in sorted(list(components.items()), key=lambda x: (x is None, x)):
+             print('%s :: %s' % (component.product, component.component) if component else 'UNKNOWN')
+             for f in sorted(files):
+                 print('  %s' % f)
+@@ -139,7 +139,7 @@
+                      help='Paths whose data to query')
+     def file_info_test_deps(self, paths, rev=None):
+         try:
+-            for p, m in self._get_files_info(paths, rev=rev).items():
++            for p, m in list(self._get_files_info(paths, rev=rev).items()):
+                 print('%s:' % mozpath.relpath(p, self.topsrcdir))
+                 if m.test_files:
+                     print('\tTest file patterns:')
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/frontend/reader.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/frontend/reader.py	(refactored)
+@@ -16,7 +16,7 @@
+ It does this by examining specific variables populated during execution.
+ """
+ 
+-from __future__ import absolute_import, print_function, unicode_literals
++
+ 
+ import ast
+ import inspect
+@@ -80,11 +80,12 @@
+ )
+ 
+ from mozbuild.base import ExecutionSummary
++from functools import reduce
+ 
+ 
+ if sys.version_info.major == 2:
+-    text_type = unicode
+-    type_type = types.TypeType
++    text_type = str
++    type_type = type
+ else:
+     text_type = str
+     type_type = type
+@@ -126,7 +127,7 @@
+             b'JS_STANDALONE': b'1',
+         })
+         udict = {}
+-        for k, v in self.substs.items():
++        for k, v in list(self.substs.items()):
+             if isinstance(v, str):
+                 udict[k.decode('utf-8')] = v.decode('utf-8')
+             else:
+@@ -338,7 +339,7 @@
+             raise Exception('`template` is a function decorator. You must '
+                 'use it as `@template` preceding a function declaration.')
+ 
+-        name = func.func_name
++        name = func.__name__
+ 
+         if name in self.templates:
+             raise KeyError(
+@@ -417,7 +418,7 @@
+             klass = self._context.__class__
+             self._context.__class__ = TemplateContext
+             # The sandbox will do all the necessary checks for these merges.
+-            for key, value in context.items():
++            for key, value in list(context.items()):
+                 if isinstance(value, dict):
+                     self[key].update(value)
+                 elif isinstance(value, (list, HierarchicalStringList)):
+@@ -434,10 +435,10 @@
+ 
+ class TemplateFunction(object):
+     def __init__(self, func, sandbox):
+-        self.path = func.func_code.co_filename
+-        self.name = func.func_name
+-
+-        code = func.func_code
++        self.path = func.__code__.co_filename
++        self.name = func.__name__
++
++        code = func.__code__
+         firstlineno = code.co_firstlineno
+         lines = sandbox._current_source.splitlines(True)
+         lines = inspect.getblock(lines[firstlineno - 1:])
+@@ -476,8 +477,8 @@
+             compile(func_ast, self.path, 'exec'),
+             glob,
+             self.name,
+-            func.func_defaults,
+-            func.func_closure,
++            func.__defaults__,
++            func.__closure__,
+         )
+         func()
+ 
+@@ -491,11 +492,11 @@
+             '__builtins__': sandbox._builtins
+         }
+         func = types.FunctionType(
+-            self._func.func_code,
++            self._func.__code__,
+             glob,
+             self.name,
+-            self._func.func_defaults,
+-            self._func.func_closure
++            self._func.__defaults__,
++            self._func.__closure__
+         )
+         sandbox.exec_function(func, args, kwargs, self.path,
+                               becomes_current_path=False)
+@@ -511,7 +512,7 @@
+         def visit_Str(self, node):
+             # String nodes we got from the AST parser are str, but we want
+             # unicode literals everywhere, so transform them.
+-            node.s = unicode(node.s)
++            node.s = str(node.s)
+             return node
+ 
+         def visit_Name(self, node):
+@@ -644,7 +645,7 @@
+ 
+             for l in traceback.format_exception(type(self.other), self.other,
+                 self.trace):
+-                s.write(unicode(l))
++                s.write(str(l))
+ 
+         return s.getvalue()
+ 
+@@ -794,7 +795,7 @@
+             s.write('    %s\n' % inner.args[2])
+             s.write('\n')
+             close_matches = difflib.get_close_matches(inner.args[2],
+-                                                      VARIABLES.keys(), 2)
++                                                      list(VARIABLES.keys()), 2)
+             if close_matches:
+                 s.write('Maybe you meant %s?\n' % ' or '.join(close_matches))
+                 s.write('\n')
+@@ -1196,7 +1197,7 @@
+ 
+                 recurse_info[d][key] = dict(sandbox.metadata[key])
+ 
+-        for path, child_metadata in recurse_info.items():
++        for path, child_metadata in list(recurse_info.items()):
+             child_path = path.join('moz.build').full_path
+ 
+             # Ensure we don't break out of the topsrcdir. We don't do realpath
+@@ -1288,7 +1289,7 @@
+         # There is room to improve this code (and the code in
+         # _find_relevant_mozbuilds) to better handle multiple files in the same
+         # directory. Bug 1136966 tracks.
+-        for path, mbpaths in relevants.items():
++        for path, mbpaths in list(relevants.items()):
+             path_mozbuilds[path] = [mozpath.join(topsrcdir, p) for p in mbpaths]
+ 
+             for i, mbpath in enumerate(mbpaths[0:-1]):
+@@ -1325,7 +1326,7 @@
+             all_contexts.append(context)
+ 
+         result = {}
+-        for path, paths in path_mozbuilds.items():
++        for path, paths in list(path_mozbuilds.items()):
+             result[path] = reduce(lambda x, y: x + y, (contexts[p] for p in paths), [])
+ 
+         return result, all_contexts
+@@ -1352,7 +1353,7 @@
+ 
+         r = {}
+ 
+-        for path, ctxs in paths.items():
++        for path, ctxs in list(paths.items()):
+             flags = Files(Context())
+ 
+             for ctx in ctxs:
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/frontend/sandbox.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/frontend/sandbox.py	(refactored)
+@@ -17,7 +17,7 @@
+ user-friendly error messages in the case of errors.
+ """
+ 
+-from __future__ import absolute_import, unicode_literals
++
+ 
+ import os
+ import sys
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/common.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/common.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import unicode_literals
++
+ 
+ from mach.logging import LoggingManager
+ 
+@@ -36,7 +36,7 @@
+         }, **extra_substs)
+ 
+         self.substs_unicode = ReadOnlyDict({k.decode('utf-8'): v.decode('utf-8',
+-            'replace') for k, v in self.substs.items()})
++            'replace') for k, v in list(self.substs.items())})
+ 
+         self.defines = self.substs
+ 
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/test_base.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/test_base.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import unicode_literals
++
+ 
+ import json
+ import os
+@@ -12,7 +12,7 @@
+ import tempfile
+ import unittest
+ 
+-from cStringIO import StringIO
++from io import StringIO
+ from mozfile.mozfile import NamedTemporaryFile
+ 
+ from mozunit import main
+@@ -93,7 +93,7 @@
+             mozconfig = os.path.join(d, 'mozconfig')
+             with open(mozconfig, 'wt') as fh:
+                 fh.write('mk_add_options MOZ_OBJDIR=@TOPSRCDIR@/foo/@CONFIG_GUESS@')
+-            print('Wrote mozconfig %s' % mozconfig)
++            print(('Wrote mozconfig %s' % mozconfig))
+ 
+             topobjdir = os.path.join(d, 'foo', guess)
+             os.makedirs(topobjdir)
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/test_containers.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/test_containers.py	(refactored)
+@@ -152,7 +152,7 @@
+ 
+         self.assertEqual(test['foo'], 1)
+ 
+-        self.assertEqual(test.keys(), ['foo', 'bar' ])
++        self.assertEqual(list(test.keys()), ['foo', 'bar' ])
+ 
+     def test_defaults(self):
+         test = OrderedDefaultDict(bool, {'foo': 1 })
+@@ -161,7 +161,7 @@
+ 
+         self.assertEqual(test['qux'], False)
+ 
+-        self.assertEqual(test.keys(), ['foo', 'qux' ])
++        self.assertEqual(list(test.keys()), ['foo', 'qux' ])
+ 
+ 
+ class TestKeyedDefaultDict(unittest.TestCase):
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/test_dotproperties.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/test_dotproperties.py	(refactored)
+@@ -1,11 +1,11 @@
+ # -*- coding: utf-8 -*-
+ 
+-from __future__ import unicode_literals
++
+ 
+ import os
+ import unittest
+ 
+-from StringIO import StringIO
++from io import StringIO
+ 
+ import mozpack.path as mozpath
+ 
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/test_expression.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/test_expression.py	(refactored)
+@@ -25,8 +25,8 @@
+ 
+   def test_in(self):
+     """test 'var in context' to not fall for fallback"""
+-    self.assert_('FAIL' in self.c)
+-    self.assert_('PASS' not in self.c)
++    self.assertTrue('FAIL' in self.c)
++    self.assertTrue('PASS' not in self.c)
+ 
+ class TestExpression(unittest.TestCase):
+   """
+@@ -48,16 +48,16 @@
+ 
+   def test_not(self):
+     """Test for the ! operator"""
+-    self.assert_(Expression('!0').evaluate(self.c))
+-    self.assert_(not Expression('!1').evaluate(self.c))
++    self.assertTrue(Expression('!0').evaluate(self.c))
++    self.assertTrue(not Expression('!1').evaluate(self.c))
+ 
+   def test_equals(self):
+     """ Test for the == operator"""
+-    self.assert_(Expression('FAIL == PASS').evaluate(self.c))
++    self.assertTrue(Expression('FAIL == PASS').evaluate(self.c))
+ 
+   def test_notequals(self):
+     """ Test for the != operator"""
+-    self.assert_(Expression('FAIL != 1').evaluate(self.c))
++    self.assertTrue(Expression('FAIL != 1').evaluate(self.c))
+ 
+   def test_logical_and(self):
+     """ Test for the && operator"""
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/test_jarmaker.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/test_jarmaker.py	(refactored)
+@@ -2,14 +2,14 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import print_function
++
+ import unittest
+ 
+ import os, sys, os.path, time, inspect
+ from filecmp import dircmp
+ from tempfile import mkdtemp
+ from shutil import rmtree, copy2
+-from StringIO import StringIO
++from io import StringIO
+ from zipfile import ZipFile
+ import mozunit
+ 
+@@ -117,12 +117,12 @@
+     """Helper to report rich results on difference between two directories.
+     """
+     def _fillDiff(self, dc, rv, basepath="{0}"):
+-        rv['right_only'] += map(lambda l: basepath.format(l), dc.right_only)
+-        rv['left_only'] += map(lambda l: basepath.format(l), dc.left_only)
+-        rv['diff_files'] += map(lambda l: basepath.format(l), dc.diff_files)
+-        rv['funny'] += map(lambda l: basepath.format(l), dc.common_funny)
+-        rv['funny'] += map(lambda l: basepath.format(l), dc.funny_files)
+-        for subdir, _dc in dc.subdirs.iteritems():
++        rv['right_only'] += [basepath.format(l) for l in dc.right_only]
++        rv['left_only'] += [basepath.format(l) for l in dc.left_only]
++        rv['diff_files'] += [basepath.format(l) for l in dc.diff_files]
++        rv['funny'] += [basepath.format(l) for l in dc.common_funny]
++        rv['funny'] += [basepath.format(l) for l in dc.funny_files]
++        for subdir, _dc in dc.subdirs.items():
+             self._fillDiff(_dc, rv, basepath.format(subdir + "/{0}"))
+     def allResults(self, left, right):
+         rv = {'right_only':[], 'left_only':[],
+@@ -295,7 +295,7 @@
+             ('hoge', 'foo', '2'): ('qux', 'foo', '2'),
+             ('hoge', 'baz'): ('qux', 'baz'),
+         }
+-        for dest, src in expected_symlinks.iteritems():
++        for dest, src in expected_symlinks.items():
+             srcpath = os.path.join(self.srcdir, *src)
+             destpath = os.path.join(self.builddir, 'chrome', 'test', 'dir',
+                                     *dest)
+@@ -317,7 +317,7 @@
+     def test_en_US(self):
+         jm = self.jm
+         jm.makeJar(self.fake_empty_file, '/NO_OUTPUT_REQUIRED')
+-        self.assertEquals(jm.localedirs,
++        self.assertEqual(jm.localedirs,
+                           [
+                             os.path.join(os.path.abspath('/TOPSOURCEDIR'),
+                                          'browser/locales', 'en-US')
+@@ -326,13 +326,13 @@
+         jm = self.jm
+         jm.l10nbase = '/L10N_BASE'
+         jm.makeJar(self.fake_empty_file, '/NO_OUTPUT_REQUIRED')
+-        self.assertEquals(jm.localedirs, [os.path.join('/L10N_BASE', 'browser')])
++        self.assertEqual(jm.localedirs, [os.path.join('/L10N_BASE', 'browser')])
+     def test_l10n_merge(self):
+         jm = self.jm
+         jm.l10nbase = '/L10N_BASE'
+         jm.l10nmerge = '/L10N_MERGE'
+         jm.makeJar(self.fake_empty_file, '/NO_OUTPUT_REQUIRED')
+-        self.assertEquals(jm.localedirs,
++        self.assertEqual(jm.localedirs,
+                           [os.path.join('/L10N_MERGE', 'browser'),
+                            os.path.join('/L10N_BASE', 'browser'),
+                            os.path.join(os.path.abspath('/TOPSOURCEDIR'),
+@@ -346,7 +346,7 @@
+ ''')
+         jarcontents.name = 'override.mn'
+         jm.makeJar(jarcontents, '/NO_OUTPUT_REQUIRED')
+-        self.assertEquals(jm.localedirs,
++        self.assertEqual(jm.localedirs,
+                           [
+                             os.path.join(os.path.abspath('/TOPSOURCEDIR'),
+                                          'dom/locales', 'en-US')
+@@ -360,7 +360,7 @@
+ ''')
+         jarcontents.name = 'override.mn'
+         jm.makeJar(jarcontents, '/NO_OUTPUT_REQUIRED')
+-        self.assertEquals(jm.localedirs, [os.path.join('/L10N_BASE', 'dom')])
++        self.assertEqual(jm.localedirs, [os.path.join('/L10N_BASE', 'dom')])
+ 
+ 
+ if __name__ == '__main__':
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/test_line_endings.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/test_line_endings.py	(refactored)
+@@ -1,6 +1,6 @@
+ import unittest
+ 
+-from StringIO import StringIO
++from io import StringIO
+ import os
+ import sys
+ import os.path
+@@ -30,17 +30,17 @@
+   def testMac(self):
+     self.createFile(['\x0D']*3)
+     self.pp.do_include(self.tempnam)
+-    self.assertEquals(self.pp.out.getvalue(), 'a\nb\nc\n')
++    self.assertEqual(self.pp.out.getvalue(), 'a\nb\nc\n')
+ 
+   def testUnix(self):
+     self.createFile(['\x0A']*3)
+     self.pp.do_include(self.tempnam)
+-    self.assertEquals(self.pp.out.getvalue(), 'a\nb\nc\n')
++    self.assertEqual(self.pp.out.getvalue(), 'a\nb\nc\n')
+ 
+   def testWindows(self):
+     self.createFile(['\x0D\x0A']*3)
+     self.pp.do_include(self.tempnam)
+-    self.assertEquals(self.pp.out.getvalue(), 'a\nb\nc\n')
++    self.assertEqual(self.pp.out.getvalue(), 'a\nb\nc\n')
+ 
+ if __name__ == '__main__':
+   mozunit.main()
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/test_makeutil.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/test_makeutil.py	(refactored)
+@@ -11,7 +11,7 @@
+ from mozunit import main
+ import os
+ import unittest
+-from StringIO import StringIO
++from io import StringIO
+ 
+ 
+ class TestMakefile(unittest.TestCase):
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/test_mozconfig.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/test_mozconfig.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import unicode_literals
++
+ 
+ import os
+ import unittest
+@@ -480,9 +480,9 @@
+ 
+             self.assertTrue(e.exception.message.startswith(
+                 'Evaluation of your mozconfig exited with an error'))
+-            self.assertEquals(e.exception.path,
++            self.assertEqual(e.exception.path,
+                 mozconfig.name.replace(os.sep, '/'))
+-            self.assertEquals(e.exception.output, ['hello world'])
++            self.assertEqual(e.exception.output, ['hello world'])
+ 
+ 
+ if __name__ == '__main__':
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/test_mozinfo.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/test_mozinfo.py	(refactored)
+@@ -8,7 +8,7 @@
+ import tempfile
+ import unittest
+ 
+-from StringIO import StringIO
++from io import StringIO
+ 
+ import mozunit
+ 
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/test_preprocessor.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/test_preprocessor.py	(refactored)
+@@ -4,7 +4,7 @@
+ 
+ import unittest
+ 
+-from StringIO import StringIO
++from io import StringIO
+ import os
+ import shutil
+ 
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/test_pythonutil.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/test_pythonutil.py	(refactored)
+@@ -12,7 +12,7 @@
+     def test_iter_modules_in_path(self):
+         mozbuild_path = os.path.normcase(os.path.dirname(os.path.dirname(__file__)))
+         paths = list(iter_modules_in_path(mozbuild_path))
+-        self.assertEquals(sorted(paths), [
++        self.assertEqual(sorted(paths), [
+             os.path.join(os.path.abspath(mozbuild_path), '__init__.py'),
+             os.path.join(os.path.abspath(mozbuild_path), 'pythonutil.py'),
+             os.path.join(os.path.abspath(mozbuild_path), 'test', 'test_pythonutil.py'),
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/test_testing.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/test_testing.py	(refactored)
+@@ -2,9 +2,9 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import unicode_literals
+-
+-import cPickle as pickle
++
++
++import pickle as pickle
+ import os
+ import shutil
+ import tempfile
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/test_util.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/test_util.py	(refactored)
+@@ -3,7 +3,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import unicode_literals
++
+ 
+ import itertools
+ import hashlib
+@@ -110,7 +110,7 @@
+             '''
+             def __call__(self, name, mode):
+                 if 'w' in mode:
+-                    raise Exception, 'Unexpected open with write mode'
++                    raise Exception('Unexpected open with write mode')
+                 return MockedOpen.__call__(self, name, mode)
+ 
+         with MyMockedOpen({'file': 'content'}):
+@@ -432,7 +432,7 @@
+         self.assertEqual(len(l), 0)
+         original = ['a', 'b', 'c']
+         l = ListWithAction(['a', 'b', 'c'], action=self.action)
+-        expected = map(self.action, original)
++        expected = list(map(self.action, original))
+         self.assertSameList(expected, l)
+ 
+         with self.assertRaises(ValueError):
+@@ -445,7 +445,7 @@
+         l = ListWithAction(action=self.action)
+         original = ['a', 'b']
+         l.extend(original)
+-        expected = map(self.action, original)
++        expected = list(map(self.action, original))
+         self.assertSameList(expected, l)
+ 
+         with self.assertRaises(ValueError):
+@@ -455,7 +455,7 @@
+         l = ListWithAction(action=self.action)
+         original = ['a', 'b']
+         l[:] = original
+-        expected = map(self.action, original)
++        expected = list(map(self.action, original))
+         self.assertSameList(expected, l)
+ 
+         with self.assertRaises(ValueError):
+@@ -465,7 +465,7 @@
+         l = ListWithAction(action=self.action)
+         original = ['a', 'b']
+         l2 = l + original
+-        expected = map(self.action, original)
++        expected = list(map(self.action, original))
+         self.assertSameList(expected, l2)
+ 
+         with self.assertRaises(ValueError):
+@@ -475,7 +475,7 @@
+         l = ListWithAction(action=self.action)
+         original = ['a', 'b']
+         l += original
+-        expected = map(self.action, original)
++        expected = list(map(self.action, original))
+         self.assertSameList(expected, l)
+ 
+         with self.assertRaises(ValueError):
+@@ -524,7 +524,7 @@
+ 
+     def test_strict_ordering_on_append_list_with_flags_factory_extend(self):
+         FooList = StrictOrderingOnAppendListWithFlagsFactory({
+-            'foo': bool, 'bar': unicode
++            'foo': bool, 'bar': str
+         })
+         foo = FooList(['a', 'b', 'c'])
+         foo['a'].foo = True
+@@ -532,7 +532,7 @@
+ 
+         # Don't allow extending lists with different flag definitions.
+         BarList = StrictOrderingOnAppendListWithFlagsFactory({
+-            'foo': unicode, 'baz': bool
++            'foo': str, 'baz': bool
+         })
+         bar = BarList(['d', 'e', 'f'])
+         bar['d'].foo = 'foo'
+@@ -752,9 +752,9 @@
+ 
+ class TypedTestStrictOrderingOnAppendList(unittest.TestCase):
+     def test_init(self):
+-        class Unicode(unicode):
++        class Unicode(str):
+             def __init__(self, other):
+-                if not isinstance(other, unicode):
++                if not isinstance(other, str):
+                     raise ValueError()
+                 super(Unicode, self).__init__(other)
+ 
+@@ -776,14 +776,14 @@
+ 
+ class TestTypedNamedTuple(unittest.TestCase):
+     def test_simple(self):
+-        FooBar = TypedNamedTuple('FooBar', [('foo', unicode), ('bar', int)])
++        FooBar = TypedNamedTuple('FooBar', [('foo', str), ('bar', int)])
+ 
+         t = FooBar(foo='foo', bar=2)
+-        self.assertEquals(type(t), FooBar)
+-        self.assertEquals(t.foo, 'foo')
+-        self.assertEquals(t.bar, 2)
+-        self.assertEquals(t[0], 'foo')
+-        self.assertEquals(t[1], 2)
++        self.assertEqual(type(t), FooBar)
++        self.assertEqual(t.foo, 'foo')
++        self.assertEqual(t.bar, 2)
++        self.assertEqual(t[0], 'foo')
++        self.assertEqual(t[1], 2)
+ 
+         FooBar('foo', 2)
+ 
+@@ -796,7 +796,7 @@
+         # arguments.
+         t1 = ('foo', 3)
+         t2 = FooBar(t1)
+-        self.assertEquals(type(t2), FooBar)
++        self.assertEqual(type(t2), FooBar)
+         self.assertEqual(FooBar(t1), FooBar('foo', 3))
+ 
+ 
+@@ -871,18 +871,18 @@
+         CompilerType = EnumString.subclass('msvc', 'gcc', 'clang', 'clang-cl')
+ 
+         type = CompilerType('msvc')
+-        self.assertEquals(type, 'msvc')
+-        self.assertNotEquals(type, 'gcc')
+-        self.assertNotEquals(type, 'clang')
+-        self.assertNotEquals(type, 'clang-cl')
++        self.assertEqual(type, 'msvc')
++        self.assertNotEqual(type, 'gcc')
++        self.assertNotEqual(type, 'clang')
++        self.assertNotEqual(type, 'clang-cl')
+         self.assertIn(type, ('msvc', 'clang-cl'))
+         self.assertNotIn(type, ('gcc', 'clang'))
+ 
+         with self.assertRaises(EnumStringComparisonError):
+-            self.assertEquals(type, 'foo')
++            self.assertEqual(type, 'foo')
+ 
+         with self.assertRaises(EnumStringComparisonError):
+-            self.assertNotEquals(type, 'foo')
++            self.assertNotEqual(type, 'foo')
+ 
+         with self.assertRaises(EnumStringComparisonError):
+             self.assertIn(type, ('foo', 'gcc'))
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/action/test_buildlist.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/action/test_buildlist.py	(refactored)
+@@ -38,11 +38,11 @@
+     lines = [line.rstrip() for line in f.readlines()]
+     f.close()
+     for line in lines:
+-      self.assert_(len(l) > 0,
++      self.assertTrue(len(l) > 0,
+                    "ran out of expected lines! (expected '{0}', got '{1}')"
+                    .format(l, lines))
+       self.assertEqual(line, l.pop(0))
+-    self.assert_(len(l) == 0, 
++    self.assertTrue(len(l) == 0, 
+                  "not enough lines in file! (expected '{0}',"
+                  " got '{1}'".format(l, lines))
+ 
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/action/test_generate_browsersearch.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/action/test_generate_browsersearch.py	(refactored)
+@@ -3,7 +3,7 @@
+ # Any copyright is dedicated to the Public Domain.
+ # http://creativecommons.org/publicdomain/zero/1.0/
+ 
+-from __future__ import unicode_literals
++
+ 
+ import json
+ import os
+@@ -43,8 +43,8 @@
+ 
+     def test_valid_unicode(self):
+         o = self._test_one('valid-zh-CN')
+-        self.assertEquals(o['default'], '百度')
+-        self.assertEquals(o['engines'], ['百度', 'Google'])
++        self.assertEqual(o['default'], '百度')
++        self.assertEqual(o['engines'], ['百度', 'Google'])
+ 
+     def test_invalid_unicode(self):
+         with self.assertRaises(UnicodeDecodeError):
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/action/test_package_fennec_apk.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/action/test_package_fennec_apk.py	(refactored)
+@@ -3,7 +3,7 @@
+ # Any copyright is dedicated to the Public Domain.
+ # http://creativecommons.org/publicdomain/zero/1.0/
+ 
+-from __future__ import unicode_literals
++
+ 
+ import os
+ import unittest
+@@ -41,14 +41,14 @@
+                          root_files=[data('root_file.txt')])
+ 
+         # omni.ja ends up in assets/omni.ja.
+-        self.assertEquals(jarrer['assets/omni.ja'].open().read().strip(), 'omni.ja')
++        self.assertEqual(jarrer['assets/omni.ja'].open().read().strip(), 'omni.ja')
+ 
+         # Everything else is in place.
+         for name in ('classes.dex',
+                      'assets/asset.txt',
+                      'lib/lib.txt',
+                      'root_file.txt'):
+-            self.assertEquals(jarrer[name].open().read().strip(), name)
++            self.assertEqual(jarrer[name].open().read().strip(), name)
+ 
+     def test_inputs(self):
+         # Language repacks take updated resources from an ap_ and pack them
+@@ -57,8 +57,8 @@
+         # overrides the first.
+         jarrer = package(inputs=[data('input2.apk'), data('input1.ap_')])
+ 
+-        files1 = JarReader(data('input1.ap_')).entries.keys()
+-        files2 = JarReader(data('input2.apk')).entries.keys()
++        files1 = list(JarReader(data('input1.ap_')).entries.keys())
++        files2 = list(JarReader(data('input2.apk')).entries.keys())
+         for name in files2:
+             self.assertTrue(name in files1 or
+                             jarrer[name].open().read().startswith('input2/'))
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/backend/common.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/backend/common.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import unicode_literals
++
+ 
+ import os
+ import unittest
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/backend/test_android_eclipse.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/backend/test_android_eclipse.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import unicode_literals
++
+ 
+ import json
+ import os
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/backend/test_build.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/backend/test_build.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import unicode_literals, print_function
++
+ 
+ import buildconfig
+ import os
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/backend/test_configenvironment.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/backend/test_configenvironment.py	(refactored)
+@@ -3,7 +3,7 @@
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+ import os, posixpath
+-from StringIO import StringIO
++from io import StringIO
+ import unittest
+ from mozunit import main, MockedOpen
+ 
+@@ -29,7 +29,7 @@
+             self.substs = ReadOnlyDict(d)
+ 
+             d = dict(self.substs_unicode)
+-            d[u'top_srcdir'] = top_srcdir.decode('utf-8')
++            d['top_srcdir'] = top_srcdir.decode('utf-8')
+             self.substs_unicode = ReadOnlyDict(d)
+ 
+ 
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/backend/test_recursivemake.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/backend/test_recursivemake.py	(refactored)
+@@ -2,9 +2,9 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import unicode_literals
+-
+-import cPickle as pickle
++
++
++import pickle as pickle
+ import json
+ import os
+ import unittest
+@@ -331,7 +331,7 @@
+             ],
+         }
+ 
+-        for var, val in expected.items():
++        for var, val in list(expected.items()):
+             # print("test_variable_passthru[%s]" % (var))
+             found = [str for str in lines if str.startswith(var)]
+             self.assertEqual(found, val)
+@@ -370,7 +370,7 @@
+             ],
+         }
+ 
+-        for var, val in expected.items():
++        for var, val in list(expected.items()):
+             found = [str for str in lines if str.startswith(var)]
+             self.assertEqual(found, val)
+ 
+@@ -544,7 +544,7 @@
+ 
+         # This is not the most robust test in the world, but it gets the job
+         # done.
+-        entries = [e for e in m._dests.keys() if '**' in e]
++        entries = [e for e in list(m._dests.keys()) if '**' in e]
+         self.assertEqual(len(entries), 1)
+         self.assertIn('support/**', entries[0])
+ 
+@@ -562,7 +562,7 @@
+                          set(['child/test_sub.js',
+                               'child/data/**',
+                               'child/another-file.sjs']))
+-        for key in test_installs.keys():
++        for key in list(test_installs.keys()):
+             self.assertIn(key, test_installs)
+ 
+         test_files_manifest = mozpath.join(env.topobjdir,
+@@ -576,7 +576,7 @@
+         # Then, synthesize one from the test-installs.pkl file. This should
+         # allow us to re-create a subset of the above.
+         synthesized_manifest = InstallManifest()
+-        for item, installs in test_installs.items():
++        for item, installs in list(test_installs.items()):
+             for install_info in installs:
+                 if len(install_info) == 3:
+                     synthesized_manifest.add_pattern_symlink(*install_info)
+@@ -584,7 +584,7 @@
+                     synthesized_manifest.add_symlink(*install_info)
+ 
+         self.assertEqual(len(synthesized_manifest), 3)
+-        for item, info in synthesized_manifest._dests.items():
++        for item, info in list(synthesized_manifest._dests.items()):
+             self.assertIn(item, m)
+             self.assertEqual(info, m._dests[item])
+ 
+@@ -758,7 +758,7 @@
+         expected[mozpath.join(env.topobjdir, 'final-target')] = [
+             'FINAL_TARGET = $(DEPTH)/random-final-target'
+         ]
+-        for key, expected_rules in expected.iteritems():
++        for key, expected_rules in expected.items():
+             backend_path = mozpath.join(key, 'backend.mk')
+             lines = [l.strip() for l in open(backend_path, 'rt').readlines()[2:]]
+             found = [str for str in lines if
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/backend/test_visualstudio.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/backend/test_visualstudio.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import unicode_literals
++
+ 
+ from xml.dom.minidom import parse
+ import os
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/configure/common.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/configure/common.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import absolute_import, print_function, unicode_literals
++
+ 
+ import copy
+ import errno
+@@ -16,7 +16,7 @@
+ from mozbuild.util import ReadOnlyNamespace
+ from mozpack import path as mozpath
+ 
+-from StringIO import StringIO
++from io import StringIO
+ from which import WhichError
+ 
+ from buildconfig import (
+@@ -78,10 +78,10 @@
+         self._search_path = environ.get('PATH', '').split(os.pathsep)
+ 
+         self._subprocess_paths = {
+-            mozpath.abspath(k): v for k, v in paths.iteritems() if v
++            mozpath.abspath(k): v for k, v in paths.items() if v
+         }
+ 
+-        paths = paths.keys()
++        paths = list(paths.keys())
+ 
+         environ = dict(environ)
+         if 'CONFIG_SHELL' not in environ:
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/configure/lint.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/configure/lint.py	(refactored)
+@@ -2,11 +2,11 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import absolute_import, print_function, unicode_literals
++
+ 
+ import os
+ import unittest
+-from StringIO import StringIO
++from io import StringIO
+ from mozunit import main
+ from buildconfig import (
+     topobjdir,
+@@ -42,9 +42,7 @@
+         return type.__new__(mcs, name, bases, attrs)
+ 
+ 
+-class Lint(unittest.TestCase):
+-    __metaclass__ = LintMeta
+-
++class Lint(unittest.TestCase, metaclass=LintMeta):
+     def setUp(self):
+         self._curdir = os.getcwd()
+         os.chdir(topobjdir)
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/configure/test_checks_configure.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/configure/test_checks_configure.py	(refactored)
+@@ -2,9 +2,9 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import absolute_import, print_function, unicode_literals
+-
+-from StringIO import StringIO
++
++
++from io import StringIO
+ import os
+ import sys
+ import textwrap
+@@ -447,7 +447,7 @@
+             checking for a... %s
+         ''' % self.OTHER_A))
+ 
+-        dirs = map(mozpath.dirname, (self.OTHER_A, self.KNOWN_A))
++        dirs = list(map(mozpath.dirname, (self.OTHER_A, self.KNOWN_A)))
+         config, out, status = self.get_result(textwrap.dedent('''\
+             check_prog("A", ("known-a",), paths=["%s"])
+         ''' % os.pathsep.join(dirs)))
+@@ -457,7 +457,7 @@
+             checking for a... %s
+         ''' % self.OTHER_A))
+ 
+-        dirs = map(mozpath.dirname, (self.KNOWN_A, self.KNOWN_B))
++        dirs = list(map(mozpath.dirname, (self.KNOWN_A, self.KNOWN_B)))
+         config, out, status = self.get_result(textwrap.dedent('''\
+             check_prog("A", ("known-a",), paths=["%s", "%s"])
+         ''' % (os.pathsep.join(dirs), self.OTHER_A)))
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/configure/test_compile_checks.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/configure/test_compile_checks.py	(refactored)
+@@ -2,14 +2,14 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import absolute_import, print_function, unicode_literals
++
+ 
+ import os
+ import textwrap
+ import unittest
+ import mozpack.path as mozpath
+ 
+-from StringIO import StringIO
++from io import StringIO
+ 
+ from buildconfig import topsrcdir
+ from common import ConfigureTestSandbox
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/configure/test_configure.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/configure/test_configure.py	(refactored)
+@@ -2,9 +2,9 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import absolute_import, print_function, unicode_literals
+-
+-from StringIO import StringIO
++
++
++from io import StringIO
+ import os
+ import sys
+ import textwrap
+@@ -43,7 +43,7 @@
+ 
+         if '--help' in options:
+             return out.getvalue(), config
+-        self.assertEquals('', out.getvalue())
++        self.assertEqual('', out.getvalue())
+         return config
+ 
+     def moz_configure(self, source):
+@@ -55,7 +55,7 @@
+     def test_defaults(self):
+         config = self.get_config()
+         self.maxDiff = None
+-        self.assertEquals({
++        self.assertEqual({
+             'CHOICES': NegativeOptionValue(),
+             'DEFAULTED': PositiveOptionValue(('not-simple',)),
+             'IS_GCC': NegativeOptionValue(),
+@@ -71,9 +71,9 @@
+     def test_help(self):
+         help, config = self.get_config(['--help'], prog='configure')
+ 
+-        self.assertEquals({}, config)
++        self.assertEqual({}, config)
+         self.maxDiff = None
+-        self.assertEquals(
++        self.assertEqual(
+             'Usage: configure [options]\n'
+             '\n'
+             'Options: [defaults in brackets after descriptions]\n'
+@@ -109,7 +109,7 @@
+         ):
+             self.assertNotIn('ENABLED_SIMPLE', config)
+             self.assertIn('SIMPLE', config)
+-            self.assertEquals(NegativeOptionValue(), config['SIMPLE'])
++            self.assertEqual(NegativeOptionValue(), config['SIMPLE'])
+ 
+         for config in (
+                 self.get_config(['--enable-simple']),
+@@ -117,7 +117,7 @@
+         ):
+             self.assertIn('ENABLED_SIMPLE', config)
+             self.assertIn('SIMPLE', config)
+-            self.assertEquals(PositiveOptionValue(), config['SIMPLE'])
++            self.assertEqual(PositiveOptionValue(), config['SIMPLE'])
+             self.assertIs(config['SIMPLE'], config['ENABLED_SIMPLE'])
+ 
+         # --enable-simple doesn't take values.
+@@ -135,7 +135,7 @@
+                                 env={'MOZ_WITH_ENV': '1'}),
+         ):
+             self.assertIn('WITH_ENV', config)
+-            self.assertEquals(NegativeOptionValue(), config['WITH_ENV'])
++            self.assertEqual(NegativeOptionValue(), config['WITH_ENV'])
+ 
+         for config in (
+                 self.get_config(['--enable-with-env']),
+@@ -145,7 +145,7 @@
+                                 env={'MOZ_WITH_ENV': ''}),
+         ):
+             self.assertIn('WITH_ENV', config)
+-            self.assertEquals(PositiveOptionValue(), config['WITH_ENV'])
++            self.assertEqual(PositiveOptionValue(), config['WITH_ENV'])
+ 
+         with self.assertRaises(InvalidOptionError):
+             self.get_config(['--enable-with-env=value'])
+@@ -160,23 +160,23 @@
+             self.get_config(['--enable-values', '--disable-values']),
+         ):
+             self.assertIn(name, config)
+-            self.assertEquals(NegativeOptionValue(), config[name])
++            self.assertEqual(NegativeOptionValue(), config[name])
+ 
+         for config in (
+             self.get_config(['--enable-values']),
+             self.get_config(['--disable-values', '--enable-values']),
+         ):
+             self.assertIn(name, config)
+-            self.assertEquals(PositiveOptionValue(), config[name])
++            self.assertEqual(PositiveOptionValue(), config[name])
+ 
+         config = self.get_config(['--enable-values=foo'])
+         self.assertIn(name, config)
+-        self.assertEquals(PositiveOptionValue(('foo',)), config[name])
++        self.assertEqual(PositiveOptionValue(('foo',)), config[name])
+ 
+         config = self.get_config(['--enable-values=foo,bar'])
+         self.assertIn(name, config)
+         self.assertTrue(config[name])
+-        self.assertEquals(PositiveOptionValue(('foo', 'bar')), config[name])
++        self.assertEqual(PositiveOptionValue(('foo', 'bar')), config[name])
+ 
+     def test_values2(self):
+         self.test_values('VALUES2')
+@@ -187,12 +187,12 @@
+     def test_returned_default(self):
+         config = self.get_config(['--enable-simple'])
+         self.assertIn('DEFAULTED', config)
+-        self.assertEquals(
++        self.assertEqual(
+             PositiveOptionValue(('simple',)), config['DEFAULTED'])
+ 
+         config = self.get_config(['--disable-simple'])
+         self.assertIn('DEFAULTED', config)
+-        self.assertEquals(
++        self.assertEqual(
+             PositiveOptionValue(('not-simple',)), config['DEFAULTED'])
+ 
+     def test_returned_choices(self):
+@@ -200,13 +200,13 @@
+             config = self.get_config(
+                 ['--enable-values=alpha', '--returned-choices=%s' % val])
+             self.assertIn('CHOICES', config)
+-            self.assertEquals(PositiveOptionValue((val,)), config['CHOICES'])
++            self.assertEqual(PositiveOptionValue((val,)), config['CHOICES'])
+ 
+         for val in ('0', '1', '2'):
+             config = self.get_config(
+                 ['--enable-values=numeric', '--returned-choices=%s' % val])
+             self.assertIn('CHOICES', config)
+-            self.assertEquals(PositiveOptionValue((val,)), config['CHOICES'])
++            self.assertEqual(PositiveOptionValue((val,)), config['CHOICES'])
+ 
+         with self.assertRaises(InvalidOptionError):
+             self.get_config(['--enable-values=numeric',
+@@ -218,12 +218,12 @@
+     def test_included(self):
+         config = self.get_config(env={'CC': 'gcc'})
+         self.assertIn('IS_GCC', config)
+-        self.assertEquals(config['IS_GCC'], True)
++        self.assertEqual(config['IS_GCC'], True)
+ 
+         config = self.get_config(
+             ['--enable-include=extra.configure', '--extra'])
+         self.assertIn('EXTRA', config)
+-        self.assertEquals(PositiveOptionValue(), config['EXTRA'])
++        self.assertEqual(PositiveOptionValue(), config['EXTRA'])
+ 
+         with self.assertRaises(InvalidOptionError):
+             self.get_config(['--extra'])
+@@ -231,7 +231,7 @@
+     def test_template(self):
+         config = self.get_config(env={'CC': 'gcc'})
+         self.assertIn('CFLAGS', config)
+-        self.assertEquals(config['CFLAGS'], ['-Werror=foobar'])
++        self.assertEqual(config['CFLAGS'], ['-Werror=foobar'])
+ 
+         config = self.get_config(env={'CC': 'clang'})
+         self.assertNotIn('CFLAGS', config)
+@@ -288,7 +288,7 @@
+             sandbox
+         )
+ 
+-        import __builtin__
++        import builtins
+         self.assertIs(sandbox['foo'](), __builtin__)
+ 
+         exec_(textwrap.dedent('''
+@@ -300,7 +300,7 @@
+         )
+ 
+         f = sandbox['foo']()
+-        self.assertEquals(f.name, os.devnull)
++        self.assertEqual(f.name, os.devnull)
+         f.close()
+ 
+         # This unlocks the sandbox
+@@ -336,8 +336,8 @@
+         self.assertIs(sandbox['foo'](), sandbox)
+ 
+         # Nothing leaked from the function being executed
+-        self.assertEquals(sandbox.keys(), ['__builtins__', 'foo'])
+-        self.assertEquals(sandbox['__builtins__'], ConfigureSandbox.BUILTINS)
++        self.assertEqual(list(sandbox.keys()), ['__builtins__', 'foo'])
++        self.assertEqual(sandbox['__builtins__'], ConfigureSandbox.BUILTINS)
+ 
+         exec_(textwrap.dedent('''
+             @template
+@@ -354,7 +354,7 @@
+         with self.assertRaises(NameError) as e:
+             sandbox._depends[sandbox['bar']].result
+ 
+-        self.assertEquals(e.exception.message,
++        self.assertEqual(e.exception.message,
+                           "global name 'sys' is not defined")
+ 
+     def test_apply_imports(self):
+@@ -380,28 +380,28 @@
+             sandbox
+         )
+ 
+-        self.assertEquals(len(imports), 1)
++        self.assertEqual(len(imports), 1)
+ 
+     def test_os_path(self):
+         config = self.get_config(['--with-imports=%s' % __file__])
+         self.assertIn('HAS_ABSPATH', config)
+-        self.assertEquals(config['HAS_ABSPATH'], True)
++        self.assertEqual(config['HAS_ABSPATH'], True)
+         self.assertIn('HAS_GETATIME', config)
+-        self.assertEquals(config['HAS_GETATIME'], True)
++        self.assertEqual(config['HAS_GETATIME'], True)
+         self.assertIn('HAS_GETATIME2', config)
+-        self.assertEquals(config['HAS_GETATIME2'], False)
++        self.assertEqual(config['HAS_GETATIME2'], False)
+ 
+     def test_template_call(self):
+         config = self.get_config(env={'CC': 'gcc'})
+         self.assertIn('TEMPLATE_VALUE', config)
+-        self.assertEquals(config['TEMPLATE_VALUE'], 42)
++        self.assertEqual(config['TEMPLATE_VALUE'], 42)
+         self.assertIn('TEMPLATE_VALUE_2', config)
+-        self.assertEquals(config['TEMPLATE_VALUE_2'], 21)
++        self.assertEqual(config['TEMPLATE_VALUE_2'], 21)
+ 
+     def test_template_imports(self):
+         config = self.get_config(['--enable-imports-in-template'])
+         self.assertIn('PLATFORM', config)
+-        self.assertEquals(config['PLATFORM'], sys.platform)
++        self.assertEqual(config['PLATFORM'], sys.platform)
+ 
+     def test_decorators(self):
+         config = {}
+@@ -419,27 +419,27 @@
+             return self.get_config(*args, configure='set_config.configure')
+ 
+         help, config = get_config(['--help'])
+-        self.assertEquals(config, {})
++        self.assertEqual(config, {})
+ 
+         config = get_config(['--set-foo'])
+         self.assertIn('FOO', config)
+-        self.assertEquals(config['FOO'], True)
++        self.assertEqual(config['FOO'], True)
+ 
+         config = get_config(['--set-bar'])
+         self.assertNotIn('FOO', config)
+         self.assertIn('BAR', config)
+-        self.assertEquals(config['BAR'], True)
++        self.assertEqual(config['BAR'], True)
+ 
+         config = get_config(['--set-value=qux'])
+         self.assertIn('VALUE', config)
+-        self.assertEquals(config['VALUE'], 'qux')
++        self.assertEqual(config['VALUE'], 'qux')
+ 
+         config = get_config(['--set-name=hoge'])
+         self.assertIn('hoge', config)
+-        self.assertEquals(config['hoge'], True)
++        self.assertEqual(config['hoge'], True)
+ 
+         config = get_config([])
+-        self.assertEquals(config, {'BAR': False})
++        self.assertEqual(config, {'BAR': False})
+ 
+         with self.assertRaises(ConfigureError):
+             # Both --set-foo and --set-name=FOO are going to try to
+@@ -454,11 +454,11 @@
+             set_config('QUX', 'qux', when='--with-qux')
+         '''):
+             config = self.get_config()
+-            self.assertEquals(config, {
++            self.assertEqual(config, {
+                 'FOO': 'foo',
+             })
+             config = self.get_config(['--with-qux'])
+-            self.assertEquals(config, {
++            self.assertEqual(config, {
+                 'FOO': 'foo',
+                 'QUX': 'qux',
+             })
+@@ -468,27 +468,27 @@
+             return self.get_config(*args, configure='set_define.configure')
+ 
+         help, config = get_config(['--help'])
+-        self.assertEquals(config, {'DEFINES': {}})
++        self.assertEqual(config, {'DEFINES': {}})
+ 
+         config = get_config(['--set-foo'])
+         self.assertIn('FOO', config['DEFINES'])
+-        self.assertEquals(config['DEFINES']['FOO'], True)
++        self.assertEqual(config['DEFINES']['FOO'], True)
+ 
+         config = get_config(['--set-bar'])
+         self.assertNotIn('FOO', config['DEFINES'])
+         self.assertIn('BAR', config['DEFINES'])
+-        self.assertEquals(config['DEFINES']['BAR'], True)
++        self.assertEqual(config['DEFINES']['BAR'], True)
+ 
+         config = get_config(['--set-value=qux'])
+         self.assertIn('VALUE', config['DEFINES'])
+-        self.assertEquals(config['DEFINES']['VALUE'], 'qux')
++        self.assertEqual(config['DEFINES']['VALUE'], 'qux')
+ 
+         config = get_config(['--set-name=hoge'])
+         self.assertIn('hoge', config['DEFINES'])
+-        self.assertEquals(config['DEFINES']['hoge'], True)
++        self.assertEqual(config['DEFINES']['hoge'], True)
+ 
+         config = get_config([])
+-        self.assertEquals(config['DEFINES'], {'BAR': False})
++        self.assertEqual(config['DEFINES'], {'BAR': False})
+ 
+         with self.assertRaises(ConfigureError):
+             # Both --set-foo and --set-name=FOO are going to try to
+@@ -503,11 +503,11 @@
+             set_define('QUX', 'qux', when='--with-qux')
+         '''):
+             config = self.get_config()
+-            self.assertEquals(config['DEFINES'], {
++            self.assertEqual(config['DEFINES'], {
+                 'FOO': 'foo',
+             })
+             config = self.get_config(['--with-qux'])
+-            self.assertEquals(config['DEFINES'], {
++            self.assertEqual(config['DEFINES'], {
+                 'FOO': 'foo',
+                 'QUX': 'qux',
+             })
+@@ -518,19 +518,19 @@
+                 *args, configure='imply_option/simple.configure')
+ 
+         help, config = get_config(['--help'])
+-        self.assertEquals(config, {})
++        self.assertEqual(config, {})
+ 
+         config = get_config([])
+-        self.assertEquals(config, {})
++        self.assertEqual(config, {})
+ 
+         config = get_config(['--enable-foo'])
+         self.assertIn('BAR', config)
+-        self.assertEquals(config['BAR'], PositiveOptionValue())
++        self.assertEqual(config['BAR'], PositiveOptionValue())
+ 
+         with self.assertRaises(InvalidOptionError) as e:
+             get_config(['--enable-foo', '--disable-bar'])
+ 
+-        self.assertEquals(
++        self.assertEqual(
+             e.exception.message,
+             "'--enable-bar' implied by '--enable-foo' conflicts with "
+             "'--disable-bar' from the command-line")
+@@ -541,31 +541,31 @@
+                 *args, configure='imply_option/negative.configure')
+ 
+         help, config = get_config(['--help'])
+-        self.assertEquals(config, {})
++        self.assertEqual(config, {})
+ 
+         config = get_config([])
+-        self.assertEquals(config, {})
++        self.assertEqual(config, {})
+ 
+         config = get_config(['--enable-foo'])
+         self.assertIn('BAR', config)
+-        self.assertEquals(config['BAR'], NegativeOptionValue())
++        self.assertEqual(config['BAR'], NegativeOptionValue())
+ 
+         with self.assertRaises(InvalidOptionError) as e:
+             get_config(['--enable-foo', '--enable-bar'])
+ 
+-        self.assertEquals(
++        self.assertEqual(
+             e.exception.message,
+             "'--disable-bar' implied by '--enable-foo' conflicts with "
+             "'--enable-bar' from the command-line")
+ 
+         config = get_config(['--disable-hoge'])
+         self.assertIn('BAR', config)
+-        self.assertEquals(config['BAR'], NegativeOptionValue())
++        self.assertEqual(config['BAR'], NegativeOptionValue())
+ 
+         with self.assertRaises(InvalidOptionError) as e:
+             get_config(['--disable-hoge', '--enable-bar'])
+ 
+-        self.assertEquals(
++        self.assertEqual(
+             e.exception.message,
+             "'--disable-bar' implied by '--disable-hoge' conflicts with "
+             "'--enable-bar' from the command-line")
+@@ -576,23 +576,23 @@
+                 *args, configure='imply_option/values.configure')
+ 
+         help, config = get_config(['--help'])
+-        self.assertEquals(config, {})
++        self.assertEqual(config, {})
+ 
+         config = get_config([])
+-        self.assertEquals(config, {})
++        self.assertEqual(config, {})
+ 
+         config = get_config(['--enable-foo=a'])
+         self.assertIn('BAR', config)
+-        self.assertEquals(config['BAR'], PositiveOptionValue(('a',)))
++        self.assertEqual(config['BAR'], PositiveOptionValue(('a',)))
+ 
+         config = get_config(['--enable-foo=a,b'])
+         self.assertIn('BAR', config)
+-        self.assertEquals(config['BAR'], PositiveOptionValue(('a','b')))
++        self.assertEqual(config['BAR'], PositiveOptionValue(('a','b')))
+ 
+         with self.assertRaises(InvalidOptionError) as e:
+             get_config(['--enable-foo=a,b', '--disable-bar'])
+ 
+-        self.assertEquals(
++        self.assertEqual(
+             e.exception.message,
+             "'--enable-bar=a,b' implied by '--enable-foo' conflicts with "
+             "'--disable-bar' from the command-line")
+@@ -603,15 +603,15 @@
+                 *args, configure='imply_option/infer.configure')
+ 
+         help, config = get_config(['--help'])
+-        self.assertEquals(config, {})
++        self.assertEqual(config, {})
+ 
+         config = get_config([])
+-        self.assertEquals(config, {})
++        self.assertEqual(config, {})
+ 
+         with self.assertRaises(InvalidOptionError) as e:
+             get_config(['--enable-foo', '--disable-bar'])
+ 
+-        self.assertEquals(
++        self.assertEqual(
+             e.exception.message,
+             "'--enable-bar' implied by '--enable-foo' conflicts with "
+             "'--disable-bar' from the command-line")
+@@ -619,7 +619,7 @@
+         with self.assertRaises(ConfigureError) as e:
+             self.get_config([], configure='imply_option/infer_ko.configure')
+ 
+-        self.assertEquals(
++        self.assertEqual(
+             e.exception.message,
+             "Cannot infer what implies '--enable-bar'. Please add a `reason` "
+             "to the `imply_option` call.")
+@@ -630,25 +630,25 @@
+                 *args, configure='imply_option/imm.configure')
+ 
+         help, config = get_config(['--help'])
+-        self.assertEquals(config, {})
++        self.assertEqual(config, {})
+ 
+         config = get_config([])
+-        self.assertEquals(config, {})
++        self.assertEqual(config, {})
+ 
+         config_path = mozpath.abspath(
+             mozpath.join(test_data_path, 'imply_option', 'imm.configure'))
+ 
+-        with self.assertRaisesRegexp(InvalidOptionError,
++        with self.assertRaisesRegex(InvalidOptionError,
+             "--enable-foo' implied by 'imply_option at %s:7' conflicts with "
+             "'--disable-foo' from the command-line" % config_path):
+             get_config(['--disable-foo'])
+ 
+-        with self.assertRaisesRegexp(InvalidOptionError,
++        with self.assertRaisesRegex(InvalidOptionError,
+             "--enable-bar=foo,bar' implied by 'imply_option at %s:16' conflicts"
+             " with '--enable-bar=a,b,c' from the command-line" % config_path):
+             get_config(['--enable-bar=a,b,c'])
+ 
+-        with self.assertRaisesRegexp(InvalidOptionError,
++        with self.assertRaisesRegex(InvalidOptionError,
+             "--enable-baz=BAZ' implied by 'imply_option at %s:25' conflicts"
+             " with '--enable-baz=QUUX' from the command-line" % config_path):
+             get_config(['--enable-baz=QUUX'])
+@@ -660,7 +660,7 @@
+             '''):
+                 self.get_config()
+ 
+-        self.assertEquals(e.exception.message,
++        self.assertEqual(e.exception.message,
+                           "`--with-foo`, emitted from `%s` line 2, is unknown."
+                           % mozpath.join(test_data_path, 'moz.configure'))
+ 
+@@ -675,7 +675,7 @@
+             '''):
+                 self.get_config()
+ 
+-        self.assertEquals(e.exception.message,
++        self.assertEqual(e.exception.message,
+                           "Unexpected type: 'int'")
+ 
+     def test_imply_option_when(self):
+@@ -686,12 +686,12 @@
+             set_config('QUX', depends('--with-qux')(lambda x: x))
+         '''):
+             config = self.get_config()
+-            self.assertEquals(config, {
++            self.assertEqual(config, {
+                 'QUX': NegativeOptionValue(),
+             })
+ 
+             config = self.get_config(['--with-foo'])
+-            self.assertEquals(config, {
++            self.assertEqual(config, {
+                 'QUX': PositiveOptionValue(),
+             })
+ 
+@@ -700,7 +700,7 @@
+             with self.moz_configure('option("--with-foo", help="foo")'):
+                 self.get_config()
+ 
+-        self.assertEquals(
++        self.assertEqual(
+             e.exception.message,
+             'Option `--with-foo` is not handled ; reference it with a @depends'
+         )
+@@ -712,7 +712,7 @@
+             '''):
+                 self.get_config()
+ 
+-        self.assertEquals(
++        self.assertEqual(
+             e.exception.message,
+             'Option `--with-foo` already defined'
+         )
+@@ -724,7 +724,7 @@
+             '''):
+                 self.get_config()
+ 
+-        self.assertEquals(
++        self.assertEqual(
+             e.exception.message,
+             'Option `MOZ_FOO` already defined'
+         )
+@@ -736,7 +736,7 @@
+             '''):
+                 self.get_config()
+ 
+-        self.assertEquals(
++        self.assertEqual(
+             e.exception.message,
+             'Option `MOZ_FOO` already defined'
+         )
+@@ -748,7 +748,7 @@
+             '''):
+                 self.get_config()
+ 
+-        self.assertEquals(
++        self.assertEqual(
+             e.exception.message,
+             'Option `MOZ_FOO` already defined'
+         )
+@@ -760,7 +760,7 @@
+             '''):
+                 self.get_config()
+ 
+-        self.assertEquals(
++        self.assertEqual(
+             e.exception.message,
+             'Option `--with-foo` already defined'
+         )
+@@ -776,18 +776,18 @@
+             set_config('QUX', depends('--with-qux', when='--with-foo')(lambda x: x))
+         '''):
+             config = self.get_config()
+-            self.assertEquals(config, {
++            self.assertEqual(config, {
+                 'FOO': NegativeOptionValue(),
+             })
+ 
+             config = self.get_config(['--with-foo'])
+-            self.assertEquals(config, {
++            self.assertEqual(config, {
+                 'FOO': PositiveOptionValue(),
+                 'QUX': NegativeOptionValue(),
+             })
+ 
+             config = self.get_config(['--with-foo', '--with-qux'])
+-            self.assertEquals(config, {
++            self.assertEqual(config, {
+                 'FOO': PositiveOptionValue(),
+                 'QUX': PositiveOptionValue(),
+             })
+@@ -795,7 +795,7 @@
+             with self.assertRaises(InvalidOptionError) as e:
+                 self.get_config(['--with-bar'])
+ 
+-            self.assertEquals(
++            self.assertEqual(
+                 e.exception.message,
+                 '--with-bar is not available in this configuration'
+             )
+@@ -803,7 +803,7 @@
+             with self.assertRaises(InvalidOptionError) as e:
+                 self.get_config(['--with-qux'])
+ 
+-            self.assertEquals(
++            self.assertEqual(
+                 e.exception.message,
+                 '--with-qux is not available in this configuration'
+             )
+@@ -811,18 +811,18 @@
+             with self.assertRaises(InvalidOptionError) as e:
+                 self.get_config(['QUX=1'])
+ 
+-            self.assertEquals(
++            self.assertEqual(
+                 e.exception.message,
+                 'QUX is not available in this configuration'
+             )
+ 
+             config = self.get_config(env={'QUX': '1'})
+-            self.assertEquals(config, {
++            self.assertEqual(config, {
+                 'FOO': NegativeOptionValue(),
+             })
+ 
+             help, config = self.get_config(['--help'])
+-            self.assertEquals(help, textwrap.dedent('''\
++            self.assertEqual(help, textwrap.dedent('''\
+                 Usage: configure [options]
+ 
+                 Options: [defaults in brackets after descriptions]
+@@ -833,7 +833,7 @@
+             '''))
+ 
+             help, config = self.get_config(['--help', '--with-foo'])
+-            self.assertEquals(help, textwrap.dedent('''\
++            self.assertEqual(help, textwrap.dedent('''\
+                 Usage: configure [options]
+ 
+                 Options: [defaults in brackets after descriptions]
+@@ -851,7 +851,7 @@
+             with self.assertRaises(ConfigureError) as e:
+                 self.get_config()
+ 
+-            self.assertEquals(e.exception.message,
++            self.assertEqual(e.exception.message,
+                               '@depends function needs the same `when` as '
+                               'options it depends on')
+ 
+@@ -868,7 +868,7 @@
+             with self.assertRaises(ConfigureError) as e:
+                 self.get_config()
+ 
+-            self.assertEquals(e.exception.message,
++            self.assertEqual(e.exception.message,
+                               '@depends function needs the same `when` as '
+                               'options it depends on')
+ 
+@@ -877,7 +877,7 @@
+             with self.moz_configure('include("../foo.configure")'):
+                 self.get_config()
+ 
+-        self.assertEquals(
++        self.assertEqual(
+             e.exception.message,
+             'Cannot include `%s` because it is not in a subdirectory of `%s`'
+             % (mozpath.normpath(mozpath.join(test_data_path, '..',
+@@ -892,7 +892,7 @@
+             '''):
+                 self.get_config()
+ 
+-        self.assertEquals(
++        self.assertEqual(
+             e.exception.message,
+             'Cannot include `%s` because it was included already.'
+             % mozpath.normpath(mozpath.join(test_data_path,
+@@ -905,7 +905,7 @@
+             '''):
+                 self.get_config()
+ 
+-        self.assertEquals(e.exception.message, "Unexpected type: 'int'")
++        self.assertEqual(e.exception.message, "Unexpected type: 'int'")
+ 
+     def test_include_when(self):
+         with MockedOpen({
+@@ -948,26 +948,26 @@
+             '''),
+         }):
+             config = self.get_config()
+-            self.assertEquals(config, {})
++            self.assertEqual(config, {})
+ 
+             config = self.get_config(['--with-foo'])
+-            self.assertEquals(config, {})
++            self.assertEqual(config, {})
+ 
+             config = self.get_config(['--with-bar'])
+-            self.assertEquals(config, {
++            self.assertEqual(config, {
+                 'BAR': 'bar',
+             })
+ 
+             with self.assertRaises(InvalidOptionError) as e:
+                 self.get_config(['--with-qux'])
+ 
+-            self.assertEquals(
++            self.assertEqual(
+                 e.exception.message,
+                 '--with-qux is not available in this configuration'
+             )
+ 
+             config = self.get_config(['--with-foo', '--with-foo-really'])
+-            self.assertEquals(config, {
++            self.assertEqual(config, {
+                 'FOO': 'foo',
+                 'FOO2': True,
+             })
+@@ -979,7 +979,7 @@
+             '''):
+                 self.get_config()
+ 
+-        self.assertEquals(e.exception.message, 'Cannot reassign builtins')
++        self.assertEqual(e.exception.message, 'Cannot reassign builtins')
+ 
+         with self.assertRaises(KeyError) as e:
+             with self.moz_configure('''
+@@ -987,7 +987,7 @@
+             '''):
+                 self.get_config()
+ 
+-        self.assertEquals(e.exception.message,
++        self.assertEqual(e.exception.message,
+                           'Cannot assign `foo` because it is neither a '
+                           '@depends nor a @template')
+ 
+@@ -1000,7 +1000,7 @@
+             '''):
+                 self.get_config()
+ 
+-        self.assertEquals(e.exception.message,
++        self.assertEqual(e.exception.message,
+                           "@depends needs at least one argument")
+ 
+         with self.assertRaises(ConfigureError) as e:
+@@ -1011,7 +1011,7 @@
+             '''):
+                 self.get_config()
+ 
+-        self.assertEquals(e.exception.message,
++        self.assertEqual(e.exception.message,
+                           "'--with-foo' is not a known option. Maybe it's "
+                           "declared too late?")
+ 
+@@ -1023,7 +1023,7 @@
+             '''):
+                 self.get_config()
+ 
+-        self.assertEquals(e.exception.message,
++        self.assertEqual(e.exception.message,
+                           "Option must not contain an '='")
+ 
+         with self.assertRaises(TypeError) as e:
+@@ -1034,7 +1034,7 @@
+             '''):
+                 self.get_config()
+ 
+-        self.assertEquals(e.exception.message,
++        self.assertEqual(e.exception.message,
+                           "Cannot use object of type 'int' as argument "
+                           "to @depends")
+ 
+@@ -1046,7 +1046,7 @@
+             '''):
+                 self.get_config()
+ 
+-        self.assertEquals(e.exception.message,
++        self.assertEqual(e.exception.message,
+                           "Cannot decorate generator functions with @depends")
+ 
+         with self.assertRaises(TypeError) as e:
+@@ -1055,7 +1055,7 @@
+             '''):
+                 self.get_config()
+ 
+-        self.assertEquals(e.exception.message,
++        self.assertEqual(e.exception.message,
+                           "Unexpected type: 'int'")
+ 
+         with self.assertRaises(ConfigureError) as e:
+@@ -1069,7 +1069,7 @@
+             '''):
+                 self.get_config()
+ 
+-        self.assertEquals(e.exception.message,
++        self.assertEqual(e.exception.message,
+                           "The `foo` function may not be called")
+ 
+         with self.assertRaises(TypeError) as e:
+@@ -1080,7 +1080,7 @@
+             '''):
+                 self.get_config()
+ 
+-        self.assertEquals(e.exception.message,
++        self.assertEqual(e.exception.message,
+                           "depends_impl() got an unexpected keyword argument 'foo'")
+ 
+     def test_depends_when(self):
+@@ -1105,12 +1105,12 @@
+             set_config('QUX', qux)
+         '''):
+             config = self.get_config()
+-            self.assertEquals(config, {
++            self.assertEqual(config, {
+                 'FOO': 'foo',
+             })
+ 
+             config = self.get_config(['--with-qux'])
+-            self.assertEquals(config, {
++            self.assertEqual(config, {
+                 'FOO': 'foo',
+                 'QUX': 'qux',
+             })
+@@ -1125,7 +1125,7 @@
+             '''):
+                 self.get_config()
+ 
+-        self.assertEquals(e.exception.message,
++        self.assertEqual(e.exception.message,
+                           '@imports must appear after @template')
+ 
+         with self.assertRaises(ConfigureError) as e:
+@@ -1138,7 +1138,7 @@
+             '''):
+                 self.get_config()
+ 
+-        self.assertEquals(e.exception.message,
++        self.assertEqual(e.exception.message,
+                           '@imports must appear after @depends')
+ 
+         for import_ in (
+@@ -1155,7 +1155,7 @@
+                 ''' % import_):
+                     self.get_config()
+ 
+-            self.assertEquals(e.exception.message, "Unexpected type: 'int'")
++            self.assertEqual(e.exception.message, "Unexpected type: 'int'")
+ 
+         with self.assertRaises(TypeError) as e:
+             with self.moz_configure('''
+@@ -1166,7 +1166,7 @@
+             '''):
+                 self.get_config()
+ 
+-        self.assertEquals(e.exception.message, "Unexpected type: 'int'")
++        self.assertEqual(e.exception.message, "Unexpected type: 'int'")
+ 
+         with self.assertRaises(ValueError) as e:
+             with self.moz_configure('''
+@@ -1176,7 +1176,7 @@
+             '''):
+                 self.get_config()
+ 
+-        self.assertEquals(e.exception.message,
++        self.assertEqual(e.exception.message,
+                           "Invalid argument to @imports: 'os*'")
+ 
+     def test_only_when(self):
+@@ -1231,7 +1231,7 @@
+             with self.assertRaises(InvalidOptionError) as e:
+                 self.get_config(['--foo'])
+ 
+-            self.assertEquals(e.exception.message,
++            self.assertEqual(e.exception.message,
+                               '--foo is not available in this configuration')
+ 
+         # Cannot depend on an option defined in a only_when block, because we
+@@ -1242,7 +1242,7 @@
+             with self.assertRaises(ConfigureError) as e:
+                 self.get_config()
+ 
+-            self.assertEquals(e.exception.message,
++            self.assertEqual(e.exception.message,
+                               '@depends function needs the same `when` as '
+                               'options it depends on')
+ 
+@@ -1259,7 +1259,7 @@
+             with self.assertRaises(InvalidOptionError) as e:
+                 self.get_config()
+ 
+-            self.assertEquals(e.exception.message,
++            self.assertEqual(e.exception.message,
+                               '--foo is not available in this configuration')
+ 
+         # And similarly doesn't fail when the condition is true.
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/configure/test_lint.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/configure/test_lint.py	(refactored)
+@@ -2,9 +2,9 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import absolute_import, print_function, unicode_literals
+ 
+-from StringIO import StringIO
++
++from io import StringIO
+ import os
+ import textwrap
+ import unittest
+@@ -62,7 +62,7 @@
+             '''):
+                 self.lint_test()
+ 
+-        self.assertEquals(e.exception.message,
++        self.assertEqual(e.exception.message,
+                           "`bar` depends on '--help' and `foo`. "
+                           "`foo` must depend on '--help'")
+ 
+@@ -85,7 +85,7 @@
+             '''):
+                 self.lint_test()
+ 
+-        self.assertEquals(e.exception.message,
++        self.assertEqual(e.exception.message,
+                           "`bar` depends on '--help' and `foo`. "
+                           "`foo` must depend on '--help'")
+ 
+@@ -111,7 +111,7 @@
+             '''):
+                 self.lint_test()
+ 
+-        self.assertEquals(e.exception.message,
++        self.assertEqual(e.exception.message,
+                           "Missing @depends for `foo`: '--help'")
+ 
+         # There is a default restricted `os` module when there is no explicit
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/configure/test_moz_configure.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/configure/test_moz_configure.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import absolute_import, print_function, unicode_literals
++
+ 
+ from mozunit import main
+ from mozpack import path as mozpath
+@@ -23,10 +23,10 @@
+             shell = mozpath.abspath('/bin/sh')
+             return result.replace('CONFIG_SHELL=%s ' % shell, '')
+ 
+-        self.assertEquals('--enable-application=browser',
++        self.assertEqual('--enable-application=browser',
+                           get_value_for(['--enable-application=browser']))
+ 
+-        self.assertEquals('--enable-application=browser '
++        self.assertEqual('--enable-application=browser '
+                           'MOZ_PROFILING=1',
+                           get_value_for(['--enable-application=browser',
+                                          'MOZ_PROFILING=1']))
+@@ -35,25 +35,25 @@
+             environ={'MOZ_PROFILING': '1'},
+             mozconfig='ac_add_options --enable-project=js')
+ 
+-        self.assertEquals('--enable-project=js MOZ_PROFILING=1',
++        self.assertEqual('--enable-project=js MOZ_PROFILING=1',
+                           value)
+ 
+         # --disable-js-shell is the default, so it's filtered out.
+-        self.assertEquals('--enable-application=browser',
++        self.assertEqual('--enable-application=browser',
+                           get_value_for(['--enable-application=browser',
+                                          '--disable-js-shell']))
+ 
+         # Normally, --without-foo would be filtered out because that's the
+         # default, but since it is a (fake) old-configure option, it always
+         # appears.
+-        self.assertEquals('--enable-application=browser --without-foo',
++        self.assertEqual('--enable-application=browser --without-foo',
+                           get_value_for(['--enable-application=browser',
+                                          '--without-foo']))
+-        self.assertEquals('--enable-application=browser --with-foo',
++        self.assertEqual('--enable-application=browser --with-foo',
+                           get_value_for(['--enable-application=browser',
+                                          '--with-foo']))
+ 
+-        self.assertEquals("--enable-application=browser '--with-foo=foo bar'",
++        self.assertEqual("--enable-application=browser '--with-foo=foo bar'",
+                           get_value_for(['--enable-application=browser',
+                                          '--with-foo=foo bar']))
+ 
+@@ -65,7 +65,7 @@
+                 self.version = version
+ 
+             def __call__(self, stdin, args):
+-                this.assertEquals(args, ('-version',))
++                this.assertEqual(args, ('-version',))
+                 return 0, self.version, ''
+ 
+         def check_nsis_version(version):
+@@ -80,13 +80,13 @@
+         with self.assertRaises(SystemExit) as e:
+             check_nsis_version('v3.0a2')
+ 
+-        self.assertEquals(check_nsis_version('v3.0b1'), '3.0b1')
+-        self.assertEquals(check_nsis_version('v3.0b2'), '3.0b2')
+-        self.assertEquals(check_nsis_version('v3.0rc1'), '3.0rc1')
+-        self.assertEquals(check_nsis_version('v3.0'), '3.0')
+-        self.assertEquals(check_nsis_version('v3.0-2'), '3.0')
+-        self.assertEquals(check_nsis_version('v3.0.1'), '3.0')
+-        self.assertEquals(check_nsis_version('v3.1'), '3.1')
++        self.assertEqual(check_nsis_version('v3.0b1'), '3.0b1')
++        self.assertEqual(check_nsis_version('v3.0b2'), '3.0b2')
++        self.assertEqual(check_nsis_version('v3.0rc1'), '3.0rc1')
++        self.assertEqual(check_nsis_version('v3.0'), '3.0')
++        self.assertEqual(check_nsis_version('v3.0-2'), '3.0')
++        self.assertEqual(check_nsis_version('v3.0.1'), '3.0')
++        self.assertEqual(check_nsis_version('v3.1'), '3.1')
+ 
+ 
+ if __name__ == '__main__':
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/configure/test_options.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/configure/test_options.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import absolute_import, print_function, unicode_literals
++
+ 
+ import unittest
+ 
+@@ -27,139 +27,139 @@
+ class TestOption(unittest.TestCase):
+     def test_option(self):
+         option = Option('--option')
+-        self.assertEquals(option.prefix, '')
+-        self.assertEquals(option.name, 'option')
+-        self.assertEquals(option.env, None)
++        self.assertEqual(option.prefix, '')
++        self.assertEqual(option.name, 'option')
++        self.assertEqual(option.env, None)
+         self.assertFalse(option.default)
+ 
+         option = Option('--enable-option')
+-        self.assertEquals(option.prefix, 'enable')
+-        self.assertEquals(option.name, 'option')
+-        self.assertEquals(option.env, None)
++        self.assertEqual(option.prefix, 'enable')
++        self.assertEqual(option.name, 'option')
++        self.assertEqual(option.env, None)
+         self.assertFalse(option.default)
+ 
+         option = Option('--disable-option')
+-        self.assertEquals(option.prefix, 'disable')
+-        self.assertEquals(option.name, 'option')
+-        self.assertEquals(option.env, None)
++        self.assertEqual(option.prefix, 'disable')
++        self.assertEqual(option.name, 'option')
++        self.assertEqual(option.env, None)
+         self.assertTrue(option.default)
+ 
+         option = Option('--with-option')
+-        self.assertEquals(option.prefix, 'with')
+-        self.assertEquals(option.name, 'option')
+-        self.assertEquals(option.env, None)
++        self.assertEqual(option.prefix, 'with')
++        self.assertEqual(option.name, 'option')
++        self.assertEqual(option.env, None)
+         self.assertFalse(option.default)
+ 
+         option = Option('--without-option')
+-        self.assertEquals(option.prefix, 'without')
+-        self.assertEquals(option.name, 'option')
+-        self.assertEquals(option.env, None)
++        self.assertEqual(option.prefix, 'without')
++        self.assertEqual(option.name, 'option')
++        self.assertEqual(option.env, None)
+         self.assertTrue(option.default)
+ 
+         option = Option('--without-option-foo', env='MOZ_OPTION')
+-        self.assertEquals(option.env, 'MOZ_OPTION')
++        self.assertEqual(option.env, 'MOZ_OPTION')
+ 
+         option = Option(env='MOZ_OPTION')
+-        self.assertEquals(option.prefix, '')
+-        self.assertEquals(option.name, None)
+-        self.assertEquals(option.env, 'MOZ_OPTION')
++        self.assertEqual(option.prefix, '')
++        self.assertEqual(option.name, None)
++        self.assertEqual(option.env, 'MOZ_OPTION')
+         self.assertFalse(option.default)
+ 
+         with self.assertRaises(InvalidOptionError) as e:
+             Option('--option', nargs=0, default=('a',))
+-        self.assertEquals(e.exception.message,
++        self.assertEqual(e.exception.message,
+                           "The given `default` doesn't satisfy `nargs`")
+ 
+         with self.assertRaises(InvalidOptionError) as e:
+             Option('--option', nargs=1, default=())
+-        self.assertEquals(
++        self.assertEqual(
+             e.exception.message,
+             'default must be a bool, a string or a tuple of strings')
+ 
+         with self.assertRaises(InvalidOptionError) as e:
+             Option('--option', nargs=1, default=True)
+-        self.assertEquals(e.exception.message,
++        self.assertEqual(e.exception.message,
+                           "The given `default` doesn't satisfy `nargs`")
+ 
+         with self.assertRaises(InvalidOptionError) as e:
+             Option('--option', nargs=1, default=('a', 'b'))
+-        self.assertEquals(e.exception.message,
++        self.assertEqual(e.exception.message,
+                           "The given `default` doesn't satisfy `nargs`")
+ 
+         with self.assertRaises(InvalidOptionError) as e:
+             Option('--option', nargs=2, default=())
+-        self.assertEquals(
++        self.assertEqual(
+             e.exception.message,
+             'default must be a bool, a string or a tuple of strings')
+ 
+         with self.assertRaises(InvalidOptionError) as e:
+             Option('--option', nargs=2, default=True)
+-        self.assertEquals(e.exception.message,
++        self.assertEqual(e.exception.message,
+                           "The given `default` doesn't satisfy `nargs`")
+ 
+         with self.assertRaises(InvalidOptionError) as e:
+             Option('--option', nargs=2, default=('a',))
+-        self.assertEquals(e.exception.message,
++        self.assertEqual(e.exception.message,
+                           "The given `default` doesn't satisfy `nargs`")
+ 
+         with self.assertRaises(InvalidOptionError) as e:
+             Option('--option', nargs='?', default=('a', 'b'))
+-        self.assertEquals(e.exception.message,
++        self.assertEqual(e.exception.message,
+                           "The given `default` doesn't satisfy `nargs`")
+ 
+         with self.assertRaises(InvalidOptionError) as e:
+             Option('--option', nargs='+', default=())
+-        self.assertEquals(
++        self.assertEqual(
+             e.exception.message,
+             'default must be a bool, a string or a tuple of strings')
+ 
+         with self.assertRaises(InvalidOptionError) as e:
+             Option('--option', nargs='+', default=True)
+-        self.assertEquals(e.exception.message,
++        self.assertEqual(e.exception.message,
+                           "The given `default` doesn't satisfy `nargs`")
+ 
+         # --disable options with a nargs value that requires at least one
+         # argument need to be given a default.
+         with self.assertRaises(InvalidOptionError) as e:
+             Option('--disable-option', nargs=1)
+-        self.assertEquals(e.exception.message,
++        self.assertEqual(e.exception.message,
+                           "The given `default` doesn't satisfy `nargs`")
+ 
+         with self.assertRaises(InvalidOptionError) as e:
+             Option('--disable-option', nargs='+')
+-        self.assertEquals(e.exception.message,
++        self.assertEqual(e.exception.message,
+                           "The given `default` doesn't satisfy `nargs`")
+ 
+         # Test nargs inference from default value
+         option = Option('--with-foo', default=True)
+-        self.assertEquals(option.nargs, 0)
++        self.assertEqual(option.nargs, 0)
+ 
+         option = Option('--with-foo', default=False)
+-        self.assertEquals(option.nargs, 0)
++        self.assertEqual(option.nargs, 0)
+ 
+         option = Option('--with-foo', default='a')
+-        self.assertEquals(option.nargs, '?')
++        self.assertEqual(option.nargs, '?')
+ 
+         option = Option('--with-foo', default=('a',))
+-        self.assertEquals(option.nargs, '?')
++        self.assertEqual(option.nargs, '?')
+ 
+         option = Option('--with-foo', default=('a', 'b'))
+-        self.assertEquals(option.nargs, '*')
++        self.assertEqual(option.nargs, '*')
+ 
+         option = Option(env='FOO', default=True)
+-        self.assertEquals(option.nargs, 0)
++        self.assertEqual(option.nargs, 0)
+ 
+         option = Option(env='FOO', default=False)
+-        self.assertEquals(option.nargs, 0)
++        self.assertEqual(option.nargs, 0)
+ 
+         option = Option(env='FOO', default='a')
+-        self.assertEquals(option.nargs, '?')
++        self.assertEqual(option.nargs, '?')
+ 
+         option = Option(env='FOO', default=('a',))
+-        self.assertEquals(option.nargs, '?')
++        self.assertEqual(option.nargs, '?')
+ 
+         option = Option(env='FOO', default=('a', 'b'))
+-        self.assertEquals(option.nargs, '*')
++        self.assertEqual(option.nargs, '*')
+ 
+     def test_option_option(self):
+         for option in (
+@@ -169,70 +169,70 @@
+             '--with-option',
+             '--without-option',
+         ):
+-            self.assertEquals(Option(option).option, option)
+-            self.assertEquals(Option(option, env='FOO').option, option)
++            self.assertEqual(Option(option).option, option)
++            self.assertEqual(Option(option, env='FOO').option, option)
+ 
+             opt = Option(option, default=False)
+-            self.assertEquals(opt.option,
++            self.assertEqual(opt.option,
+                               option.replace('-disable-', '-enable-')
+                                     .replace('-without-', '-with-'))
+ 
+             opt = Option(option, default=True)
+-            self.assertEquals(opt.option,
++            self.assertEqual(opt.option,
+                               option.replace('-enable-', '-disable-')
+                                     .replace('-with-', '-without-'))
+ 
+-        self.assertEquals(Option(env='FOO').option, 'FOO')
++        self.assertEqual(Option(env='FOO').option, 'FOO')
+ 
+     def test_option_choices(self):
+         with self.assertRaises(InvalidOptionError) as e:
+             Option('--option', nargs=3, choices=('a', 'b'))
+-        self.assertEquals(e.exception.message,
++        self.assertEqual(e.exception.message,
+                           'Not enough `choices` for `nargs`')
+ 
+         with self.assertRaises(InvalidOptionError) as e:
+             Option('--without-option', nargs=1, choices=('a', 'b'))
+-        self.assertEquals(e.exception.message,
++        self.assertEqual(e.exception.message,
+                           'A `default` must be given along with `choices`')
+ 
+         with self.assertRaises(InvalidOptionError) as e:
+             Option('--without-option', nargs='+', choices=('a', 'b'))
+-        self.assertEquals(e.exception.message,
++        self.assertEqual(e.exception.message,
+                           'A `default` must be given along with `choices`')
+ 
+         with self.assertRaises(InvalidOptionError) as e:
+             Option('--without-option', default='c', choices=('a', 'b'))
+-        self.assertEquals(e.exception.message,
++        self.assertEqual(e.exception.message,
+                           "The `default` value must be one of 'a', 'b'")
+ 
+         with self.assertRaises(InvalidOptionError) as e:
+             Option('--without-option', default=('a', 'c',), choices=('a', 'b'))
+-        self.assertEquals(e.exception.message,
++        self.assertEqual(e.exception.message,
+                           "The `default` value must be one of 'a', 'b'")
+ 
+         with self.assertRaises(InvalidOptionError) as e:
+             Option('--without-option', default=('c',), choices=('a', 'b'))
+-        self.assertEquals(e.exception.message,
++        self.assertEqual(e.exception.message,
+                           "The `default` value must be one of 'a', 'b'")
+ 
+         option = Option('--with-option', nargs='+', choices=('a', 'b'))
+         with self.assertRaises(InvalidOptionError) as e:
+             option.get_value('--with-option=c')
+-        self.assertEquals(e.exception.message, "'c' is not one of 'a', 'b'")
++        self.assertEqual(e.exception.message, "'c' is not one of 'a', 'b'")
+ 
+         value = option.get_value('--with-option=b,a')
+         self.assertTrue(value)
+-        self.assertEquals(PositiveOptionValue(('b', 'a')), value)
++        self.assertEqual(PositiveOptionValue(('b', 'a')), value)
+ 
+         option = Option('--without-option', nargs='*', default='a',
+                         choices=('a', 'b'))
+         with self.assertRaises(InvalidOptionError) as e:
+             option.get_value('--with-option=c')
+-        self.assertEquals(e.exception.message, "'c' is not one of 'a', 'b'")
++        self.assertEqual(e.exception.message, "'c' is not one of 'a', 'b'")
+ 
+         value = option.get_value('--with-option=b,a')
+         self.assertTrue(value)
+-        self.assertEquals(PositiveOptionValue(('b', 'a')), value)
++        self.assertEqual(PositiveOptionValue(('b', 'a')), value)
+ 
+         # Test nargs inference from choices
+         option = Option('--with-option', choices=('a', 'b'))
+@@ -243,71 +243,71 @@
+                         choices=('a', 'b', 'c', 'd'))
+ 
+         value = option.get_value('--with-option=+d')
+-        self.assertEquals(PositiveOptionValue(('b', 'c', 'd')), value)
++        self.assertEqual(PositiveOptionValue(('b', 'c', 'd')), value)
+ 
+         value = option.get_value('--with-option=-b')
+-        self.assertEquals(PositiveOptionValue(('c',)), value)
++        self.assertEqual(PositiveOptionValue(('c',)), value)
+ 
+         value = option.get_value('--with-option=-b,+d')
+-        self.assertEquals(PositiveOptionValue(('c','d')), value)
++        self.assertEqual(PositiveOptionValue(('c','d')), value)
+ 
+         # Adding something that is in the default is fine
+         value = option.get_value('--with-option=+b')
+-        self.assertEquals(PositiveOptionValue(('b', 'c')), value)
++        self.assertEqual(PositiveOptionValue(('b', 'c')), value)
+ 
+         # Removing something that is not in the default is fine, as long as it
+         # is one of the choices
+         value = option.get_value('--with-option=-a')
+-        self.assertEquals(PositiveOptionValue(('b', 'c')), value)
++        self.assertEqual(PositiveOptionValue(('b', 'c')), value)
+ 
+         with self.assertRaises(InvalidOptionError) as e:
+             option.get_value('--with-option=-e')
+-        self.assertEquals(e.exception.message,
++        self.assertEqual(e.exception.message,
+                           "'e' is not one of 'a', 'b', 'c', 'd'")
+ 
+         # Other "not a choice" errors.
+         with self.assertRaises(InvalidOptionError) as e:
+             option.get_value('--with-option=+e')
+-        self.assertEquals(e.exception.message,
++        self.assertEqual(e.exception.message,
+                           "'e' is not one of 'a', 'b', 'c', 'd'")
+ 
+         with self.assertRaises(InvalidOptionError) as e:
+             option.get_value('--with-option=e')
+-        self.assertEquals(e.exception.message,
++        self.assertEqual(e.exception.message,
+                           "'e' is not one of 'a', 'b', 'c', 'd'")
+ 
+     def test_option_value_format(self):
+         val = PositiveOptionValue()
+-        self.assertEquals('--with-value', val.format('--with-value'))
+-        self.assertEquals('--with-value', val.format('--without-value'))
+-        self.assertEquals('--enable-value', val.format('--enable-value'))
+-        self.assertEquals('--enable-value', val.format('--disable-value'))
+-        self.assertEquals('--value', val.format('--value'))
+-        self.assertEquals('VALUE=1', val.format('VALUE'))
++        self.assertEqual('--with-value', val.format('--with-value'))
++        self.assertEqual('--with-value', val.format('--without-value'))
++        self.assertEqual('--enable-value', val.format('--enable-value'))
++        self.assertEqual('--enable-value', val.format('--disable-value'))
++        self.assertEqual('--value', val.format('--value'))
++        self.assertEqual('VALUE=1', val.format('VALUE'))
+ 
+         val = PositiveOptionValue(('a',))
+-        self.assertEquals('--with-value=a', val.format('--with-value'))
+-        self.assertEquals('--with-value=a', val.format('--without-value'))
+-        self.assertEquals('--enable-value=a', val.format('--enable-value'))
+-        self.assertEquals('--enable-value=a', val.format('--disable-value'))
+-        self.assertEquals('--value=a', val.format('--value'))
+-        self.assertEquals('VALUE=a', val.format('VALUE'))
++        self.assertEqual('--with-value=a', val.format('--with-value'))
++        self.assertEqual('--with-value=a', val.format('--without-value'))
++        self.assertEqual('--enable-value=a', val.format('--enable-value'))
++        self.assertEqual('--enable-value=a', val.format('--disable-value'))
++        self.assertEqual('--value=a', val.format('--value'))
++        self.assertEqual('VALUE=a', val.format('VALUE'))
+ 
+         val = PositiveOptionValue(('a', 'b'))
+-        self.assertEquals('--with-value=a,b', val.format('--with-value'))
+-        self.assertEquals('--with-value=a,b', val.format('--without-value'))
+-        self.assertEquals('--enable-value=a,b', val.format('--enable-value'))
+-        self.assertEquals('--enable-value=a,b', val.format('--disable-value'))
+-        self.assertEquals('--value=a,b', val.format('--value'))
+-        self.assertEquals('VALUE=a,b', val.format('VALUE'))
++        self.assertEqual('--with-value=a,b', val.format('--with-value'))
++        self.assertEqual('--with-value=a,b', val.format('--without-value'))
++        self.assertEqual('--enable-value=a,b', val.format('--enable-value'))
++        self.assertEqual('--enable-value=a,b', val.format('--disable-value'))
++        self.assertEqual('--value=a,b', val.format('--value'))
++        self.assertEqual('VALUE=a,b', val.format('VALUE'))
+ 
+         val = NegativeOptionValue()
+-        self.assertEquals('--without-value', val.format('--with-value'))
+-        self.assertEquals('--without-value', val.format('--without-value'))
+-        self.assertEquals('--disable-value', val.format('--enable-value'))
+-        self.assertEquals('--disable-value', val.format('--disable-value'))
+-        self.assertEquals('', val.format('--value'))
+-        self.assertEquals('VALUE=', val.format('VALUE'))
++        self.assertEqual('--without-value', val.format('--with-value'))
++        self.assertEqual('--without-value', val.format('--without-value'))
++        self.assertEqual('--disable-value', val.format('--enable-value'))
++        self.assertEqual('--disable-value', val.format('--disable-value'))
++        self.assertEqual('', val.format('--value'))
++        self.assertEqual('VALUE=', val.format('VALUE'))
+ 
+     def test_option_value(self, name='option', nargs=0, default=None):
+         disabled = name.startswith(('disable-', 'without-'))
+@@ -324,28 +324,28 @@
+ 
+         if nargs in (0, '?', '*') or disabled:
+             value = option.get_value('--%s' % name, 'option')
+-            self.assertEquals(value, posOptionValue())
+-            self.assertEquals(value.origin, 'option')
++            self.assertEqual(value, posOptionValue())
++            self.assertEqual(value.origin, 'option')
+         else:
+             with self.assertRaises(InvalidOptionError) as e:
+                 option.get_value('--%s' % name)
+             if nargs == 1:
+-                self.assertEquals(e.exception.message,
++                self.assertEqual(e.exception.message,
+                                   '--%s takes 1 value' % name)
+             elif nargs == '+':
+-                self.assertEquals(e.exception.message,
++                self.assertEqual(e.exception.message,
+                                   '--%s takes 1 or more values' % name)
+             else:
+-                self.assertEquals(e.exception.message,
++                self.assertEqual(e.exception.message,
+                                   '--%s takes 2 values' % name)
+ 
+         value = option.get_value('')
+-        self.assertEquals(value, defaultValue)
+-        self.assertEquals(value.origin, 'default')
++        self.assertEqual(value, defaultValue)
++        self.assertEqual(value.origin, 'default')
+ 
+         value = option.get_value(None)
+-        self.assertEquals(value, defaultValue)
+-        self.assertEquals(value.origin, 'default')
++        self.assertEqual(value, defaultValue)
++        self.assertEqual(value.origin, 'default')
+ 
+         with self.assertRaises(AssertionError):
+             value = option.get_value('MOZ_OPTION=', 'environment')
+@@ -358,47 +358,47 @@
+ 
+         if nargs in (1, '?', '*', '+') and not disabled:
+             value = option.get_value('--%s=' % name, 'option')
+-            self.assertEquals(value, PositiveOptionValue(('',)))
+-            self.assertEquals(value.origin, 'option')
++            self.assertEqual(value, PositiveOptionValue(('',)))
++            self.assertEqual(value.origin, 'option')
+         else:
+             with self.assertRaises(InvalidOptionError) as e:
+                 option.get_value('--%s=' % name)
+             if disabled:
+-                self.assertEquals(e.exception.message,
++                self.assertEqual(e.exception.message,
+                                   'Cannot pass a value to --%s' % name)
+             else:
+-                self.assertEquals(e.exception.message,
++                self.assertEqual(e.exception.message,
+                                   '--%s takes %d values' % (name, nargs))
+ 
+         if nargs in (1, '?', '*', '+') and not disabled:
+             value = option.get_value('--%s=foo' % name, 'option')
+-            self.assertEquals(value, PositiveOptionValue(('foo',)))
+-            self.assertEquals(value.origin, 'option')
++            self.assertEqual(value, PositiveOptionValue(('foo',)))
++            self.assertEqual(value.origin, 'option')
+         else:
+             with self.assertRaises(InvalidOptionError) as e:
+                 option.get_value('--%s=foo' % name)
+             if disabled:
+-                self.assertEquals(e.exception.message,
++                self.assertEqual(e.exception.message,
+                                   'Cannot pass a value to --%s' % name)
+             else:
+-                self.assertEquals(e.exception.message,
++                self.assertEqual(e.exception.message,
+                                   '--%s takes %d values' % (name, nargs))
+ 
+         if nargs in (2, '*', '+') and not disabled:
+             value = option.get_value('--%s=foo,bar' % name, 'option')
+-            self.assertEquals(value, PositiveOptionValue(('foo', 'bar')))
+-            self.assertEquals(value.origin, 'option')
++            self.assertEqual(value, PositiveOptionValue(('foo', 'bar')))
++            self.assertEqual(value.origin, 'option')
+         else:
+             with self.assertRaises(InvalidOptionError) as e:
+                 option.get_value('--%s=foo,bar' % name, 'option')
+             if disabled:
+-                self.assertEquals(e.exception.message,
++                self.assertEqual(e.exception.message,
+                                   'Cannot pass a value to --%s' % name)
+             elif nargs == '?':
+-                self.assertEquals(e.exception.message,
++                self.assertEqual(e.exception.message,
+                                   '--%s takes 0 or 1 values' % name)
+             else:
+-                self.assertEquals(e.exception.message,
++                self.assertEqual(e.exception.message,
+                                   '--%s takes %d value%s'
+                                   % (name, nargs, 's' if nargs != 1 else ''))
+ 
+@@ -406,59 +406,59 @@
+                         default=default)
+         if nargs in (0, '?', '*') or disabled:
+             value = option.get_value('--%s' % name, 'option')
+-            self.assertEquals(value, posOptionValue())
+-            self.assertEquals(value.origin, 'option')
++            self.assertEqual(value, posOptionValue())
++            self.assertEqual(value.origin, 'option')
+         else:
+             with self.assertRaises(InvalidOptionError) as e:
+                 option.get_value('--%s' % name)
+             if disabled:
+-                self.assertEquals(e.exception.message,
++                self.assertEqual(e.exception.message,
+                                   'Cannot pass a value to --%s' % name)
+             elif nargs == '+':
+-                self.assertEquals(e.exception.message,
++                self.assertEqual(e.exception.message,
+                                   '--%s takes 1 or more values' % name)
+             else:
+-                self.assertEquals(e.exception.message,
++                self.assertEqual(e.exception.message,
+                                   '--%s takes %d value%s'
+                                   % (name, nargs, 's' if nargs != 1 else ''))
+ 
+         value = option.get_value('')
+-        self.assertEquals(value, defaultValue)
+-        self.assertEquals(value.origin, 'default')
++        self.assertEqual(value, defaultValue)
++        self.assertEqual(value.origin, 'default')
+ 
+         value = option.get_value(None)
+-        self.assertEquals(value, defaultValue)
+-        self.assertEquals(value.origin, 'default')
++        self.assertEqual(value, defaultValue)
++        self.assertEqual(value.origin, 'default')
+ 
+         value = option.get_value('MOZ_OPTION=', 'environment')
+-        self.assertEquals(value, NegativeOptionValue())
+-        self.assertEquals(value.origin, 'environment')
++        self.assertEqual(value, NegativeOptionValue())
++        self.assertEqual(value.origin, 'environment')
+ 
+         if nargs in (0, '?', '*'):
+             value = option.get_value('MOZ_OPTION=1', 'environment')
+-            self.assertEquals(value, PositiveOptionValue())
+-            self.assertEquals(value.origin, 'environment')
++            self.assertEqual(value, PositiveOptionValue())
++            self.assertEqual(value.origin, 'environment')
+         elif nargs in (1, '+'):
+             value = option.get_value('MOZ_OPTION=1', 'environment')
+-            self.assertEquals(value, PositiveOptionValue(('1',)))
+-            self.assertEquals(value.origin, 'environment')
++            self.assertEqual(value, PositiveOptionValue(('1',)))
++            self.assertEqual(value.origin, 'environment')
+         else:
+             with self.assertRaises(InvalidOptionError) as e:
+                 option.get_value('MOZ_OPTION=1', 'environment')
+-            self.assertEquals(e.exception.message, 'MOZ_OPTION takes 2 values')
++            self.assertEqual(e.exception.message, 'MOZ_OPTION takes 2 values')
+ 
+         if nargs in (1, '?', '*', '+') and not disabled:
+             value = option.get_value('--%s=' % name, 'option')
+-            self.assertEquals(value, PositiveOptionValue(('',)))
+-            self.assertEquals(value.origin, 'option')
++            self.assertEqual(value, PositiveOptionValue(('',)))
++            self.assertEqual(value.origin, 'option')
+         else:
+             with self.assertRaises(InvalidOptionError) as e:
+                 option.get_value('--%s=' % name, 'option')
+             if disabled:
+-                self.assertEquals(e.exception.message,
++                self.assertEqual(e.exception.message,
+                                   'Cannot pass a value to --%s' % name)
+             else:
+-                self.assertEquals(e.exception.message,
++                self.assertEqual(e.exception.message,
+                                   '--%s takes %d values' % (name, nargs))
+ 
+         with self.assertRaises(AssertionError):
+@@ -466,26 +466,26 @@
+ 
+         if nargs in (1, '?', '*', '+'):
+             value = option.get_value('MOZ_OPTION=foo', 'environment')
+-            self.assertEquals(value, PositiveOptionValue(('foo',)))
+-            self.assertEquals(value.origin, 'environment')
++            self.assertEqual(value, PositiveOptionValue(('foo',)))
++            self.assertEqual(value.origin, 'environment')
+         else:
+             with self.assertRaises(InvalidOptionError) as e:
+                 option.get_value('MOZ_OPTION=foo', 'environment')
+-            self.assertEquals(e.exception.message,
++            self.assertEqual(e.exception.message,
+                               'MOZ_OPTION takes %d values' % nargs)
+ 
+         if nargs in (2, '*', '+'):
+             value = option.get_value('MOZ_OPTION=foo,bar', 'environment')
+-            self.assertEquals(value, PositiveOptionValue(('foo', 'bar')))
+-            self.assertEquals(value.origin, 'environment')
++            self.assertEqual(value, PositiveOptionValue(('foo', 'bar')))
++            self.assertEqual(value.origin, 'environment')
+         else:
+             with self.assertRaises(InvalidOptionError) as e:
+                 option.get_value('MOZ_OPTION=foo,bar', 'environment')
+             if nargs == '?':
+-                self.assertEquals(e.exception.message,
++                self.assertEqual(e.exception.message,
+                                   'MOZ_OPTION takes 0 or 1 values')
+             else:
+-                self.assertEquals(e.exception.message,
++                self.assertEqual(e.exception.message,
+                                   'MOZ_OPTION takes %d value%s'
+                                   % (nargs, 's' if nargs != 1 else ''))
+ 
+@@ -497,26 +497,26 @@
+             env_option.get_value('--%s' % name)
+ 
+         value = env_option.get_value('')
+-        self.assertEquals(value, defaultValue)
+-        self.assertEquals(value.origin, 'default')
++        self.assertEqual(value, defaultValue)
++        self.assertEqual(value.origin, 'default')
+ 
+         value = env_option.get_value('MOZ_OPTION=', 'environment')
+-        self.assertEquals(value, negOptionValue())
+-        self.assertEquals(value.origin, 'environment')
++        self.assertEqual(value, negOptionValue())
++        self.assertEqual(value.origin, 'environment')
+ 
+         if nargs in (0, '?', '*'):
+             value = env_option.get_value('MOZ_OPTION=1', 'environment')
+-            self.assertEquals(value, posOptionValue())
++            self.assertEqual(value, posOptionValue())
+             self.assertTrue(value)
+-            self.assertEquals(value.origin, 'environment')
++            self.assertEqual(value.origin, 'environment')
+         elif nargs in (1, '+'):
+             value = env_option.get_value('MOZ_OPTION=1', 'environment')
+-            self.assertEquals(value, PositiveOptionValue(('1',)))
+-            self.assertEquals(value.origin, 'environment')
++            self.assertEqual(value, PositiveOptionValue(('1',)))
++            self.assertEqual(value.origin, 'environment')
+         else:
+             with self.assertRaises(InvalidOptionError) as e:
+                 env_option.get_value('MOZ_OPTION=1', 'environment')
+-            self.assertEquals(e.exception.message, 'MOZ_OPTION takes 2 values')
++            self.assertEqual(e.exception.message, 'MOZ_OPTION takes 2 values')
+ 
+         with self.assertRaises(AssertionError) as e:
+             env_option.get_value('--%s' % name)
+@@ -526,26 +526,26 @@
+ 
+         if nargs in (1, '?', '*', '+'):
+             value = env_option.get_value('MOZ_OPTION=foo', 'environment')
+-            self.assertEquals(value, PositiveOptionValue(('foo',)))
+-            self.assertEquals(value.origin, 'environment')
++            self.assertEqual(value, PositiveOptionValue(('foo',)))
++            self.assertEqual(value.origin, 'environment')
+         else:
+             with self.assertRaises(InvalidOptionError) as e:
+                 env_option.get_value('MOZ_OPTION=foo', 'environment')
+-            self.assertEquals(e.exception.message,
++            self.assertEqual(e.exception.message,
+                               'MOZ_OPTION takes %d values' % nargs)
+ 
+         if nargs in (2, '*', '+'):
+             value = env_option.get_value('MOZ_OPTION=foo,bar', 'environment')
+-            self.assertEquals(value, PositiveOptionValue(('foo', 'bar')))
+-            self.assertEquals(value.origin, 'environment')
++            self.assertEqual(value, PositiveOptionValue(('foo', 'bar')))
++            self.assertEqual(value.origin, 'environment')
+         else:
+             with self.assertRaises(InvalidOptionError) as e:
+                 env_option.get_value('MOZ_OPTION=foo,bar', 'environment')
+             if nargs == '?':
+-                self.assertEquals(e.exception.message,
++                self.assertEqual(e.exception.message,
+                                   'MOZ_OPTION takes 0 or 1 values')
+             else:
+-                self.assertEquals(e.exception.message,
++                self.assertEqual(e.exception.message,
+                                   'MOZ_OPTION takes %d value%s'
+                                   % (nargs, 's' if nargs != 1 else ''))
+ 
+@@ -557,28 +557,28 @@
+                                         default=default)
+ 
+         value = option.get_value('--%s-option' % disable, 'option')
+-        self.assertEquals(value, NegativeOptionValue())
+-        self.assertEquals(value.origin, 'option')
++        self.assertEqual(value, NegativeOptionValue())
++        self.assertEqual(value.origin, 'option')
+ 
+         option = self.test_option_value('%s-option' % disable, nargs=nargs,
+                                         default=default)
+ 
+         if nargs in (0, '?', '*'):
+             value = option.get_value('--%s-option' % enable, 'option')
+-            self.assertEquals(value, PositiveOptionValue())
+-            self.assertEquals(value.origin, 'option')
++            self.assertEqual(value, PositiveOptionValue())
++            self.assertEqual(value.origin, 'option')
+         else:
+             with self.assertRaises(InvalidOptionError) as e:
+                 option.get_value('--%s-option' % enable, 'option')
+             if nargs == 1:
+-                self.assertEquals(e.exception.message,
++                self.assertEqual(e.exception.message,
+                                   '--%s-option takes 1 value' % enable)
+             elif nargs == '+':
+-                self.assertEquals(e.exception.message,
++                self.assertEqual(e.exception.message,
+                                   '--%s-option takes 1 or more values'
+                                   % enable)
+             else:
+-                self.assertEquals(e.exception.message,
++                self.assertEqual(e.exception.message,
+                                   '--%s-option takes 2 values' % enable)
+ 
+     def test_option_value_with(self):
+@@ -587,12 +587,12 @@
+     def test_option_value_invalid_nargs(self):
+         with self.assertRaises(InvalidOptionError) as e:
+             Option('--option', nargs='foo')
+-        self.assertEquals(e.exception.message,
++        self.assertEqual(e.exception.message,
+                           "nargs must be a positive integer, '?', '*' or '+'")
+ 
+         with self.assertRaises(InvalidOptionError) as e:
+             Option('--option', nargs=-2)
+-        self.assertEquals(e.exception.message,
++        self.assertEqual(e.exception.message,
+                           "nargs must be a positive integer, '?', '*' or '+'")
+ 
+     def test_option_value_nargs_1(self):
+@@ -603,7 +603,7 @@
+         # A default is required
+         with self.assertRaises(InvalidOptionError) as e:
+             Option('--disable-option', nargs=1)
+-        self.assertEquals(e.exception.message,
++        self.assertEqual(e.exception.message,
+                           "The given `default` doesn't satisfy `nargs`")
+ 
+     def test_option_value_nargs_2(self):
+@@ -614,7 +614,7 @@
+         # A default is required
+         with self.assertRaises(InvalidOptionError) as e:
+             Option('--disable-option', nargs=2)
+-        self.assertEquals(e.exception.message,
++        self.assertEqual(e.exception.message,
+                           "The given `default` doesn't satisfy `nargs`")
+ 
+     def test_option_value_nargs_0_or_1(self):
+@@ -641,7 +641,7 @@
+         # A default is required
+         with self.assertRaises(InvalidOptionError) as e:
+             Option('--disable-option', nargs='+')
+-        self.assertEquals(e.exception.message,
++        self.assertEqual(e.exception.message,
+                           "The given `default` doesn't satisfy `nargs`")
+ 
+ 
+@@ -649,109 +649,109 @@
+     def test_basic(self):
+         helper = CommandLineHelper({}, ['cmd', '--foo', '--bar'])
+ 
+-        self.assertEquals(['--foo', '--bar'], list(helper))
++        self.assertEqual(['--foo', '--bar'], list(helper))
+ 
+         helper.add('--enable-qux')
+ 
+-        self.assertEquals(['--foo', '--bar', '--enable-qux'], list(helper))
++        self.assertEqual(['--foo', '--bar', '--enable-qux'], list(helper))
+ 
+         value, option = helper.handle(Option('--bar'))
+-        self.assertEquals(['--foo', '--enable-qux'], list(helper))
+-        self.assertEquals(PositiveOptionValue(), value)
+-        self.assertEquals('--bar', option)
++        self.assertEqual(['--foo', '--enable-qux'], list(helper))
++        self.assertEqual(PositiveOptionValue(), value)
++        self.assertEqual('--bar', option)
+ 
+         value, option = helper.handle(Option('--baz'))
+-        self.assertEquals(['--foo', '--enable-qux'], list(helper))
+-        self.assertEquals(NegativeOptionValue(), value)
+-        self.assertEquals(None, option)
++        self.assertEqual(['--foo', '--enable-qux'], list(helper))
++        self.assertEqual(NegativeOptionValue(), value)
++        self.assertEqual(None, option)
+ 
+     def test_precedence(self):
+         foo = Option('--with-foo', nargs='*')
+         helper = CommandLineHelper({}, ['cmd', '--with-foo=a,b'])
+         value, option = helper.handle(foo)
+-        self.assertEquals(PositiveOptionValue(('a', 'b')), value)
+-        self.assertEquals('command-line', value.origin)
+-        self.assertEquals('--with-foo=a,b', option)
++        self.assertEqual(PositiveOptionValue(('a', 'b')), value)
++        self.assertEqual('command-line', value.origin)
++        self.assertEqual('--with-foo=a,b', option)
+ 
+         helper = CommandLineHelper({}, ['cmd', '--with-foo=a,b',
+                                         '--without-foo'])
+         value, option = helper.handle(foo)
+-        self.assertEquals(NegativeOptionValue(), value)
+-        self.assertEquals('command-line', value.origin)
+-        self.assertEquals('--without-foo', option)
++        self.assertEqual(NegativeOptionValue(), value)
++        self.assertEqual('command-line', value.origin)
++        self.assertEqual('--without-foo', option)
+ 
+         helper = CommandLineHelper({}, ['cmd', '--without-foo',
+                                         '--with-foo=a,b'])
+         value, option = helper.handle(foo)
+-        self.assertEquals(PositiveOptionValue(('a', 'b')), value)
+-        self.assertEquals('command-line', value.origin)
+-        self.assertEquals('--with-foo=a,b', option)
++        self.assertEqual(PositiveOptionValue(('a', 'b')), value)
++        self.assertEqual('command-line', value.origin)
++        self.assertEqual('--with-foo=a,b', option)
+ 
+         foo = Option('--with-foo', env='FOO', nargs='*')
+         helper = CommandLineHelper({'FOO': ''}, ['cmd', '--with-foo=a,b'])
+         value, option = helper.handle(foo)
+-        self.assertEquals(PositiveOptionValue(('a', 'b')), value)
+-        self.assertEquals('command-line', value.origin)
+-        self.assertEquals('--with-foo=a,b', option)
++        self.assertEqual(PositiveOptionValue(('a', 'b')), value)
++        self.assertEqual('command-line', value.origin)
++        self.assertEqual('--with-foo=a,b', option)
+ 
+         helper = CommandLineHelper({'FOO': 'a,b'}, ['cmd', '--without-foo'])
+         value, option = helper.handle(foo)
+-        self.assertEquals(NegativeOptionValue(), value)
+-        self.assertEquals('command-line', value.origin)
+-        self.assertEquals('--without-foo', option)
++        self.assertEqual(NegativeOptionValue(), value)
++        self.assertEqual('command-line', value.origin)
++        self.assertEqual('--without-foo', option)
+ 
+         helper = CommandLineHelper({'FOO': ''}, ['cmd', '--with-bar=a,b'])
+         value, option = helper.handle(foo)
+-        self.assertEquals(NegativeOptionValue(), value)
+-        self.assertEquals('environment', value.origin)
+-        self.assertEquals('FOO=', option)
++        self.assertEqual(NegativeOptionValue(), value)
++        self.assertEqual('environment', value.origin)
++        self.assertEqual('FOO=', option)
+ 
+         helper = CommandLineHelper({'FOO': 'a,b'}, ['cmd', '--without-bar'])
+         value, option = helper.handle(foo)
+-        self.assertEquals(PositiveOptionValue(('a', 'b')), value)
+-        self.assertEquals('environment', value.origin)
+-        self.assertEquals('FOO=a,b', option)
++        self.assertEqual(PositiveOptionValue(('a', 'b')), value)
++        self.assertEqual('environment', value.origin)
++        self.assertEqual('FOO=a,b', option)
+ 
+         helper = CommandLineHelper({}, ['cmd', '--with-foo=a,b', 'FOO='])
+         value, option = helper.handle(foo)
+-        self.assertEquals(NegativeOptionValue(), value)
+-        self.assertEquals('command-line', value.origin)
+-        self.assertEquals('FOO=', option)
++        self.assertEqual(NegativeOptionValue(), value)
++        self.assertEqual('command-line', value.origin)
++        self.assertEqual('FOO=', option)
+ 
+         helper = CommandLineHelper({}, ['cmd', '--without-foo', 'FOO=a,b'])
+         value, option = helper.handle(foo)
+-        self.assertEquals(PositiveOptionValue(('a', 'b')), value)
+-        self.assertEquals('command-line', value.origin)
+-        self.assertEquals('FOO=a,b', option)
++        self.assertEqual(PositiveOptionValue(('a', 'b')), value)
++        self.assertEqual('command-line', value.origin)
++        self.assertEqual('FOO=a,b', option)
+ 
+         helper = CommandLineHelper({}, ['cmd', 'FOO=', '--with-foo=a,b'])
+         value, option = helper.handle(foo)
+-        self.assertEquals(PositiveOptionValue(('a', 'b')), value)
+-        self.assertEquals('command-line', value.origin)
+-        self.assertEquals('--with-foo=a,b', option)
++        self.assertEqual(PositiveOptionValue(('a', 'b')), value)
++        self.assertEqual('command-line', value.origin)
++        self.assertEqual('--with-foo=a,b', option)
+ 
+         helper = CommandLineHelper({}, ['cmd', 'FOO=a,b', '--without-foo'])
+         value, option = helper.handle(foo)
+-        self.assertEquals(NegativeOptionValue(), value)
+-        self.assertEquals('command-line', value.origin)
+-        self.assertEquals('--without-foo', option)
++        self.assertEqual(NegativeOptionValue(), value)
++        self.assertEqual('command-line', value.origin)
++        self.assertEqual('--without-foo', option)
+ 
+     def test_extra_args(self):
+         foo = Option('--with-foo', env='FOO', nargs='*')
+         helper = CommandLineHelper({}, ['cmd'])
+         helper.add('FOO=a,b,c', 'other-origin')
+         value, option = helper.handle(foo)
+-        self.assertEquals(PositiveOptionValue(('a', 'b', 'c')), value)
+-        self.assertEquals('other-origin', value.origin)
+-        self.assertEquals('FOO=a,b,c', option)
++        self.assertEqual(PositiveOptionValue(('a', 'b', 'c')), value)
++        self.assertEqual('other-origin', value.origin)
++        self.assertEqual('FOO=a,b,c', option)
+ 
+         helper = CommandLineHelper({}, ['cmd'])
+         helper.add('FOO=a,b,c', 'other-origin')
+         helper.add('--with-foo=a,b,c', 'other-origin')
+         value, option = helper.handle(foo)
+-        self.assertEquals(PositiveOptionValue(('a', 'b', 'c')), value)
+-        self.assertEquals('other-origin', value.origin)
+-        self.assertEquals('--with-foo=a,b,c', option)
++        self.assertEqual(PositiveOptionValue(('a', 'b', 'c')), value)
++        self.assertEqual('other-origin', value.origin)
++        self.assertEqual('--with-foo=a,b,c', option)
+ 
+         # Adding conflicting options is not allowed.
+         helper = CommandLineHelper({}, ['cmd'])
+@@ -771,9 +771,9 @@
+         # But adding the same is allowed.
+         helper.add('FOO=a,b,c', 'other-origin')
+         value, option = helper.handle(foo)
+-        self.assertEquals(PositiveOptionValue(('a', 'b', 'c')), value)
+-        self.assertEquals('other-origin', value.origin)
+-        self.assertEquals('FOO=a,b,c', option)
++        self.assertEqual(PositiveOptionValue(('a', 'b', 'c')), value)
++        self.assertEqual('other-origin', value.origin)
++        self.assertEqual('FOO=a,b,c', option)
+ 
+         # The same rule as above applies when using the option form vs. the
+         # variable form. But we can't detect it when .add is called.
+@@ -799,9 +799,9 @@
+         helper.add('FOO=a,b,c', 'other-origin')
+         helper.add('--with-foo=a,b,c', 'other-origin')
+         value, option = helper.handle(foo)
+-        self.assertEquals(PositiveOptionValue(('a', 'b', 'c')), value)
+-        self.assertEquals('other-origin', value.origin)
+-        self.assertEquals('--with-foo=a,b,c', option)
++        self.assertEqual(PositiveOptionValue(('a', 'b', 'c')), value)
++        self.assertEqual('other-origin', value.origin)
++        self.assertEqual('--with-foo=a,b,c', option)
+ 
+         # Conflicts are also not allowed against what is in the
+         # environment/on the command line.
+@@ -831,19 +831,19 @@
+         foo = Option('--foo',
+                      possible_origins=('command-line',))
+         value, option = helper.handle(foo)
+-        self.assertEquals(PositiveOptionValue(), value)
+-        self.assertEquals('command-line', value.origin)
+-        self.assertEquals('--foo', option)
++        self.assertEqual(PositiveOptionValue(), value)
++        self.assertEqual('command-line', value.origin)
++        self.assertEqual('--foo', option)
+ 
+         bar = Option('--bar',
+                      possible_origins=('mozconfig',))
+-        with self.assertRaisesRegexp(InvalidOptionError,
++        with self.assertRaisesRegex(InvalidOptionError,
+             "--bar can not be set by command-line. Values are accepted from: mozconfig"):
+             helper.handle(bar)
+ 
+         baz = Option(env='BAZ',
+                      possible_origins=('implied',))
+-        with self.assertRaisesRegexp(InvalidOptionError,
++        with self.assertRaisesRegex(InvalidOptionError,
+             "BAZ=1 can not be set by environment. Values are accepted from: implied"):
+             helper.handle(baz)
+ 
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/configure/test_toolchain_configure.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/configure/test_toolchain_configure.py	(refactored)
+@@ -2,12 +2,12 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import absolute_import, print_function, unicode_literals
++
+ 
+ import logging
+ import os
+ 
+-from StringIO import StringIO
++from io import StringIO
+ 
+ from mozunit import main
+ 
+@@ -291,9 +291,9 @@
+                 compiler = sandbox._value_for(sandbox[var])
+                 # Add var on both ends to make it clear which of the
+                 # variables is failing the test when that happens.
+-                self.assertEquals((var, compiler), (var, result))
++                self.assertEqual((var, compiler), (var, result))
+             except SystemExit:
+-                self.assertEquals((var, result),
++                self.assertEqual((var, result),
+                                   (var, self.out.getvalue().strip()))
+                 return
+ 
+@@ -471,7 +471,7 @@
+         # We'll try gcc and clang, but since there is no gcc (gcc-x.y doesn't
+         # count), find clang.
+         paths = {
+-            k: v for k, v in self.PATHS.iteritems()
++            k: v for k, v in self.PATHS.items()
+             if os.path.basename(k) not in ('gcc', 'g++')
+         }
+         self.do_toolchain_test(paths, {
+@@ -506,7 +506,7 @@
+         # Even if there are gcc-x.y or clang-x.y compilers available, we
+         # don't try them. This could be considered something to improve.
+         paths = {
+-            k: v for k, v in self.PATHS.iteritems()
++            k: v for k, v in self.PATHS.items()
+             if os.path.basename(k) not in ('gcc', 'g++', 'clang', 'clang++')
+         }
+         self.do_toolchain_test(paths, {
+@@ -687,7 +687,7 @@
+     def test_not_gcc(self):
+         # We won't pick GCC if it's the only thing available.
+         paths = {
+-            k: v for k, v in self.PATHS.iteritems()
++            k: v for k, v in self.PATHS.items()
+             if os.path.basename(k) not in ('clang', 'clang++')
+         }
+         self.do_toolchain_test(paths, {
+@@ -851,7 +851,7 @@
+     def test_clang_cl(self):
+         # We'll pick clang-cl if msvc can't be found.
+         paths = {
+-            k: v for k, v in self.PATHS.iteritems()
++            k: v for k, v in self.PATHS.items()
+             if os.path.basename(k) != 'cl'
+         }
+         self.do_toolchain_test(paths, {
+@@ -862,7 +862,7 @@
+     def test_gcc(self):
+         # We'll pick GCC if msvc and clang-cl can't be found.
+         paths = {
+-            k: v for k, v in self.PATHS.iteritems()
++            k: v for k, v in self.PATHS.items()
+             if os.path.basename(k) not in ('cl', 'clang-cl')
+         }
+         self.do_toolchain_test(paths, {
+@@ -881,7 +881,7 @@
+     def test_clang(self):
+         # We'll pick clang if nothing else is found.
+         paths = {
+-            k: v for k, v in self.PATHS.iteritems()
++            k: v for k, v in self.PATHS.items()
+             if os.path.basename(k) not in ('cl', 'clang-cl', 'gcc')
+         }
+         self.do_toolchain_test(paths, {
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/configure/test_toolchain_helpers.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/configure/test_toolchain_helpers.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import absolute_import, print_function, unicode_literals
++
+ 
+ import copy
+ import re
+@@ -10,7 +10,7 @@
+ import unittest
+ 
+ from fnmatch import fnmatch
+-from StringIO import StringIO
++from io import StringIO
+ from textwrap import dedent
+ 
+ from mozunit import (
+@@ -43,7 +43,7 @@
+         # Hack around it enough that the configure tests work properly.
+         context = self.context
+         def normalize_numbers(value):
+-            if isinstance(value, types.StringTypes):
++            if isinstance(value, (str,)):
+                 if value[-1:] == 'L' and value[:-1].isdigit():
+                     value = int(value[:-1])
+             return value
+@@ -53,7 +53,7 @@
+             return self.HAS_FEATURE.sub(r'\1\2', expr)
+         self.context = self.Context(
+             (normalize_has_feature(k), normalize_numbers(v))
+-            for k, v in context.iteritems()
++            for k, v in context.items()
+         )
+         try:
+             return Preprocessor.do_if(self, normalize_has_feature(expression),
+@@ -95,7 +95,7 @@
+         input.name = 'foo'
+         pp.do_include(input)
+ 
+-        self.assertEquals(pp.out.getvalue(), '1 . 2 . c "D"')
++        self.assertEqual(pp.out.getvalue(), '1 . 2 . c "D"')
+ 
+     def test_condition(self):
+         pp = CompilerPreprocessor({
+@@ -125,7 +125,7 @@
+         input.name = 'foo'
+         pp.do_include(input)
+ 
+-        self.assertEquals('IFDEF_A\nIF_A\nIF_B\nIF_NOT_C\n', pp.out.getvalue())
++        self.assertEqual('IFDEF_A\nIF_A\nIF_B\nIF_NOT_C\n', pp.out.getvalue())
+ 
+ 
+ class FakeCompiler(dict):
+@@ -164,9 +164,9 @@
+     '''
+     def __init__(self, *definitions):
+         for definition in definitions:
+-            if all(not isinstance(d, dict) for d in definition.itervalues()):
++            if all(not isinstance(d, dict) for d in definition.values()):
+                 definition = {None: definition}
+-            for key, value in definition.iteritems():
++            for key, value in definition.items():
+                 self.setdefault(key, {}).update(value)
+ 
+     def __call__(self, stdin, args):
+@@ -178,14 +178,14 @@
+             pp = CompilerPreprocessor(self[None])
+ 
+             def apply_defn(defn):
+-                for k, v in defn.iteritems():
++                for k, v in defn.items():
+                     if v is False:
+                         if k in pp.context:
+                             del pp.context[k]
+                     else:
+                         pp.context[k] = v
+ 
+-            for glob, defn in self.iteritems():
++            for glob, defn in self.items():
+                 if glob and not glob.startswith('-') and fnmatch(file, glob):
+                     apply_defn(defn)
+ 
+@@ -216,7 +216,7 @@
+                 'A': '1',
+                 'B': '2',
+             })
+-            self.assertEquals(compiler(None, ['-E', 'file']),
++            self.assertEqual(compiler(None, ['-E', 'file']),
+                               (0, '1 2 C', ''))
+ 
+             compiler = FakeCompiler({
+@@ -238,25 +238,25 @@
+                     'B': '42',
+                 },
+             })
+-            self.assertEquals(compiler(None, ['-E', 'file']),
++            self.assertEqual(compiler(None, ['-E', 'file']),
+                               (0, '1 2 C', ''))
+-            self.assertEquals(compiler(None, ['-E', '-foo', 'file']),
++            self.assertEqual(compiler(None, ['-E', '-foo', 'file']),
+                               (0, '1 2 foo', ''))
+-            self.assertEquals(compiler(None, ['-E', '-bar', 'file']),
++            self.assertEqual(compiler(None, ['-E', '-bar', 'file']),
+                               (0, '1 bar bar', ''))
+-            self.assertEquals(compiler(None, ['-E', '-qux', 'file']),
++            self.assertEqual(compiler(None, ['-E', '-qux', 'file']),
+                               (0, '1 B C', ''))
+-            self.assertEquals(compiler(None, ['-E', '-foo', '-bar', 'file']),
++            self.assertEqual(compiler(None, ['-E', '-foo', '-bar', 'file']),
+                               (0, '1 bar bar', ''))
+-            self.assertEquals(compiler(None, ['-E', '-bar', '-foo', 'file']),
++            self.assertEqual(compiler(None, ['-E', '-bar', '-foo', 'file']),
+                               (0, '1 bar foo', ''))
+-            self.assertEquals(compiler(None, ['-E', '-bar', '-qux', 'file']),
++            self.assertEqual(compiler(None, ['-E', '-bar', '-qux', 'file']),
+                               (0, '1 B bar', ''))
+-            self.assertEquals(compiler(None, ['-E', '-qux', '-bar', 'file']),
++            self.assertEqual(compiler(None, ['-E', '-qux', '-bar', 'file']),
+                               (0, '1 bar bar', ''))
+-            self.assertEquals(compiler(None, ['-E', 'file.c']),
++            self.assertEqual(compiler(None, ['-E', 'file.c']),
+                               (0, '1 42 C', ''))
+-            self.assertEquals(compiler(None, ['-E', '-bar', 'file.c']),
++            self.assertEqual(compiler(None, ['-E', '-bar', 'file.c']),
+                               (0, '1 bar bar', ''))
+ 
+     def test_multiple_definitions(self):
+@@ -267,7 +267,7 @@
+             'C': 3,
+         })
+ 
+-        self.assertEquals(compiler, {
++        self.assertEqual(compiler, {
+             None: {
+                 'A': 1,
+                 'B': 2,
+@@ -282,7 +282,7 @@
+             'C': 3,
+         })
+ 
+-        self.assertEquals(compiler, {
++        self.assertEqual(compiler, {
+             None: {
+                 'A': 1,
+                 'B': 4,
+@@ -302,7 +302,7 @@
+             },
+         })
+ 
+-        self.assertEquals(compiler, {
++        self.assertEqual(compiler, {
+             None: {
+                 'A': 1,
+                 'B': 4,
+@@ -330,7 +330,7 @@
+             },
+         })
+ 
+-        self.assertEquals(compiler, {
++        self.assertEqual(compiler, {
+             None: {
+                 'A': 1,
+                 'B': 2,
+@@ -370,7 +370,7 @@
+     def __add__(self, other):
+         assert isinstance(other, dict)
+         result = copy.deepcopy(self.__dict__)
+-        for k, v in other.iteritems():
++        for k, v in other.items():
+             if k == 'flags':
+                 result.setdefault(k, []).extend(v)
+             else:
+@@ -381,7 +381,7 @@
+ class TestCompilerResult(unittest.TestCase):
+     def test_compiler_result(self):
+         result = CompilerResult()
+-        self.assertEquals(result.__dict__, {
++        self.assertEqual(result.__dict__, {
+             'wrapper': [],
+             'compiler': mozpath.abspath(''),
+             'version': '',
+@@ -397,7 +397,7 @@
+             language='C',
+             flags=['-std=gnu99'],
+         )
+-        self.assertEquals(result.__dict__, {
++        self.assertEqual(result.__dict__, {
+             'wrapper': [],
+             'compiler': mozpath.abspath('/usr/bin/gcc'),
+             'version': '4.2.1',
+@@ -407,7 +407,7 @@
+         })
+ 
+         result2 = result + {'flags': ['-m32']}
+-        self.assertEquals(result2.__dict__, {
++        self.assertEqual(result2.__dict__, {
+             'wrapper': [],
+             'compiler': mozpath.abspath('/usr/bin/gcc'),
+             'version': '4.2.1',
+@@ -416,14 +416,14 @@
+             'flags': ['-std=gnu99', '-m32'],
+         })
+         # Original flags are untouched.
+-        self.assertEquals(result.flags, ['-std=gnu99'])
++        self.assertEqual(result.flags, ['-std=gnu99'])
+ 
+         result3 = result + {
+             'compiler': '/usr/bin/gcc-4.7',
+             'version': '4.7.3',
+             'flags': ['-m32'],
+         }
+-        self.assertEquals(result3.__dict__, {
++        self.assertEqual(result3.__dict__, {
+             'wrapper': [],
+             'compiler': mozpath.abspath('/usr/bin/gcc-4.7'),
+             'version': '4.7.3',
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/configure/test_toolkit_moz_configure.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/configure/test_toolkit_moz_configure.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import absolute_import, print_function, unicode_literals
++
+ 
+ import os
+ 
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/configure/test_util.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/configure/test_util.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import absolute_import, print_function, unicode_literals
++
+ 
+ import logging
+ import os
+@@ -11,7 +11,7 @@
+ import unittest
+ import sys
+ 
+-from StringIO import StringIO
++from io import StringIO
+ 
+ from mozunit import main
+ from mozpack import path as mozpath
+@@ -434,11 +434,11 @@
+         except SystemExit as e:
+             status = e.code
+ 
+-        self.assertEquals(status, 0)
++        self.assertEqual(status, 0)
+         quote_char = "'"
+         if getpreferredencoding().lower() == 'utf-8':
+             quote_char = '\u00B4'.encode('utf-8')
+-        self.assertEquals(out.getvalue().strip(), quote_char)
++        self.assertEqual(out.getvalue().strip(), quote_char)
+ 
+ 
+ class TestVersion(unittest.TestCase):
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/controller/test_ccachestats.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/controller/test_ccachestats.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import unicode_literals
++
+ 
+ import unittest
+ 
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/controller/test_clobber.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/controller/test_clobber.py	(refactored)
+@@ -2,14 +2,14 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import unicode_literals
++
+ 
+ import os
+ import shutil
+ import tempfile
+ import unittest
+ 
+-from StringIO import StringIO
++from io import StringIO
+ 
+ from mozunit import main
+ 
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/frontend/test_context.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/frontend/test_context.py	(refactored)
+@@ -36,7 +36,7 @@
+             'baz': (dict, dict, ''),
+         })
+ 
+-        self.assertEqual(test.keys(), [])
++        self.assertEqual(list(test.keys()), [])
+ 
+         self.assertEqual(test['foo'], 0)
+ 
+@@ -84,12 +84,12 @@
+             'baz': (dict, list, ''),
+         })
+ 
+-        self.assertEqual(test.keys(), [])
++        self.assertEqual(list(test.keys()), [])
+ 
+         with self.assertRaises(ValueError):
+             test.update(bar=True, foo={})
+ 
+-        self.assertEqual(test.keys(), [])
++        self.assertEqual(list(test.keys()), [])
+ 
+         test.update(bar=True, foo=1)
+ 
+@@ -258,19 +258,19 @@
+         self.assertEqual(lines[-1].strip(), '')
+ 
+     def test_documentation_formatting(self):
+-        for typ, inp, doc in VARIABLES.values():
++        for typ, inp, doc in list(VARIABLES.values()):
+             self._verify_doc(doc)
+ 
+-        for attr, args, doc in FUNCTIONS.values():
++        for attr, args, doc in list(FUNCTIONS.values()):
+             self._verify_doc(doc)
+ 
+-        for func, typ, doc in SPECIAL_VARIABLES.values():
++        for func, typ, doc in list(SPECIAL_VARIABLES.values()):
+             self._verify_doc(doc)
+ 
+-        for name, cls in SUBCONTEXTS.items():
++        for name, cls in list(SUBCONTEXTS.items()):
+             self._verify_doc(cls.__doc__)
+ 
+-            for name, v in cls.VARIABLES.items():
++            for name, v in list(cls.VARIABLES.items()):
+                 self._verify_doc(v[2])
+ 
+ 
+@@ -631,7 +631,7 @@
+ class TestTypedRecord(unittest.TestCase):
+ 
+     def test_fields(self):
+-        T = ContextDerivedTypedRecord(('field1', unicode),
++        T = ContextDerivedTypedRecord(('field1', str),
+                                       ('field2', list))
+         inst = T(None)
+         self.assertEqual(inst.field1, '')
+@@ -647,7 +647,7 @@
+             inst.field3 = []
+ 
+     def test_coercion(self):
+-        T = ContextDerivedTypedRecord(('field1', unicode),
++        T = ContextDerivedTypedRecord(('field1', str),
+                                       ('field2', list))
+         inst = T(None)
+         inst.field1 = 3
+@@ -673,45 +673,45 @@
+     def test_single_bug_component(self):
+         c = Context({})
+         f = Files(c, pattern='**')
+-        f['BUG_COMPONENT'] = (u'Product1', u'Component1')
++        f['BUG_COMPONENT'] = ('Product1', 'Component1')
+ 
+         files = {'moz.build': f}
+         self.assertEqual(Files.aggregate(files), {
+-            'bug_component_counts': [((u'Product1', u'Component1'), 1)],
+-            'recommended_bug_component': (u'Product1', u'Component1'),
++            'bug_component_counts': [(('Product1', 'Component1'), 1)],
++            'recommended_bug_component': ('Product1', 'Component1'),
+         })
+ 
+     def test_multiple_bug_components(self):
+         c = Context({})
+         f1 = Files(c, pattern='**')
+-        f1['BUG_COMPONENT'] = (u'Product1', u'Component1')
++        f1['BUG_COMPONENT'] = ('Product1', 'Component1')
+ 
+         f2 = Files(c, pattern='**')
+-        f2['BUG_COMPONENT'] = (u'Product2', u'Component2')
++        f2['BUG_COMPONENT'] = ('Product2', 'Component2')
+ 
+         files = {'a': f1, 'b': f2, 'c': f1}
+         self.assertEqual(Files.aggregate(files), {
+             'bug_component_counts': [
+-                ((u'Product1', u'Component1'), 2),
+-                ((u'Product2', u'Component2'), 1),
++                (('Product1', 'Component1'), 2),
++                (('Product2', 'Component2'), 1),
+             ],
+-            'recommended_bug_component': (u'Product1', u'Component1'),
++            'recommended_bug_component': ('Product1', 'Component1'),
+         })
+ 
+     def test_no_recommended_bug_component(self):
+         """If there is no clear count winner, we don't recommend a bug component."""
+         c = Context({})
+         f1 = Files(c, pattern='**')
+-        f1['BUG_COMPONENT'] = (u'Product1', u'Component1')
++        f1['BUG_COMPONENT'] = ('Product1', 'Component1')
+ 
+         f2 = Files(c, pattern='**')
+-        f2['BUG_COMPONENT'] = (u'Product2', u'Component2')
++        f2['BUG_COMPONENT'] = ('Product2', 'Component2')
+ 
+         files = {'a': f1, 'b': f2}
+         self.assertEqual(Files.aggregate(files), {
+             'bug_component_counts': [
+-                ((u'Product1', u'Component1'), 1),
+-                ((u'Product2', u'Component2'), 1),
++                (('Product1', 'Component1'), 1),
++                (('Product2', 'Component2'), 1),
+             ],
+             'recommended_bug_component': None,
+         })
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/frontend/test_emitter.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/frontend/test_emitter.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import unicode_literals
++
+ 
+ import os
+ import unittest
+@@ -204,7 +204,7 @@
+     def test_use_yasm(self):
+         # When yasm is not available, this should raise.
+         reader = self.reader('use-yasm')
+-        with self.assertRaisesRegexp(SandboxValidationError,
++        with self.assertRaisesRegex(SandboxValidationError,
+             'yasm is not available'):
+             self.read_topsrcdir(reader)
+ 
+@@ -267,25 +267,25 @@
+         o = objs[0]
+         self.assertIsInstance(o, GeneratedFile)
+         self.assertEqual(o.outputs, ('bar.c',))
+-        self.assertRegexpMatches(o.script, 'script.py$')
++        self.assertRegex(o.script, 'script.py$')
+         self.assertEqual(o.method, 'make_bar')
+         self.assertEqual(o.inputs, [])
+ 
+     def test_generated_files_no_script(self):
+         reader = self.reader('generated-files-no-script')
+-        with self.assertRaisesRegexp(SandboxValidationError,
++        with self.assertRaisesRegex(SandboxValidationError,
+             'Script for generating bar.c does not exist'):
+             self.read_topsrcdir(reader)
+ 
+     def test_generated_files_no_inputs(self):
+         reader = self.reader('generated-files-no-inputs')
+-        with self.assertRaisesRegexp(SandboxValidationError,
++        with self.assertRaisesRegex(SandboxValidationError,
+             'Input for generating foo.c does not exist'):
+             self.read_topsrcdir(reader)
+ 
+     def test_generated_files_no_python_script(self):
+         reader = self.reader('generated-files-no-python-script')
+-        with self.assertRaisesRegexp(SandboxValidationError,
++        with self.assertRaisesRegex(SandboxValidationError,
+             'Script for generating bar.c does not end in .py'):
+             self.read_topsrcdir(reader)
+ 
+@@ -314,7 +314,7 @@
+         Missing files in EXPORTS is an error.
+         '''
+         reader = self.reader('exports-missing')
+-        with self.assertRaisesRegexp(SandboxValidationError,
++        with self.assertRaisesRegex(SandboxValidationError,
+              'File listed in EXPORTS does not exist:'):
+             self.read_topsrcdir(reader)
+ 
+@@ -323,7 +323,7 @@
+         An objdir file in EXPORTS that is not in GENERATED_FILES is an error.
+         '''
+         reader = self.reader('exports-missing-generated')
+-        with self.assertRaisesRegexp(SandboxValidationError,
++        with self.assertRaisesRegex(SandboxValidationError,
+              'Objdir file listed in EXPORTS not in GENERATED_FILES:'):
+             self.read_topsrcdir(reader)
+ 
+@@ -360,7 +360,7 @@
+ 
+     def test_test_harness_files_root(self):
+         reader = self.reader('test-harness-files-root')
+-        with self.assertRaisesRegexp(SandboxValidationError,
++        with self.assertRaisesRegex(SandboxValidationError,
+             'Cannot install files to the root of TEST_HARNESS_FILES'):
+             self.read_topsrcdir(reader)
+ 
+@@ -413,14 +413,14 @@
+         """A missing manifest file should result in an error."""
+         reader = self.reader('test-manifest-missing-manifest')
+ 
+-        with self.assertRaisesRegexp(BuildReaderError, 'IOError: Missing files'):
++        with self.assertRaisesRegex(BuildReaderError, 'IOError: Missing files'):
+             self.read_topsrcdir(reader)
+ 
+     def test_empty_test_manifest_rejected(self):
+         """A test manifest without any entries is rejected."""
+         reader = self.reader('test-manifest-empty')
+ 
+-        with self.assertRaisesRegexp(SandboxValidationError, 'Empty test manifest'):
++        with self.assertRaisesRegex(SandboxValidationError, 'Empty test manifest'):
+             self.read_topsrcdir(reader)
+ 
+ 
+@@ -428,7 +428,7 @@
+         """A test manifest with no tests but support-files is not supported."""
+         reader = self.reader('test-manifest-just-support')
+ 
+-        with self.assertRaisesRegexp(SandboxValidationError, 'Empty test manifest'):
++        with self.assertRaisesRegex(SandboxValidationError, 'Empty test manifest'):
+             self.read_topsrcdir(reader)
+ 
+     def test_test_manifest_dupe_support_files(self):
+@@ -437,7 +437,7 @@
+         """
+         reader = self.reader('test-manifest-dupes')
+ 
+-        with self.assertRaisesRegexp(SandboxValidationError, 'bar.js appears multiple times '
++        with self.assertRaisesRegex(SandboxValidationError, 'bar.js appears multiple times '
+             'in a test manifest under a support-files field, please omit the duplicate entry.'):
+             self.read_topsrcdir(reader)
+ 
+@@ -454,7 +454,7 @@
+             mozpath.join(o.install_prefix, "absolute-support.ini"),
+             mozpath.join(o.install_prefix, "test_file.js"),
+         ]
+-        paths = sorted([v[0] for v in o.installs.values()])
++        paths = sorted([v[0] for v in list(o.installs.values())])
+         self.assertEqual(paths, expected)
+ 
+     @unittest.skip('Bug 1304316 - Items in the second set but not the first')
+@@ -481,7 +481,7 @@
+         """A non-existent shared support file reference produces an error."""
+         reader = self.reader('test-manifest-shared-missing')
+ 
+-        with self.assertRaisesRegexp(SandboxValidationError,
++        with self.assertRaisesRegex(SandboxValidationError,
+                                      'entry in support-files not present in the srcdir'):
+             self.read_topsrcdir(reader)
+ 
+@@ -500,7 +500,7 @@
+             mozpath.normpath(mozpath.join(o.install_prefix, "subdir/support.txt")),
+             mozpath.normpath(mozpath.join(o.install_prefix, "subdir/test_foo.html")),
+         ]
+-        paths = sorted([v[0] for v in o.installs.values()])
++        paths = sorted([v[0] for v in list(o.installs.values())])
+         self.assertEqual(paths, expected)
+ 
+     def test_test_manifest_install_includes(self):
+@@ -518,7 +518,7 @@
+             mozpath.normpath(mozpath.join(o.install_prefix, "subdir/mochitest.ini")),
+             mozpath.normpath(mozpath.join(o.install_prefix, "subdir/test_foo.html")),
+         ]
+-        paths = sorted([v[0] for v in o.installs.values()])
++        paths = sorted([v[0] for v in list(o.installs.values())])
+         self.assertEqual(paths, expected)
+ 
+     def test_test_manifest_includes(self):
+@@ -541,7 +541,7 @@
+     def test_python_unit_test_missing(self):
+         """Missing files in PYTHON_UNIT_TESTS should raise."""
+         reader = self.reader('test-python-unit-test-missing')
+-        with self.assertRaisesRegexp(SandboxValidationError,
++        with self.assertRaisesRegex(SandboxValidationError,
+             'Path specified in PYTHON_UNIT_TESTS does not exist:'):
+             self.read_topsrcdir(reader)
+ 
+@@ -635,7 +635,7 @@
+             self.assertEqual(external_normalized, m.get('external', set()))
+ 
+             self.assertEqual(len(o.installs), len(m['installs']))
+-            for path in o.installs.keys():
++            for path in list(o.installs.keys()):
+                 self.assertTrue(path.startswith(o.directory))
+                 relpath = path[len(o.directory)+1:]
+ 
+@@ -648,7 +648,7 @@
+     def test_test_manifest_unmatched_generated(self):
+         reader = self.reader('test-manifest-unmatched-generated')
+ 
+-        with self.assertRaisesRegexp(SandboxValidationError,
++        with self.assertRaisesRegex(SandboxValidationError,
+             'entry in generated-files not present elsewhere'):
+             self.read_topsrcdir(reader),
+ 
+@@ -672,7 +672,7 @@
+         """Missing test files should result in error."""
+         reader = self.reader('test-manifest-missing-test-file')
+ 
+-        with self.assertRaisesRegexp(SandboxValidationError,
++        with self.assertRaisesRegex(SandboxValidationError,
+             'lists test that does not exist: test_missing.html'):
+             self.read_topsrcdir(reader)
+ 
+@@ -680,7 +680,7 @@
+         """Missing test files should result in error, even when the test list is not filtered."""
+         reader = self.reader('test-manifest-missing-test-file-unfiltered')
+ 
+-        with self.assertRaisesRegexp(SandboxValidationError,
++        with self.assertRaisesRegex(SandboxValidationError,
+             'lists test that does not exist: missing.js'):
+             self.read_topsrcdir(reader)
+ 
+@@ -794,20 +794,20 @@
+             self.assertIsInstance(obj.path, Path)
+ 
+     def test_jar_manifests_multiple_files(self):
+-        with self.assertRaisesRegexp(SandboxValidationError, 'limited to one value'):
++        with self.assertRaisesRegex(SandboxValidationError, 'limited to one value'):
+             reader = self.reader('jar-manifests-multiple-files')
+             self.read_topsrcdir(reader)
+ 
+     def test_xpidl_module_no_sources(self):
+         """XPIDL_MODULE without XPIDL_SOURCES should be rejected."""
+-        with self.assertRaisesRegexp(SandboxValidationError, 'XPIDL_MODULE '
++        with self.assertRaisesRegex(SandboxValidationError, 'XPIDL_MODULE '
+             'cannot be defined'):
+             reader = self.reader('xpidl-module-no-sources')
+             self.read_topsrcdir(reader)
+ 
+     def test_missing_local_includes(self):
+         """LOCAL_INCLUDES containing non-existent directories should be rejected."""
+-        with self.assertRaisesRegexp(SandboxValidationError, 'Path specified in '
++        with self.assertRaisesRegex(SandboxValidationError, 'Path specified in '
+             'LOCAL_INCLUDES does not exist'):
+             reader = self.reader('missing-local-includes')
+             self.read_topsrcdir(reader)
+@@ -852,7 +852,7 @@
+             '.S': ['g.S'],
+             '.s': ['h.s', 'i.asm'],
+         }
+-        for suffix, files in expected.items():
++        for suffix, files in list(expected.items()):
+             sources = suffix_map[suffix]
+             self.assertEqual(
+                 sources.files,
+@@ -905,7 +905,7 @@
+             '.S': ['g.S'],
+             '.s': ['h.s', 'i.asm'],
+         }
+-        for suffix, files in expected.items():
++        for suffix, files in list(expected.items()):
+             sources = suffix_map[suffix]
+             self.assertEqual(
+                 sources.files,
+@@ -931,7 +931,7 @@
+             '.c': ['d.c'],
+             '.mm': ['e.mm', 'f.mm'],
+         }
+-        for suffix, files in expected.items():
++        for suffix, files in list(expected.items()):
+             sources = suffix_map[suffix]
+             self.assertEqual(
+                 sources.files,
+@@ -956,7 +956,7 @@
+             '.mm': ['objc1.mm', 'objc2.mm'],
+             '.c': ['c1.c', 'c2.c'],
+         }
+-        for suffix, files in expected.items():
++        for suffix, files in list(expected.items()):
+             sources = suffix_map[suffix]
+             self.assertEqual(
+                 sources.files,
+@@ -982,7 +982,7 @@
+             '.mm': ['objc1.mm', 'objc2.mm'],
+             '.c': ['c1.c', 'c2.c'],
+         }
+-        for suffix, files in expected.items():
++        for suffix, files in list(expected.items()):
+             sources = suffix_map[suffix]
+             self.assertEqual(
+                 sources.files,
+@@ -1005,11 +1005,11 @@
+ 
+             expected = {'install.rdf', 'main.js'}
+             for f in files:
+-                self.assertTrue(unicode(f) in expected)
++                self.assertTrue(str(f) in expected)
+ 
+     def test_missing_final_target_pp_files(self):
+         """Test that FINAL_TARGET_PP_FILES with missing files throws errors."""
+-        with self.assertRaisesRegexp(SandboxValidationError, 'File listed in '
++        with self.assertRaisesRegex(SandboxValidationError, 'File listed in '
+             'FINAL_TARGET_PP_FILES does not exist'):
+             reader = self.reader('dist-files-missing')
+             self.read_topsrcdir(reader)
+@@ -1017,49 +1017,49 @@
+     def test_final_target_pp_files_non_srcdir(self):
+         '''Test that non-srcdir paths in FINAL_TARGET_PP_FILES throws errors.'''
+         reader = self.reader('final-target-pp-files-non-srcdir')
+-        with self.assertRaisesRegexp(SandboxValidationError,
++        with self.assertRaisesRegex(SandboxValidationError,
+              'Only source directory paths allowed in FINAL_TARGET_PP_FILES:'):
+             self.read_topsrcdir(reader)
+ 
+     def test_rust_library_no_cargo_toml(self):
+         '''Test that defining a RustLibrary without a Cargo.toml fails.'''
+         reader = self.reader('rust-library-no-cargo-toml')
+-        with self.assertRaisesRegexp(SandboxValidationError,
++        with self.assertRaisesRegex(SandboxValidationError,
+              'No Cargo.toml file found'):
+             self.read_topsrcdir(reader)
+ 
+     def test_rust_library_name_mismatch(self):
+         '''Test that defining a RustLibrary that doesn't match Cargo.toml fails.'''
+         reader = self.reader('rust-library-name-mismatch')
+-        with self.assertRaisesRegexp(SandboxValidationError,
++        with self.assertRaisesRegex(SandboxValidationError,
+              'library.*does not match Cargo.toml-defined package'):
+             self.read_topsrcdir(reader)
+ 
+     def test_rust_library_no_lib_section(self):
+         '''Test that a RustLibrary Cargo.toml with no [lib] section fails.'''
+         reader = self.reader('rust-library-no-lib-section')
+-        with self.assertRaisesRegexp(SandboxValidationError,
++        with self.assertRaisesRegex(SandboxValidationError,
+              'Cargo.toml for.* has no \\[lib\\] section'):
+             self.read_topsrcdir(reader)
+ 
+     def test_rust_library_no_profile_section(self):
+         '''Test that a RustLibrary Cargo.toml with no [profile] section fails.'''
+         reader = self.reader('rust-library-no-profile-section')
+-        with self.assertRaisesRegexp(SandboxValidationError,
++        with self.assertRaisesRegex(SandboxValidationError,
+              'Cargo.toml for.* has no \\[profile\\.dev\\] section'):
+             self.read_topsrcdir(reader)
+ 
+     def test_rust_library_invalid_crate_type(self):
+         '''Test that a RustLibrary Cargo.toml has a permitted crate-type.'''
+         reader = self.reader('rust-library-invalid-crate-type')
+-        with self.assertRaisesRegexp(SandboxValidationError,
++        with self.assertRaisesRegex(SandboxValidationError,
+              'crate-type.* is not permitted'):
+             self.read_topsrcdir(reader)
+ 
+     def test_rust_library_non_abort_panic(self):
+         '''Test that a RustLibrary Cargo.toml has `panic = "abort" set'''
+         reader = self.reader('rust-library-non-abort-panic')
+-        with self.assertRaisesRegexp(SandboxValidationError,
++        with self.assertRaisesRegex(SandboxValidationError,
+              'does not specify `panic = "abort"`'):
+             self.read_topsrcdir(reader)
+ 
+@@ -1072,15 +1072,15 @@
+         self.assertEqual(len(objs), 1)
+         lib = objs[0]
+         self.assertIsInstance(lib, RustLibrary)
+-        self.assertRegexpMatches(lib.lib_name, "random_crate")
+-        self.assertRegexpMatches(lib.import_name, "random_crate")
+-        self.assertRegexpMatches(lib.basename, "random-crate")
++        self.assertRegex(lib.lib_name, "random_crate")
++        self.assertRegex(lib.import_name, "random_crate")
++        self.assertRegex(lib.basename, "random-crate")
+ 
+     def test_multiple_rust_libraries(self):
+         '''Test that linking multiple Rust libraries throws an error'''
+         reader = self.reader('multiple-rust-libraries',
+                              extra_substs=dict(RUST_TARGET='i686-pc-windows-msvc'))
+-        with self.assertRaisesRegexp(LinkageMultipleRustLibrariesError,
++        with self.assertRaisesRegex(LinkageMultipleRustLibrariesError,
+              'Cannot link multiple Rust libraries'):
+             self.read_topsrcdir(reader)
+ 
+@@ -1107,7 +1107,7 @@
+             mozpath.join(reader.config.topobjdir, 'dir2'),
+             '/dir3',
+         ]
+-        self.assertEquals([p.full_path for p in objs[0].paths], expected)
++        self.assertEqual([p.full_path for p in objs[0].paths], expected)
+ 
+     def test_binary_components(self):
+         """Test that IS_COMPONENT/NO_COMPONENTS_MANIFEST work properly."""
+@@ -1163,7 +1163,7 @@
+         from GENERATED_FILES is an error.
+         """
+         reader = self.reader('test-symbols-file-objdir-missing-generated')
+-        with self.assertRaisesRegexp(SandboxValidationError,
++        with self.assertRaisesRegex(SandboxValidationError,
+              'Objdir file specified in SYMBOLS_FILE not in GENERATED_FILES:'):
+             self.read_topsrcdir(reader)
+ 
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/frontend/test_namespaces.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/frontend/test_namespaces.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import unicode_literals
++
+ 
+ import unittest
+ 
+@@ -29,7 +29,7 @@
+ 
+ class Piyo(ContextDerivedValue):
+     def __init__(self, context, value):
+-        if not isinstance(value, unicode):
++        if not isinstance(value, str):
+             raise ValueError
+         self.context = context
+         self.value = value
+@@ -48,9 +48,9 @@
+ 
+ 
+ VARIABLES = {
+-    'HOGE': (unicode, unicode, None),
+-    'FUGA': (Fuga, unicode, None),
+-    'PIYO': (Piyo, unicode, None),
++    'HOGE': (str, str, None),
++    'FUGA': (Fuga, str, None),
++    'PIYO': (Piyo, str, None),
+     'HOGERA': (ContextDerivedTypedList(Piyo, StrictOrderingOnAppendList),
+         list, None),
+     'HOGEHOGE': (ContextDerivedTypedListWithItems(
+@@ -104,7 +104,7 @@
+         self.assertEqual(e[1], 'set_type')
+         self.assertEqual(e[2], 'HOGE')
+         self.assertEqual(e[3], True)
+-        self.assertEqual(e[4], unicode)
++        self.assertEqual(e[4], str)
+ 
+     def test_key_checking(self):
+         # Checking for existence of a key should not populate the key if it
+@@ -127,7 +127,7 @@
+         self.assertEqual(e[1], 'set_type')
+         self.assertEqual(e[2], 'FUGA')
+         self.assertEqual(e[3], False)
+-        self.assertEqual(e[4], unicode)
++        self.assertEqual(e[4], str)
+ 
+         ns['FUGA'] = 'fuga'
+         self.assertIsInstance(ns['FUGA'], Fuga)
+@@ -150,7 +150,7 @@
+         self.assertEqual(e[1], 'set_type')
+         self.assertEqual(e[2], 'PIYO')
+         self.assertEqual(e[3], False)
+-        self.assertEqual(e[4], unicode)
++        self.assertEqual(e[4], str)
+ 
+         ns['PIYO'] = 'piyo'
+         self.assertIsInstance(ns['PIYO'], Piyo)
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/frontend/test_reader.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/frontend/test_reader.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import unicode_literals
++
+ 
+ import os
+ import sys
+@@ -423,7 +423,7 @@
+             'simple/base.cpp',
+         ])
+ 
+-        for path, pattern_set in expected.items():
++        for path, pattern_set in list(expected.items()):
+             self.assertEqual(v[path].test_files,
+                              expected[path])
+ 
+@@ -438,7 +438,7 @@
+                                       'default/tests/reftests/**']),
+         }
+ 
+-        for path, pattern_set in expected.items():
++        for path, pattern_set in list(expected.items()):
+             self.assertEqual(v[path].test_files,
+                              expected[path])
+ 
+@@ -454,7 +454,7 @@
+             'tagged/src/bar.jsm': set(['tagged/**.js']),
+         }
+ 
+-        for path, pattern_set in expected_patterns.items():
++        for path, pattern_set in list(expected_patterns.items()):
+             self.assertEqual(v[path].test_files,
+                              expected_patterns[path])
+ 
+@@ -462,7 +462,7 @@
+             'tagged/src/submodule/foo.js': set(['submodule']),
+             'tagged/src/bar.jsm': set([]),
+         }
+-        for path, pattern_set in expected_tags.items():
++        for path, pattern_set in list(expected_tags.items()):
+             self.assertEqual(v[path].test_tags,
+                              expected_tags[path])
+ 
+@@ -470,7 +470,7 @@
+             'tagged/src/bar.jsm': set(['browser-chrome']),
+             'tagged/src/submodule/foo.js': set([]),
+         }
+-        for path, pattern_set in expected_flavors.items():
++        for path, pattern_set in list(expected_flavors.items()):
+             self.assertEqual(v[path].test_flavors,
+                              expected_flavors[path])
+ 
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/frontend/test_sandbox.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/test/frontend/test_sandbox.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import unicode_literals
++
+ 
+ import os
+ import shutil
+@@ -345,7 +345,7 @@
+         sandbox = MozbuildSandbox(Context(VARIABLES, config))
+ 
+         self.assertEqual(sandbox['CONFIG']['BAD_UTF8'],
+-            u'\ufffd\ufffd\ufffd\ufffd:')
++            '\ufffd\ufffd\ufffd\ufffd:')
+ 
+     def test_invalid_exports_set_base(self):
+         sandbox = self.sandbox()
+@@ -525,7 +525,7 @@
+             source = 'a = foo(1, 2)'
+             sandbox.exec_source(source, 'foo.mozbuild')
+ 
+-            self.assertEquals(sandbox['a'], (Foo, int))
++            self.assertEqual(sandbox['a'], (Foo, int))
+         finally:
+             del FUNCTIONS['foo']
+ 
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozpack/archive.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozpack/archive.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import absolute_import
++
+ 
+ import bz2
+ import gzip
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozpack/copier.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozpack/copier.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import absolute_import
++
+ 
+ import os
+ import stat
+@@ -109,7 +109,7 @@
+         '''
+         Return all paths stored in the container, in the order they were added.
+         '''
+-        return self._files.keys()
++        return list(self._files.keys())
+ 
+     def __len__(self):
+         '''
+@@ -142,7 +142,7 @@
+             for path, file in registry:
+                 (...)
+         '''
+-        return self._files.iteritems()
++        return iter(self._files.items())
+ 
+     def required_directories(self):
+         '''
+@@ -151,7 +151,7 @@
+         unspecified (virtual) root directory (and do not include said root
+         directory).
+         '''
+-        return set(k for k, v in self._required_directories.items() if v > 0)
++        return set(k for k, v in list(self._required_directories.items()) if v > 0)
+ 
+ 
+ class FileRegistrySubtree(object):
+@@ -263,7 +263,7 @@
+ 
+         Returns a FileCopyResult that details what changed.
+         '''
+-        assert isinstance(destination, basestring)
++        assert isinstance(destination, str)
+         assert not os.path.exists(destination) or os.path.isdir(destination)
+ 
+         result = FileCopyResult()
+@@ -531,7 +531,7 @@
+             def exists(self):
+                 return self.deflater is not None
+ 
+-        if isinstance(dest, basestring):
++        if isinstance(dest, str):
+             dest = Dest(dest)
+         assert isinstance(dest, Dest)
+ 
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozpack/errors.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozpack/errors.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import absolute_import
++
+ 
+ import sys
+ from contextlib import contextmanager
+@@ -97,7 +97,7 @@
+             if self._count is None:
+                 raise ErrorMessage(msg)
+             self._count += 1
+-        print >>self.out, msg
++        print(msg, file=self.out)
+ 
+     def fatal(self, msg):
+         self._handle(self.FATAL, msg)
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozpack/executables.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozpack/executables.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import absolute_import
++
+ 
+ import os
+ import struct
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozpack/files.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozpack/files.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import absolute_import
++
+ 
+ import errno
+ import os
+@@ -57,7 +57,7 @@
+ 
+     def _copyfile(src, dest):
+         # False indicates `dest` should be overwritten if it exists already.
+-        if isinstance(src, unicode) and isinstance(dest, unicode):
++        if isinstance(src, str) and isinstance(dest, str):
+             _CopyFileW(src, dest, False)
+         elif isinstance(src, str) and isinstance(dest, str):
+             _CopyFileA(src, dest, False)
+@@ -145,13 +145,13 @@
+         # - keep file type (e.g. S_IFREG)
+         ret = stat.S_IFMT(mode)
+         # - expand user read and execute permissions to everyone
+-        if mode & 0400:
+-            ret |= 0444
+-        if mode & 0100:
+-            ret |= 0111
++        if mode & 0o400:
++            ret |= 0o444
++        if mode & 0o100:
++            ret |= 0o111
+         # - keep user write permissions
+-        if mode & 0200:
+-            ret |= 0200
++        if mode & 0o200:
++            ret |= 0o200
+         # - leave away sticky bit, setuid, setgid
+         return ret
+ 
+@@ -164,7 +164,7 @@
+         disabled when skip_if_older is False.
+         Returns whether a copy was actually performed (True) or not (False).
+         '''
+-        if isinstance(dest, basestring):
++        if isinstance(dest, str):
+             dest = Dest(dest)
+         else:
+             assert isinstance(dest, Dest)
+@@ -258,11 +258,11 @@
+     '''
+     def copy(self, dest, skip_if_older=True):
+         real_dest = dest
+-        if not isinstance(dest, basestring):
++        if not isinstance(dest, str):
+             fd, dest = mkstemp()
+             os.close(fd)
+             os.remove(dest)
+-        assert isinstance(dest, basestring)
++        assert isinstance(dest, str)
+         # If File.copy didn't actually copy because dest is newer, check the
+         # file sizes. If dest is smaller, it means it is already stripped and
+         # elfhacked, so we can skip.
+@@ -299,7 +299,7 @@
+         File.__init__(self, path)
+ 
+     def copy(self, dest, skip_if_older=True):
+-        assert isinstance(dest, basestring)
++        assert isinstance(dest, str)
+ 
+         # The logic in this function is complicated by the fact that symlinks
+         # aren't universally supported. So, where symlinks aren't supported, we
+@@ -400,7 +400,7 @@
+         self.required = required
+ 
+     def copy(self, dest, skip_if_older=True):
+-        if isinstance(dest, basestring):
++        if isinstance(dest, str):
+             dest = Dest(dest)
+         else:
+             assert isinstance(dest, Dest)
+@@ -432,7 +432,7 @@
+         '''
+         Invokes the preprocessor to create the destination file.
+         '''
+-        if isinstance(dest, basestring):
++        if isinstance(dest, str):
+             dest = Dest(dest)
+         else:
+             assert isinstance(dest, Dest)
+@@ -563,7 +563,7 @@
+         the individual XPTs to link.
+         skip_if_older is ignored.
+         '''
+-        if isinstance(dest, basestring):
++        if isinstance(dest, str):
+             dest = Dest(dest)
+         assert isinstance(dest, Dest)
+ 
+@@ -1014,7 +1014,7 @@
+         from mozpack.copier import FileRegistry
+         self.files = FileRegistry()
+ 
+-        for base, finder in sorted(finders.iteritems()):
++        for base, finder in sorted(finders.items()):
+             if self.files.contains(base):
+                 self.files.remove(base)
+             for p, f in finder.find(''):
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozpack/hg.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozpack/hg.py	(refactored)
+@@ -27,7 +27,7 @@
+ # do not wish to do so, delete this exception statement from your
+ # version.
+ 
+-from __future__ import absolute_import
++
+ 
+ import mercurial.error as error
+ import mercurial.hg as hg
+@@ -56,7 +56,7 @@
+ 
+         Accepts a Mercurial localrepo and changectx instance.
+         """
+-        if isinstance(repo, (str, unicode)):
++        if isinstance(repo, str):
+             path = repo
+             repo = hg.repository(hgui.ui(), repo)
+         else:
+@@ -85,7 +85,7 @@
+         return self._get(path)
+ 
+     def _get(self, path):
+-        if isinstance(path, unicode):
++        if isinstance(path, str):
+             path = path.encode('utf-8', 'replace')
+ 
+         try:
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozpack/manifests.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozpack/manifests.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import absolute_import, unicode_literals
++
+ 
+ from contextlib import contextmanager
+ import json
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozpack/mozjar.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozpack/mozjar.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import absolute_import
++
+ 
+ from io import BytesIO
+ import struct
+@@ -13,8 +13,9 @@
+     ZIP_DEFLATED,
+ )
+ from collections import OrderedDict
+-from urlparse import urlparse, ParseResult
++from urllib.parse import urlparse, ParseResult
+ import mozpack.path as mozpath
++from functools import reduce
+ 
+ JAR_STORED = ZIP_STORED
+ JAR_DEFLATED = ZIP_DEFLATED
+@@ -68,7 +69,7 @@
+         an instance with empty fields.
+         '''
+         assert self.MAGIC and isinstance(self.STRUCT, OrderedDict)
+-        self.size_fields = set(t for t in self.STRUCT.itervalues()
++        self.size_fields = set(t for t in self.STRUCT.values()
+                                if not t in JarStruct.TYPE_MAPPING)
+         self._values = {}
+         if data:
+@@ -90,7 +91,7 @@
+         # For all fields used as other fields sizes, keep track of their value
+         # separately.
+         sizes = dict((t, 0) for t in self.size_fields)
+-        for name, t in self.STRUCT.iteritems():
++        for name, t in self.STRUCT.items():
+             if t in JarStruct.TYPE_MAPPING:
+                 value, size = JarStruct.get_data(t, data[offset:])
+             else:
+@@ -109,7 +110,7 @@
+         Initialize an instance with empty fields.
+         '''
+         self.signature = self.MAGIC
+-        for name, t in self.STRUCT.iteritems():
++        for name, t in self.STRUCT.items():
+             if name in self.size_fields:
+                 continue
+             self._values[name] = 0 if t in JarStruct.TYPE_MAPPING else ''
+@@ -134,9 +135,9 @@
+         from self.STRUCT.
+         '''
+         serialized = struct.pack('<I', self.signature)
+-        sizes = dict((t, name) for name, t in self.STRUCT.iteritems()
++        sizes = dict((t, name) for name, t in self.STRUCT.items()
+                      if not t in JarStruct.TYPE_MAPPING)
+-        for name, t in self.STRUCT.iteritems():
++        for name, t in self.STRUCT.items():
+             if t in JarStruct.TYPE_MAPPING:
+                 format, size = JarStruct.TYPE_MAPPING[t]
+                 if name in sizes:
+@@ -155,7 +156,7 @@
+         variable length fields.
+         '''
+         size = JarStruct.TYPE_MAPPING['uint32'][1]
+-        for name, type in self.STRUCT.iteritems():
++        for name, type in self.STRUCT.items():
+             if type in JarStruct.TYPE_MAPPING:
+                 size += JarStruct.TYPE_MAPPING[type][1]
+             else:
+@@ -176,7 +177,7 @@
+         return key in self._values
+ 
+     def __iter__(self):
+-        return self._values.iteritems()
++        return iter(self._values.items())
+ 
+     def __repr__(self):
+         return "<%s %s>" % (self.__class__.__name__,
+@@ -374,7 +375,7 @@
+             preload = JarStruct.get_data('uint32', self._data)[0]
+         entries = OrderedDict()
+         offset = self._cdir_end['cdir_offset']
+-        for e in xrange(self._cdir_end['cdir_entries']):
++        for e in range(self._cdir_end['cdir_entries']):
+             entry = JarCdirEntry(self._data[offset:])
+             offset += entry.size
+             # Creator host system. 0 is MSDOS, 3 is Unix
+@@ -385,7 +386,7 @@
+             xattr = entry['external_attr']
+             # Skip directories
+             if (host == 0 and xattr & 0x10) or (host == 3 and
+-                                                xattr & (040000 << 16)):
++                                                xattr & (0o40000 << 16)):
+                 continue
+             entries[entry['filename']] = entry
+             if entry['offset'] < preload:
+@@ -436,7 +437,7 @@
+             for file in jarReader:
+                 ...
+         '''
+-        for entry in self.entries.itervalues():
++        for entry in self.entries.values():
+             yield self._getreader(entry)
+ 
+     def __getitem__(self, name):
+@@ -529,7 +530,7 @@
+         headers = {}
+         preload_size = 0
+         # Prepare central directory entries
+-        for entry, content in self._contents.itervalues():
++        for entry, content in self._contents.values():
+             header = JarLocalFileHeader()
+             for name in entry.STRUCT:
+                 if name in header:
+@@ -544,7 +545,7 @@
+         end['disk_entries'] = len(self._contents)
+         end['cdir_entries'] = end['disk_entries']
+         end['cdir_size'] = reduce(lambda x, y: x + y[0].size,
+-                                  self._contents.values(), 0)
++                                  list(self._contents.values()), 0)
+         # On optimized archives, store the preloaded size and the central
+         # directory entries, followed by the first end of central directory.
+         if self._optimize:
+@@ -553,18 +554,18 @@
+             if preload_size:
+                 preload_size += offset
+             self._data.write(struct.pack('<I', preload_size))
+-            for entry, _ in self._contents.itervalues():
++            for entry, _ in self._contents.values():
+                 entry['offset'] += offset
+                 self._data.write(entry.serialize())
+             self._data.write(end.serialize())
+         # Store local file entries followed by compressed data
+-        for entry, content in self._contents.itervalues():
++        for entry, content in self._contents.values():
+             self._data.write(headers[entry].serialize())
+             self._data.write(content)
+         # On non optimized archives, store the central directory entries.
+         if not self._optimize:
+             end['cdir_offset'] = offset
+-            for entry, _ in self._contents.itervalues():
++            for entry, _ in self._contents.values():
+                 self._data.write(entry.serialize())
+         # Store the end of central directory.
+         self._data.write(end.serialize())
+@@ -599,7 +600,7 @@
+             deflater = data
+         else:
+             deflater = Deflater(compress, compress_level=self._compress_level)
+-            if isinstance(data, basestring):
++            if isinstance(data, str):
+                 deflater.write(data)
+             elif hasattr(data, 'read'):
+                 if hasattr(data, 'seek'):
+@@ -615,7 +616,7 @@
+             # Set creator host system (upper byte of creator_version)
+             # to 3 (Unix) so mode is honored when there is one.
+             entry['creator_version'] |= 3 << 8
+-            entry['external_attr'] = (mode & 0xFFFF) << 16L
++            entry['external_attr'] = (mode & 0xFFFF) << 16
+         if deflater.compressed:
+             entry['min_version'] = 20  # Version 2.0 supports deflated streams
+             entry['general_flag'] = 2  # Max compression
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozpack/path.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozpack/path.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import absolute_import
++
+ 
+ import posixpath
+ import os
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozpack/unify.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozpack/unify.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import absolute_import
++
+ 
+ from mozpack.files import (
+     BaseFinder,
+@@ -73,7 +73,7 @@
+         creating the instance.
+         skip_if_older is ignored.
+         '''
+-        assert isinstance(dest, basestring)
++        assert isinstance(dest, str)
+         tmpfiles = []
+         try:
+             for e in self._executables:
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozpack/chrome/flags.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozpack/chrome/flags.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import absolute_import
++
+ 
+ import re
+ from distutils.version import LooseVersion
+@@ -250,7 +250,7 @@
+             flags.match(application='foo', appversion='3.5') returns True
+             flags.match(application='foo', appversion='3.0') returns False
+         '''
+-        for name, value in filter.iteritems():
++        for name, value in filter.items():
+             if not name in self:
+                 continue
+             if not self[name].matches(value):
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozpack/chrome/manifest.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozpack/chrome/manifest.py	(refactored)
+@@ -2,11 +2,11 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import absolute_import
++
+ 
+ import re
+ import os
+-from urlparse import urlparse
++from urllib.parse import urlparse
+ import mozpack.path as mozpath
+ from mozpack.chrome.flags import Flags
+ from mozpack.errors import errors
+@@ -321,7 +321,7 @@
+         return self.serialize(self.contractID, self.cid)
+ 
+ # All manifest classes by their type name.
+-MANIFESTS_TYPES = dict([(c.type, c) for c in globals().values()
++MANIFESTS_TYPES = dict([(c.type, c) for c in list(globals().values())
+                        if type(c) == type and issubclass(c, ManifestEntry)
+                        and hasattr(c, 'type') and c.type])
+ 
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozpack/packager/__init__.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozpack/packager/__init__.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import absolute_import
++
+ 
+ from mozbuild.preprocessor import Preprocessor
+ import re
+@@ -50,7 +50,7 @@
+         '''
+         Split [1, 2, 3, 4, 5, 6, 7] into [(1, 2, 3), (4, 5, 6)].
+         '''
+-        return zip(*[iter(lst)] * 3)
++        return list(zip(*[iter(lst)] * 3))
+ 
+     KEY_VALUE_RE = re.compile(r'''
+         \s*                 # optional whitespace.
+@@ -118,7 +118,7 @@
+         destdir = options.pop('destdir', '')
+         if options:
+             errors.fatal('Malformed manifest: options %s not recognized'
+-                         % options.keys())
++                         % list(options.keys()))
+         return Component(name, destdir=destdir)
+ 
+ 
+@@ -328,7 +328,7 @@
+ 
+         bases = self.get_bases()
+         broken_bases = sorted(
+-            m for m, includer in self._included_manifests.iteritems()
++            m for m, includer in self._included_manifests.items()
+             if mozpath.basedir(m, bases) != mozpath.basedir(includer, bases))
+         for m in broken_bases:
+             errors.fatal('"%s" is included from "%s", which is outside "%s"' %
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozpack/packager/formats.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozpack/packager/formats.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import absolute_import
++
+ 
+ from mozpack.chrome.manifest import (
+     Manifest,
+@@ -11,7 +11,7 @@
+     ManifestBinaryComponent,
+     ManifestResource,
+ )
+-from urlparse import urlparse
++from urllib.parse import urlparse
+ import mozpack.path as mozpath
+ from mozpack.files import (
+     ManifestFile,
+@@ -89,7 +89,7 @@
+         Return the deepest base directory containing the given path.
+         '''
+         self._frozen_bases = True
+-        base = mozpath.basedir(path, self._sub_formatter.keys())
++        base = mozpath.basedir(path, list(self._sub_formatter.keys()))
+         relpath = mozpath.relpath(path, base) if base else path
+         return base, relpath
+ 
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozpack/packager/l10n.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozpack/packager/l10n.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import absolute_import
++
+ 
+ '''
+ Replace localized parts of a packaged directory with data from a langpack
+@@ -162,7 +162,7 @@
+             if not path:
+                 continue
+         else:
+-            base = mozpath.basedir(p, paths.keys())
++            base = mozpath.basedir(p, list(paths.keys()))
+             if base:
+                 subpath = mozpath.relpath(p, base)
+                 path = mozpath.normpath(mozpath.join(paths[base],
+@@ -206,7 +206,7 @@
+                     formatter.add(p, f)
+ 
+     # Transplant jar preloading information.
+-    for path, log in app_finder.jarlogs.iteritems():
++    for path, log in app_finder.jarlogs.items():
+         assert isinstance(copier[path], Jarrer)
+         copier[path].preload([l.replace(locale, l10n_locale) for l in log])
+ 
+@@ -237,7 +237,7 @@
+         finders = {
+             '': l10n_finder,
+         }
+-        for base, path in extra_l10n.iteritems():
++        for base, path in extra_l10n.items():
+             finders[base] = UnpackFinder(path)
+         l10n_finder = ComposedFinder(finders)
+     copier = FileCopier()
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozpack/packager/unpack.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozpack/packager/unpack.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import absolute_import
++
+ 
+ import mozpack.path as mozpath
+ from mozpack.files import (
+@@ -27,7 +27,7 @@
+     FlatFormatter,
+     STARTUP_CACHE_PATHS,
+ )
+-from urlparse import urlparse
++from urllib.parse import urlparse
+ 
+ 
+ class UnpackFinder(BaseFinder):
+@@ -149,7 +149,7 @@
+         if not any(f.compressed for f in jar):
+             self.compressed = False
+         if jar.last_preloaded:
+-            jarlog = jar.entries.keys()
++            jarlog = list(jar.entries.keys())
+             self.jarlogs[path] = jarlog[:jarlog.index(jar.last_preloaded) + 1]
+         return jar
+ 
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozpack/test/test_archive.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozpack/test/test_archive.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import absolute_import
++
+ 
+ import hashlib
+ import os
+@@ -69,7 +69,7 @@
+         try:
+             tp = os.path.join(d, 'test.tar')
+             with open(tp, 'wb') as fh:
+-                with self.assertRaisesRegexp(ValueError, 'not a regular'):
++                with self.assertRaisesRegex(ValueError, 'not a regular'):
+                     create_tar_from_files(fh, {'test': d})
+         finally:
+             shutil.rmtree(d)
+@@ -89,9 +89,9 @@
+ 
+             tp = os.path.join(d, 'test.tar')
+             with open(tp, 'wb') as fh:
+-                with self.assertRaisesRegexp(ValueError, 'cannot add file with setuid'):
++                with self.assertRaisesRegex(ValueError, 'cannot add file with setuid'):
+                     create_tar_from_files(fh, {'test': uid})
+-                with self.assertRaisesRegexp(ValueError, 'cannot add file with setuid'):
++                with self.assertRaisesRegex(ValueError, 'cannot add file with setuid'):
+                     create_tar_from_files(fh, {'test': gid})
+         finally:
+             shutil.rmtree(d)
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozpack/test/test_chrome_manifest.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozpack/test/test_chrome_manifest.py	(refactored)
+@@ -24,7 +24,7 @@
+     parse_manifest_line,
+ )
+ from mozpack.errors import errors, AccumulatedErrors
+-from test_errors import TestErrors
++from .test_errors import TestErrors
+ 
+ 
+ class TestManifest(unittest.TestCase):
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozpack/test/test_copier.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozpack/test/test_copier.py	(refactored)
+@@ -127,7 +127,7 @@
+             'bar': [],
+         }
+         reg = FileRegistry()
+-        for path, parts in cases.iteritems():
++        for path, parts in cases.items():
+             self.assertEqual(reg._partial_paths(path), parts)
+ 
+     def test_file_registry(self):
+@@ -414,7 +414,7 @@
+         self.assertTrue(stat.S_ISDIR(st.st_mode))
+ 
+         # What's worse, we have no record that dest was created.
+-        self.assertEquals(len(result.updated_files), 0)
++        self.assertEqual(len(result.updated_files), 0)
+ 
+         # But we do have an erroneous record of an optional file
+         # existing when it does not.
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozpack/test/test_errors.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozpack/test/test_errors.py	(refactored)
+@@ -10,7 +10,7 @@
+ import unittest
+ import mozunit
+ import sys
+-from cStringIO import StringIO
++from io import StringIO
+ 
+ 
+ class TestErrors(object):
+@@ -30,14 +30,14 @@
+         errors.warn('foo')
+         self.assertRaises(ErrorMessage, errors.error, 'foo')
+         self.assertRaises(ErrorMessage, errors.fatal, 'foo')
+-        self.assertEquals(self.get_output(), ['Warning: foo'])
++        self.assertEqual(self.get_output(), ['Warning: foo'])
+ 
+     def test_ignore_errors(self):
+         errors.ignore_errors()
+         errors.warn('foo')
+         errors.error('bar')
+         self.assertRaises(ErrorMessage, errors.fatal, 'foo')
+-        self.assertEquals(self.get_output(), ['Warning: foo', 'Warning: bar'])
++        self.assertEqual(self.get_output(), ['Warning: foo', 'Warning: bar'])
+ 
+     def test_no_error(self):
+         with errors.accumulate():
+@@ -47,14 +47,14 @@
+         with self.assertRaises(AccumulatedErrors):
+             with errors.accumulate():
+                 errors.error('1')
+-        self.assertEquals(self.get_output(), ['Error: 1'])
++        self.assertEqual(self.get_output(), ['Error: 1'])
+ 
+     def test_error_loop(self):
+         with self.assertRaises(AccumulatedErrors):
+             with errors.accumulate():
+                 for i in range(3):
+                     errors.error('%d' % i)
+-        self.assertEquals(self.get_output(),
++        self.assertEqual(self.get_output(),
+                           ['Error: 0', 'Error: 1', 'Error: 2'])
+ 
+     def test_multiple_errors(self):
+@@ -67,7 +67,7 @@
+                     else:
+                         errors.error('%d' % i)
+                 errors.error('bar')
+-        self.assertEquals(self.get_output(),
++        self.assertEqual(self.get_output(),
+                           ['Error: foo', 'Error: 0', 'Error: 1',
+                            'Warning: 2', 'Error: bar'])
+ 
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozpack/test/test_files.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozpack/test/test_files.py	(refactored)
+@@ -61,7 +61,7 @@
+ import mozpack.path as mozpath
+ from tempfile import mkdtemp
+ from io import BytesIO
+-from StringIO import StringIO
++from io import StringIO
+ from xpt import Typelib
+ 
+ 
+@@ -146,7 +146,7 @@
+         dest.write('qux')
+         self.assertEqual(dest.read(), 'qux')
+ 
+-rand = ''.join(random.choice(string.letters) for i in xrange(131597))
++rand = ''.join(random.choice(string.letters) for i in range(131597))
+ samples = [
+     '',
+     'test',
+@@ -268,7 +268,7 @@
+     def test_absolute_relative(self):
+         AbsoluteSymlinkFile('/foo')
+ 
+-        with self.assertRaisesRegexp(ValueError, 'Symlink target not absolute'):
++        with self.assertRaisesRegex(ValueError, 'Symlink target not absolute'):
+             AbsoluteSymlinkFile('./foo')
+ 
+     def test_symlink_file(self):
+@@ -477,7 +477,7 @@
+ 
+ class TestExistingFile(TestWithTmpDir):
+     def test_required_missing_dest(self):
+-        with self.assertRaisesRegexp(ErrorMessage, 'Required existing file'):
++        with self.assertRaisesRegex(ErrorMessage, 'Required existing file'):
+             f = ExistingFile(required=True)
+             f.copy(self.tmppath('dest'))
+ 
+@@ -566,7 +566,7 @@
+         with JarWriter(src) as jar:
+             for content in samples:
+                 name = ''.join(random.choice(string.letters)
+-                               for i in xrange(8))
++                               for i in range(8))
+                 jar.add(name, content, compress=True)
+                 contents[name] = content
+ 
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozpack/test/test_manifests.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozpack/test/test_manifests.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import unicode_literals
++
+ 
+ import os
+ 
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozpack/test/test_mozjar.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozpack/test/test_mozjar.py	(refactored)
+@@ -16,8 +16,8 @@
+ from mozpack.test.test_files import MockDest
+ import unittest
+ import mozunit
+-from cStringIO import StringIO
+-from urllib import pathname2url
++from io import StringIO
++from urllib.request import pathname2url
+ import mozpack.path as mozpath
+ import os
+ 
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozpack/test/test_packager_formats.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozpack/test/test_packager_formats.py	(refactored)
+@@ -128,7 +128,7 @@
+                 'foo': read_interfaces(foo2_xpt.open())['foo'],
+                 'bar': read_interfaces(bar_xpt.open())['bar'],
+             },
+-        }.iteritems()
++        }.items()
+     })
+ 
+ RESULT_JAR = {
+@@ -174,7 +174,7 @@
+     },
+     'addon1.xpi': {
+         mozpath.relpath(p, 'addon1'): f
+-        for p, f in RESULT_FLAT.iteritems()
++        for p, f in RESULT_FLAT.items()
+         if p.startswith('addon1/')
+     },
+ })
+@@ -234,7 +234,7 @@
+ CONTENTS_WITH_BASE = {
+     'bases': {
+         mozpath.join('base/root', b) if b else 'base/root': a
+-        for b, a in CONTENTS['bases'].iteritems()
++        for b, a in CONTENTS['bases'].items()
+     },
+     'manifests': [
+         m.move(mozpath.join('base/root', m.base))
+@@ -242,7 +242,7 @@
+     ],
+     'files': {
+         mozpath.join('base/root', p): f
+-        for p, f in CONTENTS['files'].iteritems()
++        for p, f in CONTENTS['files'].items()
+     },
+ }
+ 
+@@ -255,7 +255,7 @@
+ def result_with_base(results):
+     result = {
+         mozpath.join('base/root', p): v
+-        for p, v in results.iteritems()
++        for p, v in results.items()
+     }
+     result.update(EXTRA_CONTENTS)
+     return result
+@@ -271,13 +271,13 @@
+ 
+ 
+ def fill_formatter(formatter, contents):
+-    for base, is_addon in contents['bases'].items():
++    for base, is_addon in list(contents['bases'].items()):
+         formatter.add_base(base, is_addon)
+ 
+     for manifest in contents['manifests']:
+         formatter.add_manifest(manifest)
+ 
+-    for k, v in contents['files'].iteritems():
++    for k, v in contents['files'].items():
+         if k.endswith('.xpt'):
+             formatter.add_interfaces(k, v)
+         else:
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozpack/test/test_packager_l10n.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozpack/test/test_packager_l10n.py	(refactored)
+@@ -4,7 +4,7 @@
+ 
+ import unittest
+ import mozunit
+-from test_packager import MockFinder
++from .test_packager import MockFinder
+ from mozpack.packager import l10n
+ from mozpack.files import (
+     GeneratedFile,
+@@ -118,7 +118,7 @@
+ 
+         self.assertEqual(
+             dict((p, f.open().read()) for p, f in copier),
+-            dict((p, f.open().read()) for p, f in repacked.iteritems())
++            dict((p, f.open().read()) for p, f in repacked.items())
+         )
+ 
+ 
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozpack/test/test_unify.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozpack/test/test_unify.py	(refactored)
+@@ -13,7 +13,7 @@
+ from mozpack.files import FileFinder
+ from mozpack.mozjar import JarWriter
+ from mozpack.test.test_files import MockDest
+-from cStringIO import StringIO
++from io import StringIO
+ import os
+ import sys
+ from mozpack.errors import (
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozpack/test/support/minify_js_verify.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozpack/test/support/minify_js_verify.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import print_function
++
+ import sys
+ 
+ 
+--- thunderbird-52.9.0/mozilla/build/moz.configure/keyfiles.configure.old	2017-04-11 02:13:08.000000000 +0000
++++ thunderbird-52.9.0/mozilla/build/moz.configure/keyfiles.configure	2018-07-10 07:57:16.030000000 +0000
+@@ -16,8 +16,8 @@
+ 
+     @depends('--with-%s-keyfile' % name)
+     @checking('for the %s key' % desc, lambda x: x and x is not no_key)
+-    @imports(_from='__builtin__', _import='open')
+-    @imports(_from='__builtin__', _import='IOError')
++    @imports(_from='builtins', _import='open')
++    @imports(_from='builtins', _import='IOError')
+     def keyfile(value):
+         if value:
+             try:
+--- thunderbird-52.9.0/mozilla/build/moz.configure/windows.configure.old	2017-04-11 02:13:08.000000000 +0000
++++ thunderbird-52.9.0/mozilla/build/moz.configure/windows.configure	2018-07-10 07:57:25.210000000 +0000
+@@ -19,7 +19,7 @@
+ 
+ 
+ @depends_win('--with-windows-version')
+-@imports(_from='__builtin__', _import='ValueError')
++@imports(_from='builtins', _import='ValueError')
+ def valid_windows_version(value):
+     if not value:
+         die('Cannot build with --without-windows-version')
+@@ -56,8 +56,8 @@
+ # 8.1.
+ @imports('os')
+ @imports('re')
+-@imports(_from='__builtin__', _import='sorted')
+-@imports(_from='__builtin__', _import='WindowsError')
++@imports(_from='builtins', _import='sorted')
++@imports(_from='builtins', _import='WindowsError')
+ def get_sdk_dirs(sdk, subdir):
+     def get_dirs_containing(sdk, stem, subdir):
+         base = os.path.join(sdk, stem)
+@@ -102,7 +102,7 @@
+ @depends_win(c_compiler, windows_sdk_dir, valid_windows_version,
+              'WINDOWSSDKDIR')
+ @checking('for Windows SDK', valid_windows_sdk_dir_result)
+-@imports(_from='__builtin__', _import='sorted')
++@imports(_from='builtins', _import='sorted')
+ @imports(_from='textwrap', _import='dedent')
+ def valid_windows_sdk_dir(compiler, windows_sdk_dir, target_version,
+                           windows_sdk_dir_env):
+@@ -179,7 +179,7 @@
+ @depends_win(windows_sdk_dir, 'WINDOWSSDKDIR')
+ @checking('for Universal CRT SDK', valid_ucrt_sdk_dir_result)
+ @imports('os')
+-@imports(_from='__builtin__', _import='sorted')
++@imports(_from='builtins', _import='sorted')
+ @imports(_import='mozpack.path', _as='mozpath')
+ def valid_ucrt_sdk_dir(windows_sdk_dir, windows_sdk_dir_env):
+     if windows_sdk_dir_env:
+--- thunderbird-52.9.0/mozilla/build/moz.configure/toolchain.configure.old	2017-04-11 02:13:08.000000000 +0000
++++ thunderbird-52.9.0/mozilla/build/moz.configure/toolchain.configure	2018-07-10 07:57:28.830000000 +0000
+@@ -395,7 +395,7 @@
+ 
+ 
+ @imports(_from='collections', _import='defaultdict')
+-@imports(_from='__builtin__', _import='sorted')
++@imports(_from='builtins', _import='sorted')
+ def get_vc_paths(base):
+     vc = defaultdict(lambda: defaultdict(dict))
+     subkey = r'Microsoft\VisualStudio\VC\*\*\*\Compiler'
+--- thunderbird-52.9.0/mozilla/build/moz.configure/util.configure.old	2017-04-11 02:13:08.000000000 +0000
++++ thunderbird-52.9.0/mozilla/build/moz.configure/util.configure	2018-07-10 07:57:31.540000000 +0000
+@@ -234,7 +234,7 @@
+ #     ('19.0', 'x64', r'C:\...\amd64\cl.exe')
+ #     ('19.0', 'x86', r'C:\...\amd64_x86\cl.exe')
+ @imports(_import='_winreg', _as='winreg')
+-@imports(_from='__builtin__', _import='WindowsError')
++@imports(_from='builtins', _import='WindowsError')
+ @imports(_from='fnmatch', _import='fnmatch')
+ def get_registry_values(pattern):
+     def enum_helper(func, key):
+--- thunderbird-52.9.0/mozilla/build/moz.configure/checks.configure.old	2017-04-11 02:13:08.000000000 +0000
++++ thunderbird-52.9.0/mozilla/build/moz.configure/checks.configure	2018-07-10 07:57:35.630000000 +0000
+@@ -12,7 +12,7 @@
+ # _declare_exceptions template, and add it to the return statement. Then
+ # destructure in the assignment below the function declaration.
+ @template
+-@imports(_from='__builtin__', _import='Exception')
++@imports(_from='builtins', _import='Exception')
+ def _declare_exceptions():
+     class FatalCheckError(Exception):
+         '''An exception to throw from a function decorated with @checking.
+--- thunderbird-52.9.0/mozilla/build/moz.configure/old.configure.old	2017-04-11 02:13:08.000000000 +0000
++++ thunderbird-52.9.0/mozilla/build/moz.configure/old.configure	2018-07-10 07:57:38.970000000 +0000
+@@ -61,8 +61,8 @@
+ 
+ @depends('OLD_CONFIGURE', mozconfig, autoconf, check_build_environment, shell,
+          old_configure_assignments, build_project)
+-@imports(_from='__builtin__', _import='open')
+-@imports(_from='__builtin__', _import='print')
++@imports(_from='builtins', _import='open')
++@imports(_from='builtins', _import='print')
+ @imports('glob')
+ @imports('itertools')
+ @imports('subprocess')
+@@ -302,8 +302,8 @@
+     '--enable-calendar',
+     '--enable-incomplete-external-linkage',
+ )
+-@imports(_from='__builtin__', _import='compile')
+-@imports(_from='__builtin__', _import='open')
++@imports(_from='builtins', _import='compile')
++@imports(_from='builtins', _import='open')
+ @imports('logging')
+ @imports('os')
+ @imports('subprocess')
+--- thunderbird-52.9.0/mozilla/build/moz.configure/android-ndk.configure.old	2017-04-11 02:13:08.000000000 +0000
++++ thunderbird-52.9.0/mozilla/build/moz.configure/android-ndk.configure	2018-07-10 07:57:41.120000000 +0000
+@@ -22,7 +22,7 @@
+           default=min_android_version)
+ 
+ @depends('--with-android-version', min_android_version)
+-@imports(_from='__builtin__', _import='ValueError')
++@imports(_from='builtins', _import='ValueError')
+ def android_version(value, min_version):
+     if not value:
+         # Someone has passed --without-android-version.
+--- thunderbird-52.9.0/mozilla/build/moz.configure/init.configure.old	2017-04-11 02:13:08.000000000 +0000
++++ thunderbird-52.9.0/mozilla/build/moz.configure/init.configure	2018-07-10 07:57:44.330000000 +0000
+@@ -636,7 +636,7 @@
+ # - otherwise, if we have "a" in GRE_MILESTONE, we're building Nightly or Aurora
+ # - otherwise, we're building Release/Beta (define RELEASE_OR_BETA)
+ @depends(check_build_environment, '--help')
+-@imports(_from='__builtin__', _import='open')
++@imports(_from='builtins', _import='open')
+ def milestone(build_env, _):
+     milestone_path = os.path.join(build_env.topsrcdir,
+                                   'config',
+diff -aur thunderbird-52.9.0/mozilla/config/mozunit.py mozjs-fixed/config/mozunit.py
+--- thunderbird-52.9.0/mozilla/config/mozunit.py	2017-04-11 02:13:09.000000000 +0000
++++ mozjs-fixed/config/mozunit.py	2018-07-10 08:19:37.770000000 +0000
+@@ -5,7 +5,7 @@
+ from unittest import TextTestRunner as _TestRunner, TestResult as _TestResult
+ import unittest
+ import inspect
+-from StringIO import StringIO
++from io import StringIO
+ import os
+ import sys
+ 
+@@ -74,7 +74,7 @@
+ 
+     def printFail(self, test, err):
+         exctype, value, tb = err
+-        message = value.message.splitlines()[0] if value.message else 'NO MESSAGE'
++        message = str(value).splitlines()[0] if str(value) else 'NO MESSAGE'
+         # Skip test runner traceback levels
+         while tb and self._is_relevant_tb_level(tb):
+             tb = tb.tb_next
+diff -aur thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/backend/configenvironment.py mozjs-fixed/python/mozbuild/mozbuild/backend/configenvironment.py
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/backend/configenvironment.py	2018-07-10 08:24:43.150000000 +0000
++++ mozjs-fixed/python/mozbuild/mozbuild/backend/configenvironment.py	2018-07-10 08:15:12.650000000 +0000
+@@ -8,7 +8,8 @@
+ import sys
+ 
+ from collections import Iterable
+-from types import StringTypes, ModuleType
++from types import ModuleType
++StringTypes = (str,)
+ 
+ import mozpack.path as mozpath
+ 
+diff -aur thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/makeutil.py mozjs-fixed/python/mozbuild/mozbuild/makeutil.py
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/makeutil.py	2018-07-10 08:24:43.150000000 +0000
++++ mozjs-fixed/python/mozbuild/mozbuild/makeutil.py	2018-07-10 08:20:05.230000000 +0000
+@@ -6,7 +6,6 @@
+ 
+ import os
+ import re
+-from types import StringTypes
+ from collections import Iterable
+ 
+ 
+@@ -103,19 +102,19 @@
+ 
+     def add_targets(self, targets):
+         '''Add additional targets to the rule.'''
+-        assert isinstance(targets, Iterable) and not isinstance(targets, StringTypes)
++        assert isinstance(targets, Iterable) and not isinstance(targets, str)
+         self._targets.update(targets)
+         return self
+ 
+     def add_dependencies(self, deps):
+         '''Add dependencies to the rule.'''
+-        assert isinstance(deps, Iterable) and not isinstance(deps, StringTypes)
++        assert isinstance(deps, Iterable) and not isinstance(deps, str)
+         self._dependencies.update(deps)
+         return self
+ 
+     def add_commands(self, commands):
+         '''Add commands to the rule.'''
+-        assert isinstance(commands, Iterable) and not isinstance(commands, StringTypes)
++        assert isinstance(commands, Iterable) and not isinstance(commands, str)
+         self._commands.extend(commands)
+         return self
+ 
+diff -aur thunderbird-52.9.0/mozilla/python/which/which.py mozjs-fixed/python/which/which.py
+--- thunderbird-52.9.0/mozilla/python/which/which.py	2017-04-11 02:13:24.000000000 +0000
++++ mozjs-fixed/python/which/which.py	2018-07-10 08:11:01.570000000 +0000
+@@ -280,17 +280,17 @@
+     try:
+         optlist, args = getopt.getopt(argv[1:], 'haVvqp:e:',
+             ['help', 'all', 'version', 'verbose', 'quiet', 'path=', 'exts='])
+-    except getopt.GetoptError, msg:
++    except getopt.GetoptError as msg:
+         sys.stderr.write("which: error: %s. Your invocation was: %s\n"\
+                          % (msg, argv))
+         sys.stderr.write("Try 'which --help'.\n")
+         return 1
+     for opt, optarg in optlist:
+         if opt in ('-h', '--help'):
+-            print _cmdlnUsage
++            print(_cmdlnUsage)
+             return 0
+         elif opt in ('-V', '--version'):
+-            print "which %s" % __version__
++            print("which %s" % __version__)
+             return 0
+         elif opt in ('-a', '--all'):
+             all = 1
+@@ -318,9 +318,9 @@
+         nmatches = 0
+         for match in whichgen(arg, path=altpath, verbose=verbose, exts=exts):
+             if verbose:
+-                print "%s (%s)" % match
++                print("%s (%s)" % match)
+             else:
+-                print match
++                print(match)
+             nmatches += 1
+             if not all:
+                 break
+diff -aur thunderbird-52.9.0/mozilla/testing/mozbase/mozprocess/mozprocess/processhandler.py mozjs-fixed/testing/mozbase/mozprocess/mozprocess/processhandler.py
+--- thunderbird-52.9.0/mozilla/testing/mozbase/mozprocess/mozprocess/processhandler.py	2017-04-11 02:13:06.000000000 +0000
++++ mozjs-fixed/testing/mozbase/mozprocess/mozprocess/processhandler.py	2018-07-10 08:13:15.440000000 +0000
+@@ -11,7 +11,7 @@
+ import threading
+ import time
+ import traceback
+-from Queue import Queue, Empty
++from queue import Queue, Empty
+ from datetime import datetime
+ 
+ __all__ = ['ProcessHandlerMixin', 'ProcessHandler', 'LogOutput',
+@@ -121,14 +121,14 @@
+             thread = threading.current_thread().name
+             print("DBG::MOZPROC PID:{} ({}) | {}".format(self.pid, thread, msg))
+ 
+-        def __del__(self, _maxint=sys.maxint):
++        def __del__(self, _maxint=sys.maxsize):
+             if isWin:
+                 handle = getattr(self, '_handle', None)
+                 if handle:
+                     if hasattr(self, '_internal_poll'):
+                         self._internal_poll(_deadstate=_maxint)
+                     else:
+-                        self.poll(_deadstate=sys.maxint)
++                        self.poll(_deadstate=sys.maxsize)
+                 if handle or self._job or self._io_port:
+                     self._cleanup()
+             else:
+@@ -267,7 +267,7 @@
+                 if not (can_create_job or can_nest_jobs):
+                     # Since we've warned, we just log info here to inform you
+                     # of the consequence of setting ignore_children = True
+-                    print "ProcessManager NOT managing child processes"
++                    print("ProcessManager NOT managing child processes")
+ 
+                 # create the process
+                 hp, ht, pid, tid = winprocess.CreateProcess(
+@@ -534,11 +534,11 @@
+ 
+                     if rc == winprocess.WAIT_TIMEOUT:
+                         # The process isn't dead, so kill it
+-                        print "Timed out waiting for process to close, attempting TerminateProcess"
++                        print("Timed out waiting for process to close, attempting TerminateProcess")
+                         self.kill()
+                     elif rc == winprocess.WAIT_OBJECT_0:
+                         # We caught WAIT_OBJECT_0, which indicates all is well
+-                        print "Single process terminated successfully"
++                        print("Single process terminated successfully")
+                         self.returncode = winprocess.GetExitCodeProcess(self._handle)
+                     else:
+                         # An error occured we should probably throw
+diff -aur thunderbird-52.9.0/mozilla/build/moz.configure/checks.configure mozjs-fixed/build/moz.configure/checks.configure
+--- thunderbird-52.9.0/mozilla/build/moz.configure/checks.configure	2018-07-10 10:29:50.380000000 +0000
++++ mozjs-fixed/build/moz.configure/checks.configure	2018-07-10 10:27:45.350000000 +0000
+@@ -52,7 +52,7 @@
+                 try:
+                     ret = func(*args, **kwargs)
+                 except FatalCheckError as e:
+-                    error = e.message
++                    error = str(e)
+                 display_ret = callback(ret) if callback else ret
+                 if display_ret is True:
+                     log.info('yes')
+diff -aur thunderbird-52.9.0/mozilla/build/moz.configure/init.configure mozjs-fixed/build/moz.configure/init.configure
+--- thunderbird-52.9.0/mozilla/build/moz.configure/init.configure	2018-07-10 10:29:50.380000000 +0000
++++ mozjs-fixed/build/moz.configure/init.configure	2018-07-10 10:23:19.960000000 +0000
+@@ -213,7 +213,7 @@
+     def early_options():
+         return set(
+             option.env
+-            for option in __sandbox__._options.itervalues()
++            for option in list(__sandbox__._options.values())
+             if option.env
+         )
+     return early_options
+@@ -297,7 +297,7 @@
+     # There is also a quartet form:
+     #   CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
+     # But we can consider the "KERNEL-OPERATING_SYSTEM" as one.
+-    cpu, manufacturer, os = triplet.split('-', 2)
++    cpu, manufacturer, os = triplet.decode('utf-8').split('-', 2)
+ 
+     # Autoconf uses config.sub to validate and canonicalize those triplets,
+     # but the granularity of its results has never been satisfying to our
+@@ -727,7 +727,7 @@
+ def all_configure_options(_):
+     result = []
+     previous = None
+-    for option in __sandbox__._options.itervalues():
++    for option in list(__sandbox__._options.values()):
+         # __sandbox__._options contains items for both option.name and
+         # option.env. But it's also an OrderedDict, meaning both are
+         # consecutive.
+diff -aur thunderbird-52.9.0/mozilla/build/moz.configure/old.configure mozjs-fixed/build/moz.configure/old.configure
+--- thunderbird-52.9.0/mozilla/build/moz.configure/old.configure	2018-07-10 10:29:50.380000000 +0000
++++ mozjs-fixed/build/moz.configure/old.configure	2018-07-10 10:19:19.880000000 +0000
+@@ -360,7 +360,7 @@
+         # Every variation of the exec() function I tried led to:
+         # SyntaxError: unqualified exec is not allowed in function 'main' it
+         # contains a nested function with free variables
+-        exec code in raw_config
++        exec(code, raw_config)
+ 
+     # Ensure all the flags known to old-configure appear in the
+     # @old_configure_options above.
+diff -aur thunderbird-52.9.0/mozilla/build/moz.configure/toolchain.configure mozjs-fixed/build/moz.configure/toolchain.configure
+--- thunderbird-52.9.0/mozilla/build/moz.configure/toolchain.configure	2018-07-10 10:29:50.380000000 +0000
++++ mozjs-fixed/build/moz.configure/toolchain.configure	2018-07-10 10:28:13.020000000 +0000
+@@ -244,7 +244,7 @@
+         ('CPU', CPU_preprocessor_checks),
+         ('KERNEL', kernel_preprocessor_checks),
+     ):
+-        for n, (value, condition) in enumerate(preprocessor_checks.iteritems()):
++        for n, (value, condition) in enumerate(preprocessor_checks.items()):
+             check += dedent('''\
+                 #%(if)s %(condition)s
+                 %%%(name)s "%(value)s"
+@@ -278,9 +278,9 @@
+     data = {}
+     for line in result.splitlines():
+         if line.startswith(b'%'):
+-            k, _, v = line.partition(' ')
+-            k = k.lstrip('%')
+-            data[k] = v.replace(' ', '').lstrip('"').rstrip('"')
++            k, _, v = line.partition(b' ')
++            k = k.lstrip(b'%').decode('utf-8')
++            data[k] = v.replace(b' ', b'').lstrip(b'"').rstrip(b'"').decode('utf-8')
+             log.debug('%s = %s', k, data[k])
+ 
+     try:
+diff -aur thunderbird-52.9.0/mozilla/build/moz.configure/util.configure mozjs-fixed/build/moz.configure/util.configure
+--- thunderbird-52.9.0/mozilla/build/moz.configure/util.configure	2018-07-10 10:29:50.380000000 +0000
++++ mozjs-fixed/build/moz.configure/util.configure	2018-07-10 10:23:54.980000000 +0000
+@@ -299,6 +299,8 @@
+ @imports(_from='mozbuild.configure.util', _import='Version', _as='_Version')
+ def Version(v):
+     'A version number that can be compared usefully.'
++    if isinstance(v, bytes):
++    	v = v.decode('utf-8')
+     return _Version(v)
+ 
+ # Denotes a deprecated option. Combines option() and @depends:
+diff -aur thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/configure/__init__.py mozjs-fixed/python/mozbuild/mozbuild/configure/__init__.py
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/configure/__init__.py	2018-07-10 10:29:50.350000000 +0000
++++ mozjs-fixed/python/mozbuild/mozbuild/configure/__init__.py	2018-07-10 10:24:33.200000000 +0000
+@@ -97,6 +97,9 @@
+             ', '.join(repr(d) for d in self.dependencies),
+         )
+ 
++    def __hash__(self):
++        return hash((str(self.func), tuple(self.dependencies), self.when))
++
+ 
+ class CombinedDependsFunction(DependsFunction):
+     def __init__(self, sandbox, func, dependencies):
+@@ -142,6 +145,9 @@
+     def __ne__(self, other):
+         return not self == other
+ 
++    def __hash__(self):
++        return hash((str(self.func), tuple(self.dependencies)))
++
+ class SandboxedGlobal(dict):
+     '''Identifiable dict type for use as function global'''
+ 
+@@ -192,7 +198,8 @@
+         b: __builtins__[b]
+         for b in ('None', 'False', 'True', 'int', 'bool', 'any', 'all', 'len',
+                   'list', 'tuple', 'set', 'dict', 'isinstance', 'getattr',
+-                  'hasattr', 'enumerate', 'range', 'zip')
++                  'hasattr', 'enumerate', 'range', 'zip', '__build_class__',
++                  'bytes', 'exec')
+     }, __import__=forbidden_import, str=str)
+ 
+     # Expose a limited set of functions from os.path
+@@ -267,7 +274,7 @@
+                 return method
+             def wrapped(*args, **kwargs):
+                 out_args = [
+-                    arg.decode(encoding) if isinstance(arg, str) else arg
++                    arg.decode(encoding) if isinstance(arg, bytes) else arg
+                     for arg in args
+                 ]
+                 return method(*out_args, **kwargs)
+diff -aur thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/configure/options.py mozjs-fixed/python/mozbuild/mozbuild/configure/options.py
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/configure/options.py	2018-07-10 10:29:50.350000000 +0000
++++ mozjs-fixed/python/mozbuild/mozbuild/configure/options.py	2018-07-10 10:15:41.760000000 +0000
+@@ -65,6 +65,9 @@
+         return '%s%s' % (self.__class__.__name__,
+                          super(OptionValue, self).__repr__())
+ 
++    def __hash__(self):
++        return hash(str(self))
++
+ 
+ class PositiveOptionValue(OptionValue):
+     '''Represents the value for a positive option (--enable/--with/--foo)
+diff -aur thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/configure/util.py mozjs-fixed/python/mozbuild/mozbuild/configure/util.py
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/configure/util.py	2018-07-10 10:29:50.350000000 +0000
++++ mozjs-fixed/python/mozbuild/mozbuild/configure/util.py	2018-07-10 09:04:28.150000000 +0000
+@@ -200,7 +200,7 @@
+         self._encoding = getpreferredencoding()
+ 
+     def write(self, buf):
+-        if self._encoding and isinstance(buf, str):
++        if self._encoding and isinstance(buf, bytes):
+             buf = buf.decode(self._encoding)
+         lines = buf.splitlines()
+         if not lines:
+diff -aur thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/virtualenv.py mozjs-fixed/python/mozbuild/mozbuild/virtualenv.py
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/virtualenv.py	2018-07-10 10:29:50.350000000 +0000
++++ mozjs-fixed/python/mozbuild/mozbuild/virtualenv.py	2018-07-10 10:11:47.710000000 +0000
+@@ -108,7 +108,7 @@
+         """
+         ver = subprocess.check_output([python, '-c', 'import sys; print(sys.hexversion)']).rstrip()
+         with open(self.exe_info_path, 'w') as fh:
+-            fh.write("%s\n" % ver)
++            fh.write("%s\n" % ver.decode('utf-8'))
+             fh.write("%s\n" % os.path.getsize(python))
+ 
+     def up_to_date(self, python=sys.executable):
+@@ -207,7 +207,7 @@
+         return self.virtualenv_root
+ 
+     def packages(self):
+-        with file(self.manifest_path, 'rU') as fh:
++        with open(self.manifest_path, 'rU') as fh:
+             packages = [line.rstrip().split(':')
+                         for line in fh]
+         return packages
+@@ -530,9 +530,9 @@
+ 
+     our = LooseVersion('%d.%d.%d' % (major, minor, micro))
+ 
+-    if major != MINIMUM_PYTHON_MAJOR or our < MINIMUM_PYTHON_VERSION:
+-        log_handle.write('Python %s or greater (but not Python 3) is '
+-            'required to build. ' % MINIMUM_PYTHON_VERSION)
++    if our < MINIMUM_PYTHON_VERSION:
++        log_handle.write('Python %s or greater is required to build. '
++            % MINIMUM_PYTHON_VERSION)
+         log_handle.write('You are running Python %s.\n' % our)
+ 
+         if os.name in ('nt', 'ce'):
+diff -aur thunderbird-52.9.0/mozilla/python/which/which.py mozjs-fixed/python/which/which.py
+--- thunderbird-52.9.0/mozilla/python/which/which.py	2018-07-10 10:29:50.380000000 +0000
++++ mozjs-fixed/python/which/which.py	2018-07-10 10:20:16.770000000 +0000
+@@ -243,7 +243,7 @@
+     If no match is found for the command, a WhichError is raised.
+     """
+     try:
+-        match = whichgen(command, path, verbose, exts).next()
++        match = next(whichgen(command, path, verbose, exts))
+     except StopIteration:
+         raise WhichError("Could not find '%s' on the path." % command)
+     return match
+--- thunderbird-52.9.0/mozilla/build/moz.configure/old.configure.old	2018-07-10 10:32:48.550000000 +0000
++++ thunderbird-52.9.0/mozilla/build/moz.configure/old.configure	2018-07-10 10:35:32.440000000 +0000
+@@ -107,7 +107,7 @@
+ 
+         # Make old-configure append to config.log, where we put our own log.
+         # This could be done with a m4 macro, but it's way easier this way
+-        script = script.replace('>./config.log', '>>./config.log')
++        script = script.replace(b'>./config.log', b'>>./config.log')
+ 
+         with open(old_configure, 'wb') as fh:
+             fh.write(script)
+--- thunderbird-52.9.0/mozilla/build/subconfigure.py.old	2017-04-11 02:13:09.000000000 +0000
++++ thunderbird-52.9.0/mozilla/build/subconfigure.py	2018-07-10 10:38:08.910000000 +0000
+@@ -27,7 +27,7 @@
+             return super(Pool, cls).__new__(cls)
+ 
+     def imap_unordered(self, fn, iterable):
+-        return itertools.imap(fn, iterable)
++        return map(fn, iterable)
+ 
+     def close(self):
+         pass
+@@ -131,11 +131,11 @@
+         is_set = cache.get('ac_cv_env_%s_set' % precious) == 'set'
+         value = cache.get('ac_cv_env_%s_value' % precious) if is_set else None
+         if value != env.get(precious):
+-            print 'Removing %s because of %s value change from:' \
+-                % (data['cache-file'], precious)
+-            print '  %s' % (value if value is not None else 'undefined')
+-            print 'to:'
+-            print '  %s' % env.get(precious, 'undefined')
++            print('Removing %s because of %s value change from:' \
++                % (data['cache-file'], precious))
++            print('  %s' % (value if value is not None else 'undefined'))
++            print('to:')
++            print('  %s' % env.get(precious, 'undefined'))
+             os.remove(data['cache-file'])
+             return True
+     return False
+@@ -333,8 +333,8 @@
+         # We're going to run it ourselves.
+         command += ['--no-create']
+ 
+-        print prefix_lines('configuring', relobjdir)
+-        print prefix_lines('running %s' % ' '.join(command[:-1]), relobjdir)
++        print(prefix_lines('configuring', relobjdir))
++        print(prefix_lines('running %s' % ' '.join(command[:-1]), relobjdir))
+         sys.stdout.flush()
+         try:
+             output += subprocess.check_output(command,
+@@ -368,7 +368,7 @@
+ 
+     if not skip_config_status:
+         if skip_configure:
+-            print prefix_lines('running config.status', relobjdir)
++            print(prefix_lines('running config.status', relobjdir))
+             sys.stdout.flush()
+         try:
+             output += subprocess.check_output([data['shell'], '-c',
+@@ -410,7 +410,7 @@
+     pool = Pool(len(subconfigures))
+     for relobjdir, returncode, output in \
+             pool.imap_unordered(run, subconfigures):
+-        print prefix_lines(output, relobjdir)
++        print(prefix_lines(output, relobjdir))
+         sys.stdout.flush()
+         ret = max(returncode, ret)
+         if ret:
+--- thunderbird-52.9.0/mozilla/build/moz.configure/old.configure.old	2018-07-10 10:38:53.530000000 +0000
++++ thunderbird-52.9.0/mozilla/build/moz.configure/old.configure	2018-07-10 10:41:32.560000000 +0000
+@@ -394,7 +394,7 @@
+ def post_old_configure(raw_config):
+     for k, v in raw_config['substs']:
+         set_old_configure_config(
+-            k[1:-1], v[1:-1] if isinstance(v, types.StringTypes) else v)
++            k[1:-1], v[1:-1] if isinstance(v, str) else v)
+ 
+-    for k, v in dict(raw_config['defines']).iteritems():
++    for k, v in dict(raw_config['defines']).items():
+         set_old_configure_define(k[1:-1], v[1:-1])
+--- thunderbird-52.9.0/mozilla/configure.py	(original)
++++ thunderbird-52.9.0/mozilla/configure.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from __future__ import print_function, unicode_literals
++
+ 
+ import codecs
+ import os
+@@ -45,11 +45,11 @@
+ 
+     sanitized_config = {}
+     sanitized_config['substs'] = {
+-        k: sanitized_bools(v) for k, v in config.iteritems()
++        k: sanitized_bools(v) for k, v in config.items()
+         if k not in ('DEFINES', 'non_global_defines', 'TOPSRCDIR', 'TOPOBJDIR')
+     }
+     sanitized_config['defines'] = {
+-        k: sanitized_bools(v) for k, v in config['DEFINES'].iteritems()
++        k: sanitized_bools(v) for k, v in config['DEFINES'].items()
+     }
+     sanitized_config['non_global_defines'] = config['non_global_defines']
+     sanitized_config['topsrcdir'] = config['TOPSRCDIR']
+@@ -71,7 +71,7 @@
+         ''') % {'python': config['PYTHON'], 'encoding': encoding})
+         # A lot of the build backend code is currently expecting byte
+         # strings and breaks in subtle ways with unicode strings. (bug 1296508)
+-        for k, v in sanitized_config.iteritems():
++        for k, v in sanitized_config.items():
+             fh.write('%s = encode(%s, encoding)\n' % (k, indented_repr(v)))
+         fh.write("__all__ = ['topobjdir', 'topsrcdir', 'defines', "
+                  "'non_global_defines', 'substs', 'mozconfig']")
+@@ -88,7 +88,7 @@
+     # executable permissions.
+     os.chmod('config.status', 0o755)
+     if config.get('MOZ_BUILD_APP') != 'js' or config.get('JS_STANDALONE'):
+-        os.environ[b'WRITE_MOZINFO'] = b'1'
++        os.environ['WRITE_MOZINFO'] = '1'
+         from mozbuild.config_status import config_status
+ 
+         # Some values in sanitized_config also have more complex types, such as
+--- thunderbird-52.9.0/mozilla/python/blessings/blessings/__init__.py.old	2017-04-11 02:13:23.000000000 +0000
++++ thunderbird-52.9.0/mozilla/python/blessings/blessings/__init__.py	2018-07-10 10:56:52.820000000 +0000
+@@ -333,7 +333,7 @@
+                       'shadow', 'standout', 'subscript', 'superscript']))
+ 
+ 
+-class ParametrizingString(unicode):
++class ParametrizingString(str):
+     """A Unicode string which can be called to parametrize it as a terminal capability"""
+     def __new__(cls, formatting, normal=None):
+         """Instantiate.
+@@ -343,7 +343,7 @@
+             "normal" capability.
+ 
+         """
+-        new = unicode.__new__(cls, formatting)
++        new = str.__new__(cls, formatting)
+         new._normal = normal
+         return new
+ 
+@@ -375,10 +375,10 @@
+                 raise
+ 
+ 
+-class FormattingString(unicode):
++class FormattingString(str):
+     """A Unicode string which can be called upon a piece of text to wrap it in formatting"""
+     def __new__(cls, formatting, normal):
+-        new = unicode.__new__(cls, formatting)
++        new = str.__new__(cls, formatting)
+         new._normal = normal
+         return new
+ 
+@@ -393,7 +393,7 @@
+         return self + text + self._normal
+ 
+ 
+-class NullCallableString(unicode):
++class NullCallableString(str):
+     """A dummy class to stand in for ``FormattingString`` and ``ParametrizingString``
+ 
+     A callable bytestring that returns an empty Unicode when called with an int
+@@ -402,7 +402,7 @@
+ 
+     """
+     def __new__(cls):
+-        new = unicode.__new__(cls, u'')
++        new = str.__new__(cls, u'')
+         return new
+ 
+     def __call__(self, arg):
+--- thunderbird-52.9.0/mozilla/testing/mozbase/mozfile/mozfile/mozfile.py	(original)
++++ thunderbird-52.9.0/mozilla/testing/mozbase/mozfile/mozfile/mozfile.py	(refactored)
+@@ -6,7 +6,7 @@
+ 
+ # We don't import all modules at the top for performance reasons. See Bug 1008943
+ 
+-from __future__ import absolute_import
++
+ 
+ from contextlib import contextmanager
+ import errno
+@@ -55,7 +55,7 @@
+         try:
+             bundle = zipfile.ZipFile(src)
+         except Exception:
+-            print "src: %s" % src
++            print("src: %s" % src)
+             raise
+ 
+     namelist = bundle.namelist()
+@@ -161,8 +161,8 @@
+ 
+             retry_count += 1
+ 
+-            print '%s() failed for "%s". Reason: %s (%s). Retrying...' % \
+-                (func.__name__, args, e.strerror, e.errno)
++            print('%s() failed for "%s". Reason: %s (%s). Retrying...' % \
++                (func.__name__, args, e.strerror, e.errno))
+             time.sleep(retry_count * retry_delay)
+         else:
+             # If no exception has been thrown it should be done
+@@ -420,9 +420,9 @@
+     Return True if thing looks like a URL.
+     """
+ 
+-    import urlparse
+-
+-    parsed = urlparse.urlparse(thing)
++    import urllib.parse
++
++    parsed = urllib.parse.urlparse(thing)
+     if 'scheme' in parsed:
+         return len(parsed.scheme) >= 2
+     else:
+@@ -436,7 +436,7 @@
+     result of urllib2.urlopen()
+     """
+ 
+-    import urllib2
++    import urllib.request, urllib.error, urllib.parse
+ 
+     # handle file URLs separately due to python stdlib limitations
+     if resource.startswith('file://'):
+@@ -446,4 +446,4 @@
+         # if no scheme is given, it is a file path
+-        return file(resource)
++        return open(resource)
+ 
+-    return urllib2.urlopen(resource)
++    return urllib.request.urlopen(resource)
+--- thunderbird-52.9.0/mozilla/testing/mozbase/mozinfo/mozinfo/mozinfo.py	(original)
++++ thunderbird-52.9.0/mozilla/testing/mozbase/mozinfo/mozinfo/mozinfo.py	(refactored)
+@@ -8,7 +8,7 @@
+ # linux) to the information; I certainly wouldn't want anyone parsing this
+ # information and having behaviour depend on it
+ 
+-from __future__ import absolute_import
++
+ 
+ import os
+ import platform
+@@ -24,7 +24,7 @@
+ class unknown(object):
+     """marker class for unknown information"""
+ 
+-    def __nonzero__(self):
++    def __bool__(self):
+         return False
+ 
+     def __str__(self):
+@@ -184,7 +184,7 @@
+                      to a json file containing the new info.
+     """
+ 
+-    if isinstance(new_info, basestring):
++    if isinstance(new_info, str):
+         # lazy import
+         import mozfile
+         import json
+@@ -246,7 +246,7 @@
+ update({})
+ 
+ # exports
+-__all__ = info.keys()
++__all__ = list(info.keys())
+ __all__ += ['is' + os_name.title() for os_name in choices['os']]
+ __all__ += [
+     'info',
+@@ -284,17 +284,17 @@
+ 
+     # print out choices if requested
+     flag = False
+-    for key, value in options.__dict__.items():
++    for key, value in list(options.__dict__.items()):
+         if value is True:
+-            print '%s choices: %s' % (key, ' '.join([str(choice)
+-                                                     for choice in choices[key]]))
++            print('%s choices: %s' % (key, ' '.join([str(choice)
++                                                     for choice in choices[key]])))
+             flag = True
+     if flag:
+         return
+ 
+     # otherwise, print out all info
+-    for key, value in info.items():
+-        print '%s: %s' % (key, value)
++    for key, value in list(info.items()):
++        print('%s: %s' % (key, value))
+ 
+ if __name__ == '__main__':
+     main()
+--- thunderbird-52.9.0/mozilla/testing/mozbase/mozinfo/mozinfo/string_version.py.old	2017-04-11 02:13:06.000000000 +0000
++++ thunderbird-52.9.0/mozilla/testing/mozbase/mozinfo/mozinfo/string_version.py	2018-07-10 11:10:56.140000000 +0000
+@@ -10,8 +10,10 @@
+     A string version that can be compared with comparison operators.
+     """
+ 
++    def __new__(cls, value):
++        return str.__new__(cls, value)
++
+     def __init__(self, vstring):
+-        str.__init__(self, vstring)
+         self.version = LooseVersion(vstring)
+ 
+     def __repr__(self):
+--- thunderbird-52.9.0/mozilla/testing/mozbase/manifestparser/manifestparser/manifestparser.py	(original)
++++ thunderbird-52.9.0/mozilla/testing/mozbase/manifestparser/manifestparser/manifestparser.py	(refactored)
+@@ -2,7 +2,7 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ # You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-from StringIO import StringIO
++from io import StringIO
+ import json
+ import fnmatch
+ import os
+@@ -21,7 +21,7 @@
+ __all__ = ['ManifestParser', 'TestManifest', 'convert']
+ 
+ relpath = os.path.relpath
+-string = (basestring,)
++string = (str,)
+ 
+ 
+ # path normalization
+@@ -178,7 +178,7 @@
+ 
+             # otherwise an item
+             # apply ancestor defaults, while maintaining current file priority
+-            data = dict(self._ancestor_defaults.items() + data.items())
++            data = dict(list(self._ancestor_defaults.items()) + list(data.items()))
+ 
+             test = data
+             test['name'] = section
+@@ -306,19 +306,19 @@
+         # make some check functions
+         if inverse:
+             def has_tags(test):
+-                return not tags.intersection(test.keys())
++                return not tags.intersection(list(test.keys()))
+ 
+             def dict_query(test):
+-                for key, value in kwargs.items():
++                for key, value in list(kwargs.items()):
+                     if test.get(key) == value:
+                         return False
+                 return True
+         else:
+             def has_tags(test):
+-                return tags.issubset(test.keys())
++                return tags.issubset(list(test.keys()))
+ 
+             def dict_query(test):
+-                for key, value in kwargs.items():
++                for key, value in list(kwargs.items()):
+                     if test.get(key) != value:
+                         return False
+                 return True
+@@ -340,7 +340,7 @@
+         """
+         if tests is None:
+             # Make sure to return all the manifests, even ones without tests.
+-            return self.manifest_defaults.keys()
++            return list(self.manifest_defaults.keys())
+ 
+         manifests = []
+         for test in tests:
+@@ -373,8 +373,8 @@
+                 raise IOError("Strict mode enabled, test paths must exist. "
+                               "The following test(s) are missing: %s" %
+                               json.dumps(missing_paths, indent=2))
+-            print >> sys.stderr, "Warning: The following test(s) are missing: %s" % \
+-                json.dumps(missing_paths, indent=2)
++            print("Warning: The following test(s) are missing: %s" % \
++                json.dumps(missing_paths, indent=2), file=sys.stderr)
+         return missing
+ 
+     def verifyDirectory(self, directories, pattern=None, extensions=None):
+@@ -385,7 +385,7 @@
+         """
+ 
+         files = set([])
+-        if isinstance(directories, basestring):
++        if isinstance(directories, str):
+             directories = [directories]
+ 
+         # get files in directories
+@@ -449,12 +449,12 @@
+ 
+         # print the .ini manifest
+         if global_tags or global_kwargs:
+-            print >> fp, '[DEFAULT]'
++            print('[DEFAULT]', file=fp)
+             for tag in global_tags:
+-                print >> fp, '%s =' % tag
+-            for key, value in global_kwargs.items():
+-                print >> fp, '%s = %s' % (key, value)
+-            print >> fp
++                print('%s =' % tag, file=fp)
++            for key, value in list(global_kwargs.items()):
++                print('%s = %s' % (key, value), file=fp)
++            print(file=fp)
+ 
+         for test in tests:
+             test = test.copy()  # don't overwrite
+@@ -465,7 +465,7 @@
+                 if self.rootdir:
+                     path = relpath(test['path'], self.rootdir)
+                 path = denormalize_path(path)
+-            print >> fp, '[%s]' % path
++            print('[%s]' % path, file=fp)
+ 
+             # reserved keywords:
+             reserved = ['path', 'name', 'here', 'manifest', 'relpath', 'ancestor-manifest']
+@@ -476,8 +476,8 @@
+                     continue
+                 if key in global_tags and not test[key]:
+                     continue
+-                print >> fp, '%s = %s' % (key, test[key])
+-            print >> fp
++                print('%s = %s' % (key, test[key]), file=fp)
++            print(file=fp)
+ 
+         if close:
+             # close the created file
+@@ -565,7 +565,7 @@
+                     message = "Missing test: '%s' does not exist!"
+                     if self.strict:
+                         raise IOError(message)
+-                    print >> sys.stderr, message + " Skipping."
++                    print(message + " Skipping.", file=sys.stderr)
+                     continue
+                 destination = os.path.join(rootdir, _relpath)
+                 shutil.copy(source, destination)
+@@ -578,7 +578,7 @@
+         internal function to import directories
+         """
+ 
+-        if isinstance(pattern, basestring):
++        if isinstance(pattern, str):
+             patterns = [pattern]
+         else:
+             patterns = pattern
+@@ -670,9 +670,9 @@
+             if (dirnames or filenames) and not (os.path.exists(manifest_path) and overwrite):
+                 with file(manifest_path, 'w') as manifest:
+                     for dirname in dirnames:
+-                        print >> manifest, '[include:%s]' % os.path.join(dirname, filename)
++                        print('[include:%s]' % os.path.join(dirname, filename), file=manifest)
+                     for _filename in filenames:
+-                        print >> manifest, '[%s]' % _filename
++                        print('[%s]' % _filename, file=manifest)
+ 
+                 # add to list of manifests
+                 manifest_dict.setdefault(directory, manifest_path)
+@@ -722,8 +722,8 @@
+                              for filename in filenames]
+ 
+             # write to manifest
+-            print >> write, '\n'.join(['[%s]' % denormalize_path(filename)
+-                                       for filename in filenames])
++            print('\n'.join(['[%s]' % denormalize_path(filename)
++                                       for filename in filenames]), file=write)
+ 
+         cls._walk_directories(directories, callback, pattern=pattern, ignore=ignore)
+ 
+--- thunderbird-52.9.0/mozilla/testing/mozbase/manifestparser/manifestparser/cli.py	(original)
++++ thunderbird-52.9.0/mozilla/testing/mozbase/manifestparser/manifestparser/cli.py	(refactored)
+@@ -81,7 +81,7 @@
+         # parse the arguments
+         try:
+             kwargs, tags, args = parse_args(args)
+-        except ParserError, e:
++        except ParserError as e:
+             self._parser.error(e.message)
+ 
+         # make sure we have some manifests, otherwise it will
+@@ -132,7 +132,7 @@
+             manifest = convert(args, pattern=options.pattern, ignore=options.ignore,
+                                write=options.in_place)
+         if manifest:
+-            print manifest
++            print(manifest)
+ 
+ 
+ class WriteCLI(CLICommand):
+@@ -146,7 +146,7 @@
+         # parse the arguments
+         try:
+             kwargs, tags, args = parse_args(args)
+-        except ParserError, e:
++        except ParserError as e:
+             self._parser.error(e.message)
+ 
+         # make sure we have some manifests, otherwise it will
+@@ -175,9 +175,9 @@
+             commands[args[0]](self._parser).parser().print_help()
+         else:
+             self._parser.print_help()
+-            print '\nCommands:'
++            print('\nCommands:')
+             for command in sorted(commands):
+-                print '  %s : %s' % (command, commands[command].__doc__.strip())
++                print('  %s : %s' % (command, commands[command].__doc__.strip()))
+ 
+ 
+ class UpdateCLI(CLICommand):
+@@ -190,7 +190,7 @@
+         # parse the arguments
+         try:
+             kwargs, tags, args = parse_args(args)
+-        except ParserError, e:
++        except ParserError as e:
+             self._parser.error(e.message)
+ 
+         # make sure we have some manifests, otherwise it will
+--- thunderbird-52.9.0/mozilla/testing/mozbase/manifestparser/manifestparser/expression.py	(original)
++++ thunderbird-52.9.0/mozilla/testing/mozbase/manifestparser/manifestparser/expression.py	(refactored)
+@@ -275,7 +275,7 @@
+         """
+         if not isinstance(self.token, expected):
+             raise Exception("Unexpected token!")
+-        self.token = self.iter.next()
++        self.token = next(self.iter)
+ 
+     def expression(self, rbp=0):
+         """
+@@ -283,11 +283,11 @@
+         right binding power greater than rbp is encountered.
+         """
+         t = self.token
+-        self.token = self.iter.next()
++        self.token = next(self.iter)
+         left = t.nud(self)
+         while rbp < self.token.lbp:
+             t = self.token
+-            self.token = self.iter.next()
++            self.token = next(self.iter)
+             left = t.led(self, left)
+         return left
+ 
+@@ -299,7 +299,7 @@
+         """
+         try:
+             self.iter = self._tokenize()
+-            self.token = self.iter.next()
++            self.token = next(self.iter)
+             return self.expression()
+         except:
+             extype, ex, tb = sys.exc_info()
+@@ -307,7 +307,7 @@
+             raise ParseError("could not parse: "
+                              "%s\nexception: %svariables: %s" % (self.text,
+                                                                  formatted,
+-                                                                 self.valuemapping)), None, tb
++                                                                 self.valuemapping)).with_traceback(tb)
+ 
+     __call__ = parse
+ 
+--- thunderbird-52.9.0/mozilla/testing/mozbase/manifestparser/manifestparser/filters.py	(original)
++++ thunderbird-52.9.0/mozilla/testing/mozbase/manifestparser/manifestparser/filters.py	(refactored)
+@@ -92,7 +92,7 @@
+     def __init__(self, *args, **kwargs):
+         self.fmt_args = ', '.join(itertools.chain(
+             [str(a) for a in args],
+-            ['{}={}'.format(k, v) for k, v in kwargs.iteritems()]))
++            ['{}={}'.format(k, v) for k, v in kwargs.items()]))
+ 
+     def __eq__(self, other):
+         if self.unique:
+@@ -249,7 +249,7 @@
+         # be yielded for reporting purposes. Put them all in chunk 1 for
+         # simplicity.
+         if self.this_chunk == 1:
+-            disabled_dirs = [v for k, v in tests_by_dir.iteritems()
++            disabled_dirs = [v for k, v in tests_by_dir.items()
+                              if k not in ordered_dirs]
+             for disabled_test in itertools.chain(*disabled_dirs):
+                 yield disabled_test
+@@ -326,7 +326,7 @@
+ 
+     def __init__(self, tags):
+         InstanceFilter.__init__(self, tags)
+-        if isinstance(tags, basestring):
++        if isinstance(tags, str):
+             tags = [tags]
+         self.tags = tags
+ 
+@@ -349,7 +349,7 @@
+ 
+     def __init__(self, paths):
+         InstanceFilter.__init__(self, paths)
+-        if isinstance(paths, basestring):
++        if isinstance(paths, str):
+             paths = [paths]
+         self.paths = paths
+ 
+--- thunderbird-52.9.0/mozilla/testing/mozbase/manifestparser/manifestparser/ini.py	(original)
++++ thunderbird-52.9.0/mozilla/testing/mozbase/manifestparser/manifestparser/ini.py	(refactored)
+@@ -27,7 +27,7 @@
+     sections = []
+     key = value = None
+     section_names = set()
+-    if isinstance(fp, basestring):
+-        fp = file(fp)
++    if isinstance(fp, str):
++        fp = open(fp)
+ 
+     # read the lines
+@@ -131,7 +131,7 @@
+         'support-files': '%s %s',
+     }
+     final_mapping = global_vars.copy()
+-    for field_name, value in local_vars.items():
++    for field_name, value in list(local_vars.items()):
+         if field_name not in field_patterns or field_name not in global_vars:
+             final_mapping[field_name] = value
+             continue
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/util.py.old	2018-07-10 11:16:10.690000000 +0000
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/util.py	2018-07-10 23:36:02.250000000 +0000
+@@ -1254,10 +1254,10 @@
+             encode(k, encoding): encode(v, encoding)
+             for k, v in obj.items()
+         }
+-    if isinstance(obj, bytes):
+-        return obj
+     if isinstance(obj, str):
+-        return obj.encode(encoding)
++        return obj
++    if isinstance(obj, bytes):
++        return obj.decode(encoding)
+     if isinstance(obj, Iterable):
+         return [encode(i, encoding) for i in obj]
+     return obj
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/util.py.old	2018-07-10 23:36:33.980000000 +0000
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/util.py	2018-07-10 23:41:51.900000000 +0000
+@@ -1179,6 +1179,9 @@
+             POSSIBLE_VALUES = possible_values
+         return EnumStringSubclass
+ 
++    def __hash__(self):
++        return hash(str(self))
++
+ 
+ def _escape_char(c):
+     # str.encode('unicode_espace') doesn't escape quotes, presumably because
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/mozinfo.py.old	2018-07-10 23:42:19.390000000 +0000
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/mozinfo.py	2018-07-11 01:45:57.930000000 +0000
+@@ -155,6 +155,6 @@
+     """
+     build_conf = build_dict(config, env)
+     if isinstance(file, str):
+-        file = open(file, 'wb')
++        file = open(file, 'w')
+ 
+     json.dump(build_conf, file, sort_keys=True, indent=4)
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/jar.py.old	2018-07-11 01:46:26.540000000 +0000
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/jar.py	2018-07-11 02:02:36.010000000 +0000
+@@ -16,7 +16,7 @@
+ import re
+ import logging
+ from time import localtime
+-from MozZipFile import ZipFile
++from zipfile import ZipFile
+ from io import StringIO
+ from collections import defaultdict
+ 
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/util.py.old	2018-07-11 02:05:38.530000000 +0000
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/util.py	2018-07-11 02:08:21.020000000 +0000
+@@ -987,8 +987,6 @@
+                                     'got %s, expected %s' % (fname,
+                                     type(value), ftype))
+ 
+-            super(TypedTuple, self).__init__(*args, **kwargs)
+-
+     TypedTuple._fields = fields
+ 
+     return TypedTuple
+--- thunderbird-52.9.0/mozilla/build/moz.configure/init.configure.old	2018-07-11 02:08:50.000000000 +0000
++++ thunderbird-52.9.0/mozilla/build/moz.configure/init.configure	2018-07-11 04:04:26.600000000 +0000
+@@ -297,7 +297,9 @@
+     # There is also a quartet form:
+     #   CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
+     # But we can consider the "KERNEL-OPERATING_SYSTEM" as one.
+-    cpu, manufacturer, os = triplet.decode('utf-8').split('-', 2)
++    if isinstance(triplet, bytes):
++        triplet = triplet.decode('utf-8')
++    cpu, manufacturer, os = triplet.split('-', 2)
+ 
+     # Autoconf uses config.sub to validate and canonicalize those triplets,
+     # but the granularity of its results has never been satisfying to our
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/frontend/reader.py.old	2018-07-11 04:25:33.980000000 +0000
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/frontend/reader.py	2018-07-11 04:24:50.630000000 +0000
+@@ -441,6 +441,8 @@
+         code = func.__code__
+         firstlineno = code.co_firstlineno
+         lines = sandbox._current_source.splitlines(True)
++        if len(lines) and isinstance(lines[0], bytes):
++            lines = [l.decode('utf-8') for l in lines]
+         lines = inspect.getblock(lines[firstlineno - 1:])
+ 
+         # The code lines we get out of inspect.getsourcelines look like
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/frontend/reader.py.old	2018-07-11 04:25:57.090000000 +0000
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/frontend/reader.py	2018-07-11 04:29:46.140000000 +0000
+@@ -460,7 +460,7 @@
+         # actually never calls __getitem__ and __setitem__, so we need to
+         # modify the AST so that accesses to globals are properly directed
+         # to a dict.
+-        self._global_name = b'_data' # AST wants str for this, not unicode
++        self._global_name = '_data' # AST wants str for this, not unicode
+         # In case '_data' is a name used for a variable in the function code,
+         # prepend more underscores until we find an unused name.
+         while (self._global_name in code.co_names or
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/frontend/context.py.old	2018-07-11 04:30:06.590000000 +0000
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/frontend/context.py	2018-07-11 04:35:38.670000000 +0000
+@@ -384,8 +384,8 @@
+ 
+     def __cmp__(self, other):
+         if isinstance(other, Path) and self.srcdir != other.srcdir:
+-            return cmp(self.full_path, other.full_path)
+-        return cmp(str(self), other)
++            return self.full_path == other.full_path
++        return str(self) == other
+ 
+     # __cmp__ is not enough because unicode has __eq__, __ne__, etc. defined
+     # and __cmp__ is only used for those when they don't exist.
+--- thunderbird-52.9.0/mozilla/mozglue/build/moz.build.old	2018-07-11 04:40:17.930000000 +0000
++++ thunderbird-52.9.0/mozilla/mozglue/build/moz.build	2018-07-11 04:42:00.830000000 +0000
+@@ -4,20 +4,15 @@
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+-# Build mozglue as a shared lib on Windows, OSX and Android.
++# Build mozglue as a shared lib on Windows and OSX.
+ # If this is ever changed, update MOZ_SHARED_MOZGLUE in browser/installer/Makefile.in
+-if CONFIG['OS_TARGET'] in ('WINNT', 'Darwin', 'Android'):
++if CONFIG['OS_TARGET'] in ('WINNT', 'Darwin'):
+     SharedLibrary('mozglue')
+ else:
+     Library('mozglue')
+ 
+ SDK_LIBRARY = True
+ 
+-if CONFIG['OS_TARGET'] == 'Android':
+-    SOURCES += [
+-        'BionicGlue.cpp',
+-    ]
+-
+ if CONFIG['MOZ_ASAN']:
+     SOURCES += [
+         'AsanOptions.cpp',
+--- thunderbird-52.9.0/mozilla/js/src/moz.build.old	2017-04-11 02:13:16.000000000 +0000
++++ thunderbird-52.9.0/mozilla/js/src/moz.build	2018-07-11 04:43:59.920000000 +0000
+@@ -707,7 +707,7 @@
+     CXXFLAGS += ['-wd4577']
+     CXXFLAGS += ['-wd4312']
+ 
+-if CONFIG['OS_ARCH'] not in ('WINNT', 'HP-UX'):
++if CONFIG['OS_ARCH'] != 'WINNT':
+     OS_LIBS += [
+         'm',
+     ]
+@@ -722,13 +722,13 @@
+         'dl',
+     ]
+ 
+-if CONFIG['OS_ARCH'] == 'SunOS':
+-    OS_LIBS += [
+-        'posix4',
+-        'dl',
+-        'nsl',
+-        'socket',
+-    ]
++#if CONFIG['OS_ARCH'] == 'SunOS':
++#    OS_LIBS += [
++#        'posix4',
++#        'dl',
++#        'nsl',
++#        'socket',
++#    ]
+ 
+ OS_LIBS += CONFIG['REALTIME_LIBS']
+ 
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/util.py.old	2018-07-11 04:44:24.900000000 +0000
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/util.py	2018-07-11 04:46:47.310000000 +0000
+@@ -267,6 +267,10 @@
+             if 'b' in self.mode:
+                 writemode += 'b'
+             with open(self.name, writemode) as file:
++                if 'b' in self.mode and isinstance(buf, str):
++                    buf = buf.encode('utf-8')
++                elif 'b' not in self.mode and isinstance(buf, bytes):
++                    buf = buf.decode('utf-8')
+                 file.write(buf)
+ 
+         if self._capture_diff:
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/backend/recursivemake.py.old	2018-07-11 04:47:15.020000000 +0000
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/backend/recursivemake.py	2018-07-11 04:53:51.750000000 +0000
+@@ -77,86 +77,86 @@
+ from functools import reduce
+ 
+ MOZBUILD_VARIABLES = [
+-    b'ANDROID_APK_NAME',
+-    b'ANDROID_APK_PACKAGE',
+-    b'ANDROID_ASSETS_DIRS',
+-    b'ANDROID_EXTRA_PACKAGES',
+-    b'ANDROID_EXTRA_RES_DIRS',
+-    b'ANDROID_GENERATED_RESFILES',
+-    b'ANDROID_RES_DIRS',
+-    b'ASFLAGS',
+-    b'CMSRCS',
+-    b'CMMSRCS',
+-    b'CPP_UNIT_TESTS',
+-    b'DIRS',
+-    b'DIST_INSTALL',
+-    b'EXTRA_DSO_LDOPTS',
+-    b'EXTRA_JS_MODULES',
+-    b'EXTRA_PP_COMPONENTS',
+-    b'EXTRA_PP_JS_MODULES',
+-    b'FORCE_SHARED_LIB',
+-    b'FORCE_STATIC_LIB',
+-    b'FINAL_LIBRARY',
+-    b'HOST_CFLAGS',
+-    b'HOST_CSRCS',
+-    b'HOST_CMMSRCS',
+-    b'HOST_CXXFLAGS',
+-    b'HOST_EXTRA_LIBS',
+-    b'HOST_LIBRARY_NAME',
+-    b'HOST_PROGRAM',
+-    b'HOST_SIMPLE_PROGRAMS',
+-    b'IS_COMPONENT',
+-    b'JAR_MANIFEST',
+-    b'JAVA_JAR_TARGETS',
+-    b'LD_VERSION_SCRIPT',
+-    b'LIBRARY_NAME',
+-    b'LIBS',
+-    b'MAKE_FRAMEWORK',
+-    b'MODULE',
+-    b'NO_DIST_INSTALL',
+-    b'NO_EXPAND_LIBS',
+-    b'NO_INTERFACES_MANIFEST',
+-    b'NO_JS_MANIFEST',
+-    b'OS_LIBS',
+-    b'PARALLEL_DIRS',
+-    b'PREF_JS_EXPORTS',
+-    b'PROGRAM',
+-    b'PYTHON_UNIT_TESTS',
+-    b'RESOURCE_FILES',
+-    b'SDK_HEADERS',
+-    b'SDK_LIBRARY',
+-    b'SHARED_LIBRARY_LIBS',
+-    b'SHARED_LIBRARY_NAME',
+-    b'SIMPLE_PROGRAMS',
+-    b'SONAME',
+-    b'STATIC_LIBRARY_NAME',
+-    b'TEST_DIRS',
+-    b'TOOL_DIRS',
++    'ANDROID_APK_NAME',
++    'ANDROID_APK_PACKAGE',
++    'ANDROID_ASSETS_DIRS',
++    'ANDROID_EXTRA_PACKAGES',
++    'ANDROID_EXTRA_RES_DIRS',
++    'ANDROID_GENERATED_RESFILES',
++    'ANDROID_RES_DIRS',
++    'ASFLAGS',
++    'CMSRCS',
++    'CMMSRCS',
++    'CPP_UNIT_TESTS',
++    'DIRS',
++    'DIST_INSTALL',
++    'EXTRA_DSO_LDOPTS',
++    'EXTRA_JS_MODULES',
++    'EXTRA_PP_COMPONENTS',
++    'EXTRA_PP_JS_MODULES',
++    'FORCE_SHARED_LIB',
++    'FORCE_STATIC_LIB',
++    'FINAL_LIBRARY',
++    'HOST_CFLAGS',
++    'HOST_CSRCS',
++    'HOST_CMMSRCS',
++    'HOST_CXXFLAGS',
++    'HOST_EXTRA_LIBS',
++    'HOST_LIBRARY_NAME',
++    'HOST_PROGRAM',
++    'HOST_SIMPLE_PROGRAMS',
++    'IS_COMPONENT',
++    'JAR_MANIFEST',
++    'JAVA_JAR_TARGETS',
++    'LD_VERSION_SCRIPT',
++    'LIBRARY_NAME',
++    'LIBS',
++    'MAKE_FRAMEWORK',
++    'MODULE',
++    'NO_DIST_INSTALL',
++    'NO_EXPAND_LIBS',
++    'NO_INTERFACES_MANIFEST',
++    'NO_JS_MANIFEST',
++    'OS_LIBS',
++    'PARALLEL_DIRS',
++    'PREF_JS_EXPORTS',
++    'PROGRAM',
++    'PYTHON_UNIT_TESTS',
++    'RESOURCE_FILES',
++    'SDK_HEADERS',
++    'SDK_LIBRARY',
++    'SHARED_LIBRARY_LIBS',
++    'SHARED_LIBRARY_NAME',
++    'SIMPLE_PROGRAMS',
++    'SONAME',
++    'STATIC_LIBRARY_NAME',
++    'TEST_DIRS',
++    'TOOL_DIRS',
+     # XXX config/Makefile.in specifies this in a make invocation
+     #'USE_EXTENSION_MANIFEST',
+-    b'XPCSHELL_TESTS',
+-    b'XPIDL_MODULE',
++    'XPCSHELL_TESTS',
++    'XPIDL_MODULE',
+ ]
+ 
+ DEPRECATED_VARIABLES = [
+-    b'ANDROID_RESFILES',
+-    b'EXPORT_LIBRARY',
+-    b'EXTRA_LIBS',
+-    b'HOST_LIBS',
+-    b'LIBXUL_LIBRARY',
+-    b'MOCHITEST_A11Y_FILES',
+-    b'MOCHITEST_BROWSER_FILES',
+-    b'MOCHITEST_BROWSER_FILES_PARTS',
+-    b'MOCHITEST_CHROME_FILES',
+-    b'MOCHITEST_FILES',
+-    b'MOCHITEST_FILES_PARTS',
+-    b'MOCHITEST_METRO_FILES',
+-    b'MOCHITEST_ROBOCOP_FILES',
+-    b'MODULE_OPTIMIZE_FLAGS',
+-    b'MOZ_CHROME_FILE_FORMAT',
+-    b'SHORT_LIBNAME',
+-    b'TESTING_JS_MODULES',
+-    b'TESTING_JS_MODULE_DIR',
++    'ANDROID_RESFILES',
++    'EXPORT_LIBRARY',
++    'EXTRA_LIBS',
++    'HOST_LIBS',
++    'LIBXUL_LIBRARY',
++    'MOCHITEST_A11Y_FILES',
++    'MOCHITEST_BROWSER_FILES',
++    'MOCHITEST_BROWSER_FILES_PARTS',
++    'MOCHITEST_CHROME_FILES',
++    'MOCHITEST_FILES',
++    'MOCHITEST_FILES_PARTS',
++    'MOCHITEST_METRO_FILES',
++    'MOCHITEST_ROBOCOP_FILES',
++    'MODULE_OPTIMIZE_FLAGS',
++    'MOZ_CHROME_FILE_FORMAT',
++    'SHORT_LIBNAME',
++    'TESTING_JS_MODULES',
++    'TESTING_JS_MODULE_DIR',
+ ]
+ 
+ MOZBUILD_VARIABLES_MESSAGE = 'It should only be defined in moz.build files.'
+@@ -758,7 +758,7 @@
+             rule.add_dependencies(['$(CURDIR)/%: %'])
+ 
+     def _check_blacklisted_variables(self, makefile_in, makefile_content):
+-        if b'EXTERNALLY_MANAGED_MAKE_FILE' in makefile_content:
++        if 'EXTERNALLY_MANAGED_MAKE_FILE' in makefile_content:
+             # Bypass the variable restrictions for externally managed makefiles.
+             return
+ 
+@@ -765,7 +765,7 @@
+         for l in makefile_content.splitlines():
+             l = l.strip()
+             # Don't check comments
+-            if l.startswith(b'#'):
++            if l.startswith('#'):
+                 continue
+             for x in chain(MOZBUILD_VARIABLES, DEPRECATED_VARIABLES):
+                 if x not in l:
+@@ -822,11 +822,11 @@
+                     # Skip every directory but those with a Makefile
+                     # containing a tools target, or XPI_PKGNAME or
+                     # INSTALL_EXTENSION_ID.
+-                    for t in (b'XPI_PKGNAME', b'INSTALL_EXTENSION_ID',
+-                            b'tools'):
++                    for t in ('XPI_PKGNAME', 'INSTALL_EXTENSION_ID',
++                            'tools'):
+                         if t not in content:
+                             continue
+-                        if t == b'tools' and not re.search('(?:^|\s)tools.*::', content, re.M):
++                        if t == 'tools' and not re.search('(?:^|\s)tools.*::', content, re.M):
+                             continue
+                         if objdir == self.environment.topobjdir:
+                             continue
+@@ -1422,20 +1422,20 @@
+                 pp.context.update(extra)
+             if not pp.context.get('autoconfmk', ''):
+                 pp.context['autoconfmk'] = 'autoconf.mk'
+-            pp.handleLine(b'# THIS FILE WAS AUTOMATICALLY GENERATED. DO NOT MODIFY BY HAND.\n');
+-            pp.handleLine(b'DEPTH := @DEPTH@\n')
+-            pp.handleLine(b'topobjdir := @topobjdir@\n')
+-            pp.handleLine(b'topsrcdir := @top_srcdir@\n')
+-            pp.handleLine(b'srcdir := @srcdir@\n')
+-            pp.handleLine(b'VPATH := @srcdir@\n')
+-            pp.handleLine(b'relativesrcdir := @relativesrcdir@\n')
+-            pp.handleLine(b'include $(DEPTH)/config/@autoconfmk@\n')
++            pp.handleLine('# THIS FILE WAS AUTOMATICALLY GENERATED. DO NOT MODIFY BY HAND.\n');
++            pp.handleLine('DEPTH := @DEPTH@\n')
++            pp.handleLine('topobjdir := @topobjdir@\n')
++            pp.handleLine('topsrcdir := @top_srcdir@\n')
++            pp.handleLine('srcdir := @srcdir@\n')
++            pp.handleLine('VPATH := @srcdir@\n')
++            pp.handleLine('relativesrcdir := @relativesrcdir@\n')
++            pp.handleLine('include $(DEPTH)/config/@autoconfmk@\n')
+             if not stub:
+                 pp.do_include(obj.input_path)
+             # Empty line to avoid failures when last line in Makefile.in ends
+             # with a backslash.
+-            pp.handleLine(b'\n')
+-            pp.handleLine(b'include $(topsrcdir)/config/recurse.mk\n')
++            pp.handleLine('\n')
++            pp.handleLine('include $(topsrcdir)/config/recurse.mk\n')
+         if not stub:
+             # Adding the Makefile.in here has the desired side-effect
+             # that if the Makefile.in disappears, this will force
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozpack/manifests.py.old	2018-07-11 04:54:39.260000000 +0000
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozpack/manifests.py	2018-07-11 04:57:31.710000000 +0000
+@@ -115,7 +115,7 @@
+         self._source_files = set()
+ 
+         if path or fileobj:
+-            with _auto_fileobj(path, fileobj, 'rb') as fh:
++            with _auto_fileobj(path, fileobj, 'r') as fh:
+                 self._source_files.add(fh.name)
+                 self._load_from_fileobj(fh)
+ 
+@@ -174,7 +174,7 @@
+                 dest, content = fields[1:]
+ 
+                 self.add_content(
+-                    self._decode_field_entry(content).encode('utf-8'), dest)
++                    self._decode_field_entry(content), dest)
+                 continue
+ 
+             # Don't fail for non-actionable items, allowing
+@@ -236,7 +236,7 @@
+ 
+         It is an error if both are specified.
+         """
+-        with _auto_fileobj(path, fileobj, 'wb') as fh:
++        with _auto_fileobj(path, fileobj, 'w') as fh:
+             fh.write('%d\n' % self.CURRENT_VERSION)
+ 
+             for dest in sorted(self._dests):
+@@ -244,8 +244,7 @@
+ 
+                 parts = ['%d' % entry[0], dest]
+                 parts.extend(entry[1:])
+-                fh.write('%s\n' % self.FIELD_SEPARATOR.join(
+-                    p.encode('utf-8') for p in parts))
++                fh.write('%s\n' % self.FIELD_SEPARATOR.join(parts))
+ 
+     def add_symlink(self, source, dest):
+         """Add a symlink to this manifest.
+@@ -391,7 +390,7 @@
+             if install_type == self.CONTENT:
+                 # GeneratedFile expect the buffer interface, which the unicode
+                 # type doesn't have, so encode to a str.
+-                content = self._decode_field_entry(entry[1]).encode('utf-8')
++                content = self._decode_field_entry(entry[1])
+                 registry.add(dest, GeneratedFile(content))
+                 continue
+ 
+--- thunderbird-52.9.0/mozilla/config/MozZipFile.py	(original)
++++ thunderbird-52.9.0/mozilla/config/MozZipFile.py	(refactored)
+@@ -18,7 +18,7 @@
+   def __init__(self, file, mode="r", compression=zipfile.ZIP_STORED,
+                lock = False):
+     if lock:
+-      assert isinstance(file, basestring)
++      assert isinstance(file, str)
+       self.lockfile = lock_file(file + '.lck')
+     else:
+       self.lockfile = None
+@@ -46,7 +46,7 @@
+                               date_time=time.localtime(time.time()))
+       zinfo.compress_type = self.compression
+       # Add some standard UNIX file access permissions (-rw-r--r--).
+-      zinfo.external_attr = (0x81a4 & 0xFFFF) << 16L
++      zinfo.external_attr = (0x81a4 & 0xFFFF) << 16
+     else:
+       zinfo = zinfo_or_arcname
+ 
+@@ -58,7 +58,7 @@
+     # as the old, reuse the existing entry.
+ 
+     doSeek = False # store if we need to seek to the eof after overwriting
+-    if self.NameToInfo.has_key(zinfo.filename):
++    if zinfo.filename in self.NameToInfo:
+       # Find the last ZipInfo with our name.
+       # Last, because that's catching multiple overwrites
+       i = len(self.filelist)
+@@ -109,14 +109,14 @@
+       # adjust file mode if we originally just wrote, now we rewrite
+       self.fp.close()
+       self.fp = open(self.filename, 'r+b')
+-    all = map(lambda zi: (zi, True), self.filelist) + \
+-        map(lambda zi: (zi, False), self._remove)
++    all = [(zi, True) for zi in self.filelist] + \
++        [(zi, False) for zi in self._remove]
+     all.sort(lambda l, r: cmp(l[0].header_offset, r[0].header_offset))
+     # empty _remove for multiple closes
+     self._remove = []
+ 
+     lengths = [all[i+1][0].header_offset - all[i][0].header_offset
+-               for i in xrange(len(all)-1)]
++               for i in range(len(all)-1)]
+     lengths.append(self.end - all[-1][0].header_offset)
+     to_pos = 0
+     for (zi, keep), length in zip(all, lengths):
+--- thunderbird-52.9.0/mozilla/config/check_spidermonkey_style.py	(original)
++++ thunderbird-52.9.0/mozilla/config/check_spidermonkey_style.py	(refactored)
+@@ -35,7 +35,7 @@
+ #   isolation, but don't try to do any order checking between such blocks.
+ #----------------------------------------------------------------------------
+ 
+-from __future__ import print_function
++
+ 
+ import difflib
+ import os
+@@ -270,7 +270,7 @@
+         edges[inclname] = set()
+ 
+     # Process all the JS files.
+-    for filename in js_names.keys():
++    for filename in list(js_names.keys()):
+         inclname = js_names[filename]
+         file_kind = FileKind.get(filename)
+         if file_kind == FileKind.C or file_kind == FileKind.CPP or \
+--- thunderbird-52.9.0/mozilla/config/expandlibs.py	(original)
++++ thunderbird-52.9.0/mozilla/config/expandlibs.py	(refactored)
+@@ -26,7 +26,7 @@
+   descriptor contains. And for each of these LIBS, also apply the same
+   rules.
+ '''
+-from __future__ import with_statement
++
+ import sys, os, errno
+ import expandlibs_config as conf
+ 
+@@ -36,7 +36,7 @@
+     if dir and not os.path.exists(dir):
+         try:
+             os.makedirs(dir)
+-        except OSError, error:
++        except OSError as error:
+             if error.errno != errno.EEXIST:
+                 raise
+ 
+@@ -140,4 +140,4 @@
+         return [relativize(arg)]
+ 
+ if __name__ == '__main__':
+-    print " ".join(ExpandArgs(sys.argv[1:]))
++    print(" ".join(ExpandArgs(sys.argv[1:])))
+--- thunderbird-52.9.0/mozilla/config/expandlibs_exec.py	(original)
++++ thunderbird-52.9.0/mozilla/config/expandlibs_exec.py	(refactored)
+@@ -20,7 +20,7 @@
+ relevant linker options to change the order in which the linker puts the
+ symbols appear in the resulting binary. Only works for ELF targets.
+ '''
+-from __future__ import with_statement
++
+ import sys
+ import os
+ from expandlibs import (
+@@ -304,11 +304,11 @@
+         return syms
+ 
+ def print_command(out, args):
+-    print >>out, "Executing: " + " ".join(args)
++    print("Executing: " + " ".join(args), file=out)
+     for tmp in [f for f in args.tmp if os.path.isfile(f)]:
+-        print >>out, tmp + ":"
++        print(tmp + ":", file=out)
+         with open(tmp) as file:
+-            print >>out, "".join(["    " + l for l in file.readlines()])
++            print("".join(["    " + l for l in file.readlines()]), file=out)
+     out.flush()
+ 
+ def main(args, proc_callback=None):
+@@ -338,8 +338,8 @@
+             proc = subprocess.Popen(args, stdout = subprocess.PIPE, stderr = subprocess.STDOUT)
+             if proc_callback:
+                 proc_callback(proc)
+-        except Exception, e:
+-            print >>sys.stderr, 'error: Launching', args, ':', e
++        except Exception as e:
++            print('error: Launching', args, ':', e, file=sys.stderr)
+             raise e
+         (stdout, stderr) = proc.communicate()
+         if proc.returncode and not options.verbose:
+--- thunderbird-52.9.0/mozilla/config/expandlibs_gen.py	(original)
++++ thunderbird-52.9.0/mozilla/config/expandlibs_gen.py	(refactored)
+@@ -5,7 +5,7 @@
+ '''Given a list of object files and library names, prints a library
+ descriptor to standard output'''
+ 
+-from __future__ import with_statement
++
+ import sys
+ import os
+ import expandlibs_config as conf
+@@ -38,4 +38,4 @@
+ 
+     ensureParentDir(options.output)
+     with open(options.output, 'w') as outfile:
+-        print >>outfile, generate(args)
++        print(generate(args), file=outfile)
+--- thunderbird-52.9.0/mozilla/config/find_OOM_errors.py	(original)
++++ thunderbird-52.9.0/mozilla/config/find_OOM_errors.py	(refactored)
+@@ -2,7 +2,7 @@
+ # This Source Code Form is subject to the terms of the Mozilla Public
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+-from __future__ import print_function
++
+ 
+ usage = """%prog: A test for OOM conditions in the shell.
+ 
+@@ -95,12 +95,12 @@
+   """Keep track of the amount of times individual lines occur, in order to
+      prioritize the errors which occur most frequently."""
+   counts = {}
+-  for string,count in blacklist.items():
++  for string,count in list(blacklist.items()):
+     for line in string.split("\n"):
+       counts[line] = counts.get(line, 0) + count
+ 
+   lines = []
+-  for k,v in counts.items():
++  for k,v in list(counts.items()):
+     lines.append("{0:6}: {1}".format(v, k))
+ 
+   lines.sort()
+--- thunderbird-52.9.0/mozilla/config/link.py	(original)
++++ thunderbird-52.9.0/mozilla/config/link.py	(refactored)
+@@ -18,7 +18,7 @@
+         time.sleep(0.5)
+         idleTime += 0.5
+         if idleTime > 20 * 60:
+-          print "Still linking, 20 minutes passed..."
++          print("Still linking, 20 minutes passed...")
+           sys.stdout.flush()
+           idleTime = 0
+ 
+@@ -42,6 +42,6 @@
+ 
+ if __name__ == "__main__":
+     if len(sys.argv) < 2:
+-        print >>sys.stderr, "Usage: link.py <commandline>"
++        print("Usage: link.py <commandline>", file=sys.stderr)
+         sys.exit(1)
+     sys.exit(wrap_linker(sys.argv[1:]))
+--- thunderbird-52.9.0/mozilla/config/mozunit.py	(original)
++++ thunderbird-52.9.0/mozilla/config/mozunit.py	(refactored)
+@@ -140,7 +140,7 @@
+     '''
+     def __init__(self, files = {}):
+         self.files = {}
+-        for name, content in files.iteritems():
++        for name, content in files.items():
+             self.files[normcase(os.path.abspath(name))] = content
+ 
+     def __call__(self, name, mode = 'r'):
+@@ -158,19 +158,19 @@
+         return file
+ 
+     def __enter__(self):
+-        import __builtin__
+-        self.open = __builtin__.open
++        import builtins
++        self.open = builtins.open
+         self._orig_path_exists = os.path.exists
+         self._orig_path_isdir = os.path.isdir
+         self._orig_path_isfile = os.path.isfile
+-        __builtin__.open = self
++        builtins.open = self
+         os.path.exists = self._wrapped_exists
+         os.path.isdir = self._wrapped_isdir
+         os.path.isfile = self._wrapped_isfile
+ 
+     def __exit__(self, type, value, traceback):
+-        import __builtin__
+-        __builtin__.open = self.open
++        import builtins
++        builtins.open = self.open
+         os.path.exists = self._orig_path_exists
+         os.path.isdir = self._orig_path_isdir
+         os.path.isfile = self._orig_path_isfile
+--- thunderbird-52.9.0/mozilla/config/nsinstall.py	(original)
++++ thunderbird-52.9.0/mozilla/config/nsinstall.py	(refactored)
+@@ -9,7 +9,7 @@
+ # a full build environment set up.
+ # The basic limitation is, it doesn't even try to link and ignores
+ # all related options.
+-from __future__ import print_function
++
+ from optparse import OptionParser
+ import mozfile
+ import os
+@@ -149,7 +149,7 @@
+ 
+ # nsinstall as a native command is always UTF-8
+ def nsinstall(argv):
+-  return _nsinstall_internal([unicode(arg, "utf-8") for arg in argv])
++  return _nsinstall_internal([str(arg, "utf-8") for arg in argv])
+ 
+ if __name__ == '__main__':
+   # sys.argv corrupts characters outside the system code page on Windows
+@@ -175,8 +175,8 @@
+   else:
+     # For consistency, do it on Unix as well
+     if sys.stdin.encoding is not None:
+-      argv = [unicode(arg, sys.stdin.encoding) for arg in sys.argv]
++      argv = [str(arg, sys.stdin.encoding) for arg in sys.argv]
+     else:
+-      argv = [unicode(arg) for arg in sys.argv]
++      argv = [str(arg) for arg in sys.argv]
+ 
+   sys.exit(_nsinstall_internal(argv[1:]))
+--- thunderbird-52.9.0/mozilla/config/printconfigsetting.py	(original)
++++ thunderbird-52.9.0/mozilla/config/printconfigsetting.py	(refactored)
+@@ -5,12 +5,12 @@
+ import configobj
+ import sys
+ import re
+-from StringIO import StringIO
++from io import StringIO
+ 
+ try:
+     (file, section, key) = sys.argv[1:]
+ except ValueError:
+-    print "Usage: printconfigsetting.py <file> <section> <setting>"
++    print("Usage: printconfigsetting.py <file> <section> <setting>")
+     sys.exit(1)
+ 
+ with open(file) as fh:
+@@ -21,11 +21,11 @@
+ try:
+     s = c[section]
+ except KeyError:
+-    print >>sys.stderr, "Section [%s] not found." % section
++    print("Section [%s] not found." % section, file=sys.stderr)
+     sys.exit(1)
+ 
+ try:
+-    print s[key]
++    print(s[key])
+ except KeyError:
+-    print >>sys.stderr, "Key %s not found." % key
++    print("Key %s not found." % key, file=sys.stderr)
+     sys.exit(1)
+--- thunderbird-52.9.0/mozilla/config/pythonpath.py	(original)
++++ thunderbird-52.9.0/mozilla/config/pythonpath.py	(refactored)
+@@ -9,7 +9,7 @@
+ 
+ def main(args):
+     def usage():
+-        print >>sys.stderr, "pythonpath.py -I directory script.py [args...]"
++        print("pythonpath.py -I directory script.py [args...]", file=sys.stderr)
+         sys.exit(150)
+ 
+     paths = []
+@@ -45,7 +45,7 @@
+     frozenglobals['__name__'] = '__main__'
+     frozenglobals['__file__'] = script
+ 
+-    execfile(script, frozenglobals)
++    exec(compile(open(script).read(), script, 'exec'), frozenglobals)
+ 
+ # Freeze scope here ... why this makes things work I have no idea ...
+ frozenglobals = globals()
+--- thunderbird-52.9.0/mozilla/config/rebuild_check.py	(original)
++++ thunderbird-52.9.0/mozilla/config/rebuild_check.py	(refactored)
+@@ -18,7 +18,7 @@
+     deps = args[1:]
+     t = mtime(target)
+     if t < 0:
+-        print target
++        print(target)
+         return
+ 
+     newer = []
+@@ -31,13 +31,13 @@
+             newer.append(dep)
+ 
+     if newer and removed:
+-        print 'Rebuilding %s because %s changed and %s was removed' % (target, ', '.join(newer), ', '.join(removed))
++        print('Rebuilding %s because %s changed and %s was removed' % (target, ', '.join(newer), ', '.join(removed)))
+     elif newer:
+-        print 'Rebuilding %s because %s changed' % (target, ', '.join(newer))
++        print('Rebuilding %s because %s changed' % (target, ', '.join(newer)))
+     elif removed:
+-        print 'Rebuilding %s because %s was removed' % (target, ', '.join(removed))
++        print('Rebuilding %s because %s was removed' % (target, ', '.join(removed)))
+     else:
+-        print 'Rebuilding %s for an unknown reason' % target
++        print('Rebuilding %s for an unknown reason' % target)
+ 
+ if __name__ == '__main__':
+     import sys
+--- thunderbird-52.9.0/mozilla/config/tests/unit-expandlibs.py	(original)
++++ thunderbird-52.9.0/mozilla/config/tests/unit-expandlibs.py	(refactored)
+@@ -7,7 +7,7 @@
+ from shutil import rmtree
+ import mozunit
+ 
+-from UserString import UserString
++from collections import UserString
+ # Create a controlled configuration for use by expandlibs
+ config_win = {
+     'AR': 'lib',
+@@ -120,8 +120,7 @@
+             del dict[name]
+         return type.__new__(cls, clsName, bases, dict)
+ 
+-class TestCaseWithTmpDir(unittest.TestCase):
+-    __metaclass__ = ReplicateTests
++class TestCaseWithTmpDir(unittest.TestCase, metaclass=ReplicateTests):
+     def init(self):
+         self.tmpdir = os.path.abspath(mkdtemp(dir=os.curdir))
+ 
+--- thunderbird-52.9.0/mozilla/config/tests/unit-nsinstall.py	(original)
++++ thunderbird-52.9.0/mozilla/config/tests/unit-nsinstall.py	(refactored)
+@@ -28,9 +28,9 @@
+         # Unicode strings means non-ASCII children can be deleted properly on
+         # Windows
+         if sys.stdin.encoding is None:
+-            tmpdir = unicode(self.tmpdir)
++            tmpdir = str(self.tmpdir)
+         else:
+-            tmpdir = unicode(self.tmpdir, sys.stdin.encoding)
++            tmpdir = str(self.tmpdir, sys.stdin.encoding)
+         rmtree(tmpdir)
+ 
+     # utility methods for tests
+@@ -50,14 +50,14 @@
+         "Test nsinstall -D <dir>"
+         testdir = os.path.join(self.tmpdir, "test")
+         self.assertEqual(nsinstall(["-D", testdir]), 0)
+-        self.assert_(os.path.isdir(testdir))
++        self.assertTrue(os.path.isdir(testdir))
+ 
+     def test_nsinstall_basic(self):
+         "Test nsinstall <file> <dir>"
+         testfile = self.touch("testfile")
+         testdir = self.mkdirs("testdir")
+         self.assertEqual(nsinstall([testfile, testdir]), 0)
+-        self.assert_(os.path.isfile(os.path.join(testdir, "testfile")))
++        self.assertTrue(os.path.isfile(os.path.join(testdir, "testfile")))
+ 
+     def test_nsinstall_basic_recursive(self):
+         "Test nsinstall <dir> <dest dir>"
+@@ -76,12 +76,12 @@
+                                     '-X', Xdir]), 0)
+ 
+         testdir = os.path.join(destdir, "sourcedir")
+-        self.assert_(os.path.isdir(testdir))
+-        self.assert_(os.path.isfile(os.path.join(testdir, "testfile")))
+-        self.assert_(not os.path.exists(os.path.join(testdir, "Xfile")))
+-        self.assert_(os.path.isdir(os.path.join(testdir, "copieddir")))
+-        self.assert_(os.path.isfile(os.path.join(testdir, "copieddir", "testfile2")))
+-        self.assert_(not os.path.exists(os.path.join(testdir, "Xdir")))
++        self.assertTrue(os.path.isdir(testdir))
++        self.assertTrue(os.path.isfile(os.path.join(testdir, "testfile")))
++        self.assertTrue(not os.path.exists(os.path.join(testdir, "Xfile")))
++        self.assertTrue(os.path.isdir(os.path.join(testdir, "copieddir")))
++        self.assertTrue(os.path.isfile(os.path.join(testdir, "copieddir", "testfile2")))
++        self.assertTrue(not os.path.exists(os.path.join(testdir, "Xdir")))
+ 
+     def test_nsinstall_multiple(self):
+         "Test nsinstall <three files> <dest dir>"
+@@ -91,7 +91,7 @@
+         testdir = self.mkdirs("testdir")
+         self.assertEqual(nsinstall(testfiles + [testdir]), 0)
+         for f in testfiles:
+-            self.assert_(os.path.isfile(os.path.join(testdir,
++            self.assertTrue(os.path.isfile(os.path.join(testdir,
+                                                      os.path.basename(f))))
+ 
+     def test_nsinstall_dir_exists(self):
+@@ -99,7 +99,7 @@
+         srcdir = self.mkdirs("test")
+         destdir = self.mkdirs("testdir/test")
+         self.assertEqual(nsinstall([srcdir, os.path.dirname(destdir)]), 0)
+-        self.assert_(os.path.isdir(destdir))
++        self.assertTrue(os.path.isdir(destdir))
+ 
+     def test_nsinstall_t(self):
+         "Test that nsinstall -t works (preserve timestamp)"
+@@ -110,7 +110,7 @@
+         os.utime(testfile, (t, t))
+         self.assertEqual(nsinstall(["-t", testfile, testdir]), 0)
+         destfile = os.path.join(testdir, "testfile")
+-        self.assert_(os.path.isfile(destfile))
++        self.assertTrue(os.path.isfile(destfile))
+         self.assertEqual(os.stat(testfile).st_mtime,
+                          os.stat(destfile).st_mtime)
+ 
+@@ -125,7 +125,7 @@
+             self.assertEqual(nsinstall(["-m", "{0:04o}"
+                                         .format(mode), testfile, testdir]), 0)
+             destfile = os.path.join(testdir, "testfile")
+-            self.assert_(os.path.isfile(destfile))
++            self.assertTrue(os.path.isfile(destfile))
+             self.assertEqual(os.stat(testfile).st_mode,
+                              os.stat(destfile).st_mode)
+ 
+@@ -136,25 +136,25 @@
+         testdir = self.mkdirs("testdir")
+         destdir = os.path.join(testdir, "subdir")
+         self.assertEqual(nsinstall(["-d", testfile, destdir]), 0)
+-        self.assert_(os.path.isdir(os.path.join(destdir, "testfile")))
++        self.assertTrue(os.path.isdir(os.path.join(destdir, "testfile")))
+ 
+     if RUN_NON_ASCII_TESTS:
+         def test_nsinstall_non_ascii(self):
+             "Test that nsinstall handles non-ASCII files"
+-            filename = u"\u2325\u3452\u2415\u5081"
++            filename = "\u2325\u3452\u2415\u5081"
+             testfile = self.touch(filename)
+-            testdir = self.mkdirs(u"\u4241\u1D04\u1414")
++            testdir = self.mkdirs("\u4241\u1D04\u1414")
+             self.assertEqual(nsinstall([testfile.encode("utf-8"),
+                                         testdir.encode("utf-8")]), 0)
+ 
+             destfile = os.path.join(testdir, filename)
+-            self.assert_(os.path.isfile(destfile))
++            self.assertTrue(os.path.isfile(destfile))
+ 
+         def test_nsinstall_non_ascii_subprocess(self):
+             "Test that nsinstall as a subprocess handles non-ASCII files"
+-            filename = u"\u2325\u3452\u2415\u5081"
++            filename = "\u2325\u3452\u2415\u5081"
+             testfile = self.touch(filename)
+-            testdir = self.mkdirs(u"\u4241\u1D04\u1414")
++            testdir = self.mkdirs("\u4241\u1D04\u1414")
+             # We don't use subprocess because it can't handle Unicode on
+             # Windows <http://bugs.python.org/issue1759845>. mozprocess calls
+             # CreateProcessW directly so it's perfect.
+@@ -166,7 +166,7 @@
+ 
+             self.assertEqual(rv, 0)
+             destfile = os.path.join(testdir, filename)
+-            self.assert_(os.path.isfile(destfile))
++            self.assertTrue(os.path.isfile(destfile))
+ 
+     #TODO: implement -R, -l, -L and test them!
+ 
+--- thunderbird-52.9.0/mozilla/config/tests/unitMozZipFile.py	(original)
++++ thunderbird-52.9.0/mozilla/config/tests/unitMozZipFile.py	(refactored)
+@@ -11,6 +11,7 @@
+ import random
+ import copy
+ from string import letters
++from functools import reduce
+ 
+ '''
+ Test case infrastructure for MozZipFile.
+@@ -38,7 +39,7 @@
+   'firstdir/oneleaf',
+   'seconddir/twoleaf',
+   'thirddir/with/sub/threeleaf')
+-_lengths = map(lambda n: n * 64, [16, 64, 80])
++_lengths = [n * 64 for n in [16, 64, 80]]
+ lengths = 3
+ writes = 5
+ 
+@@ -71,7 +72,7 @@
+ def getContent(length):
+   'Get pseudo random content of given length.'
+   rv = [None] * length
+-  for i in xrange(length):
++  for i in range(length):
+     rv[i] = random.choice(letters)
+   return ''.join(rv)
+ 
+@@ -133,13 +134,13 @@
+   def _verifyZip(self):
+     zf = zipfile.ZipFile(self.f)
+     badEntry = zf.testzip()
+-    self.failIf(badEntry, badEntry)
++    self.assertFalse(badEntry, badEntry)
+     zlist = zf.namelist()
+     zlist.sort()
+-    vlist = self.ref.keys()
++    vlist = list(self.ref.keys())
+     vlist.sort()
+     self.assertEqual(zlist, vlist)
+-    for leaf, content in self.ref.iteritems():
++    for leaf, content in self.ref.items():
+       zcontent = zf.read(leaf)
+       self.assertEqual(content, zcontent)
+   
+@@ -158,16 +159,16 @@
+     open(self.leaf('stage', leaf), 'w').write(content)
+ 
+ # all leafs in all lengths
+-atomics = list(prod(xrange(len(leafs)), xrange(lengths)))
++atomics = list(prod(range(len(leafs)), range(lengths)))
+ 
+ # populate TestExtensiveStore with testcases
+-for w in xrange(writes):
++for w in range(writes):
+   # Don't iterate over all files for the the first n passes,
+   # those are redundant as long as w < lengths.
+   # There are symmetries in the trailing end, too, but I don't know
+   # how to reduce those out right now.
+-  nonatomics = [list(prod(range(min(i,len(leafs))), xrange(lengths)))
+-                for i in xrange(1, w+1)] + [atomics]
++  nonatomics = [list(prod(list(range(min(i,len(leafs)))), range(lengths)))
++                for i in range(1, w+1)] + [atomics]
+   for descs in prod(*nonatomics):
+     suffix = getid(descs)
+     dicts = [dict(leaf=leaf, length=length) for leaf, length in descs]
+@@ -181,9 +182,9 @@
+ # and then write all atomics again.
+ # This should catch more or less all artifacts generated
+ # by the final ordering step when closing the jar.
+-files = [list(prod([i], xrange(lengths))) for i in xrange(len(leafs))]
++files = [list(prod([i], range(lengths))) for i in range(len(leafs))]
+ allfiles = reduce(lambda l,r:l+r,
+-                  [list(prod(*files[:(i+1)])) for i in xrange(len(leafs))])
++                  [list(prod(*files[:(i+1)])) for i in range(len(leafs))])
+ 
+ for first in allfiles:
+   testbasename = 'test{0}_'.format(getid(first))
+--- thunderbird-52.9.0/mozilla/config/expandlibs_exec.py.old	2018-07-11 05:07:18.280000000 +0000
++++ thunderbird-52.9.0/mozilla/config/expandlibs_exec.py	2018-07-11 05:08:45.670000000 +0000
+@@ -344,7 +344,7 @@
+         (stdout, stderr) = proc.communicate()
+         if proc.returncode and not options.verbose:
+             print_command(sys.stderr, args)
+-        sys.stderr.write(stdout)
++        sys.stderr.write(stdout.decode('utf-8'))
+         sys.stderr.flush()
+         if proc.returncode:
+             return proc.returncode
+--- thunderbird-52.9.0/mozilla/js/src/jsautokw.py.old	2017-04-11 02:13:16.000000000 +0000
++++ thunderbird-52.9.0/mozilla/js/src/jsautokw.py	2018-07-11 05:11:27.080000000 +0000
+@@ -80,14 +80,14 @@
+         per_column = column_dict.setdefault(keyword[column], [])
+         per_column.append(item)
+ 
+-    return sorted(column_dict.items(), key=lambda (char, keyword): ord(char))
++    return sorted(list(column_dict.items()), key=lambda char_keyword: ord(char_keyword[0]))
+ 
+ def generate_letter_switch(opt, unprocessed_columns, keyword_list,
+                            columns=None):
+     assert(len(keyword_list) != 0);
+ 
+     if not columns:
+-        columns = range(0, unprocessed_columns)
++        columns = list(range(0, unprocessed_columns))
+ 
+     if len(keyword_list) == 1:
+         index, keyword = keyword_list[0]
+@@ -161,7 +161,7 @@
+         per_length = length_dict.setdefault(len(keyword), [])
+         per_length.append(item)
+ 
+-    return sorted(length_dict.items(), key=lambda (length, keyword): length)
++    return sorted(list(length_dict.items()), key=lambda length_keyword: length_keyword[0])
+ 
+ def generate_switch(opt, keyword_list):
+     assert(len(keyword_list) != 0);
+--- thunderbird-52.9.0/mozilla/js/src/builtin/embedjs.py	(original)
++++ thunderbird-52.9.0/mozilla/js/src/builtin/embedjs.py	(refactored)
+@@ -36,7 +36,7 @@
+ #
+ # It uses the C preprocessor to process its inputs.
+ 
+-from __future__ import with_statement
++
+ import re, sys, os, subprocess
+ import shlex
+ import which
+@@ -109,7 +109,7 @@
+ 
+   with open(tmpIn, 'wb') as input:
+     input.write(source)
+-  print(' '.join(cxx + outputArg + args + [tmpIn]))
++  print((' '.join(cxx + outputArg + args + [tmpIn])))
+   result = subprocess.Popen(cxx + outputArg + args + [tmpIn]).wait()
+   if (result != 0):
+     sys.exit(result);
+@@ -132,7 +132,7 @@
+ 
+ def get_config_defines(buildconfig):
+   # Collect defines equivalent to ACDEFINES and add MOZ_DEBUG_DEFINES.
+-  env = {key: value for key, value in buildconfig.defines.iteritems()
++  env = {key: value for key, value in buildconfig.defines.items()
+          if key not in buildconfig.non_global_defines}
+   for define in buildconfig.substs['MOZ_DEBUG_DEFINES']:
+     env[define] = 1
+--- thunderbird-52.9.0/mozilla/js/src/builtin/embedjs.py.old	2018-07-11 05:13:28.920000000 +0000
++++ thunderbird-52.9.0/mozilla/js/src/builtin/embedjs.py	2018-07-11 05:15:15.390000000 +0000
+@@ -107,7 +107,7 @@
+   tmpOut = 'self-hosting-preprocessed.pp';
+   outputArg = shlex.split(preprocessorOption + tmpOut)
+ 
+-  with open(tmpIn, 'wb') as input:
++  with open(tmpIn, 'w') as input:
+     input.write(source)
+   print((' '.join(cxx + outputArg + args + [tmpIn])))
+   result = subprocess.Popen(cxx + outputArg + args + [tmpIn]).wait()
+--- thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/preprocessor.py.old	2018-07-11 05:15:42.900000000 +0000
++++ thunderbird-52.9.0/mozilla/python/mozbuild/mozbuild/preprocessor.py	2018-07-11 05:23:10.870000000 +0000
+@@ -451,7 +451,7 @@
+                 except OSError as error:
+                     if error.errno != errno.EEXIST:
+                         raise
+-            return open(path, 'wb')
++            return open(path, 'w')
+ 
+         p = self.getCommandLineParser()
+         options, args = p.parse_args(args=args)
+--- thunderbird-52.9.0/mozilla/js/src/builtin/embedjs.py.old	2018-07-11 05:15:42.950000000 +0000
++++ thunderbird-52.9.0/mozilla/js/src/builtin/embedjs.py	2018-07-11 05:20:14.280000000 +0000
+@@ -53,7 +53,7 @@
+ def ToCArray(lines):
+   result = []
+   for chr in lines:
+-    result.append(str(ord(chr)))
++    result.append(str(chr))
+   return ", ".join(result)
+ 
+ HEADER_TEMPLATE = """\
+@@ -87,7 +87,7 @@
+ 
+   js_out.write(processed)
+   import zlib
+-  compressed = zlib.compress(processed)
++  compressed = zlib.compress(processed.encode('utf-8'))
+   data = ToCArray(compressed)
+   c_out.write(HEADER_TEMPLATE % {
+     'sources_type': 'unsigned char',
+--- thunderbird-52.9.0/mozilla/build/mach_bootstrap.py.old	2018-06-29 23:00:39.000000000 +0000
++++ thunderbird-52.9.0/mozilla/build/mach_bootstrap.py	2018-07-26 00:41:17.740000000 +0000
+@@ -12,7 +12,7 @@
+ import subprocess
+ import sys
+ import uuid
+-import __builtin__
++import builtins
+ 
+ from types import ModuleType
+ 
+@@ -187,10 +187,10 @@
+     # Ensure we are running Python 2.7+. We put this check here so we generate a
+     # user-friendly error message rather than a cryptic stack trace on module
+     # import.
+-    if sys.version_info[0] != 2 or sys.version_info[1] < 7:
+-        print('Python 2.7 or above (but not Python 3) is required to run mach.')
+-        print('You are running Python', platform.python_version())
+-        sys.exit(1)
++    #if sys.version_info[0] != 2 or sys.version_info[1] < 7:
++    #    print('Python 2.7 or above (but not Python 3) is required to run mach.')
++    #    print('You are running Python', platform.python_version())
++    #    sys.exit(1)
+ 
+     # Global build system and mach state is stored in a central directory. By
+     # default, this is ~/.mozbuild. However, it can be defined via an
+@@ -410,4 +410,4 @@
+ 
+ 
+ # Install our hook
+-__builtin__.__import__ = ImportHook(__builtin__.__import__)
++builtins.__import__ = ImportHook(builtins.__import__)
+--- thunderbird-52.9.0/mozilla/python/mach/mach/config.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mach/mach/config.py	(refactored)
+@@ -163,7 +163,7 @@
+             return func(*args, **kwargs)
+         except KeyError:
+             exc_class, exc, tb = sys.exc_info()
+-            raise AttributeError().__class__, exc, tb
++            raise AttributeError().__class__(exc).with_traceback(tb)
+     return _
+ 
+ 
+@@ -357,7 +357,7 @@
+             extra -- A dict of additional key/value pairs to add to the
+                 setting metadata.
+         """
+-        if isinstance(type_cls, basestring):
++        if isinstance(type_cls, str_type):
+             type_cls = TYPE_CLASSES[type_cls]
+ 
+         meta = {
+@@ -397,10 +397,10 @@
+             meta = self._format_metadata(provider, section, option, *setting[1:])
+             config_settings[section][option] = meta
+ 
+-        for section_name, settings in config_settings.items():
++        for section_name, settings in list(config_settings.items()):
+             section = self._settings.get(section_name, {})
+ 
+-            for k, v in settings.items():
++            for k, v in list(settings.items()):
+                 if k in section:
+                     raise ConfigException('Setting already registered: %s.%s' %
+                                           section_name, k)
+@@ -432,7 +432,7 @@
+         if self._finalized:
+             return
+ 
+-        for section, settings in self._settings.items():
++        for section, settings in list(self._settings.items()):
+             s = ConfigSettings.ConfigSection(self._config, section, settings)
+             self._sections[section] = s
+ 
+@@ -445,7 +445,7 @@
+     def __iter__(self):
+         self._finalize()
+ 
+-        return iter(self._sections.keys())
++        return iter(list(self._sections.keys()))
+ 
+     def __contains__(self, k):
+         return k in self._settings
+--- thunderbird-52.9.0/mozilla/python/mach/mach/dispatcher.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mach/mach/dispatcher.py	(refactored)
+@@ -243,7 +243,7 @@
+         r = self._mach_registrar
+         disabled_commands = []
+ 
+-        cats = [(k, v[2]) for k, v in r.categories.items()]
++        cats = [(k, v[2]) for k, v in list(r.categories.items())]
+         sorted_cats = sorted(cats, key=itemgetter(1), reverse=True)
+         for category, priority in sorted_cats:
+             group = None
+@@ -375,7 +375,7 @@
+             ' subcommand [subcommand arguments]'
+         group = parser.add_argument_group('Sub Commands')
+ 
+-        for subcommand, subhandler in sorted(handler.subcommand_handlers.iteritems()):
++        for subcommand, subhandler in sorted(handler.subcommand_handlers.items()):
+             group.add_argument(subcommand, help=subhandler.description,
+                 action='store_true')
+ 
+@@ -406,7 +406,7 @@
+ 
+     def _suggest_command(self, command):
+         # Make sure we don't suggest any deprecated commands.
+-        names = [h.name for h in self._mach_registrar.command_handlers.values()
++        names = [h.name for h in list(self._mach_registrar.command_handlers.values())
+                     if h.cls.__name__ != 'DeprecatedCommands']
+         # We first try to look for a valid command that is very similar to the given command.
+         suggested_commands = difflib.get_close_matches(command, names, cutoff=0.8)
+@@ -437,13 +437,13 @@
+     if not docstring:
+         return ''
+     lines = docstring.expandtabs().splitlines()
+-    indent = sys.maxint
++    indent = sys.maxsize
+     for line in lines[1:]:
+         stripped = line.lstrip()
+         if stripped:
+             indent = min(indent, len(line) - len(stripped))
+     trimmed = [lines[0].strip()]
+-    if indent < sys.maxint:
++    if indent < sys.maxsize:
+         for line in lines[1:]:
+             trimmed.append(line[indent:].rstrip())
+     while trimmed and not trimmed[-1]:
+--- thunderbird-52.9.0/mozilla/python/mach/mach/main.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mach/mach/main.py	(refactored)
+@@ -528,7 +528,7 @@
+ 
+             machrc, .machrc
+         """
+-        if isinstance(paths, basestring):
++        if isinstance(paths, str):
+             paths = [paths]
+ 
+         valid_names = ('machrc', '.machrc')
+@@ -541,8 +541,8 @@
+                 if os.path.isfile(path):
+                     return path
+ 
+-        files = map(find_in_dir, self.settings_paths)
+-        files = filter(bool, files)
++        files = list(map(find_in_dir, self.settings_paths))
++        files = list(filter(bool, files))
+ 
+         self.settings.load_files(files)
+ 
+--- thunderbird-52.9.0/mozilla/python/mach/mach/registrar.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mach/mach/registrar.py	(refactored)
+@@ -90,7 +90,7 @@
+             result = fn(**kwargs)
+ 
+         result = result or 0
+-        assert isinstance(result, (int, long))
++        assert isinstance(result, int)
+ 
+         if context:
+             postrun = getattr(context, 'post_dispatch_handler', None)
+--- thunderbird-52.9.0/mozilla/python/mach/mach/terminal.py	(original)
++++ thunderbird-52.9.0/mozilla/python/mach/mach/terminal.py	(refactored)
+@@ -59,7 +59,7 @@
+         self.fh = sys.stdout
+ 
+     def _clear_lines(self, n):
+-        for i in xrange(n):
++        for i in range(n):
+             self.fh.write(self.t.move_x(0))
+             self.fh.write(self.t.clear_eol())
+             self.fh.write(self.t.move_up())
+--- thunderbird-52.9.0/mozilla/python/mach/mach/main.py.old	2018-07-26 00:55:29.330000000 +0000
++++ thunderbird-52.9.0/mozilla/python/mach/mach/main.py	2018-07-26 00:57:01.260000000 +0000
+@@ -256,9 +256,9 @@
+         if module_name is None:
+             # Ensure parent module is present otherwise we'll (likely) get
+             # an error due to unknown parent.
+-            if b'mach.commands' not in sys.modules:
+-                mod = imp.new_module(b'mach.commands')
+-                sys.modules[b'mach.commands'] = mod
++            if 'mach.commands' not in sys.modules:
++                mod = imp.new_module('mach.commands')
++                sys.modules['mach.commands'] = mod
+ 
+             module_name = 'mach.commands.%s' % uuid.uuid1().get_hex()
+ 
+@@ -347,7 +347,7 @@
+             # is a TTY. This provides a mechanism to allow said processes to
+             # enable emitting code codes, for example.
+             if os.isatty(orig_stdout.fileno()):
+-                os.environ[b'MACH_STDOUT_ISATTY'] = b'1'
++                os.environ['MACH_STDOUT_ISATTY'] = '1'
+ 
+             return self._run(argv)
+         except KeyboardInterrupt:
-- 
GitLab