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

Proposed by Colin Watson
Status: Merged
Approved by: Colin Watson
Approved revision: 41b7c40b4fb1d1e98de3754b3cd7f38c34eaf8c2
Merge reported by: Otto Co-Pilot
Merged at revision: not available
Proposed branch: ~cjwatson/launchpad:six-dict-iter
Merge into: launchpad:master
Diff against target: 2251 lines (+249/-155)
90 files modified
brzplugins/lpserve/__init__.py (+3/-2)
brzplugins/lpserve/test_lpserve.py (+4/-3)
database/schema/security.py (+10/-8)
lib/contrib/oauth.py (+7/-4)
lib/lp/app/browser/launchpadform.py (+2/-1)
lib/lp/app/browser/tales.py (+3/-2)
lib/lp/archivepublisher/domination.py (+3/-2)
lib/lp/archivepublisher/model/ftparchive.py (+4/-3)
lib/lp/archivepublisher/publishing.py (+2/-1)
lib/lp/archivepublisher/scripts/publish_ftpmaster.py (+12/-11)
lib/lp/archivepublisher/tests/test_dominator.py (+3/-2)
lib/lp/archivepublisher/tests/test_publish_ftpmaster.py (+3/-4)
lib/lp/archiveuploader/dscfile.py (+1/-1)
lib/lp/archiveuploader/nascentuploadfile.py (+3/-2)
lib/lp/archiveuploader/utils.py (+3/-1)
lib/lp/bugs/browser/bugalsoaffects.py (+3/-2)
lib/lp/bugs/browser/bugtarget.py (+2/-1)
lib/lp/bugs/interfaces/bugtaskfilter.py (+3/-1)
lib/lp/bugs/model/bugtask.py (+2/-1)
lib/lp/bugs/model/structuralsubscription.py (+3/-2)
lib/lp/bugs/model/tests/test_bugsubscriptioninfo.py (+3/-2)
lib/lp/bugs/scripts/bugsummaryrebuild.py (+7/-5)
lib/lp/bugs/scripts/bugtasktargetnamecaches.py (+2/-1)
lib/lp/bugs/scripts/bzremotecomponentfinder.py (+2/-1)
lib/lp/bugs/tests/externalbugtracker.py (+2/-1)
lib/lp/buildmaster/browser/builder.py (+2/-1)
lib/lp/buildmaster/manager.py (+2/-1)
lib/lp/buildmaster/model/builder.py (+2/-1)
lib/lp/buildmaster/queuedepth.py (+2/-1)
lib/lp/buildmaster/tests/test_queuedepth.py (+2/-1)
lib/lp/code/browser/branch.py (+3/-2)
lib/lp/code/browser/branchlisting.py (+3/-2)
lib/lp/code/browser/branchmergeproposal.py (+2/-1)
lib/lp/code/browser/gitrepository.py (+3/-3)
lib/lp/code/model/branchcollection.py (+2/-1)
lib/lp/code/model/branchjob.py (+1/-1)
lib/lp/code/model/codereviewinlinecomment.py (+2/-1)
lib/lp/code/model/revision.py (+3/-2)
lib/lp/codehosting/inmemory.py (+1/-1)
lib/lp/codehosting/scanner/buglinks.py (+2/-1)
lib/lp/codehosting/sshserver/session.py (+2/-1)
lib/lp/codehosting/vfs/branchfsclient.py (+2/-1)
lib/lp/registry/model/distroseriesdifference.py (+5/-4)
lib/lp/registry/scripts/tests/test_populate_distroseriesdiff.py (+3/-1)
lib/lp/registry/services/sharingservice.py (+2/-1)
lib/lp/scripts/utilities/warninghandler.py (+3/-1)
lib/lp/services/command_spawner.py (+1/-1)
lib/lp/services/config/__init__.py (+2/-1)
lib/lp/services/database/bulk.py (+2/-1)
lib/lp/services/features/testing.py (+2/-1)
lib/lp/services/librarianserver/testing/fake.py (+3/-2)
lib/lp/services/mail/basemailer.py (+2/-1)
lib/lp/services/messaging/tests/test_rabbit.py (+2/-1)
lib/lp/services/osutils.py (+3/-1)
lib/lp/services/testing/customresult.py (+2/-1)
lib/lp/services/utils.py (+2/-1)
lib/lp/services/webapp/escaping.py (+2/-1)
lib/lp/services/webapp/login.py (+1/-1)
lib/lp/services/webapp/servers.py (+1/-1)
lib/lp/services/webhooks/interfaces.py (+2/-1)
lib/lp/soyuz/browser/tests/archive-views.txt (+3/-1)
lib/lp/soyuz/model/packagediff.py (+2/-1)
lib/lp/soyuz/model/publishing.py (+2/-1)
lib/lp/soyuz/scripts/custom_uploads_copier.py (+3/-1)
lib/lp/soyuz/scripts/gina/handlers.py (+2/-1)
lib/lp/soyuz/scripts/gina/runner.py (+2/-2)
lib/lp/testing/__init__.py (+4/-3)
lib/lp/testing/swift/fakeswift.py (+2/-1)
lib/lp/translations/browser/translationlinksaggregator.py (+4/-2)
lib/lp/translations/browser/translationmessage.py (+3/-2)
lib/lp/translations/doc/translationimportqueue.txt (+3/-1)
lib/lp/translations/model/potemplate.py (+2/-1)
lib/lp/translations/model/potmsgset.py (+4/-3)
lib/lp/translations/model/translationimportqueue.py (+3/-1)
lib/lp/translations/model/translationsharingjob.py (+2/-2)
lib/lp/translations/scripts/po_import.py (+2/-1)
lib/lp/translations/scripts/tests/test_reupload_translations.py (+3/-2)
lib/lp/translations/scripts/tests/test_translations_to_branch.py (+4/-3)
lib/lp/translations/stories/webservice/xx-translationimportqueue.txt (+1/-1)
lib/lp/translations/tests/test_potemplate.py (+3/-2)
lib/lp/translations/tests/test_side.py (+3/-2)
lib/lp/translations/utilities/gettext_po_parser.py (+3/-2)
lib/lp/translations/utilities/pluralforms.py (+3/-1)
lib/lp/translations/utilities/tests/test_translation_importer.py (+2/-1)
lib/lp/translations/utilities/translation_import.py (+3/-2)
lib/lp/translations/utilities/translationmerger.py (+3/-3)
lib/lp/translations/utilities/validate.py (+2/-1)
utilities/findimports.py (+3/-1)
utilities/generate-external-bug-status-docs (+3/-1)
utilities/list-pages (+2/-1)
Reviewer Review Type Date Requested Status
Thiago F. Pappacena (community) Approve
Review via email: mp+383369@code.launchpad.net

Commit message

Use six for dict iteration

Description of the change

`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)`.

To post a comment you must log in.
Revision history for this message
Thiago F. Pappacena (pappacena) wrote :

I totally agree with iterating over the dict instead of doing dict.iterkeys(), but is it really necessary to use `six.iter{keys,values,items}`?

I mean, under the hood six's methods are basically `return iter(dict.values())` (for values, keys and items), and `keys.values()` works fine both on python2 and python3.

The only downside of using directly `dict.values` on Python2 that I can spot is the overhead of creating a copy list of all values, but that's exactly what six does anyway.

review: Needs Information
Revision history for this message
Colin Watson (cjwatson) wrote :

Well, but that last isn't true; on Python 2, six.itervalues(dict) is implemented using dict.itervalues(), not using iter(d.values()) as it is on Python 3 (where .values() doesn't materialise the whole list).

