Merge lp:~mvo/ubuntu-release-upgrader/use-str.format into lp:ubuntu-release-upgrader

Proposed by Michael Vogt on 2014-04-24
Status: Rejected
Rejected by: Michael Vogt on 2014-06-13
Proposed branch: lp:~mvo/ubuntu-release-upgrader/use-str.format
Merge into: lp:ubuntu-release-upgrader
Diff against target: 888 lines (+175/-142)
14 files modified
DistUpgrade/DistUpgradeAptCdrom.py (+1/-1)
DistUpgrade/DistUpgradeCache.py (+10/-10)
DistUpgrade/DistUpgradeController.py (+39/-38)
DistUpgrade/DistUpgradeFetcherCore.py (+5/-5)
DistUpgrade/DistUpgradeFetcherKDE.py (+4/-3)
DistUpgrade/DistUpgradeGettext.py (+7/-2)
DistUpgrade/DistUpgradeView.py (+27/-24)
DistUpgrade/DistUpgradeViewGtk3.py (+25/-21)
DistUpgrade/DistUpgradeViewKDE.py (+23/-15)
DistUpgrade/DistUpgradeViewText.py (+10/-5)
DistUpgrade/GtkProgress.py (+8/-8)
check-new-release-gtk (+8/-7)
do-partial-upgrade (+3/-3)
pre-build.sh (+5/-0)
To merge this branch: bzr merge lp:~mvo/ubuntu-release-upgrader/use-str.format
Reviewer Review Type Date Requested Status
Ubuntu Core Development Team 2014-04-24 Pending
Review via email: mp+217004@code.launchpad.net

Description of the change

Use str.format() with gettext (eventually everywhere) to avoid crashes if e.g. incorrect translations are specified. The worst case with str.format() is that the information is not displayed correctly, but it won't crash like e.g. LP: #1311396

To post a comment you must log in.
Michael Terry (mterry) wrote :

Maybe str.format is better for other reasons, but you can certainly still get tracebacks from it:

$ python3 -c "'hello {0'.format('world')"
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: expected '}' before end of string

Michael Vogt (mvo) wrote :

Hi Michael, thanks for your comment. Indeed, I should have been more clear.

The issue that is fixed is:

$ python -c 'print("foo" % "bar")'
Traceback (most recent call last):
  File "<string>", line 1, in <module>
TypeError: not all arguments converted during string formatting

$ python -c 'print("foo".format("bar"))'
foo

But yes, if a translation has a incorrect number of {} it will still crash, so maybe its not worth
doing all the work and instead use the "Foo %(bar)s" % { "bar": "some-str" } approach where its
also ok to leave out a argument. I.e.
$ python -c 'print("foo" % {"bar": "bar"})'
foo

Barry Warsaw (barry) wrote :

It might be a lot of work to port, but I will suggest using my flufl.i18n package, which has a lot of nice convenience APIs and safeguards for doing i18n interpolation.

http://pythonhosted.org/flufl.i18n/docs/using.html

And of course, it's packaged up :).

Barry Warsaw (barry) wrote :

Also note that with modern Pythons

'{0}'.format('foo')

is equivalent to

'{}'.format('foo')

Unmerged revisions

2779. By Michael Vogt on 2014-04-24

replace all %s style format with str.format() in gettext to be more robust against crashes from incorrectly formated translations

2778. By Michael Vogt on 2014-04-24

