← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] ~cjwatson/launchpad:py3-scripts-universal-newlines into launchpad:master

 

Colin Watson has proposed merging ~cjwatson/launchpad:py3-scripts-universal-newlines into launchpad:master.

Commit message:
Run scripts with universal_newlines=True

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

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

This is nearly always what we want (especially in doctests), so just make the various run_script implementations default to text mode.
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of ~cjwatson/launchpad:py3-scripts-universal-newlines into launchpad:master.
diff --git a/lib/launchpad_loggerhead/testing.py b/lib/launchpad_loggerhead/testing.py
index cc0d8fe..2266fab 100644
--- a/lib/launchpad_loggerhead/testing.py
+++ b/lib/launchpad_loggerhead/testing.py
@@ -51,7 +51,8 @@ class LoggerheadFixture(Fixture):
             os.path.join("scripts", "start-loggerhead.py"), ["--daemon"],
             # The testrunner-appserver config provides the correct
             # openid_provider_root URL.
-            extra_env={"LPCONFIG": BaseLayer.appserver_config_name})
+            extra_env={"LPCONFIG": BaseLayer.appserver_config_name},
+            universal_newlines=False)
         self._waitForStartup()
 
     def _hasStarted(self):
diff --git a/lib/lp/answers/doc/expiration.txt b/lib/lp/answers/doc/expiration.txt
index 6f1d240..5ab4e8a 100644
--- a/lib/lp/answers/doc/expiration.txt
+++ b/lib/lp/answers/doc/expiration.txt
@@ -140,7 +140,7 @@ somebody are subject to expiration.
     >>> process = subprocess.Popen(
     ...     'cronscripts/expire-questions.py', shell=True,
     ...     stdin=subprocess.PIPE, stdout=subprocess.PIPE,
-    ...     stderr=subprocess.PIPE)
+    ...     stderr=subprocess.PIPE, universal_newlines=True)
     >>> (out, err) = process.communicate()
     >>> print(err)
     INFO    Creating lockfile: /var/lock/launchpad-expire-questions.lock
diff --git a/lib/lp/answers/tests/test_questionjob.py b/lib/lp/answers/tests/test_questionjob.py
index 48f9a47..aa21aad 100644
--- a/lib/lp/answers/tests/test_questionjob.py
+++ b/lib/lp/answers/tests/test_questionjob.py
@@ -7,8 +7,7 @@ from __future__ import absolute_import, print_function, unicode_literals
 
 __metaclass__ = type
 
-from testtools.content import Content
-from testtools.content_type import UTF8_TEXT
+from testtools.content import text_content
 import transaction
 from zope.component import getUtility
 from zope.security.proxy import removeSecurityProxy
@@ -330,8 +329,8 @@ class QuestionEmailJobTestCase(TestCaseWithFactory):
         out, err, exit_code = run_script(
             "LP_DEBUG_SQL=1 cronscripts/process-job-source.py -vv %s" % (
                 IQuestionEmailJobSource.getName()))
-        self.addDetail("stdout", Content(UTF8_TEXT, lambda: [out]))
-        self.addDetail("stderr", Content(UTF8_TEXT, lambda: [err]))
+        self.addDetail("stdout", text_content(out))
+        self.addDetail("stderr", text_content(err))
         self.assertEqual(0, exit_code)
         self.assertTrue(
             'Traceback (most recent call last)' not in err)
diff --git a/lib/lp/app/tests/test_versioninfo.py b/lib/lp/app/tests/test_versioninfo.py
index 7db9b35..3515679 100644
--- a/lib/lp/app/tests/test_versioninfo.py
+++ b/lib/lp/app/tests/test_versioninfo.py
@@ -19,6 +19,7 @@ class TestVersionInfo(unittest.TestCase):
         args = [os.path.join(TREE_ROOT, "bin/py"), "-c",
                 "from lp.app.versioninfo import revision;"
                 "print(revision)"]
-        process = subprocess.Popen(args, cwd='/tmp', stdout=subprocess.PIPE)
+        process = subprocess.Popen(
+            args, cwd='/tmp', stdout=subprocess.PIPE, universal_newlines=True)
         (output, errors) = process.communicate(None)
         self.assertEqual(revision, output.rstrip("\n"))
diff --git a/lib/lp/bugs/doc/bugnotification-sending.txt b/lib/lp/bugs/doc/bugnotification-sending.txt
index e0ed9aa..a26cd85 100644
--- a/lib/lp/bugs/doc/bugnotification-sending.txt
+++ b/lib/lp/bugs/doc/bugnotification-sending.txt
@@ -534,7 +534,7 @@ makes it write out the emails it sends.
     >>> process = subprocess.Popen(
     ...     'cronscripts/send-bug-notifications.py -v', shell=True,
     ...     stdin=subprocess.PIPE, stdout=subprocess.PIPE,
-    ...     stderr=subprocess.PIPE)
+    ...     stderr=subprocess.PIPE, universal_newlines=True)
     >>> (out, err) = process.communicate()
     >>> process.returncode
     0
diff --git a/lib/lp/bugs/doc/bugtask-expiration.txt b/lib/lp/bugs/doc/bugtask-expiration.txt
index f8a1605..4ff1d08 100644
--- a/lib/lp/bugs/doc/bugtask-expiration.txt
+++ b/lib/lp/bugs/doc/bugtask-expiration.txt
@@ -508,7 +508,7 @@ config.malone.expiration_dbuser.
     >>> process = subprocess.Popen(
     ...     'cronscripts/expire-bugtasks.py', shell=True,
     ...     stdin=subprocess.PIPE, stdout=subprocess.PIPE,
-    ...     stderr=subprocess.PIPE)
+    ...     stderr=subprocess.PIPE, universal_newlines=True)
     >>> (out, err) = process.communicate()
     >>> print(err)
     INFO    Creating lockfile: /var/lock/launchpad-expire-bugtasks.lock
diff --git a/lib/lp/bugs/doc/checkwatches-cli-switches.txt b/lib/lp/bugs/doc/checkwatches-cli-switches.txt
index 92db527..d3613e9 100644
--- a/lib/lp/bugs/doc/checkwatches-cli-switches.txt
+++ b/lib/lp/bugs/doc/checkwatches-cli-switches.txt
@@ -119,7 +119,7 @@ tracker option fully.
     >>> process = subprocess.Popen(
     ...     ['cronscripts/checkwatches.py', '-h'],
     ...     stdin=subprocess.PIPE, stdout=subprocess.PIPE,
-    ...     stderr=subprocess.PIPE)
+    ...     stderr=subprocess.PIPE, universal_newlines=True)
     >>> (out, err) = process.communicate()
     >>> print(out)
     Usage: checkwatches.py [options]
diff --git a/lib/lp/bugs/doc/cve-update.txt b/lib/lp/bugs/doc/cve-update.txt
index d9701ad..417728a 100644
--- a/lib/lp/bugs/doc/cve-update.txt
+++ b/lib/lp/bugs/doc/cve-update.txt
@@ -47,7 +47,7 @@ Now run the cronscript.
     ...         process = subprocess.Popen(
     ...             [sys.executable, script, '-u', url],
     ...             stdin=subprocess.PIPE, stdout=subprocess.PIPE,
-    ...             stderr=subprocess.STDOUT)
+    ...             stderr=subprocess.STDOUT, universal_newlines=True)
     ...         return process.communicate()
     ...     finally:
     ...         shutil.rmtree(tempdir)
diff --git a/lib/lp/bugs/doc/externalbugtracker-debbugs.txt b/lib/lp/bugs/doc/externalbugtracker-debbugs.txt
index 4353e79..1054a96 100644
--- a/lib/lp/bugs/doc/externalbugtracker-debbugs.txt
+++ b/lib/lp/bugs/doc/externalbugtracker-debbugs.txt
@@ -648,7 +648,7 @@ tracker.
     >>> process = subprocess.Popen(
     ...     'scripts/import-debian-bugs.py 237001 322535', shell=True,
     ...     stdin=subprocess.PIPE, stdout=subprocess.PIPE,
-    ...     stderr=subprocess.PIPE)
+    ...     stderr=subprocess.PIPE, universal_newlines=True)
     >>> (out, err) = process.communicate()
     >>> process.returncode
     0
diff --git a/lib/lp/bugs/doc/product-update-remote-product-script.txt b/lib/lp/bugs/doc/product-update-remote-product-script.txt
index 5cb6af7..4f76945 100644
--- a/lib/lp/bugs/doc/product-update-remote-product-script.txt
+++ b/lib/lp/bugs/doc/product-update-remote-product-script.txt
@@ -6,10 +6,10 @@ The script that uses RemoteProductUpdater is update-remote-product.py.
     >>> process = subprocess.Popen(
     ...     ['cronscripts/update-remote-product.py'],
     ...     stdin=subprocess.PIPE, stdout=subprocess.PIPE,
-    ...     stderr=subprocess.PIPE)
+    ...     stderr=subprocess.PIPE, universal_newlines=True)
     >>> (out, err) = process.communicate()
-    >>> out
-    ''
+    >>> print(out)
+    <BLANKLINE>
     >>> process.returncode
     0
 
diff --git a/lib/lp/bugs/doc/sourceforge-remote-products.txt b/lib/lp/bugs/doc/sourceforge-remote-products.txt
index cef4ee9..8f15721 100644
--- a/lib/lp/bugs/doc/sourceforge-remote-products.txt
+++ b/lib/lp/bugs/doc/sourceforge-remote-products.txt
@@ -154,10 +154,10 @@ remote_product fields.
     >>> process = subprocess.Popen(
     ...     ['cronscripts/update-sourceforge-remote-products.py', '-v'],
     ...     stdin=subprocess.PIPE, stdout=subprocess.PIPE,
-    ...     stderr=subprocess.PIPE)
+    ...     stderr=subprocess.PIPE, universal_newlines=True)
     >>> (out, err) = process.communicate()
-    >>> out
-    ''
+    >>> print(out)
+    <BLANKLINE>
     >>> process.returncode
     0
 
diff --git a/lib/lp/bugs/model/tests/test_bugtask.py b/lib/lp/bugs/model/tests/test_bugtask.py
index ec8c2e1..f2d4044 100644
--- a/lib/lp/bugs/model/tests/test_bugtask.py
+++ b/lib/lp/bugs/model/tests/test_bugtask.py
@@ -3142,7 +3142,7 @@ class TestTargetNameCache(TestCase):
         process = subprocess.Popen(
             'cronscripts/update-bugtask-targetnamecaches.py', shell=True,
             stdin=subprocess.PIPE, stdout=subprocess.PIPE,
-            stderr=subprocess.PIPE)
+            stderr=subprocess.PIPE, universal_newlines=True)
         (out, err) = process.communicate()
 
         self.assertTrue(err.startswith(("INFO    Creating lockfile: "
diff --git a/lib/lp/registry/doc/cache-country-mirrors.txt b/lib/lp/registry/doc/cache-country-mirrors.txt
index 47ede28..af85dd9 100644
--- a/lib/lp/registry/doc/cache-country-mirrors.txt
+++ b/lib/lp/registry/doc/cache-country-mirrors.txt
@@ -19,10 +19,13 @@ files are saved.
     >>> directory = tempfile.mkdtemp()
     >>> process = Popen(
     ...     'scripts/cache-country-mirrors.py -q %s' % directory,
-    ...     shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE)
+    ...     shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE,
+    ...     universal_newlines=True)
     >>> (out, err) = process.communicate()
-    >>> out, err
-    ('', '')
+    >>> print(out)
+    <BLANKLINE>
+    >>> print(err)
+    <BLANKLINE>
     >>> process.returncode
     0
 
diff --git a/lib/lp/registry/doc/convert-person-to-team.txt b/lib/lp/registry/doc/convert-person-to-team.txt
index edea9e4..6df6e6c 100644
--- a/lib/lp/registry/doc/convert-person-to-team.txt
+++ b/lib/lp/registry/doc/convert-person-to-team.txt
@@ -17,10 +17,13 @@ team and the name of the team owner as arguments.
     >>> from subprocess import Popen, PIPE
     >>> process = Popen(
     ...     'scripts/convert-person-to-team.py -q matsubara mark',
-    ...     shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE)
+    ...     shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE,
+    ...     universal_newlines=True)
     >>> (out, err) = process.communicate()
-    >>> out, err
-    ('', '')
+    >>> print(out)
+    <BLANKLINE>
+    >>> print(err)
+    <BLANKLINE>
     >>> process.returncode
     0
 
diff --git a/lib/lp/registry/doc/distribution-mirror.txt b/lib/lp/registry/doc/distribution-mirror.txt
index 432dffc..8dee6d7 100644
--- a/lib/lp/registry/doc/distribution-mirror.txt
+++ b/lib/lp/registry/doc/distribution-mirror.txt
@@ -637,7 +637,7 @@ First we need to run the http server that's going to answer our requests.
     ...            '--no-remote-hosts' % arguments)
     ...     prober = subprocess.Popen(
     ...         cmd, shell=True,stdin=subprocess.PIPE, stdout=subprocess.PIPE,
-    ...         stderr=subprocess.PIPE)
+    ...         stderr=subprocess.PIPE, universal_newlines=True)
     ...     stdout, stderr = prober.communicate()
     ...     return prober, stdout, stderr
 
diff --git a/lib/lp/registry/doc/person-karma.txt b/lib/lp/registry/doc/person-karma.txt
index 7610a70..da52f64 100644
--- a/lib/lp/registry/doc/person-karma.txt
+++ b/lib/lp/registry/doc/person-karma.txt
@@ -121,7 +121,7 @@ is updated by the foaf-update-karma-cache.py cronscript.
     >>> process = subprocess.Popen(
     ...     'cronscripts/foaf-update-karma-cache.py', shell=True,
     ...     stdin=subprocess.PIPE, stdout=subprocess.PIPE,
-    ...     stderr=subprocess.PIPE)
+    ...     stderr=subprocess.PIPE, universal_newlines=True)
     >>> (out, err) = process.communicate()
     >>> print err
     INFO    Creating lockfile: /var/lock/launchpad-karma-update.lock
diff --git a/lib/lp/registry/doc/person-notification.txt b/lib/lp/registry/doc/person-notification.txt
index d823895..beaf428 100644
--- a/lib/lp/registry/doc/person-notification.txt
+++ b/lib/lp/registry/doc/person-notification.txt
@@ -81,10 +81,12 @@ This includes notifications to teams owned by other teams.
     >>> process = subprocess.Popen(
     ...     'cronscripts/send-person-notifications.py -q', shell=True,
     ...     stdin=subprocess.PIPE, stdout=subprocess.PIPE,
-    ...     stderr=subprocess.PIPE)
+    ...     stderr=subprocess.PIPE, universal_newlines=True)
     >>> (out, err) = process.communicate()
-    >>> out, err
-    ('', '')
+    >>> print(out)
+    <BLANKLINE>
+    >>> print(err)
+    <BLANKLINE>
     >>> process.returncode
     0
 
diff --git a/lib/lp/registry/doc/standing.txt b/lib/lp/registry/doc/standing.txt
index 7e0b0f3..02ce8ce 100644
--- a/lib/lp/registry/doc/standing.txt
+++ b/lib/lp/registry/doc/standing.txt
@@ -233,7 +233,7 @@ standing untouched.
     >>> process = subprocess.Popen(
     ...     'cronscripts/update-standing.py', shell=True,
     ...     stdin=subprocess.PIPE, stdout=subprocess.PIPE,
-    ...     stderr=subprocess.PIPE)
+    ...     stderr=subprocess.PIPE, universal_newlines=True)
     >>> stdout, stderr = process.communicate()
     >>> print stdout
     <BLANKLINE>
@@ -264,7 +264,7 @@ update-standing script bumps his standing to Good too.
     >>> process = subprocess.Popen(
     ...     'cronscripts/update-standing.py', shell=True,
     ...     stdin=subprocess.PIPE, stdout=subprocess.PIPE,
-    ...     stderr=subprocess.PIPE)
+    ...     stderr=subprocess.PIPE, universal_newlines=True)
     >>> stdout, stderr = process.communicate()
     >>> print stdout
     <BLANKLINE>
diff --git a/lib/lp/registry/tests/test_membership_notification_job.py b/lib/lp/registry/tests/test_membership_notification_job.py
index e4c09a2..cad34e6 100644
--- a/lib/lp/registry/tests/test_membership_notification_job.py
+++ b/lib/lp/registry/tests/test_membership_notification_job.py
@@ -5,8 +5,7 @@
 
 __metaclass__ = type
 
-from testtools.content import Content
-from testtools.content_type import UTF8_TEXT
+from testtools.content import text_content
 import transaction
 from zope.component import getUtility
 
@@ -113,8 +112,8 @@ class MembershipNotificationJobTest(TestCaseWithFactory):
         out, err, exit_code = run_script(
             "LP_DEBUG_SQL=1 cronscripts/process-job-source.py -vv %s" % (
                 IMembershipNotificationJobSource.getName()))
-        self.addDetail("stdout", Content(UTF8_TEXT, lambda: [out]))
-        self.addDetail("stderr", Content(UTF8_TEXT, lambda: [err]))
+        self.addDetail("stdout", text_content(out))
+        self.addDetail("stderr", text_content(err))
         self.assertEqual(0, exit_code)
         self.assertTrue(job_repr in err, err)
         self.assertTrue("MembershipNotificationJob sent email" in err, err)
diff --git a/lib/lp/registry/tests/test_person_merge_job.py b/lib/lp/registry/tests/test_person_merge_job.py
index cea0979..24d85fb 100644
--- a/lib/lp/registry/tests/test_person_merge_job.py
+++ b/lib/lp/registry/tests/test_person_merge_job.py
@@ -5,8 +5,7 @@
 
 __metaclass__ = type
 
-from testtools.content import Content
-from testtools.content_type import UTF8_TEXT
+from testtools.content import text_content
 import transaction
 from zope.component import getUtility
 from zope.interface.verify import verifyObject
@@ -144,8 +143,8 @@ class TestPersonMergeJob(TestCaseWithFactory):
             "LP_DEBUG_SQL=1 cronscripts/process-job-source.py -vv %s" % (
                 IPersonMergeJobSource.getName()))
 
-        self.addDetail("stdout", Content(UTF8_TEXT, lambda: [out]))
-        self.addDetail("stderr", Content(UTF8_TEXT, lambda: [err]))
+        self.addDetail("stdout", text_content(out))
+        self.addDetail("stderr", text_content(err))
 
         self.assertEqual(0, exit_code)
         IStore(self.from_person).invalidate()
diff --git a/lib/lp/registry/tests/test_productjob.py b/lib/lp/registry/tests/test_productjob.py
index 38d8634..70ae61b 100644
--- a/lib/lp/registry/tests/test_productjob.py
+++ b/lib/lp/registry/tests/test_productjob.py
@@ -11,8 +11,7 @@ from datetime import (
     )
 
 import pytz
-from testtools.content import Content
-from testtools.content_type import UTF8_TEXT
+from testtools.content import text_content
 import transaction
 from zope.component import getUtility
 from zope.interface import (
@@ -155,8 +154,8 @@ class DailyProductJobsTestCase(TestCaseWithFactory, CommercialHelpers):
         transaction.commit()
         stdout, stderr, retcode = run_script(
             'cronscripts/daily_product_jobs.py')
-        self.addDetail("stdout", Content(UTF8_TEXT, lambda: [stdout]))
-        self.addDetail("stderr", Content(UTF8_TEXT, lambda: [stderr]))
+        self.addDetail("stdout", text_content(stdout))
+        self.addDetail("stderr", text_content(stderr))
         self.assertEqual(0, retcode)
         self.assertIn('Requested 3 total product jobs.', stderr)
 
@@ -590,8 +589,8 @@ class CommericialExpirationMixin(CommercialHelpers):
         out, err, exit_code = run_script(
             "LP_DEBUG_SQL=1 cronscripts/process-job-source.py -vv %s" %
              self.JOB_SOURCE_INTERFACE.getName())
-        self.addDetail("stdout", Content(UTF8_TEXT, lambda: [out]))
-        self.addDetail("stderr", Content(UTF8_TEXT, lambda: [err]))
+        self.addDetail("stdout", text_content(out))
+        self.addDetail("stderr", text_content(err))
         self.assertEqual(0, exit_code)
         self.assertTrue(
             'Traceback (most recent call last)' not in err)
diff --git a/lib/lp/registry/tests/test_sharingjob.py b/lib/lp/registry/tests/test_sharingjob.py
index bda6a52..58fa5c1 100644
--- a/lib/lp/registry/tests/test_sharingjob.py
+++ b/lib/lp/registry/tests/test_sharingjob.py
@@ -5,8 +5,7 @@
 
 __metaclass__ = type
 
-from testtools.content import Content
-from testtools.content_type import UTF8_TEXT
+from testtools.content import text_content
 import transaction
 from zope.component import getUtility
 from zope.security.proxy import removeSecurityProxy
@@ -210,8 +209,8 @@ class TestRunViaCron(TestCaseWithFactory):
         out, err, exit_code = run_script(
             "LP_DEBUG_SQL=1 cronscripts/process-job-source.py -vv %s" % (
                 job_type))
-        self.addDetail("stdout", Content(UTF8_TEXT, lambda: [out]))
-        self.addDetail("stderr", Content(UTF8_TEXT, lambda: [err]))
+        self.addDetail("stdout", text_content(out))
+        self.addDetail("stderr", text_content(err))
         self.assertEqual(0, exit_code)
         self.assertTrue(
             'Traceback (most recent call last)' not in err)
diff --git a/lib/lp/registry/tests/test_teammembership.py b/lib/lp/registry/tests/test_teammembership.py
index a794a43..31e6e44 100644
--- a/lib/lp/registry/tests/test_teammembership.py
+++ b/lib/lp/registry/tests/test_teammembership.py
@@ -1133,7 +1133,7 @@ class TestCheckTeamParticipationScript(TestCase):
         cmd.extend(args)
         process = subprocess.Popen(
             cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
-            stderr=subprocess.PIPE)
+            stderr=subprocess.PIPE, universal_newlines=True)
         out, err = process.communicate()
         if out != "":
             self.addDetail("stdout", text_content(out))
diff --git a/lib/lp/services/database/tests/test_isolation_changes.py b/lib/lp/services/database/tests/test_isolation_changes.py
index 3104776..783ab31 100644
--- a/lib/lp/services/database/tests/test_isolation_changes.py
+++ b/lib/lp/services/database/tests/test_isolation_changes.py
@@ -109,7 +109,9 @@ class TestIsolation(unittest.TestCase):
         script = os.path.join(
                 os.path.dirname(__file__), 'script_isolation.py')
         cmd = [sys.executable, script]
-        process = Popen(cmd, stdout=PIPE, stderr=STDOUT, stdin=PIPE)
+        process = Popen(
+            cmd, stdout=PIPE, stderr=STDOUT, stdin=PIPE,
+            universal_newlines=True)
         (script_output, _empty) = process.communicate()
         self.assertEqual(process.returncode, 0, 'Error: ' + script_output)
         self.assertEqual(script_output, dedent("""\
diff --git a/lib/lp/services/librarianserver/tests/test_gc.py b/lib/lp/services/librarianserver/tests/test_gc.py
index 0c2465d..0d8ac5f 100644
--- a/lib/lp/services/librarianserver/tests/test_gc.py
+++ b/lib/lp/services/librarianserver/tests/test_gc.py
@@ -569,7 +569,9 @@ class TestLibrarianGarbageCollectionBase:
                 config.root, 'cronscripts', 'librarian-gc.py'
                 )
         cmd = [sys.executable, script_path, '-q']
-        process = Popen(cmd, stdout=PIPE, stderr=STDOUT, stdin=PIPE)
+        process = Popen(
+            cmd, stdout=PIPE, stderr=STDOUT, stdin=PIPE,
+            universal_newlines=True)
         (script_output, _empty) = process.communicate()
         self.assertEqual(
             process.returncode, 0, 'Error: %s' % script_output)
@@ -1143,7 +1145,9 @@ class TestBlobCollection(TestCase):
                 config.root, 'cronscripts', 'librarian-gc.py'
                 )
         cmd = [sys.executable, script_path, '-q']
-        process = Popen(cmd, stdout=PIPE, stderr=STDOUT, stdin=PIPE)
+        process = Popen(
+            cmd, stdout=PIPE, stderr=STDOUT, stdin=PIPE,
+            universal_newlines=True)
         (script_output, _empty) = process.communicate()
         self.assertEqual(
             process.returncode, 0, 'Error: %s' % script_output)
diff --git a/lib/lp/services/scripts/doc/launchpad-scripts.txt b/lib/lp/services/scripts/doc/launchpad-scripts.txt
index f5765a0..7eea651 100644
--- a/lib/lp/services/scripts/doc/launchpad-scripts.txt
+++ b/lib/lp/services/scripts/doc/launchpad-scripts.txt
@@ -24,7 +24,7 @@ LaunchpadCronScript) also log warnings and and errors as OOPS reports.
     >>> p = subprocess.Popen(
     ...     [sys.executable, cronscript_crash_path, '-vq'],
     ...     stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
-    ...     stdin=subprocess.PIPE)
+    ...     stdin=subprocess.PIPE, universal_newlines=True)
     >>> print(p.communicate()[0])
     INFO    Creating lockfile: ...
     WARNING This is a warning
diff --git a/lib/lp/services/scripts/doc/scripts-and-zcml.txt b/lib/lp/services/scripts/doc/scripts-and-zcml.txt
index 76819f9..989911a 100644
--- a/lib/lp/services/scripts/doc/scripts-and-zcml.txt
+++ b/lib/lp/services/scripts/doc/scripts-and-zcml.txt
@@ -29,7 +29,8 @@ Run the script (making sure it uses the testrunner configuration).
     >>> from lp.services.config import config
     >>> bin_py = os.path.join(config.root, 'bin', 'py')
     >>> proc = subprocess.Popen(
-    ...     [bin_py, script_file.name], stdout=subprocess.PIPE, stderr=None)
+    ...     [bin_py, script_file.name], stdout=subprocess.PIPE, stderr=None,
+    ...     universal_newlines=True)
 
 Check that we get the expected output.
 
diff --git a/lib/lp/services/scripts/tests/__init__.py b/lib/lp/services/scripts/tests/__init__.py
index 9aea9b2..8ca2036 100644
--- a/lib/lp/services/scripts/tests/__init__.py
+++ b/lib/lp/services/scripts/tests/__init__.py
@@ -44,7 +44,8 @@ def find_lp_scripts():
     return sorted(scripts)
 
 
-def run_script(script_relpath, args, expect_returncode=0, extra_env=None):
+def run_script(script_relpath, args, expect_returncode=0, extra_env=None,
+               universal_newlines=True):
     """Run a script for testing purposes.
 
     :param script_relpath: The relative path to the script, from the tree
@@ -54,6 +55,8 @@ def run_script(script_relpath, args, expect_returncode=0, extra_env=None):
         is returned, and exception will be raised.
     :param extra_env: A dictionary of extra environment variables to provide
         to the script, or None.
+    :param universal_newlines: Passed to `subprocess.Popen`, defaulting to
+        True.
     """
     script = os.path.join(config.root, script_relpath)
     args = [script] + args
@@ -61,7 +64,8 @@ def run_script(script_relpath, args, expect_returncode=0, extra_env=None):
     if extra_env is not None:
         env.update(extra_env)
     process = subprocess.Popen(
-        args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)
+        args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env,
+        universal_newlines=universal_newlines)
     stdout, stderr = process.communicate()
     if process.returncode != expect_returncode:
         raise AssertionError('Failed:\n%s\n%s' % (stdout, stderr))
diff --git a/lib/lp/services/scripts/tests/test_all_scripts.py b/lib/lp/services/scripts/tests/test_all_scripts.py
index 60efb8c..9e69804 100644
--- a/lib/lp/services/scripts/tests/test_all_scripts.py
+++ b/lib/lp/services/scripts/tests/test_all_scripts.py
@@ -35,7 +35,7 @@ class ScriptsTestCase(WithScenarios, TestCase):
     def test_script(self):
         # Run self.script_path with '-h' to make sure it runs cleanly.
         cmd_line = self.script_path + " -h"
-        out, err, returncode = run_script(cmd_line, universal_newlines=True)
+        out, err, returncode = run_script(cmd_line)
         self.assertThat(err, DocTestMatches('', doctest.REPORT_NDIFF))
         self.assertEqual('', err)
         self.assertEqual(os.EX_OK, returncode)
diff --git a/lib/lp/services/scripts/tests/test_logger.txt b/lib/lp/services/scripts/tests/test_logger.txt
index 05cb8aa..f27edff 100644
--- a/lib/lp/services/scripts/tests/test_logger.txt
+++ b/lib/lp/services/scripts/tests/test_logger.txt
@@ -13,7 +13,8 @@ by our logging handler.
     ...     cmd = [sys.executable, test_script_path] + list(args)
     ...     proc = subprocess.Popen(
     ...         cmd, stdin=subprocess.PIPE,
-    ...         stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kw)
+    ...         stdout=subprocess.PIPE, stderr=subprocess.PIPE,
+    ...         universal_newlines=True, **kw)
     ...     out, err = proc.communicate()
     ...     assert out == "", "Output to stdout"
     ...     print(err)
diff --git a/lib/lp/services/statistics/tests/test_update_stats.py b/lib/lp/services/statistics/tests/test_update_stats.py
index 254176e..32550b8 100644
--- a/lib/lp/services/statistics/tests/test_update_stats.py
+++ b/lib/lp/services/statistics/tests/test_update_stats.py
@@ -85,7 +85,7 @@ class UpdateStatsTest(unittest.TestCase):
         cmd = [sys.executable, get_script(), '--quiet']
         process = subprocess.Popen(
             cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
-            stderr=subprocess.STDOUT)
+            stderr=subprocess.STDOUT, universal_newlines=True)
         (stdout, empty_stderr) = process.communicate()
 
         # Ensure it returned a success code
diff --git a/lib/lp/soyuz/doc/buildd-mass-retry.txt b/lib/lp/soyuz/doc/buildd-mass-retry.txt
index 9365f95..bfc8726 100644
--- a/lib/lp/soyuz/doc/buildd-mass-retry.txt
+++ b/lib/lp/soyuz/doc/buildd-mass-retry.txt
@@ -32,7 +32,8 @@ Passing only suite, request retry on all failed states:
 
     >>> process = subprocess.Popen(
     ...     [sys.executable, script, "-v", "-NFDC", "-s", "hoary"],
-    ...     stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+    ...     stdout=subprocess.PIPE, stderr=subprocess.PIPE,
+    ...     universal_newlines=True)
     >>> stdout, stderr = process.communicate()
     >>> process.returncode
     0
@@ -70,7 +71,8 @@ A new run doesn't pick it up.
 
     >>> process = subprocess.Popen(
     ...     [sys.executable, script, "-v", "-NFDC", "-s", "hoary"],
-    ...     stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+    ...     stdout=subprocess.PIPE, stderr=subprocess.PIPE,
+    ...     universal_newlines=True)
     >>> stdout, stderr = process.communicate()
     >>> process.returncode
     0
@@ -99,7 +101,8 @@ nothing is done:
     ...         sys.executable, script,
     ...         "-v", "-NFDC", "-s", "hoary", "-a", "hppa",
     ...     ],
-    ...     stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+    ...     stdout=subprocess.PIPE, stderr=subprocess.PIPE,
+    ...     universal_newlines=True)
     >>> stdout, stderr = process.communicate()
     >>> process.returncode
     0
@@ -120,7 +123,8 @@ Selecting only a specific failed state:
 
     >>> process = subprocess.Popen(
     ...     [sys.executable, script, "-v", "-NF", "-s", "hoary"],
-    ...     stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+    ...     stdout=subprocess.PIPE, stderr=subprocess.PIPE,
+    ...     universal_newlines=True)
     >>> stdout, stderr = process.communicate()
     >>> process.returncode
     0
diff --git a/lib/lp/soyuz/doc/gina-multiple-arch.txt b/lib/lp/soyuz/doc/gina-multiple-arch.txt
index 893e260..727df8a 100644
--- a/lib/lp/soyuz/doc/gina-multiple-arch.txt
+++ b/lib/lp/soyuz/doc/gina-multiple-arch.txt
@@ -79,7 +79,8 @@ Let's set up the filesystem:
 
     >>> gina_proc = [sys.executable, 'scripts/gina.py', '-q',
     ...              'dapper', 'dapper-updates']
-    >>> proc = subprocess.Popen(gina_proc, stderr=subprocess.PIPE)
+    >>> proc = subprocess.Popen(
+    ...     gina_proc, stderr=subprocess.PIPE, universal_newlines=True)
     >>> print(proc.stderr.read())
     WARNING ...
     WARNING No source package bdftopcf (0.99.0-1) listed for bdftopcf (0.99.0-1), scrubbing archive...
diff --git a/lib/lp/soyuz/doc/gina.txt b/lib/lp/soyuz/doc/gina.txt
index 0377498..2e2e253 100644
--- a/lib/lp/soyuz/doc/gina.txt
+++ b/lib/lp/soyuz/doc/gina.txt
@@ -126,7 +126,8 @@ And give it a spin:
 
     >>> gina_proc = [sys.executable, 'scripts/gina.py', '-q',
     ...              'hoary', 'breezy']
-    >>> proc = subprocess.Popen(gina_proc, stderr=subprocess.PIPE)
+    >>> proc = subprocess.Popen(
+    ...     gina_proc, stderr=subprocess.PIPE, universal_newlines=True)
 
 Check STDERR for the errors we expected:
 
@@ -515,7 +516,8 @@ run.
 
     >>> gina_proc = [sys.executable, 'scripts/gina.py', '-q',
     ...              'hoary', 'breezy']
-    >>> proc = subprocess.Popen(gina_proc, stderr=subprocess.PIPE)
+    >>> proc = subprocess.Popen(
+    ...     gina_proc, stderr=subprocess.PIPE, universal_newlines=True)
     >>> print(proc.stderr.read())
     ERROR   Error processing package files for clearlooks
     ...
@@ -644,7 +646,8 @@ First get a set of existing publishings for both source and binary:
 Now run gina to import packages and convert them to partner:
 
     >>> gina_proc = [sys.executable, 'scripts/gina.py', '-q', 'partner']
-    >>> proc = subprocess.Popen(gina_proc, stderr=subprocess.PIPE)
+    >>> proc = subprocess.Popen(
+    ...     gina_proc, stderr=subprocess.PIPE, universal_newlines=True)
     >>> proc.wait()
     0
     >>> transaction.commit()
@@ -747,7 +750,8 @@ Commit the changes and run the importer script.
 
     >>> gina_proc = [
     ...     sys.executable, 'scripts/gina.py', '-q', 'lenny']
-    >>> proc = subprocess.Popen(gina_proc, stderr=subprocess.PIPE)
+    >>> proc = subprocess.Popen(
+    ...     gina_proc, stderr=subprocess.PIPE, universal_newlines=True)
     >>> proc.wait()
     0
 
@@ -785,7 +789,8 @@ batch.
 
     >>> gina_proc = [
     ...     sys.executable, 'scripts/gina.py', 'lenny', 'partner']
-    >>> proc = subprocess.Popen(gina_proc, stderr=subprocess.PIPE)
+    >>> proc = subprocess.Popen(
+    ...     gina_proc, stderr=subprocess.PIPE, universal_newlines=True)
 
     >>> print(proc.stderr.read())
     INFO    Creating lockfile: /var/lock/launchpad-gina.lock
@@ -805,7 +810,8 @@ Other tests
 For kicks, finally, run gina on a configured but incomplete archive:
 
     >>> gina_proc = [sys.executable, 'scripts/gina.py', '-q', 'bogus']
-    >>> proc = subprocess.Popen(gina_proc, stderr=subprocess.PIPE)
+    >>> proc = subprocess.Popen(
+    ...     gina_proc, stderr=subprocess.PIPE, universal_newlines=True)
     >>> print(proc.stderr.read())
     ERROR   Failed to analyze archive for bogoland
     ...
diff --git a/lib/lp/soyuz/doc/package-cache-script.txt b/lib/lp/soyuz/doc/package-cache-script.txt
index ed58499..7b1a698 100644
--- a/lib/lp/soyuz/doc/package-cache-script.txt
+++ b/lib/lp/soyuz/doc/package-cache-script.txt
@@ -38,7 +38,8 @@ distribution and respective distroseriess considered in stderr.
   >>> import sys
   >>> process = subprocess.Popen([sys.executable, script],
   ...                            stdout=subprocess.PIPE,
-  ...                            stderr=subprocess.PIPE)
+  ...                            stderr=subprocess.PIPE,
+  ...                            universal_newlines=True)
   >>> stdout, stderr = process.communicate()
   >>> process.returncode
   0
diff --git a/lib/lp/soyuz/doc/soyuz-upload.txt b/lib/lp/soyuz/doc/soyuz-upload.txt
index 9c819ba..5030c6a 100644
--- a/lib/lp/soyuz/doc/soyuz-upload.txt
+++ b/lib/lp/soyuz/doc/soyuz-upload.txt
@@ -246,7 +246,8 @@ just upload number 1.
     ...     "-C", "sync", "-J", upload_dir_1_name, temp_dir,
     ...     ],
     ...     stdout=subprocess.PIPE,
-    ...     stderr=subprocess.PIPE)
+    ...     stderr=subprocess.PIPE,
+    ...     universal_newlines=True)
     >>> stdout, stderr = process.communicate()
     >>> process.returncode
     0
@@ -269,7 +270,8 @@ Now continue with the real upload.
     ...     "-C", "sync", temp_dir,
     ...     ],
     ...     stdout=subprocess.PIPE,
-    ...     stderr=subprocess.PIPE)
+    ...     stderr=subprocess.PIPE,
+    ...     universal_newlines=True)
 
     >>> stdout, stderr = process.communicate()
     >>> if process.returncode != 0:
@@ -456,7 +458,8 @@ Invoke Publisher script against the 'ubuntutest' distribution:
     >>> process = subprocess.Popen([sys.executable, script, "-vvCq",
     ...                             "-d", "ubuntutest"],
     ...                            stdout=subprocess.PIPE,
-    ...                            stderr=subprocess.PIPE)
+    ...                            stderr=subprocess.PIPE,
+    ...                            universal_newlines=True)
     >>> stdout, stderr = process.communicate()
     >>> print(stdout)
     <BLANKLINE>
@@ -554,7 +557,8 @@ Invoke Publisher script again to land our changes in the archive
     >>> process = subprocess.Popen([sys.executable, script, "-vvCq",
     ...                             "-d", "ubuntutest"],
     ...                            stdout=subprocess.PIPE,
-    ...                            stderr=subprocess.PIPE)
+    ...                            stderr=subprocess.PIPE,
+    ...                            universal_newlines=True)
     >>> stdout, stderr = process.communicate()
     >>> process.returncode
     0
diff --git a/lib/lp/soyuz/scripts/tests/test_obsoletedistroseries.py b/lib/lp/soyuz/scripts/tests/test_obsoletedistroseries.py
index 2dbd468..87a4a57 100644
--- a/lib/lp/soyuz/scripts/tests/test_obsoletedistroseries.py
+++ b/lib/lp/soyuz/scripts/tests/test_obsoletedistroseries.py
@@ -47,7 +47,8 @@ class TestObsoleteDistroseriesScript(TestCase):
         args = [sys.executable, script, '-y']
         args.extend(extra_args)
         process = subprocess.Popen(
-            args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+            args, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
+            universal_newlines=True)
         stdout, stderr = process.communicate()
         return (process.returncode, stdout, stderr)
 
diff --git a/lib/lp/soyuz/tests/test_processacceptedbugsjob.py b/lib/lp/soyuz/tests/test_processacceptedbugsjob.py
index 1a7beb3..005494d 100644
--- a/lib/lp/soyuz/tests/test_processacceptedbugsjob.py
+++ b/lib/lp/soyuz/tests/test_processacceptedbugsjob.py
@@ -410,8 +410,7 @@ class TestProcessAcceptedBugsJob(TestCaseWithFactory):
 
         out, err, exit_code = run_script(
             "LP_DEBUG_SQL=1 cronscripts/process-job-source.py -vv %s" % (
-                IProcessAcceptedBugsJobSource.getName()),
-            universal_newlines=True)
+                IProcessAcceptedBugsJobSource.getName()))
 
         self.addDetail("stdout", text_content(out))
         self.addDetail("stderr", text_content(err))
diff --git a/lib/lp/testing/__init__.py b/lib/lp/testing/__init__.py
index b63632a..6e16010 100644
--- a/lib/lp/testing/__init__.py
+++ b/lib/lp/testing/__init__.py
@@ -1319,7 +1319,7 @@ def time_counter(origin=None, delta=timedelta(seconds=5)):
         now += delta
 
 
-def run_script(cmd_line, env=None, cwd=None, universal_newlines=False):
+def run_script(cmd_line, env=None, cwd=None, universal_newlines=True):
     """Run the given command line as a subprocess.
 
     :param cmd_line: A command line suitable for passing to
@@ -1329,6 +1329,7 @@ def run_script(cmd_line, env=None, cwd=None, universal_newlines=False):
         PYTHONPATH will be removed from it because it will break the
         script.
     :param universal_newlines: If True, return stdout and stderr as text.
+        Defaults to True.
     :return: A 3-tuple of stdout, stderr, and the process' return code.
     """
     if env is None:
@@ -1342,7 +1343,7 @@ def run_script(cmd_line, env=None, cwd=None, universal_newlines=False):
     return out, err, process.returncode
 
 
-def run_process(cmd, env=None):
+def run_process(cmd, env=None, universal_newlines=True):
     """Run the given command as a subprocess.
 
     This differs from `run_script` in that it does not execute via a shell and
@@ -1353,6 +1354,8 @@ def run_process(cmd, env=None):
     :param env: An optional environment dict. If none is given, the script
         will get a copy of your present environment. Either way, PYTHONPATH
         will be removed from it because it will break the script.
+    :param universal_newlines: If True, return stdout and stderr as text.
+        Defaults to True.
     :return: A 3-tuple of stdout, stderr, and the process' return code.
     """
     if env is None:
@@ -1361,7 +1364,8 @@ def run_process(cmd, env=None):
     with open(os.devnull, "rb") as devnull:
         process = subprocess.Popen(
             cmd, stdin=devnull, stdout=subprocess.PIPE,
-            stderr=subprocess.PIPE, env=env)
+            stderr=subprocess.PIPE, env=env,
+            universal_newlines=universal_newlines)
         stdout, stderr = process.communicate()
         return stdout, stderr, process.returncode
 
diff --git a/lib/lp/testing/script.py b/lib/lp/testing/script.py
index 44cd39c..4e6f2ec 100644
--- a/lib/lp/testing/script.py
+++ b/lib/lp/testing/script.py
@@ -13,13 +13,14 @@ import subprocess
 import sys
 
 
-def run_command(command, args=None, input=None, shell=False):
+def run_command(command, args=None, input=None, universal_newlines=True):
     """Run an external command in a separate process.
 
     :param command: executable to run.
     :param args: optional list of command-line arguments.
     :param input: optional text to feed to command's standard input.
-    :param shell: passed directly to `subprocess.Popen`.
+    :param universal_newlines: passed to `subprocess.Popen`, defaulting to
+        True.
     :return: tuple of return value, standard output, and standard error.
     """
     command_line = [command]
@@ -32,22 +33,26 @@ def run_command(command, args=None, input=None, shell=False):
 
     child = subprocess.Popen(
         command_line, stdin=stdin, stdout=subprocess.PIPE,
-        stderr=subprocess.PIPE)
+        stderr=subprocess.PIPE, universal_newlines=universal_newlines)
     stdout, stderr = child.communicate(input)
     result = child.wait()
     return (result, stdout, stderr)
 
 
-def run_script(script, args=None, input=None):
+def run_script(script, args=None, input=None, universal_newlines=True):
     """Run a Python script in a child process, using current interpreter.
 
     :param script: Python script to run.
     :param args: optional list of command-line arguments.
     :param input: optional string to feed to standard input.
+    :param universal_newlines: passed to `subprocess.Popen`, defaulting to
+        True.
     :return: tuple of return value, standard output, and standard error.
     """
     interpreter_args = [script]
     if args:
         interpreter_args.extend(args)
 
-    return run_command(sys.executable, interpreter_args, input)
+    return run_command(
+        sys.executable, interpreter_args, input,
+        universal_newlines=universal_newlines)
diff --git a/lib/lp/translations/doc/poexport-language-pack.txt b/lib/lp/translations/doc/poexport-language-pack.txt
index a892b4d..da5d142 100644
--- a/lib/lp/translations/doc/poexport-language-pack.txt
+++ b/lib/lp/translations/doc/poexport-language-pack.txt
@@ -373,7 +373,7 @@ number of command-line arguments is wrong.
     ...     return subprocess.Popen(
     ...         command, shell=True,
     ...         stdin=subprocess.PIPE, stdout=subprocess.PIPE,
-    ...         stderr=subprocess.PIPE)
+    ...         stderr=subprocess.PIPE, universal_newlines=True)
 
     >>> proc = get_subprocess('cronscripts/language-pack-exporter.py')
     >>> (out, err) = proc.communicate()
diff --git a/lib/lp/translations/doc/poexport-request.txt b/lib/lp/translations/doc/poexport-request.txt
index 8e87314..fe46f0f 100644
--- a/lib/lp/translations/doc/poexport-request.txt
+++ b/lib/lp/translations/doc/poexport-request.txt
@@ -248,7 +248,7 @@ The script is run.
     >>> process = subprocess.Popen([
     ...     sys.executable, 'cronscripts/rosetta-export-queue.py', '-v'
     ...     ], stdin=subprocess.PIPE, stdout=subprocess.PIPE,
-    ...     stderr=subprocess.STDOUT
+    ...     stderr=subprocess.STDOUT, universal_newlines=True,
     ...     )
     >>> (output, empty) = process.communicate()
     >>> print(output)
diff --git a/lib/lp/translations/doc/remove-translations-by.txt b/lib/lp/translations/doc/remove-translations-by.txt
index d403933..b8be106 100644
--- a/lib/lp/translations/doc/remove-translations-by.txt
+++ b/lib/lp/translations/doc/remove-translations-by.txt
@@ -146,8 +146,8 @@ database.
     >>> returncode
     0
 
-    >>> out
-    ''
+    >>> print(out)
+    <BLANKLINE>
 
     >>> print(err)
     WARNING Safety override in effect.  Deleting translations for template ...
diff --git a/lib/lp/translations/doc/rosetta-poimport-script.txt b/lib/lp/translations/doc/rosetta-poimport-script.txt
index e667ba2..8866db1 100644
--- a/lib/lp/translations/doc/rosetta-poimport-script.txt
+++ b/lib/lp/translations/doc/rosetta-poimport-script.txt
@@ -84,7 +84,8 @@ has enough privileges for the script to run to completion.
     ...                       "rosetta-poimport.py")
     >>> process = subprocess.Popen([sys.executable, script],
     ...                            stdout=subprocess.PIPE,
-    ...                            stderr=subprocess.PIPE,)
+    ...                            stderr=subprocess.PIPE,
+    ...                            universal_newlines=True)
     >>> stdout, stderr = process.communicate()
     >>> process.returncode
     0
diff --git a/lib/lp/translations/doc/sourcepackagerelease-translations.txt b/lib/lp/translations/doc/sourcepackagerelease-translations.txt
index 0ede73d..ac783b8 100644
--- a/lib/lp/translations/doc/sourcepackagerelease-translations.txt
+++ b/lib/lp/translations/doc/sourcepackagerelease-translations.txt
@@ -101,7 +101,7 @@ The first one, approves the import.
     >>> process = subprocess.Popen([
     ...     sys.executable, 'cronscripts/rosetta-approve-imports.py'
     ...     ], stdin=subprocess.PIPE, stdout=subprocess.PIPE,
-    ...     stderr=subprocess.STDOUT
+    ...     stderr=subprocess.STDOUT, universal_newlines=True,
     ...     )
     >>> (output, empty) = process.communicate()
     >>> print(output)
@@ -117,7 +117,7 @@ The second one, executes the import.
     >>> process = subprocess.Popen([
     ...     sys.executable, 'cronscripts/rosetta-poimport.py'
     ...     ], stdin=subprocess.PIPE, stdout=subprocess.PIPE,
-    ...     stderr=subprocess.STDOUT
+    ...     stderr=subprocess.STDOUT, universal_newlines=True,
     ...     )
     >>> (output, empty) = process.communicate()
     >>> print(output)
diff --git a/lib/lp/translations/doc/translations-export-to-branch.txt b/lib/lp/translations/doc/translations-export-to-branch.txt
index 0ce521b..abdce05 100644
--- a/lib/lp/translations/doc/translations-export-to-branch.txt
+++ b/lib/lp/translations/doc/translations-export-to-branch.txt
@@ -10,8 +10,8 @@ to that branch.
     ...     'cronscripts/translations-export-to-branch.py', [])
     >>> ret_code
     0
-    >>> stdout
-    ''
+    >>> print(stdout)
+    <BLANKLINE>
     >>> print(stderr)
     INFO Creating lockfile:
     /var/lock/launchpad-translations-export-to-branch.lock
diff --git a/lib/lp/translations/utilities/tests/test_gettext_mo_exporter.py b/lib/lp/translations/utilities/tests/test_gettext_mo_exporter.py
index db6133a..b56970d 100644
--- a/lib/lp/translations/utilities/tests/test_gettext_mo_exporter.py
+++ b/lib/lp/translations/utilities/tests/test_gettext_mo_exporter.py
@@ -65,7 +65,8 @@ class TestGettextMOExporter(TestCaseWithFactory):
 
         # The file can even be converted back to PO format.
         retval, text, stderr = run_command(
-            '/usr/bin/msgunfmt', args=['-'], input=output.read())
+            '/usr/bin/msgunfmt', args=['-'], input=output.read(),
+            universal_newlines=False)
 
         self.assertEqual(0, retval)
         self.assertIn('MIME-Version', text)