← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] ~cjwatson/launchpad:six-dict-iter into launchpad:master

 

Colin Watson has proposed merging ~cjwatson/launchpad:six-dict-iter into launchpad:master.

Commit message:
Use six for dict iteration

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/+git/launchpad/+merge/383369

`dict.iterkeys()` can just be replaced with `dict` if the result is being iterated over, since `iter(dict)` iterates over its keys.  Otherwise, `dict.iter{keys,values,items}()` becomes `six.iter{keys,values,items}(dict)`.
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of ~cjwatson/launchpad:six-dict-iter into launchpad:master.
diff --git a/brzplugins/lpserve/__init__.py b/brzplugins/lpserve/__init__.py
index b956568..3ce2081 100644
--- a/brzplugins/lpserve/__init__.py
+++ b/brzplugins/lpserve/__init__.py
@@ -48,6 +48,7 @@ from breezy.transport import (
     get_transport,
     transport_server_registry,
     )
+import six
 
 
 class cmd_launchpad_server(Command):
@@ -591,7 +592,7 @@ class LPForkingService(object):
             pid = os.getpid()
             trace.mutter('%d spawned' % (pid,))
             self._server_socket.close()
-            for env_var, value in env.iteritems():
+            for env_var, value in six.iteritems(env):
                 osutils.set_or_unset_env(env_var, value)
             # See [Decision #3]
             self._create_child_file_descriptors(temp_name)
@@ -769,7 +770,7 @@ class LPForkingService(object):
             # We sent the SIGKILL signal, see if they exited
             self._wait_for_children(self.SLEEP_FOR_CHILDREN_TIMEOUT)
         if self._child_processes:
-            for c_id, (c_path, sock) in self._child_processes.iteritems():
+            for c_id, (c_path, sock) in six.iteritems(self._child_processes):
                 # TODO: We should probably put something into this message?
                 #       However, the likelyhood is very small that this isn't
                 #       already closed because of SIGKILL + _wait_for_children
diff --git a/brzplugins/lpserve/test_lpserve.py b/brzplugins/lpserve/test_lpserve.py
index 3801df4..1420ed7 100644
--- a/brzplugins/lpserve/test_lpserve.py
+++ b/brzplugins/lpserve/test_lpserve.py
@@ -18,6 +18,7 @@ from breezy import (
     trace,
     )
 from breezy.plugins import lpserve
+import six
 from testtools import content
 
 from lp.codehosting import (
@@ -336,11 +337,11 @@ class TestCaseWithSubprocess(tests.TestCaseWithTransport):
         old_env = {}
 
         def cleanup_environment():
-            for env_var, value in env_changes.iteritems():
+            for env_var, value in six.iteritems(env_changes):
                 old_env[env_var] = osutils.set_or_unset_env(env_var, value)
 
         def restore_environment():
-            for env_var, value in old_env.iteritems():
+            for env_var, value in six.iteritems(old_env):
                 osutils.set_or_unset_env(env_var, value)
 
         cwd = None
@@ -406,7 +407,7 @@ class TestCaseWithLPForkingServiceSubprocess(TestCaseWithSubprocess):
     def send_fork_request(self, command, env=None):
         if env is not None:
             request_lines = ['fork-env %s\n' % (command,)]
-            for key, value in env.iteritems():
+            for key, value in six.iteritems(env):
                 request_lines.append('%s: %s\n' % (key, value))
             request_lines.append('end\n')
             request = ''.join(request_lines)
diff --git a/database/schema/security.py b/database/schema/security.py
index 1c53659..274bb03 100755
--- a/database/schema/security.py
+++ b/database/schema/security.py
@@ -13,6 +13,8 @@ import os
 import re
 import sys
 
+import six
+
 from fti import quote_identifier
 from lp.services.compat import SafeConfigParser
 from lp.services.database.sqlbase import connect
@@ -302,8 +304,8 @@ class PermissionGatherer:
             to grant or revoke for.  Each is a string.
         """
         result = []
-        for permission, parties in self.permissions.iteritems():
-            for principal, entities in parties.iteritems():
+        for permission, parties in six.iteritems(self.permissions):
+            for principal, entities in six.iteritems(parties):
                 result.append(
                     (permission, ", ".join(entities), principal))
         return result
@@ -315,8 +317,8 @@ class PermissionGatherer:
     def countEntities(self):
         """Count the number of different entities."""
         entities = set()
-        for entities_and_entities in self.permissions.itervalues():
-            for extra_entities in entities_and_entities.itervalues():
+        for entities_and_entities in six.itervalues(self.permissions):
+            for extra_entities in six.itervalues(entities_and_entities):
                 entities.update(extra_entities)
         return len(entities)
 
@@ -324,7 +326,7 @@ class PermissionGatherer:
         """Count the number of different principals."""
         return len(set(sum([
             principals.keys()
-            for principals in self.permissions.itervalues()], [])))
+            for principals in six.itervalues(self.permissions)], [])))
 
     def grant(self, cur):
         """Grant all gathered permissions.
@@ -480,7 +482,7 @@ def reset_permissions(con, config, options):
 
     log.debug('Updating group memberships')
     existing_memberships = list_role_members(cur, memberships.keys())
-    for group, users in memberships.iteritems():
+    for group, users in six.iteritems(memberships):
         cur_users = managed_roles.intersection(existing_memberships[group])
         to_grant = users - cur_users
         if to_grant:
@@ -517,7 +519,7 @@ def reset_permissions(con, config, options):
     # Set permissions as per config file
     desired_permissions = defaultdict(lambda: defaultdict(set))
 
-    valid_objs = set(schema.iterkeys())
+    valid_objs = set(schema)
 
     # Any object with permissions granted is accessible to the 'read'
     # role. Some (eg. the lp_* replicated tables and internal or trigger
@@ -618,7 +620,7 @@ def reset_permissions(con, config, options):
             new = desired_permissions[obj][role]
             old_privs = obj.acl.get(role, {})
             old = set(old_privs)
-            if any(old_privs.itervalues()):
+            if any(six.itervalues(old_privs)):
                 log.warning("%s has grant option on %s", role, obj.fullname)
             if new == old:
                 continue
diff --git a/lib/contrib/oauth.py b/lib/contrib/oauth.py
index 47a821d..2d11e6c 100644
--- a/lib/contrib/oauth.py
+++ b/lib/contrib/oauth.py
@@ -3,6 +3,7 @@ import hmac
 import random
 import time
 
+import six
 from six.moves.urllib.parse import (
     parse_qs,
     quote,
@@ -118,7 +119,7 @@ class OAuthRequest(object):
     # get any non-oauth parameters
     def get_nonoauth_parameters(self):
         parameters = {}
-        for k, v in self.parameters.iteritems():
+        for k, v in six.iteritems(self.parameters):
             # ignore oauth parameters
             if k.find('oauth_') < 0:
                 parameters[k] = v
@@ -129,13 +130,15 @@ class OAuthRequest(object):
         auth_header = 'OAuth realm="%s"' % realm
         # add the oauth parameters
         if self.parameters:
-            for k, v in self.parameters.iteritems():
+            for k, v in six.iteritems(self.parameters):
                 auth_header += ', %s="%s"' % (k, v)
         return {'Authorization': auth_header}
 
     # serialize as post data for a POST request
     def to_postdata(self):
-        return '&'.join('%s=%s' % (escape(str(k)), escape(str(v))) for k, v in self.parameters.iteritems())
+        return '&'.join(
+            '%s=%s' % (escape(str(k)), escape(str(v)))
+            for k, v in six.iteritems(self.parameters))
 
     # serialize as a url for a GET request
     def to_url(self):
@@ -269,7 +272,7 @@ class OAuthRequest(object):
     @staticmethod
     def _split_url_string(param_str):
         parameters = parse_qs(param_str, keep_blank_values=False)
-        for k, v in parameters.iteritems():
+        for k, v in six.iteritems(parameters):
             parameters[k] = unquote(v[0])
         return parameters
 
diff --git a/lib/lp/app/browser/launchpadform.py b/lib/lp/app/browser/launchpadform.py
index ac6dd15..8899cbd 100644
--- a/lib/lp/app/browser/launchpadform.py
+++ b/lib/lp/app/browser/launchpadform.py
@@ -19,6 +19,7 @@ __all__ = [
 from lazr.lifecycle.event import ObjectModifiedEvent
 from lazr.lifecycle.snapshot import Snapshot
 import simplejson
+import six
 import transaction
 from zope.event import notify
 from zope.formlib import form
@@ -213,7 +214,7 @@ class LaunchpadFormView(LaunchpadView):
             self.form_fields, self.prefix, context, self.request,
             data=self.initial_values, adapters=self.adapters,
             ignore_request=False)
-        for field_name, help_link in self.help_links.iteritems():
+        for field_name, help_link in six.iteritems(self.help_links):
             self.widgets[field_name].help_link = help_link
 
     @property
diff --git a/lib/lp/app/browser/tales.py b/lib/lp/app/browser/tales.py
index df20fcb..8e4921e 100644
--- a/lib/lp/app/browser/tales.py
+++ b/lib/lp/app/browser/tales.py
@@ -23,6 +23,7 @@ from lazr.enum import enumerated_type_registry
 from lazr.restful.utils import get_current_browser_request
 from lazr.uri import URI
 import pytz
+import six
 from six.moves.urllib.parse import quote
 from zope.browserpage import ViewPageTemplateFile
 from zope.component import (
@@ -1418,7 +1419,7 @@ class CustomizableFormatter(ObjectFormatterAPI):
         """
         values = dict(
             (k, v if v is not None else '')
-            for k, v in self._link_summary_values().iteritems())
+            for k, v in six.iteritems(self._link_summary_values()))
         return structured(self._link_summary_template, **values).escapedtext
 
     def _title_values(self):
@@ -1440,7 +1441,7 @@ class CustomizableFormatter(ObjectFormatterAPI):
             return None
         values = dict(
             (k, v if v is not None else '')
-            for k, v in self._title_values().iteritems())
+            for k, v in six.iteritems(self._title_values()))
         return structured(title_template, **values).escapedtext
 
     def sprite_css(self):
diff --git a/lib/lp/archivepublisher/domination.py b/lib/lp/archivepublisher/domination.py
index a02fd78..356a2ee 100644
--- a/lib/lp/archivepublisher/domination.py
+++ b/lib/lp/archivepublisher/domination.py
@@ -60,6 +60,7 @@ from operator import (
     )
 
 import apt_pkg
+import six
 from six.moves import (
     filter as ifilter,
     filterfalse as ifilterfalse,
@@ -669,7 +670,7 @@ class Dominator:
             bins = self.findBinariesForDomination(distroarchseries, pocket)
             sorted_packages = self._sortPackages(bins, generalization)
             self.logger.info("Planning domination of binaries...")
-            for name, pubs in sorted_packages.iteritems():
+            for name, pubs in six.iteritems(sorted_packages):
                 self.logger.debug("Planning domination of %s" % name)
                 assert len(pubs) > 0, "Dominating zero binaries!"
                 live_versions = find_live_binary_versions_pass_1(pubs)
@@ -774,7 +775,7 @@ class Dominator:
         delete = []
 
         self.logger.debug("Dominating sources...")
-        for name, pubs in sorted_packages.iteritems():
+        for name, pubs in six.iteritems(sorted_packages):
             self.logger.debug("Dominating %s" % name)
             assert len(pubs) > 0, "Dominating zero sources!"
             live_versions = find_live_source_versions(pubs)
diff --git a/lib/lp/archivepublisher/model/ftparchive.py b/lib/lp/archivepublisher/model/ftparchive.py
index 39e7d40..6fe3c3c 100644
--- a/lib/lp/archivepublisher/model/ftparchive.py
+++ b/lib/lp/archivepublisher/model/ftparchive.py
@@ -8,6 +8,7 @@ from StringIO import StringIO
 import time
 
 import scandir
+import six
 from storm.expr import (
     Desc,
     Join,
@@ -201,7 +202,7 @@ class FTPArchiveHandler:
         stderr_handler.finalize()
         failures = sorted([
             (tag, receiver.returncode)
-            for tag, receiver in returncodes.iteritems()
+            for tag, receiver in six.iteritems(returncodes)
                 if receiver.returncode != 0])
         if len(failures) > 0:
             by_arch = ["%s (returned %d)" % failure for failure in failures]
@@ -694,8 +695,8 @@ class FTPArchiveHandler:
 
         self.log.debug("Writing file lists for %s" % suite)
         series, pocket = self.distro.getDistroSeriesAndPocket(suite)
-        for component, architectures in filelist.iteritems():
-            for architecture, file_names in architectures.iteritems():
+        for component, architectures in six.iteritems(filelist):
+            for architecture, file_names in six.iteritems(architectures):
                 # XXX wgrant 2010-10-06: There must be a better place to do
                 # this.
                 if architecture == "source":
diff --git a/lib/lp/archivepublisher/publishing.py b/lib/lp/archivepublisher/publishing.py
index cd19ed8..f6246b0 100644
--- a/lib/lp/archivepublisher/publishing.py
+++ b/lib/lp/archivepublisher/publishing.py
@@ -36,6 +36,7 @@ from debian.deb822 import (
     Release,
     )
 import scandir
+import six
 from storm.expr import Desc
 from zope.component import getUtility
 from zope.interface import (
@@ -984,7 +985,7 @@ class Publisher(object):
                             translation_stanza.makeOutput().encode('utf-8')
                             + '\n\n')
 
-            for index in indices.itervalues():
+            for index in six.itervalues(indices):
                 index.close()
 
         if separate_long_descriptions:
diff --git a/lib/lp/archivepublisher/scripts/publish_ftpmaster.py b/lib/lp/archivepublisher/scripts/publish_ftpmaster.py
index 5fcd7d3..c307846 100644
--- a/lib/lp/archivepublisher/scripts/publish_ftpmaster.py
+++ b/lib/lp/archivepublisher/scripts/publish_ftpmaster.py
@@ -15,6 +15,7 @@ import shutil
 
 from pytz import utc
 import scandir
+import six
 from zope.component import getUtility
 
 from lp.archivepublisher.config import getPubConfig
@@ -224,9 +225,7 @@ class PublishFTPMaster(LaunchpadCronScript):
             return []
 
         # May need indexes for this series.
-        suites = [
-            distroseries.getSuite(pocket)
-            for pocket in pocketsuffix.iterkeys()]
+        suites = [distroseries.getSuite(pocket) for pocket in pocketsuffix]
         return [
             suite for suite in suites
                 if not file_exists(self.locateIndexesMarker(distro, suite))]
@@ -289,7 +288,8 @@ class PublishFTPMaster(LaunchpadCronScript):
 
         :param archive_purpose: The (purpose of the) archive to copy.
         """
-        for purpose, archive_config in self.configs[distribution].iteritems():
+        for purpose, archive_config in (
+                six.iteritems(self.configs[distribution])):
             dists = get_dists(archive_config)
             backup_dists = get_backup_dists(archive_config)
             execute_subprocess(
@@ -319,14 +319,15 @@ class PublishFTPMaster(LaunchpadCronScript):
         run died while in this state, restore the directory to its
         permanent location.
         """
-        for distro_configs in self.configs.itervalues():
-            for archive_config in distro_configs.itervalues():
+        for distro_configs in six.itervalues(self.configs):
+            for archive_config in six.itervalues(distro_configs):
                 self.recoverArchiveWorkingDir(archive_config)
 
     def setUpDirs(self):
         """Create archive roots and such if they did not yet exist."""
-        for distro_configs in self.configs.itervalues():
-            for archive_purpose, archive_config in distro_configs.iteritems():
+        for distro_configs in six.itervalues(self.configs):
+            for archive_purpose, archive_config in (
+                    six.iteritems(distro_configs)):
                 archiveroot = archive_config.archiveroot
                 if not file_exists(archiveroot):
                     self.logger.debug(
@@ -407,7 +408,7 @@ class PublishFTPMaster(LaunchpadCronScript):
         backup dists directory around.
         """
         self.logger.debug("Moving the new dists into place...")
-        for archive_config in self.configs[distribution].itervalues():
+        for archive_config in six.itervalues(self.configs[distribution]):
             # Use the dists "working location" as a temporary place to
             # move the current dists out of the way for the switch.  If
             # we die in this state, the next run will know to move the
@@ -422,7 +423,7 @@ class PublishFTPMaster(LaunchpadCronScript):
 
     def clearEmptyDirs(self, distribution):
         """Clear out any redundant empty directories."""
-        for archive_config in self.configs[distribution].itervalues():
+        for archive_config in six.itervalues(self.configs[distribution]):
             execute_subprocess(
                 ["find", archive_config.archiveroot, "-type", "d", "-empty",
                  "-delete"],
@@ -432,7 +433,7 @@ class PublishFTPMaster(LaunchpadCronScript):
         """Run the finalize.d parts to finalize publication."""
         archive_roots = ' '.join([
             archive_config.archiveroot
-            for archive_config in self.configs[distribution].itervalues()])
+            for archive_config in six.itervalues(self.configs[distribution])])
 
         env = {
             'SECURITY_UPLOAD_ONLY': 'yes' if security_only else 'no',
diff --git a/lib/lp/archivepublisher/tests/test_dominator.py b/lib/lp/archivepublisher/tests/test_dominator.py
index b88da56..4d073e0 100755
--- a/lib/lp/archivepublisher/tests/test_dominator.py
+++ b/lib/lp/archivepublisher/tests/test_dominator.py
@@ -11,6 +11,7 @@ import datetime
 from operator import attrgetter
 
 import apt_pkg
+import six
 from testtools.matchers import (
     GreaterThan,
     LessThan,
@@ -813,14 +814,14 @@ class TestDominatorMethods(TestCaseWithFactory):
         # Actually the "oldest to newest" order on the publications only
         # applies to their creation dates.  Their creation orders are
         # irrelevant.
-        for pubs_list in pubs_by_version.itervalues():
+        for pubs_list in six.itervalues(pubs_by_version):
             alter_creation_dates(pubs_list, ages)
             pubs_list.sort(key=attrgetter('datecreated'))
 
         live_versions = ["1.1", "1.2"]
         last_version_alive = sorted(live_versions)[-1]
 
-        all_pubs = sum(pubs_by_version.itervalues(), [])
+        all_pubs = sum(six.itervalues(pubs_by_version), [])
         dominator = Dominator(DevNullLogger(), series.main_archive)
         supersede, _, delete = dominator.planPackageDomination(
             generalization.sortPublications(all_pubs), live_versions,
diff --git a/lib/lp/archivepublisher/tests/test_publish_ftpmaster.py b/lib/lp/archivepublisher/tests/test_publish_ftpmaster.py
index 8c99b73..450da11 100644
--- a/lib/lp/archivepublisher/tests/test_publish_ftpmaster.py
+++ b/lib/lp/archivepublisher/tests/test_publish_ftpmaster.py
@@ -121,8 +121,7 @@ def get_a_suite(distroseries):
 
 def get_marker_files(script, distroseries):
     """Return filesystem paths for all indexes markers for `distroseries`."""
-    suites = [
-        distroseries.getSuite(pocket) for pocket in pocketsuffix.iterkeys()]
+    suites = [distroseries.getSuite(pocket) for pocket in pocketsuffix]
     distro = distroseries.distribution
     return [script.locateIndexesMarker(distro, suite) for suite in suites]
 
@@ -998,7 +997,7 @@ class TestCreateDistroSeriesIndexes(TestCaseWithFactory, HelpersMixin):
         script = self.makeScript(series.distribution)
         script.setUp()
         self.assertContentEqual(
-            [series.getSuite(pocket) for pocket in pocketsuffix.iterkeys()],
+            [series.getSuite(pocket) for pocket in pocketsuffix],
             script.listSuitesNeedingIndexes(series))
 
     def test_listSuitesNeedingIndexes_is_empty_for_nonfrozen_series(self):
@@ -1133,7 +1132,7 @@ class TestCreateDistroSeriesIndexes(TestCaseWithFactory, HelpersMixin):
         [((given_distro, given_suites), kwargs)] = script.createIndexes.calls
         self.assertEqual(distro, given_distro)
         self.assertContentEqual(
-            [series.getSuite(pocket) for pocket in pocketsuffix.iterkeys()],
+            [series.getSuite(pocket) for pocket in pocketsuffix],
             given_suites)
 
     def test_createIndexes_ignores_other_series(self):
diff --git a/lib/lp/archiveuploader/dscfile.py b/lib/lp/archiveuploader/dscfile.py
index 604193c..fbe1e25 100644
--- a/lib/lp/archiveuploader/dscfile.py
+++ b/lib/lp/archiveuploader/dscfile.py
@@ -684,7 +684,7 @@ class DSCFile(SourceUploadFile, SignableTagFile):
             ISourcePackageNameSet).getOrCreateByName(self.source)
 
         user_defined_fields = self.extractUserDefinedFields([
-            (field, encoded[field]) for field in self._dict.iterkeys()])
+            (field, encoded[field]) for field in self._dict])
 
         if self.changes.buildinfo is not None:
             buildinfo_lfa = self.changes.buildinfo.storeInDatabase()
diff --git a/lib/lp/archiveuploader/nascentuploadfile.py b/lib/lp/archiveuploader/nascentuploadfile.py
index f28e43e..0791141 100644
--- a/lib/lp/archiveuploader/nascentuploadfile.py
+++ b/lib/lp/archiveuploader/nascentuploadfile.py
@@ -26,6 +26,7 @@ import time
 import apt_inst
 import apt_pkg
 from debian.deb822 import Deb822Dict
+import six
 from zope.component import getUtility
 
 from lp.app.errors import NotFoundError
@@ -218,7 +219,7 @@ class NascentUploadFile:
         ckfile = open(self.filepath, "r")
         size = 0
         for chunk in filechunks(ckfile):
-            for digester in digesters.itervalues():
+            for digester in six.itervalues(digesters):
                 digester.update(chunk)
             size += len(chunk)
         ckfile.close()
@@ -905,7 +906,7 @@ class BaseBinaryUploadFile(PackageUploadFile):
             debug_package = None
 
         user_defined_fields = self.extractUserDefinedFields(
-            [(field, encoded[field]) for field in self.control.iterkeys()])
+            [(field, encoded[field]) for field in self.control])
 
         binary = build.createBinaryPackageRelease(
             binarypackagename=binary_name,
diff --git a/lib/lp/archiveuploader/utils.py b/lib/lp/archiveuploader/utils.py
index ac8057d..67a009f 100644
--- a/lib/lp/archiveuploader/utils.py
+++ b/lib/lp/archiveuploader/utils.py
@@ -39,6 +39,8 @@ import re
 import signal
 import subprocess
 
+import six
+
 from lp.services.encoding import guess as guess_encoding
 from lp.soyuz.enums import BinaryPackageFileType
 
@@ -333,7 +335,7 @@ def merge_file_lists(files, checksums_sha1, checksums_sha256, changes=True):
                 (filename, file_hashes[filename], size))
 
     # Ensure that each filename was only listed in Files once.
-    if set(file_counter.itervalues()) - set([1]):
+    if set(six.itervalues(file_counter)) - set([1]):
         raise UploadError("Duplicate filenames in Files field.")
 
     # Ensure that the Checksums-Sha1 and Checksums-Sha256 fields, if
diff --git a/lib/lp/bugs/browser/bugalsoaffects.py b/lib/lp/bugs/browser/bugalsoaffects.py
index 4fa3421..b4f5ea5 100644
--- a/lib/lp/bugs/browser/bugalsoaffects.py
+++ b/lib/lp/bugs/browser/bugalsoaffects.py
@@ -16,6 +16,7 @@ from lazr.enum import (
     Item,
     )
 from lazr.lifecycle.event import ObjectCreatedEvent
+import six
 from zope.browserpage import ViewPageTemplateFile
 from zope.component import getUtility
 from zope.event import notify
@@ -605,7 +606,7 @@ class ProductBugTaskCreationStep(BugTaskCreationStep):
 
             # Don't request validation for text widgets that are not
             # related to the current radio selection.
-            for option, name in link_upstream_options.iteritems():
+            for option, name in six.iteritems(link_upstream_options):
                 if link_upstream_how != option:
                     names.discard(name)
                 elif self.widgets[name].hasValidInput():
@@ -619,7 +620,7 @@ class ProductBugTaskCreationStep(BugTaskCreationStep):
         else:
             # Don't validate these widgets when we don't yet know how
             # we intend to link upstream.
-            names.difference_update(link_upstream_options.itervalues())
+            names.difference_update(six.itervalues(link_upstream_options))
 
         return super(ProductBugTaskCreationStep,
                      self).validate_widgets(data, names)
diff --git a/lib/lp/bugs/browser/bugtarget.py b/lib/lp/bugs/browser/bugtarget.py
index 5755c1b..01b323d 100644
--- a/lib/lp/bugs/browser/bugtarget.py
+++ b/lib/lp/bugs/browser/bugtarget.py
@@ -28,6 +28,7 @@ from lazr.restful.interface import copy_field
 from lazr.restful.interfaces import IJSONRequestCache
 from pytz import timezone
 from simplejson import dumps
+import six
 from six.moves import http_client
 from six.moves.urllib.parse import (
     quote,
@@ -1235,7 +1236,7 @@ class BugTargetBugTagsView(LaunchpadView):
                 count=count,
                 url=self._getSearchURL(tag),
                 )
-            for (tag, count) in tags.iteritems()],
+            for (tag, count) in six.iteritems(tags)],
             key=itemgetter('count'), reverse=True)
 
     @property
diff --git a/lib/lp/bugs/interfaces/bugtaskfilter.py b/lib/lp/bugs/interfaces/bugtaskfilter.py
index e3407ed..898c955 100644
--- a/lib/lp/bugs/interfaces/bugtaskfilter.py
+++ b/lib/lp/bugs/interfaces/bugtaskfilter.py
@@ -17,6 +17,8 @@ from collections import (
     )
 from operator import attrgetter
 
+import six
+
 from lp.bugs.interfaces.bugtarget import IHasBugs
 
 
@@ -67,5 +69,5 @@ def filter_bugtasks_by_context(context, bugtasks):
     for task in bugtasks:
         bug_mapping[task.bugID].append(weight_calculator(task))
 
-    filtered = [sorted(tasks)[0].task for tasks in bug_mapping.itervalues()]
+    filtered = [sorted(tasks)[0].task for tasks in six.itervalues(bug_mapping)]
     return sorted(filtered, key=attrgetter('bugID'))
diff --git a/lib/lp/bugs/model/bugtask.py b/lib/lp/bugs/model/bugtask.py
index 2c3b6ef..fe7d366 100644
--- a/lib/lp/bugs/model/bugtask.py
+++ b/lib/lp/bugs/model/bugtask.py
@@ -32,6 +32,7 @@ import re
 
 from lazr.lifecycle.event import ObjectDeletedEvent
 import pytz
+import six
 from sqlobject import (
     ForeignKey,
     SQLObjectNotFound,
@@ -1138,7 +1139,7 @@ class BugTask(SQLBase):
             new_key['sourcepackagename'] != self.sourcepackagename):
             self._syncSourcePackages(new_key['sourcepackagename'], user)
 
-        for name, value in new_key.iteritems():
+        for name, value in six.iteritems(new_key):
             setattr(self, name, value)
         self.updateTargetNameCache()
         self.bug._reconcileAccess()