Mainly I wanted to use a reasonably obvious mechanical transformation that didn't require me to reason about whether there might be a real performance implication in any of the >130 cases here. A secondary benefit was that it means that we don't lose information as part of this commit: we can still look at the existing calls to dict.values and consider whether those might need to take a copy (I've certainly run across some that do, for example because the loop body deletes items from the dict). Doing that across the whole tree is a large chunk of work that I haven't tackled yet.

So, it's true it's not strictly necessary to use six.iter*, but I did think that it was worthwhile and better than reverting to bare .values() etc. in a systematic way.

Revision history for this message
Thiago F. Pappacena (pappacena) wrote :

Fair enough. In that case, LGTM.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/brzplugins/lpserve/__init__.py b/brzplugins/lpserve/__init__.py
2index b956568..3ce2081 100644
3--- a/brzplugins/lpserve/__init__.py
4+++ b/brzplugins/lpserve/__init__.py
5@@ -48,6 +48,7 @@ from breezy.transport import (
6 get_transport,
7 transport_server_registry,
8 )
9+import six
10
11
12 class cmd_launchpad_server(Command):
13@@ -591,7 +592,7 @@ class LPForkingService(object):
14 pid = os.getpid()
15 trace.mutter('%d spawned' % (pid,))
16 self._server_socket.close()
17- for env_var, value in env.iteritems():
18+ for env_var, value in six.iteritems(env):
19 osutils.set_or_unset_env(env_var, value)
20 # See [Decision #3]
21 self._create_child_file_descriptors(temp_name)
22@@ -769,7 +770,7 @@ class LPForkingService(object):
23 # We sent the SIGKILL signal, see if they exited
24 self._wait_for_children(self.SLEEP_FOR_CHILDREN_TIMEOUT)
25 if self._child_processes:
26- for c_id, (c_path, sock) in self._child_processes.iteritems():
27+ for c_id, (c_path, sock) in six.iteritems(self._child_processes):
28 # TODO: We should probably put something into this message?
29 # However, the likelyhood is very small that this isn't
30 # already closed because of SIGKILL + _wait_for_children
31diff --git a/brzplugins/lpserve/test_lpserve.py b/brzplugins/lpserve/test_lpserve.py
32index 3801df4..1420ed7 100644
33--- a/brzplugins/lpserve/test_lpserve.py
34+++ b/brzplugins/lpserve/test_lpserve.py
35@@ -18,6 +18,7 @@ from breezy import (
36 trace,
37 )
38 from breezy.plugins import lpserve
39+import six
40 from testtools import content
41
42 from lp.codehosting import (
43@@ -336,11 +337,11 @@ class TestCaseWithSubprocess(tests.TestCaseWithTransport):
44 old_env = {}
45
46 def cleanup_environment():
47- for env_var, value in env_changes.iteritems():
48+ for env_var, value in six.iteritems(env_changes):
49 old_env[env_var] = osutils.set_or_unset_env(env_var, value)
50
51 def restore_environment():
52- for env_var, value in old_env.iteritems():
53+ for env_var, value in six.iteritems(old_env):
54 osutils.set_or_unset_env(env_var, value)
55
56 cwd = None
57@@ -406,7 +407,7 @@ class TestCaseWithLPForkingServiceSubprocess(TestCaseWithSubprocess):
58 def send_fork_request(self, command, env=None):
59 if env is not None:
60 request_lines = ['fork-env %s\n' % (command,)]
61- for key, value in env.iteritems():
62+ for key, value in six.iteritems(env):
63 request_lines.append('%s: %s\n' % (key, value))
64 request_lines.append('end\n')
65 request = ''.join(request_lines)
66diff --git a/database/schema/security.py b/database/schema/security.py
67index 1c53659..274bb03 100755
68--- a/database/schema/security.py
69+++ b/database/schema/security.py
70@@ -13,6 +13,8 @@ import os
71 import re
72 import sys
73
74+import six
75+
76 from fti import quote_identifier
77 from lp.services.compat import SafeConfigParser
78 from lp.services.database.sqlbase import connect
79@@ -302,8 +304,8 @@ class PermissionGatherer:
80 to grant or revoke for. Each is a string.
81 """
82 result = []
83- for permission, parties in self.permissions.iteritems():
84- for principal, entities in parties.iteritems():
85+ for permission, parties in six.iteritems(self.permissions):
86+ for principal, entities in six.iteritems(parties):
87 result.append(
88 (permission, ", ".join(entities), principal))
89 return result
90@@ -315,8 +317,8 @@ class PermissionGatherer:
91 def countEntities(self):
92 """Count the number of different entities."""
93 entities = set()
94- for entities_and_entities in self.permissions.itervalues():
95- for extra_entities in entities_and_entities.itervalues():
96+ for entities_and_entities in six.itervalues(self.permissions):
97+ for extra_entities in six.itervalues(entities_and_entities):
98 entities.update(extra_entities)
99 return len(entities)
100
101@@ -324,7 +326,7 @@ class PermissionGatherer:
102 """Count the number of different principals."""
103 return len(set(sum([
104 principals.keys()
105- for principals in self.permissions.itervalues()], [])))
106+ for principals in six.itervalues(self.permissions)], [])))
107
108 def grant(self, cur):
109 """Grant all gathered permissions.
110@@ -480,7 +482,7 @@ def reset_permissions(con, config, options):
111
112 log.debug('Updating group memberships')
113 existing_memberships = list_role_members(cur, memberships.keys())
114- for group, users in memberships.iteritems():
115+ for group, users in six.iteritems(memberships):
116 cur_users = managed_roles.intersection(existing_memberships[group])
117 to_grant = users - cur_users
118 if to_grant:
119@@ -517,7 +519,7 @@ def reset_permissions(con, config, options):
120 # Set permissions as per config file
121 desired_permissions = defaultdict(lambda: defaultdict(set))
122
123- valid_objs = set(schema.iterkeys())
124+ valid_objs = set(schema)
125
126 # Any object with permissions granted is accessible to the 'read'
127 # role. Some (eg. the lp_* replicated tables and internal or trigger
128@@ -618,7 +620,7 @@ def reset_permissions(con, config, options):
129 new = desired_permissions[obj][role]
130 old_privs = obj.acl.get(role, {})
131 old = set(old_privs)
132- if any(old_privs.itervalues()):
133+ if any(six.itervalues(old_privs)):
134 log.warning("%s has grant option on %s", role, obj.fullname)
135 if new == old:
136 continue
137diff --git a/lib/contrib/oauth.py b/lib/contrib/oauth.py
138index 47a821d..2d11e6c 100644
139--- a/lib/contrib/oauth.py
140+++ b/lib/contrib/oauth.py
141@@ -3,6 +3,7 @@ import hmac
142 import random
143 import time
144
145+import six
146 from six.moves.urllib.parse import (
147 parse_qs,
148 quote,
149@@ -118,7 +119,7 @@ class OAuthRequest(object):
150 # get any non-oauth parameters
151 def get_nonoauth_parameters(self):
152 parameters = {}
153- for k, v in self.parameters.iteritems():
154+ for k, v in six.iteritems(self.parameters):
155 # ignore oauth parameters
156 if k.find('oauth_') < 0:
157 parameters[k] = v
158@@ -129,13 +130,15 @@ class OAuthRequest(object):
159 auth_header = 'OAuth realm="%s"' % realm
160 # add the oauth parameters
161 if self.parameters:
162- for k, v in self.parameters.iteritems():
163+ for k, v in six.iteritems(self.parameters):
164 auth_header += ', %s="%s"' % (k, v)
165 return {'Authorization': auth_header}
166
167 # serialize as post data for a POST request
168 def to_postdata(self):
169- return '&'.join('%s=%s' % (escape(str(k)), escape(str(v))) for k, v in self.parameters.iteritems())
170+ return '&'.join(
171+ '%s=%s' % (escape(str(k)), escape(str(v)))
172+ for k, v in six.iteritems(self.parameters))
173
174 # serialize as a url for a GET request
175 def to_url(self):
176@@ -269,7 +272,7 @@ class OAuthRequest(object):
177 @staticmethod
178 def _split_url_string(param_str):
179 parameters = parse_qs(param_str, keep_blank_values=False)
180- for k, v in parameters.iteritems():
181+ for k, v in six.iteritems(parameters):
182 parameters[k] = unquote(v[0])
183 return parameters
184
185diff --git a/lib/lp/app/browser/launchpadform.py b/lib/lp/app/browser/launchpadform.py
186index ac6dd15..8899cbd 100644
187--- a/lib/lp/app/browser/launchpadform.py
188+++ b/lib/lp/app/browser/launchpadform.py
189@@ -19,6 +19,7 @@ __all__ = [
190 from lazr.lifecycle.event import ObjectModifiedEvent
191 from lazr.lifecycle.snapshot import Snapshot
192 import simplejson
193+import six
194 import transaction
195 from zope.event import notify
196 from zope.formlib import form
197@@ -213,7 +214,7 @@ class LaunchpadFormView(LaunchpadView):
198 self.form_fields, self.prefix, context, self.request,
199 data=self.initial_values, adapters=self.adapters,
200 ignore_request=False)
201- for field_name, help_link in self.help_links.iteritems():
202+ for field_name, help_link in six.iteritems(self.help_links):
203 self.widgets[field_name].help_link = help_link
204
205 @property
206diff --git a/lib/lp/app/browser/tales.py b/lib/lp/app/browser/tales.py
207index df20fcb..8e4921e 100644
208--- a/lib/lp/app/browser/tales.py
209+++ b/lib/lp/app/browser/tales.py
210@@ -23,6 +23,7 @@ from lazr.enum import enumerated_type_registry
211 from lazr.restful.utils import get_current_browser_request
212 from lazr.uri import URI
213 import pytz
214+import six
215 from six.moves.urllib.parse import quote
216 from zope.browserpage import ViewPageTemplateFile
217 from zope.component import (
218@@ -1418,7 +1419,7 @@ class CustomizableFormatter(ObjectFormatterAPI):
219 """
220 values = dict(
221 (k, v if v is not None else '')
222- for k, v in self._link_summary_values().iteritems())
223+ for k, v in six.iteritems(self._link_summary_values()))
224 return structured(self._link_summary_template, **values).escapedtext
225
226 def _title_values(self):
227@@ -1440,7 +1441,7 @@ class CustomizableFormatter(ObjectFormatterAPI):
228 return None
229 values = dict(
230 (k, v if v is not None else '')
231- for k, v in self._title_values().iteritems())
232+ for k, v in six.iteritems(self._title_values()))
233 return structured(title_template, **values).escapedtext
234
235 def sprite_css(self):
236diff --git a/lib/lp/archivepublisher/domination.py b/lib/lp/archivepublisher/domination.py
237index a02fd78..356a2ee 100644
238--- a/lib/lp/archivepublisher/domination.py
239+++ b/lib/lp/archivepublisher/domination.py
240@@ -60,6 +60,7 @@ from operator import (
241 )
242
243 import apt_pkg
244+import six
245 from six.moves import (
246 filter as ifilter,
247 filterfalse as ifilterfalse,
248@@ -669,7 +670,7 @@ class Dominator:
249 bins = self.findBinariesForDomination(distroarchseries, pocket)
250 sorted_packages = self._sortPackages(bins, generalization)
251 self.logger.info("Planning domination of binaries...")
252- for name, pubs in sorted_packages.iteritems():
253+ for name, pubs in six.iteritems(sorted_packages):
254 self.logger.debug("Planning domination of %s" % name)
255 assert len(pubs) > 0, "Dominating zero binaries!"
256 live_versions = find_live_binary_versions_pass_1(pubs)
257@@ -774,7 +775,7 @@ class Dominator:
258 delete = []
259
260 self.logger.debug("Dominating sources...")
261- for name, pubs in sorted_packages.iteritems():
262+ for name, pubs in six.iteritems(sorted_packages):
263 self.logger.debug("Dominating %s" % name)
264 assert len(pubs) > 0, "Dominating zero sources!"
265 live_versions = find_live_source_versions(pubs)
266diff --git a/lib/lp/archivepublisher/model/ftparchive.py b/lib/lp/archivepublisher/model/ftparchive.py
267index 39e7d40..6fe3c3c 100644
268--- a/lib/lp/archivepublisher/model/ftparchive.py
269+++ b/lib/lp/archivepublisher/model/ftparchive.py
270@@ -8,6 +8,7 @@ from StringIO import StringIO
271 import time
272
273 import scandir
274+import six
275 from storm.expr import (
276 Desc,
277 Join,
278@@ -201,7 +202,7 @@ class FTPArchiveHandler:
279 stderr_handler.finalize()
280 failures = sorted([
281 (tag, receiver.returncode)
282- for tag, receiver in returncodes.iteritems()
283+ for tag, receiver in six.iteritems(returncodes)
284 if receiver.returncode != 0])
285 if len(failures) > 0:
286 by_arch = ["%s (returned %d)" % failure for failure in failures]
287@@ -694,8 +695,8 @@ class FTPArchiveHandler:
288
289 self.log.debug("Writing file lists for %s" % suite)
290 series, pocket = self.distro.getDistroSeriesAndPocket(suite)
291- for component, architectures in filelist.iteritems():
292- for architecture, file_names in architectures.iteritems():
293+ for component, architectures in six.iteritems(filelist):
294+ for architecture, file_names in six.iteritems(architectures):
295 # XXX wgrant 2010-10-06: There must be a better place to do
296 # this.
297 if architecture == "source":
298diff --git a/lib/lp/archivepublisher/publishing.py b/lib/lp/archivepublisher/publishing.py
299index cd19ed8..f6246b0 100644
300--- a/lib/lp/archivepublisher/publishing.py
301+++ b/lib/lp/archivepublisher/publishing.py
302@@ -36,6 +36,7 @@ from debian.deb822 import (
303 Release,
304 )
305 import scandir
306+import six
307 from storm.expr import Desc
308 from zope.component import getUtility
309 from zope.interface import (
310@@ -984,7 +985,7 @@ class Publisher(object):
311 translation_stanza.makeOutput().encode('utf-8')
312 + '\n\n')
313
314- for index in indices.itervalues():
315+ for index in six.itervalues(indices):
316 index.close()
317
318 if separate_long_descriptions:
319diff --git a/lib/lp/archivepublisher/scripts/publish_ftpmaster.py b/lib/lp/archivepublisher/scripts/publish_ftpmaster.py
320index 5fcd7d3..c307846 100644
321--- a/lib/lp/archivepublisher/scripts/publish_ftpmaster.py
322+++ b/lib/lp/archivepublisher/scripts/publish_ftpmaster.py
323@@ -15,6 +15,7 @@ import shutil
324
325 from pytz import utc
326 import scandir
327+import six
328 from zope.component import getUtility
329
330 from lp.archivepublisher.config import getPubConfig
331@@ -224,9 +225,7 @@ class PublishFTPMaster(LaunchpadCronScript):
332 return []
333
334 # May need indexes for this series.
335- suites = [
336- distroseries.getSuite(pocket)
337- for pocket in pocketsuffix.iterkeys()]
338+ suites = [distroseries.getSuite(pocket) for pocket in pocketsuffix]
339 return [
340 suite for suite in suites
341 if not file_exists(self.locateIndexesMarker(distro, suite))]
342@@ -289,7 +288,8 @@ class PublishFTPMaster(LaunchpadCronScript):
343
344 :param archive_purpose: The (purpose of the) archive to copy.
345 """
346- for purpose, archive_config in self.configs[distribution].iteritems():
347+ for purpose, archive_config in (
348+ six.iteritems(self.configs[distribution])):
349 dists = get_dists(archive_config)
350 backup_dists = get_backup_dists(archive_config)
351 execute_subprocess(
352@@ -319,14 +319,15 @@ class PublishFTPMaster(LaunchpadCronScript):
353 run died while in this state, restore the directory to its
354 permanent location.
355 """
356- for distro_configs in self.configs.itervalues():
357- for archive_config in distro_configs.itervalues():
358+ for distro_configs in six.itervalues(self.configs):
359+ for archive_config in six.itervalues(distro_configs):
360 self.recoverArchiveWorkingDir(archive_config)
361
362 def setUpDirs(self):
363 """Create archive roots and such if they did not yet exist."""
364- for distro_configs in self.configs.itervalues():
365- for archive_purpose, archive_config in distro_configs.iteritems():
366+ for distro_configs in six.itervalues(self.configs):
367+ for archive_purpose, archive_config in (
368+ six.iteritems(distro_configs)):
369 archiveroot = archive_config.archiveroot
370 if not file_exists(archiveroot):
371 self.logger.debug(
372@@ -407,7 +408,7 @@ class PublishFTPMaster(LaunchpadCronScript):
373 backup dists directory around.
374 """
375 self.logger.debug("Moving the new dists into place...")
376- for archive_config in self.configs[distribution].itervalues():
377+ for archive_config in six.itervalues(self.configs[distribution]):
378 # Use the dists "working location" as a temporary place to
379 # move the current dists out of the way for the switch. If
380 # we die in this state, the next run will know to move the
381@@ -422,7 +423,7 @@ class PublishFTPMaster(LaunchpadCronScript):
382
383 def clearEmptyDirs(self, distribution):
384 """Clear out any redundant empty directories."""
385- for archive_config in self.configs[distribution].itervalues():
386+ for archive_config in six.itervalues(self.configs[distribution]):
387 execute_subprocess(
388 ["find", archive_config.archiveroot, "-type", "d", "-empty",
389 "-delete"],
390@@ -432,7 +433,7 @@ class PublishFTPMaster(LaunchpadCronScript):
391 """Run the finalize.d parts to finalize publication."""
392 archive_roots = ' '.join([
393 archive_config.archiveroot
394- for archive_config in self.configs[distribution].itervalues()])
395+ for archive_config in six.itervalues(self.configs[distribution])])
396
397 env = {
398 'SECURITY_UPLOAD_ONLY': 'yes' if security_only else 'no',
399diff --git a/lib/lp/archivepublisher/tests/test_dominator.py b/lib/lp/archivepublisher/tests/test_dominator.py
400index b88da56..4d073e0 100755
401--- a/lib/lp/archivepublisher/tests/test_dominator.py
402+++ b/lib/lp/archivepublisher/tests/test_dominator.py
403@@ -11,6 +11,7 @@ import datetime
404 from operator import attrgetter
405
406 import apt_pkg
407+import six
408 from testtools.matchers import (
409 GreaterThan,
410 LessThan,
411@@ -813,14 +814,14 @@ class TestDominatorMethods(TestCaseWithFactory):
412 # Actually the "oldest to newest" order on the publications only
413 # applies to their creation dates. Their creation orders are
414 # irrelevant.
415- for pubs_list in pubs_by_version.itervalues():
416+ for pubs_list in six.itervalues(pubs_by_version):
417 alter_creation_dates(pubs_list, ages)
418 pubs_list.sort(key=attrgetter('datecreated'))
419
420 live_versions = ["1.1", "1.2"]
421 last_version_alive = sorted(live_versions)[-1]
422
423- all_pubs = sum(pubs_by_version.itervalues(), [])
424+ all_pubs = sum(six.itervalues(pubs_by_version), [])
425 dominator = Dominator(DevNullLogger(), series.main_archive)
426 supersede, _, delete = dominator.planPackageDomination(
427 generalization.sortPublications(all_pubs), live_versions,
428diff --git a/lib/lp/archivepublisher/tests/test_publish_ftpmaster.py b/lib/lp/archivepublisher/tests/test_publish_ftpmaster.py
429index 8c99b73..450da11 100644
430--- a/lib/lp/archivepublisher/tests/test_publish_ftpmaster.py
431+++ b/lib/lp/archivepublisher/tests/test_publish_ftpmaster.py
432@@ -121,8 +121,7 @@ def get_a_suite(distroseries):
433
434 def get_marker_files(script, distroseries):
435 """Return filesystem paths for all indexes markers for `distroseries`."""
436- suites = [
437- distroseries.getSuite(pocket) for pocket in pocketsuffix.iterkeys()]
438+ suites = [distroseries.getSuite(pocket) for pocket in pocketsuffix]
439 distro = distroseries.distribution
440 return [script.locateIndexesMarker(distro, suite) for suite in suites]
441
442@@ -998,7 +997,7 @@ class TestCreateDistroSeriesIndexes(TestCaseWithFactory, HelpersMixin):
443 script = self.makeScript(series.distribution)
444 script.setUp()
445 self.assertContentEqual(
446- [series.getSuite(pocket) for pocket in pocketsuffix.iterkeys()],
447+ [series.getSuite(pocket) for pocket in pocketsuffix],
448 script.listSuitesNeedingIndexes(series))
449
450 def test_listSuitesNeedingIndexes_is_empty_for_nonfrozen_series(self):
451@@ -1133,7 +1132,7 @@ class TestCreateDistroSeriesIndexes(TestCaseWithFactory, HelpersMixin):
452 [((given_distro, given_suites), kwargs)] = script.createIndexes.calls
453 self.assertEqual(distro, given_distro)
454 self.assertContentEqual(
455- [series.getSuite(pocket) for pocket in pocketsuffix.iterkeys()],
456+ [series.getSuite(pocket) for pocket in pocketsuffix],
457 given_suites)
458
459 def test_createIndexes_ignores_other_series(self):
460diff --git a/lib/lp/archiveuploader/dscfile.py b/lib/lp/archiveuploader/dscfile.py
461index 604193c..fbe1e25 100644
462--- a/lib/lp/archiveuploader/dscfile.py
463+++ b/lib/lp/archiveuploader/dscfile.py
464@@ -684,7 +684,7 @@ class DSCFile(SourceUploadFile, SignableTagFile):
465 ISourcePackageNameSet).getOrCreateByName(self.source)
466
467 user_defined_fields = self.extractUserDefinedFields([
468- (field, encoded[field]) for field in self._dict.iterkeys()])
469+ (field, encoded[field]) for field in self._dict])
470
471 if self.changes.buildinfo is not None:
472 buildinfo_lfa = self.changes.buildinfo.storeInDatabase()
473diff --git a/lib/lp/archiveuploader/nascentuploadfile.py b/lib/lp/archiveuploader/nascentuploadfile.py
474index f28e43e..0791141 100644
475--- a/lib/lp/archiveuploader/nascentuploadfile.py
476+++ b/lib/lp/archiveuploader/nascentuploadfile.py
477@@ -26,6 +26,7 @@ import time
478 import apt_inst
479 import apt_pkg
480 from debian.deb822 import Deb822Dict
481+import six
482 from zope.component import getUtility
483
484 from lp.app.errors import NotFoundError
485@@ -218,7 +219,7 @@ class NascentUploadFile:
486 ckfile = open(self.filepath, "r")
487 size = 0
488 for chunk in filechunks(ckfile):
489- for digester in digesters.itervalues():
490+ for digester in six.itervalues(digesters):
491 digester.update(chunk)
492 size += len(chunk)
493 ckfile.close()
494@@ -905,7 +906,7 @@ class BaseBinaryUploadFile(PackageUploadFile):
495 debug_package = None
496
497 user_defined_fields = self.extractUserDefinedFields(
498- [(field, encoded[field]) for field in self.control.iterkeys()])
499+ [(field, encoded[field]) for field in self.control])
500
501 binary = build.createBinaryPackageRelease(
502 binarypackagename=binary_name,
503diff --git a/lib/lp/archiveuploader/utils.py b/lib/lp/archiveuploader/utils.py
504index ac8057d..67a009f 100644
505--- a/lib/lp/archiveuploader/utils.py
506+++ b/lib/lp/archiveuploader/utils.py
507@@ -39,6 +39,8 @@ import re
508 import signal
509 import subprocess
510
511+import six
512+
513 from lp.services.encoding import guess as guess_encoding
514 from lp.soyuz.enums import BinaryPackageFileType
515
516@@ -333,7 +335,7 @@ def merge_file_lists(files, checksums_sha1, checksums_sha256, changes=True):
517 (filename, file_hashes[filename], size))
518
519 # Ensure that each filename was only listed in Files once.
520- if set(file_counter.itervalues()) - set([1]):
521+ if set(six.itervalues(file_counter)) - set([1]):
522 raise UploadError("Duplicate filenames in Files field.")
523
524 # Ensure that the Checksums-Sha1 and Checksums-Sha256 fields, if
525diff --git a/lib/lp/bugs/browser/bugalsoaffects.py b/lib/lp/bugs/browser/bugalsoaffects.py
526index 4fa3421..b4f5ea5 100644
527--- a/lib/lp/bugs/browser/bugalsoaffects.py
528+++ b/lib/lp/bugs/browser/bugalsoaffects.py
529@@ -16,6 +16,7 @@ from lazr.enum import (
530 Item,
531 )
532 from lazr.lifecycle.event import ObjectCreatedEvent
533+import six
534 from zope.browserpage import ViewPageTemplateFile
535 from zope.component import getUtility
536 from zope.event import notify
537@@ -605,7 +606,7 @@ class ProductBugTaskCreationStep(BugTaskCreationStep):
538
539 # Don't request validation for text widgets that are not
540 # related to the current radio selection.
541- for option, name in link_upstream_options.iteritems():
542+ for option, name in six.iteritems(link_upstream_options):
543 if link_upstream_how != option:
544 names.discard(name)
545 elif self.widgets[name].hasValidInput():
546@@ -619,7 +620,7 @@ class ProductBugTaskCreationStep(BugTaskCreationStep):
547 else:
548 # Don't validate these widgets when we don't yet know how
549 # we intend to link upstream.
550- names.difference_update(link_upstream_options.itervalues())
551+ names.difference_update(six.itervalues(link_upstream_options))
552
553 return super(ProductBugTaskCreationStep,
554 self).validate_widgets(data, names)
555diff --git a/lib/lp/bugs/browser/bugtarget.py b/lib/lp/bugs/browser/bugtarget.py
556index 5755c1b..01b323d 100644
557--- a/lib/lp/bugs/browser/bugtarget.py
558+++ b/lib/lp/bugs/browser/bugtarget.py
559@@ -28,6 +28,7 @@ from lazr.restful.interface import copy_field
560 from lazr.restful.interfaces import IJSONRequestCache
561 from pytz import timezone
562 from simplejson import dumps
563+import six
564 from six.moves import http_client
565 from six.moves.urllib.parse import (
566 quote,
567@@ -1235,7 +1236,7 @@ class BugTargetBugTagsView(LaunchpadView):
568 count=count,
569 url=self._getSearchURL(tag),
570 )
571- for (tag, count) in tags.iteritems()],
572+ for (tag, count) in six.iteritems(tags)],
573 key=itemgetter('count'), reverse=True)
574
575 @property
576diff --git a/lib/lp/bugs/interfaces/bugtaskfilter.py b/lib/lp/bugs/interfaces/bugtaskfilter.py
577index e3407ed..898c955 100644
578--- a/lib/lp/bugs/interfaces/bugtaskfilter.py
579+++ b/lib/lp/bugs/interfaces/bugtaskfilter.py
580@@ -17,6 +17,8 @@ from collections import (
581 )
582 from operator import attrgetter
583
584+import six
585+
586 from lp.bugs.interfaces.bugtarget import IHasBugs
587
588
589@@ -67,5 +69,5 @@ def filter_bugtasks_by_context(context, bugtasks):
590 for task in bugtasks:
591 bug_mapping[task.bugID].append(weight_calculator(task))
592
593- filtered = [sorted(tasks)[0].task for tasks in bug_mapping.itervalues()]
594+ filtered = [sorted(tasks)[0].task for tasks in six.itervalues(bug_mapping)]
595 return sorted(filtered, key=attrgetter('bugID'))
596diff --git a/lib/lp/bugs/model/bugtask.py b/lib/lp/bugs/model/bugtask.py
597index 2c3b6ef..fe7d366 100644
598--- a/lib/lp/bugs/model/bugtask.py
599+++ b/lib/lp/bugs/model/bugtask.py
600@@ -32,6 +32,7 @@ import re
601
602 from lazr.lifecycle.event import ObjectDeletedEvent
603 import pytz
604+import six
605 from sqlobject import (
606 ForeignKey,
607 SQLObjectNotFound,
608@@ -1138,7 +1139,7 @@ class BugTask(SQLBase):
609 new_key['sourcepackagename'] != self.sourcepackagename):
610 self._syncSourcePackages(new_key['sourcepackagename'], user)
611
612- for name, value in new_key.iteritems():
613+ for name, value in six.iteritems(new_key):
614 setattr(self, name, value)
615 self.updateTargetNameCache()
616 self.bug._reconcileAccess()
617diff --git a/lib/lp/bugs/model/structuralsubscription.py b/lib/lp/bugs/model/structuralsubscription.py
618index aec76a8..c2834e3 100644
619--- a/lib/lp/bugs/model/structuralsubscription.py
620+++ b/lib/lp/bugs/model/structuralsubscription.py
621@@ -15,6 +15,7 @@ __all__ = [
622 from collections import defaultdict
623
624 import pytz
625+import six
626 from storm.base import Storm
627 from storm.expr import (
628 And,
629@@ -137,7 +138,7 @@ class StructuralSubscription(Storm):
630 def __init__(self, subscriber, subscribed_by, **kwargs):
631 self.subscriber = subscriber
632 self.subscribed_by = subscribed_by
633- for arg, value in kwargs.iteritems():
634+ for arg, value in six.iteritems(kwargs):
635 setattr(self, arg, value)
636
637 @property
638@@ -463,7 +464,7 @@ class StructuralSubscriptionTargetMixin:
639 """See `IStructuralSubscriptionTarget`."""
640 from lp.registry.model.person import Person
641 clauses = [StructuralSubscription.subscriberID == Person.id]
642- for key, value in self._target_args.iteritems():
643+ for key, value in six.iteritems(self._target_args):
644 clauses.append(
645 getattr(StructuralSubscription, key) == value)
646
647diff --git a/lib/lp/bugs/model/tests/test_bugsubscriptioninfo.py b/lib/lp/bugs/model/tests/test_bugsubscriptioninfo.py
648index 89c5c0f..f261fcb 100644
649--- a/lib/lp/bugs/model/tests/test_bugsubscriptioninfo.py
650+++ b/lib/lp/bugs/model/tests/test_bugsubscriptioninfo.py
651@@ -7,6 +7,7 @@ __metaclass__ = type
652
653 from contextlib import contextmanager
654
655+import six
656 from storm.store import Store
657 from testtools.matchers import Equals
658 from zope.component import queryAdapter
659@@ -64,7 +65,7 @@ class TestSubscriptionRelatedSets(TestCaseWithFactory):
660 subscribers = dict(
661 (name_pair, make_person(*name_pair))
662 for name_pair in self.name_pairs)
663- self.subscribers_set = frozenset(subscribers.itervalues())
664+ self.subscribers_set = frozenset(six.itervalues(subscribers))
665 self.subscribers_sorted = tuple(
666 subscribers[name_pair] for name_pair in self.name_pairs_sorted)
667
668@@ -428,7 +429,7 @@ class TestBugSubscriptionInfoPermissions(TestCaseWithFactory):
669 self.assertEqual({}, checker.set_permissions)
670
671 # All attributes require launchpad.View.
672- permissions = set(checker.get_permissions.itervalues())
673+ permissions = set(six.itervalues(checker.get_permissions))
674 self.assertContentEqual(["launchpad.View"], permissions)
675
676 # The security adapter for launchpad.View lets anyone reference the
677diff --git a/lib/lp/bugs/scripts/bugsummaryrebuild.py b/lib/lp/bugs/scripts/bugsummaryrebuild.py
678index f41d8c2..f000905 100644
679--- a/lib/lp/bugs/scripts/bugsummaryrebuild.py
680+++ b/lib/lp/bugs/scripts/bugsummaryrebuild.py
681@@ -3,6 +3,7 @@
682
683 __metaclass__ = type
684
685+import six
686 from storm.expr import (
687 Alias,
688 And,
689@@ -116,7 +117,7 @@ def get_bugsummary_constraint(target, cls=RawBugSummary):
690 # Map to ID columns to work around Storm bug #682989.
691 return [
692 getattr(cls, k) == v
693- for (k, v) in _get_bugsummary_constraint_bits(target).iteritems()]
694+ for (k, v) in six.iteritems(_get_bugsummary_constraint_bits(target))]
695
696
697 def get_bugtaskflat_constraint(target):
698@@ -161,8 +162,8 @@ def calculate_bugsummary_changes(old, new):
699 from the old one.
700 """
701 keys = set()
702- keys.update(old.iterkeys())
703- keys.update(new.iterkeys())
704+ keys.update(six.iterkeys(old))
705+ keys.update(six.iterkeys(new))
706 added = {}
707 updated = {}
708 removed = []
709@@ -198,7 +199,7 @@ def apply_bugsummary_changes(target, added, updated, removed):
710 RawBugSummary.access_policy_id)
711
712 # Postgres doesn't do bulk updates, so do a delete+add.
713- for key, count in updated.iteritems():
714+ for key, count in six.iteritems(updated):
715 removed.append(key)
716 added[key] = count
717
718@@ -219,7 +220,8 @@ def apply_bugsummary_changes(target, added, updated, removed):
719 if added:
720 create(
721 target_cols + key_cols + (RawBugSummary.count,),
722- [target_key + key + (count,) for key, count in added.iteritems()])
723+ [target_key + key + (count,)
724+ for key, count in six.iteritems(added)])
725
726
727 def rebuild_bugsummary_for_target(target, log):
728diff --git a/lib/lp/bugs/scripts/bugtasktargetnamecaches.py b/lib/lp/bugs/scripts/bugtasktargetnamecaches.py
729index 5d14052..429ff49 100644
730--- a/lib/lp/bugs/scripts/bugtasktargetnamecaches.py
731+++ b/lib/lp/bugs/scripts/bugtasktargetnamecaches.py
732@@ -8,6 +8,7 @@ __all__ = ['BugTaskTargetNameCacheUpdater']
733
734 from collections import defaultdict
735
736+import six
737 from zope.interface import implementer
738
739 from lp.bugs.model.bugtask import (
740@@ -65,7 +66,7 @@ class BugTaskTargetNameCachesTunableLoop(object):
741 candidates = defaultdict(set)
742 for candidate in candidate_set:
743 candidates[candidate[:-1]].add(candidate[-1])
744- return list(candidates.iteritems())
745+ return list(six.iteritems(candidates))
746
747 def isDone(self):
748 """See `ITunableLoop`."""
749diff --git a/lib/lp/bugs/scripts/bzremotecomponentfinder.py b/lib/lp/bugs/scripts/bzremotecomponentfinder.py
750index 4dcb6cb..cf4246e 100644
751--- a/lib/lp/bugs/scripts/bzremotecomponentfinder.py
752+++ b/lib/lp/bugs/scripts/bzremotecomponentfinder.py
753@@ -12,6 +12,7 @@ __all__ = [
754 import re
755
756 import requests
757+import six
758 import transaction
759 from zope.component import getUtility
760
761@@ -163,7 +164,7 @@ class BugzillaRemoteComponentFinder:
762 def storeRemoteProductsAndComponents(self, bz_bugtracker, lp_bugtracker):
763 """Stores parsed product/component data from bz_bugtracker"""
764 components_to_add = []
765- for product in bz_bugtracker.products.itervalues():
766+ for product in six.itervalues(bz_bugtracker.products):
767 # Look up the component group id from Launchpad for the product
768 # if it already exists. Otherwise, add it.
769 lp_component_group = lp_bugtracker.getRemoteComponentGroup(
770diff --git a/lib/lp/bugs/tests/externalbugtracker.py b/lib/lp/bugs/tests/externalbugtracker.py
771index b8a5b9d..afb1d81 100644
772--- a/lib/lp/bugs/tests/externalbugtracker.py
773+++ b/lib/lp/bugs/tests/externalbugtracker.py
774@@ -17,6 +17,7 @@ import re
775 import time
776
777 import responses
778+import six
779 from six.moves import xmlrpc_client
780 from six.moves.urllib_parse import (
781 parse_qs,
782@@ -720,7 +721,7 @@ class TestBugzillaXMLRPCTransport(RequestsTransport):
783 def _copy_comment(self, comment, fields_to_return=None):
784 # Copy wanted fields.
785 return dict(
786- (key, value) for (key, value) in comment.iteritems()
787+ (key, value) for (key, value) in six.iteritems(comment)
788 if fields_to_return is None or key in fields_to_return)
789
790 def comments(self, arguments):
791diff --git a/lib/lp/buildmaster/browser/builder.py b/lib/lp/buildmaster/browser/builder.py
792index 0c5d05a..b378ea3 100644
793--- a/lib/lp/buildmaster/browser/builder.py
794+++ b/lib/lp/buildmaster/browser/builder.py
795@@ -20,6 +20,7 @@ from itertools import groupby
796 import operator
797
798 from lazr.restful.utils import smartquote
799+import six
800 from zope.component import getUtility
801 from zope.event import notify
802 from zope.formlib.widget import CustomWidgetFactory
803@@ -273,7 +274,7 @@ class BuilderCategory:
804 else:
805 grouped_builders[processor] = [builder]
806
807- for processor, builders in grouped_builders.iteritems():
808+ for processor, builders in six.iteritems(grouped_builders):
809 virt_str = 'virt' if self.virtualized else 'nonvirt'
810 processor_name = processor.name if processor else None
811 queue_size, duration = build_queue_sizes[virt_str].get(
812diff --git a/lib/lp/buildmaster/manager.py b/lib/lp/buildmaster/manager.py
813index ea29ff2..3c80622 100644
814--- a/lib/lp/buildmaster/manager.py
815+++ b/lib/lp/buildmaster/manager.py
816@@ -15,6 +15,7 @@ import datetime
817 import functools
818 import logging
819
820+import six
821 from storm.expr import LeftJoin
822 import transaction
823 from twisted.application import service
824@@ -139,7 +140,7 @@ class PrefetchedBuilderFactory:
825
826 def iterVitals(self):
827 """See `BuilderFactory`."""
828- return (b for n, b in sorted(self.vitals_map.iteritems()))
829+ return (b for n, b in sorted(six.iteritems(self.vitals_map)))
830
831
832 def judge_failure(builder_count, job_count, exc, retry=True):
833diff --git a/lib/lp/buildmaster/model/builder.py b/lib/lp/buildmaster/model/builder.py
834index 4d32dcf..a44e7fc 100644
835--- a/lib/lp/buildmaster/model/builder.py
836+++ b/lib/lp/buildmaster/model/builder.py
837@@ -11,6 +11,7 @@ __all__ = [
838
839 import logging
840
841+import six
842 from sqlobject import (
843 BoolCol,
844 ForeignKey,
845@@ -243,7 +244,7 @@ class Builder(SQLBase):
846
847 job_type_conditions = []
848 job_sources = specific_build_farm_job_sources()
849- for job_type, job_source in job_sources.iteritems():
850+ for job_type, job_source in six.iteritems(job_sources):
851 query = job_source.addCandidateSelectionCriteria(
852 self.processor, self.virtualized)
853 if query:
854diff --git a/lib/lp/buildmaster/queuedepth.py b/lib/lp/buildmaster/queuedepth.py
855index 1fdb151..66f1986 100644
856--- a/lib/lp/buildmaster/queuedepth.py
857+++ b/lib/lp/buildmaster/queuedepth.py
858@@ -14,6 +14,7 @@ from datetime import (
859 )
860
861 from pytz import utc
862+import six
863 from storm.expr import Count
864
865 from lp.buildmaster.enums import BuildQueueStatus
866@@ -275,7 +276,7 @@ def estimate_job_delay(bq, builder_stats):
867
868 sum_of_delays = 0
869 # Now devide the delays based on a jobs/builders comparison.
870- for platform, duration in delays.iteritems():
871+ for platform, duration in six.iteritems(delays):
872 jobs = job_counts[platform]
873 builders = builder_stats[platform]
874 # If there are less jobs than builders that can take them on,
875diff --git a/lib/lp/buildmaster/tests/test_queuedepth.py b/lib/lp/buildmaster/tests/test_queuedepth.py
876index 090fd11..b9198e1 100644
877--- a/lib/lp/buildmaster/tests/test_queuedepth.py
878+++ b/lib/lp/buildmaster/tests/test_queuedepth.py
879@@ -10,6 +10,7 @@ from datetime import (
880 )
881
882 from pytz import utc
883+import six
884 from zope.component import getUtility
885 from zope.security.proxy import removeSecurityProxy
886
887@@ -698,7 +699,7 @@ class TestMinTimeToNextBuilderMulti(MultiArchBuildsBase):
888 check_mintime_to_builder(self, job, 0)
889
890 # Let's disable all builders.
891- for builders in self.builders.itervalues():
892+ for builders in six.itervalues(self.builders):
893 for builder in builders:
894 builder.builderok = False
895
896diff --git a/lib/lp/code/browser/branch.py b/lib/lp/code/browser/branch.py
897index 404c272..6ea17ac 100644
898--- a/lib/lp/code/browser/branch.py
899+++ b/lib/lp/code/browser/branch.py
900@@ -36,6 +36,7 @@ from lazr.restful.interface import (
901 from lazr.uri import URI
902 import pytz
903 import simplejson
904+import six
905 from zope.component import getUtility
906 from zope.event import notify
907 from zope.formlib import form
908@@ -954,8 +955,8 @@ class BranchDeletionView(LaunchpadFormView):
909 :return: A list of tuples of (item, action, reason, allowed)
910 """
911 reqs = []
912- for item, (action, reason) in (
913- self.context.deletionRequirements(eager_load=True).iteritems()):
914+ for item, (action, reason) in six.iteritems(
915+ self.context.deletionRequirements(eager_load=True)):
916 allowed = check_permission('launchpad.Edit', item)
917 reqs.append((item, action, reason, allowed))
918 return reqs
919diff --git a/lib/lp/code/browser/branchlisting.py b/lib/lp/code/browser/branchlisting.py
920index f472ef0..23a1e9c 100644
921--- a/lib/lp/code/browser/branchlisting.py
922+++ b/lib/lp/code/browser/branchlisting.py
923@@ -33,6 +33,7 @@ from lazr.enum import (
924 EnumeratedType,
925 Item,
926 )
927+import six
928 from six.moves.urllib.parse import parse_qs
929 from storm.expr import Desc
930 from zope.browserpage import ViewPageTemplateFile
931@@ -1306,7 +1307,7 @@ class GroupedDistributionSourcePackageBranchesView(LaunchpadView,
932 # For each distro series, we only want the "best" pocket if one branch
933 # is linked to more than one pocket. Best here means smaller value.
934 official_branches = {}
935- for key, value in distro_links.iteritems():
936+ for key, value in six.iteritems(distro_links):
937 ordered = sorted(value, key=attrgetter('pocket'))
938 seen_branches = set()
939 branches = []
940@@ -1363,7 +1364,7 @@ class GroupedDistributionSourcePackageBranchesView(LaunchpadView,
941 and merge proposal links for badges.
942 """
943 visible_branches = []
944- for branches, count in self.series_branches_map.itervalues():
945+ for branches, count in six.itervalues(self.series_branches_map):
946 visible_branches.extend(branches)
947 return visible_branches
948
949diff --git a/lib/lp/code/browser/branchmergeproposal.py b/lib/lp/code/browser/branchmergeproposal.py
950index a1b41fb..6d33f97 100644
951--- a/lib/lp/code/browser/branchmergeproposal.py
952+++ b/lib/lp/code/browser/branchmergeproposal.py
953@@ -35,6 +35,7 @@ from lazr.restful.interfaces import (
954 IWebServiceClientRequest,
955 )
956 import simplejson
957+import six
958 from zope.component import (
959 adapter,
960 getMultiAdapter,
961@@ -145,7 +146,7 @@ def latest_proposals_for_each_branch(proposals):
962 targets[target] = (proposal, date_created)
963
964 return sorted(
965- [proposal for proposal, date_created in targets.itervalues()],
966+ [proposal for proposal, date_created in six.itervalues(targets)],
967 key=operator.attrgetter('date_created'), reverse=True)
968
969
970diff --git a/lib/lp/code/browser/gitrepository.py b/lib/lp/code/browser/gitrepository.py
971index c05dcc1..4f38702 100644
972--- a/lib/lp/code/browser/gitrepository.py
973+++ b/lib/lp/code/browser/gitrepository.py
974@@ -31,6 +31,7 @@ from lazr.restful.interface import (
975 copy_field,
976 use_template,
977 )
978+import six
979 from six.moves.urllib_parse import (
980 urlsplit,
981 urlunsplit,
982@@ -1273,9 +1274,8 @@ class GitRepositoryDeletionView(LaunchpadFormView):
983 :return: A list of tuples of (item, action, reason, allowed)
984 """
985 reqs = []
986- for item, (action, reason) in (
987- self.context.getDeletionRequirements(
988- eager_load=True).iteritems()):
989+ for item, (action, reason) in six.iteritems(
990+ self.context.getDeletionRequirements(eager_load=True)):
991 allowed = check_permission("launchpad.Edit", item)
992 reqs.append((item, action, reason, allowed))
993 return reqs
994diff --git a/lib/lp/code/model/branchcollection.py b/lib/lp/code/model/branchcollection.py
995index 79411cc..9c0b097 100644
996--- a/lib/lp/code/model/branchcollection.py
997+++ b/lib/lp/code/model/branchcollection.py
998@@ -16,6 +16,7 @@ from lazr.uri import (
999 InvalidURIError,
1000 URI,
1001 )
1002+import six
1003 from storm.expr import (
1004 And,
1005 Asc,
1006@@ -562,7 +563,7 @@ class GenericBranchCollection:
1007 bugtasks_for_branch[bugbranch.branch].append(bugtask)
1008
1009 # Now filter those down to one bugtask per branch
1010- for branch, tasks in bugtasks_for_branch.iteritems():
1011+ for branch, tasks in six.iteritems(bugtasks_for_branch):
1012 linked_bugtasks[branch.id].extend(
1013 filter_bugtasks_by_context(branch.target.context, tasks))
1014
1015diff --git a/lib/lp/code/model/branchjob.py b/lib/lp/code/model/branchjob.py
1016index a26c47b..7067f8a 100644
1017--- a/lib/lp/code/model/branchjob.py
1018+++ b/lib/lp/code/model/branchjob.py
1019@@ -684,7 +684,7 @@ class RevisionsAddedJob(BranchJobDerived):
1020 proposals[source_id] = (proposal, date_created)
1021
1022 return sorted(
1023- [proposal for proposal, date_created in proposals.itervalues()],
1024+ [proposal for proposal, date_created in six.itervalues(proposals)],
1025 key=operator.attrgetter('date_created'), reverse=True)
1026
1027 def getRevisionMessage(self, revision_id, revno):
1028diff --git a/lib/lp/code/model/codereviewinlinecomment.py b/lib/lp/code/model/codereviewinlinecomment.py
1029index 580eb44..2ac74b0 100644
1030--- a/lib/lp/code/model/codereviewinlinecomment.py
1031+++ b/lib/lp/code/model/codereviewinlinecomment.py
1032@@ -10,6 +10,7 @@ __all__ = [
1033 'CodeReviewInlineCommentSet',
1034 ]
1035
1036+import six
1037 from storm.expr import LeftJoin
1038 from storm.locals import (
1039 Int,
1040@@ -113,7 +114,7 @@ class CodeReviewInlineCommentSet:
1041 list(crics), key=lambda c: c.comment.date_created)
1042 inline_comments = []
1043 for cric in sorted_crics:
1044- for line_number, text in cric.comments.iteritems():
1045+ for line_number, text in six.iteritems(cric.comments):
1046 comment = {
1047 'line_number': line_number,
1048 'person': cric.person,
1049diff --git a/lib/lp/code/model/revision.py b/lib/lp/code/model/revision.py
1050index 4895ebb..a979c74 100644
1051--- a/lib/lp/code/model/revision.py
1052+++ b/lib/lp/code/model/revision.py
1053@@ -18,6 +18,7 @@ import email
1054
1055 from breezy.revision import NULL_REVISION
1056 import pytz
1057+import six
1058 from sqlobject import (
1059 BoolCol,
1060 ForeignKey,
1061@@ -287,7 +288,7 @@ class RevisionSet:
1062 parent_id=parent_id)
1063
1064 # Create revision properties.
1065- for name, value in properties.iteritems():
1066+ for name, value in six.iteritems(properties):
1067 RevisionProperty(revision=revision, name=name, value=value)
1068
1069 return revision
1070@@ -378,7 +379,7 @@ class RevisionSet:
1071 for bzr_revision in revisions:
1072 db_id = revision_db_id[bzr_revision.revision_id]
1073 # Property data: revision DB id, name, value.
1074- for name, value in bzr_revision.properties.iteritems():
1075+ for name, value in six.iteritems(bzr_revision.properties):
1076 # pristine-tar properties can be huge, and storing them
1077 # in the database provides no value. Exclude them.
1078 if name.startswith('deb-pristine-delta'):
1079diff --git a/lib/lp/codehosting/inmemory.py b/lib/lp/codehosting/inmemory.py
1080index a2a7103..2b704fd 100644
1081--- a/lib/lp/codehosting/inmemory.py
1082+++ b/lib/lp/codehosting/inmemory.py
1083@@ -131,7 +131,7 @@ class ObjectSet:
1084 del self._objects[db_object.id]
1085
1086 def __iter__(self):
1087- return self._objects.itervalues()
1088+ return six.itervalues(self._objects)
1089
1090 def _find(self, **kwargs):
1091 [(key, value)] = kwargs.items()
1092diff --git a/lib/lp/codehosting/scanner/buglinks.py b/lib/lp/codehosting/scanner/buglinks.py
1093index f9609c3..116fb90 100644
1094--- a/lib/lp/codehosting/scanner/buglinks.py
1095+++ b/lib/lp/codehosting/scanner/buglinks.py
1096@@ -9,6 +9,7 @@ __all__ = [
1097 ]
1098
1099 from breezy.bugtracker import InvalidBugStatus
1100+import six
1101 from six.moves.urllib.parse import urlsplit
1102 from zope.component import getUtility
1103
1104@@ -77,7 +78,7 @@ class BugBranchLinker:
1105 except InvalidBugStatus:
1106 return
1107 bug_set = getUtility(IBugSet)
1108- for bug_id, status in bug_info.iteritems():
1109+ for bug_id, status in six.iteritems(bug_info):
1110 try:
1111 bug = bug_set.get(bug_id)
1112 except NotFoundError:
1113diff --git a/lib/lp/codehosting/sshserver/session.py b/lib/lp/codehosting/sshserver/session.py
1114index 38bea68..a77855b 100644
1115--- a/lib/lp/codehosting/sshserver/session.py
1116+++ b/lib/lp/codehosting/sshserver/session.py
1117@@ -15,6 +15,7 @@ import sys
1118
1119 from lazr.sshserver.events import AvatarEvent
1120 from lazr.sshserver.session import DoNothingSession
1121+import six
1122 from six import reraise
1123 from six.moves.urllib.parse import urlparse
1124 from twisted.internet import (
1125@@ -144,7 +145,7 @@ class ForkedProcessTransport(process.BaseProcess):
1126 assert executable == 'brz', executable # Maybe .endswith()
1127 assert args[0] == 'brz', args[0]
1128 message = ['fork-env %s\n' % (' '.join(args[1:]),)]
1129- for key, value in environment.iteritems():
1130+ for key, value in six.iteritems(environment):
1131 # XXX: Currently we only pass BRZ_EMAIL, should we be passing
1132 # everything else? Note that many won't be handled properly,
1133 # since the process is already running.
1134diff --git a/lib/lp/codehosting/vfs/branchfsclient.py b/lib/lp/codehosting/vfs/branchfsclient.py
1135index b96d780..3307830 100644
1136--- a/lib/lp/codehosting/vfs/branchfsclient.py
1137+++ b/lib/lp/codehosting/vfs/branchfsclient.py
1138@@ -14,6 +14,7 @@ __all__ = [
1139
1140 import time
1141
1142+import six
1143 from twisted.internet import defer
1144
1145 from lp.code.interfaces.codehosting import BRANCH_TRANSPORT
1146@@ -84,7 +85,7 @@ class BranchFileSystemClient:
1147 def _getFromCache(self, path):
1148 """Get the cached 'transport_tuple' for 'path'."""
1149 split_path = path.strip('/').split('/')
1150- for object_path, value in self._cache.iteritems():
1151+ for object_path, value in six.iteritems(self._cache):
1152 transport_type, data, inserted_time = value
1153 split_object_path = object_path.strip('/').split('/')
1154 # Do a segment-by-segment comparison. Python sucks, lists should
1155diff --git a/lib/lp/registry/model/distroseriesdifference.py b/lib/lp/registry/model/distroseriesdifference.py
1156index e0ee875..0672584 100644
1157--- a/lib/lp/registry/model/distroseriesdifference.py
1158+++ b/lib/lp/registry/model/distroseriesdifference.py
1159@@ -19,6 +19,7 @@ from debian.changelog import (
1160 Version,
1161 )
1162 from lazr.enum import DBItem
1163+import six
1164 from sqlobject import StringCol
1165 from storm.expr import (
1166 And,
1167@@ -279,10 +280,10 @@ def eager_load_dsds(dsds):
1168 # referred to.
1169 sprs = bulk.load_related(
1170 SourcePackageRelease, chain(
1171- source_pubs.itervalues(),
1172- parent_source_pubs.itervalues(),
1173- source_pubs_for_release.itervalues(),
1174- parent_source_pubs_for_release.itervalues()),
1175+ six.itervalues(source_pubs),
1176+ six.itervalues(parent_source_pubs),
1177+ six.itervalues(source_pubs_for_release),
1178+ six.itervalues(parent_source_pubs_for_release)),
1179 ("sourcepackagereleaseID",))
1180
1181 # Get packagesets and parent_packagesets for each DSD.
1182diff --git a/lib/lp/registry/scripts/tests/test_populate_distroseriesdiff.py b/lib/lp/registry/scripts/tests/test_populate_distroseriesdiff.py
1183index a8acf8a..84f9f74 100644
1184--- a/lib/lp/registry/scripts/tests/test_populate_distroseriesdiff.py
1185+++ b/lib/lp/registry/scripts/tests/test_populate_distroseriesdiff.py
1186@@ -5,6 +5,7 @@
1187
1188 __metaclass__ = type
1189
1190+import six
1191 from storm.store import Store
1192 import transaction
1193 from zope.security.proxy import removeSecurityProxy
1194@@ -171,7 +172,8 @@ class TestFindLatestSourcePackageReleases(TestCaseWithFactory, FactoryHelper):
1195 for status in active_publishing_status)
1196 query = compose_sql_find_latest_source_package_releases(distroseries)
1197 self.assertContentEqual(
1198- [self.getExpectedResultFor(spph) for spph in spphs.itervalues()],
1199+ [self.getExpectedResultFor(spph)
1200+ for spph in six.itervalues(spphs)],
1201 Store.of(distroseries).execute(query))
1202
1203 def test_does_not_find_inactive_publication(self):
1204diff --git a/lib/lp/registry/services/sharingservice.py b/lib/lp/registry/services/sharingservice.py
1205index 68e33c3..09744ee 100644
1206--- a/lib/lp/registry/services/sharingservice.py
1207+++ b/lib/lp/registry/services/sharingservice.py
1208@@ -13,6 +13,7 @@ from operator import attrgetter
1209
1210 from lazr.restful.interfaces import IWebBrowserOriginatingRequest
1211 from lazr.restful.utils import get_current_web_service_request
1212+import six
1213 from storm.expr import (
1214 And,
1215 Count,
1216@@ -608,7 +609,7 @@ class SharingService:
1217 for (grantee, permissions, shared_artifact_types) in grant_permissions:
1218 some_things_shared = len(shared_artifact_types) > 0
1219 grantee_permissions = {}
1220- for (policy, permission) in permissions.iteritems():
1221+ for (policy, permission) in six.iteritems(permissions):
1222 grantee_permissions[policy.type.name] = permission.name
1223 shared_artifact_type_names = [
1224 info_type.name for info_type in shared_artifact_types]
1225diff --git a/lib/lp/scripts/utilities/warninghandler.py b/lib/lp/scripts/utilities/warninghandler.py
1226index 46b8a73..cb0c4ff 100644
1227--- a/lib/lp/scripts/utilities/warninghandler.py
1228+++ b/lib/lp/scripts/utilities/warninghandler.py
1229@@ -12,6 +12,8 @@ import StringIO
1230 import sys
1231 import warnings
1232
1233+import six
1234+
1235
1236 class WarningReport:
1237
1238@@ -211,7 +213,7 @@ def report_other_warnings():
1239 if other_warnings:
1240 print(file=sys.stderr)
1241 print("General warnings.", file=sys.stderr)
1242- for warninginfo in other_warnings.itervalues():
1243+ for warninginfo in six.itervalues(other_warnings):
1244 print(file=sys.stderr)
1245 print(warninginfo, file=sys.stderr)
1246
1247diff --git a/lib/lp/services/command_spawner.py b/lib/lp/services/command_spawner.py
1248index dcca28f..539a3fb 100644
1249--- a/lib/lp/services/command_spawner.py
1250+++ b/lib/lp/services/command_spawner.py
1251@@ -151,7 +151,7 @@ class CommandSpawner:
1252 processes are cleaned up. Until then, they will stay around as
1253 zombies.
1254 """
1255- for process in self.running_processes.iterkeys():
1256+ for process in self.running_processes:
1257 process.terminate()
1258
1259 def _spawn(self, command):
1260diff --git a/lib/lp/services/config/__init__.py b/lib/lp/services/config/__init__.py
1261index 3c6ad51..ebe9ebf 100644
1262--- a/lib/lp/services/config/__init__.py
1263+++ b/lib/lp/services/config/__init__.py
1264@@ -22,6 +22,7 @@ import sys
1265
1266 from lazr.config import ImplicitTypeSchema
1267 from lazr.config.interfaces import ConfigErrors
1268+import six
1269 from six.moves.urllib.parse import (
1270 urlparse,
1271 urlunparse,
1272@@ -459,7 +460,7 @@ class DatabaseConfig:
1273
1274 Overriding a value to None removes the override.
1275 """
1276- for attr, value in kwargs.iteritems():
1277+ for attr, value in six.iteritems(kwargs):
1278 assert attr in self._db_config_attrs, (
1279 "%s cannot be overridden" % attr)
1280 if value is None:
1281diff --git a/lib/lp/services/database/bulk.py b/lib/lp/services/database/bulk.py
1282index f73bbf8..840e402 100644
1283--- a/lib/lp/services/database/bulk.py
1284+++ b/lib/lp/services/database/bulk.py
1285@@ -25,6 +25,7 @@ from operator import (
1286 itemgetter,
1287 )
1288
1289+import six
1290 from storm.databases.postgres import Returning
1291 from storm.expr import (
1292 And,
1293@@ -52,7 +53,7 @@ def collate(things, key):
1294 collection = defaultdict(list)
1295 for thing in things:
1296 collection[key(thing)].append(thing)
1297- return collection.iteritems()
1298+ return six.iteritems(collection)
1299
1300
1301 def get_type(thing):
1302diff --git a/lib/lp/services/features/testing.py b/lib/lp/services/features/testing.py
1303index 998138d..3a2d69b 100644
1304--- a/lib/lp/services/features/testing.py
1305+++ b/lib/lp/services/features/testing.py
1306@@ -13,6 +13,7 @@ __all__ = [
1307 from fixtures import Fixture
1308 from lazr.restful.utils import get_current_browser_request
1309 import psycopg2
1310+import six
1311
1312 from lp.services.features import (
1313 get_relevant_feature_controller,
1314@@ -91,7 +92,7 @@ class FeatureFixtureMixin:
1315 scope='default',
1316 priority=999,
1317 value=unicode(value))
1318- for flag_name, value in self.desired_features.iteritems()
1319+ for flag_name, value in six.iteritems(self.desired_features)
1320 if value is not None]
1321
1322 if self.full_feature_rules is not None:
1323diff --git a/lib/lp/services/librarianserver/testing/fake.py b/lib/lp/services/librarianserver/testing/fake.py
1324index 37d6bb6..e1e1f5e 100644
1325--- a/lib/lp/services/librarianserver/testing/fake.py
1326+++ b/lib/lp/services/librarianserver/testing/fake.py
1327@@ -19,6 +19,7 @@ import hashlib
1328 from StringIO import StringIO
1329
1330 from fixtures import Fixture
1331+import six
1332 from six.moves.urllib.parse import urljoin
1333 import transaction
1334 from transaction.interfaces import ISynchronizer
1335@@ -138,7 +139,7 @@ class FakeLibrarian(Fixture):
1336 database transaction.
1337 """
1338 # Note that all files have been committed to storage.
1339- for alias in self.aliases.itervalues():
1340+ for alias in six.itervalues(self.aliases):
1341 alias.file_committed = True
1342
1343 def _makeAlias(self, file_id, name, content, content_type):
1344@@ -175,7 +176,7 @@ class FakeLibrarian(Fixture):
1345
1346 def findBySHA256(self, sha256):
1347 "See `ILibraryFileAliasSet`."""
1348- for alias in self.aliases.itervalues():
1349+ for alias in six.itervalues(self.aliases):
1350 if alias.content.sha256 == sha256:
1351 return alias
1352
1353diff --git a/lib/lp/services/mail/basemailer.py b/lib/lp/services/mail/basemailer.py
1354index a2f8a40..280ab78 100644
1355--- a/lib/lp/services/mail/basemailer.py
1356+++ b/lib/lp/services/mail/basemailer.py
1357@@ -12,6 +12,7 @@ import logging
1358 from smtplib import SMTPException
1359 import sys
1360
1361+import six
1362 from zope.component import getUtility
1363 from zope.error.interfaces import IErrorReportingUtility
1364 from zope.security.management import getSecurityPolicy
1365@@ -79,7 +80,7 @@ class BaseMailer:
1366 self._subject_template = subject
1367 self._template_name = template_name
1368 self._recipients = NotificationRecipientSet()
1369- for recipient, reason in recipients.iteritems():
1370+ for recipient, reason in six.iteritems(recipients):
1371 self._recipients.add(recipient, reason, reason.mail_header)
1372 self.from_address = from_address
1373 self.delta = delta
1374diff --git a/lib/lp/services/messaging/tests/test_rabbit.py b/lib/lp/services/messaging/tests/test_rabbit.py
1375index 6cd9f99..dfd48af 100644
1376--- a/lib/lp/services/messaging/tests/test_rabbit.py
1377+++ b/lib/lp/services/messaging/tests/test_rabbit.py
1378@@ -9,6 +9,7 @@ from functools import partial
1379 from itertools import count
1380 import socket
1381
1382+import six
1383 from testtools.testcase import ExpectedException
1384 import transaction
1385 from transaction._transaction import Status as TransactionStatus
1386@@ -396,7 +397,7 @@ class TestRabbit(RabbitTestCase):
1387 return set()
1388 else:
1389 return set(
1390- sync.session for sync in syncs_set.data.itervalues()
1391+ sync.session for sync in six.itervalues(syncs_set.data)
1392 if isinstance(sync, RabbitSessionTransactionSync))
1393
1394 def test_global_session(self):
1395diff --git a/lib/lp/services/osutils.py b/lib/lp/services/osutils.py
1396index b487165..7eafc40 100644
1397--- a/lib/lp/services/osutils.py
1398+++ b/lib/lp/services/osutils.py
1399@@ -28,6 +28,8 @@ from signal import (
1400 )
1401 import time
1402
1403+import six
1404+
1405
1406 def remove_tree(path):
1407 """Remove the tree at 'path' from disk."""
1408@@ -41,7 +43,7 @@ def set_environ(new_values):
1409 :return: a dict of the old values
1410 """
1411 old_values = {}
1412- for name, value in new_values.iteritems():
1413+ for name, value in six.iteritems(new_values):
1414 old_values[name] = os.environ.get(name)
1415 if value is None:
1416 if old_values[name] is not None:
1417diff --git a/lib/lp/services/testing/customresult.py b/lib/lp/services/testing/customresult.py
1418index 2164f3d..5862f84 100644
1419--- a/lib/lp/services/testing/customresult.py
1420+++ b/lib/lp/services/testing/customresult.py
1421@@ -11,6 +11,7 @@ __all__ = [
1422
1423 from unittest import TestSuite
1424
1425+import six
1426 from zope.testrunner import find
1427
1428
1429@@ -65,7 +66,7 @@ def filter_tests(list_name, reorder_tests=False):
1430 test_lookup = {}
1431 # Multiple unique testcases can be represented by a single id and they
1432 # must be tracked separately.
1433- for layer_name, suite in tests_by_layer_name.iteritems():
1434+ for layer_name, suite in six.iteritems(tests_by_layer_name):
1435 for testcase in suite:
1436 layer_to_tests = test_lookup.setdefault(
1437 testcase.id(), {})
1438diff --git a/lib/lp/services/utils.py b/lib/lp/services/utils.py
1439index beda118..ea661c5 100644
1440--- a/lib/lp/services/utils.py
1441+++ b/lib/lp/services/utils.py
1442@@ -52,6 +52,7 @@ from fixtures import (
1443 )
1444 from lazr.enum import BaseItem
1445 import pytz
1446+import six
1447 from six.moves import cPickle as pickle
1448 from twisted.python.util import mergeFunctionMetadata
1449 from zope.security.proxy import isinstance as zope_isinstance
1450@@ -388,7 +389,7 @@ def obfuscate_structure(o):
1451 elif isinstance(o, (dict)):
1452 return dict(
1453 (obfuscate_structure(key), obfuscate_structure(value))
1454- for key, value in o.iteritems())
1455+ for key, value in six.iteritems(o))
1456 else:
1457 return o
1458
1459diff --git a/lib/lp/services/webapp/escaping.py b/lib/lp/services/webapp/escaping.py
1460index 8c4d965..bce499e 100644
1461--- a/lib/lp/services/webapp/escaping.py
1462+++ b/lib/lp/services/webapp/escaping.py
1463@@ -9,6 +9,7 @@ __all__ = [
1464 ]
1465
1466 from lazr.restful.utils import get_current_browser_request
1467+import six
1468 from zope.i18n import (
1469 Message,
1470 translate,
1471@@ -95,7 +96,7 @@ class structured:
1472 self.escapedtext = text % tuple(html_escape(rep) for rep in reps)
1473 elif kwreps:
1474 self.escapedtext = text % dict(
1475- (k, html_escape(v)) for k, v in kwreps.iteritems())
1476+ (k, html_escape(v)) for k, v in six.iteritems(kwreps))
1477 else:
1478 self.escapedtext = text
1479
1480diff --git a/lib/lp/services/webapp/login.py b/lib/lp/services/webapp/login.py
1481index 43eb478..fdbd079 100644
1482--- a/lib/lp/services/webapp/login.py
1483+++ b/lib/lp/services/webapp/login.py
1484@@ -290,7 +290,7 @@ class OpenIDCallbackView(OpenIDLogin):
1485
1486 def _gather_params(self, request):
1487 params = dict(request.form)
1488- for key, value in request.query_string_params.iteritems():
1489+ for key, value in six.iteritems(request.query_string_params):
1490 if len(value) > 1:
1491 raise ValueError(
1492 'Did not expect multi-valued fields.')
1493diff --git a/lib/lp/services/webapp/servers.py b/lib/lp/services/webapp/servers.py
1494index 8b85df8..1f77b76 100644
1495--- a/lib/lp/services/webapp/servers.py
1496+++ b/lib/lp/services/webapp/servers.py
1497@@ -529,7 +529,7 @@ def get_query_string_params(request):
1498 parsed_qs = parse_qs(query_string, keep_blank_values=True)
1499 # Use BrowserRequest._decode() for decoding the received parameters.
1500 decoded_qs = {}
1501- for key, values in parsed_qs.iteritems():
1502+ for key, values in six.iteritems(parsed_qs):
1503 decoded_qs[key] = [
1504 request._decode(value) for value in values]
1505 return decoded_qs
1506diff --git a/lib/lp/services/webhooks/interfaces.py b/lib/lp/services/webhooks/interfaces.py
1507index d4d20e3..ad01625 100644
1508--- a/lib/lp/services/webhooks/interfaces.py
1509+++ b/lib/lp/services/webhooks/interfaces.py
1510@@ -40,6 +40,7 @@ from lazr.restful.fields import (
1511 Reference,
1512 )
1513 from lazr.restful.interface import copy_field
1514+import six
1515 from six.moves import http_client
1516 from zope.interface import (
1517 Attribute,
1518@@ -105,7 +106,7 @@ class AnyWebhookEventTypeVocabulary(SimpleVocabulary):
1519 def __init__(self, context):
1520 terms = [
1521 self.createTerm(key, key, value)
1522- for key, value in WEBHOOK_EVENT_TYPES.iteritems()]
1523+ for key, value in six.iteritems(WEBHOOK_EVENT_TYPES)]
1524 super(AnyWebhookEventTypeVocabulary, self).__init__(terms)
1525
1526
1527diff --git a/lib/lp/soyuz/browser/tests/archive-views.txt b/lib/lp/soyuz/browser/tests/archive-views.txt
1528index 8f440d7..72c1392 100644
1529--- a/lib/lp/soyuz/browser/tests/archive-views.txt
1530+++ b/lib/lp/soyuz/browser/tests/archive-views.txt
1531@@ -105,8 +105,10 @@ usage details in a dictionary containing:
1532 We will use a helper function for printing the returned dictionary
1533 contents.
1534
1535+ >>> import six
1536+
1537 >>> def print_repository_usage(repository_usage):
1538- ... for key, value in sorted(repository_usage.iteritems()):
1539+ ... for key, value in sorted(six.iteritems(repository_usage)):
1540 ... print('%s: %s' % (key, value))
1541
1542 Celso PPA has some packages, but still below the quota.
1543diff --git a/lib/lp/soyuz/model/packagediff.py b/lib/lp/soyuz/model/packagediff.py
1544index 6933c31..12437cd 100644
1545--- a/lib/lp/soyuz/model/packagediff.py
1546+++ b/lib/lp/soyuz/model/packagediff.py
1547@@ -16,6 +16,7 @@ import shutil
1548 import subprocess
1549 import tempfile
1550
1551+import six
1552 from sqlobject import ForeignKey
1553 from storm.expr import Desc
1554 from storm.store import EmptyResultSet
1555@@ -215,7 +216,7 @@ class PackageDiff(SQLBase):
1556 zip(directions, (self.from_source, self.to_source)))
1557
1558 # Iterate over the packages to be diff'ed.
1559- for direction, package in packages.iteritems():
1560+ for direction, package in six.iteritems(packages):
1561 # Create distinct directory locations for
1562 # 'from' and 'to' files.
1563 absolute_path = os.path.join(tmp_dir, direction)
1564diff --git a/lib/lp/soyuz/model/publishing.py b/lib/lp/soyuz/model/publishing.py
1565index fafd7f9..1c5d384 100644
1566--- a/lib/lp/soyuz/model/publishing.py
1567+++ b/lib/lp/soyuz/model/publishing.py
1568@@ -22,6 +22,7 @@ import os
1569 import sys
1570
1571 import pytz
1572+import six
1573 from sqlobject import (
1574 ForeignKey,
1575 IntCol,
1576@@ -1012,7 +1013,7 @@ def expand_binary_requests(distroseries, binaries):
1577 arch_map = dict((arch.architecturetag, arch) for arch in archs)
1578
1579 expanded = []
1580- for bpr, overrides in binaries.iteritems():
1581+ for bpr, overrides in six.iteritems(binaries):
1582 if bpr.architecturespecific:
1583 # Find the DAS in this series corresponding to the original
1584 # build arch tag. If it does not exist or is disabled, we should
1585diff --git a/lib/lp/soyuz/scripts/custom_uploads_copier.py b/lib/lp/soyuz/scripts/custom_uploads_copier.py
1586index e6f7e83..21cc1f2 100644
1587--- a/lib/lp/soyuz/scripts/custom_uploads_copier.py
1588+++ b/lib/lp/soyuz/scripts/custom_uploads_copier.py
1589@@ -14,6 +14,8 @@ __all__ = [
1590
1591 from operator import attrgetter
1592
1593+import six
1594+
1595 from lp.archivepublisher.ddtp_tarball import DdtpTarballUpload
1596 from lp.archivepublisher.debian_installer import DebianInstallerUpload
1597 from lp.archivepublisher.dist_upgrader import DistUpgraderUpload
1598@@ -163,7 +165,7 @@ class CustomUploadsCopier:
1599 self.target_series, source_pocket=self.target_pocket)
1600 source_uploads = self.getLatestUploads(
1601 source_series, source_pocket=source_pocket)
1602- for upload in source_uploads.itervalues():
1603+ for upload in six.itervalues(source_uploads):
1604 if (not self.isObsolete(upload, target_uploads) and
1605 self.isForValidDAS(upload)):
1606 self.copyUpload(upload)
1607diff --git a/lib/lp/soyuz/scripts/gina/handlers.py b/lib/lp/soyuz/scripts/gina/handlers.py
1608index 54539a4..597e5aa 100644
1609--- a/lib/lp/soyuz/scripts/gina/handlers.py
1610+++ b/lib/lp/soyuz/scripts/gina/handlers.py
1611@@ -24,6 +24,7 @@ from cStringIO import StringIO
1612 import os
1613 import re
1614
1615+import six
1616 from sqlobject import (
1617 SQLObjectMoreThanOneResultError,
1618 SQLObjectNotFound,
1619@@ -504,7 +505,7 @@ class SourcePackageHandler:
1620 dsc_contents = parse_tagfile(dsc_path)
1621 dsc_contents = dict([
1622 (name.lower(), value) for
1623- (name, value) in dsc_contents.iteritems()])
1624+ (name, value) in six.iteritems(dsc_contents)])
1625
1626 # Since the dsc doesn't know, we add in the directory, package
1627 # component and section
1628diff --git a/lib/lp/soyuz/scripts/gina/runner.py b/lib/lp/soyuz/scripts/gina/runner.py
1629index d9edd1f..631323f 100644
1630--- a/lib/lp/soyuz/scripts/gina/runner.py
1631+++ b/lib/lp/soyuz/scripts/gina/runner.py
1632@@ -157,7 +157,7 @@ def import_sourcepackages(distro, packages_map, package_root,
1633 npacks = len(packages_map.src_map)
1634 log.info('%i Source Packages to be imported', npacks)
1635
1636- for package in sorted(packages_map.src_map.iterkeys()):
1637+ for package in sorted(packages_map.src_map):
1638 for source in packages_map.src_map[package]:
1639 attempt_source_package_import(
1640 distro, source, package_root, importer_handler)
1641@@ -193,7 +193,7 @@ def import_binarypackages(distro, packages_map, package_root,
1642 log.info(
1643 '%i Binary Packages to be imported for %s', npacks, archtag)
1644 # Go over binarypackages importing them for this architecture
1645- for package_name in sorted(packages_map.bin_map[archtag].iterkeys()):
1646+ for package_name in sorted(packages_map.bin_map[archtag]):
1647 binary = packages_map.bin_map[archtag][package_name]
1648 try:
1649 try:
1650diff --git a/lib/lp/testing/__init__.py b/lib/lp/testing/__init__.py
1651index 20a50d9..81e86f1 100644
1652--- a/lib/lp/testing/__init__.py
1653+++ b/lib/lp/testing/__init__.py
1654@@ -96,6 +96,7 @@ import oops_datedir_repo.serializer_rfc822
1655 import pytz
1656 import scandir
1657 import simplejson
1658+import six
1659 from storm.store import Store
1660 import subunit
1661 import testtools
1662@@ -697,7 +698,7 @@ class TestCase(testtools.TestCase, fixtures.TestWithFixtures):
1663 The config values will be restored during test tearDown.
1664 """
1665 name = self.factory.getUniqueString()
1666- body = '\n'.join("%s: %s" % (k, v) for k, v in kwargs.iteritems())
1667+ body = '\n'.join("%s: %s" % (k, v) for k, v in six.iteritems(kwargs))
1668 config.push(name, "\n[%s]\n%s\n" % (section, body))
1669 self.addCleanup(config.pop, name)
1670
1671@@ -1527,13 +1528,13 @@ def monkey_patch(context, **kwargs):
1672 """
1673 old_values = {}
1674 not_set = object()
1675- for name, value in kwargs.iteritems():
1676+ for name, value in six.iteritems(kwargs):
1677 old_values[name] = getattr(context, name, not_set)
1678 setattr(context, name, value)
1679 try:
1680 yield
1681 finally:
1682- for name, value in old_values.iteritems():
1683+ for name, value in six.iteritems(old_values):
1684 if value is not_set:
1685 delattr(context, name)
1686 else:
1687diff --git a/lib/lp/testing/swift/fakeswift.py b/lib/lp/testing/swift/fakeswift.py
1688index 0712e7a..c90dc0d 100644
1689--- a/lib/lp/testing/swift/fakeswift.py
1690+++ b/lib/lp/testing/swift/fakeswift.py
1691@@ -22,6 +22,7 @@ import sys
1692 import time
1693 import uuid
1694
1695+import six
1696 from twisted.web import (
1697 http,
1698 resource,
1699@@ -77,7 +78,7 @@ class FakeKeystone(resource.Resource):
1700 if self._isValidToken(token, tenant_name):
1701 return token
1702 else:
1703- for id, token in self.tokens.iteritems():
1704+ for id, token in six.iteritems(self.tokens):
1705 if self._isValidToken(token, tenant_name):
1706 return token
1707
1708diff --git a/lib/lp/translations/browser/translationlinksaggregator.py b/lib/lp/translations/browser/translationlinksaggregator.py
1709index 3f196bc..4b06509 100644
1710--- a/lib/lp/translations/browser/translationlinksaggregator.py
1711+++ b/lib/lp/translations/browser/translationlinksaggregator.py
1712@@ -7,6 +7,8 @@ __all__ = [
1713 'TranslationLinksAggregator',
1714 ]
1715
1716+import six
1717+
1718 from lp.services.webapp import canonical_url
1719 from lp.translations.interfaces.pofile import IPOFile
1720 from lp.translations.model.productserieslanguage import ProductSeriesLanguage
1721@@ -177,10 +179,10 @@ class TranslationLinksAggregator:
1722 returns for the sensible chunks.
1723 """
1724 links = []
1725- for target, sheets in self._bundle(sheets).iteritems():
1726+ for target, sheets in six.iteritems(self._bundle(sheets)):
1727 assert sheets, "Translation target has no POFiles or templates."
1728 links_and_sheets = self._circumscribe(sheets)
1729- for link, covered_sheets in links_and_sheets.iteritems():
1730+ for link, covered_sheets in six.iteritems(links_and_sheets):
1731 links.append(self.describe(target, link, covered_sheets))
1732
1733 return links
1734diff --git a/lib/lp/translations/browser/translationmessage.py b/lib/lp/translations/browser/translationmessage.py
1735index ef75f6c..dbdf1d1 100644
1736--- a/lib/lp/translations/browser/translationmessage.py
1737+++ b/lib/lp/translations/browser/translationmessage.py
1738@@ -23,6 +23,7 @@ import operator
1739 import re
1740
1741 import pytz
1742+import six
1743 from six.moves.urllib.parse import (
1744 parse_qsl,
1745 urlencode,
1746@@ -96,7 +97,7 @@ def revert_unselected_translations(translations, current_message,
1747 original_translations = dict(enumerate(current_message.translations))
1748
1749 output = {}
1750- for plural_form, translation in translations.iteritems():
1751+ for plural_form, translation in six.iteritems(translations):
1752 if plural_form in plural_indices_to_store:
1753 output[plural_form] = translation
1754 elif original_translations.get(plural_form) is None:
1755@@ -113,7 +114,7 @@ def contains_translations(translations):
1756 :param translations: a dict mapping plural forms to their respective
1757 translation strings.
1758 """
1759- for text in translations.itervalues():
1760+ for text in six.itervalues(translations):
1761 if text is not None and len(text) != 0:
1762 return True
1763 return False
1764diff --git a/lib/lp/translations/doc/translationimportqueue.txt b/lib/lp/translations/doc/translationimportqueue.txt
1765index 9f7d8c9..c62423b 100644
1766--- a/lib/lp/translations/doc/translationimportqueue.txt
1767+++ b/lib/lp/translations/doc/translationimportqueue.txt
1768@@ -1223,9 +1223,11 @@ bug 138650 for an example).
1769 If such bad requests do end up on the import queue, the import queue code will
1770 raise errors about them.
1771
1772+ >>> import six
1773+
1774 >>> def print_import_failures(import_script):
1775 ... """List failures recorded in an import script instance."""
1776- ... for reason, entries in script.failures.iteritems():
1777+ ... for reason, entries in six.iteritems(script.failures):
1778 ... print(reason)
1779 ... for entry in entries:
1780 ... print("-> " + entry)
1781diff --git a/lib/lp/translations/model/potemplate.py b/lib/lp/translations/model/potemplate.py
1782index ba0a632..70dedd2 100644
1783--- a/lib/lp/translations/model/potemplate.py
1784+++ b/lib/lp/translations/model/potemplate.py
1785@@ -19,6 +19,7 @@ import operator
1786 import os
1787
1788 from psycopg2.extensions import TransactionRollbackError
1789+import six
1790 from sqlobject import (
1791 BoolCol,
1792 ForeignKey,
1793@@ -1560,7 +1561,7 @@ class POTemplateSharingSubset(object):
1794 equivalents[key] = []
1795 equivalents[key].append(template)
1796
1797- for equivalence_list in equivalents.itervalues():
1798+ for equivalence_list in six.itervalues(equivalents):
1799 # Sort potemplates from "most representative" to "least
1800 # representative."
1801 equivalence_list.sort(key=POTemplate.sharingKey, reverse=True)
1802diff --git a/lib/lp/translations/model/potmsgset.py b/lib/lp/translations/model/potmsgset.py
1803index 12f9f05..f256384 100644
1804--- a/lib/lp/translations/model/potmsgset.py
1805+++ b/lib/lp/translations/model/potmsgset.py
1806@@ -14,6 +14,7 @@ from collections import (
1807 import logging
1808 import re
1809
1810+import six
1811 from sqlobject import (
1812 ForeignKey,
1813 SQLObjectNotFound,
1814@@ -122,7 +123,7 @@ def dictify_translations(translations):
1815 # Filter out None values.
1816 return dict(
1817 (form, translation)
1818- for form, translation in translations.iteritems()
1819+ for form, translation in six.iteritems(translations)
1820 if translation is not None)
1821
1822
1823@@ -611,7 +612,7 @@ class POTMsgSet(SQLBase):
1824
1825 forms = dict(
1826 ('msgstr%d' % form, potranslation)
1827- for form, potranslation in potranslations.iteritems())
1828+ for form, potranslation in six.iteritems(potranslations))
1829
1830 if from_import:
1831 origin = RosettaTranslationOrigin.SCM
1832@@ -748,7 +749,7 @@ class POTMsgSet(SQLBase):
1833
1834 translation_args = dict(
1835 ('msgstr%d' % form, translation)
1836- for form, translation in translations.iteritems())
1837+ for form, translation in six.iteritems(translations))
1838
1839 return TranslationMessage(
1840 potmsgset=self,
1841diff --git a/lib/lp/translations/model/translationimportqueue.py b/lib/lp/translations/model/translationimportqueue.py
1842index 3ded490..483a4c3 100644
1843--- a/lib/lp/translations/model/translationimportqueue.py
1844+++ b/lib/lp/translations/model/translationimportqueue.py
1845@@ -19,6 +19,7 @@ from textwrap import dedent
1846
1847 import posixpath
1848 import pytz
1849+import six
1850 from sqlobject import (
1851 BoolCol,
1852 ForeignKey,
1853@@ -1457,7 +1458,8 @@ class TranslationImportQueue:
1854 """
1855 now = datetime.datetime.now(pytz.UTC)
1856 deletion_clauses = []
1857- for status, max_age in translation_import_queue_entry_age.iteritems():
1858+ for status, max_age in six.iteritems(
1859+ translation_import_queue_entry_age):
1860 cutoff = now - max_age
1861 deletion_clauses.append(And(
1862 TranslationImportQueueEntry.status == status,
1863diff --git a/lib/lp/translations/model/translationsharingjob.py b/lib/lp/translations/model/translationsharingjob.py
1864index 8a60008..313e8db 100644
1865--- a/lib/lp/translations/model/translationsharingjob.py
1866+++ b/lib/lp/translations/model/translationsharingjob.py
1867@@ -170,7 +170,7 @@ class TranslationSharingJobDerived(
1868 for.
1869 :param event: The event itself.
1870 """
1871- for event_type, job_classes in cls._event_types.iteritems():
1872+ for event_type, job_classes in six.iteritems(cls._event_types):
1873 if not event_type.providedBy(event):
1874 continue
1875 for job_class in job_classes:
1876@@ -191,7 +191,7 @@ class TranslationSharingJobDerived(
1877 # Ignore changes to POTemplates that are neither renames,
1878 # nor moves to a different package/project.
1879 return
1880- for event_type, job_classes in cls._event_types.iteritems():
1881+ for event_type, job_classes in six.iteritems(cls._event_types):
1882 if not event_type.providedBy(event):
1883 continue
1884 for job_class in job_classes:
1885diff --git a/lib/lp/translations/scripts/po_import.py b/lib/lp/translations/scripts/po_import.py
1886index 3e97f8c..9c9779e 100644
1887--- a/lib/lp/translations/scripts/po_import.py
1888+++ b/lib/lp/translations/scripts/po_import.py
1889@@ -17,6 +17,7 @@ from datetime import (
1890 import sys
1891
1892 import pytz
1893+import six
1894 from zope.component import getUtility
1895
1896 from lp.app.interfaces.launchpad import ILaunchpadCelebrities
1897@@ -212,5 +213,5 @@ class TranslationsImport(LaunchpadCronScript):
1898
1899 def _reportFailures(self):
1900 """Bulk-report deferred failures as oopses."""
1901- for reason, entries in self.failures.iteritems():
1902+ for reason, entries in six.iteritems(self.failures):
1903 self._reportOops(reason, entries)
1904diff --git a/lib/lp/translations/scripts/tests/test_reupload_translations.py b/lib/lp/translations/scripts/tests/test_reupload_translations.py
1905index f2a449f..2ec0cee 100644
1906--- a/lib/lp/translations/scripts/tests/test_reupload_translations.py
1907+++ b/lib/lp/translations/scripts/tests/test_reupload_translations.py
1908@@ -11,6 +11,7 @@ import re
1909 from StringIO import StringIO
1910 import tarfile
1911
1912+import six
1913 import transaction
1914 from zope.security.proxy import removeSecurityProxy
1915
1916@@ -61,7 +62,7 @@ def upload_tarball(translation_files):
1917 """
1918 buf = StringIO()
1919 tarball = tarfile.open('', 'w:gz', buf)
1920- for name, contents in translation_files.iteritems():
1921+ for name, contents in six.iteritems(translation_files):
1922 pseudofile = StringIO(contents)
1923 tarinfo = tarfile.TarInfo()
1924 tarinfo.name = name
1925@@ -93,7 +94,7 @@ def filter_paths(files_dict):
1926 applied to each file's path, and non-Ubuntu files left out.
1927 """
1928 filtered_dict = {}
1929- for original_path, content in files_dict.iteritems():
1930+ for original_path, content in six.iteritems(files_dict):
1931 new_path = _filter_ubuntu_translation_file(original_path)
1932 if new_path:
1933 filtered_dict[new_path] = content
1934diff --git a/lib/lp/translations/scripts/tests/test_translations_to_branch.py b/lib/lp/translations/scripts/tests/test_translations_to_branch.py
1935index 248b2e0..0cd058a 100644
1936--- a/lib/lp/translations/scripts/tests/test_translations_to_branch.py
1937+++ b/lib/lp/translations/scripts/tests/test_translations_to_branch.py
1938@@ -9,6 +9,7 @@ from textwrap import dedent
1939
1940 from breezy.errors import NotBranchError
1941 import pytz
1942+import six
1943 from testtools.matchers import MatchesRegex
1944 import transaction
1945 from zope.component import getUtility
1946@@ -116,8 +117,8 @@ class TestExportTranslationsToBranch(TestCaseWithFactory):
1947 msgstr "Hallo Wereld"\n""",
1948 }
1949
1950- branch_filenames = set(branch_contents.iterkeys())
1951- expected_filenames = set(expected_contents.iterkeys())
1952+ branch_filenames = set(branch_contents)
1953+ expected_filenames = set(expected_contents)
1954
1955 unexpected_filenames = branch_filenames - expected_filenames
1956 self.assertEqual(set(), unexpected_filenames)
1957@@ -125,7 +126,7 @@ class TestExportTranslationsToBranch(TestCaseWithFactory):
1958 missing_filenames = expected_filenames - branch_filenames
1959 self.assertEqual(set(), missing_filenames)
1960
1961- for filename, expected in expected_contents.iteritems():
1962+ for filename, expected in six.iteritems(expected_contents):
1963 contents = branch_contents[filename].lstrip('\n')
1964 pattern = dedent(expected.lstrip('\n'))
1965 if not re.match(pattern, contents, re.MULTILINE):
1966diff --git a/lib/lp/translations/stories/webservice/xx-translationimportqueue.txt b/lib/lp/translations/stories/webservice/xx-translationimportqueue.txt
1967index ca3f633..4b668ed 100644
1968--- a/lib/lp/translations/stories/webservice/xx-translationimportqueue.txt
1969+++ b/lib/lp/translations/stories/webservice/xx-translationimportqueue.txt
1970@@ -29,7 +29,7 @@ to be cleaned up.
1971 ... shown. If omitted, all keys are shown.
1972 ... """
1973 ... print('Entry:')
1974- ... for key in sorted(a_dict.iterkeys()):
1975+ ... for key in sorted(a_dict):
1976 ... if shown_keys is None or key in shown_keys:
1977 ... print('', key, a_dict[key])
1978
1979diff --git a/lib/lp/translations/tests/test_potemplate.py b/lib/lp/translations/tests/test_potemplate.py
1980index 4ebb735..d08826e 100644
1981--- a/lib/lp/translations/tests/test_potemplate.py
1982+++ b/lib/lp/translations/tests/test_potemplate.py
1983@@ -5,6 +5,7 @@ __metaclass__ = type
1984
1985 from operator import methodcaller
1986
1987+import six
1988 from zope.component import getUtility
1989 from zope.security.proxy import removeSecurityProxy
1990
1991@@ -193,8 +194,8 @@ class EquivalenceClassTestMixin:
1992 This ignores the ordering of templates in an equivalence class.
1993 A separate test looks at ordering.
1994 """
1995- self.assertEqual(set(actual.iterkeys()), set(expected.iterkeys()))
1996- for key, value in actual.iteritems():
1997+ self.assertEqual(set(actual), set(expected))
1998+ for key, value in six.iteritems(actual):
1999 self.assertEqual(set(value), set(expected[key]))
2000
2001
2002diff --git a/lib/lp/translations/tests/test_side.py b/lib/lp/translations/tests/test_side.py
2003index f5807d0..c7ab892 100644
2004--- a/lib/lp/translations/tests/test_side.py
2005+++ b/lib/lp/translations/tests/test_side.py
2006@@ -5,6 +5,7 @@
2007
2008 __metaclass__ = type
2009
2010+import six
2011 from zope.component import getUtility
2012 from zope.interface.verify import verifyObject
2013
2014@@ -23,7 +24,7 @@ class TestTranslationSideTraitsSet(TestCaseWithFactory):
2015 def test_baseline(self):
2016 utility = getUtility(ITranslationSideTraitsSet)
2017 self.assertTrue(verifyObject(ITranslationSideTraitsSet, utility))
2018- for traits in utility.getAllTraits().itervalues():
2019+ for traits in six.itervalues(utility.getAllTraits()):
2020 self.assertTrue(verifyObject(ITranslationSideTraits, traits))
2021
2022 def test_other_sides(self):
2023@@ -64,7 +65,7 @@ class TestTranslationSideTraitsSet(TestCaseWithFactory):
2024 [TranslationSide.UPSTREAM, TranslationSide.UBUNTU],
2025 traits_dict.keys())
2026
2027- for side, traits in traits_dict.iteritems():
2028+ for side, traits in six.iteritems(traits_dict):
2029 self.assertEqual(side, traits.side)
2030 self.assertEqual(traits, utility.getTraits(side))
2031
2032diff --git a/lib/lp/translations/utilities/gettext_po_parser.py b/lib/lp/translations/utilities/gettext_po_parser.py
2033index c087ef9..78de456 100644
2034--- a/lib/lp/translations/utilities/gettext_po_parser.py
2035+++ b/lib/lp/translations/utilities/gettext_po_parser.py
2036@@ -20,6 +20,7 @@ import logging
2037 import re
2038
2039 import pytz
2040+import six
2041 from zope import datetime as zope_datetime
2042 from zope.interface import implementer
2043
2044@@ -230,7 +231,7 @@ class POHeader:
2045
2046 def _parseHeaderFields(self):
2047 """Return plural form values based on the parsed header."""
2048- for key, value in self._header_dictionary.iteritems():
2049+ for key, value in six.iteritems(self._header_dictionary):
2050 if key == 'plural-forms':
2051 parts = self._parseAssignments(value)
2052 nplurals = parts.get('nplurals')
2053@@ -362,7 +363,7 @@ class POHeader:
2054 raise AssertionError('key %s is not being handled!' % value)
2055
2056 # Now, we copy any other header information in the original .po file.
2057- for key, value in self._header_dictionary.iteritems():
2058+ for key, value in six.iteritems(self._header_dictionary):
2059 if key in self._handled_keys_mapping:
2060 # It's already handled, skip it.
2061 continue
2062diff --git a/lib/lp/translations/utilities/pluralforms.py b/lib/lp/translations/utilities/pluralforms.py
2063index 680ba14..8a82685 100644
2064--- a/lib/lp/translations/utilities/pluralforms.py
2065+++ b/lib/lp/translations/utilities/pluralforms.py
2066@@ -12,6 +12,8 @@ __all__ = [
2067 import gettext
2068 import re
2069
2070+import six
2071+
2072 from lp.translations.interfaces.translations import TranslationConstants
2073
2074
2075@@ -47,7 +49,7 @@ def make_friendly_plural_forms(expression, expected_forms):
2076
2077 return [
2078 {'form': form, 'examples': examples}
2079- for (form, examples) in forms.iteritems()
2080+ for (form, examples) in six.iteritems(forms)
2081 ]
2082
2083
2084diff --git a/lib/lp/translations/utilities/tests/test_translation_importer.py b/lib/lp/translations/utilities/tests/test_translation_importer.py
2085index dcf203b..2d47d01 100644
2086--- a/lib/lp/translations/utilities/tests/test_translation_importer.py
2087+++ b/lib/lp/translations/utilities/tests/test_translation_importer.py
2088@@ -7,6 +7,7 @@ __metaclass__ = type
2089
2090 from io import BytesIO
2091
2092+import six
2093 import transaction
2094
2095 from lp.services.log.logger import DevNullLogger
2096@@ -96,7 +97,7 @@ class TranslationImporterTestCase(TestCaseWithFactory):
2097 exactly the same priority."""
2098 for file_extension in TranslationImporter().supported_file_extensions:
2099 priorities = []
2100- for format, importer in importers.iteritems():
2101+ for format, importer in six.iteritems(importers):
2102 if file_extension in importer.file_extensions:
2103 self.assertNotIn(importer.priority, priorities)
2104 priorities.append(importer.priority)
2105diff --git a/lib/lp/translations/utilities/translation_import.py b/lib/lp/translations/utilities/translation_import.py
2106index 197153d..f9af932 100644
2107--- a/lib/lp/translations/utilities/translation_import.py
2108+++ b/lib/lp/translations/utilities/translation_import.py
2109@@ -14,6 +14,7 @@ from operator import attrgetter
2110
2111 import posixpath
2112 import pytz
2113+import six
2114 from storm.exceptions import TimeoutError
2115 import transaction
2116 from zope.component import getUtility
2117@@ -274,7 +275,7 @@ class TranslationImporter:
2118 """See `ITranslationImporter`."""
2119 file_extensions = []
2120
2121- for importer in importers.itervalues():
2122+ for importer in six.itervalues(importers):
2123 file_extensions.extend(importer.file_extensions)
2124
2125 return sorted(set(file_extensions))
2126@@ -290,7 +291,7 @@ class TranslationImporter:
2127
2128 def isTemplateName(self, path):
2129 """See `ITranslationImporter`."""
2130- for importer in importers.itervalues():
2131+ for importer in six.itervalues(importers):
2132 if path.endswith(importer.template_suffix):
2133 return True
2134 return False
2135diff --git a/lib/lp/translations/utilities/translationmerger.py b/lib/lp/translations/utilities/translationmerger.py
2136index 044d058..b507fb6 100644
2137--- a/lib/lp/translations/utilities/translationmerger.py
2138+++ b/lib/lp/translations/utilities/translationmerger.py
2139@@ -283,7 +283,7 @@ class MessageSharingMerge(LaunchpadScript):
2140 log.info("Merging %d template equivalence classes." % class_count)
2141
2142 tm = TransactionManager(self.txn, self.options.dry_run)
2143- for number, name in enumerate(sorted(equivalence_classes.iterkeys())):
2144+ for number, name in enumerate(sorted(equivalence_classes)):
2145 templates = equivalence_classes[name]
2146 log.info(
2147 "Merging equivalence class '%s': %d template(s) (%d / %d)" % (
2148@@ -429,7 +429,7 @@ class TranslationMerger:
2149
2150 self.tm.endTransaction(intermediate=True)
2151
2152- for representative_id in representatives.itervalues():
2153+ for representative_id in six.itervalues(representatives):
2154 representative = POTMsgSet.get(representative_id)
2155 self._scrubPOTMsgSetTranslations(representative)
2156 self.tm.endTransaction(intermediate=True)
2157@@ -484,7 +484,7 @@ class TranslationMerger:
2158 num_representatives = len(subordinates)
2159 representative_num = 0
2160
2161- for representative, potmsgsets in subordinates.iteritems():
2162+ for representative, potmsgsets in six.iteritems(subordinates):
2163 representative_num += 1
2164 log.debug("Message %d/%d: %d subordinate(s)." % (
2165 representative_num, num_representatives, len(potmsgsets)))
2166diff --git a/lib/lp/translations/utilities/validate.py b/lib/lp/translations/utilities/validate.py
2167index 0b9e8df..371a060 100644
2168--- a/lib/lp/translations/utilities/validate.py
2169+++ b/lib/lp/translations/utilities/validate.py
2170@@ -9,6 +9,7 @@ __all__ = [
2171 ]
2172
2173 import gettextpo
2174+import six
2175
2176
2177 class GettextValidationError(ValueError):
2178@@ -37,7 +38,7 @@ def validate_translation(original_singular, original_plural,
2179 else:
2180 # Message with plural forms.
2181 msg.set_msgid_plural(original_plural)
2182- for form, translation in translations.iteritems():
2183+ for form, translation in six.iteritems(translations):
2184 msg.set_msgstr_plural(form, translation)
2185
2186 for flag in flags:
2187diff --git a/utilities/findimports.py b/utilities/findimports.py
2188index b5615a8..09e0cf1 100755
2189--- a/utilities/findimports.py
2190+++ b/utilities/findimports.py
2191@@ -46,6 +46,8 @@ import linecache
2192 import os
2193 import sys
2194
2195+import six
2196+
2197
2198 class ImportFinder(ASTVisitor):
2199 """AST visitor that collects all imported names in its imports attribute.
2200@@ -285,7 +287,7 @@ class ModuleGraph(object):
2201 def printUnusedImports(self):
2202 for module in self.listModules():
2203 names = [(unused.lineno, unused.name)
2204- for unused in module.unused_names.itervalues()]
2205+ for unused in six.itervalues(module.unused_names)]
2206 names.sort()
2207 for lineno, name in names:
2208 if not self.all_unused:
2209diff --git a/utilities/generate-external-bug-status-docs b/utilities/generate-external-bug-status-docs
2210index a2d28e8..43e0263 100755
2211--- a/utilities/generate-external-bug-status-docs
2212+++ b/utilities/generate-external-bug-status-docs
2213@@ -26,6 +26,8 @@ from itertools import chain
2214 from optparse import OptionParser
2215 import sys
2216
2217+import six
2218+
2219 from lp.bugs.externalbugtracker import BUG_TRACKER_CLASSES
2220
2221
2222@@ -85,7 +87,7 @@ def generate_table(typ, cls):
2223
2224 def generate_documentable_classes():
2225 """Yield each class that has a mapping table defined."""
2226- for typ, cls in BUG_TRACKER_CLASSES.iteritems():
2227+ for typ, cls in six.iteritems(BUG_TRACKER_CLASSES):
2228 if getattr(cls, '_status_lookup', None) is not None:
2229 yield typ, cls
2230
2231diff --git a/utilities/list-pages b/utilities/list-pages
2232index 9989cf6..bf08d0a 100755
2233--- a/utilities/list-pages
2234+++ b/utilities/list-pages
2235@@ -49,6 +49,7 @@ import _pythonpath
2236 from inspect import getmro
2237 import os
2238
2239+import six
2240 from zope.app.wsgi.testlayer import BrowserLayer
2241 from zope.browserpage.simpleviewclass import simple
2242 from zope.component import adapter, getGlobalSiteManager
2243@@ -191,7 +192,7 @@ class Whatever(object):
2244
2245 def __call__(self, *args, **kwargs):
2246 args = map(repr, args)
2247- args.extend('%s=%r' % (k, v) for k, v in kwargs.iteritems())
2248+ args.extend('%s=%r' % (k, v) for k, v in six.iteritems(kwargs))
2249 # If we're being called with no args, assume this is part of crazy
2250 # TALES stuff:
2251 # webapp/metazcml.py(365)path()

Subscribers

People subscribed via source and target branches

to status/vote changes: