← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] ~cjwatson/launchpad:py3-exception-repr into launchpad:master

 

Colin Watson has proposed merging ~cjwatson/launchpad:py3-exception-repr into launchpad:master.

Commit message:
Adjust for different rendering of several exceptions on Python 3

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

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

In a few cases it makes sense to adjust the exception message ('%s' instead of %r, or similar), but most of this is just a matter of tweaking tests so that they handle both the Python 2 and the Python 3 form.
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of ~cjwatson/launchpad:py3-exception-repr into launchpad:master.
diff --git a/lib/lp/app/doc/displaying-numbers.txt b/lib/lp/app/doc/displaying-numbers.txt
index 59ed96b..eb1069b 100644
--- a/lib/lp/app/doc/displaying-numbers.txt
+++ b/lib/lp/app/doc/displaying-numbers.txt
@@ -82,5 +82,5 @@ Here's a set of exhaustive examples:
     >>> test_tales('foo/fmt:float/bong', foo=12345.67890)
     Traceback (most recent call last):
     ...
-    ValueError: ... float...: bong
+    ValueError: ... float...bong...
 
diff --git a/lib/lp/app/stories/basics/xx-lowercase-redirection.txt b/lib/lp/app/stories/basics/xx-lowercase-redirection.txt
index 6dbee0c..394eda3 100644
--- a/lib/lp/app/stories/basics/xx-lowercase-redirection.txt
+++ b/lib/lp/app/stories/basics/xx-lowercase-redirection.txt
@@ -34,4 +34,4 @@ When doing a POST to an invalid URL, we get an error:
     ... """))
     HTTP/1.1 500 Internal Server Error
     ...
-    </ul><p>POSTToNonCanonicalURL...
+    </ul><p>...POSTToNonCanonicalURL...
diff --git a/lib/lp/app/widgets/doc/announcement-date-widget.txt b/lib/lp/app/widgets/doc/announcement-date-widget.txt
index 39e69dc..c6ac676 100644
--- a/lib/lp/app/widgets/doc/announcement-date-widget.txt
+++ b/lib/lp/app/widgets/doc/announcement-date-widget.txt
@@ -59,7 +59,7 @@ If you choose to publish immediately, the date field must be empty.
     ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    zope.formlib.interfaces.WidgetInputError: ('field.foo', u'Foo', LaunchpadValidationError(u'Please do not provide a date if you want to publish immediately.'))
+    zope.formlib.interfaces.WidgetInputError: ('field.foo', ...'Foo', LaunchpadValidationError(...'Please do not provide a date if you want to publish immediately.'))
 
 If you choose to publish at a specific date in the future, the date field
 must be filled.
@@ -70,4 +70,4 @@ must be filled.
     ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    zope.formlib.interfaces.WidgetInputError: ('field.foo', u'Foo', LaunchpadValidationError(u'Please provide a publication date.'))
+    zope.formlib.interfaces.WidgetInputError: ('field.foo', ...'Foo', LaunchpadValidationError(...'Please provide a publication date.'))
diff --git a/lib/lp/app/widgets/doc/image-widget.txt b/lib/lp/app/widgets/doc/image-widget.txt
index fb740cf..0d68f3b 100644
--- a/lib/lp/app/widgets/doc/image-widget.txt
+++ b/lib/lp/app/widgets/doc/image-widget.txt
@@ -269,7 +269,7 @@ dimensions smaller than person_mugshot.dimensions, it must be rejected.
     >>> widget.getInputValue()  # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    zope.formlib.interfaces.WidgetInputError: ('field.mugshot', u'Mugshot', LaunchpadValidationError(u'\nThis image is not exactly 192x192\npixels in size.'))
+    zope.formlib.interfaces.WidgetInputError: ('field.mugshot', ...'Mugshot', LaunchpadValidationError(...'\nThis image is not exactly 192x192\npixels in size.'))
 
 This is what we see when the image is the correct dimensions, and within
 the max_size:
@@ -313,7 +313,7 @@ image, we'll get a validation error.
     >>> widget.getInputValue()  # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    zope.formlib.interfaces.WidgetInputError: ('field.mugshot', u'Mugshot', LaunchpadValidationError(u'\nThis image exceeds the maximum allowed size in bytes.'))
+    zope.formlib.interfaces.WidgetInputError: ('field.mugshot', ...'Mugshot', LaunchpadValidationError(...'\nThis image exceeds the maximum allowed size in bytes.'))
 
 A similar error will be raised if the image's dimensions are bigger than
 the maximum we allow.
@@ -326,7 +326,7 @@ the maximum we allow.
     >>> widget.getInputValue()  # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    zope.formlib.interfaces.WidgetInputError: ('field.mugshot', u'Mugshot', LaunchpadValidationError(u'\nThis image is not exactly 191x193\npixels in size.'))
+    zope.formlib.interfaces.WidgetInputError: ('field.mugshot', ...'Mugshot', LaunchpadValidationError(...'\nThis image is not exactly 191x193\npixels in size.'))
 
     >>> person_mugshot.dimensions = (image.size[0] + 1, image.size[1] - 1)
     >>> _ = mugshot.seek(0)
@@ -335,7 +335,7 @@ the maximum we allow.
     >>> widget.getInputValue()  # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    zope.formlib.interfaces.WidgetInputError: ('field.mugshot', u'Mugshot', LaunchpadValidationError(u'\nThis image is not exactly 193x191\npixels in size.'))
+    zope.formlib.interfaces.WidgetInputError: ('field.mugshot', ...'Mugshot', LaunchpadValidationError(...'\nThis image is not exactly 193x191\npixels in size.'))
 
 Finally, if the user specifies the 'change' action they must also provide
 a file to be uploaded.
@@ -346,7 +346,7 @@ a file to be uploaded.
     >>> widget.getInputValue()  # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    zope.formlib.interfaces.WidgetInputError: ('field.mugshot', u'Mugshot', LaunchpadValidationError(u'Please specify the image you want to use.'))
+    zope.formlib.interfaces.WidgetInputError: ('field.mugshot', ...'Mugshot', LaunchpadValidationError(...'Please specify the image you want to use.'))
 
 
 Non-exact Image Dimensions
@@ -368,7 +368,7 @@ by setting the exact_dimensions attribute of the field to False:
     >>> widget.getInputValue()  # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    zope.formlib.interfaces.WidgetInputError: ('field.mugshot', u'Mugshot', LaunchpadValidationError(u'\nThis image is larger than 64x64\npixels in size.'))
+    zope.formlib.interfaces.WidgetInputError: ('field.mugshot', ...'Mugshot', LaunchpadValidationError(...'\nThis image is larger than 64x64\npixels in size.'))
 
 If the image is smaller than the dimensions, the input validates:
 
diff --git a/lib/lp/app/widgets/doc/project-scope-widget.txt b/lib/lp/app/widgets/doc/project-scope-widget.txt
index 4c3980f..8816009 100644
--- a/lib/lp/app/widgets/doc/project-scope-widget.txt
+++ b/lib/lp/app/widgets/doc/project-scope-widget.txt
@@ -120,7 +120,7 @@ raised:
     ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    zope.formlib.interfaces.WidgetInputError: ('field.scope', u'', LaunchpadValidationError(u'There is no project named &#x27;invalid&#x27; registered in Launchpad'))
+    zope.formlib.interfaces.WidgetInputError: ('field.scope', ...'', LaunchpadValidationError(...'There is no project named &#x27;invalid&#x27; registered in Launchpad'))
 
 The same error text is returned by error():
 
@@ -139,7 +139,7 @@ If no project name is given at all, a widget error is also raised:
     ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    zope.formlib.interfaces.WidgetInputError: ('field.scope', u'', LaunchpadValidationError(u'Please enter a project name'))
+    zope.formlib.interfaces.WidgetInputError: ('field.scope', ...'', LaunchpadValidationError(...'Please enter a project name'))
 
     >>> print(widget.error())
     Please enter a project name
@@ -154,7 +154,7 @@ If no project name is given at all, a widget error is also raised:
     ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    zope.formlib.interfaces.WidgetInputError: ('field.scope', u'', LaunchpadValidationError(u'Please enter a project name'))
+    zope.formlib.interfaces.WidgetInputError: ('field.scope', ...'', LaunchpadValidationError(...'Please enter a project name'))
 
     >>> print(widget.error())
     Please enter a project name
diff --git a/lib/lp/app/widgets/doc/stripped-text-widget.txt b/lib/lp/app/widgets/doc/stripped-text-widget.txt
index c98c6e2..354fe5f 100644
--- a/lib/lp/app/widgets/doc/stripped-text-widget.txt
+++ b/lib/lp/app/widgets/doc/stripped-text-widget.txt
@@ -63,4 +63,4 @@ provided.
   >>> widget.getInputValue()  # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
   Traceback (most recent call last):
   ...
-  zope.formlib.interfaces.WidgetInputError: ('field', u'Title', RequiredMissing('field'))
+  zope.formlib.interfaces.WidgetInputError: ('field', ...'Title', RequiredMissing('field'))
diff --git a/lib/lp/bugs/doc/bug.txt b/lib/lp/bugs/doc/bug.txt
index 56a21c0..4e5f41b 100644
--- a/lib/lp/bugs/doc/bug.txt
+++ b/lib/lp/bugs/doc/bug.txt
@@ -38,7 +38,7 @@ raised:
     ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
       ...
-    lp.app.errors.NotFoundError: u'Unable to locate bug with nickname +bugs.'
+    lp.app.errors.NotFoundError: ...'Unable to locate bug with nickname +bugs.'
 
 It is also possible to retrieve a number of bugs by specifying the bug numbers
 of interest.
diff --git a/lib/lp/bugs/doc/bugattachments.txt b/lib/lp/bugs/doc/bugattachments.txt
index af62209..3925204 100644
--- a/lib/lp/bugs/doc/bugattachments.txt
+++ b/lib/lp/bugs/doc/bugattachments.txt
@@ -669,4 +669,4 @@ does not match the file name of the Librarian file.
     ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    lp.app.errors.NotFoundError: u'nonsense'
+    lp.app.errors.NotFoundError: ...'nonsense'
diff --git a/lib/lp/bugs/doc/externalbugtracker-bugzilla-oddities.txt b/lib/lp/bugs/doc/externalbugtracker-bugzilla-oddities.txt
index 28f926b..b91e7ee 100644
--- a/lib/lp/bugs/doc/externalbugtracker-bugzilla-oddities.txt
+++ b/lib/lp/bugs/doc/externalbugtracker-bugzilla-oddities.txt
@@ -38,7 +38,7 @@ Mozilla's Bugzilla is an Issuezilla instance here:
     ...     bug_watch_updater.updateBugWatches(
     ...         issuezilla, mozilla_bugzilla.watches)
     INFO Updating 4 watches for 3 bugs on https://bugzilla.mozilla.org
-    INFO Didn't find bug u'42' on https://bugzilla.mozilla.org
+    INFO Didn't find bug '42' on https://bugzilla.mozilla.org
     (local bugs: 1, 2).
 
     >>> for bug_watch in mozilla_bugzilla.watches:
diff --git a/lib/lp/bugs/doc/externalbugtracker-bugzilla.txt b/lib/lp/bugs/doc/externalbugtracker-bugzilla.txt
index 71589c2..97065c4 100644
--- a/lib/lp/bugs/doc/externalbugtracker-bugzilla.txt
+++ b/lib/lp/bugs/doc/externalbugtracker-bugzilla.txt
@@ -496,7 +496,7 @@ update:
     ...     bug_watch_updater.updateBugWatches(
     ...         external_bugzilla, gnome_bugzilla.watches)
     INFO Updating 2 watches for 2 bugs on http://bugzilla.gnome.org/bugs
-    INFO Didn't find bug u'304070' on
+    INFO Didn't find bug '304070' on
     http://bugzilla.gnome.org/bugs (local bugs: 15).
 
     >>> for bug_watch in gnome_bugzilla.watches:
@@ -541,7 +541,7 @@ Then updateBugWatches() will make one request per bug watch:
     ...     bug_watch_updater.updateBugWatches(
     ...         external_bugzilla, gnome_bugzilla.watches)
     INFO Updating 7 watches for 7 bugs on http://bugzilla.gnome.org/bugs
-    INFO Didn't find bug u'304070' on
+    INFO Didn't find bug '304070' on
     http://bugzilla.gnome.org/bugs (local bugs: 15).
     POST http://bugzilla.gnome.org/bugs/buglist.cgi
     POST http://bugzilla.gnome.org/bugs/buglist.cgi
@@ -593,7 +593,7 @@ updateBugWatches() issues only one request to update all watches:
     ...     bug_watch_updater.updateBugWatches(
     ...         external_bugzilla, gnome_bugzilla.watches)
     INFO Updating 207 watches for 207 bugs...
-    INFO Didn't find bug u'304070' on
+    INFO Didn't find bug '304070' on
     http://bugzilla.gnome.org/bugs (local bugs: 15).
     POST http://bugzilla.gnome.org/bugs/buglist.cgi
 
diff --git a/lib/lp/bugs/doc/externalbugtracker-mantis.txt b/lib/lp/bugs/doc/externalbugtracker-mantis.txt
index 368cbb5..bfc1b0a 100644
--- a/lib/lp/bugs/doc/externalbugtracker-mantis.txt
+++ b/lib/lp/bugs/doc/externalbugtracker-mantis.txt
@@ -140,7 +140,7 @@ updateBugWatches() issues only one request to update all watches:
     ...     bug_watch_updater.updateBugWatches(
     ...         mantis, sorted(example_bug_tracker.watches, key=getid))
     INFO Updating 7 watches for 6 bugs on http://bugs.some.where
-    INFO Didn't find bug u'1798' on http://bugs.some.where
+    INFO Didn't find bug '1798' on http://bugs.some.where
     (local bugs: 10).
     GET http://bugs.some.where/view.php?id=1550
     GET http://bugs.some.where/view.php?id=1679
diff --git a/lib/lp/bugs/doc/externalbugtracker.txt b/lib/lp/bugs/doc/externalbugtracker.txt
index 6882a5d..2dceb45 100644
--- a/lib/lp/bugs/doc/externalbugtracker.txt
+++ b/lib/lp/bugs/doc/externalbugtracker.txt
@@ -845,7 +845,7 @@ to make that assumption.
     ...     )
     Traceback (most recent call last):
     ...
-    TypeError: Result is not a member of BugTaskStatus: u'Not a BugTaskStatus'
+    TypeError: Result is not a member of BugTaskStatus: ...'Not a BugTaskStatus'
 
 
 Getting the remote product from a remote bug
diff --git a/lib/lp/bugs/doc/treelookup.txt b/lib/lp/bugs/doc/treelookup.txt
index 46da657..e0aa092 100644
--- a/lib/lp/bugs/doc/treelookup.txt
+++ b/lib/lp/bugs/doc/treelookup.txt
@@ -89,7 +89,7 @@ instances), `LookupTree._verify` also checks that every branch is a
   >>> invalid_tree._verify()
   Traceback (most recent call last):
   ...
-  TypeError: Not a LookupBranch: u'Greenland'
+  TypeError: Not a LookupBranch: ...'Greenland'
 
 
 == Searching ==
@@ -110,7 +110,7 @@ But an exception is raised if it does not reach a leaf.
   >>> tree.find('Snack')
   Traceback (most recent call last):
   ...
-  KeyError: u'Snack'
+  KeyError: ...'Snack'
 
 
 == Development ==
diff --git a/lib/lp/bugs/scripts/checkwatches/remotebugupdater.py b/lib/lp/bugs/scripts/checkwatches/remotebugupdater.py
index b59c551..287039b 100644
--- a/lib/lp/bugs/scripts/checkwatches/remotebugupdater.py
+++ b/lib/lp/bugs/scripts/checkwatches/remotebugupdater.py
@@ -56,17 +56,17 @@ class RemoteBugUpdater(WorkingBase):
 
         self.error_type_messages = {
             BugWatchActivityStatus.INVALID_BUG_ID:
-                ("Invalid bug %(bug_id)r on %(base_url)s "
+                ("Invalid bug '%(bug_id)s' on %(base_url)s "
                  "(local bugs: %(local_ids)s)."),
             BugWatchActivityStatus.BUG_NOT_FOUND:
-                ("Didn't find bug %(bug_id)r on %(base_url)s "
+                ("Didn't find bug '%(bug_id)s' on %(base_url)s "
                  "(local bugs: %(local_ids)s)."),
             BugWatchActivityStatus.PRIVATE_REMOTE_BUG:
-                ("Remote bug %(bug_id)r on %(base_url)s is private "
+                ("Remote bug '%(bug_id)s' on %(base_url)s is private "
                  "(local bugs: %(local_ids)s)."),
             }
         self.error_type_message_default = (
-            "remote bug: %(bug_id)r; "
+            "remote bug: '%(bug_id)s'; "
             "base url: %(base_url)s; "
             "local bugs: %(local_ids)s"
             )
diff --git a/lib/lp/bugs/stories/bugs/xx-unique-ids-on-bug-page.txt b/lib/lp/bugs/stories/bugs/xx-unique-ids-on-bug-page.txt
index 960dca1..0fec67a 100644
--- a/lib/lp/bugs/stories/bugs/xx-unique-ids-on-bug-page.txt
+++ b/lib/lp/bugs/stories/bugs/xx-unique-ids-on-bug-page.txt
@@ -13,7 +13,7 @@ For example, bug one has more than one Package field.
     ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    zope.testbrowser.browser.AmbiguityError: label u'Package' matches: ...
+    zope.testbrowser.browser.AmbiguityError: label ...'Package' matches: ...
 
 Still, the ids of the fields are unique.
 
diff --git a/lib/lp/bugs/stories/bugtask-management/xx-change-assignee.txt b/lib/lp/bugs/stories/bugtask-management/xx-change-assignee.txt
index 6aef343..9ad1915 100644
--- a/lib/lp/bugs/stories/bugtask-management/xx-change-assignee.txt
+++ b/lib/lp/bugs/stories/bugtask-management/xx-change-assignee.txt
@@ -119,7 +119,7 @@ any team and hence does no see the option to asign somebody else.
     >>> assignee_control.value = ["jokosher.assignee.assign_to"]
     Traceback (most recent call last):
     ...
-    ValueError: Option u'jokosher.assignee.assign_to' not found ...
+    ValueError: Option ...'jokosher.assignee.assign_to' not found ...
     >>> user_browser.getControl(name="jokosher.assignee", index=0)
     Traceback (most recent call last):
     ...
diff --git a/lib/lp/bugs/stories/webservice/xx-bug.txt b/lib/lp/bugs/stories/webservice/xx-bug.txt
index 6191014..20e11c6 100644
--- a/lib/lp/bugs/stories/webservice/xx-bug.txt
+++ b/lib/lp/bugs/stories/webservice/xx-bug.txt
@@ -1887,7 +1887,7 @@ Pass isExpirable() a string for days_old.
     ...     days_old='sixty'))
     HTTP/1.1 400 Bad Request
     ...
-    days_old: got 'unicode', expected int: u'sixty'
+    days_old: got '...', expected int: ...'sixty'
 
 Can expire
 ----------
diff --git a/lib/lp/bugs/tests/bugzilla-xmlrpc-transport.txt b/lib/lp/bugs/tests/bugzilla-xmlrpc-transport.txt
index c035029..f7f7027 100644
--- a/lib/lp/bugs/tests/bugzilla-xmlrpc-transport.txt
+++ b/lib/lp/bugs/tests/bugzilla-xmlrpc-transport.txt
@@ -29,8 +29,7 @@ error will be raised.
     >>> server.spam.eggs()
     Traceback (most recent call last):
       ...
-    AttributeError: TestBugzillaXMLRPCTransport instance has no
-    attribute 'eggs'
+    AttributeError: ...TestBugzillaXMLRPCTransport... has no attribute 'eggs'
 
     >>> del bugzilla_transport.methods['spam']
 
diff --git a/lib/lp/bugs/tests/test_searchtasks_webservice.py b/lib/lp/bugs/tests/test_searchtasks_webservice.py
index 40e5b32..14d4be2 100644
--- a/lib/lp/bugs/tests/test_searchtasks_webservice.py
+++ b/lib/lp/bugs/tests/test_searchtasks_webservice.py
@@ -107,7 +107,7 @@ class TestProductSearchTasks(TestCaseWithFactory):
             api_version='devel', order_by='date_created')
         self.assertEqual(400, response.status)
         self.assertRaisesWithContent(
-            ValueError, "Unrecognized order_by: u'date_created'",
+            ValueError, "Unrecognized order_by: %r" % u'date_created',
             response.jsonBody)
 
     def test_search_incomplete_status_results(self):
diff --git a/lib/lp/buildmaster/model/buildfarmjobbehaviour.py b/lib/lp/buildmaster/model/buildfarmjobbehaviour.py
index 13a38d7..32913fd 100644
--- a/lib/lp/buildmaster/model/buildfarmjobbehaviour.py
+++ b/lib/lp/buildmaster/model/buildfarmjobbehaviour.py
@@ -321,7 +321,7 @@ class BuildFarmJobBehaviourBase:
             # subsequent files.
             if not os.path.realpath(out_file_name).startswith(upload_path):
                 raise BuildDaemonError(
-                    "Build returned a file named %r." % filename)
+                    "Build returned a file named '%s'." % filename)
             filenames_to_download.append((sha1, out_file_name))
         yield self._slave.getFiles(filenames_to_download, logger=logger)
 
diff --git a/lib/lp/buildmaster/tests/test_buildfarmjobbehaviour.py b/lib/lp/buildmaster/tests/test_buildfarmjobbehaviour.py
index 586c80b..baf8884 100644
--- a/lib/lp/buildmaster/tests/test_buildfarmjobbehaviour.py
+++ b/lib/lp/buildmaster/tests/test_buildfarmjobbehaviour.py
@@ -401,7 +401,7 @@ class TestHandleStatusMixin:
         # directory will not be collected.
         with ExpectedException(
                 BuildDaemonError,
-                "Build returned a file named u'/tmp/myfile.py'."):
+                "Build returned a file named '/tmp/myfile.py'."):
             with dbuser(config.builddmaster.dbuser):
                 yield self.behaviour.handleStatus(
                     self.build.buildqueue_record, 'OK',
@@ -413,7 +413,7 @@ class TestHandleStatusMixin:
         # the upload directory will not be collected.
         with ExpectedException(
                 BuildDaemonError,
-                "Build returned a file named u'../myfile.py'."):
+                "Build returned a file named '../myfile.py'."):
             with dbuser(config.builddmaster.dbuser):
                 yield self.behaviour.handleStatus(
                     self.build.buildqueue_record, 'OK',
@@ -474,7 +474,7 @@ class TestHandleStatusMixin:
             self.build.updateStatus(BuildStatus.BUILDING)
             with ExpectedException(
                     BuildDaemonError,
-                    "Build returned unexpected status: u'ABORTED'"):
+                    "Build returned unexpected status: %r" % 'ABORTED'):
                 yield self.behaviour.handleStatus(
                     self.build.buildqueue_record, "ABORTED", {})
 
@@ -502,7 +502,7 @@ class TestHandleStatusMixin:
     def test_givenback_collection(self):
         with ExpectedException(
                 BuildDaemonError,
-                "Build returned unexpected status: u'GIVENBACK'"):
+                "Build returned unexpected status: %r" % 'GIVENBACK'):
             with dbuser(config.builddmaster.dbuser):
                 yield self.behaviour.handleStatus(
                     self.build.buildqueue_record, "GIVENBACK", {})
@@ -511,7 +511,7 @@ class TestHandleStatusMixin:
     def test_builderfail_collection(self):
         with ExpectedException(
                 BuildDaemonError,
-                "Build returned unexpected status: u'BUILDERFAIL'"):
+                "Build returned unexpected status: %r" % 'BUILDERFAIL'):
             with dbuser(config.builddmaster.dbuser):
                 yield self.behaviour.handleStatus(
                     self.build.buildqueue_record, "BUILDERFAIL", {})
@@ -520,7 +520,7 @@ class TestHandleStatusMixin:
     def test_invalid_status_collection(self):
         with ExpectedException(
                 BuildDaemonError,
-                "Build returned unexpected status: u'BORKED'"):
+                "Build returned unexpected status: %r" % 'BORKED'):
             with dbuser(config.builddmaster.dbuser):
                 yield self.behaviour.handleStatus(
                     self.build.buildqueue_record, "BORKED", {})
diff --git a/lib/lp/buildmaster/tests/test_manager.py b/lib/lp/buildmaster/tests/test_manager.py
index 2b00f76..99b9e52 100644
--- a/lib/lp/buildmaster/tests/test_manager.py
+++ b/lib/lp/buildmaster/tests/test_manager.py
@@ -1008,8 +1008,8 @@ class TestSlaveScannerWithoutDB(TestCase):
 
         with ExpectedException(
                 BuildDaemonIsolationError,
-                r"Allegedly clean slave not idle "
-                r"\(u'BuilderStatus.BUILDING' instead\)"):
+                r"Allegedly clean slave not idle \(%r instead\)" %
+                'BuilderStatus.BUILDING'):
             yield scanner.scan()
         self.assertEqual(['status'], slave.call_log)
 
@@ -1170,7 +1170,7 @@ class TestCancellationChecking(TestCaseWithFactory):
         slave = LostBuildingBrokenSlave()
         self.builder.current_build.cancel()
         with ExpectedException(
-                xmlrpc_client.Fault, "<Fault 8002: u'Could not abort'>"):
+                xmlrpc_client.Fault, "<Fault 8002: %r>" % 'Could not abort'):
             yield self._getScanner().checkCancellation(self.vitals, slave)
 
 
diff --git a/lib/lp/code/model/tests/test_sourcepackagerecipebuild.py b/lib/lp/code/model/tests/test_sourcepackagerecipebuild.py
index d905c44..d7c8595 100644
--- a/lib/lp/code/model/tests/test_sourcepackagerecipebuild.py
+++ b/lib/lp/code/model/tests/test_sourcepackagerecipebuild.py
@@ -321,7 +321,7 @@ class TestSourcePackageRecipeBuild(TestCaseWithFactory):
         self.assertEqual(
             'DEBUG Recipe eric/funky-recipe is stale\n'
             'DEBUG  - daily build failed for Warty (4.10): ' +
-            "ArchiveDisabled(u'PPA for Eric is disabled.',)\n",
+            "ArchiveDisabled(%r,)\n" % 'PPA for Eric is disabled.',
             logger.getLogBuffer())
 
     def test_makeDailyBuilds_skips_archive_with_no_permission(self):
diff --git a/lib/lp/code/stories/branches/xx-branch-listings.txt b/lib/lp/code/stories/branches/xx-branch-listings.txt
index f0808a2..463fc8d 100644
--- a/lib/lp/code/stories/branches/xx-branch-listings.txt
+++ b/lib/lp/code/stories/branches/xx-branch-listings.txt
@@ -302,7 +302,7 @@ options that can be selected.
     >>> sort_by_control.value = ['by project name']
     Traceback (most recent call last):
       ...
-    ValueError: Option u'by project name' not found ...
+    ValueError: Option ...'by project name' not found ...
     >>> for option in sort_by_control.options:
     ...     print(option)
     by most interesting
diff --git a/lib/lp/oci/tests/test_ocirecipebuildbehaviour.py b/lib/lp/oci/tests/test_ocirecipebuildbehaviour.py
index f32476b..5cf9d08 100644
--- a/lib/lp/oci/tests/test_ocirecipebuildbehaviour.py
+++ b/lib/lp/oci/tests/test_ocirecipebuildbehaviour.py
@@ -884,7 +884,7 @@ class TestHandleStatusForOCIRecipeBuild(MakeOCIBuildMixin,
             self.build.updateStatus(BuildStatus.BUILDING)
             with ExpectedException(
                     BuildDaemonError,
-                    "Build returned unexpected status: u'ABORTED'"):
+                    "Build returned unexpected status: %r" % 'ABORTED'):
                 yield self.behaviour.handleStatus(
                     self.build.buildqueue_record, "ABORTED", {})
 
@@ -912,7 +912,7 @@ class TestHandleStatusForOCIRecipeBuild(MakeOCIBuildMixin,
     def test_givenback_collection(self):
         with ExpectedException(
                 BuildDaemonError,
-                "Build returned unexpected status: u'GIVENBACK'"):
+                "Build returned unexpected status: %r" % 'GIVENBACK'):
             with dbuser(config.builddmaster.dbuser):
                 yield self.behaviour.handleStatus(
                     self.build.buildqueue_record, "GIVENBACK", {})
@@ -921,7 +921,7 @@ class TestHandleStatusForOCIRecipeBuild(MakeOCIBuildMixin,
     def test_builderfail_collection(self):
         with ExpectedException(
                 BuildDaemonError,
-                "Build returned unexpected status: u'BUILDERFAIL'"):
+                "Build returned unexpected status: %r" % 'BUILDERFAIL'):
             with dbuser(config.builddmaster.dbuser):
                 yield self.behaviour.handleStatus(
                     self.build.buildqueue_record, "BUILDERFAIL", {})
@@ -930,7 +930,7 @@ class TestHandleStatusForOCIRecipeBuild(MakeOCIBuildMixin,
     def test_invalid_status_collection(self):
         with ExpectedException(
                 BuildDaemonError,
-                "Build returned unexpected status: u'BORKED'"):
+                "Build returned unexpected status: %r" % 'BORKED'):
             with dbuser(config.builddmaster.dbuser):
                 yield self.behaviour.handleStatus(
                     self.build.buildqueue_record, "BORKED", {})
diff --git a/lib/lp/registry/browser/tests/nameblacklist-views.txt b/lib/lp/registry/browser/tests/nameblacklist-views.txt
index 9f69f0b..82fbd5e 100644
--- a/lib/lp/registry/browser/tests/nameblacklist-views.txt
+++ b/lib/lp/registry/browser/tests/nameblacklist-views.txt
@@ -46,7 +46,7 @@ An invalid regular expression cannot be added.
     >>> view = create_initialized_view(name_blacklist_set, '+add', form=form)
     >>> for error in view.errors:
     ...     print(error)
-    Invalid regular expression: unbalanced parenthesis
+    Invalid regular expression: ...
 
 A duplicate regular expression cannot be added.
 
@@ -86,7 +86,7 @@ When a regular expression is edited, it still must be valid.
     >>> view = create_initialized_view(foo_exp, '+edit', form=form)
     >>> for error in view.errors:
     ...     print(error)
-    Invalid regular expression: unbalanced parenthesis
+    Invalid regular expression: ...
 
 It cannot changed to conflict with another regular expression.
 
diff --git a/lib/lp/registry/browser/tests/person-admin-views.txt b/lib/lp/registry/browser/tests/person-admin-views.txt
index 35a7a7b..8f996da 100644
--- a/lib/lp/registry/browser/tests/person-admin-views.txt
+++ b/lib/lp/registry/browser/tests/person-admin-views.txt
@@ -143,7 +143,8 @@ No one can force account status to an invalid transition:
     ...     }
     >>> view = create_initialized_view(user, '+reviewaccount', form=form)
     >>> [e.args[2] for e in view.errors]
-    [AccountStatusError(u'The status cannot change from Suspended to Active')]
+    [AccountStatusError(...'The status cannot change from Suspended to
+    Active')]
 
 
 An admin can deactivate a suspended user's account too. Unlike the act of
diff --git a/lib/lp/registry/browser/tests/project-add-views.txt b/lib/lp/registry/browser/tests/project-add-views.txt
index 36f1960..3db6cc3 100644
--- a/lib/lp/registry/browser/tests/project-add-views.txt
+++ b/lib/lp/registry/browser/tests/project-add-views.txt
@@ -182,4 +182,4 @@ that's checked for duplicates is the 'name' field.
     >>> for error in view.errors:
     ...     print(error)
     ('name', 'URL',
-     LaunchpadValidationError(u'snowdog is already used by another project'))
+     LaunchpadValidationError(...'snowdog is already used by another project'))
diff --git a/lib/lp/registry/doc/distribution.txt b/lib/lp/registry/doc/distribution.txt
index 22ed60f..c5553f8 100644
--- a/lib/lp/registry/doc/distribution.txt
+++ b/lib/lp/registry/doc/distribution.txt
@@ -592,7 +592,7 @@ Milestones for distros can only be created by distro owners or admins.
     ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    zope.security.interfaces.Unauthorized: (<DistroSeries u'woody'>, 'newMilestone', 'launchpad.Edit')
+    zope.security.interfaces.Unauthorized: (<DistroSeries ...'woody'>, 'newMilestone', 'launchpad.Edit')
     >>> login('mark@xxxxxxxxxxx')
     >>> debian_milestone = woody.newMilestone(
     ...     name='woody-rc1', dateexpected=datetime(2028, 10, 1))
diff --git a/lib/lp/registry/doc/pillar.txt b/lib/lp/registry/doc/pillar.txt
index 8beb155..4144e18 100644
--- a/lib/lp/registry/doc/pillar.txt
+++ b/lib/lp/registry/doc/pillar.txt
@@ -73,7 +73,7 @@ It also works if you use Unicode strings.
     ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    lp.app.errors.NotFoundError: u'launchpad'
+    lp.app.errors.NotFoundError: ...'launchpad'
     >>> IProduct.providedBy(pillar_set.getByName(u'launchpad'))
     True
 
diff --git a/lib/lp/services/gpg/doc/gpg-signatures.txt b/lib/lp/services/gpg/doc/gpg-signatures.txt
index 50c807b..ff52cb3 100644
--- a/lib/lp/services/gpg/doc/gpg-signatures.txt
+++ b/lib/lp/services/gpg/doc/gpg-signatures.txt
@@ -89,7 +89,7 @@ The text below was "clear signed" by 0xDFD20543 master key but tampered with:
     ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    lp.services.gpg.interfaces.GPGVerificationError: (7, 8, u'Bad signature')
+    lp.services.gpg.interfaces.GPGVerificationError: (7, 8, ...'Bad signature')
 
 If no signed content is found, an exception is raised:
 
@@ -229,7 +229,7 @@ itself.
     ...     print e.code
     ...     print e.signatures
     ...     print e.source
-    (7, 89, u'Bad data')
+    (7, 89, ...'Bad data')
     89
     [<gpgme.Signature object at ...>]
     7
diff --git a/lib/lp/services/mail/doc/emailauthentication.txt b/lib/lp/services/mail/doc/emailauthentication.txt
index 48427bb..0f9c5a7 100644
--- a/lib/lp/services/mail/doc/emailauthentication.txt
+++ b/lib/lp/services/mail/doc/emailauthentication.txt
@@ -126,7 +126,7 @@ message.
     ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    lp.services.gpg.interfaces.GPGVerificationError: (7, 8, u'Bad signature')
+    lp.services.gpg.interfaces.GPGVerificationError: (7, 8, ...'Bad signature')
 
     >>> getUtility(IGPGHandler).getVerifiedSignature(
     ...     msg.signedContent.replace(b'\n', b'\r\n'), msg.signature)
diff --git a/lib/lp/services/mail/tests/incomingmail.txt b/lib/lp/services/mail/tests/incomingmail.txt
index c42ef12..20ad713 100644
--- a/lib/lp/services/mail/tests/incomingmail.txt
+++ b/lib/lp/services/mail/tests/incomingmail.txt
@@ -367,7 +367,7 @@ out:
     ERROR:...:An exception was raised inside the handler: http://...
     Traceback (most recent call last):
     ...
-    DataError: division by zero
+    ...DataError: division by zero
     <BLANKLINE>
     WARNING...
 
diff --git a/lib/lp/services/messages/doc/message.txt b/lib/lp/services/messages/doc/message.txt
index 06d63e5..0ae44d0 100644
--- a/lib/lp/services/messages/doc/message.txt
+++ b/lib/lp/services/messages/doc/message.txt
@@ -392,7 +392,7 @@ explicitly told to do so:
     ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
         [...]
-    lp.services.messages.interfaces.message.UnknownSender: u'invalid@xxxxxxxxxxx'
+    lp.services.messages.interfaces.message.UnknownSender: ...'invalid@xxxxxxxxxxx'
 
     >>> msg = msgset.fromEmail(b'''\
     ... From: invalid@xxxxxxxxxxx
diff --git a/lib/lp/soyuz/browser/tests/archive-views.txt b/lib/lp/soyuz/browser/tests/archive-views.txt
index ed0598a..d217569 100644
--- a/lib/lp/soyuz/browser/tests/archive-views.txt
+++ b/lib/lp/soyuz/browser/tests/archive-views.txt
@@ -1344,7 +1344,7 @@ value is submitted.
     ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    zope.formlib.interfaces.WidgetInputError: ('destination_archive', u'Destination PPA', RequiredMissing('destination_archive'))
+    zope.formlib.interfaces.WidgetInputError: ('destination_archive', ...'Destination PPA', RequiredMissing('destination_archive'))
 
 
 Copy private files to public archives
diff --git a/lib/lp/soyuz/browser/tests/publishing-views.txt b/lib/lp/soyuz/browser/tests/publishing-views.txt
index 549d8ba..9e04d21 100644
--- a/lib/lp/soyuz/browser/tests/publishing-views.txt
+++ b/lib/lp/soyuz/browser/tests/publishing-views.txt
@@ -149,7 +149,7 @@ exception to be thrown.
     >>> print(BasePublishingRecordView.timestamp_map['key_not_there'])
     Traceback (most recent call last):
     ...
-    KeyError: u'key_not_there'
+    KeyError: ...'key_not_there'
 
 The view knows how to render a publication's phased update percentage.
 
diff --git a/lib/lp/soyuz/doc/build-files.txt b/lib/lp/soyuz/doc/build-files.txt
index 90e82e4..9db3eeb 100644
--- a/lib/lp/soyuz/doc/build-files.txt
+++ b/lib/lp/soyuz/doc/build-files.txt
@@ -38,7 +38,7 @@ Unsupported filename lookups also result in a `NotFoundError`.
     ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    lp.app.errors.NotFoundError: u'biscuit.cookie'
+    lp.app.errors.NotFoundError: ...'biscuit.cookie'
 
 And unreachable files in `NotFoundError`.
 
@@ -46,7 +46,7 @@ And unreachable files in `NotFoundError`.
     ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    lp.app.errors.NotFoundError: u'boing.changes'
+    lp.app.errors.NotFoundError: ...'boing.changes'
 
 Retrieving a binary changesfile.  "test_1.0_i386.changes" is created when
 SoyuzTestPublisher creates the "test" binary publication.
diff --git a/lib/lp/soyuz/stories/ppa/xx-ppa-files.txt b/lib/lp/soyuz/stories/ppa/xx-ppa-files.txt
index ebe6c7b..f4ad646 100644
--- a/lib/lp/soyuz/stories/ppa/xx-ppa-files.txt
+++ b/lib/lp/soyuz/stories/ppa/xx-ppa-files.txt
@@ -427,8 +427,7 @@ the error occurred based on the traceback included in the page.
     >>> main_content = find_main_content(str(not_found_file))
     >>> print(extract_text(main_content))
     Lost something?
-    ...
-    NotFound:
+    ...NotFound:
     Object: &lt;Archive at ...&gt;, name: 'test-pkg'...
     ...
 
diff --git a/lib/lp/soyuz/stories/soyuz/xx-builds-pages.txt b/lib/lp/soyuz/stories/soyuz/xx-builds-pages.txt
index 8f86dc6..7a0c950 100644
--- a/lib/lp/soyuz/stories/soyuz/xx-builds-pages.txt
+++ b/lib/lp/soyuz/stories/soyuz/xx-builds-pages.txt
@@ -270,7 +270,7 @@ to the form:
     >>> anon_browser.getControl(name="build_state").value = ['foo']
     Traceback (most recent call last):
     ...
-    ValueError: Option u'foo' not found ...
+    ValueError: Option ...'foo' not found ...
 
 However even if anonymous user builds an URL with a incorrect value,
 code is prepared to raise the correct exception:
@@ -292,7 +292,7 @@ form values are submitted:
     ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    lp.app.errors.UnexpectedFormData: No suitable state found for value "[u'building', u'all']"
+    lp.app.errors.UnexpectedFormData: No suitable state found for value "[...'building', ...'all']"
 
 
 Builder history
diff --git a/lib/lp/soyuz/stories/soyuz/xx-distribution-archives.txt b/lib/lp/soyuz/stories/soyuz/xx-distribution-archives.txt
index a9bb1f7..bac48fc 100644
--- a/lib/lp/soyuz/stories/soyuz/xx-distribution-archives.txt
+++ b/lib/lp/soyuz/stories/soyuz/xx-distribution-archives.txt
@@ -200,7 +200,7 @@ it and the 'disabled' styling is gone.
     >>> main_content = find_main_content(nopriv_browser.contents)
     >>> print(main_content.h1['class'])
     Traceback (most recent call last):
-    KeyError: u'class'
+    KeyError: ...'class'
 
     >>> print(first_tag_by_class(nopriv_browser.contents, 'warning message'))
     None
diff --git a/lib/lp/translations/doc/potranslation.txt b/lib/lp/translations/doc/potranslation.txt
index 6851646..be0a5ff 100644
--- a/lib/lp/translations/doc/potranslation.txt
+++ b/lib/lp/translations/doc/potranslation.txt
@@ -41,4 +41,4 @@ either UTF-8 string or, better, a unicode object.
     >>> got = POTranslation.getOrCreateTranslation(b'\xc0')
     Traceback (most recent call last):
     ...
-    UnicodeDecodeError: 'utf8' codec can't decode byte 0xc0 in position...
+    UnicodeDecodeError: 'utf...8' codec can't decode byte 0xc0 in position...