use str.format() for all ngettext() to avoid crashes on incorrect number of parameters (LP: #1311396

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'DistUpgrade/DistUpgradeAptCdrom.py'
2--- DistUpgrade/DistUpgradeAptCdrom.py 2012-06-12 13:52:04 +0000
3+++ DistUpgrade/DistUpgradeAptCdrom.py 2014-04-24 08:46:39 +0000
4@@ -295,7 +295,7 @@
5 _("There was a error adding the CD, the "
6 "upgrade will abort. Please report this as "
7 "a bug if this is a valid Ubuntu CD.\n\n"
8- "The error message was:\n'%s'") % e)
9+ "The error message was:\n'{0}'").format(e))
10 return False
11 logging.debug("AptCdrom.add() returned: %s" % res)
12 return res
13
14=== modified file 'DistUpgrade/DistUpgradeCache.py'
15--- DistUpgrade/DistUpgradeCache.py 2014-04-15 08:57:53 +0000
16+++ DistUpgrade/DistUpgradeCache.py 2014-04-24 08:46:39 +0000
17@@ -152,17 +152,17 @@
18 header = ngettext("Remove package in bad state",
19 "Remove packages in bad state",
20 len(reqreinst))
21- summary = ngettext("The package '%s' is in an inconsistent "
22+ summary = ngettext("The package '{0}' is in an inconsistent "
23 "state and needs to be reinstalled, but "
24 "no archive can be found for it. "
25 "Do you want to remove this package "
26 "now to continue?",
27- "The packages '%s' are in an inconsistent "
28+ "The packages '{0}' are in an inconsistent "
29 "state and need to be reinstalled, but "
30 "no archives can be found for them. Do you "
31 "want to remove these packages now to "
32 "continue?",
33- len(reqreinst)) % ", ".join(reqreinst)
34+ len(reqreinst)).format(", ".join(reqreinst))
35 if view.askYesNoQuestion(header, summary):
36 self.release_lock()
37 cmd = ["/usr/bin/dpkg", "--remove", "--force-remove-reinstreq"] + list(reqreinst)
38@@ -727,13 +727,13 @@
39 for pkg in self.get_changes():
40 if pkg.marked_delete and self._inRemovalBlacklist(pkg.name):
41 logging.debug("The package '%s' is marked for removal but it's in the removal blacklist", pkg.name)
42- raise SystemError(_("The package '%s' is marked for removal but it is in the removal blacklist.") % pkg.name)
43+ raise SystemError(_("The package '{0}' is marked for removal but it is in the removal blacklist.").format(pkg.name))
44 if pkg.marked_delete and (
45 pkg._pkg.essential == True and
46 pkg.installed.architecture == main_arch and
47 not pkg.name in removeEssentialOk):
48 logging.debug("The package '%s' is marked for removal but it's an ESSENTIAL package", pkg.name)
49- raise SystemError(_("The essential package '%s' is marked for removal.") % pkg.name)
50+ raise SystemError(_("The essential package '{0}' is marked for removal.").format(pkg.name))
51 # check bad-versions blacklist
52 badVersions = self.config.getlist("Distro", "BadVersions")
53 for bv in badVersions:
54@@ -742,7 +742,7 @@
55 self[pkgname].candidate.version == ver and
56 (self[pkgname].marked_install or
57 self[pkgname].marked_upgrade)):
58- raise SystemError(_("Trying to install blacklisted version '%s'") % bv)
59+ raise SystemError(_("Trying to install blacklisted version '{0}'").format(bv))
60 return True
61
62 def _lookupPkgRecord(self, pkg):
63@@ -861,7 +861,7 @@
64 except (SystemError, KeyError) as e:
65 logging.error("failed to mark '%s' for install (%s)" %
66 (key, e))
67- view.error(_("Can't install '%s'") % key,
68+ view.error(_("Can't install '{0}'").format(key),
69 _("It was impossible to install a "
70 "required package. Please report "
71 "this as a bug using "
72@@ -875,13 +875,13 @@
73 meta_pkgs = ', '.join(metapkgs[0:-1])
74 view.error(_("Can't guess meta-package"),
75 _("Your system does not contain a "
76- "%s or %s package and it was not "
77+ "{0} or {1} package and it was not "
78 "possible to detect which version of "
79 "Ubuntu you are running.\n "
80 "Please install one of the packages "
81 "above first using synaptic or "
82- "apt-get before proceeding.") %
83- (meta_pkgs, metapkgs[-1]))
84+ "apt-get before proceeding.").format(
85+ meta_pkgs, metapkgs[-1]))
86 return False
87 return True
88
89
90=== modified file 'DistUpgrade/DistUpgradeController.py'
91--- DistUpgrade/DistUpgradeController.py 2014-04-11 22:30:39 +0000
92+++ DistUpgrade/DistUpgradeController.py 2014-04-24 08:46:39 +0000
93@@ -307,8 +307,8 @@
94 "over ssh currently because in case of failure "
95 "it is harder to recover.\n\n"
96 "If you continue, an additional ssh daemon will be "
97- "started at port '%s'.\n"
98- "Do you want to continue?") % port)
99+ "started at port '{0}'.\n"
100+ "Do you want to continue?").format(port))
101 # abort
102 if res == False:
103 sys.exit(1)
104@@ -318,10 +318,10 @@
105 if res == 0:
106 summary = _("Starting additional sshd")
107 descr = _("To make recovery in case of failure easier, an "
108- "additional sshd will be started on port '%s'. "
109+ "additional sshd will be started on port '{0}'. "
110 "If anything goes wrong with the running ssh "
111 "you can still connect to the additional one.\n"
112- ) % port
113+ ).format(port)
114 if iptables_active():
115 cmd = "iptables -I INPUT -p tcp --dport %s -j ACCEPT" % port
116 descr += _(
117@@ -398,8 +398,9 @@
118 if not (release == self.fromDist or release == self.toDist):
119 logging.error("Bad upgrade: '%s' != '%s' " % (release, self.fromDist))
120 self._view.error(_("Can not upgrade"),
121- _("An upgrade from '%s' to '%s' is not "
122- "supported with this tool." % (release, self.toDist)))
123+ _("An upgrade from '{0}' to '{1}' is not "
124+ "supported with this tool.").format(
125+ release, self.toDist))
126 sys.exit(1)
127
128 # setup aufs
129@@ -417,11 +418,11 @@
130 self._view.information(_("Sandbox mode"),
131 _("This upgrade is running in sandbox "
132 "(test) mode. All changes are written "
133- "to '%s' and will be lost on the next "
134+ "to '{0}' and will be lost on the next "
135 "reboot.\n\n"
136 "*No* changes written to a system directory "
137 "from now until the next reboot are "
138- "permanent.") % aufs_rw_dir)
139+ "permanent.").format(aufs_rw_dir))
140
141 # setup backports (if we have them)
142 if self.options and self.options.havePrerequists:
143@@ -482,12 +483,12 @@
144 if os.path.exists(systemdir) and not os.access(systemdir, os.W_OK):
145 logging.error("%s not writable" % systemdir)
146 self._view.error(
147- _("Can not write to '%s'") % systemdir,
148+ _("Can not write to '{0}'").format(systemdir),
149 _("Its not possible to write to the system directory "
150- "'%s' on your system. The upgrade can not "
151+ "'{0}' on your system. The upgrade can not "
152 "continue.\n"
153 "Please make sure that the system directory is "
154- "writable.") % systemdir)
155+ "writable.").format(systemdir))
156 self.abort()
157
158
159@@ -640,7 +641,7 @@
160 "%s-proposed" % self.fromDist in entry.dist):
161 logging.debug("upgrade to development release, disabling proposed")
162 entry.dist = "%s-proposed" % self.toDist
163- entry.comment += _("Not for humans during development stage of release %s") % self.toDist
164+ entry.comment += _("Not for humans during development stage of release {0}").format(self.toDist)
165 entry.disabled = True
166 continue
167
168@@ -726,7 +727,7 @@
169 # disable anything that is not from a official mirror or a whitelisted third party
170 if entry.dist == self.fromDist:
171 entry.dist = self.toDist
172- disable_comment = " " + _("disabled on upgrade to %s") % self.toDist
173+ disable_comment = " " + _("disabled on upgrade to {0}").format(self.toDist)
174 if isinstance(entry.comment, bytes):
175 entry.comment += disable_comment.encode('UTF-8')
176 else:
177@@ -770,10 +771,10 @@
178 "out of date.\n\n"
179 "Do you want to rewrite your "
180 "'sources.list' file anyway? If you choose "
181- "'Yes' here it will update all '%s' to '%s' "
182+ "'Yes' here it will update all '{0}' to '{1}' "
183 "entries.\n"
184 "If you select 'No' the upgrade will cancel."
185- ) % (self.fromDist, self.toDist))
186+ ).format(self.fromDist, self.toDist))
187 if res:
188 # re-init the sources and try again
189 self.sources = SourcesList(matcherPath=".")
190@@ -784,10 +785,10 @@
191 #hm, still nothing useful ...
192 prim = _("Generate default sources?")
193 secon = _("After scanning your 'sources.list' no "
194- "valid entry for '%s' was found.\n\n"
195- "Should default entries for '%s' be "
196+ "valid entry for '{0}' was found.\n\n"
197+ "Should default entries for '{1}' be "
198 "added? If you select 'No', the upgrade "
199- "will cancel.") % (self.fromDist, self.toDist)
200+ "will cancel.").format(self.fromDist, self.toDist)
201 if not self._view.askYesNoQuestion(prim, secon):
202 self.abort()
203
204@@ -880,17 +881,17 @@
205 header = ngettext("Package in inconsistent state",
206 "Packages in inconsistent state",
207 len(reqreinst))
208- summary = ngettext("The package '%s' is in an inconsistent "
209+ summary = ngettext("The package '{0}' is in an inconsistent "
210 "state and needs to be reinstalled, but "
211 "no archive can be found for it. "
212 "Please reinstall the package manually "
213 "or remove it from the system.",
214- "The packages '%s' are in an inconsistent "
215+ "The packages '{0}' are in an inconsistent "
216 "state and need to be reinstalled, but "
217 "no archive can be found for them. "
218 "Please reinstall the packages manually "
219 "or remove them from the system.",
220- len(reqreinst)) % ", ".join(reqreinst)
221+ len(reqreinst)).format(", ".join(reqreinst))
222 self._view.error(header, summary)
223 return False
224 # FIXME: check out what packages are downloadable etc to
225@@ -899,8 +900,8 @@
226 self.foreign_pkgs = self.cache._getForeignPkgs(self.origin, self.fromDist, self.toDist)
227 if self.serverMode:
228 self.tasks = self.cache.installedTasks
229- logging.debug("Foreign: %s" % " ".join(self.foreign_pkgs))
230- logging.debug("Obsolete: %s" % " ".join(self.obsolete_pkgs))
231+ logging.debug("Foreign: {0}".format(" ".join(self.foreign_pkgs)))
232+ logging.debug("Obsolete: {0}".format(" ".join(self.obsolete_pkgs)))
233 return True
234
235 def doUpdate(self, showErrors=True, forceRetries=None):
236@@ -940,9 +941,10 @@
237 " this checks if we have enough free space on /var and /usr"
238 err_sum = _("Not enough free disk space")
239 err_long= _("The upgrade has aborted. "
240- "The upgrade needs a total of %s free space on disk '%s'. "
241- "Please free at least an additional %s of disk "
242- "space on '%s'. "
243+ "The upgrade needs a total of {0} free space on disk "
244+ "'{1}'. "
245+ "Please free at least an additional {2} of disk "
246+ "space on '{3}'. "
247 "Empty your trash and remove temporary "
248 "packages of former installations using "
249 "'sudo apt-get clean'.")
250@@ -959,10 +961,10 @@
251 # perspective, but it means we do not need to break the
252 # string freeze
253 for required in e.free_space_required_list:
254- self._view.error(err_sum, err_long % (required.size_total,
255- required.dir,
256- required.size_needed,
257- required.dir))
258+ self._view.error(err_sum, err_long.format(required.size_total,
259+ required.dir,
260+ required.size_needed,
261+ required.dir))
262 return False
263 return True
264
265@@ -1172,7 +1174,7 @@
266 "http://bugs.launchpad.net/ubuntu/+source/ubuntu-release-upgrader/+filebug "
267 "and attach the files in /var/log/dist-upgrade/ "
268 "to the bug report.\n"
269- "%s" % e)
270+ "{0}".format(e))
271 self._view.error(_("Could not install the upgrades"), msg)
272 # installing the packages failed, can't be retried
273 cmd = ["/usr/bin/dpkg","--configure","-a"]
274@@ -1208,7 +1210,7 @@
275 _("The upgrade has aborted. Please check your "\
276 "Internet connection or "\
277 "installation media and try again. "),
278- "%s" % e)
279+ "{0}".format(e))
280 # abort here because we want our sources.list back
281 self.abort()
282
283@@ -1301,7 +1303,7 @@
284 _("A problem occurred during the clean-up. "
285 "Please see the below message for more "
286 "information. "),
287- "%s" % e)
288+ "{0}".format(e))
289 # run stuff after cleanup
290 self.quirks.run("PostCleanup")
291 # run the post upgrade scripts that can do fixup like xorg.conf
292@@ -1375,8 +1377,8 @@
293 # FIXME: instead of error out, fetch and install it
294 # here
295 self._view.error(_("Required depends is not installed"),
296- _("The required dependency '%s' is not "
297- "installed. " % dep))
298+ _("The required dependency '{0}' is not "
299+ "installed. ".format(dep)))
300 sys.exit(1)
301 return res
302
303@@ -1755,7 +1757,7 @@
304 logging.error("No '%s' available/downloadable after sources.list rewrite+update" % pkg)
305 self._view.error(_("Invalid package information"),
306 _("After updating your package "
307- "information, the essential package '%s' "
308+ "information, the essential package '{0}' "
309 "could not be located. This may be "
310 "because you have no official mirrors "
311 "listed in your software sources, or "
312@@ -1765,8 +1767,7 @@
313 "software sources."
314 "\n"
315 "In the case of an overloaded mirror, you "
316- "may want to try the upgrade again later.")
317- % pkg)
318+ "may want to try the upgrade again later.").format(pkg))
319 if os.path.exists("/usr/bin/apport-bug"):
320 subprocess.Popen(["apport-bug", "ubuntu-release-upgrader-core"])
321 else:
322
323=== modified file 'DistUpgrade/DistUpgradeFetcherCore.py'
324--- DistUpgrade/DistUpgradeFetcherCore.py 2014-02-25 00:14:50 +0000
325+++ DistUpgrade/DistUpgradeFetcherCore.py 2014-04-24 08:46:39 +0000
326@@ -72,9 +72,9 @@
327 f = self.tmpdir + "/" + os.path.basename(self.new_dist.upgradeTool)
328 sig = self.tmpdir + "/" + os.path.basename(
329 self.new_dist.upgradeToolSig)
330- print(_("authenticate '%(file)s' against '%(signature)s' ") % {
331- 'file': os.path.basename(f),
332- 'signature': os.path.basename(sig)})
333+ print(_("authenticate '{file}' against '{signature}' ").format(
334+ file=os.path.basename(f),
335+ signature=os.path.basename(sig)))
336 if self.gpgauthenticate(f, sig):
337 return True
338 return False
339@@ -136,7 +136,7 @@
340 def extractDistUpgrader(self):
341 # extract the tarball
342 fname = os.path.join(self.tmpdir, os.path.basename(self.uri))
343- print(_("extracting '%s'") % os.path.basename(fname))
344+ print(_("extracting '{0}'").format(os.path.basename(fname)))
345 if not os.path.exists(fname):
346 return False
347 try:
348@@ -316,7 +316,7 @@
349 return False
350 else:
351 self.error(_("Can not run the upgrade"),
352- _("The error message is '%s'.") % e.strerror)
353+ _("The error message is '{0}'.").format(e.strerror))
354 return True
355
356 if __name__ == "__main__":
357
358=== modified file 'DistUpgrade/DistUpgradeFetcherKDE.py'
359--- DistUpgrade/DistUpgradeFetcherKDE.py 2014-02-25 00:28:19 +0000
360+++ DistUpgrade/DistUpgradeFetcherKDE.py 2014-04-24 08:46:39 +0000
361@@ -157,18 +157,19 @@
362 current_item = self.total_items
363 label_text = _("Downloading additional package files...")
364 if self.current_cps > 0:
365- label_text += _("File %s of %s at %sB/s") % (
366+ label_text += _("File {0} of {1} at {2}B/s").format(
367 self.current_items, self.total_items,
368 apt_pkg.size_to_str(self.current_cps))
369 else:
370- label_text += _("File %s of %s") % (
371+ label_text += _("File {0} of {1}").format(
372 self.current_items, self.total_items)
373 self.label.setText(label_text)
374 KApplication.kApplication().processEvents()
375 return True
376
377 def mediaChange(self, medium, drive):
378- msg = _("Please insert '%s' into the drive '%s'") % (medium, drive)
379+ msg = _("Please insert '{0}' into the drive '{1}'").format(
380+ medium, drive)
381 #change = QMessageBox.question(None, _("Media Change"), msg,
382 # QMessageBox.Ok, QMessageBox.Cancel)
383 change = KMessageBox.questionYesNo(None, _("Media Change"),
384
385=== modified file 'DistUpgrade/DistUpgradeGettext.py'
386--- DistUpgrade/DistUpgradeGettext.py 2012-06-13 11:40:17 +0000
387+++ DistUpgrade/DistUpgradeGettext.py 2014-04-24 08:46:39 +0000
388@@ -64,7 +64,9 @@
389 return ""
390 translated_msg = unicode_gettext(_translation(), message)
391 if not _verify(message, translated_msg):
392- logging.error("incorrect translation for message '%s' to '%s' (wrong number of arguments)" % (message, translated_msg))
393+ logging.error(
394+ "incorrect translation for message '{0}' to '{1}' "
395+ "(wrong number of arguments)".format(message, translated_msg))
396 return message
397 return translated_msg
398
399@@ -75,7 +77,10 @@
400 """
401 translated_msg = unicode_ngettext(_translation(), msgid1, msgid2, n)
402 if not _verify(msgid1, translated_msg):
403- logging.error("incorrect translation for ngettext message '%s' plural: '%s' to '%s' (wrong number of arguments)" % (msgid1, msgid2, translated_msg))
404+ logging.error(
405+ "incorrect translation for ngettext message '{0}' "
406+ "plural: '{1}' to '{2}' (wrong number of arguments)".format(
407+ msgid1, msgid2, translated_msg))
408 # dumb fallback to not crash
409 if n == 1:
410 return msgid1
411
412=== modified file 'DistUpgrade/DistUpgradeView.py'
413--- DistUpgrade/DistUpgradeView.py 2014-04-14 16:16:56 +0000
414+++ DistUpgrade/DistUpgradeView.py 2014-04-24 08:46:39 +0000
415@@ -76,12 +76,12 @@
416 # get the fragments, this is not ideal i18n wise, but its
417 # difficult to do it differently
418 if days > 0:
419- map["str_days"] = ngettext("%li day","%li days", days) % days
420+ map["str_days"] = ngettext("{0} day","{0} days", days).format(days)
421 if hours > 0:
422- map["str_hours"] = ngettext("%li hour","%li hours", hours) % hours
423+ map["str_hours"] = ngettext("{0} hour","{0} hours", hours).format(hours)
424 if minutes > 0:
425- map["str_minutes"] = ngettext("%li minute","%li minutes", minutes) % minutes
426- map["str_seconds"] = ngettext("%li second","%li seconds", seconds) % seconds
427+ map["str_minutes"] = ngettext("{0} minute","{0} minutes", minutes).format(minutes)
428+ map["str_seconds"] = ngettext("{0} second","{0} seconds", seconds).format(seconds)
429
430 # now assemble the string
431 if days > 0:
432@@ -97,7 +97,8 @@
433 # plural form
434 #
435 # Note: most western languages will not need to change this
436- return _("%(str_days)s %(str_hours)s") % map
437+ return _("{str_days} {str_hours}").format(str_days=map["str_days"],
438+ str_hours=map["str_hours"])
439 # display no minutes for time > 3h, see LP: #144455
440 elif hours > 3:
441 return map["str_hours"]
442@@ -115,7 +116,9 @@
443 # plural form
444 #
445 # Note: most western languages will not need to change this
446- return _("%(str_hours)s %(str_minutes)s") % map
447+ return _("{str_hours} {str_minutes}").format(
448+ str_hours=map["str_hours"],
449+ str_minutes=map["str_minutes"])
450 elif minutes > 0:
451 return map["str_minutes"]
452 return map["str_seconds"]
453@@ -166,11 +169,11 @@
454 if self.est_speed == 0:
455 timeModem = required_download/(56*1024/8) # 56 kbit
456 timeDSL = required_download/(1024*1024/8) # 1Mbit = 1024 kbit
457- s= _("This download will take about %s with a 1Mbit DSL connection "
458- "and about %s with a 56k modem.") % (FuzzyTimeToStr(timeDSL), FuzzyTimeToStr(timeModem))
459+ s= _("This download will take about {0} with a 1Mbit DSL connection "
460+ "and about {1} with a 56k modem.").format(FuzzyTimeToStr(timeDSL), FuzzyTimeToStr(timeModem))
461 return s
462 # if we have a estimated speed, use it
463- s = _("This download will take about %s with your connection. ") % FuzzyTimeToStr(required_download/self.est_speed)
464+ s = _("This download will take about {0} with your connection. ").format(FuzzyTimeToStr(required_download/self.est_speed))
465 return s
466
467
468@@ -366,35 +369,35 @@
469 # FIXME: show detailed packages
470 if len(self.demotions) > 0:
471 msg += ngettext(
472- "%(amount)d installed package is no longer supported by Canonical. "
473- "You can still get support from the community.",
474- "%(amount)d installed packages are no longer supported by "
475- "Canonical. You can still get support from the community.",
476- len(self.demotions)) % { 'amount' : len(self.demotions) }
477+ "{amount} installed package is no longer supported by Canonical. "
478+ "You can still get support from the community.",
479+ "{amount} installed packages are no longer supported by "
480+ "Canonical. You can still get support from the community.",
481+ len(self.demotions)).format(amount=len(self.demotions))
482 msg += "\n\n"
483 if pkgs_remove > 0:
484 # FIXME: make those two separate lines to make it clear
485 # that the "%" applies to the result of ngettext
486- msg += ngettext("%d package is going to be removed.",
487- "%d packages are going to be removed.",
488- pkgs_remove) % pkgs_remove
489+ msg += ngettext("{0} package is going to be removed.",
490+ "{0} packages are going to be removed.",
491+ pkgs_remove).format(pkgs_remove)
492 msg += " "
493 if pkgs_inst > 0:
494- msg += ngettext("%d new package is going to be "
495+ msg += ngettext("{0} new package is going to be "
496 "installed.",
497- "%d new packages are going to be "
498- "installed.",pkgs_inst) % pkgs_inst
499+ "{0} new packages are going to be "
500+ "installed.",pkgs_inst).format(pkgs_inst)
501 msg += " "
502 if pkgs_upgrade > 0:
503- msg += ngettext("%d package is going to be upgraded.",
504- "%d packages are going to be upgraded.",
505- pkgs_upgrade) % pkgs_upgrade
506+ msg += ngettext("{0} package is going to be upgraded.",
507+ "{0} packages are going to be upgraded.",
508+ pkgs_upgrade).format(pkgs_upgrade)
509 msg +=" "
510 if downloadSize > 0:
511 downloadSizeStr = apt_pkg.size_to_str(downloadSize)
512 if isinstance(downloadSizeStr, bytes):
513 downloadSizeStr = downloadSizeStr.decode(ENCODING)
514- msg += _("\n\nYou have to download a total of %s. ") % (
515+ msg += _("\n\nYou have to download a total of {0}. ").format(
516 downloadSizeStr)
517 msg += self.getAcquireProgress().estimatedDownloadTime(downloadSize)
518 if ((pkgs_upgrade + pkgs_inst) > 0) and ((pkgs_upgrade + pkgs_inst + pkgs_remove) > 100):
519
520=== modified file 'DistUpgrade/DistUpgradeViewGtk3.py'
521--- DistUpgrade/DistUpgradeViewGtk3.py 2014-04-24 08:42:34 +0000
522+++ DistUpgrade/DistUpgradeViewGtk3.py 2014-04-24 08:46:39 +0000
523@@ -112,7 +112,7 @@
524 self.canceled = True
525 def media_change(self, medium, drive):
526 #print("mediaChange %s %s" % (medium, drive))
527- msg = _("Please insert '%s' into the drive '%s'") % (medium,drive)
528+ msg = _("Please insert '{0}' into the drive '{0}'").format(medium,drive)
529 dialog = Gtk.MessageDialog(parent=self.parent.window_main,
530 flags=Gtk.DialogFlags.MODAL,
531 type=Gtk.MessageType.QUESTION,
532@@ -148,12 +148,13 @@
533 if isinstance(current_cps, bytes):
534 current_cps = current_cps.decode(
535 locale.getpreferredencoding())
536- self.status.set_text(_("Fetching file %li of %li at %sB/s") % (
537- currentItem, self.total_items, current_cps))
538- self.progress.set_text(_("About %s remaining") % FuzzyTimeToStr(
539- self.eta))
540+ self.status.set_text(
541+ _("Fetching file {0} of {1} at {2}B/s").format(
542+ currentItem, self.total_items, current_cps))
543+ self.progress.set_text(_("About {0} remaining").format(
544+ FuzzyTimeToStr(self.eta)))
545 else:
546- self.status.set_text(_("Fetching file %li of %li") % (
547+ self.status.set_text(_("Fetching file {0} of {1}").format(
548 currentItem, self.total_items))
549 self.progress.set_text(" ")
550 while Gtk.events_pending():
551@@ -215,11 +216,11 @@
552
553 #self.expander_terminal.set_expanded(True)
554 self.parent.dialog_error.set_transient_for(self.parent.window_main)
555- summary = _("Could not install '%s'") % pkg
556- msg = _("The upgrade will continue but the '%s' package may not "
557+ summary = _("Could not install '{0}'").format(pkg)
558+ msg = _("The upgrade will continue but the '{0}' package may not "
559 "be in a working state. Please consider submitting a "
560- "bug report about it.") % pkg
561- markup="<big><b>%s</b></big>\n\n%s" % (summary, msg)
562+ "bug report about it.").format(pkg)
563+ markup="<big><b>{0}</b></big>\n\n{1}".format(summary, msg)
564 self.parent.dialog_error.realize()
565 self.parent.dialog_error.set_title("")
566 self.parent.dialog_error.get_window().set_functions(Gdk.WMFunction.MOVE)
567@@ -233,11 +234,12 @@
568 logging.debug("got a conffile-prompt from dpkg for file: '%s'" % current)
569 start = time.time()
570 #self.expander.set_expanded(True)
571- prim = _("Replace the customized configuration file\n'%s'?") % current
572+ prim = _("Replace the customized configuration file\n'{0}'?").format(
573+ current)
574 sec = _("You will lose any changes you have made to this "
575 "configuration file if you choose to replace it with "
576 "a newer version.")
577- markup = "<span weight=\"bold\" size=\"larger\">%s </span> \n\n%s" % (prim, sec)
578+ markup = "<span weight=\"bold\" size=\"larger\">{0} </span> \n\n{1}".format(prim, sec)
579 self.parent.label_conffile.set_markup(markup)
580 self.parent.dialog_conffile.set_title("")
581 self.parent.dialog_conffile.set_transient_for(self.parent.window_main)
582@@ -254,7 +256,8 @@
583 diff = diff.decode("UTF-8", "replace")
584 self.parent.textview_conffile.get_buffer().set_text(diff)
585 else:
586- self.parent.textview_conffile.get_buffer().set_text(_("The 'diff' command was not found"))
587+ self.parent.textview_conffile.get_buffer().set_text(
588+ _("The 'diff' command was not found"))
589 res = self.parent.dialog_conffile.run()
590 self.parent.dialog_conffile.hide()
591 self.time_ui += time.time() - start
592@@ -307,7 +310,8 @@
593 eta = (100.0 - percent) * time_per_percent
594 # only show if we have some sensible data (60sec < eta < 2days)
595 if eta > 61.0 and eta < (60*60*24*2):
596- self.progress.set_text(_("About %s remaining") % FuzzyTimeToStr(eta))
597+ self.progress.set_text(
598+ _("About {0} remaining").format(FuzzyTimeToStr(eta)))
599 else:
600 self.progress.set_text(" ")
601 # 2 == WEBKIT_LOAD_FINISHED - the enums is not exposed via python
602@@ -652,16 +656,16 @@
603 # fill in the details
604 self.details_list.clear()
605 for (parent_text, details_list) in (
606- ( _("No longer supported by Canonical (%s)"), self.demotions),
607- ( _("<b>Downgrade (%s)</b>"), self.toDowngrade),
608- ( _("Remove (%s)"), self.toRemove),
609- ( _("No longer needed (%s)"), self.toRemoveAuto),
610- ( _("Install (%s)"), self.toInstall),
611- ( _("Upgrade (%s)"), self.toUpgrade),
612+ ( _("No longer supported by Canonical ({0})"), self.demotions),
613+ ( _("<b>Downgrade ({0})</b>"), self.toDowngrade),
614+ ( _("Remove ({0})"), self.toRemove),
615+ ( _("No longer needed ({0})"), self.toRemoveAuto),
616+ ( _("Install ({0})"), self.toInstall),
617+ ( _("Upgrade ({0})"), self.toUpgrade),
618 ):
619 if details_list:
620 node = self.details_list.append(None,
621- [parent_text % len(details_list)])
622+ [parent_text.format(len(details_list))])
623 for pkg in details_list:
624 self.details_list.append(node, ["<b>%s</b> - %s" % (
625 pkg.name, GLib.markup_escape_text(getattr(pkg.candidate, "summary", None)))])
626
627=== modified file 'DistUpgrade/DistUpgradeViewKDE.py'
628--- DistUpgrade/DistUpgradeViewKDE.py 2012-10-09 16:35:12 +0000
629+++ DistUpgrade/DistUpgradeViewKDE.py 2014-04-24 08:46:39 +0000
630@@ -193,7 +193,7 @@
631 self.parent = parent
632
633 def media_change(self, medium, drive):
634- msg = _("Please insert '%s' into the drive '%s'") % (medium,drive)
635+ msg = _("Please insert '{0}' into the drive '{1}'").format(medium,drive)
636 change = QMessageBox.question(self.parent.window_main, _("Media Change"), msg, QMessageBox.Ok, QMessageBox.Cancel)
637 if change == QMessageBox.Ok:
638 return True
639@@ -223,10 +223,15 @@
640 current_cps = apt_pkg.size_to_str(self.current_cps)
641 if isinstance(current_cps, bytes):
642 current_cps = current_cps.decode(locale.getpreferredencoding())
643- self.status.setText(_("Fetching file %li of %li at %sB/s") % (current_item, self.total_items, current_cps))
644- self.parent.window_main.progress_text.setText("<i>" + _("About %s remaining") % FuzzyTimeToStr(self.eta) + "</i>")
645+ self.status.setText(_("Fetching file {0} of {1} at {2}B/s").format(
646+ current_item, self.total_items, current_cps))
647+ self.parent.window_main.progress_text.setText(
648+ "<i>" + _("About {0} remaining").format(
649+ FuzzyTimeToStr(self.eta)) +
650+ "</i>")
651 else:
652- self.status.setText(_("Fetching file %li of %li") % (current_item, self.total_items))
653+ self.status.setText(_("Fetching file {0} of {1}").format(
654+ current_item, self.total_items))
655 self.parent.window_main.progress_text.setText(" ")
656
657 QApplication.processEvents()
658@@ -278,10 +283,10 @@
659 # we do not report followup errors from earlier failures
660 if gettext.dgettext('dpkg', "dependency problems - leaving unconfigured") in errormsg:
661 return False
662- summary = _("Could not install '%s'") % pkg
663- msg = _("The upgrade will continue but the '%s' package may not "
664+ summary = _("Could not install '{0}'").format(pkg)
665+ msg = _("The upgrade will continue but the '{0}' package may not "
666 "be in a working state. Please consider submitting a "
667- "bug report about it.") % pkg
668+ "bug report about it.").format(pkg)
669 msg = "<big><b>%s</b></big><br />%s" % (summary, msg)
670
671 dialogue = QDialog(self.parent.window_main)
672@@ -300,11 +305,13 @@
673 """ask question in case conffile has been changed by user"""
674 logging.debug("got a conffile-prompt from dpkg for file: '%s'" % current)
675 start = time.time()
676- prim = _("Replace the customized configuration file\n'%s'?") % current
677+ prim = _("Replace the customized configuration file\n'{0}'?").format(
678+ current)
679 sec = _("You will lose any changes you have made to this "
680 "configuration file if you choose to replace it with "
681 "a newer version.")
682- markup = "<span weight=\"bold\" size=\"larger\">%s </span> \n\n%s" % (prim, sec)
683+ markup = "<span weight=\"bold\" size=\"larger\">{0} </span> "\
684+ "\n\n{1}".format(prim, sec)
685 self.confDialogue = QDialog(self.parent.window_main)
686 loadUi("dialog_conffile.ui", self.confDialogue)
687 self.confDialogue.label_conffile.setText(markup)
688@@ -372,7 +379,8 @@
689 eta = (100.0 - self.percent) * time_per_percent
690 # only show if we have some sensible data (60sec < eta < 2days)
691 if eta > 61.0 and eta < (60*60*24*2):
692- self.progress_text.setText(_("About %s remaining") % FuzzyTimeToStr(eta))
693+ self.progress_text.setText(_("About {0} remaining").format(
694+ FuzzyTimeToStr(eta)))
695 else:
696 self.progress_text.setText(" ")
697
698@@ -778,15 +786,15 @@
699 self.changesDialogue.treeview_details.setHeaderLabels(["Packages"])
700 self.changesDialogue.treeview_details.header().hide()
701 for demoted in self.demotions:
702- self.changesDialogue.treeview_details.insertTopLevelItem(0, QTreeWidgetItem(self.changesDialogue.treeview_details, [_("No longer supported %s") % demoted.name]) )
703+ self.changesDialogue.treeview_details.insertTopLevelItem(0, QTreeWidgetItem(self.changesDialogue.treeview_details, [_("No longer supported {0}").format(demoted.name)]) )
704 for rm in self.toRemove:
705- self.changesDialogue.treeview_details.insertTopLevelItem(0, QTreeWidgetItem(self.changesDialogue.treeview_details, [_("Remove %s") % rm.name]) )
706+ self.changesDialogue.treeview_details.insertTopLevelItem(0, QTreeWidgetItem(self.changesDialogue.treeview_details, [_("Remove {0}").format(rm.name)]) )
707 for rm in self.toRemoveAuto:
708- self.changesDialogue.treeview_details.insertTopLevelItem(0, QTreeWidgetItem(self.changesDialogue.treeview_details, [_("Remove (was auto installed) %s") % rm.name]) )
709+ self.changesDialogue.treeview_details.insertTopLevelItem(0, QTreeWidgetItem(self.changesDialogue.treeview_details, [_("Remove (was auto installed) {0}").format(rm.name)]) )
710 for inst in self.toInstall:
711- self.changesDialogue.treeview_details.insertTopLevelItem(0, QTreeWidgetItem(self.changesDialogue.treeview_details, [_("Install %s") % inst.name]) )
712+ self.changesDialogue.treeview_details.insertTopLevelItem(0, QTreeWidgetItem(self.changesDialogue.treeview_details, [_("Install {0}").format(inst.name)]) )
713 for up in self.toUpgrade:
714- self.changesDialogue.treeview_details.insertTopLevelItem(0, QTreeWidgetItem(self.changesDialogue.treeview_details, [_("Upgrade %s") % up.name]) )
715+ self.changesDialogue.treeview_details.insertTopLevelItem(0, QTreeWidgetItem(self.changesDialogue.treeview_details, [_("Upgrade {0}").format(up.name)]) )
716
717 #FIXME resize label, stop it being shrinkable
718 res = self.changesDialogue.exec_()
719
720=== modified file 'DistUpgrade/DistUpgradeViewText.py'
721--- DistUpgrade/DistUpgradeViewText.py 2014-04-14 12:14:57 +0000
722+++ DistUpgrade/DistUpgradeViewText.py 2014-04-24 08:46:39 +0000
723@@ -196,27 +196,32 @@
724 if len(self.demotions) > 0:
725 output += "\n"
726 output += twrap(
727- _("No longer supported: %s\n") % " ".join([p.name for p in self.demotions]),
728+ _("No longer supported: {0}\n").format(
729+ " ".join([p.name for p in self.demotions])),
730 subsequent_indent=' ')
731 if len(self.toRemove) > 0:
732 output += "\n"
733 output += twrap(
734- _("Remove: %s\n") % " ".join([p.name for p in self.toRemove]),
735+ _("Remove: {0}\n").format(
736+ " ".join([p.name for p in self.toRemove])),
737 subsequent_indent=' ')
738 if len(self.toRemoveAuto) > 0:
739 output += twrap(
740- _("Remove (was auto installed) %s") % " ".join([p.name for p in self.toRemoveAuto]),
741+ _("Remove (was auto installed) {0}").format(
742+ " ".join([p.name for p in self.toRemoveAuto])),
743 subsequent_indent=' ')
744 output += "\n"
745 if len(self.toInstall) > 0:
746 output += "\n"
747 output += twrap(
748- _("Install: %s\n") % " ".join([p.name for p in self.toInstall]),
749+ _("Install: {0}\n").format(
750+ " ".join([p.name for p in self.toInstall])),
751 subsequent_indent=' ')
752 if len(self.toUpgrade) > 0:
753 output += "\n"
754 output += twrap(
755- _("Upgrade: %s\n") % " ".join([p.name for p in self.toUpgrade]),
756+ _("Upgrade: {0}\n").format(
757+ " ".join([p.name for p in self.toUpgrade])),
758 subsequent_indent=' ')
759 self.showInPager(output)
760 print("%s %s" % (_("Continue [yN] "), _("Details [d]")), end="")
761
762=== modified file 'DistUpgrade/GtkProgress.py'
763--- DistUpgrade/GtkProgress.py 2013-01-15 15:51:10 +0000
764+++ DistUpgrade/GtkProgress.py 2014-04-24 08:46:39 +0000
765@@ -70,15 +70,15 @@
766 if current_item > self.total_items:
767 current_item = self.total_items
768 if self.current_cps > 0:
769- status_text = (_("Downloading file %(current)li of %(total)li "
770- "with %(speed)s/s") % {
771- "current": current_item,
772- "total": self.total_items,
773- "speed": humanize_size(self.current_cps)})
774+ status_text = (_("Downloading file {current} of {total} "
775+ "with {speed}/s").format(
776+ current=current_item,
777+ total=self.total_items,
778+ speed=humanize_size(self.current_cps)))
779 else:
780- status_text = (_("Downloading file %(current)li of %(total)li") %
781- {"current": current_item,
782- "total": self.total_items})
783+ status_text = (_("Downloading file {current} of {total}").format(
784+ current=current_item,
785+ total=self.total_items))
786 self.progress.set_fraction(
787 (self.current_bytes + self.current_items) /
788 float(self.total_bytes + self.total_items))
789
790=== modified file 'check-new-release-gtk'
791--- check-new-release-gtk 2013-10-05 02:17:39 +0000
792+++ check-new-release-gtk 2014-04-24 08:46:39 +0000
793@@ -73,7 +73,7 @@
794 self.options = options
795 self.datadir = options.datadir
796 self.new_dist = None
797- logging.debug("running with devel=%s proposed=%s" % (
798+ logging.debug("running with devel={0} proposed={0}".format(
799 options.devel_release, options.proposed_release))
800 m = MetaRelease(useDevelopmentRelease=options.devel_release,
801 useProposed=options.proposed_release)
802@@ -93,7 +93,7 @@
803 Gtk.main()
804
805 def new_dist_available(self, meta_release, new_dist):
806- logging.debug("new_dist_available: %s" % new_dist)
807+ logging.debug("new_dist_available: {0}".format(new_dist))
808 self.new_dist = new_dist
809 client = Gio.Settings("com.ubuntu.update-manager")
810 ignore_dist = client.get_string("check-new-release-ignore")
811@@ -101,7 +101,7 @@
812 # go into nag mode
813 if (ignore_dist == new_dist.name and
814 meta_release.no_longer_supported is None):
815- logging.warn("found new dist '%s' but it is on the ignore list" % new_dist.name)
816+ logging.warn("found new dist '{0}' but it is on the ignore list".format(new_dist.name))
817 sys.exit()
818
819 # show alert on unsupported distros
820@@ -110,7 +110,7 @@
821 Gtk.main_quit()
822 else:
823 self.build_ui()
824- self.window_main.set_title(_("Ubuntu %(version)s Upgrade Available") % {'version': new_dist.version})
825+ self.window_main.set_title(_("Ubuntu {version} Upgrade Available").format(version=new_dist.version))
826 self.window_main.show()
827
828 def close(self):
829@@ -129,7 +129,7 @@
830 extra_args = extra_args + " --proposed"
831 os.execl("/bin/sh", "/bin/sh", "-c",
832 "/usr/bin/pkexec /usr/bin/do-release-upgrade "
833- "--frontend=DistUpgradeViewGtk3%s" % extra_args)
834+ "--frontend=DistUpgradeViewGtk3{0}".format(extra_args))
835
836 def on_button_ask_me_later_clicked(self, button):
837 logging.debug("ask me later")
838@@ -141,8 +141,9 @@
839
840 def on_button_dont_upgrade_clicked(self, button):
841 #print("don't upgrade")
842- s = _("You have declined the upgrade to Ubuntu %s") % self.new_dist.version
843- self.dialog_really_do_not_upgrade.set_markup("<b>%s</b>" % s)
844+ s = _("You have declined the upgrade to Ubuntu {0}").format(
845+ self.new_dist.version)
846+ self.dialog_really_do_not_upgrade.set_markup("<b>{0}</b>".format(s))
847 if self.dialog_really_do_not_upgrade.run() == Gtk.ResponseType.OK:
848 client = Gio.Settings("com.ubuntu.update-manager")
849 client.set_string("check-new-release-ignore", self.new_dist.name)
850
851=== modified file 'do-partial-upgrade'
852--- do-partial-upgrade 2012-06-28 19:22:04 +0000
853+++ do-partial-upgrade 2014-04-24 08:46:39 +0000
854@@ -77,7 +77,7 @@
855 data_dir = os.path.normpath(options.data_dir)+"/"
856
857 if options.show_version:
858- print("%s: version %s" % (os.path.basename(sys.argv[0]), VERSION))
859+ print("{0}: version {1}".format(os.path.basename(sys.argv[0]), VERSION))
860 sys.exit(0)
861
862 module_name = "DistUpgrade." + options.frontend
863@@ -86,7 +86,7 @@
864 view_class = getattr(submodule, options.frontend)
865 view = view_class(data_dir)
866 if options.frontend == "DistUpgradeViewGtk3":
867- view.label_title.set_markup("<b><big>%s</big></b>" %
868- _("Running partial upgrade"))
869+ view.label_title.set_markup("<b><big>{0}</big></b>".format(
870+ _("Running partial upgrade")))
871 controller = DistUpgradeController(view, datadir=data_dir)
872 controller.doPartialUpgrade()
873
874=== modified file 'pre-build.sh'
875--- pre-build.sh 2014-04-07 12:30:43 +0000
876+++ pre-build.sh 2014-04-24 08:46:39 +0000
877@@ -8,6 +8,11 @@
878 dpkg-checkbuilddeps -d 'python3-apt, apt-btrfs-snapshot, parsewiki, python-feedparser,
879 python3-mock, xvfb, gir1.2-gtk-3.0, python3-gi, python3-nose'
880
881+# check the po files (LP: #1311396)
882+for po in po/*.po; do
883+ msgfmt -c $po
884+done
885+
886 # update demotions
887 (cd utils && ./demotions.py saucy trusty > demoted.cfg)
888 # when this gets enabled, make sure to add symlink in DistUpgrade

Subscribers

People subscribed via source and target branches