diff --git a/lib/lp/bugs/model/structuralsubscription.py b/lib/lp/bugs/model/structuralsubscription.py
index aec76a8..c2834e3 100644
--- a/lib/lp/bugs/model/structuralsubscription.py
+++ b/lib/lp/bugs/model/structuralsubscription.py
@@ -15,6 +15,7 @@ __all__ = [
 from collections import defaultdict
 
 import pytz
+import six
 from storm.base import Storm
 from storm.expr import (
     And,
@@ -137,7 +138,7 @@ class StructuralSubscription(Storm):
     def __init__(self, subscriber, subscribed_by, **kwargs):
         self.subscriber = subscriber
         self.subscribed_by = subscribed_by
-        for arg, value in kwargs.iteritems():
+        for arg, value in six.iteritems(kwargs):
             setattr(self, arg, value)
 
     @property
@@ -463,7 +464,7 @@ class StructuralSubscriptionTargetMixin:
         """See `IStructuralSubscriptionTarget`."""
         from lp.registry.model.person import Person
         clauses = [StructuralSubscription.subscriberID == Person.id]
-        for key, value in self._target_args.iteritems():
+        for key, value in six.iteritems(self._target_args):
             clauses.append(
                 getattr(StructuralSubscription, key) == value)
 
diff --git a/lib/lp/bugs/model/tests/test_bugsubscriptioninfo.py b/lib/lp/bugs/model/tests/test_bugsubscriptioninfo.py
index 89c5c0f..f261fcb 100644
--- a/lib/lp/bugs/model/tests/test_bugsubscriptioninfo.py
+++ b/lib/lp/bugs/model/tests/test_bugsubscriptioninfo.py
@@ -7,6 +7,7 @@ __metaclass__ = type
 
 from contextlib import contextmanager
 
+import six
 from storm.store import Store
 from testtools.matchers import Equals
 from zope.component import queryAdapter
@@ -64,7 +65,7 @@ class TestSubscriptionRelatedSets(TestCaseWithFactory):
         subscribers = dict(
             (name_pair, make_person(*name_pair))
             for name_pair in self.name_pairs)
-        self.subscribers_set = frozenset(subscribers.itervalues())
+        self.subscribers_set = frozenset(six.itervalues(subscribers))
         self.subscribers_sorted = tuple(
             subscribers[name_pair] for name_pair in self.name_pairs_sorted)
 
@@ -428,7 +429,7 @@ class TestBugSubscriptionInfoPermissions(TestCaseWithFactory):
         self.assertEqual({}, checker.set_permissions)
 
         # All attributes require launchpad.View.
-        permissions = set(checker.get_permissions.itervalues())
+        permissions = set(six.itervalues(checker.get_permissions))
         self.assertContentEqual(["launchpad.View"], permissions)
 
         # The security adapter for launchpad.View lets anyone reference the
diff --git a/lib/lp/bugs/scripts/bugsummaryrebuild.py b/lib/lp/bugs/scripts/bugsummaryrebuild.py
index f41d8c2..f000905 100644
--- a/lib/lp/bugs/scripts/bugsummaryrebuild.py
+++ b/lib/lp/bugs/scripts/bugsummaryrebuild.py
@@ -3,6 +3,7 @@
 
 __metaclass__ = type
 
+import six
 from storm.expr import (
     Alias,
     And,
@@ -116,7 +117,7 @@ def get_bugsummary_constraint(target, cls=RawBugSummary):
     # Map to ID columns to work around Storm bug #682989.
     return [
         getattr(cls, k) == v
-        for (k, v) in _get_bugsummary_constraint_bits(target).iteritems()]
+        for (k, v) in six.iteritems(_get_bugsummary_constraint_bits(target))]
 
 
 def get_bugtaskflat_constraint(target):
@@ -161,8 +162,8 @@ def calculate_bugsummary_changes(old, new):
     from the old one.
     """
     keys = set()
-    keys.update(old.iterkeys())
-    keys.update(new.iterkeys())
+    keys.update(six.iterkeys(old))
+    keys.update(six.iterkeys(new))
     added = {}
     updated = {}
     removed = []
@@ -198,7 +199,7 @@ def apply_bugsummary_changes(target, added, updated, removed):
         RawBugSummary.access_policy_id)
 
     # Postgres doesn't do bulk updates, so do a delete+add.
-    for key, count in updated.iteritems():
+    for key, count in six.iteritems(updated):
         removed.append(key)
         added[key] = count
 
@@ -219,7 +220,8 @@ def apply_bugsummary_changes(target, added, updated, removed):
     if added:
         create(
             target_cols + key_cols + (RawBugSummary.count,),
-            [target_key + key + (count,) for key, count in added.iteritems()])
+            [target_key + key + (count,)
+             for key, count in six.iteritems(added)])
 
 
 def rebuild_bugsummary_for_target(target, log):
diff --git a/lib/lp/bugs/scripts/bugtasktargetnamecaches.py b/lib/lp/bugs/scripts/bugtasktargetnamecaches.py
index 5d14052..429ff49 100644
--- a/lib/lp/bugs/scripts/bugtasktargetnamecaches.py
+++ b/lib/lp/bugs/scripts/bugtasktargetnamecaches.py
@@ -8,6 +8,7 @@ __all__ = ['BugTaskTargetNameCacheUpdater']
 
 from collections import defaultdict
 
+import six
 from zope.interface import implementer
 
 from lp.bugs.model.bugtask import (
@@ -65,7 +66,7 @@ class BugTaskTargetNameCachesTunableLoop(object):
         candidates = defaultdict(set)
         for candidate in candidate_set:
             candidates[candidate[:-1]].add(candidate[-1])
-        return list(candidates.iteritems())
+        return list(six.iteritems(candidates))
 
     def isDone(self):
         """See `ITunableLoop`."""
diff --git a/lib/lp/bugs/scripts/bzremotecomponentfinder.py b/lib/lp/bugs/scripts/bzremotecomponentfinder.py
index 4dcb6cb..cf4246e 100644
--- a/lib/lp/bugs/scripts/bzremotecomponentfinder.py
+++ b/lib/lp/bugs/scripts/bzremotecomponentfinder.py
@@ -12,6 +12,7 @@ __all__ = [
 import re
 
 import requests
+import six
 import transaction
 from zope.component import getUtility
 
@@ -163,7 +164,7 @@ class BugzillaRemoteComponentFinder:
     def storeRemoteProductsAndComponents(self, bz_bugtracker, lp_bugtracker):
         """Stores parsed product/component data from bz_bugtracker"""
         components_to_add = []
-        for product in bz_bugtracker.products.itervalues():
+        for product in six.itervalues(bz_bugtracker.products):
             # Look up the component group id from Launchpad for the product
             # if it already exists.  Otherwise, add it.
             lp_component_group = lp_bugtracker.getRemoteComponentGroup(
diff --git a/lib/lp/bugs/tests/externalbugtracker.py b/lib/lp/bugs/tests/externalbugtracker.py
index b8a5b9d..afb1d81 100644
--- a/lib/lp/bugs/tests/externalbugtracker.py
+++ b/lib/lp/bugs/tests/externalbugtracker.py
@@ -17,6 +17,7 @@ import re
 import time
 
 import responses
+import six
 from six.moves import xmlrpc_client
 from six.moves.urllib_parse import (
     parse_qs,
@@ -720,7 +721,7 @@ class TestBugzillaXMLRPCTransport(RequestsTransport):
     def _copy_comment(self, comment, fields_to_return=None):
         # Copy wanted fields.
         return dict(
-            (key, value) for (key, value) in comment.iteritems()
+            (key, value) for (key, value) in six.iteritems(comment)
             if fields_to_return is None or key in fields_to_return)
 
     def comments(self, arguments):
diff --git a/lib/lp/buildmaster/browser/builder.py b/lib/lp/buildmaster/browser/builder.py
index 0c5d05a..b378ea3 100644
--- a/lib/lp/buildmaster/browser/builder.py
+++ b/lib/lp/buildmaster/browser/builder.py
@@ -20,6 +20,7 @@ from itertools import groupby
 import operator
 
 from lazr.restful.utils import smartquote
+import six
 from zope.component import getUtility
 from zope.event import notify
 from zope.formlib.widget import CustomWidgetFactory
@@ -273,7 +274,7 @@ class BuilderCategory:
                 else:
                     grouped_builders[processor] = [builder]
 
-        for processor, builders in grouped_builders.iteritems():
+        for processor, builders in six.iteritems(grouped_builders):
             virt_str = 'virt' if self.virtualized else 'nonvirt'
             processor_name = processor.name if processor else None
             queue_size, duration = build_queue_sizes[virt_str].get(
diff --git a/lib/lp/buildmaster/manager.py b/lib/lp/buildmaster/manager.py
index ea29ff2..3c80622 100644
--- a/lib/lp/buildmaster/manager.py
+++ b/lib/lp/buildmaster/manager.py
@@ -15,6 +15,7 @@ import datetime
 import functools
 import logging
 
+import six
 from storm.expr import LeftJoin
 import transaction
 from twisted.application import service
@@ -139,7 +140,7 @@ class PrefetchedBuilderFactory:
 
     def iterVitals(self):
         """See `BuilderFactory`."""
-        return (b for n, b in sorted(self.vitals_map.iteritems()))
+        return (b for n, b in sorted(six.iteritems(self.vitals_map)))
 
 
 def judge_failure(builder_count, job_count, exc, retry=True):
diff --git a/lib/lp/buildmaster/model/builder.py b/lib/lp/buildmaster/model/builder.py
index 4d32dcf..a44e7fc 100644
--- a/lib/lp/buildmaster/model/builder.py
+++ b/lib/lp/buildmaster/model/builder.py
@@ -11,6 +11,7 @@ __all__ = [
 
 import logging
 
+import six
 from sqlobject import (
     BoolCol,
     ForeignKey,
@@ -243,7 +244,7 @@ class Builder(SQLBase):
 
         job_type_conditions = []
         job_sources = specific_build_farm_job_sources()
-        for job_type, job_source in job_sources.iteritems():
+        for job_type, job_source in six.iteritems(job_sources):
             query = job_source.addCandidateSelectionCriteria(
                 self.processor, self.virtualized)
             if query:
diff --git a/lib/lp/buildmaster/queuedepth.py b/lib/lp/buildmaster/queuedepth.py
index 1fdb151..66f1986 100644
--- a/lib/lp/buildmaster/queuedepth.py
+++ b/lib/lp/buildmaster/queuedepth.py
@@ -14,6 +14,7 @@ from datetime import (
     )
 
 from pytz import utc
+import six
 from storm.expr import Count
 
 from lp.buildmaster.enums import BuildQueueStatus
@@ -275,7 +276,7 @@ def estimate_job_delay(bq, builder_stats):
 
     sum_of_delays = 0
     # Now devide the delays based on a jobs/builders comparison.
-    for platform, duration in delays.iteritems():
+    for platform, duration in six.iteritems(delays):
         jobs = job_counts[platform]
         builders = builder_stats[platform]
         # If there are less jobs than builders that can take them on,
diff --git a/lib/lp/buildmaster/tests/test_queuedepth.py b/lib/lp/buildmaster/tests/test_queuedepth.py
index 090fd11..b9198e1 100644
--- a/lib/lp/buildmaster/tests/test_queuedepth.py
+++ b/lib/lp/buildmaster/tests/test_queuedepth.py
@@ -10,6 +10,7 @@ from datetime import (
     )
 
 from pytz import utc
+import six
 from zope.component import getUtility
 from zope.security.proxy import removeSecurityProxy
 
@@ -698,7 +699,7 @@ class TestMinTimeToNextBuilderMulti(MultiArchBuildsBase):
         check_mintime_to_builder(self, job, 0)
 
         # Let's disable all builders.
-        for builders in self.builders.itervalues():
+        for builders in six.itervalues(self.builders):
             for builder in builders:
                 builder.builderok = False
 
diff --git a/lib/lp/code/browser/branch.py b/lib/lp/code/browser/branch.py
index 404c272..6ea17ac 100644
--- a/lib/lp/code/browser/branch.py
+++ b/lib/lp/code/browser/branch.py
@@ -36,6 +36,7 @@ from lazr.restful.interface import (
 from lazr.uri import URI
 import pytz
 import simplejson
+import six
 from zope.component import getUtility
 from zope.event import notify
 from zope.formlib import form
@@ -954,8 +955,8 @@ class BranchDeletionView(LaunchpadFormView):
         :return: A list of tuples of (item, action, reason, allowed)
         """
         reqs = []
-        for item, (action, reason) in (
-            self.context.deletionRequirements(eager_load=True).iteritems()):
+        for item, (action, reason) in six.iteritems(
+                self.context.deletionRequirements(eager_load=True)):
             allowed = check_permission('launchpad.Edit', item)
             reqs.append((item, action, reason, allowed))
         return reqs
diff --git a/lib/lp/code/browser/branchlisting.py b/lib/lp/code/browser/branchlisting.py
index f472ef0..23a1e9c 100644
--- a/lib/lp/code/browser/branchlisting.py
+++ b/lib/lp/code/browser/branchlisting.py
@@ -33,6 +33,7 @@ from lazr.enum import (
     EnumeratedType,
     Item,
     )
+import six
 from six.moves.urllib.parse import parse_qs
 from storm.expr import Desc
 from zope.browserpage import ViewPageTemplateFile
@@ -1306,7 +1307,7 @@ class GroupedDistributionSourcePackageBranchesView(LaunchpadView,
         # For each distro series, we only want the "best" pocket if one branch
         # is linked to more than one pocket.  Best here means smaller value.
         official_branches = {}
-        for key, value in distro_links.iteritems():
+        for key, value in six.iteritems(distro_links):
             ordered = sorted(value, key=attrgetter('pocket'))
             seen_branches = set()
             branches = []
@@ -1363,7 +1364,7 @@ class GroupedDistributionSourcePackageBranchesView(LaunchpadView,
         and merge proposal links for badges.
         """
         visible_branches = []
-        for branches, count in self.series_branches_map.itervalues():
+        for branches, count in six.itervalues(self.series_branches_map):
             visible_branches.extend(branches)
         return visible_branches
 
diff --git a/lib/lp/code/browser/branchmergeproposal.py b/lib/lp/code/browser/branchmergeproposal.py
index a1b41fb..6d33f97 100644
--- a/lib/lp/code/browser/branchmergeproposal.py
+++ b/lib/lp/code/browser/branchmergeproposal.py
@@ -35,6 +35,7 @@ from lazr.restful.interfaces import (
     IWebServiceClientRequest,
     )
 import simplejson
+import six
 from zope.component import (
     adapter,
     getMultiAdapter,
@@ -145,7 +146,7 @@ def latest_proposals_for_each_branch(proposals):
             targets[target] = (proposal, date_created)
 
     return sorted(
-        [proposal for proposal, date_created in targets.itervalues()],
+        [proposal for proposal, date_created in six.itervalues(targets)],
         key=operator.attrgetter('date_created'), reverse=True)
 
 
diff --git a/lib/lp/code/browser/gitrepository.py b/lib/lp/code/browser/gitrepository.py
index c05dcc1..4f38702 100644
--- a/lib/lp/code/browser/gitrepository.py
+++ b/lib/lp/code/browser/gitrepository.py
@@ -31,6 +31,7 @@ from lazr.restful.interface import (
     copy_field,
     use_template,
     )
+import six
 from six.moves.urllib_parse import (
     urlsplit,
     urlunsplit,
@@ -1273,9 +1274,8 @@ class GitRepositoryDeletionView(LaunchpadFormView):
         :return: A list of tuples of (item, action, reason, allowed)
         """
         reqs = []
-        for item, (action, reason) in (
-                self.context.getDeletionRequirements(
-                    eager_load=True).iteritems()):
+        for item, (action, reason) in six.iteritems(
+                self.context.getDeletionRequirements(eager_load=True)):
             allowed = check_permission("launchpad.Edit", item)
             reqs.append((item, action, reason, allowed))
         return reqs
diff --git a/lib/lp/code/model/branchcollection.py b/lib/lp/code/model/branchcollection.py
index 79411cc..9c0b097 100644
--- a/lib/lp/code/model/branchcollection.py
+++ b/lib/lp/code/model/branchcollection.py
@@ -16,6 +16,7 @@ from lazr.uri import (
     InvalidURIError,
     URI,
     )
+import six
 from storm.expr import (
     And,
     Asc,
@@ -562,7 +563,7 @@ class GenericBranchCollection:
                 bugtasks_for_branch[bugbranch.branch].append(bugtask)
 
             # Now filter those down to one bugtask per branch
-            for branch, tasks in bugtasks_for_branch.iteritems():
+            for branch, tasks in six.iteritems(bugtasks_for_branch):
                 linked_bugtasks[branch.id].extend(
                     filter_bugtasks_by_context(branch.target.context, tasks))
 
diff --git a/lib/lp/code/model/branchjob.py b/lib/lp/code/model/branchjob.py
index a26c47b..7067f8a 100644
--- a/lib/lp/code/model/branchjob.py
+++ b/lib/lp/code/model/branchjob.py
@@ -684,7 +684,7 @@ class RevisionsAddedJob(BranchJobDerived):
                 proposals[source_id] = (proposal, date_created)
 
         return sorted(
-            [proposal for proposal, date_created in proposals.itervalues()],
+            [proposal for proposal, date_created in six.itervalues(proposals)],
             key=operator.attrgetter('date_created'), reverse=True)
 
     def getRevisionMessage(self, revision_id, revno):
diff --git a/lib/lp/code/model/codereviewinlinecomment.py b/lib/lp/code/model/codereviewinlinecomment.py
index 580eb44..2ac74b0 100644
--- a/lib/lp/code/model/codereviewinlinecomment.py
+++ b/lib/lp/code/model/codereviewinlinecomment.py
@@ -10,6 +10,7 @@ __all__ = [
     'CodeReviewInlineCommentSet',
     ]
 
+import six
 from storm.expr import LeftJoin
 from storm.locals import (
     Int,
@@ -113,7 +114,7 @@ class CodeReviewInlineCommentSet:
             list(crics), key=lambda c: c.comment.date_created)
         inline_comments = []
         for cric in sorted_crics:
-            for line_number, text in cric.comments.iteritems():
+            for line_number, text in six.iteritems(cric.comments):
                 comment = {
                     'line_number': line_number,
                     'person': cric.person,
diff --git a/lib/lp/code/model/revision.py b/lib/lp/code/model/revision.py
index 4895ebb..a979c74 100644
--- a/lib/lp/code/model/revision.py
+++ b/lib/lp/code/model/revision.py
@@ -18,6 +18,7 @@ import email
 
 from breezy.revision import NULL_REVISION
 import pytz
+import six
 from sqlobject import (
     BoolCol,
     ForeignKey,
@@ -287,7 +288,7 @@ class RevisionSet:
                            parent_id=parent_id)
 
         # Create revision properties.
-        for name, value in properties.iteritems():
+        for name, value in six.iteritems(properties):
             RevisionProperty(revision=revision, name=name, value=value)
 
         return revision
@@ -378,7 +379,7 @@ class RevisionSet:
         for bzr_revision in revisions:
             db_id = revision_db_id[bzr_revision.revision_id]
             # Property data: revision DB id, name, value.
-            for name, value in bzr_revision.properties.iteritems():
+            for name, value in six.iteritems(bzr_revision.properties):
                 # pristine-tar properties can be huge, and storing them
                 # in the database provides no value. Exclude them.
                 if name.startswith('deb-pristine-delta'):
diff --git a/lib/lp/codehosting/inmemory.py b/lib/lp/codehosting/inmemory.py
index a2a7103..2b704fd 100644
--- a/lib/lp/codehosting/inmemory.py
+++ b/lib/lp/codehosting/inmemory.py
@@ -131,7 +131,7 @@ class ObjectSet:
         del self._objects[db_object.id]
 
     def __iter__(self):
-        return self._objects.itervalues()
+        return six.itervalues(self._objects)
 
     def _find(self, **kwargs):
         [(key, value)] = kwargs.items()
diff --git a/lib/lp/codehosting/scanner/buglinks.py b/lib/lp/codehosting/scanner/buglinks.py
index f9609c3..116fb90 100644
--- a/lib/lp/codehosting/scanner/buglinks.py
+++ b/lib/lp/codehosting/scanner/buglinks.py
@@ -9,6 +9,7 @@ __all__ = [
     ]
 
 from breezy.bugtracker import InvalidBugStatus
+import six
 from six.moves.urllib.parse import urlsplit
 from zope.component import getUtility
 
@@ -77,7 +78,7 @@ class BugBranchLinker:
         except InvalidBugStatus:
             return
         bug_set = getUtility(IBugSet)
-        for bug_id, status in bug_info.iteritems():
+        for bug_id, status in six.iteritems(bug_info):
             try:
                 bug = bug_set.get(bug_id)
             except NotFoundError:
diff --git a/lib/lp/codehosting/sshserver/session.py b/lib/lp/codehosting/sshserver/session.py
index 38bea68..a77855b 100644
--- a/lib/lp/codehosting/sshserver/session.py
+++ b/lib/lp/codehosting/sshserver/session.py
@@ -15,6 +15,7 @@ import sys
 
 from lazr.sshserver.events import AvatarEvent
 from lazr.sshserver.session import DoNothingSession
+import six
 from six import reraise
 from six.moves.urllib.parse import urlparse
 from twisted.internet import (
@@ -144,7 +145,7 @@ class ForkedProcessTransport(process.BaseProcess):
         assert executable == 'brz', executable  # Maybe .endswith()
         assert args[0] == 'brz', args[0]
         message = ['fork-env %s\n' % (' '.join(args[1:]),)]
-        for key, value in environment.iteritems():
+        for key, value in six.iteritems(environment):
             # XXX: Currently we only pass BRZ_EMAIL, should we be passing
             #      everything else? Note that many won't be handled properly,
             #      since the process is already running.
diff --git a/lib/lp/codehosting/vfs/branchfsclient.py b/lib/lp/codehosting/vfs/branchfsclient.py
index b96d780..3307830 100644
--- a/lib/lp/codehosting/vfs/branchfsclient.py
+++ b/lib/lp/codehosting/vfs/branchfsclient.py
@@ -14,6 +14,7 @@ __all__ = [
 
 import time
 
+import six
 from twisted.internet import defer
 
 from lp.code.interfaces.codehosting import BRANCH_TRANSPORT
@@ -84,7 +85,7 @@ class BranchFileSystemClient:
     def _getFromCache(self, path):
         """Get the cached 'transport_tuple' for 'path'."""
         split_path = path.strip('/').split('/')
-        for object_path, value in self._cache.iteritems():
+        for object_path, value in six.iteritems(self._cache):
             transport_type, data, inserted_time = value
             split_object_path = object_path.strip('/').split('/')
             # Do a segment-by-segment comparison. Python sucks, lists should
diff --git a/lib/lp/registry/model/distroseriesdifference.py b/lib/lp/registry/model/distroseriesdifference.py
index e0ee875..0672584 100644
--- a/lib/lp/registry/model/distroseriesdifference.py
+++ b/lib/lp/registry/model/distroseriesdifference.py
@@ -19,6 +19,7 @@ from debian.changelog import (
     Version,
     )
 from lazr.enum import DBItem
+import six
 from sqlobject import StringCol
 from storm.expr import (
     And,
@@ -279,10 +280,10 @@ def eager_load_dsds(dsds):
     # referred to.
     sprs = bulk.load_related(
         SourcePackageRelease, chain(
-            source_pubs.itervalues(),
-            parent_source_pubs.itervalues(),
-            source_pubs_for_release.itervalues(),
-            parent_source_pubs_for_release.itervalues()),
+            six.itervalues(source_pubs),
+            six.itervalues(parent_source_pubs),
+            six.itervalues(source_pubs_for_release),
+            six.itervalues(parent_source_pubs_for_release)),
         ("sourcepackagereleaseID",))
 
     # Get packagesets and parent_packagesets for each DSD.
diff --git a/lib/lp/registry/scripts/tests/test_populate_distroseriesdiff.py b/lib/lp/registry/scripts/tests/test_populate_distroseriesdiff.py
index a8acf8a..84f9f74 100644
--- a/lib/lp/registry/scripts/tests/test_populate_distroseriesdiff.py
+++ b/lib/lp/registry/scripts/tests/test_populate_distroseriesdiff.py
@@ -5,6 +5,7 @@
 
 __metaclass__ = type
 
+import six
 from storm.store import Store
 import transaction
 from zope.security.proxy import removeSecurityProxy
@@ -171,7 +172,8 @@ class TestFindLatestSourcePackageReleases(TestCaseWithFactory, FactoryHelper):
             for status in active_publishing_status)
         query = compose_sql_find_latest_source_package_releases(distroseries)
         self.assertContentEqual(
-            [self.getExpectedResultFor(spph) for spph in spphs.itervalues()],
+            [self.getExpectedResultFor(spph)
+             for spph in six.itervalues(spphs)],
             Store.of(distroseries).execute(query))
 
     def test_does_not_find_inactive_publication(self):
diff --git a/lib/lp/registry/services/sharingservice.py b/lib/lp/registry/services/sharingservice.py
index 68e33c3..09744ee 100644
--- a/lib/lp/registry/services/sharingservice.py
+++ b/lib/lp/registry/services/sharingservice.py
@@ -13,6 +13,7 @@ from operator import attrgetter
 
 from lazr.restful.interfaces import IWebBrowserOriginatingRequest
 from lazr.restful.utils import get_current_web_service_request
+import six
 from storm.expr import (
     And,
     Count,
@@ -608,7 +609,7 @@ class SharingService:
         for (grantee, permissions, shared_artifact_types) in grant_permissions:
             some_things_shared = len(shared_artifact_types) > 0
             grantee_permissions = {}
-            for (policy, permission) in permissions.iteritems():
+            for (policy, permission) in six.iteritems(permissions):
                 grantee_permissions[policy.type.name] = permission.name
             shared_artifact_type_names = [
                 info_type.name for info_type in shared_artifact_types]
diff --git a/lib/lp/scripts/utilities/warninghandler.py b/lib/lp/scripts/utilities/warninghandler.py
index 46b8a73..cb0c4ff 100644
--- a/lib/lp/scripts/utilities/warninghandler.py
+++ b/lib/lp/scripts/utilities/warninghandler.py
@@ -12,6 +12,8 @@ import StringIO
 import sys
 import warnings
 
+import six
+
 
 class WarningReport:
 
@@ -211,7 +213,7 @@ def report_other_warnings():
     if other_warnings:
         print(file=sys.stderr)
         print("General warnings.", file=sys.stderr)
-        for warninginfo in other_warnings.itervalues():
+        for warninginfo in six.itervalues(other_warnings):
             print(file=sys.stderr)
             print(warninginfo, file=sys.stderr)
 
diff --git a/lib/lp/services/command_spawner.py b/lib/lp/services/command_spawner.py
index dcca28f..539a3fb 100644
--- a/lib/lp/services/command_spawner.py
+++ b/lib/lp/services/command_spawner.py
@@ -151,7 +151,7 @@ class CommandSpawner:
         processes are cleaned up.  Until then, they will stay around as
         zombies.
         """
-        for process in self.running_processes.iterkeys():
+        for process in self.running_processes:
             process.terminate()
 
     def _spawn(self, command):
diff --git a/lib/lp/services/config/__init__.py b/lib/lp/services/config/__init__.py
index 3c6ad51..ebe9ebf 100644
--- a/lib/lp/services/config/__init__.py
+++ b/lib/lp/services/config/__init__.py
@@ -22,6 +22,7 @@ import sys
 
 from lazr.config import ImplicitTypeSchema
 from lazr.config.interfaces import ConfigErrors
+import six
 from six.moves.urllib.parse import (
     urlparse,
     urlunparse,
@@ -459,7 +460,7 @@ class DatabaseConfig:
 
         Overriding a value to None removes the override.
         """
-        for attr, value in kwargs.iteritems():
+        for attr, value in six.iteritems(kwargs):
             assert attr in self._db_config_attrs, (
                 "%s cannot be overridden" % attr)
             if value is None:
diff --git a/lib/lp/services/database/bulk.py b/lib/lp/services/database/bulk.py
index f73bbf8..840e402 100644
--- a/lib/lp/services/database/bulk.py
+++ b/lib/lp/services/database/bulk.py
@@ -25,6 +25,7 @@ from operator import (
     itemgetter,
     )
 
+import six
 from storm.databases.postgres import Returning
 from storm.expr import (
     And,
@@ -52,7 +53,7 @@ def collate(things, key):
     collection = defaultdict(list)
     for thing in things:
         collection[key(thing)].append(thing)
-    return collection.iteritems()
+    return six.iteritems(collection)
 
 
 def get_type(thing):
diff --git a/lib/lp/services/features/testing.py b/lib/lp/services/features/testing.py
index 998138d..3a2d69b 100644
--- a/lib/lp/services/features/testing.py
+++ b/lib/lp/services/features/testing.py
@@ -13,6 +13,7 @@ __all__ = [
 from fixtures import Fixture
 from lazr.restful.utils import get_current_browser_request
 import psycopg2
+import six
 
 from lp.services.features import (
     get_relevant_feature_controller,
@@ -91,7 +92,7 @@ class FeatureFixtureMixin:
                 scope='default',
                 priority=999,
                 value=unicode(value))
-            for flag_name, value in self.desired_features.iteritems()
+            for flag_name, value in six.iteritems(self.desired_features)
                 if value is not None]
 
         if self.full_feature_rules is not None:
diff --git a/lib/lp/services/librarianserver/testing/fake.py b/lib/lp/services/librarianserver/testing/fake.py
index 37d6bb6..e1e1f5e 100644
--- a/lib/lp/services/librarianserver/testing/fake.py
+++ b/lib/lp/services/librarianserver/testing/fake.py
@@ -19,6 +19,7 @@ import hashlib
 from StringIO import StringIO
 
 from fixtures import Fixture
+import six
 from six.moves.urllib.parse import urljoin
 import transaction
 from transaction.interfaces import ISynchronizer
@@ -138,7 +139,7 @@ class FakeLibrarian(Fixture):
         database transaction.
         """
         # Note that all files have been committed to storage.
-        for alias in self.aliases.itervalues():
+        for alias in six.itervalues(self.aliases):
             alias.file_committed = True
 
     def _makeAlias(self, file_id, name, content, content_type):
@@ -175,7 +176,7 @@ class FakeLibrarian(Fixture):
 
     def findBySHA256(self, sha256):
         "See `ILibraryFileAliasSet`."""
-        for alias in self.aliases.itervalues():
+        for alias in six.itervalues(self.aliases):
             if alias.content.sha256 == sha256:
                 return alias
 
diff --git a/lib/lp/services/mail/basemailer.py b/lib/lp/services/mail/basemailer.py
index a2f8a40..280ab78 100644
--- a/lib/lp/services/mail/basemailer.py
+++ b/lib/lp/services/mail/basemailer.py
@@ -12,6 +12,7 @@ import logging
 from smtplib import SMTPException
 import sys
 
+import six
 from zope.component import getUtility
 from zope.error.interfaces import IErrorReportingUtility
 from zope.security.management import getSecurityPolicy
@@ -79,7 +80,7 @@ class BaseMailer:
         self._subject_template = subject
         self._template_name = template_name
         self._recipients = NotificationRecipientSet()
-        for recipient, reason in recipients.iteritems():
+        for recipient, reason in six.iteritems(recipients):
             self._recipients.add(recipient, reason, reason.mail_header)
         self.from_address = from_address
         self.delta = delta
diff --git a/lib/lp/services/messaging/tests/test_rabbit.py b/lib/lp/services/messaging/tests/test_rabbit.py
index 6cd9f99..dfd48af 100644
--- a/lib/lp/services/messaging/tests/test_rabbit.py
+++ b/lib/lp/services/messaging/tests/test_rabbit.py
@@ -9,6 +9,7 @@ from functools import partial
 from itertools import count
 import socket
 
+import six
 from testtools.testcase import ExpectedException
 import transaction
 from transaction._transaction import Status as TransactionStatus
@@ -396,7 +397,7 @@ class TestRabbit(RabbitTestCase):
             return set()
         else:
             return set(
-                sync.session for sync in syncs_set.data.itervalues()
+                sync.session for sync in six.itervalues(syncs_set.data)
                 if isinstance(sync, RabbitSessionTransactionSync))
 
     def test_global_session(self):
diff --git a/lib/lp/services/osutils.py b/lib/lp/services/osutils.py
index b487165..7eafc40 100644
--- a/lib/lp/services/osutils.py
+++ b/lib/lp/services/osutils.py
@@ -28,6 +28,8 @@ from signal import (
     )
 import time
 
+import six
+
 
 def remove_tree(path):
     """Remove the tree at 'path' from disk."""
@@ -41,7 +43,7 @@ def set_environ(new_values):
     :return: a dict of the old values
     """
     old_values = {}
-    for name, value in new_values.iteritems():
+    for name, value in six.iteritems(new_values):
         old_values[name] = os.environ.get(name)
         if value is None:
             if old_values[name] is not None:
diff --git a/lib/lp/services/testing/customresult.py b/lib/lp/services/testing/customresult.py
index 2164f3d..5862f84 100644
--- a/lib/lp/services/testing/customresult.py
+++ b/lib/lp/services/testing/customresult.py
@@ -11,6 +11,7 @@ __all__ = [
 
 from unittest import TestSuite
 
+import six
 from zope.testrunner import find
 
 
@@ -65,7 +66,7 @@ def filter_tests(list_name, reorder_tests=False):
         test_lookup = {}
         # Multiple unique testcases can be represented by a single id and they
         # must be tracked separately.
-        for layer_name, suite in tests_by_layer_name.iteritems():
+        for layer_name, suite in six.iteritems(tests_by_layer_name):
             for testcase in suite:
                 layer_to_tests = test_lookup.setdefault(
                     testcase.id(), {})
diff --git a/lib/lp/services/utils.py b/lib/lp/services/utils.py
index beda118..ea661c5 100644
--- a/lib/lp/services/utils.py
+++ b/lib/lp/services/utils.py
@@ -52,6 +52,7 @@ from fixtures import (
     )
 from lazr.enum import BaseItem
 import pytz
+import six
 from six.moves import cPickle as pickle
 from twisted.python.util import mergeFunctionMetadata
 from zope.security.proxy import isinstance as zope_isinstance
@@ -388,7 +389,7 @@ def obfuscate_structure(o):
     elif isinstance(o, (dict)):
         return dict(
             (obfuscate_structure(key), obfuscate_structure(value))
-            for key, value in o.iteritems())
+            for key, value in six.iteritems(o))
     else:
         return o
 
diff --git a/lib/lp/services/webapp/escaping.py b/lib/lp/services/webapp/escaping.py
index 8c4d965..bce499e 100644
--- a/lib/lp/services/webapp/escaping.py
+++ b/lib/lp/services/webapp/escaping.py
@@ -9,6 +9,7 @@ __all__ = [
     ]
 
 from lazr.restful.utils import get_current_browser_request
+import six
 from zope.i18n import (
     Message,
     translate,
@@ -95,7 +96,7 @@ class structured:
             self.escapedtext = text % tuple(html_escape(rep) for rep in reps)
         elif kwreps:
             self.escapedtext = text % dict(
-                (k, html_escape(v)) for k, v in kwreps.iteritems())
+                (k, html_escape(v)) for k, v in six.iteritems(kwreps))
         else:
             self.escapedtext = text
 
diff --git a/lib/lp/services/webapp/login.py b/lib/lp/services/webapp/login.py
index 43eb478..fdbd079 100644
--- a/lib/lp/services/webapp/login.py
+++ b/lib/lp/services/webapp/login.py
@@ -290,7 +290,7 @@ class OpenIDCallbackView(OpenIDLogin):
 
     def _gather_params(self, request):
         params = dict(request.form)
-        for key, value in request.query_string_params.iteritems():
+        for key, value in six.iteritems(request.query_string_params):
             if len(value) > 1:
                 raise ValueError(
                     'Did not expect multi-valued fields.')
diff --git a/lib/lp/services/webapp/servers.py b/lib/lp/services/webapp/servers.py
index 8b85df8..1f77b76 100644
--- a/lib/lp/services/webapp/servers.py
+++ b/lib/lp/services/webapp/servers.py
@@ -529,7 +529,7 @@ def get_query_string_params(request):
     parsed_qs = parse_qs(query_string, keep_blank_values=True)
     # Use BrowserRequest._decode() for decoding the received parameters.
     decoded_qs = {}
-    for key, values in parsed_qs.iteritems():
+    for key, values in six.iteritems(parsed_qs):
         decoded_qs[key] = [
             request._decode(value) for value in values]
     return decoded_qs
diff --git a/lib/lp/services/webhooks/interfaces.py b/lib/lp/services/webhooks/interfaces.py
index d4d20e3..ad01625 100644
--- a/lib/lp/services/webhooks/interfaces.py
+++ b/lib/lp/services/webhooks/interfaces.py
@@ -40,6 +40,7 @@ from lazr.restful.fields import (
     Reference,
     )
 from lazr.restful.interface import copy_field
+import six
 from six.moves import http_client
 from zope.interface import (
     Attribute,
@@ -105,7 +106,7 @@ class AnyWebhookEventTypeVocabulary(SimpleVocabulary):
     def __init__(self, context):
         terms = [
             self.createTerm(key, key, value)
-            for key, value in WEBHOOK_EVENT_TYPES.iteritems()]
+            for key, value in six.iteritems(WEBHOOK_EVENT_TYPES)]
         super(AnyWebhookEventTypeVocabulary, self).__init__(terms)
 
 
diff --git a/lib/lp/soyuz/browser/tests/archive-views.txt b/lib/lp/soyuz/browser/tests/archive-views.txt
index 8f440d7..72c1392 100644
--- a/lib/lp/soyuz/browser/tests/archive-views.txt
+++ b/lib/lp/soyuz/browser/tests/archive-views.txt
@@ -105,8 +105,10 @@ usage details in a dictionary containing:
 We will use a helper function for printing the returned dictionary
 contents.
 
+    >>> import six
+
     >>> def print_repository_usage(repository_usage):
-    ...     for key, value in sorted(repository_usage.iteritems()):
+    ...     for key, value in sorted(six.iteritems(repository_usage)):
     ...         print('%s: %s' % (key, value))
 
 Celso PPA has some packages, but still below the quota.
diff --git a/lib/lp/soyuz/model/packagediff.py b/lib/lp/soyuz/model/packagediff.py
index 6933c31..12437cd 100644
--- a/lib/lp/soyuz/model/packagediff.py
+++ b/lib/lp/soyuz/model/packagediff.py
@@ -16,6 +16,7 @@ import shutil
 import subprocess
 import tempfile
 
+import six
 from sqlobject import ForeignKey
 from storm.expr import Desc
 from storm.store import EmptyResultSet
@@ -215,7 +216,7 @@ class PackageDiff(SQLBase):
                 zip(directions, (self.from_source, self.to_source)))
 
             # Iterate over the packages to be diff'ed.
-            for direction, package in packages.iteritems():
+            for direction, package in six.iteritems(packages):
                 # Create distinct directory locations for
                 # 'from' and 'to' files.
                 absolute_path = os.path.join(tmp_dir, direction)
diff --git a/lib/lp/soyuz/model/publishing.py b/lib/lp/soyuz/model/publishing.py
index fafd7f9..1c5d384 100644
--- a/lib/lp/soyuz/model/publishing.py
+++ b/lib/lp/soyuz/model/publishing.py
@@ -22,6 +22,7 @@ import os
 import sys
 
 import pytz
+import six
 from sqlobject import (
     ForeignKey,
     IntCol,
@@ -1012,7 +1013,7 @@ def expand_binary_requests(distroseries, binaries):
     arch_map = dict((arch.architecturetag, arch) for arch in archs)
 
     expanded = []
-    for bpr, overrides in binaries.iteritems():
+    for bpr, overrides in six.iteritems(binaries):
         if bpr.architecturespecific:
             # Find the DAS in this series corresponding to the original
             # build arch tag. If it does not exist or is disabled, we should
diff --git a/lib/lp/soyuz/scripts/custom_uploads_copier.py b/lib/lp/soyuz/scripts/custom_uploads_copier.py
index e6f7e83..21cc1f2 100644
--- a/lib/lp/soyuz/scripts/custom_uploads_copier.py
+++ b/lib/lp/soyuz/scripts/custom_uploads_copier.py
@@ -14,6 +14,8 @@ __all__ = [
 
 from operator import attrgetter
 
+import six
+
 from lp.archivepublisher.ddtp_tarball import DdtpTarballUpload
 from lp.archivepublisher.debian_installer import DebianInstallerUpload
 from lp.archivepublisher.dist_upgrader import DistUpgraderUpload
@@ -163,7 +165,7 @@ class CustomUploadsCopier:
             self.target_series, source_pocket=self.target_pocket)
         source_uploads = self.getLatestUploads(
             source_series, source_pocket=source_pocket)
-        for upload in source_uploads.itervalues():
+        for upload in six.itervalues(source_uploads):
             if (not self.isObsolete(upload, target_uploads) and
                 self.isForValidDAS(upload)):
                 self.copyUpload(upload)
diff --git a/lib/lp/soyuz/scripts/gina/handlers.py b/lib/lp/soyuz/scripts/gina/handlers.py
index 54539a4..597e5aa 100644
--- a/lib/lp/soyuz/scripts/gina/handlers.py
+++ b/lib/lp/soyuz/scripts/gina/handlers.py
@@ -24,6 +24,7 @@ from cStringIO import StringIO
 import os
 import re
 
+import six
 from sqlobject import (
     SQLObjectMoreThanOneResultError,
     SQLObjectNotFound,
@@ -504,7 +505,7 @@ class SourcePackageHandler:
         dsc_contents = parse_tagfile(dsc_path)
         dsc_contents = dict([
             (name.lower(), value) for
-            (name, value) in dsc_contents.iteritems()])
+            (name, value) in six.iteritems(dsc_contents)])
 
         # Since the dsc doesn't know, we add in the directory, package
         # component and section
diff --git a/lib/lp/soyuz/scripts/gina/runner.py b/lib/lp/soyuz/scripts/gina/runner.py
index d9edd1f..631323f 100644
--- a/lib/lp/soyuz/scripts/gina/runner.py
+++ b/lib/lp/soyuz/scripts/gina/runner.py
@@ -157,7 +157,7 @@ def import_sourcepackages(distro, packages_map, package_root,
     npacks = len(packages_map.src_map)
     log.info('%i Source Packages to be imported', npacks)
 
-    for package in sorted(packages_map.src_map.iterkeys()):
+    for package in sorted(packages_map.src_map):
         for source in packages_map.src_map[package]:
             attempt_source_package_import(
                 distro, source, package_root, importer_handler)
@@ -193,7 +193,7 @@ def import_binarypackages(distro, packages_map, package_root,
         log.info(
             '%i Binary Packages to be imported for %s', npacks, archtag)
         # Go over binarypackages importing them for this architecture
-        for package_name in sorted(packages_map.bin_map[archtag].iterkeys()):
+        for package_name in sorted(packages_map.bin_map[archtag]):
             binary = packages_map.bin_map[archtag][package_name]
             try:
                 try:
diff --git a/lib/lp/testing/__init__.py b/lib/lp/testing/__init__.py
index 20a50d9..81e86f1 100644
--- a/lib/lp/testing/__init__.py
+++ b/lib/lp/testing/__init__.py
@@ -96,6 +96,7 @@ import oops_datedir_repo.serializer_rfc822
 import pytz
 import scandir
 import simplejson
+import six
 from storm.store import Store
 import subunit
 import testtools
@@ -697,7 +698,7 @@ class TestCase(testtools.TestCase, fixtures.TestWithFixtures):
         The config values will be restored during test tearDown.
         """
         name = self.factory.getUniqueString()
-        body = '\n'.join("%s: %s" % (k, v) for k, v in kwargs.iteritems())
+        body = '\n'.join("%s: %s" % (k, v) for k, v in six.iteritems(kwargs))
         config.push(name, "\n[%s]\n%s\n" % (section, body))
         self.addCleanup(config.pop, name)
 
@@ -1527,13 +1528,13 @@ def monkey_patch(context, **kwargs):
     """
     old_values = {}
     not_set = object()
-    for name, value in kwargs.iteritems():
+    for name, value in six.iteritems(kwargs):
         old_values[name] = getattr(context, name, not_set)
         setattr(context, name, value)
     try:
         yield
     finally:
-        for name, value in old_values.iteritems():
+        for name, value in six.iteritems(old_values):
             if value is not_set:
                 delattr(context, name)
             else:
diff --git a/lib/lp/testing/swift/fakeswift.py b/lib/lp/testing/swift/fakeswift.py
index 0712e7a..c90dc0d 100644
--- a/lib/lp/testing/swift/fakeswift.py
+++ b/lib/lp/testing/swift/fakeswift.py
@@ -22,6 +22,7 @@ import sys
 import time
 import uuid
 
+import six
 from twisted.web import (
     http,
     resource,
@@ -77,7 +78,7 @@ class FakeKeystone(resource.Resource):
             if self._isValidToken(token, tenant_name):
                 return token
         else:
-            for id, token in self.tokens.iteritems():
+            for id, token in six.iteritems(self.tokens):
                 if self._isValidToken(token, tenant_name):
                     return token
 
diff --git a/lib/lp/translations/browser/translationlinksaggregator.py b/lib/lp/translations/browser/translationlinksaggregator.py
index 3f196bc..4b06509 100644
--- a/lib/lp/translations/browser/translationlinksaggregator.py
+++ b/lib/lp/translations/browser/translationlinksaggregator.py
@@ -7,6 +7,8 @@ __all__ = [
     'TranslationLinksAggregator',
     ]
 
+import six
+
 from lp.services.webapp import canonical_url
 from lp.translations.interfaces.pofile import IPOFile
 from lp.translations.model.productserieslanguage import ProductSeriesLanguage
@@ -177,10 +179,10 @@ class TranslationLinksAggregator:
             returns for the sensible chunks.
         """
         links = []
-        for target, sheets in self._bundle(sheets).iteritems():
+        for target, sheets in six.iteritems(self._bundle(sheets)):
             assert sheets, "Translation target has no POFiles or templates."
             links_and_sheets = self._circumscribe(sheets)
-            for link, covered_sheets in links_and_sheets.iteritems():
+            for link, covered_sheets in six.iteritems(links_and_sheets):
                 links.append(self.describe(target, link, covered_sheets))
 
         return links
diff --git a/lib/lp/translations/browser/translationmessage.py b/lib/lp/translations/browser/translationmessage.py
index ef75f6c..dbdf1d1 100644
--- a/lib/lp/translations/browser/translationmessage.py
+++ b/lib/lp/translations/browser/translationmessage.py
@@ -23,6 +23,7 @@ import operator
 import re
 
 import pytz
+import six
 from six.moves.urllib.parse import (
     parse_qsl,
     urlencode,
@@ -96,7 +97,7 @@ def revert_unselected_translations(translations, current_message,
         original_translations = dict(enumerate(current_message.translations))
 
     output = {}
-    for plural_form, translation in translations.iteritems():
+    for plural_form, translation in six.iteritems(translations):
         if plural_form in plural_indices_to_store:
             output[plural_form] = translation
         elif original_translations.get(plural_form) is None:
@@ -113,7 +114,7 @@ def contains_translations(translations):
     :param translations: a dict mapping plural forms to their respective
         translation strings.
     """
-    for text in translations.itervalues():
+    for text in six.itervalues(translations):
         if text is not None and len(text) != 0:
             return True
     return False
diff --git a/lib/lp/translations/doc/translationimportqueue.txt b/lib/lp/translations/doc/translationimportqueue.txt
index 9f7d8c9..c62423b 100644
--- a/lib/lp/translations/doc/translationimportqueue.txt
+++ b/lib/lp/translations/doc/translationimportqueue.txt
@@ -1223,9 +1223,11 @@ bug 138650 for an example).
 If such bad requests do end up on the import queue, the import queue code will
 raise errors about them.
 
+    >>> import six
+
     >>> def print_import_failures(import_script):
     ...     """List failures recorded in an import script instance."""
-    ...     for reason, entries in script.failures.iteritems():
+    ...     for reason, entries in six.iteritems(script.failures):
     ...         print(reason)
     ...         for entry in entries:
     ...             print("-> " + entry)
diff --git a/lib/lp/translations/model/potemplate.py b/lib/lp/translations/model/potemplate.py
index ba0a632..70dedd2 100644
--- a/lib/lp/translations/model/potemplate.py
+++ b/lib/lp/translations/model/potemplate.py
@@ -19,6 +19,7 @@ import operator
 import os
 
 from psycopg2.extensions import TransactionRollbackError
+import six
 from sqlobject import (
     BoolCol,
     ForeignKey,
@@ -1560,7 +1561,7 @@ class POTemplateSharingSubset(object):
                 equivalents[key] = []
             equivalents[key].append(template)
 
-        for equivalence_list in equivalents.itervalues():
+        for equivalence_list in six.itervalues(equivalents):
             # Sort potemplates from "most representative" to "least
             # representative."
             equivalence_list.sort(key=POTemplate.sharingKey, reverse=True)
diff --git a/lib/lp/translations/model/potmsgset.py b/lib/lp/translations/model/potmsgset.py
index 12f9f05..f256384 100644
--- a/lib/lp/translations/model/potmsgset.py
+++ b/lib/lp/translations/model/potmsgset.py
@@ -14,6 +14,7 @@ from collections import (
 import logging
 import re
 
+import six
 from sqlobject import (
     ForeignKey,
     SQLObjectNotFound,
@@ -122,7 +123,7 @@ def dictify_translations(translations):
     # Filter out None values.
     return dict(
         (form, translation)
-        for form, translation in translations.iteritems()
+        for form, translation in six.iteritems(translations)
         if translation is not None)
 
 
@@ -611,7 +612,7 @@ class POTMsgSet(SQLBase):
 
         forms = dict(
             ('msgstr%d' % form, potranslation)
-            for form, potranslation in potranslations.iteritems())
+            for form, potranslation in six.iteritems(potranslations))
 
         if from_import:
             origin = RosettaTranslationOrigin.SCM
@@ -748,7 +749,7 @@ class POTMsgSet(SQLBase):
 
         translation_args = dict(
             ('msgstr%d' % form, translation)
-            for form, translation in translations.iteritems())
+            for form, translation in six.iteritems(translations))
 
         return TranslationMessage(
             potmsgset=self,
diff --git a/lib/lp/translations/model/translationimportqueue.py b/lib/lp/translations/model/translationimportqueue.py
index 3ded490..483a4c3 100644
--- a/lib/lp/translations/model/translationimportqueue.py
+++ b/lib/lp/translations/model/translationimportqueue.py
@@ -19,6 +19,7 @@ from textwrap import dedent
 
 import posixpath
 import pytz
+import six
 from sqlobject import (
     BoolCol,
     ForeignKey,
@@ -1457,7 +1458,8 @@ class TranslationImportQueue:
         """
         now = datetime.datetime.now(pytz.UTC)
         deletion_clauses = []
-        for status, max_age in translation_import_queue_entry_age.iteritems():
+        for status, max_age in six.iteritems(
+                translation_import_queue_entry_age):
             cutoff = now - max_age
             deletion_clauses.append(And(
                 TranslationImportQueueEntry.status == status,
diff --git a/lib/lp/translations/model/translationsharingjob.py b/lib/lp/translations/model/translationsharingjob.py
index 8a60008..313e8db 100644
--- a/lib/lp/translations/model/translationsharingjob.py
+++ b/lib/lp/translations/model/translationsharingjob.py
@@ -170,7 +170,7 @@ class TranslationSharingJobDerived(
             for.
         :param event: The event itself.
         """
-        for event_type, job_classes in cls._event_types.iteritems():
+        for event_type, job_classes in six.iteritems(cls._event_types):
             if not event_type.providedBy(event):
                 continue
             for job_class in job_classes:
@@ -191,7 +191,7 @@ class TranslationSharingJobDerived(
             # Ignore changes to POTemplates that are neither renames,
             # nor moves to a different package/project.
             return
-        for event_type, job_classes in cls._event_types.iteritems():
+        for event_type, job_classes in six.iteritems(cls._event_types):
             if not event_type.providedBy(event):
                 continue
             for job_class in job_classes:
diff --git a/lib/lp/translations/scripts/po_import.py b/lib/lp/translations/scripts/po_import.py
index 3e97f8c..9c9779e 100644
--- a/lib/lp/translations/scripts/po_import.py
+++ b/lib/lp/translations/scripts/po_import.py
@@ -17,6 +17,7 @@ from datetime import (
 import sys
 
 import pytz
+import six
 from zope.component import getUtility
 
 from lp.app.interfaces.launchpad import ILaunchpadCelebrities
@@ -212,5 +213,5 @@ class TranslationsImport(LaunchpadCronScript):
 
     def _reportFailures(self):
         """Bulk-report deferred failures as oopses."""
-        for reason, entries in self.failures.iteritems():
+        for reason, entries in six.iteritems(self.failures):
             self._reportOops(reason, entries)
diff --git a/lib/lp/translations/scripts/tests/test_reupload_translations.py b/lib/lp/translations/scripts/tests/test_reupload_translations.py
index f2a449f..2ec0cee 100644
--- a/lib/lp/translations/scripts/tests/test_reupload_translations.py
+++ b/lib/lp/translations/scripts/tests/test_reupload_translations.py
@@ -11,6 +11,7 @@ import re
 from StringIO import StringIO
 import tarfile
 
+import six
 import transaction
 from zope.security.proxy import removeSecurityProxy
 
@@ -61,7 +62,7 @@ def upload_tarball(translation_files):
     """
     buf = StringIO()
     tarball = tarfile.open('', 'w:gz', buf)
-    for name, contents in translation_files.iteritems():
+    for name, contents in six.iteritems(translation_files):
         pseudofile = StringIO(contents)
         tarinfo = tarfile.TarInfo()
         tarinfo.name = name
@@ -93,7 +94,7 @@ def filter_paths(files_dict):
         applied to each file's path, and non-Ubuntu files left out.
     """
     filtered_dict = {}
-    for original_path, content in files_dict.iteritems():
+    for original_path, content in six.iteritems(files_dict):
         new_path = _filter_ubuntu_translation_file(original_path)
         if new_path:
             filtered_dict[new_path] = content
diff --git a/lib/lp/translations/scripts/tests/test_translations_to_branch.py b/lib/lp/translations/scripts/tests/test_translations_to_branch.py
index 248b2e0..0cd058a 100644
--- a/lib/lp/translations/scripts/tests/test_translations_to_branch.py
+++ b/lib/lp/translations/scripts/tests/test_translations_to_branch.py
@@ -9,6 +9,7 @@ from textwrap import dedent
 
 from breezy.errors import NotBranchError
 import pytz
+import six
 from testtools.matchers import MatchesRegex
 import transaction
 from zope.component import getUtility
@@ -116,8 +117,8 @@ class TestExportTranslationsToBranch(TestCaseWithFactory):
                 msgstr "Hallo Wereld"\n""",
         }
 
-        branch_filenames = set(branch_contents.iterkeys())
-        expected_filenames = set(expected_contents.iterkeys())
+        branch_filenames = set(branch_contents)
+        expected_filenames = set(expected_contents)
 
         unexpected_filenames = branch_filenames - expected_filenames
         self.assertEqual(set(), unexpected_filenames)
@@ -125,7 +126,7 @@ class TestExportTranslationsToBranch(TestCaseWithFactory):
         missing_filenames = expected_filenames - branch_filenames
         self.assertEqual(set(), missing_filenames)
 
-        for filename, expected in expected_contents.iteritems():
+        for filename, expected in six.iteritems(expected_contents):
             contents = branch_contents[filename].lstrip('\n')
             pattern = dedent(expected.lstrip('\n'))
             if not re.match(pattern, contents, re.MULTILINE):
diff --git a/lib/lp/translations/stories/webservice/xx-translationimportqueue.txt b/lib/lp/translations/stories/webservice/xx-translationimportqueue.txt
index ca3f633..4b668ed 100644
--- a/lib/lp/translations/stories/webservice/xx-translationimportqueue.txt
+++ b/lib/lp/translations/stories/webservice/xx-translationimportqueue.txt
@@ -29,7 +29,7 @@ to be cleaned up.
     ...         shown.  If omitted, all keys are shown.
     ...     """
     ...     print('Entry:')
-    ...     for key in sorted(a_dict.iterkeys()):
+    ...     for key in sorted(a_dict):
     ...         if shown_keys is None or key in shown_keys:
     ...             print('', key, a_dict[key])
 
diff --git a/lib/lp/translations/tests/test_potemplate.py b/lib/lp/translations/tests/test_potemplate.py
index 4ebb735..d08826e 100644
--- a/lib/lp/translations/tests/test_potemplate.py
+++ b/lib/lp/translations/tests/test_potemplate.py
@@ -5,6 +5,7 @@ __metaclass__ = type
 
 from operator import methodcaller
 
+import six
 from zope.component import getUtility
 from zope.security.proxy import removeSecurityProxy
 
@@ -193,8 +194,8 @@ class EquivalenceClassTestMixin:
         This ignores the ordering of templates in an equivalence class.
         A separate test looks at ordering.
         """
-        self.assertEqual(set(actual.iterkeys()), set(expected.iterkeys()))
-        for key, value in actual.iteritems():
+        self.assertEqual(set(actual), set(expected))
+        for key, value in six.iteritems(actual):
             self.assertEqual(set(value), set(expected[key]))
 
 
diff --git a/lib/lp/translations/tests/test_side.py b/lib/lp/translations/tests/test_side.py
index f5807d0..c7ab892 100644
--- a/lib/lp/translations/tests/test_side.py
+++ b/lib/lp/translations/tests/test_side.py
@@ -5,6 +5,7 @@
 
 __metaclass__ = type
 
+import six
 from zope.component import getUtility
 from zope.interface.verify import verifyObject
 
@@ -23,7 +24,7 @@ class TestTranslationSideTraitsSet(TestCaseWithFactory):
     def test_baseline(self):
         utility = getUtility(ITranslationSideTraitsSet)
         self.assertTrue(verifyObject(ITranslationSideTraitsSet, utility))
-        for traits in utility.getAllTraits().itervalues():
+        for traits in six.itervalues(utility.getAllTraits()):
             self.assertTrue(verifyObject(ITranslationSideTraits, traits))
 
     def test_other_sides(self):
@@ -64,7 +65,7 @@ class TestTranslationSideTraitsSet(TestCaseWithFactory):
             [TranslationSide.UPSTREAM, TranslationSide.UBUNTU],
             traits_dict.keys())
 
-        for side, traits in traits_dict.iteritems():
+        for side, traits in six.iteritems(traits_dict):
             self.assertEqual(side, traits.side)
             self.assertEqual(traits, utility.getTraits(side))
 
diff --git a/lib/lp/translations/utilities/gettext_po_parser.py b/lib/lp/translations/utilities/gettext_po_parser.py
index c087ef9..78de456 100644
--- a/lib/lp/translations/utilities/gettext_po_parser.py
+++ b/lib/lp/translations/utilities/gettext_po_parser.py
@@ -20,6 +20,7 @@ import logging
 import re
 
 import pytz
+import six
 from zope import datetime as zope_datetime
 from zope.interface import implementer
 
@@ -230,7 +231,7 @@ class POHeader:
 
     def _parseHeaderFields(self):
         """Return plural form values based on the parsed header."""
-        for key, value in self._header_dictionary.iteritems():
+        for key, value in six.iteritems(self._header_dictionary):
             if key == 'plural-forms':
                 parts = self._parseAssignments(value)
                 nplurals = parts.get('nplurals')
@@ -362,7 +363,7 @@ class POHeader:
                 raise AssertionError('key %s is not being handled!' % value)
 
         # Now, we copy any other header information in the original .po file.
-        for key, value in self._header_dictionary.iteritems():
+        for key, value in six.iteritems(self._header_dictionary):
             if key in self._handled_keys_mapping:
                 # It's already handled, skip it.
                 continue
diff --git a/lib/lp/translations/utilities/pluralforms.py b/lib/lp/translations/utilities/pluralforms.py
index 680ba14..8a82685 100644
--- a/lib/lp/translations/utilities/pluralforms.py
+++ b/lib/lp/translations/utilities/pluralforms.py
@@ -12,6 +12,8 @@ __all__ = [
 import gettext
 import re
 
+import six
+
 from lp.translations.interfaces.translations import TranslationConstants
 
 
@@ -47,7 +49,7 @@ def make_friendly_plural_forms(expression, expected_forms):
 
     return [
         {'form': form, 'examples': examples}
-        for (form, examples) in forms.iteritems()
+        for (form, examples) in six.iteritems(forms)
         ]
 
 
diff --git a/lib/lp/translations/utilities/tests/test_translation_importer.py b/lib/lp/translations/utilities/tests/test_translation_importer.py
index dcf203b..2d47d01 100644
--- a/lib/lp/translations/utilities/tests/test_translation_importer.py
+++ b/lib/lp/translations/utilities/tests/test_translation_importer.py
@@ -7,6 +7,7 @@ __metaclass__ = type
 
 from io import BytesIO
 
+import six
 import transaction
 
 from lp.services.log.logger import DevNullLogger
@@ -96,7 +97,7 @@ class TranslationImporterTestCase(TestCaseWithFactory):
         exactly the same priority."""
         for file_extension in TranslationImporter().supported_file_extensions:
             priorities = []
-            for format, importer in importers.iteritems():
+            for format, importer in six.iteritems(importers):
                 if file_extension in importer.file_extensions:
                     self.assertNotIn(importer.priority, priorities)
                     priorities.append(importer.priority)
diff --git a/lib/lp/translations/utilities/translation_import.py b/lib/lp/translations/utilities/translation_import.py
index 197153d..f9af932 100644
--- a/lib/lp/translations/utilities/translation_import.py
+++ b/lib/lp/translations/utilities/translation_import.py
@@ -14,6 +14,7 @@ from operator import attrgetter
 
 import posixpath
 import pytz
+import six
 from storm.exceptions import TimeoutError
 import transaction
 from zope.component import getUtility
@@ -274,7 +275,7 @@ class TranslationImporter:
         """See `ITranslationImporter`."""
         file_extensions = []
 
-        for importer in importers.itervalues():
+        for importer in six.itervalues(importers):
             file_extensions.extend(importer.file_extensions)
 
         return sorted(set(file_extensions))
@@ -290,7 +291,7 @@ class TranslationImporter:
 
     def isTemplateName(self, path):
         """See `ITranslationImporter`."""
-        for importer in importers.itervalues():
+        for importer in six.itervalues(importers):
             if path.endswith(importer.template_suffix):
                 return True
         return False
diff --git a/lib/lp/translations/utilities/translationmerger.py b/lib/lp/translations/utilities/translationmerger.py
index 044d058..b507fb6 100644
--- a/lib/lp/translations/utilities/translationmerger.py
+++ b/lib/lp/translations/utilities/translationmerger.py
@@ -283,7 +283,7 @@ class MessageSharingMerge(LaunchpadScript):
         log.info("Merging %d template equivalence classes." % class_count)
 
         tm = TransactionManager(self.txn, self.options.dry_run)
-        for number, name in enumerate(sorted(equivalence_classes.iterkeys())):
+        for number, name in enumerate(sorted(equivalence_classes)):
             templates = equivalence_classes[name]
             log.info(
                 "Merging equivalence class '%s': %d template(s) (%d / %d)" % (
@@ -429,7 +429,7 @@ class TranslationMerger:
 
         self.tm.endTransaction(intermediate=True)
 
-        for representative_id in representatives.itervalues():
+        for representative_id in six.itervalues(representatives):
             representative = POTMsgSet.get(representative_id)
             self._scrubPOTMsgSetTranslations(representative)
             self.tm.endTransaction(intermediate=True)
@@ -484,7 +484,7 @@ class TranslationMerger:
         num_representatives = len(subordinates)
         representative_num = 0
 
-        for representative, potmsgsets in subordinates.iteritems():
+        for representative, potmsgsets in six.iteritems(subordinates):
             representative_num += 1
             log.debug("Message %d/%d: %d subordinate(s)." % (
                 representative_num, num_representatives, len(potmsgsets)))
diff --git a/lib/lp/translations/utilities/validate.py b/lib/lp/translations/utilities/validate.py
index 0b9e8df..371a060 100644
--- a/lib/lp/translations/utilities/validate.py
+++ b/lib/lp/translations/utilities/validate.py
@@ -9,6 +9,7 @@ __all__ = [
     ]
 
 import gettextpo
+import six
 
 
 class GettextValidationError(ValueError):
@@ -37,7 +38,7 @@ def validate_translation(original_singular, original_plural,
     else:
         # Message with plural forms.
         msg.set_msgid_plural(original_plural)
-        for form, translation in translations.iteritems():
+        for form, translation in six.iteritems(translations):
             msg.set_msgstr_plural(form, translation)
 
     for flag in flags:
diff --git a/utilities/findimports.py b/utilities/findimports.py
index b5615a8..09e0cf1 100755
--- a/utilities/findimports.py
+++ b/utilities/findimports.py
@@ -46,6 +46,8 @@ import linecache
 import os
 import sys
 
+import six
+
 
 class ImportFinder(ASTVisitor):
     """AST visitor that collects all imported names in its imports attribute.
@@ -285,7 +287,7 @@ class ModuleGraph(object):
     def printUnusedImports(self):
         for module in self.listModules():
             names = [(unused.lineno, unused.name)
-                     for unused in module.unused_names.itervalues()]
+                     for unused in six.itervalues(module.unused_names)]
             names.sort()
             for lineno, name in names:
                 if not self.all_unused:
diff --git a/utilities/generate-external-bug-status-docs b/utilities/generate-external-bug-status-docs
index a2d28e8..43e0263 100755
--- a/utilities/generate-external-bug-status-docs
+++ b/utilities/generate-external-bug-status-docs
@@ -26,6 +26,8 @@ from itertools import chain
 from optparse import OptionParser
 import sys
 
+import six
+
 from lp.bugs.externalbugtracker import BUG_TRACKER_CLASSES
 
 
@@ -85,7 +87,7 @@ def generate_table(typ, cls):
 
 def generate_documentable_classes():
     """Yield each class that has a mapping table defined."""
-    for typ, cls in BUG_TRACKER_CLASSES.iteritems():
+    for typ, cls in six.iteritems(BUG_TRACKER_CLASSES):
         if getattr(cls, '_status_lookup', None) is not None:
             yield typ, cls
 
diff --git a/utilities/list-pages b/utilities/list-pages
index 9989cf6..bf08d0a 100755
--- a/utilities/list-pages
+++ b/utilities/list-pages
@@ -49,6 +49,7 @@ import _pythonpath
 from inspect import getmro
 import os
 
+import six
 from zope.app.wsgi.testlayer import BrowserLayer
 from zope.browserpage.simpleviewclass import simple
 from zope.component import adapter, getGlobalSiteManager
@@ -191,7 +192,7 @@ class Whatever(object):
 
     def __call__(self, *args, **kwargs):
         args = map(repr, args)
-        args.extend('%s=%r' % (k, v) for k, v in kwargs.iteritems())
+        args.extend('%s=%r' % (k, v) for k, v in six.iteritems(kwargs))
         # If we're being called with no args, assume this is part of crazy
         # TALES stuff:
         #   webapp/metazcml.py(365)path()