← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~cjwatson/launchpad/translations-doctests-future-imports into lp:launchpad

 

Colin Watson has proposed merging lp:~cjwatson/launchpad/translations-doctests-future-imports into lp:launchpad.

Commit message:
Convert doctests under lp.translations to Launchpad's preferred __future__ imports.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/translations-doctests-future-imports/+merge/347327
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~cjwatson/launchpad/translations-doctests-future-imports into lp:launchpad.
=== modified file 'lib/lp/translations/doc/browser-helpers.txt'
--- lib/lp/translations/doc/browser-helpers.txt	2017-08-07 08:17:31 +0000
+++ lib/lp/translations/doc/browser-helpers.txt	2018-06-02 14:12:45 +0000
@@ -12,30 +12,30 @@
 
 Normal strings get passed through unmodified.
 
-    >>> contract_rosetta_escapes('foo')
-    'foo'
-    >>> contract_rosetta_escapes('foo\\nbar')
-    'foo\\nbar'
+    >>> print(contract_rosetta_escapes('foo'))
+    foo
+    >>> print(contract_rosetta_escapes('foo\\nbar'))
+    foo\nbar
 
 The string '[tab]' gets converted to a tab character.
 
     >>> contract_rosetta_escapes('foo[tab]bar')
-    'foo\tbar'
+    u'foo\tbar'
 
 The string '\[tab]' gets converted to a literal '[tab]'.
 
-    >>> contract_rosetta_escapes('foo\\[tab]bar')
-    'foo[tab]bar'
+    >>> print(contract_rosetta_escapes('foo\\[tab]bar'))
+    foo[tab]bar
 
 The string '\\[tab]' gets converted to a literal '\[tab]'.
 
-    >>> contract_rosetta_escapes('foo\\\\[tab]bar')
-    'foo\\[tab]bar'
+    >>> print(contract_rosetta_escapes('foo\\\\[tab]bar'))
+    foo\[tab]bar
 
 And so on...
 
-    >>> contract_rosetta_escapes('foo\\\\\\[tab]bar')
-    'foo\\\\[tab]bar'
+    >>> print(contract_rosetta_escapes('foo\\\\\\[tab]bar'))
+    foo\\[tab]bar
 
 Similarly, string '[nbsp]' gets converted to no-break space character.
 
@@ -44,8 +44,8 @@
 
 The string '\[nbsp]' gets converted to a literal '[nbsp]'.
 
-    >>> contract_rosetta_escapes('foo\\[nbsp]bar')
-    'foo[nbsp]bar'
+    >>> print(contract_rosetta_escapes('foo\\[nbsp]bar'))
+    foo[nbsp]bar
 
 Similarly, string '[nnbsp]' gets converted to narrow no-break space
 character.
@@ -55,8 +55,8 @@
 
 The string '\[nnbsp]' gets converted to a literal '[nnbsp]'.
 
-    >>> contract_rosetta_escapes('foo\\[nnbsp]bar')
-    'foo[nnbsp]bar'
+    >>> print(contract_rosetta_escapes('foo\\[nnbsp]bar'))
+    foo[nnbsp]bar
 
 
 expand_rosetta_escapes
@@ -129,11 +129,12 @@
     >>> parse_cformat_string('')
     []
     >>> parse_cformat_string('foo')
-    [('string', 'foo')]
+    [('string', u'foo')]
     >>> parse_cformat_string('blah %d blah')
-    [('string', 'blah '), ('interpolation', '%d'), ('string', ' blah')]
+    [('string', u'blah '), ('interpolation', u'%d'), ('string', u' blah')]
     >>> parse_cformat_string('%sfoo%%bar%s')
-    [('interpolation', '%s'), ('string', 'foo%%bar'), ('interpolation', '%s')]
+    [('interpolation', u'%s'), ('string', u'foo%%bar'),
+     ('interpolation', u'%s')]
     >>> parse_cformat_string('%')
     Traceback (most recent call last):
     ...

=== modified file 'lib/lp/translations/doc/canonical_url_examples.txt'
--- lib/lp/translations/doc/canonical_url_examples.txt	2011-12-24 17:49:30 +0000
+++ lib/lp/translations/doc/canonical_url_examples.txt	2018-06-02 14:12:45 +0000
@@ -65,7 +65,7 @@
     >>> translationmessage = potmsgset.getCurrentTranslation(
     ...     pofile.potemplate, pofile.language, potemplate.translation_side)
     >>> translationmessage.setPOFile(pofile)
-    >>> print canonical_url(translationmessage)
+    >>> print(canonical_url(translationmessage))
     http://transl.../hoary/+source/evolution/+pots/evolution-2.2/es/1
 
 Even for a dummy one.
@@ -73,7 +73,7 @@
     >>> potmsgset = potemplate.getPOTMsgSetBySequence(20)
     >>> translationmessage = potmsgset.getCurrentTranslationMessageOrDummy(
     ...     pofile)
-    >>> print canonical_url(translationmessage)
+    >>> print(canonical_url(translationmessage))
     http://transl.../hoary/+source/evolution/+pots/evolution-2.2/es/20
 
 Upstream POTemplateSubsets work in much the same way, except they hang off a
@@ -103,7 +103,7 @@
     >>> translationmessage = potmsgset.getCurrentTranslation(
     ...     pofile.potemplate, pofile.language, potemplate.translation_side)
     >>> translationmessage.setPOFile(pofile)
-    >>> print canonical_url(translationmessage)
+    >>> print(canonical_url(translationmessage))
     http://translations.../evolution/trunk/+pots/evolution-2.2/es/1
 
 Even for a dummy PO msgset
@@ -111,7 +111,7 @@
     >>> potmsgset = potemplate.getPOTMsgSetBySequence(20)
     >>> translationmessage = potmsgset.getCurrentTranslationMessageOrDummy(
     ...     pofile)
-    >>> print canonical_url(translationmessage)
+    >>> print(canonical_url(translationmessage))
     http://translations.../evolution/trunk/+pots/evolution-2.2/es/20
 
 

=== modified file 'lib/lp/translations/doc/distroseries-language.txt'
--- lib/lp/translations/doc/distroseries-language.txt	2011-12-30 06:14:56 +0000
+++ lib/lp/translations/doc/distroseries-language.txt	2018-06-02 14:12:45 +0000
@@ -31,7 +31,7 @@
 contain messages.
 
     >>> for po in hoary_spanish.pofiles:
-    ...     print po.potemplate.name
+    ...     print(po.potemplate.name)
     evolution-2.2
     pmount
     pkgconf-mozilla
@@ -41,7 +41,7 @@
 
     >>> hoary_templates = list(hoary.getCurrentTranslationTemplates())
     >>> for template in hoary_templates:
-    ...     print template.name
+    ...     print(template.name)
     evolution-2.2
     man
     man
@@ -59,9 +59,9 @@
     ...     Creates `DummyPOFile`s where needed.  Prints types.
     ...     """
     ...     for pofile in distroserieslanguage.getPOFilesFor(templates):
-    ...         print "%s (%s) %s" % (
+    ...         print("%s (%s) %s" % (
     ...             pofile.potemplate.name, pofile.language.code,
-    ...             removeSecurityProxy(pofile).__class__)
+    ...             removeSecurityProxy(pofile).__class__))
 
     >>> print_augmented_pofiles(hoary_spanish, hoary_templates)
     evolution-2.2     (es)   <class '...pofile.POFile'>
@@ -79,7 +79,7 @@
 listing.
 
     >>> for potemplate in hoary.getTranslationTemplates():
-    ...     print potemplate.name
+    ...     print(potemplate.name)
     evolution-2.2
     disabled-template
     man
@@ -90,13 +90,13 @@
 This is the one obsolete template.
 
     >>> potemplate = hoary.getTranslationTemplateByName('disabled-template')
-    >>> print potemplate.iscurrent
+    >>> print(potemplate.iscurrent)
     False
 
 Also, we can see that the template has an Spanish translation that
 hoary_spanish.pofiles is hiding as expected.
 
-    >>> print potemplate.getPOFileByLang('es').title
+    >>> print(potemplate.getPOFileByLang('es').title)
     Spanish (es) translation of disabled-template in Ubuntu Hoary package
     "evolution"
 
@@ -104,7 +104,7 @@
 
     >>> amharic = getUtility(ILanguageSet)['am']
     >>> hoary_amharic = hoary.getDistroSeriesLanguageOrDummy(amharic)
-    >>> print hoary_amharic.__class__
+    >>> print(hoary_amharic.__class__)
     <class '...DummyDistroSeriesLanguage'>
 
 English is not a translatable language because we store the source messages
@@ -133,7 +133,7 @@
 been extended, and the DummyDistroSeriesLanguage has not been similarly
 extended.
 
-    >>> print IDistroSeriesLanguage.providedBy(hoary_amharic)
+    >>> print(IDistroSeriesLanguage.providedBy(hoary_amharic))
     True
 
 
@@ -148,19 +148,19 @@
 
     >>> potemplates = list(hoary.getCurrentTranslationTemplates())
     >>> evo = potemplates[0]
-    >>> print evo.name
+    >>> print(evo.name)
     evolution-2.2
     >>> man1 = potemplates[1]
-    >>> print man1.name
+    >>> print(man1.name)
     man
     >>> man2 = potemplates[2]
-    >>> print man2.name
+    >>> print(man2.name)
     man
     >>> mozconf = potemplates[3]
-    >>> print mozconf.name
+    >>> print(mozconf.name)
     pkgconf-mozilla
     >>> pm = potemplates[4]
-    >>> print pm.name
+    >>> print(pm.name)
     pmount
 
 OK, so we have the five templates. Let's set their priorities and see if
@@ -185,7 +185,7 @@
 And now we can confirm that priority does in fact dominate:
 
     >>> for pot in hoary.getCurrentTranslationTemplates():
-    ...     print pot.priority, pot.name
+    ...     print(pot.priority, pot.name)
     9 pmount
     8 pkgconf-mozilla
     7 man
@@ -202,4 +202,3 @@
     man              (am)  <class '...pofile.DummyPOFile'>
     man              (am)  <class '...pofile.DummyPOFile'>
     evolution-2.2    (am)  <class '...pofile.DummyPOFile'>
-

=== modified file 'lib/lp/translations/doc/distroseries-translations-copy.txt'
--- lib/lp/translations/doc/distroseries-translations-copy.txt	2014-08-20 06:41:00 +0000
+++ lib/lp/translations/doc/distroseries-translations-copy.txt	2018-06-02 14:12:45 +0000
@@ -89,7 +89,7 @@
     ...     IPOTemplateSet).getSubset(distroseries=carty)
     >>> len(carty_templates)
     2
-    >>> print sorted([template.name for template in carty_templates])
+    >>> print(sorted([template.name for template in carty_templates]))
     [u'template1', u'template2']
     >>> carty_template1 = carty_templates.getPOTemplateByName('template1')
     >>> carty_template2 = carty_templates.getPOTemplateByName('template2')
@@ -102,7 +102,7 @@
 
     >>> all_pofiles = sum(
     ...     [list(template.pofiles) for template in carty_templates], [])
-    >>> print sorted([pofile.path for pofile in all_pofiles])
+    >>> print(sorted([pofile.path for pofile in all_pofiles]))
     [u'template1-domain-eo.po',
     u'template2-domain-de.po',
     u'template2-domain-eo.po']
@@ -111,13 +111,13 @@
 in the new series.
 
     >>> potmsgsets = carty_template1.getPOTMsgSets()
-    >>> print potmsgsets.count()
+    >>> print(potmsgsets.count())
     1
     >>> potmsgsets[0] == msgset11
     True
 
     >>> potmsgsets = carty_template2.getPOTMsgSets()
-    >>> print potmsgsets.count()
+    >>> print(potmsgsets.count())
     2
     >>> potmsgsets[0] == msgset21
     True
@@ -164,7 +164,7 @@
     ...     ['--distribution=foobuntu', '--series=darty'])
     >>> returnvalue
     1
-    >>> print error_output
+    >>> print(error_output)
     INFO    Creating lockfile:
       /var/lock/launchpad-copy-missing-translations-foobuntu-darty.lock
     ERROR   Before this process starts, set the hide_all_translations and
@@ -192,7 +192,7 @@
     ...     ['--distribution=foobuntu', '--series=darty', '--force'])
     >>> returnvalue
     0
-    >>> print error_output
+    >>> print(error_output)
     INFO    Creating lockfile:
       /var/lock/launchpad-copy-missing-translations-foobuntu-darty.lock
     INFO    Starting...
@@ -229,7 +229,7 @@
     >>> dartempls = getUtility(IPOTemplateSet).getSubset(distroseries=darty)
     >>> len(dartempls)
     2
-    >>> print sorted([template.name for template in dartempls])
+    >>> print(sorted([template.name for template in dartempls]))
     [u'template1', u'template2']
 
 The script defaults to copying from the given series' previous_series,
@@ -246,7 +246,7 @@
     ...     ['--distribution=notbuntu', '--series=grumpy'])
     >>> returnvalue
     2
-    >>> print error_output
+    >>> print(error_output)
     INFO    Creating lockfile:
       /var/lock/launchpad-copy-missing-translations-notbuntu-grumpy.lock
     Usage: copy-distroseries-translations.py [options]
@@ -260,7 +260,7 @@
     ...      '--from-distribution=foobuntu' , '--from-series=darty'])
     >>> returnvalue
     0
-    >>> print error_output
+    >>> print(error_output)
     INFO    Creating lockfile:
       /var/lock/launchpad-copy-missing-translations-notbuntu-grumpy.lock
     INFO    Starting...

=== modified file 'lib/lp/translations/doc/fix_translation_credits.txt'
--- lib/lp/translations/doc/fix_translation_credits.txt	2011-12-28 17:03:06 +0000
+++ lib/lp/translations/doc/fix_translation_credits.txt	2018-06-02 14:12:45 +0000
@@ -6,9 +6,9 @@
     >>> from lp.testing.script import run_script
     >>> (returncode, out, err) = run_script(
     ...     'scripts/rosetta/fix_translation_credits.py')
-    >>> print returncode
+    >>> print(returncode)
     0
-    >>> print err
+    >>> print(err)
     INFO    Creating lockfile:
         /var/lock/launchpad-fix-translation-credits.lock
     INFO    Figuring out POFiles that need fixing: this may take a while...

=== modified file 'lib/lp/translations/doc/language.txt'
--- lib/lp/translations/doc/language.txt	2009-07-01 20:45:39 +0000
+++ lib/lp/translations/doc/language.txt	2018-06-02 14:12:45 +0000
@@ -11,7 +11,7 @@
     # translations.  That other person is Foo Bar (name16).
     >>> from lp.registry.interfaces.person import IPersonSet
     >>> foo_bar = getUtility(IPersonSet).getByName('name16')
-    >>> print foo_bar.displayname
+    >>> print(foo_bar.displayname)
     Foo Bar
 
     >>> from lp.services.worlddata.interfaces.language import ILanguageSet
@@ -22,6 +22,6 @@
     ...     for karma_category_cache in translator.karma_category_caches:
     ...         if (karma_category_cache.category.name == 'translations'):
     ...             karma = karma_category_cache.karmavalue
-    ...     print (translator.displayname, karma)
+    ...     print((translator.displayname, karma))
     (u'Foo Bar', 164)
     (u'Carlos Perell\xf3 Mar\xedn', 9)

=== modified file 'lib/lp/translations/doc/poexport-language-pack.txt'
--- lib/lp/translations/doc/poexport-language-pack.txt	2017-04-19 11:01:16 +0000
+++ lib/lp/translations/doc/poexport-language-pack.txt	2018-06-02 14:12:45 +0000
@@ -38,7 +38,7 @@
     ...             size = 'bin'
     ...         else:
     ...             size = len(tarfile.extractfile(member).readlines())
-    ...         print "| %5s | %s" % (size, member.name)
+    ...         print("| %5s | %s" % (size, member.name))
 
 
 Base language pack export using Librarian
@@ -60,7 +60,7 @@
 
 Check that the log looks ok.
 
-    >>> print logger.getLogBuffer()
+    >>> print(logger.getLogBuffer())
     DEBUG Selecting PO files for export
     INFO  Number of PO files to export: 12
     DEBUG Exporting PO file ... (1/12)
@@ -78,7 +78,7 @@
 The tarball has the right members.
 
     >>> for member in tarfile.getmembers():
-    ...     print member.name
+    ...     print(member.name)
     rosetta-hoary
     ...
     rosetta-hoary/es
@@ -91,8 +91,8 @@
 Directory permissions allow correct use of those directories:
 
     >>> directory = tarfile.getmember('rosetta-hoary')
-    >>> '0o%o' % directory.mode
-    '0o755'
+    >>> print('0o%o' % directory.mode)
+    0o755
 
 And one of the included .po files look like what we expected.
 
@@ -119,7 +119,7 @@
 Get hold of a person.
 
     >>> mark = getUtility(IPersonSet).getByName('mark')
-    >>> print mark.displayname
+    >>> print(mark.displayname)
     Mark Shuttleworth
 
 Get the Grumpy distro series.
@@ -301,7 +301,7 @@
 First, export without any existing base language pack: should get both
 PO files.
 
-    >>> print series.language_pack_base
+    >>> print(series.language_pack_base)
     None
 
     >>> logger = BufferLogger()
@@ -317,7 +317,7 @@
 
 Check that the log looks ok.
 
-    >>> print logger.getLogBuffer()
+    >>> print(logger.getLogBuffer())
     DEBUG Selecting PO files for export
     INFO  Number of PO files to export: 4
     DEBUG Exporting PO file ... (1/4)
@@ -353,7 +353,7 @@
 Check the files look OK.
 
     >>> fh = tarfile.extractfile('rosetta-grumpy/es/LC_MESSAGES/test.po')
-    >>> print fh.read().decode('UTF-8')
+    >>> print(fh.read().decode('UTF-8'))
     # Spanish translation for evolution
     # Copyright (c) ... Rosetta Contributors and Canonical Ltd ...
     # This file is distributed under the same license as the evolution pack...
@@ -377,7 +377,7 @@
     msgstr "blah (es)"
 
     >>> fh = tarfile.extractfile('rosetta-grumpy/cy/LC_MESSAGES/test.po')
-    >>> print fh.read().decode('UTF-8')
+    >>> print(fh.read().decode('UTF-8'))
     # Welsh translation for evolution
     # Copyright (c) ... Rosetta Contributors and Canonical Ltd ...
     # This file is distributed under the same license as the evolution pack...
@@ -513,7 +513,7 @@
 
     >>> proc = get_subprocess('cronscripts/language-pack-exporter.py')
     >>> (out, err) = proc.communicate()
-    >>> print err
+    >>> print(err)
     Traceback (most recent call last):
      ...
     lp.services.scripts.base.LaunchpadScriptFailure:
@@ -536,7 +536,7 @@
     >>> proc = get_subprocess(
     ...     'cronscripts/language-pack-exporter.py ubuntu hoary')
     >>> (out, err) = proc.communicate()
-    >>> print err
+    >>> print(err)
     INFO    Setting lockfile name to
             launchpad-language-pack-exporter__ubuntu__hoary.lock.
     INFO    Creating lockfile:
@@ -549,10 +549,8 @@
     INFO    Done.
     INFO    Registered the language pack.
 
-    >>> print out
+    >>> print(out)
     <BLANKLINE>
 
     >>> proc.returncode
     0
-
-

=== modified file 'lib/lp/translations/doc/poexport-queue.txt'
--- lib/lp/translations/doc/poexport-queue.txt	2011-12-22 05:09:10 +0000
+++ lib/lp/translations/doc/poexport-queue.txt	2018-06-02 14:12:45 +0000
@@ -133,7 +133,7 @@
 Those are specially handled and reported.
 
     >>> try:
-    ...     raise AssertionError, "Really nasty \xc3 non-ASCII error!"
+    ...     raise AssertionError, b"Really nasty \xc3 non-ASCII error!"
     ... except AssertionError:
     ...     result.addFailure()
     >>> result.notify()
@@ -351,7 +351,7 @@
     >>> process_queue(transaction, logging.getLogger())
     INFO:root:Stored file at http://...eo.po
 
-    >>> print get_newest_librarian_file().read()
+    >>> print(get_newest_librarian_file().read())
     # Esperanto translation for ...
     ...
     "X-Generator: Launchpad (build ...)\n"
@@ -372,7 +372,7 @@
     >>> process_queue(transaction, logging.getLogger())
     INFO:root:Stored file at http://...eo.po
 
-    >>> print get_newest_librarian_file().read()
+    >>> print(get_newest_librarian_file().read())
     # IMPORTANT: This file does NOT contain a complete PO file structure.
     # DO NOT attempt to import this file back into Launchpad.
     ...
@@ -391,5 +391,3 @@
     >>> process_queue(fake_transaction, logging.getLogger())
     >>> len(pop_notifications())
     0
-
-

=== modified file 'lib/lp/translations/doc/poexport-request-productseries.txt'
--- lib/lp/translations/doc/poexport-request-productseries.txt	2014-01-30 09:58:18 +0000
+++ lib/lp/translations/doc/poexport-request-productseries.txt	2018-06-02 14:12:45 +0000
@@ -34,7 +34,6 @@
 
 Now we request that the queue be processed.
 
-    >>> import logging
     >>> import transaction
     >>> from lp.translations.scripts.po_export_queue import process_queue
     >>> logger = FakeLogger()
@@ -89,7 +88,7 @@
     >>> from lp.services.helpers import string_to_tarfile
     >>> tarball = string_to_tarfile(urllib2.urlopen(url).read())
     >>> for name in sorted(tarball.getnames()):
-    ...     print name
+    ...     print(name)
     evolution-2.2
     evolution-2.2/evolution-2.2-es.po
     po

=== modified file 'lib/lp/translations/doc/poexport-request.txt'
--- lib/lp/translations/doc/poexport-request.txt	2014-02-27 09:30:38 +0000
+++ lib/lp/translations/doc/poexport-request.txt	2018-06-02 14:12:45 +0000
@@ -91,7 +91,7 @@
     >>> from lp.services.helpers import string_to_tarfile
     >>> tarball = string_to_tarfile(urllib2.urlopen(url).read())
     >>> for name in sorted(tarball.getnames()):
-    ...     print name
+    ...     print(name)
     pmount
     pmount/pmount-ca.po
     pmount/pmount-cs.po
@@ -115,7 +115,7 @@
     ...         render_request(request)
     ...         for request in POExportRequest.select()]
     ...     for request in sorted(requests):
-    ...         print request
+    ...         print(request)
     >>> print_queue()
 
     >>> request_set.addRequest(person, None, [ca])
@@ -249,7 +249,7 @@
     ...     stderr=subprocess.STDOUT
     ...     )
     >>> (output, empty) = process.communicate()
-    >>> print output
+    >>> print(output)
     DEBUG   ...
     INFO    Creating lockfile: /var/lock/launchpad-rosetta-export-queue.lock
     DEBUG   Exporting objects for Happy Downloader, related to template
@@ -265,4 +265,3 @@
     DEBUG   rosetta-export-queue ran in ...s (excl. load & lock)
     DEBUG   Removing lock file: /var/lock/launchpad-rosetta-export-queue.lock
     <BLANKLINE>
-

=== modified file 'lib/lp/translations/doc/poexportqueue-replication-lag.txt'
--- lib/lp/translations/doc/poexportqueue-replication-lag.txt	2013-06-20 05:50:00 +0000
+++ lib/lp/translations/doc/poexportqueue-replication-lag.txt	2018-06-02 14:12:45 +0000
@@ -53,7 +53,7 @@
     ...         else:
     ...             summary.append('(template)')
     ...     for entry in sorted(summary):
-    ...         print entry
+    ...         print(entry)
 
     >>> summarize_request(queue.getRequest())
     (template)
@@ -75,7 +75,7 @@
 is still technically on the queue, but no longer "live."
 
     >>> person, sources, format, request_ids = repeated_request
-    >>> print len(request_ids)
+    >>> print(len(request_ids))
     3
     >>> queue.removeRequest(request_ids)
 

=== modified file 'lib/lp/translations/doc/pofile-verify-stats.txt'
--- lib/lp/translations/doc/pofile-verify-stats.txt	2012-08-06 22:14:44 +0000
+++ lib/lp/translations/doc/pofile-verify-stats.txt	2018-06-02 14:12:45 +0000
@@ -109,7 +109,7 @@
     ...     if in_header:
     ...         in_header = (line != '')
     ...     else:
-    ...         print line
+    ...         print(line)
     The POFile statistics verifier encountered errors while checking cached
     statistics in the database:
     <BLANKLINE>
@@ -129,10 +129,9 @@
     >>> from lp.testing.script import run_script
     >>> (returncode, out, err) = run_script(
     ...     'cronscripts/rosetta-pofile-stats.py', ['--start-id=99'])
-    >>> print returncode
+    >>> print(returncode)
     0
-    >>> print err
+    >>> print(err)
     INFO    Creating lockfile: /var/lock/launchpad-pofile-stats.lock
     INFO    Starting verification of POFile stats at id 99
     INFO    Done.
-

=== modified file 'lib/lp/translations/doc/pofile.txt'
--- lib/lp/translations/doc/pofile.txt	2016-01-26 15:47:37 +0000
+++ lib/lp/translations/doc/pofile.txt	2018-06-02 14:12:45 +0000
@@ -93,9 +93,9 @@
     ...                translation = message.translations[0]
     ...             if len(translation) > 20:
     ...                 translation = translation[:17] + "..."
-    ...         print "%2d. %-20s   %-20s   %-20s" % (
+    ...         print("%2d. %-20s   %-20s   %-20s" % (
     ...             potmsgset.getSequence(pofile.potemplate),
-    ...             singular, plural, translation)
+    ...             singular, plural, translation))
 
 
 getFullLanguageCode
@@ -103,11 +103,11 @@
 
 Returns the complete code for this POFile's language.
 
-    >>> print potemplate.getPOFileByLang('es').getFullLanguageCode()
+    >>> print(potemplate.getPOFileByLang('es').getFullLanguageCode())
     es
 
     >>> sr_latin = factory.makeLanguage('sr@latin', 'Serbian Latin')
-    >>> print potemplate.getDummyPOFile(sr_latin).getFullLanguageCode()
+    >>> print(potemplate.getDummyPOFile(sr_latin).getFullLanguageCode())
     sr@latin
 
 
@@ -116,10 +116,10 @@
 
 Returns the complete English name for this POFile's language.
 
-    >>> print potemplate.getPOFileByLang('es').getFullLanguageName()
+    >>> print(potemplate.getPOFileByLang('es').getFullLanguageName())
     Spanish
 
-    >>> print potemplate.getDummyPOFile(sr_latin).getFullLanguageName()
+    >>> print(potemplate.getDummyPOFile(sr_latin).getFullLanguageName())
     Serbian Latin
 
 
@@ -289,7 +289,7 @@
     [u' traducci\xf3n de es.po al Spanish',
      u' translation of es.po to Spanish']
 
-    >>> print pofile.header
+    >>> print(pofile.header)
     Project-Id-Version: es
     POT-Creation-Date: 2004-08-17 11:10+0200
     PO-Revision-Date: 2005-04-07 13:22+0000
@@ -307,7 +307,7 @@
 
 And the new header contains the new string.
 
-    >>> print pofile.header
+    >>> print(pofile.header)
     Project-Id-Version: es
     Report-Msgid-Bugs-To: serrador@xxxxxxxxxxxxx
     POT-Creation-Date: 2004-08-18 11:10+0200
@@ -328,7 +328,7 @@
 translators forget to update that field from time to time and sometimes,
 we were losing translations because we were ignoring those imports too.
 
-    >>> print pofile.header
+    >>> print(pofile.header)
     Project-Id-Version: es
     ...
     PO-Revision-Date: 2005-08-18 13:22+0000
@@ -414,7 +414,7 @@
 the most common number of plural forms:
 
     >>> divehi = getUtility(ILanguageSet)['dv']
-    >>> print divehi.pluralforms
+    >>> print(divehi.pluralforms)
     None
 
     >>> evolution_dv = evolution_pot.getDummyPOFile(divehi)
@@ -431,7 +431,7 @@
 
     >>> from lp.translations.model.pofile import POFile
     >>> pofile = POFile.get(24)
-    >>> print pofile.header
+    >>> print(pofile.header)
     Project-Id-Version: PACKAGE VERSION
     ...
     Content-Type: text/plain; charset=EUC-JP
@@ -445,16 +445,16 @@
 The header is not changed.
 
     >>> for i in range(len(stream_list)):
-    ...     if stream_list[i].startswith('"Content-Type:'):
-    ...         print stream_list[i]
+    ...     if stream_list[i].startswith(b'"Content-Type:'):
+    ...         print(stream_list[i])
     "Content-Type: text/plain; charset=EUC-JP\n"
 
 And checking one of the translations, we can see that it's using the
 EUC-JP encoding.
 
     >>> for i in range(len(stream_list)):
-    ...     if (stream_list[i].startswith('msgstr') and
-    ...         'prefs.js' in stream_list[i]):
+    ...     if (stream_list[i].startswith(b'msgstr') and
+    ...         b'prefs.js' in stream_list[i]):
     ...         break
     >>> stream_list[i]
     'msgstr "\xc0\xdf\xc4\xea\xa4\xce\xa5\xab...\xa5\xba\xa4\xcb
@@ -469,15 +469,15 @@
 We can see that the header has been updated to have UTF-8
 
     >>> for i in range(len(stream_list)):
-    ...     if stream_list[i].startswith('"Content-Type:'):
-    ...         print stream_list[i]
+    ...     if stream_list[i].startswith(b'"Content-Type:'):
+    ...         print(stream_list[i])
     "Content-Type: text/plain; charset=UTF-8\n"
 
 And the encoding used is also using UTF-8 chars.
 
     >>> for i in range(len(stream_list)):
-    ...     if (stream_list[i].startswith('msgstr') and
-    ...         'prefs.js' in stream_list[i]):
+    ...     if (stream_list[i].startswith(b'msgstr') and
+    ...         b'prefs.js' in stream_list[i]):
     ...         break
     >>> stream_list[i]
     'msgstr "\xe8\xa8\xad\xe5\xae\x9a\xe3\x81...\xba\xe3\x81\xab
@@ -490,7 +490,7 @@
 So for a concrete export, we have a message like:
 
     >>> pofile_es = potemplate.getPOFileByLang('es')
-    >>> print pofile_es.export(force_utf8=True).decode('utf8')
+    >>> print(pofile_es.export(force_utf8=True).decode('utf8'))
     # ...
     ...
     #: addressbook/gui/widgets/foo.c:345
@@ -507,7 +507,7 @@
 
     # It has plural forms.
 
-    >>> print potmsgset.plural_text
+    >>> print(potmsgset.plural_text)
     %d bars
 
     # We change the plural form.
@@ -515,12 +515,12 @@
     >>> potmsgset.updatePluralForm(u'something else')
     >>> from lp.services.database.sqlbase import flush_database_updates
     >>> flush_database_updates()
-    >>> print potmsgset.plural_text
+    >>> print(potmsgset.plural_text)
     something else
 
 ...the export reflects that change.
 
-    >>> print pofile_es.export(force_utf8=True).decode('utf8')
+    >>> print(pofile_es.export(force_utf8=True).decode('utf8'))
     # ...
     ...
     #: addressbook/gui/widgets/foo.c:345
@@ -549,9 +549,9 @@
 
     >>> potmsgset = pofile_sr.potemplate.getOrCreateSharedPOTMsgSet(
     ...     singular_text=msgid, plural_text=msgid_plural)
-    >>> print potmsgset.getCurrentTranslation(
+    >>> print(potmsgset.getCurrentTranslation(
     ...     pofile_sr.potemplate, pofile_sr.language,
-    ...     pofile_sr.potemplate.translation_side)
+    ...     pofile_sr.potemplate.translation_side))
     None
 
 Is time to create it.  We need some extra privileges here.
@@ -568,13 +568,13 @@
 
 As we can see, is the msgid we were looking for.
 
-    >>> print translation_message.potmsgset.msgid_singular.msgid
+    >>> print(translation_message.potmsgset.msgid_singular.msgid)
     Found %i invalid file.
 
-    >>> print pofile_sr.language.code
+    >>> print(pofile_sr.language.code)
     sr
 
-    >>> print translation_message.language.code
+    >>> print(translation_message.language.code)
     sr
 
 We created it without translations.
@@ -592,8 +592,8 @@
     >>> def print_names(persons):
     ...     """Print name for each of `persons`."""
     ...     for person in persons:
-    ...         print person.name
-    ...     print "--"
+    ...         print(person.name)
+    ...     print("--")
 
     >>> evolution = getUtility(IProductSet).getByName('evolution')
     >>> evolution_trunk = evolution.getSeries('trunk')
@@ -660,8 +660,8 @@
 translated for a given POFile.
 
     >>> def print_message_status(potmsgsets, pofile):
-    ...     print "%-10s %-5s %-10s %-11s" % (
-    ...         "msgid", "form", "translat.", "Has plurals")
+    ...     print("%-10s %-5s %-10s %-11s" % (
+    ...         "msgid", "form", "translat.", "Has plurals"))
     ...     for potmsgset in potmsgsets:
     ...         translationmessage = potmsgset.getCurrentTranslation(
     ...             pofile.potemplate, pofile.language,
@@ -676,9 +676,9 @@
     ...                 translation = translationmessage.translations[index]
     ...                 if len(translation) > 10:
     ...                     translation = translation[:7] + '...'
-    ...             print "%-10s %-5s %-10s %s" % (
+    ...             print("%-10s %-5s %-10s %s" % (
     ...                 msgid, index, translation,
-    ...                 potmsgset.msgid_plural is not None)
+    ...                 potmsgset.msgid_plural is not None))
 
     >>> potmsgsets_translated = evolution_es.getPOTMsgSetTranslated()
     >>> print_message_status(
@@ -706,7 +706,7 @@
     >>> carlos = person_set.getByName('carlos')
     >>> translationmessages = evolution_es.getTranslationsFilteredBy(carlos)
     >>> for translationmessage in translationmessages:
-    ...     print translationmessage.translations
+    ...     print(translationmessage.translations)
     [u'libreta de direcciones de Evolution']
     [u'carpeta de libretas de direcciones actual']
     [u'lalalala']
@@ -751,7 +751,7 @@
     >>> current = potmsgset.getCurrentTranslation(
     ...     alsa_template, alsa_translation.language,
     ...     alsa_template.translation_side)
-    >>> print current.translations
+    >>> print(current.translations)
     [u'This is a dummy translation so that
     the credits are counted as translated.']
 
@@ -765,7 +765,7 @@
     >>> current = potmsgset.getCurrentTranslation(
     ...     alsa_template, alsa_translation.language,
     ...     alsa_template.translation_side)
-    >>> print current.translations
+    >>> print(current.translations)
     [u'Happy translator']
 
 If we submit non-upstream translation, it's rejected.
@@ -773,14 +773,14 @@
     >>> no_credits = potmsgset.submitSuggestion(
     ...     alsa_translation, alsa_translation.owner,
     ...     {0: u'Unhappy translator'})
-    >>> print no_credits
+    >>> print(no_credits)
     None
 
     >>> flush_database_updates()
     >>> current = potmsgset.getCurrentTranslation(
     ...     alsa_template, alsa_translation.language,
     ...     alsa_template.translation_side)
-    >>> print current.translations
+    >>> print(current.translations)
     [u'Happy translator']
 
 
@@ -812,7 +812,7 @@
 We get an updated header based on some metadata in our database instead
 of the imported one stored in POFile.header.
 
-    >>> print evolution_ja.header
+    >>> print(evolution_ja.header)
     Project-Id-Version: evolution
     Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>
     POT-Creation-Date: 2005-05-06 20:39:27.778946+00:00
@@ -824,7 +824,7 @@
     Content-Transfer-Encoding: 8bit
     Plural-Forms: nplurals=1; plural=0
 
-    >>> print translation_file_data.header.getRawContent()
+    >>> print(translation_file_data.header.getRawContent())
     Project-Id-Version: evolution
     Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>
     POT-Creation-Date: 2005-04-07 14:10+0200
@@ -846,7 +846,7 @@
 
 And the PO Revision Date matches when was the PO file last changed.
 
-    >>> print evolution_ja.date_changed
+    >>> print(evolution_ja.date_changed)
     2005-10-11 23:08:01.899322+00:00
 
 

=== modified file 'lib/lp/translations/doc/poimport-pofile-not-exported-from-rosetta.txt'
--- lib/lp/translations/doc/poimport-pofile-not-exported-from-rosetta.txt	2012-11-21 16:50:43 +0000
+++ lib/lp/translations/doc/poimport-pofile-not-exported-from-rosetta.txt	2018-06-02 14:12:45 +0000
@@ -66,7 +66,7 @@
 header 'X-Rosetta-Export-Date'. That header is the one that notes that the
 file comes from a previous export from Rosetta and when did it happen.
 
-    >>> pofile_contents = r'''
+    >>> pofile_contents = br'''
     ... msgid ""
     ... msgstr ""
     ... "PO-Revision-Date: 2005-06-03 19:41+0100\n"
@@ -100,7 +100,7 @@
     >>> subject
     u'Import problem - Welsh (cy) - firefox in Mozilla Firefox trunk'
 
-    >>> print message
+    >>> print(message)
     Hello Mark Shuttleworth,
     <BLANKLINE>
     On ..., you uploaded a file with
@@ -127,7 +127,7 @@
 A much shorter version of that information is stored in the entry's
 error_output.
 
-    >>> print entry.error_output
+    >>> print(entry.error_output)
     File was not exported from Launchpad.
 
 We should also be sure that we don't block any import that is coming from
@@ -168,14 +168,13 @@
 The import code has also composed an email with the notification of the
 import.
 
-    >>> print subject
+    >>> print(subject)
     None
-    >>> print message
+    >>> print(message)
     Hello Mark Shuttleworth,
     ...
 
 There was no error output this time.
 
-    >>> print entry.error_output
+    >>> print(entry.error_output)
     None
-

=== modified file 'lib/lp/translations/doc/poimport-pofile-old-po-imported.txt'
--- lib/lp/translations/doc/poimport-pofile-old-po-imported.txt	2012-11-21 16:50:43 +0000
+++ lib/lp/translations/doc/poimport-pofile-old-po-imported.txt	2018-06-02 14:12:45 +0000
@@ -62,7 +62,7 @@
 
 First, we do a valid import.
 
-    >>> pofile_contents = r'''
+    >>> pofile_contents = br'''
     ... msgid ""
     ... msgstr ""
     ... "PO-Revision-Date: 2005-05-03 20:41+0100\n"
@@ -88,7 +88,7 @@
 We do the import.  This succeeds without errors.
 
     >>> (subject, message) = pofile.importFromQueue(entry)
-    >>> print entry.error_output
+    >>> print(entry.error_output)
     None
 
 The status is now IMPORTED:
@@ -102,7 +102,7 @@
 We can see that the header has the same 'PO-Revision-Date' as the
 file we just imported.
 
-    >>> print pofile.header
+    >>> print(pofile.header)
     Project-Id-Version:...
     PO-Revision-Date: 2005-05-03 20:41+0100
     ...
@@ -110,7 +110,7 @@
 Now, we are going to import a .po file that has a 'PO-Revision-Date'
 field with a date older than a previous .po import.
 
-    >>> pofile_contents = r'''
+    >>> pofile_contents = br'''
     ... msgid ""
     ... msgstr ""
     ... "PO-Revision-Date: 2005-05-03 19:41+0100\n"
@@ -142,14 +142,14 @@
     >>> entry.status == RosettaImportStatus.FAILED
     True
 
-    >>> print entry.error_output
+    >>> print(entry.error_output)
     Outdated translation.  The last imported version of this file was dated
     2005-05-03 20:41:00+01:00; the timestamp in the file you uploaded is
     2005-05-03 19:41:00+01:00.
 
 We can see that the header remains unchanged
 
-    >>> print pofile.header
+    >>> print(pofile.header)
     Project-Id-Version:...
     PO-Revision-Date: 2005-05-03 20:41+0100
     ...
@@ -158,7 +158,7 @@
 
     >>> subject
     u'Import problem - Welsh (cy) - firefox in Mozilla Firefox trunk'
-    >>> print message
+    >>> print(message)
     Hello Mark Shuttleworth,
     <BLANKLINE>
     On ..., you uploaded a file with
@@ -202,12 +202,12 @@
 
     >>> entry.status == RosettaImportStatus.IMPORTED
     True
-    >>> print entry.error_output
+    >>> print(entry.error_output)
     None
 
 But the header remains unchanged, so that the older date is not recorded.
 
-    >>> print pofile.header
+    >>> print(pofile.header)
     Project-Id-Version:...
     PO-Revision-Date: 2005-05-03 20:41+0100
     ...

=== modified file 'lib/lp/translations/doc/poimport-pofile-syntax-error.txt'
--- lib/lp/translations/doc/poimport-pofile-syntax-error.txt	2012-11-21 16:50:43 +0000
+++ lib/lp/translations/doc/poimport-pofile-syntax-error.txt	2018-06-02 14:12:45 +0000
@@ -58,7 +58,7 @@
 Let's import a .po file that misses the '"' char after msgstr. That's a
 syntax error.
 
-    >>> pofile_contents = r'''
+    >>> pofile_contents = br'''
     ... msgid ""
     ... msgstr ""
     ... "PO-Revision-Date: 2005-06-03 20:41+0100\n"
@@ -84,10 +84,10 @@
 The import fails.
 
     >>> (subject, message) = pofile.importFromQueue(entry)
-    >>> print entry.status.name
+    >>> print(entry.status.name)
     FAILED
 
-    >>> print entry.error_output
+    >>> print(entry.error_output)
     Line 12: String is not quoted
 
 And the code composed an email with the notification of the error.
@@ -95,7 +95,7 @@
     >>> subject
     u'Import problem - Welsh (cy) - firefox in Mozilla Firefox trunk'
 
-    >>> print message
+    >>> print(message)
     Hello Mark Shuttleworth,
     <BLANKLINE>
     On ..., you uploaded a file with Welsh (cy) translations for firefox in
@@ -143,7 +143,7 @@
     >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts)
     >>> transaction.commit()
     >>> (subject, message) = pofile.importFromQueue(entry)
-    >>> print entry.status.name
+    >>> print(entry.status.name)
     FAILED
 
 An email describes the problem in relatively helpful terms.
@@ -151,7 +151,7 @@
     >>> subject
     u'Import problem - Frisian (fy) - firefox in Mozilla Firefox trunk'
 
-    >>> print message
+    >>> print(message)
     Hello Mark Shuttleworth,
     <BLANKLINE>
     On ..., you uploaded a file with Frisian (fy) translations for
@@ -175,7 +175,7 @@
 
 The error output field is more terse.
 
-    >>> print entry.error_output
+    >>> print(entry.error_output)
     'ascii' codec can't decode byte ... in position ...: ordinal not in
     range(128)
 
@@ -194,7 +194,7 @@
 submits a translation with a nonsensical plurals definition.
 
     >>> pofile = potemplate.newPOFile('sux')
-    >>> pofile_contents = r'''
+    >>> pofile_contents = br'''
     ... msgid ""
     ... msgstr ""
     ... "PO-Revision-Date: 2005-06-29 11:44+0100\n"
@@ -215,13 +215,13 @@
 
 The submission is rejected with a syntax error.
 
-    >>> print entry.status.name
+    >>> print(entry.status.name)
     FAILED
 
     >>> subject
     u'Import problem - Sumerian (sux) - firefox in Mozilla Firefox trunk'
 
-    >>> print message
+    >>> print(message)
     Hello Mark Shuttleworth,
     ...
     <BLANKLINE>
@@ -238,7 +238,7 @@
 Mark mistakenly attempts to import a translation with "zero" plural
 forms.  He receives an email notifying him of a syntax error.
 
-    >>> pofile_contents = r'''
+    >>> pofile_contents = br'''
     ... msgid ""
     ... msgstr ""
     ... "PO-Revision-Date: 2005-06-14 18:33+0100\n"
@@ -257,13 +257,13 @@
     >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts)
     >>> (subject, message) = pofile.importFromQueue(entry)
 
-    >>> print entry.status.name
+    >>> print(entry.status.name)
     FAILED
 
     >>> subject
     u'Import problem - Sumerian (sux) - firefox in Mozilla Firefox trunk'
 
-    >>> print message
+    >>> print(message)
     Hello Mark Shuttleworth,
     ...
     <BLANKLINE>
@@ -276,7 +276,7 @@
 On his next attempt, Mark accidentally types a negative number of plural
 forms.  The same error is given.
 
-    >>> pofile_contents = r'''
+    >>> pofile_contents = br'''
     ... msgid ""
     ... msgstr ""
     ... "PO-Revision-Date: 2005-06-15 19:04+0100\n"
@@ -295,13 +295,13 @@
     >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts)
     >>> (subject, message) = pofile.importFromQueue(entry)
 
-    >>> print entry.status.name
+    >>> print(entry.status.name)
     FAILED
 
     >>> subject
     u'Import problem - Sumerian (sux) - firefox in Mozilla Firefox trunk'
 
-    >>> print message
+    >>> print(message)
     Hello Mark Shuttleworth,
     ...
     We were unable to import the file because of errors in its format:
@@ -325,7 +325,7 @@
     >>> pofile = potemplate.newPOFile('ar')
 
     # PO file with nplurals=7, a value we can't handle.
-    >>> pofile_contents = r'''
+    >>> pofile_contents = br'''
     ... msgid ""
     ... msgstr ""
     ... "PO-Revision-Date: 2005-07-01 08:35+0100\n"
@@ -351,13 +351,13 @@
     >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts)
     >>> (subject, message) = pofile.importFromQueue(entry)
 
-    >>> print entry.status.name
+    >>> print(entry.status.name)
     FAILED
 
     >>> subject
     u'Import problem - Arabic (ar) - firefox in Mozilla Firefox trunk'
 
-    >>> print message
+    >>> print(message)
     Hello Mark Shuttleworth,
     <BLANKLINE>
     On ..., you uploaded a file with Arabic (ar) translations for firefox in
@@ -389,7 +389,7 @@
 plural forms, the file imports just fine.
 
     # Same PO file as before, but with nplurals=6.
-    >>> pofile_contents = r'''
+    >>> pofile_contents = br'''
     ... msgid ""
     ... msgstr ""
     ... "PO-Revision-Date: 2005-07-01 08:35+0100\n"
@@ -414,7 +414,5 @@
     >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts)
     >>> (subject, message) = pofile.importFromQueue(entry)
 
-    >>> print entry.status.name
+    >>> print(entry.status.name)
     IMPORTED
-
-

=== modified file 'lib/lp/translations/doc/poimport-potemplate-syntax-error.txt'
--- lib/lp/translations/doc/poimport-potemplate-syntax-error.txt	2012-11-21 16:50:43 +0000
+++ lib/lp/translations/doc/poimport-potemplate-syntax-error.txt	2018-06-02 14:12:45 +0000
@@ -52,7 +52,7 @@
 Let's import a .pot file that is missing its header. That's a
 TranslationFormatSyntaxError
 
-    >>> potemplate_contents = r'''
+    >>> potemplate_contents = br'''
     ... msgid "foo"
     ... msgstr ""
     ... '''
@@ -66,17 +66,17 @@
 
 The import failed.
 
-    >>> print entry.status.name
+    >>> print(entry.status.name)
     FAILED
 
-    >>> print entry.error_output
+    >>> print(entry.error_output)
     No header found in this pofile
 
 And the code composed email with a notification of the error.
 
     >>> subject
     u'Import problem - firefox in Mozilla Firefox trunk'
-    >>> print message
+    >>> print(message)
     Hello Mark Shuttleworth,
     <BLANKLINE>
     On ..., you uploaded a file with translation templates for firefox in
@@ -128,15 +128,15 @@
 
 The import failed.
 
-    >>> print entry.status.name
+    >>> print(entry.status.name)
     FAILED
 
 The uploader receives an email about the encoding problem.
 
-    >>> print subject
+    >>> print(subject)
     Import problem - nonascii in Mozilla Firefox trunk
 
-    >>> print message
+    >>> print(message)
     Hello Mark Shuttleworth,
     <BLANKLINE>
     On ..., you uploaded a file with translation templates for nonascii
@@ -160,6 +160,6 @@
 The queue entry's error_output field also contains a brief
 description of the error.
 
-    >>> print entry.error_output
+    >>> print(entry.error_output)
     'ascii' codec can't decode byte ... in position ...: ordinal not in
     range(128)

=== modified file 'lib/lp/translations/doc/poimport.txt'
--- lib/lp/translations/doc/poimport.txt	2011-09-26 06:49:22 +0000
+++ lib/lp/translations/doc/poimport.txt	2018-06-02 14:12:45 +0000
@@ -56,7 +56,7 @@
 
 This is the file that'll get imported.
 
-    >>> potemplate_contents = r'''
+    >>> potemplate_contents = br'''
     ... msgid ""
     ... msgstr ""
     ... "POT-Creation-Date: 2004-07-11 16:16+0900\n"
@@ -125,7 +125,7 @@
 
 Our request has now been serviced.
 
-    >>> print entry.status.name
+    >>> print(entry.status.name)
     IMPORTED
 
 The last update date is the one we got.
@@ -135,12 +135,12 @@
 
 We don't send mail about successful imports.
 
-    >>> print subject
+    >>> print(subject)
     None
 
 Since there was no error, the queue entry's error_output is blank.
 
-    >>> print entry.error_output
+    >>> print(entry.error_output)
     None
 
 The correct message IDs now show up in the template.
@@ -195,7 +195,7 @@
 
 By default, we got a safe path to prevent collisions with other IPOFile.
 
-    >>> print pofile.path
+    >>> print(pofile.path)
     unique-...-cy.po
 
 Let's override the default good path with one we know is the right one.
@@ -220,7 +220,7 @@
 Here are the contents of the file we'll be importing. It has some
 validation errors.
 
-    >>> pofile_with_errors = r'''
+    >>> pofile_with_errors = br'''
     ... msgid ""
     ... msgstr ""
     ... "PO-Revision-Date: 2005-06-03 19:41+0100\n"
@@ -288,7 +288,7 @@
 
 The status is now IMPORTED:
 
-    >>> print entry.status.name
+    >>> print(entry.status.name)
     IMPORTED
 
 Three translations have been properly imported. Note that the translation
@@ -296,9 +296,9 @@
 not appear in the POTemplate.
 
     >>> def show_translation_details(translationmessage, pofile):
-    ...     print translationmessage.potmsgset.singular_text,
-    ...     print translationmessage.translations,
-    ...     print translationmessage.potmsgset.getSequence(pofile.potemplate)
+    ...     print(translationmessage.potmsgset.singular_text, end=' ')
+    ...     print(translationmessage.translations, end=' ')
+    ...     print(translationmessage.potmsgset.getSequence(pofile.potemplate))
     >>> for translationmessage in pofile.translation_messages:
     ...     if translationmessage.is_current_ubuntu:
     ...         show_translation_details(translationmessage, pofile)
@@ -321,7 +321,7 @@
 set in the PO template.
 
     >>> message = pofile.translation_messages[1]
-    >>> print message.potmsgset.singular_text
+    >>> print(message.potmsgset.singular_text)
     foo
 
 And should be accepted by our validator.
@@ -339,7 +339,7 @@
     ...         pofile.potemplate, pofile.language,
     ...         pofile.potemplate.translation_side)
     >>> message = get_pofile_translation_message(pofile, u'bar')
-    >>> print message
+    >>> print(message)
     None
 
 Check that the plural form was imported correctly.
@@ -348,7 +348,7 @@
     ...     u'Singular %d', u'Plural %d')
     >>> imported_translationmessage = potmsgset.getOtherTranslation(
     ...     pofile.language, pofile.potemplate.translation_side)
-    >>> print imported_translationmessage.validation_status.name
+    >>> print(imported_translationmessage.validation_status.name)
     OK
 
     >>> imported_translationmessage.translations
@@ -365,10 +365,10 @@
 doesn't send it out for published uploads (indicated with subject of
 None).
 
-    >>> print subject
+    >>> print(subject)
     None
 
-    >>> print body
+    >>> print(body)
     Hello Mark Shuttleworth,
     <BLANKLINE>
     On ..., you uploaded 5
@@ -391,7 +391,7 @@
 The error information is also stored more compactly in the entry's
 error_output.
 
-    >>> print entry.error_output
+    >>> print(entry.error_output)
     Imported, but with errors:
     <BLANKLINE>
     4. "format spec... 'msgid' and 'msgstr' for argument 1 are not the same":
@@ -410,7 +410,7 @@
 
 For example, here's a gettext PO file with two headers.
 
-    >>> pofile_with_warning = r'''
+    >>> pofile_with_warning = br'''
     ... msgid ""
     ... msgstr ""
     ... "Content-Type: text/plain; charset=UTF-8\n"
@@ -441,7 +441,7 @@
 number points at the next message.  There's not much we can do about
 that, but it should help a bit.
 
-    >>> print message
+    >>> print(message)
     Hello ...
     This mail is to notify you that all translations have now been
     imported.
@@ -456,7 +456,7 @@
     <BLANKLINE>
     The Launchpad team
 
-    >>> print warning_entry.error_output
+    >>> print(warning_entry.error_output)
     There were warnings while parsing the file.  These are not
     fatal, but please correct them if you can.
     <BLANKLINE>
@@ -471,7 +471,7 @@
 Now, let's import one without errors. This file changes one translation
 and adds another one.
 
-    >>> pofile_without_errors = r'''
+    >>> pofile_without_errors = br'''
     ... msgid ""
     ... msgstr ""
     ... "PO-Revision-Date: 2005-06-03 20:41+0100\n"
@@ -498,7 +498,7 @@
 
 The new upload clears the entry's error_output.
 
-    >>> print entry.error_output
+    >>> print(entry.error_output)
     None
 
 The guess IPOFile should be the same we already had.
@@ -520,7 +520,7 @@
 
 The status is now IMPORTED:
 
-    >>> print entry.status.name
+    >>> print(entry.status.name)
     IMPORTED
 
 Since the translations from the older imports are still present,
@@ -543,17 +543,17 @@
 that nothing is emailed out (subject is None) because this is an upstream
 upload.
 
-    >>> print subject
+    >>> print(subject)
     None
 
-    >>> print body
+    >>> print(body)
     Hello Rosetta Administrators,
     <BLANKLINE>
     ...
 
 There was no error output either.
 
-    >>> print entry.error_output
+    >>> print(entry.error_output)
     None
 
 The translation has been augmented with the strings from the imported
@@ -621,7 +621,7 @@
 plural forms).
 
     >>> language = factory.makeLanguage()
-    >>> print language.pluralforms
+    >>> print(language.pluralforms)
     None
 
     >>> potemplate = factory.makePOTemplate(
@@ -632,7 +632,7 @@
 
 We'll import a POFile with 3 plural forms into this POFile:
 
-    >>> pofile_with_plurals = r'''
+    >>> pofile_with_plurals = br'''
     ... msgid ""
     ... msgstr ""
     ... "PO-Revision-Date: 2005-06-03 19:41+0100\n"
@@ -662,7 +662,7 @@
     >>> entry.setStatus(RosettaImportStatus.APPROVED, rosetta_experts)
     >>> (subject, body) = pofile.importFromQueue(entry, FakeLogger())
     >>> flush_database_updates()
-    >>> print entry.status.name
+    >>> print(entry.status.name)
     IMPORTED
 
 If we get a current translation for this PO file, it will list only two
@@ -690,7 +690,7 @@
 Add a maintainer POFile import (i.e. from a package or bzr branch),
 approve and import it.
 
-    >>> pofile_contents = r'''
+    >>> pofile_contents = br'''
     ... msgid ""
     ... msgstr ""
     ... "PO-Revision-Date: 2005-05-03 20:41+0100\n"
@@ -717,7 +717,7 @@
     >>> entry.status == RosettaImportStatus.IMPORTED
     True
 
-    >>> print subject
+    >>> print(subject)
     None
 
 For syntax errors, failure notification is still sent out.

=== modified file 'lib/lp/translations/doc/potemplate.txt'
--- lib/lp/translations/doc/potemplate.txt	2014-01-30 09:58:18 +0000
+++ lib/lp/translations/doc/potemplate.txt	2018-06-02 14:12:45 +0000
@@ -70,7 +70,7 @@
     >>> new_template = alsa_subset.new(
     ...        'testtemplate', 'testing', 'po/testing.pot', user)
 
-    >>> print new_template.header
+    >>> print(new_template.header)
     Project-Id-Version: PACKAGE VERSION
     Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>
     POT-Creation-Date: ...
@@ -95,7 +95,7 @@
 
     >>> potemplate = potemplatesubset.getPOTemplateByName(
     ...        'evolution-2.2')
-    >>> print potemplate.title
+    >>> print(potemplate.title)
     Template "evolution-2.2" in Evolution trunk
 
 
@@ -107,7 +107,7 @@
 
     >>> potemplate = potemplatesubset.getPOTemplateByPath(
     ...        'po/evolution-2.2-test.pot')
-    >>> print potemplate.title
+    >>> print(potemplate.title)
     Template "evolution-2.2-test" in Evolution trunk
 
 
@@ -140,7 +140,7 @@
     ...        productseries=productseries)
 
     >>> for template in potemplatesubset:
-    ...       print template.path
+    ...       print(template.path)
     po/evolution-2.2.pot
     po/evolution-2.2-test.pot
 
@@ -154,7 +154,7 @@
     ...        productseries=productseries)
 
     >>> for template in potemplatesubset:
-    ...       print template.path
+    ...       print(template.path)
     po/netapplet.pot
 
     >>> potemplatesubset.getClosestPOTemplate('po') is None
@@ -192,7 +192,7 @@
 We can get an IPOFile inside a template based on its path.
 
     >>> pofile = potemplate.getPOFileByPath('es.po')
-    >>> print pofile.title
+    >>> print(pofile.title)
     Spanish (es) translation of evolution-2.2 in Evolution trunk
 
 
@@ -206,7 +206,7 @@
     >>> xx_language = factory.makeLanguage(
     ...        'xx@test', name='Test language')
     >>> xx_pofile = potemplate.getDummyPOFile(xx_language)
-    >>> print xx_pofile.title
+    >>> print(xx_pofile.title)
     Test language (xx@test) translation of evolution-2.2 in Evolution trunk
 
 
@@ -249,13 +249,13 @@
 
     >>> for relative_potemplate in potemplate.relatives_by_source:
     ...        assert relative_potemplate.iscurrent
-    ...        print relative_potemplate.title
+    ...        print(relative_potemplate.title)
     Template "evolution-2.2-test" in Evolution trunk
 
 Let's get a new IPOTemplate that belongs to an IDistroSeries:
 
     >>> potemplate = POTemplate.get(4)
-    >>> print potemplate.title
+    >>> print(potemplate.title)
     Template "evolution-2.2" in Ubuntu Hoary package "evolution"
 
 And this is the list of templates related with this one based on its
@@ -263,7 +263,7 @@
 
     >>> for relative_potemplate in potemplate.relatives_by_source:
     ...        assert relative_potemplate.iscurrent
-    ...        print relative_potemplate.title
+    ...        print(relative_potemplate.title)
     Template "man" in Ubuntu Hoary package "evolution"
 
 But we can see that there is a third template in this context:
@@ -294,7 +294,7 @@
     ...        if 'X-Launchpad-Export-Date' in line:
     ...            # Avoid a time bomb in our tests and ignore this field.
     ...            continue
-    ...        print line
+    ...        print(line)
     #, fuzzy
     msgid ""
     msgstr ""
@@ -569,7 +569,7 @@
 In this case, we are going to see how a PO template is exported with
 translations.
 
-    >>> print potemplate.source_file_format.name
+    >>> print(potemplate.source_file_format.name)
     PO
 
     >>> exported_translation_file = potemplate.exportWithTranslations()
@@ -577,7 +577,7 @@
 PO file format doesn't have a native way to export template +
 translations, instead, we get a tarball with all those files.
 
-    >>> print exported_translation_file.content_type
+    >>> print(exported_translation_file.content_type)
     application/x-gtar
 
 Inspecting the tarball content, we have the list of entries exported.
@@ -606,7 +606,7 @@
     >>> from lp.services.helpers import simple_popen2
     >>> contents = simple_popen2(['tar', 'ztf', '-'], tarfile_string)
     >>> for line in sorted(contents.splitlines()):
-    ...        print line
+    ...        print(line)
     evolution-2.2/
     evolution-2.2/evolution-2.2-es.po
     evolution-2.2/evolution-2.2-ja.po
@@ -618,6 +618,5 @@
     >>> pofile = simple_popen2(
     ...        ['tar', 'zxfO', '-', 'evolution-2.2/evolution-2.2-es.po'],
     ...        tarfile_string)
-    >>> pofile.split('\n')[0]
+    >>> pofile.split(b'\n')[0]
     '# traducci\xc3\xb3n de es.po al Spanish'
-

=== modified file 'lib/lp/translations/doc/potmsgset.txt'
--- lib/lp/translations/doc/potmsgset.txt	2012-12-26 01:32:19 +0000
+++ lib/lp/translations/doc/potmsgset.txt	2018-06-02 14:12:45 +0000
@@ -44,16 +44,16 @@
 is_translation_credit indicates if the POTMsgSet is translation credits. The
 property translation_credit_type contains the type of translation credits.
 
-    >>> print potmsgset.is_translation_credit
+    >>> print(potmsgset.is_translation_credit)
     False
-    >>> print potmsgset.translation_credits_type.title
+    >>> print(potmsgset.translation_credits_type.title)
     Not a translation credits message
 
     >>> credits = factory.makePOTMsgSet(
     ...     potemplate, singular=u'translator-credits')
-    >>> print credits.is_translation_credit
+    >>> print(credits.is_translation_credit)
     True
-    >>> print credits.translation_credits_type.title
+    >>> print(credits.translation_credits_type.title)
     Gnome credits message
 
 
@@ -83,7 +83,7 @@
     >>> current = potmsgset.getCurrentTranslation(
     ...     evolution_potemplate, pt_BR_dummypofile.language,
     ...     evolution_potemplate.translation_side)
-    >>> print current
+    >>> print(current)
     None
     >>> pt_BR_dummy_current = potmsgset.getCurrentTranslationMessageOrDummy(
     ...     pt_BR_dummypofile)
@@ -94,7 +94,7 @@
 
 A TranslationMessage knows what language it is in.
 
-    >>> print pt_BR_dummy_current.language.code
+    >>> print(pt_BR_dummy_current.language.code)
     pt_BR
 
 Using another dummy pofile we'll get a POTMsgset that's not a singular
@@ -104,13 +104,13 @@
     >>> apa_dummypofile = evolution_potemplate.getDummyPOFile(language_apa)
     >>> plural_potmsgset = apa_dummypofile.potemplate.getPOTMsgSetByMsgIDText(
     ...     u'%d contact', u'%d contacts')
-    >>> print apa_dummypofile.language.code
+    >>> print(apa_dummypofile.language.code)
     apa
 
 We don't know anything about pluralforms for this language, so we fall
 back to the most common case:
 
-    >>> print apa_dummypofile.language.pluralforms
+    >>> print(apa_dummypofile.language.pluralforms)
     None
     >>> apa_dummy_current = (
     ...     plural_potmsgset.getCurrentTranslationMessageOrDummy(
@@ -128,7 +128,7 @@
     ...     plural_potmsgset.getCurrentTranslationMessageOrDummy(
     ...     	ru_dummypofile))
 
-    >>> print ru_dummypofile.language.pluralforms
+    >>> print(ru_dummypofile.language.pluralforms)
     3
     >>> ru_dummy_current.plural_forms
     3
@@ -179,9 +179,9 @@
 The number of plural forms in the Zapotec language is not configured,
 so for now, the system guesses that it has two.
 
-    >>> print zap.pluralforms
+    >>> print(zap.pluralforms)
     None
-    >>> print pm_translation.plural_forms
+    >>> print(pm_translation.plural_forms)
     2
 
     >>> pm_template = pm_translation.potemplate
@@ -294,7 +294,7 @@
     ...             suggestion.translations[0],
     ...             ' & '.join(usage)))
     ...     for line in sorted(lines):
-    ...         print line
+    ...         print(line)
 
 
 POTMsgSet.getExternallyUsedTranslationMessages
@@ -303,7 +303,7 @@
  On one side, we have a translation template for the evolution product.
 
     >>> evo_product_template = evolution_potemplate
-    >>> print evo_product_template.title
+    >>> print(evo_product_template.title)
     Template "evolution-2.2" in Evolution trunk
 
 On the other, we have a translation template for the evolution package in
@@ -316,7 +316,7 @@
     >>> evo_distro_template = templateset.getSubset(
     ...     sourcepackagename=evo_hoary_package.sourcepackagename,
     ...     distroseries=ubuntu_hoary).getPOTemplateByName('evolution-2.2')
-    >>> print evo_distro_template.title
+    >>> print(evo_distro_template.title)
     Template "evolution-2.2" in Ubuntu Hoary package "evolution"
 
 Both, product and distribution use Launchpad Translations.
@@ -446,12 +446,12 @@
     >>> potmsgset_translated = evo_man_template.getPOTMsgSetByMsgIDText(
     ...     'test man page')
     >>> pofile = evo_man_template.getPOFileByLang('es')
-    >>> print pofile.title
+    >>> print(pofile.title)
     Spanish (es) translation of man in Ubuntu Hoary package "evolution"
     >>> current = potmsgset_translated.getCurrentTranslation(
     ...     evo_man_template, pofile.language,
     ...     evo_man_template.translation_side)
-    >>> print current.translations
+    >>> print(current.translations)
     [u'just a translation']
 
 It doesn't return other submissions done in the given IPOMsgSet because
@@ -479,7 +479,7 @@
     ...     'test man page')
     >>> language_es = getUtility(ILanguageSet).getLanguageByCode('es')
     >>> pofile = pmount_man_template.getDummyPOFile(language_es)
-    >>> print pofile.title
+    >>> print(pofile.title)
     Spanish (es) translation of man in Ubuntu Hoary package "pmount"
 
 Given that it doesn't exist in our database, is impossible to have a
@@ -488,11 +488,11 @@
     >>> current = potmsgset_untranslated.getCurrentTranslation(
     ...     pmount_man_template, pofile.language,
     ...     pmount_man_template.translation_side)
-    >>> print current
+    >>> print(current)
     None
     >>> imported = potmsgset_untranslated.getOtherTranslation(
     ...     pofile.language, pmount_man_template.translation_side)
-    >>> print imported
+    >>> print(imported)
     None
 
 This other dummy IPOMsgSet though, will get all submissions done in
@@ -674,8 +674,8 @@
 
     >>> def print_flags(potmsgset):
     ...     for flag in sorted(potmsgset.flags):
-    ...         print '"%s"' % flag
-    ...     print '.'
+    ...         print('"%s"' % flag)
+    ...     print('.')
 
     >>> print_flags(flagged_potmsgset)
     "c-format"
@@ -686,4 +686,3 @@
 
     >>> print_flags(POTMsgSet())
     .
-

=== modified file 'lib/lp/translations/doc/potranslation.txt'
--- lib/lp/translations/doc/potranslation.txt	2009-07-02 17:16:50 +0000
+++ lib/lp/translations/doc/potranslation.txt	2018-06-02 14:12:45 +0000
@@ -34,11 +34,10 @@
 If you want to pass non-ascii characters to either of these, it had better be
 either UTF-8 string or, better, a unicode object.
 
-    >>> got = POTranslation.getOrCreateTranslation('\xc3\x81')
+    >>> got = POTranslation.getOrCreateTranslation(b'\xc3\x81')
     >>> got = POTranslation.getOrCreateTranslation(u'\u00c2')
 
-    >>> got = POTranslation.getOrCreateTranslation('\xc0')
+    >>> got = POTranslation.getOrCreateTranslation(b'\xc0')
     Traceback (most recent call last):
     ...
     UnicodeDecodeError: 'utf8' codec can't decode byte 0xc0 in position...
-

=== modified file 'lib/lp/translations/doc/remove-translations-by.txt'
--- lib/lp/translations/doc/remove-translations-by.txt	2011-12-23 16:39:35 +0000
+++ lib/lp/translations/doc/remove-translations-by.txt	2018-06-02 14:12:45 +0000
@@ -28,7 +28,7 @@
     ...         for message in pofile.translation_messages
     ...         if message.msgstr0 is not None)
     ...     for item in contents:
-    ...         print item
+    ...         print(item)
 
 
 == Running the script==
@@ -56,7 +56,7 @@
     True
     >>> nl_message.is_current_ubuntu
     False
-    >>> print nl_message.potmsgset.msgid_singular.msgid
+    >>> print(nl_message.potmsgset.msgid_singular.msgid)
     My goose is undercooked.
     >>> nl_message.origin == RosettaTranslationOrigin.ROSETTAWEB
     True
@@ -106,7 +106,7 @@
 The long list of matching options we gave above indicated exactly 1
 message.
 
-    >>> print err
+    >>> print(err)
     WARNING Deleting messages currently in use:
     WARNING Message ... is a current translation in upstream
     DEBUG Sample of messages to be deleted follows.
@@ -149,7 +149,7 @@
     >>> out
     ''
 
-    >>> print err
+    >>> print(err)
     WARNING Safety override in effect.  Deleting translations for template ...
     INFO    Dry run only.  Not really deleting.
     WARNING Deleting messages currently in use:

=== modified file 'lib/lp/translations/doc/rosetta-karma.txt'
--- lib/lp/translations/doc/rosetta-karma.txt	2016-01-26 15:47:37 +0000
+++ lib/lp/translations/doc/rosetta-karma.txt	2018-06-02 14:12:45 +0000
@@ -37,7 +37,7 @@
 
 Let's say that we have this .pot file to import:
 
-    >>> potemplate_contents = r'''
+    >>> potemplate_contents = br'''
     ... msgid ""
     ... msgstr ""
     ... "Content-Type: text/plain; charset=CHARSET\n"
@@ -121,7 +121,7 @@
     >>> import datetime
     >>> import pytz
     >>> UTC = pytz.timezone('UTC')
-    >>> pofile_contents = r'''
+    >>> pofile_contents = br'''
     ... msgid ""
     ... msgstr ""
     ... "Content-Type: text/plain; charset=UTF-8\n"
@@ -193,7 +193,7 @@
 Now, the user is going to upload a local edition of the .po file. In this
 case, we will give karma *only* because the translation change.
 
-    >>> pofile_contents = r'''
+    >>> pofile_contents = br'''
     ... msgid ""
     ... msgstr ""
     ... "Content-Type: text/plain; charset=UTF-8\n"

=== modified file 'lib/lp/translations/doc/rosetta-poimport-script.txt'
--- lib/lp/translations/doc/rosetta-poimport-script.txt	2012-06-14 20:21:38 +0000
+++ lib/lp/translations/doc/rosetta-poimport-script.txt	2018-06-02 14:12:45 +0000
@@ -28,11 +28,11 @@
 Provide a POFile with Last-Translator set to a user not existing in
 the sampledata.
 
-    >>> print getUtility(IPersonSet).getByEmail('danilo@xxxxxxxxxxxxx')
+    >>> print(getUtility(IPersonSet).getByEmail('danilo@xxxxxxxxxxxxx'))
     None
 
     >>> pofile = potemplate.newPOFile('sr')
-    >>> pofile_content = r'''
+    >>> pofile_content = br'''
     ... msgid ""
     ... msgstr ""
     ... "PO-Revision-Date: 2005-06-04 20:41+0100\n"
@@ -88,7 +88,7 @@
     >>> stdout, stderr = process.communicate()
     >>> process.returncode
     0
-    >>> print stderr
+    >>> print(stderr)
     INFO    Creating lockfile: /var/lock/launchpad-rosetta-poimport.lock
     INFO    Importing: Serbian (sr) ... of evolution-2.2 in Evolution trunk
     INFO    Import requests completed.
@@ -101,4 +101,3 @@
     ...     filter_status=False)
     >>> danilo.displayname
     u'Danilo \u0160egan'
-

=== modified file 'lib/lp/translations/doc/sourcepackagerelease-translations.txt'
--- lib/lp/translations/doc/sourcepackagerelease-translations.txt	2014-04-24 08:21:42 +0000
+++ lib/lp/translations/doc/sourcepackagerelease-translations.txt	2018-06-02 14:12:45 +0000
@@ -43,7 +43,7 @@
     >>> spr_test = SourcePackageRelease.get(20)
     >>> sp_test = spr_test.upload_distroseries.getSourcePackage(
     ...     spr_test.sourcepackagename)
-    >>> print spr_test.title
+    >>> print(spr_test.title)
     pmount - 0.1-1
 
     >>> from lp.soyuz.interfaces.packagetranslationsuploadjob import (
@@ -86,7 +86,7 @@
     2
 
     >>> for entry in queue_entries:
-    ...     print entry.path, entry.importer.name
+    ...     print(entry.path, entry.importer.name)
     something/en-US.xpi  maria
     po/es.po             maria
 
@@ -105,7 +105,7 @@
     ...     stderr=subprocess.STDOUT
     ...     )
     >>> (output, empty) = process.communicate()
-    >>> print output
+    >>> print(output)
     INFO    Creating lockfile:
          /var/lock/launchpad-translations-import-queue-gardener.lock
     INFO    The automatic approval system approved some entries.
@@ -121,7 +121,7 @@
     ...     stderr=subprocess.STDOUT
     ...     )
     >>> (output, empty) = process.communicate()
-    >>> print output
+    >>> print(output)
     INFO    Creating lockfile: /var/lock/launchpad-rosetta-poimport.lock
     INFO    Importing: Spanish (es) translation of pmount in Ubuntu Hoary
             package "pmount"
@@ -139,51 +139,49 @@
 
 Anything not in the "source/" directory is ignored.
 
-    >>> print _filter_ubuntu_translation_file('foo/bar.po')
+    >>> print(_filter_ubuntu_translation_file('foo/bar.po'))
     None
 
 Files in source/ have that directory stripped off.
 
-    >>> print _filter_ubuntu_translation_file('source/bar.po')
+    >>> print(_filter_ubuntu_translation_file('source/bar.po'))
     bar.po
 
 Files in source/debian/po/* and a few other paths are ignored.
 
 Ones in debian/po are generally debconf translations, unused in Ubuntu.
 
-    >>> print _filter_ubuntu_translation_file('source/debian/po/bar.po')
+    >>> print(_filter_ubuntu_translation_file('source/debian/po/bar.po'))
     None
 
 Ones in d-i are Debian Installer translations.  Ubuntu builds those
 translations very differently from how Debian does it, so we don't need
 these uploads.
 
-    >>> print _filter_ubuntu_translation_file('source/d-i/foo.po')
+    >>> print(_filter_ubuntu_translation_file('source/d-i/foo.po'))
     None
 
 Then there are some documentation directories whose contents we can't
 translate in Launchpad.
 
-    >>> print _filter_ubuntu_translation_file('source/help/xx.pot')
-    None
-
-    >>> print _filter_ubuntu_translation_file('source/man/po/yy.po')
-    None
-
-    >>> print _filter_ubuntu_translation_file('source/man/po4a/zz.pot')
+    >>> print(_filter_ubuntu_translation_file('source/help/xx.pot'))
+    None
+
+    >>> print(_filter_ubuntu_translation_file('source/man/po/yy.po'))
+    None
+
+    >>> print(_filter_ubuntu_translation_file('source/man/po4a/zz.pot'))
     None
 
 The match is on a path component boundary, so we don't filter other
 uploads whose paths happen to begin with the same words as a directory
 we filter.
 
-    >>> print _filter_ubuntu_translation_file('source/debian/pool.pot')
+    >>> print(_filter_ubuntu_translation_file('source/debian/pool.pot'))
     debian/pool.pot
 
-    >>> print _filter_ubuntu_translation_file('source/d-input.pot')
+    >>> print(_filter_ubuntu_translation_file('source/d-input.pot'))
     d-input.pot
 
-    >>> print _filter_ubuntu_translation_file('source/man/positive/nl.po')
+    >>> print(_filter_ubuntu_translation_file('source/man/positive/nl.po'))
     man/positive/nl.po
-
-

=== modified file 'lib/lp/translations/doc/translationbranchapprover.txt'
--- lib/lp/translations/doc/translationbranchapprover.txt	2016-01-26 15:47:37 +0000
+++ lib/lp/translations/doc/translationbranchapprover.txt	2018-06-02 14:12:45 +0000
@@ -62,7 +62,7 @@
     >>> from lp.translations.interfaces.translationimportqueue import (
     ...     ITranslationImportQueue)
     >>> queue = getUtility(ITranslationImportQueue)
-    >>> entry = queue.addOrUpdateEntry("foo.pot", "foo pot content", True,
+    >>> entry = queue.addOrUpdateEntry("foo.pot", b"foo pot content", True,
     ...     series.owner, productseries=series)
 
 The job initializes the approver with the list of template files in the tree,
@@ -73,15 +73,15 @@
 
 It approves the entry which leads to the creation of a new POTemplate object.
 
-    >>> print entry.potemplate
+    >>> print(entry.potemplate)
     None
     >>> entry = approver.approve(entry)
-    >>> print repr(entry.potemplate)
+    >>> print(repr(entry.potemplate))
     <POTemplate at ...>
     >>> foo_potemplate = entry.potemplate
-    >>> print foo_potemplate.name
+    >>> print(foo_potemplate.name)
     foo
-    >>> print repr(entry.status)
+    >>> print(repr(entry.status))
     <DBItem RosettaImportStatus.APPROVED, (1) Approved>
 
 Now the project owner wants to use two translation domains in their project
@@ -89,7 +89,7 @@
 detects this new file on its next run and places it into the import queue
 (but not the first one which is left unchanged).
 
-    >>> entry = queue.addOrUpdateEntry("bar.pot", "bar pot content", True,
+    >>> entry = queue.addOrUpdateEntry("bar.pot", b"bar pot content", True,
     ...     series.owner, productseries=series)
 
 The job does know about all the template files in the tree and so it
@@ -101,15 +101,15 @@
 It approves the entry which leads to the creation of another POTemplate
 object.
 
-    >>> print entry.potemplate
+    >>> print(entry.potemplate)
     None
     >>> entry = approver.approve(entry)
-    >>> print repr(entry.potemplate)
+    >>> print(repr(entry.potemplate))
     <POTemplate at ...>
     >>> bar_potemplate = entry.potemplate
-    >>> print bar_potemplate.name
+    >>> print(bar_potemplate.name)
     bar
-    >>> print repr(entry.status)
+    >>> print(repr(entry.status))
     <DBItem RosettaImportStatus.APPROVED, (1) Approved>
 
 
@@ -120,9 +120,9 @@
 upload job detects two changed files and places them in the upload queue.
 
     >>> foo_entry = queue.addOrUpdateEntry("po/foo/messages.pot",
-    ...     "foo pot content", True, series.owner, productseries=series)
+    ...     b"foo pot content", True, series.owner, productseries=series)
     >>> bar_entry = queue.addOrUpdateEntry("po/bar/messages.pot",
-    ...     "bar pot content", True, series.owner, productseries=series)
+    ...     b"bar pot content", True, series.owner, productseries=series)
 
 Since these two files are all the translation template files in the tree,
 the job initializes the approver with their names. This is situation 1.
@@ -135,18 +135,18 @@
 attributes of the linked objects are updated.
 
     >>> foo_entry = approver.approve(foo_entry)
-    >>> print foo_entry.potemplate == foo_potemplate
+    >>> print(foo_entry.potemplate == foo_potemplate)
     True
-    >>> print foo_potemplate.path
+    >>> print(foo_potemplate.path)
     po/foo/messages.pot
-    >>> print repr(foo_entry.status)
+    >>> print(repr(foo_entry.status))
     <DBItem RosettaImportStatus.APPROVED, (1) Approved>
     >>> bar_entry = approver.approve(bar_entry)
-    >>> print bar_entry.potemplate == bar_potemplate
+    >>> print(bar_entry.potemplate == bar_potemplate)
     True
-    >>> print bar_potemplate.path
+    >>> print(bar_potemplate.path)
     po/bar/messages.pot
-    >>> print repr(bar_entry.status)
+    >>> print(repr(bar_entry.status))
     <DBItem RosettaImportStatus.APPROVED, (1) Approved>
 
 But now the branch owner messes things up and renames the bar template
@@ -154,7 +154,7 @@
 queue.
 
     >>> spam_entry = queue.addOrUpdateEntry("po/spam/messages.pot",
-    ...     "bar pot content", True, series.owner, productseries=series)
+    ...     b"bar pot content", True, series.owner, productseries=series)
 
 Since these two files are again all the translation template files in the
 tree, the job initializes the approver with their names. But this is
@@ -170,21 +170,20 @@
 each other.
 
     >>> spam_entry = approver.approve(spam_entry)
-    >>> print spam_entry.potemplate
+    >>> print(spam_entry.potemplate)
     None
-    >>> print repr(spam_entry.status)
+    >>> print(repr(spam_entry.status))
     <DBItem RosettaImportStatus.NEEDS_REVIEW, (5) Needs Review>
 
 It is however still possible to update the foo template as this can be matched
 safely against the existing foo_template object, even in situation 3.
 
     >>> foo_entry = queue.addOrUpdateEntry("po/foo/messages.pot",
-    ...     "CHANGED foo pot content", True,
+    ...     b"CHANGED foo pot content", True,
     ...     series.owner, productseries=series)
     >>> approver = TranslationBranchApprover(
     ...     ['po/foo/messages.pot', 'po/spam/messages.pot'],
     ...     productseries=series)
     >>> foo_entry = approver.approve(foo_entry)
-    >>> print repr(foo_entry.status)
+    >>> print(repr(foo_entry.status))
     <DBItem RosettaImportStatus.APPROVED, (1) Approved>
-

=== modified file 'lib/lp/translations/doc/translationbuildapprover.txt'
--- lib/lp/translations/doc/translationbuildapprover.txt	2014-01-30 15:04:06 +0000
+++ lib/lp/translations/doc/translationbuildapprover.txt	2018-06-02 14:12:45 +0000
@@ -14,7 +14,7 @@
     >>> from lp.translations.model.approver import TranslationBuildApprover
     >>> def makeQueueEntry(path, series):
     ...     return queue.addOrUpdateEntry(
-    ...         path, "#Dummy content.", False, importer_person,
+    ...         path, b"#Dummy content.", False, importer_person,
     ...         productseries=series)
     >>> login("foo.bar@xxxxxxxxxxxxx")
 
@@ -25,10 +25,10 @@
     >>> approver = TranslationBuildApprover(
     ...     ["po/my_domain.pot"], productseries=productseries)
     >>> entry = makeQueueEntry("po/my_domain.pot", productseries)
-    >>> print entry.status.title
+    >>> print(entry.status.title)
     Needs Review
     >>> entry = approver.approve(entry)
-    >>> print entry.status.title
+    >>> print(entry.status.title)
     Approved
 
 If a template with the name exists, it will target the import entry to it and
@@ -42,9 +42,9 @@
     >>> entry = makeQueueEntry(
     ...     "po/existing_domain.pot", productseries)
     >>> entry = approver.approve(entry)
-    >>> print entry.status.title
+    >>> print(entry.status.title)
     Approved
-    >>> print entry.potemplate == existing_potemplate
+    >>> print(entry.potemplate == existing_potemplate)
     True
 
 A template file with generic names is only approved if it is the only one that
@@ -57,11 +57,11 @@
     >>> approver = TranslationBuildApprover(
     ...     ['po/messages.pot'], productseries=productseries)
     >>> generic_entry = approver.approve(generic_entry)
-    >>> print generic_entry.status.title
+    >>> print(generic_entry.status.title)
     Approved
-    >>> print generic_entry.potemplate.translation_domain
+    >>> print(generic_entry.potemplate.translation_domain)
     fooproduct
-    >>> print generic_entry.potemplate.name
+    >>> print(generic_entry.potemplate.name)
     fooproduct
 
 If there are other files or templates, files with generic names are not
@@ -73,10 +73,9 @@
     ...     productseries=productseries)
     >>> generic_entry = makeQueueEntry("po/messages.pot", productseries)
     >>> generic_entry = approver.approve(generic_entry)
-    >>> print generic_entry.status.title
+    >>> print(generic_entry.status.title)
     Needs Review
     >>> valid_entry = makeQueueEntry("validdomain.pot", productseries)
     >>> valid_entry = approver.approve(valid_entry)
-    >>> print valid_entry.status.title
+    >>> print(valid_entry.status.title)
     Approved
-

=== modified file 'lib/lp/translations/doc/translationgroup.txt'
--- lib/lp/translations/doc/translationgroup.txt	2016-01-26 15:47:37 +0000
+++ lib/lp/translations/doc/translationgroup.txt	2018-06-02 14:12:45 +0000
@@ -134,26 +134,26 @@
 is a part of:
 
     >>> for group in translation_group_set.getByPerson(carlos):
-    ...     print group.name
+    ...     print(group.name)
     testing-translation-team
 
     >>> for group in translation_group_set.getByPerson(no_priv):
-    ...     print group.name
+    ...     print(group.name)
     foo-translators
 
     >>> translators = getUtility(ITranslatorSet)
     >>> for trans in translators.getByTranslator(carlos):
-    ...     print trans.language.code
-    ...     print trans.translationgroup.name
-    ...     print trans.style_guide_url
+    ...     print(trans.language.code)
+    ...     print(trans.translationgroup.name)
+    ...     print(trans.style_guide_url)
     es
     testing-translation-team
     None
 
     >>> for trans in translators.getByTranslator(no_priv):
-    ...     print trans.language.code
-    ...     print trans.translationgroup.name
-    ...     print trans.style_guide_url
+    ...     print(trans.language.code)
+    ...     print(trans.translationgroup.name)
+    ...     print(trans.style_guide_url)
     cy
     foo-translators
     None
@@ -183,7 +183,7 @@
 assigned to that language.
 
     >>> for (translator, language, team) in group.fetchTranslatorData():
-    ...     print translator.language.code, language.code, team.name
+    ...     print(translator.language.code, language.code, team.name)
     nl nl nl-team
     de de de-team
     la la la-team
@@ -191,7 +191,7 @@
 The members are sorted by language name in English.
 
     >>> for (translator, language, person) in group.fetchTranslatorData():
-    ...     print language.englishname
+    ...     print(language.englishname)
     Dutch
     German
     Latin

=== modified file 'lib/lp/translations/doc/translationimportqueue.txt'
--- lib/lp/translations/doc/translationimportqueue.txt	2015-10-05 08:36:52 +0000
+++ lib/lp/translations/doc/translationimportqueue.txt	2018-06-02 14:12:45 +0000
@@ -35,7 +35,7 @@
     >>> def print_list(strings):
     ...     """Print list of strings as list of lines."""
     ...     for string in strings:
-    ...         print string
+    ...         print(string)
 
 
 getGuessedPOFile
@@ -81,7 +81,7 @@
 potemplate.
 
     >>> entry = translationimportqueue.addOrUpdateEntry(
-    ...     u'po/sr.po', 'foo', True, rosetta_experts,
+    ...     u'po/sr.po', b'foo', True, rosetta_experts,
     ...      productseries=evolution_productseries)
 
 This entry has no information about the IPOFile where it should be attached
@@ -103,7 +103,7 @@
     >>> hoary_distroseries = DistroSeries.get(3)
     >>> evolution_sourcepackagename = SourcePackageName.get(9)
     >>> entry = translationimportqueue.addOrUpdateEntry(
-    ...     u'po/sr.po', 'foo', True, rosetta_experts,
+    ...     u'po/sr.po', b'foo', True, rosetta_experts,
     ...      distroseries=hoary_distroseries,
     ...      sourcepackagename=evolution_sourcepackagename)
     >>> transaction.commit()
@@ -139,7 +139,7 @@
 Now, we do a new upload.
 
     >>> entry = translationimportqueue.addOrUpdateEntry(
-    ...     u'po/sr.po', 'foo', True, rosetta_experts,
+    ...     u'po/sr.po', b'foo', True, rosetta_experts,
     ...      distroseries=hoary_distroseries,
     ...      sourcepackagename=evolution_sourcepackagename)
     >>> transaction.commit()
@@ -178,13 +178,13 @@
 
     >>> by_maintainer = True
     >>> po_sr_entry = translationimportqueue.addOrUpdateEntry(
-    ...     u'po/sr.po', 'foo', by_maintainer, rosetta_experts,
+    ...     u'po/sr.po', b'foo', by_maintainer, rosetta_experts,
     ...      distroseries=hoary_distroseries,
     ...      sourcepackagename=evolution_sourcepackagename)
 
 And the new status is
 
-    >>> print po_sr_entry.status.title
+    >>> print(po_sr_entry.status.title)
     Needs Review
 
 The dateimported remains the same as it was already waiting to be imported.
@@ -200,7 +200,7 @@
 First, we import a new .pot file.
 
     >>> pot_entry = translationimportqueue.addOrUpdateEntry(
-    ...     'po/evolution-2.2.pot', 'foo', True, rosetta_experts,
+    ...     'po/evolution-2.2.pot', b'foo', True, rosetta_experts,
     ...      distroseries=hoary_distroseries,
     ...      sourcepackagename=evolution_sourcepackagename)
 
@@ -287,7 +287,7 @@
 Let's attach the .pot file
 
     >>> kde_pot_entry = translationimportqueue.addOrUpdateEntry(
-    ...     'po/kdebugdialog.pot', 'foo content', True, rosetta_experts,
+    ...     'po/kdebugdialog.pot', b'foo content', True, rosetta_experts,
     ...      distroseries=hoary_distroseries,
     ...      sourcepackagename=kdebase)
 
@@ -311,7 +311,7 @@
 Let's attach a .po file from kde-i18n-es
 
     >>> es_entry = translationimportqueue.addOrUpdateEntry(
-    ...     'messages/kdebase/kdebugdialog.po', 'foo content', True,
+    ...     'messages/kdebase/kdebugdialog.po', b'foo content', True,
     ...     rosetta_experts, distroseries=hoary_distroseries,
     ...      sourcepackagename=kde_i18n_es)
 
@@ -325,7 +325,7 @@
 
     >>> sr_latin = factory.makeLanguage('sr@latin', 'Serbian Latin')
     >>> sr_latin_entry = translationimportqueue.addOrUpdateEntry(
-    ...     'messages/kdebase/kdebugdialog.po', 'foo content', True,
+    ...     'messages/kdebase/kdebugdialog.po', b'foo content', True,
     ...     rosetta_experts, distroseries=hoary_distroseries,
     ...      sourcepackagename=kde_l10n_sr_latin)
 
@@ -338,7 +338,7 @@
 that is not yet imported.
 
     >>> es_without_potemplate_entry = translationimportqueue.addOrUpdateEntry(
-    ...     'messages/kdebase/konqueror.po', 'foo content', True,
+    ...     'messages/kdebase/konqueror.po', b'foo content', True,
     ...     rosetta_experts, distroseries=hoary_distroseries,
     ...      sourcepackagename=kde_i18n_es)
 
@@ -354,7 +354,7 @@
 We will see it working here with this example:
 
     >>> kde_pot_entry = translationimportqueue.addOrUpdateEntry(
-    ...     'po/kio_sftp.pot', 'foo content', True, rosetta_experts,
+    ...     'po/kio_sftp.pot', b'foo content', True, rosetta_experts,
     ...      distroseries=hoary_distroseries,
     ...      sourcepackagename=kdebase)
 
@@ -376,7 +376,7 @@
 Let's attach a .po file from kde-i18n-es
 
     >>> es_entry = translationimportqueue.addOrUpdateEntry(
-    ...     'messages/kdebase/kio_sftp.po', 'foo content', True,
+    ...     'messages/kdebase/kio_sftp.po', b'foo content', True,
     ...     rosetta_experts, distroseries=hoary_distroseries,
     ...      sourcepackagename=kde_i18n_es)
 
@@ -415,7 +415,7 @@
 Let's attach the .pot file
 
     >>> koffice_pot_entry = translationimportqueue.addOrUpdateEntry(
-    ...     'po/koffice.pot', 'foo content', True, rosetta_experts,
+    ...     'po/koffice.pot', b'foo content', True, rosetta_experts,
     ...      distroseries=hoary_distroseries,
     ...      sourcepackagename=koffice)
 
@@ -440,7 +440,7 @@
 
     >>> es_entry = translationimportqueue.addOrUpdateEntry(
     ...     'koffice-i18n-es-1.5.2/messages/koffice/koffice.po',
-    ...     'foo content', True, rosetta_experts,
+    ...     b'foo content', True, rosetta_experts,
     ...     distroseries=hoary_distroseries, sourcepackagename=koffice_l10n)
 
 And we will get the right IPOFile.
@@ -453,7 +453,7 @@
     >>> sr_latin = factory.makeLanguage('sr@latin', 'Serbian Latin')
     >>> sr_latin_entry = translationimportqueue.addOrUpdateEntry(
     ...     'koffice-i18n-sr@latin-1.5.2/messages/koffice/koffice.po',
-    ...     'foo content', True, rosetta_experts,
+    ...     b'foo content', True, rosetta_experts,
     ...     distroseries=hoary_distroseries, sourcepackagename=koffice_l10n)
 
 And we will get the right IPOFile.
@@ -466,7 +466,7 @@
 
     >>> es_without_potemplate_entry = translationimportqueue.addOrUpdateEntry(
     ...     'koffice-i18n-es-1.5.2/messages/koffice/kchart.po',
-    ...     'foo content', True, rosetta_experts,
+    ...     b'foo content', True, rosetta_experts,
     ...     distroseries=hoary_distroseries, sourcepackagename=koffice_l10n)
 
 We don't know the IPOFile where it should be imported.
@@ -516,7 +516,7 @@
 Let's attach the .pot file
 
     >>> adept_pot_entry = translationimportqueue.addOrUpdateEntry(
-    ...     'po/adept.pot', 'foo content', True, rosetta_experts,
+    ...     'po/adept.pot', b'foo content', True, rosetta_experts,
     ...      distroseries=hoary_distroseries,
     ...      sourcepackagename=adept)
 
@@ -539,7 +539,7 @@
 Let's attach a .po file now.
 
     >>> es_entry = translationimportqueue.addOrUpdateEntry(
-    ...     'po/es/adept.po', 'foo content', True,
+    ...     'po/es/adept.po', b'foo content', True,
     ...     rosetta_experts, distroseries=hoary_distroseries,
     ...      sourcepackagename=adept)
 
@@ -552,7 +552,7 @@
 that is not yet imported.
 
     >>> es_without_potemplate_entry = translationimportqueue.addOrUpdateEntry(
-    ...     'po/es/adept-foo.po', 'foo content', True,
+    ...     'po/es/adept-foo.po', b'foo content', True,
     ...     rosetta_experts, distroseries=hoary_distroseries,
     ...      sourcepackagename=adept)
 
@@ -569,7 +569,7 @@
 Let's attach the .pot file
 
     >>> ktorrent_pot_entry = translationimportqueue.addOrUpdateEntry(
-    ...     'po/ktorrent.pot', 'foo content', True, rosetta_experts,
+    ...     'po/ktorrent.pot', b'foo content', True, rosetta_experts,
     ...      distroseries=hoary_distroseries,
     ...      sourcepackagename=ktorrent)
 
@@ -592,7 +592,7 @@
 Let's attach a .po file now.
 
     >>> es_entry = translationimportqueue.addOrUpdateEntry(
-    ...     'translations/es/messages/ktorrent.po', 'foo content', True,
+    ...     'translations/es/messages/ktorrent.po', b'foo content', True,
     ...     rosetta_experts, distroseries=hoary_distroseries,
     ...      sourcepackagename=ktorrent)
 
@@ -605,7 +605,7 @@
 that is not yet imported.
 
     >>> es_without_potemplate_entry = translationimportqueue.addOrUpdateEntry(
-    ...     'translations/es/messages/ktorrent-foo.po', 'foo content', True,
+    ...     'translations/es/messages/ktorrent-foo.po', b'foo content', True,
     ...     rosetta_experts, distroseries=hoary_distroseries,
     ...      sourcepackagename=ktorrent)
 
@@ -624,7 +624,7 @@
     >>> zope_pot_entry = translationimportqueue.addOrUpdateEntry(
     ...     'debian/zope3/usr/lib/python2.4/site-packages/zope/app/'
     ...     'locales/zope.pot',
-    ...     'foo content', True, rosetta_experts,
+    ...     b'foo content', True, rosetta_experts,
     ...     distroseries=hoary_distroseries, sourcepackagename=zope)
 
 Create the template name and attach this new import to it.
@@ -650,7 +650,7 @@
     >>> es_entry = translationimportqueue.addOrUpdateEntry(
     ...     'debian/zope3/usr/lib/python2.4/site-packages/zope/app/locales'
     ...     '/es/LC_MESSAGES/zope.po',
-    ...     'foo content', True, rosetta_experts,
+    ...     b'foo content', True, rosetta_experts,
     ...     distroseries=hoary_distroseries, sourcepackagename=zope)
 
 And we will get the right IPOFile.
@@ -664,7 +664,7 @@
     >>> es_without_potemplate_entry = translationimportqueue.addOrUpdateEntry(
     ...     'debian/zope3/usr/lib/python2.4/site-packages/zope/app/'
     ...     'locales/es/LC_MESSAGES/zope-test.po',
-    ...     'foo content', True, rosetta_experts,
+    ...     b'foo content', True, rosetta_experts,
     ...     distroseries=hoary_distroseries, sourcepackagename=zope)
 
 We don't know the IPOFile where it should be imported.
@@ -682,7 +682,7 @@
 Let's attach the .pot file
 
     >>> k3b_pot_entry = translationimportqueue.addOrUpdateEntry(
-    ...     'po/k3b.pot', 'foo content', True, rosetta_experts,
+    ...     'po/k3b.pot', b'foo content', True, rosetta_experts,
     ...     distroseries=hoary_distroseries, sourcepackagename=k3b)
 
 Create the template name and attach this new import to it.
@@ -702,7 +702,7 @@
 Let's attach a .po file now.
 
     >>> es_entry = translationimportqueue.addOrUpdateEntry(
-    ...     'es/messages/k3b.po', 'foo content', True, rosetta_experts,
+    ...     'es/messages/k3b.po', b'foo content', True, rosetta_experts,
     ...     distroseries=hoary_distroseries, sourcepackagename=k3b_i18n)
 
 And we will get the right IPOFile.
@@ -714,7 +714,7 @@
 that is not yet imported.
 
     >>> es_without_potemplate_entry = translationimportqueue.addOrUpdateEntry(
-    ...     'es/messages/libk3b.po', 'foo content', True, rosetta_experts,
+    ...     'es/messages/libk3b.po', b'foo content', True, rosetta_experts,
     ...     distroseries=hoary_distroseries, sourcepackagename=k3b_i18n)
 
 We don't know the IPOFile where it should be imported.
@@ -730,7 +730,7 @@
 Let's attach the .pot file
 
     >>> terminal_pot_entry = translationimportqueue.addOrUpdateEntry(
-    ...     'drivemount/help/drivemount.pot', 'foo content', True,
+    ...     'drivemount/help/drivemount.pot', b'foo content', True,
     ...     rosetta_experts, distroseries=hoary_distroseries,
     ...     sourcepackagename=gnome_terminal)
 
@@ -752,7 +752,7 @@
 Let's attach a .po file now.
 
     >>> es_entry = translationimportqueue.addOrUpdateEntry(
-    ...     'drivemount/help/es/es.po', 'foo content', True, rosetta_experts,
+    ...     'drivemount/help/es/es.po', b'foo content', True, rosetta_experts,
     ...     distroseries=hoary_distroseries,
     ...     sourcepackagename=gnome_terminal)
 
@@ -765,7 +765,7 @@
 that is not yet imported.
 
     >>> es_without_potemplate_entry = translationimportqueue.addOrUpdateEntry(
-    ...     'wanda/help/es/es.po', 'foo content', True, rosetta_experts,
+    ...     'wanda/help/es/es.po', b'foo content', True, rosetta_experts,
     ...     distroseries=hoary_distroseries, sourcepackagename=gnome_terminal)
 
 We don't know the IPOFile where it should be imported.
@@ -873,7 +873,7 @@
 
     >>> productseries = ProductSeries.get(1)
     >>> entry = translationimportqueue.addOrUpdateEntry(
-    ...     'foo/bar.po', 'foo content', True,
+    ...     'foo/bar.po', b'foo content', True,
     ...     rosetta_experts, productseries=productseries)
 
 When we just import it, this method tells us that it's "just requested"
@@ -989,7 +989,7 @@
     ...         template = 'None'
     ...         if entry.potemplate is not None:
     ...             template = entry.potemplate.name
-    ...         print '%s | %s | %s' % (context, template, entry.path)
+    ...         print('%s | %s | %s' % (context, template, entry.path))
 
 Current entries in the queue are:
 
@@ -1058,47 +1058,47 @@
 
     >>> existing_entry = getFirstEvoEntryByPath(queue, 'foo.pot')
     >>> existing_entry = removeSecurityProxy(existing_entry)
-    >>> print existing_entry.content.read()
+    >>> print(existing_entry.content.read())
     Foo template
 
     >>> entry = translationimportqueue.addOrUpdateEntry(
-    ...     "foo.pot", "New content", by_maintainer, rosetta_experts,
+    ...     "foo.pot", b"New content", by_maintainer, rosetta_experts,
     ...     productseries=evolution_productseries,
     ...     potemplate=evolution_22_test_template)
     >>> entry = removeSecurityProxy(entry)
     >>> transaction.commit()
     >>> entry is existing_entry
     True
-    >>> print entry.content.read()
+    >>> print(entry.content.read())
     New content
 
 Not specifying the potemplate in this situation still selects the same entry
 on a best match basis. The entry is updated.
 
     >>> entry = translationimportqueue.addOrUpdateEntry(
-    ...     "foo.pot", "Even newer content", by_maintainer, rosetta_experts,
+    ...     "foo.pot", b"Even newer content", by_maintainer, rosetta_experts,
     ...     productseries=evolution_productseries)
     >>> entry = removeSecurityProxy(entry)
     >>> transaction.commit()
     >>> entry is existing_entry
     True
-    >>> print entry.content.read()
+    >>> print(entry.content.read())
     Even newer content
 
 Same goes for pofile entries.
 
     >>> existing_entry = getFirstEvoEntryByPath(queue, 'es.po')
     >>> existing_entry = removeSecurityProxy(existing_entry)
-    >>> print existing_entry.content.read()
+    >>> print(existing_entry.content.read())
     Spanish translation
 
     >>> entry = removeSecurityProxy(translationimportqueue.addOrUpdateEntry(
-    ...     "es.po", "New po content", by_maintainer, rosetta_experts,
+    ...     "es.po", b"New po content", by_maintainer, rosetta_experts,
     ...     productseries=evolution_productseries))
     >>> transaction.commit()
     >>> entry is existing_entry
     True
-    >>> print entry.content.read()
+    >>> print(entry.content.read())
     New po content
 
 Now, attaching the same layout to a different template for the same product,
@@ -1128,20 +1128,20 @@
 Not specifying the potemplate now is ambiguous and so no entry is added or
 updated.
 
-    >>> print queue.addOrUpdateEntry(
-    ...     "foo.pot", "Latest content", by_maintainer, rosetta_experts,
-    ...     productseries=evolution_productseries)
+    >>> print(queue.addOrUpdateEntry(
+    ...     "foo.pot", b"Latest content", by_maintainer, rosetta_experts,
+    ...     productseries=evolution_productseries))
     None
 
 Ambiguity is also resolved when a file is uploaded to the product first and
 then to a specific template.
 
     >>> existing_entry = queue.addOrUpdateEntry(
-    ...     "bar.pot", "Bar content", by_maintainer, rosetta_experts,
+    ...     "bar.pot", b"Bar content", by_maintainer, rosetta_experts,
     ...     productseries=evolution_productseries)
     >>> existing_entry = removeSecurityProxy(existing_entry)
     >>> entry = queue.addOrUpdateEntry(
-    ...     "bar.pot", "Bar content", by_maintainer, rosetta_experts,
+    ...     "bar.pot", b"Bar content", by_maintainer, rosetta_experts,
     ...     productseries=evolution_productseries,
     ...     potemplate=evolution_22_template)
 
@@ -1158,13 +1158,13 @@
 When uploading to the prouct now, the best matching entry is updated.
 
     >>> entry = queue.addOrUpdateEntry(
-    ...     "bar.pot", "New bar content", by_maintainer, rosetta_experts,
+    ...     "bar.pot", b"New bar content", by_maintainer, rosetta_experts,
     ...     productseries=evolution_productseries)
     >>> entry = removeSecurityProxy(entry)
     >>> transaction.commit()
     >>> entry is existing_entry
     True
-    >>> print entry.content.read()
+    >>> print(entry.content.read())
     New bar content
 
 Filename filters
@@ -1226,14 +1226,14 @@
     >>> def print_import_failures(import_script):
     ...     """List failures recorded in an import script instance."""
     ...     for reason, entries in script.failures.iteritems():
-    ...         print reason
+    ...         print(reason)
     ...         for entry in entries:
-    ...             print "-> " + entry
+    ...             print("-> " + entry)
 
     >>> clear_queue(translationimportqueue)
 
     >>> entry = translationimportqueue.addOrUpdateEntry(
-    ...     u'po/sr.po', 'foo', True, rosetta_experts,
+    ...     u'po/sr.po', b'foo', True, rosetta_experts,
     ...      productseries=evolution_productseries)
 
     >>> entry.import_into is None
@@ -1260,7 +1260,7 @@
 
 The entry is marked as Failed.
 
-    >>> print entry.status.name
+    >>> print(entry.status.name)
     FAILED
 
 This happens for distribution packages as well as products.
@@ -1268,7 +1268,7 @@
     >>> clear_queue(translationimportqueue)
 
     >>> entry = translationimportqueue.addOrUpdateEntry(
-    ...     u'po/th.po', 'bar', False, rosetta_experts,
+    ...     u'po/th.po', b'bar', False, rosetta_experts,
     ...     distroseries=hoary_distroseries,
     ...     sourcepackagename=evolution_sourcepackagename)
 
@@ -1285,7 +1285,7 @@
     Entry is approved but has no place to import to.
     -> 'po/th.po' (id ...) in ubuntu Hoary package evolution
 
-    >>> print entry.status.name
+    >>> print(entry.status.name)
     FAILED
 
     >>> clear_queue(translationimportqueue)
@@ -1319,7 +1319,7 @@
 days.
 
     >>> entry = translationimportqueue.addOrUpdateEntry(
-    ...     u'po/nl.po', 'hoi', True, rosetta_experts,
+    ...     u'po/nl.po', b'hoi', True, rosetta_experts,
     ...      productseries=evolution_productseries)
     >>> entry.pofile = factory.makePOFile('nl')
     >>> entry.setStatus(RosettaImportStatus.IMPORTED, rosetta_experts)
@@ -1357,7 +1357,7 @@
     ...     # In another completely arbitrary move, we make all import
     ...     # requests for products non-imported.
     ...     return translationimportqueue.addOrUpdateEntry('messages.pot',
-    ...         'dummy file', False, rosetta_experts, productseries=series,
+    ...         b'dummy file', False, rosetta_experts, productseries=series,
     ...         potemplate=template)
 
     >>> jokosher = productset['jokosher']

=== modified file 'lib/lp/translations/doc/translationmessage-destroy.txt'
--- lib/lp/translations/doc/translationmessage-destroy.txt	2012-05-08 04:41:05 +0000
+++ lib/lp/translations/doc/translationmessage-destroy.txt	2018-06-02 14:12:45 +0000
@@ -62,7 +62,7 @@
     ...     pofile=devel_sr_pofile,
     ...     potmsgset=potmsgset,
     ...     translations=[u"blah"])
-    >>> print POFileTranslator.select(
+    >>> print(POFileTranslator.select(
     ...     "pofile IN (%s, %s)"
-    ...     % sqlvalues(devel_sr_pofile, stable_sr_pofile)).count()
+    ...     % sqlvalues(devel_sr_pofile, stable_sr_pofile)).count())
     2

=== modified file 'lib/lp/translations/doc/translationmessage.txt'
--- lib/lp/translations/doc/translationmessage.txt	2012-12-26 01:32:19 +0000
+++ lib/lp/translations/doc/translationmessage.txt	2018-06-02 14:12:45 +0000
@@ -41,7 +41,7 @@
 
 We can look at a POTMsgSet with no plural forms:
 
-    >>> print potmsgset.plural_text
+    >>> print(potmsgset.plural_text)
     None
 
 Any TranslationMessage for such a POTMsgSet returns a single plural form in
@@ -57,7 +57,7 @@
     1
 
     >>> divehi = getUtility(ILanguageSet)['dv']
-    >>> print divehi.pluralforms
+    >>> print(divehi.pluralforms)
     None
     >>> current_dv = potmsgset.getCurrentTranslationMessageOrDummy(
     ...     DummyPOFile(potemplate, divehi))
@@ -82,7 +82,7 @@
 In case the language doesn't have number of plural forms defined, we return
 a default of 2, which is the most common number of plural forms:
 
-    >>> print divehi.pluralforms
+    >>> print(divehi.pluralforms)
     None
     >>> current_dv = potmsgset_plural.getCurrentTranslationMessageOrDummy(
     ...     DummyPOFile(potemplate, divehi))
@@ -247,9 +247,9 @@
 
     >>> for translation in message.all_msgstrs:
     ...     if translation is None:
-    ...         print 'None'
+    ...         print('None')
     ...     else:
-    ...         print translation.translation
+    ...         print(translation.translation)
     new
     None
     None
@@ -271,7 +271,7 @@
 The helper function make_plurals_fragment repeats a fragment of text
 for the number of plural forms we support (starting at zero).
 
-    >>> print make_plurals_fragment("x%(form)dx", ", ")
+    >>> print(make_plurals_fragment("x%(form)dx", ", "))
     x0x,
     x1x,
     x2x,
@@ -283,7 +283,7 @@
 The make_plurals_sql_fragment helper adds some parentheses and spaces
 where you might otherwise forget them--or want to.
 
-    >>> print make_plurals_sql_fragment("msgstr%(form)d IS NOT NULL")
+    >>> print(make_plurals_sql_fragment("msgstr%(form)d IS NOT NULL"))
     (msgstr0 IS NOT NULL) AND
     (msgstr1 IS NOT NULL) AND
     (msgstr2 IS NOT NULL) AND
@@ -293,7 +293,7 @@
 
 The sub-clauses don't have to be tied together with AND:
 
-    >>> print make_plurals_sql_fragment("msgstr%(form)d IS NULL", "OR")
+    >>> print(make_plurals_sql_fragment("msgstr%(form)d IS NULL", "OR"))
     (msgstr0 IS NULL) OR
     (msgstr1 IS NULL) OR
     (msgstr2 IS NULL) OR

=== modified file 'lib/lp/translations/doc/translationrelicensingagreement.txt'
--- lib/lp/translations/doc/translationrelicensingagreement.txt	2012-12-26 01:32:19 +0000
+++ lib/lp/translations/doc/translationrelicensingagreement.txt	2018-06-02 14:12:45 +0000
@@ -21,20 +21,20 @@
 
 When Karl has not made his selection yet, it is marked as None.
 
-    >>> print translations_person.translations_relicensing_agreement
+    >>> print(translations_person.translations_relicensing_agreement)
     None
     >>> choice = TranslationRelicensingAgreement.selectOneBy(person=person)
-    >>> print choice
+    >>> print(choice)
     None
 
 Setting a value will create a new TranslationRelicensingAgreement
 object.
 
     >>> translations_person.translations_relicensing_agreement = True
-    >>> print translations_person.translations_relicensing_agreement
+    >>> print(translations_person.translations_relicensing_agreement)
     True
     >>> choice = TranslationRelicensingAgreement.selectOneBy(person=person)
-    >>> print choice.allow_relicensing
+    >>> print(choice.allow_relicensing)
     True
 
 A `choice` implements ITranslationRelicensingAgreement interface:
@@ -45,8 +45,8 @@
 A translator can also change their mind later.
 
     >>> translations_person.translations_relicensing_agreement = False
-    >>> print translations_person.translations_relicensing_agreement
+    >>> print(translations_person.translations_relicensing_agreement)
     False
     >>> choice = TranslationRelicensingAgreement.selectOneBy(person=person)
-    >>> print choice.allow_relicensing
+    >>> print(choice.allow_relicensing)
     False

=== modified file 'lib/lp/translations/doc/translations-export-to-branch.txt'
--- lib/lp/translations/doc/translations-export-to-branch.txt	2011-12-21 19:25:05 +0000
+++ lib/lp/translations/doc/translations-export-to-branch.txt	2018-06-02 14:12:45 +0000
@@ -12,7 +12,7 @@
     0
     >>> stdout
     ''
-    >>> print stderr
+    >>> print(stderr)
     INFO Creating lockfile:
     /var/lock/launchpad-translations-export-to-branch.lock
     INFO Exporting to translations branches.
@@ -256,10 +256,10 @@
 
     >>> sender, recipients, body = stub.test_emails.pop()
     >>> message = message_from_string(body)
-    >>> print message['Subject']
+    >>> print(message['Subject'])
     Launchpad: translations branch has not been set up.
 
-    >>> print message.get_payload()
+    >>> print(message.get_payload())
     Hello,
     There was a problem with translations branch synchronization for
     ...

=== modified file 'lib/lp/translations/doc/translationsoverview.txt'
--- lib/lp/translations/doc/translationsoverview.txt	2012-11-23 22:10:04 +0000
+++ lib/lp/translations/doc/translationsoverview.txt	2018-06-02 14:12:45 +0000
@@ -57,7 +57,7 @@
     >>> naked_overview = removeSecurityProxy(overview)
     >>> result = naked_overview._normalizeSizes(test_list, 0, 3)
     >>> for pillar in result:
-    ...     print "%s: %d" % (pillar['pillar'], pillar['weight'])
+    ...     print("%s: %d" % (pillar['pillar'], pillar['weight']))
     one: 18
     two: 10
     three: 13
@@ -91,8 +91,8 @@
 
     >>> def display_pillars(pillars):
     ...     for pillar in pillars:
-    ...         print "%s: %d" % (
-    ...             pillar['pillar'].displayname, pillar['weight'])
+    ...         print("%s: %d" % (
+    ...             pillar['pillar'].displayname, pillar['weight']))
     >>> display_pillars(overview.getMostTranslatedPillars())
     Evolution: 14
 

=== modified file 'lib/lp/translations/doc/translationsperson.txt'
--- lib/lp/translations/doc/translationsperson.txt	2010-10-05 00:08:16 +0000
+++ lib/lp/translations/doc/translationsperson.txt	2018-06-02 14:12:45 +0000
@@ -17,7 +17,7 @@
     >>> daf = personset.getByName('daf')
     >>> translations_daf = ITranslationsPerson(daf)
     >>> for language in translations_daf.translatable_languages:
-    ...     print language.code, language.englishname
+    ...     print(language.code, language.englishname)
     en_GB  English (United Kingdom)
     ja     Japanese
     cy     Welsh
@@ -25,7 +25,7 @@
     >>> carlos = personset.getByName('carlos')
     >>> translations_carlos = ITranslationsPerson(carlos)
     >>> for language in translations_carlos.translatable_languages:
-    ...     print language.code, language.englishname
+    ...     print(language.code, language.englishname)
     ca     Catalan
     es     Spanish
 
@@ -33,12 +33,10 @@
 for a Person:
 
     >>> for pt in translations_carlos.translation_history:
-    ...     print pt.pofile.title
+    ...     print(pt.pofile.title)
     English (en) trans... of pkgconf-mozilla in Ubuntu Hoary package "mozilla"
     Spanish (es) translation of alsa-utils in alsa-utils trunk
     Spanish (es) translation of man in Ubuntu Hoary package "evolution"
     Spanish (es) translation of evolution-2.2 in Evolution trunk
     Japanese (ja) tran... of evolution-2.2 in Ubuntu Hoary package "evolution"
     Spanish (es) trans... of evolution-2.2 in Ubuntu Hoary package "evolution"
-
-

=== modified file 'lib/lp/translations/doc/vpoexport.txt'
--- lib/lp/translations/doc/vpoexport.txt	2013-03-28 05:05:15 +0000
+++ lib/lp/translations/doc/vpoexport.txt	2018-06-02 14:12:45 +0000
@@ -23,7 +23,7 @@
     ...     describe_pofile(pofile)
     ...     for pofile in vpoexportset.get_distroseries_pofiles(hoary)])
     >>> for pofile in pofiles:
-    ...     print pofile
+    ...     print(pofile)
     evolution evolution-2.2 es
     evolution evolution-2.2 ja
     evolution evolution-2.2 xh
@@ -52,9 +52,9 @@
     pmount    pmount it_IT
     pmount    pmount nb
 
-    >>> print len(pofiles)
+    >>> print(len(pofiles))
     27
-    >>> print vpoexportset.get_distroseries_pofiles_count(hoary)
+    >>> print(vpoexportset.get_distroseries_pofiles_count(hoary))
     27
 
 The getTranslationRows method lists all translations found in the
@@ -82,7 +82,7 @@
     ...     describe_poexport_row(row)
     ...     for row in pofile.getTranslationRows()])
     >>> for row in rows:
-    ...     print row
+    ...     print(row)
     1 english1 esperanto1
     2 english2 esperanto2
 
@@ -94,7 +94,7 @@
     ...     describe_poexport_row(row)
     ...     for row in pofile.getChangedRows()])
     >>> for row in rows:
-    ...     print row
+    ...     print(row)
     2 english2 esperanto2
 
 
@@ -137,13 +137,13 @@
 When we export trunk, only the trunk message shows up.
 
     >>> for row in trunk_pofile.getTranslationRows():
-    ...     print describe_poexport_row(row)
+    ...     print(describe_poexport_row(row))
     1   1   een
     2   2   None
 
 In an export for stable, only the stable message shows up.
 
     >>> for row in stable_pofile.getTranslationRows():
-    ...     print describe_poexport_row(row)
+    ...     print(describe_poexport_row(row))
     2   2   twee
     1   1   None

=== modified file 'lib/lp/translations/doc/vpotexport.txt'
--- lib/lp/translations/doc/vpotexport.txt	2011-10-06 08:57:09 +0000
+++ lib/lp/translations/doc/vpotexport.txt	2018-06-02 14:12:45 +0000
@@ -69,7 +69,7 @@
 The POT file header is a template of common information that will get filled
 in with language specifics by translators.
 
-    >>> print first.template_header
+    >>> print(first.template_header)
     Project-Id-Version: PACKAGE VERSION
     Report-Msgid-Bugs-To:
     POT-Creation-Date: 2005-08-25 14:56+0200

=== modified file 'lib/lp/translations/tests/test_doc.py'
--- lib/lp/translations/tests/test_doc.py	2018-05-06 08:52:34 +0000
+++ lib/lp/translations/tests/test_doc.py	2018-06-02 14:12:45 +0000
@@ -18,6 +18,7 @@
 from lp.testing.pages import PageTestSuite
 from lp.testing.systemdocs import (
     LayeredDocFileSuite,
+    setGlobs,
     setUp,
     tearDown,
     )
@@ -29,23 +30,28 @@
 special = {
     'poexport-queue.txt': LayeredDocFileSuite(
         '../doc/poexport-queue.txt',
-        setUp=setUp, tearDown=tearDown, layer=LaunchpadFunctionalLayer
+        setUp=lambda test: setUp(test, future=True), tearDown=tearDown,
+        layer=LaunchpadFunctionalLayer,
         ),
     'translationimportqueue.txt': LayeredDocFileSuite(
         '../doc/translationimportqueue.txt',
-        setUp=setUp, tearDown=tearDown, layer=LaunchpadFunctionalLayer
+        setUp=lambda test: setUp(test, future=True), tearDown=tearDown,
+        layer=LaunchpadFunctionalLayer,
         ),
     'rosetta-karma.txt': LayeredDocFileSuite(
         '../doc/rosetta-karma.txt',
-        setUp=setUp, tearDown=tearDown, layer=LaunchpadFunctionalLayer
+        setUp=lambda test: setUp(test, future=True), tearDown=tearDown,
+        layer=LaunchpadFunctionalLayer,
         ),
     'translationmessage-destroy.txt': LayeredDocFileSuite(
         '../doc/translationmessage-destroy.txt',
-        layer=LaunchpadZopelessLayer
+        setUp=lambda test: setGlobs(test, future=True),
+        layer=LaunchpadZopelessLayer,
         ),
     'translationsoverview.txt': LayeredDocFileSuite(
         '../doc/translationsoverview.txt',
-        layer=LaunchpadZopelessLayer
+        setUp=lambda test: setGlobs(test, future=True),
+        layer=LaunchpadZopelessLayer,
         ),
     }
 
@@ -79,7 +85,8 @@
     for filename in filenames:
         path = os.path.join('../doc/', filename)
         one_test = LayeredDocFileSuite(
-            path, setUp=setUp, tearDown=tearDown,
+            path,
+            setUp=lambda test: setUp(test, future=True), tearDown=tearDown,
             layer=LaunchpadFunctionalLayer,
             stdout_logging_level=logging.WARNING)
         suite.addTest(one_test)


Follow ups