← Back to team overview

launchpad-reviewers team mailing list archive

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

 

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

Commit message:
lp.translations: Use IGNORE_EXCEPTION_MODULE_IN_PYTHON2

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

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

This allows doctests that test tracebacks to work on both Python 2 and 3.
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of ~cjwatson/launchpad:py3-translations-exception-modules into launchpad:master.
diff --git a/lib/lp/translations/browser/tests/pofile-views.txt b/lib/lp/translations/browser/tests/pofile-views.txt
index 5b84b54..f1efd7e 100644
--- a/lib/lp/translations/browser/tests/pofile-views.txt
+++ b/lib/lp/translations/browser/tests/pofile-views.txt
@@ -441,24 +441,27 @@ The traversal value should be an integer.
 
     >>> request.method = 'GET'
     >>> navigation.traverse('foo')
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    NotFoundError:...
+    lp.app.errors.NotFoundError: ...
 
 Also, translation message sequence numbers are always >= 1.
 
     >>> navigation.traverse('0')
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    NotFoundError:...
+    lp.app.errors.NotFoundError: ...
 
 The given sequence number, we also need that is part of the available ones,
 if we use a high one, we should detect it.
 
     >>> navigation.traverse('30')
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    NotFoundError:...
+    lp.app.errors.NotFoundError: ...
 
 But if we have a right sequence number, we will get a valid translation
 message.
diff --git a/lib/lp/translations/doc/browser-helpers.txt b/lib/lp/translations/doc/browser-helpers.txt
index abbdcc2..e7d4c8c 100644
--- a/lib/lp/translations/doc/browser-helpers.txt
+++ b/lib/lp/translations/doc/browser-helpers.txt
@@ -136,9 +136,10 @@ parse_cformat_string
     [('interpolation', u'%s'), ('string', u'foo%%bar'),
      ('interpolation', u'%s')]
     >>> parse_cformat_string('%')
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    UnrecognisedCFormatString: %
+    lp.translations.browser.browser_helpers.UnrecognisedCFormatString: %
 
 
 text_to_html
@@ -190,9 +191,10 @@ Test bad format strings are caught and passed through.
 
     >>> text = u'foo %z bar'
     >>> parse_cformat_string(text)
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    UnrecognisedCFormatString: foo %z bar
+    lp.translations.browser.browser_helpers.UnrecognisedCFormatString: foo %z bar
 
     >>> text_to_html(text, ['c-format']) == text
     True
diff --git a/lib/lp/translations/doc/potranslation.txt b/lib/lp/translations/doc/potranslation.txt
index 8fc1e94..33ae87b 100644
--- a/lib/lp/translations/doc/potranslation.txt
+++ b/lib/lp/translations/doc/potranslation.txt
@@ -20,9 +20,10 @@ To get hold of a PO translation, use POTranslation.getByTranslation.
 However, if the translation doesn't already exist, you'll get an error.
 
     >>> got = POTranslation.getByTranslation("In Xanadu did Kubla Khan")
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    NotFoundError: 'In Xanadu did Kubla Khan'
+    lp.app.errors.NotFoundError: 'In Xanadu did Kubla Khan'
 
 If you want to get hold of one, and have it automatically created if it
 doesn't already exist, use POTranslation.getOrCreateTranslation.
diff --git a/lib/lp/translations/doc/translationmessage-destroy.txt b/lib/lp/translations/doc/translationmessage-destroy.txt
index 6709859..e32a113 100644
--- a/lib/lp/translations/doc/translationmessage-destroy.txt
+++ b/lib/lp/translations/doc/translationmessage-destroy.txt
@@ -22,9 +22,10 @@ Select an existing ITranslationMessage and try to remove it.
 It should not exist now.
 
     >>> translationmessage = TranslationMessage.get(1)
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    SQLObjectNotFound:...
+    storm.sqlobject.SQLObjectNotFound: ...
 
 
 POFileTranslator update on remove
diff --git a/lib/lp/translations/stories/distribution/xx-distribution-change-language-pack-admins.txt b/lib/lp/translations/stories/distribution/xx-distribution-change-language-pack-admins.txt
index 45be5eb..1871bae 100644
--- a/lib/lp/translations/stories/distribution/xx-distribution-change-language-pack-admins.txt
+++ b/lib/lp/translations/stories/distribution/xx-distribution-change-language-pack-admins.txt
@@ -8,15 +8,17 @@ owner, on the +select-language-pack-admin page.
     >>> browser = setupBrowser(auth='Basic test@xxxxxxxxxxxxx:test')
     >>> browser.open('http://translations.launchpad.test/ubuntu/')
     >>> browser.getLink('Set language pack administrator')
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    LinkNotFoundError
+    zope.testbrowser.browser.LinkNotFoundError
     >>> browser.open(
     ...     'http://translations.launchpad.test/ubuntu/'
     ...     '+select-language-pack-admin')
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    Unauthorized: ...
+    zope.security.interfaces.Unauthorized: ...
 
 Mark is one of the distribution owners, so he's able to reach that page.
 
diff --git a/lib/lp/translations/stories/distribution/xx-distribution-translations.txt b/lib/lp/translations/stories/distribution/xx-distribution-translations.txt
index d5e30aa..5eb3ffa 100644
--- a/lib/lp/translations/stories/distribution/xx-distribution-translations.txt
+++ b/lib/lp/translations/stories/distribution/xx-distribution-translations.txt
@@ -80,9 +80,10 @@ But we are already showing the status for the translation focus one,
 we should not have a link to it.
 
     >>> browser.getLink('5.04 The Hoary Hedgehog Release')
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    LinkNotFoundError
+    zope.testbrowser.browser.LinkNotFoundError
 
 Let's try now a distribution that lacks a translation focus. Debian is
 a good example, if enable translations for it.
@@ -130,9 +131,10 @@ But we are already showing the status for the translation focus one,
 we should not have a link to it.
 
     >>> browser.getLink('3.1 Sarge')
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    LinkNotFoundError
+    zope.testbrowser.browser.LinkNotFoundError
 
 Administrator can change the translation focus for a distribution.
 
diff --git a/lib/lp/translations/stories/distroseries/xx-distroseries-translations.txt b/lib/lp/translations/stories/distroseries/xx-distroseries-translations.txt
index 9421546..bb3e509 100644
--- a/lib/lp/translations/stories/distroseries/xx-distroseries-translations.txt
+++ b/lib/lp/translations/stories/distroseries/xx-distroseries-translations.txt
@@ -11,16 +11,18 @@ In this case, we're asking for the translation overview for Hoary.
 The system is not showing non visible languages:
 
     >>> anon_browser.getLink('Spanish (Spain)')
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    LinkNotFoundError
+    zope.testbrowser.browser.LinkNotFoundError
 
 The system will not show English because it is not translatable:
 
     >>> anon_browser.getLink('English')
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    LinkNotFoundError
+    zope.testbrowser.browser.LinkNotFoundError
 
 But it shows the ones not hidden:
 
@@ -31,18 +33,20 @@ Launchpad has an option to hide all of the translations for a distribution
 series.  The link to hide translations is not available to anonymous users:
 
     >>> anon_browser.getLink('Change settings')
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    LinkNotFoundError
+    zope.testbrowser.browser.LinkNotFoundError
 
 And the page is not available either:
 
     >>> anon_browser.open(
     ...     'http://translations.launchpad.test/ubuntu/hoary/'
     ...     '+translations-admin')
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    Unauthorized:...
+    zope.security.interfaces.Unauthorized: ...
 
 ... but the link is available to administrators:
 
@@ -70,9 +74,10 @@ Now, the translation status page will no longer display any languages to
 regular users.
 
     >>> user_browser.open('http://translations.launchpad.test/ubuntu/hoary')
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    TranslationUnavailable: ...
+    lp.app.errors.TranslationUnavailable: ...
 
 Also, if the user tries to navigate directly to launchpad pages,
 the system tells them that they're not allowed to see those pages.
@@ -80,9 +85,10 @@ the system tells them that they're not allowed to see those pages.
     >>> user_browser.handleErrors = True
     >>> user_browser.open(
     ...     'http://translations.launchpad.test/ubuntu/hoary/+lang/es')
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    HTTPError: HTTP Error 503: Service Unavailable
+    urllib.error.HTTPError: HTTP Error 503: Service Unavailable
     >>> main_content = find_main_content(user_browser.contents)
     >>> print(main_content.findNext('p').decode_contents())
     Translations for this release series are not available yet.
@@ -100,15 +106,17 @@ should not viewed
 
     >>> user_browser.open(
     ...     'http://translations.launchpad.test/ubuntu/hoary/+lang/notexists')
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    NotFound: ...
+    zope.publisher.interfaces.NotFound: ...
 
     >>> user_browser.open(
     ...     'http://translations.launchpad.test/ubuntu/hoary/+lang/en')
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    NotFound: ...
+    zope.publisher.interfaces.NotFound: ...
 
 Translation pages for source packages are also unavailable to
 non-administrative users.
@@ -116,9 +124,10 @@ non-administrative users.
     >>> user_browser.open(
     ...     'http://translations.launchpad.test/ubuntu/hoary/'
     ...     '+sources/evolution/+pots/evolution-2.2')
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    TranslationUnavailable: ...
+    lp.app.errors.TranslationUnavailable: ...
 
 However, source package translations are still available to the
 administrators.
diff --git a/lib/lp/translations/stories/importqueue/xx-translation-import-queue-filtering.txt b/lib/lp/translations/stories/importqueue/xx-translation-import-queue-filtering.txt
index 6d500ed..2f20ce5 100644
--- a/lib/lp/translations/stories/importqueue/xx-translation-import-queue-filtering.txt
+++ b/lib/lp/translations/stories/importqueue/xx-translation-import-queue-filtering.txt
@@ -256,9 +256,10 @@ An attempt to filter for an undefined status is an UnexpectedFormData.
     >>> browser.open(
     ...     'http://translations.launchpad.test/+imports?'
     ...     'field.filter_status=boguscode')
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    UnexpectedFormData: Invalid status parameter.
+    lp.app.errors.UnexpectedFormData: Invalid status parameter.
 
 
 == Target changes ==
@@ -390,6 +391,7 @@ mistyped URL, an UnexpectedFormData is raised.
     >>> browser.open(
     ...     'http://translations.launchpad.test/+imports?'
     ...     'field.filter_target=bogus/target')
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    UnexpectedFormData: Unknown target.
+    lp.app.errors.UnexpectedFormData: Unknown target.
diff --git a/lib/lp/translations/stories/productseries/xx-productseries-export-to-branch.txt b/lib/lp/translations/stories/productseries/xx-productseries-export-to-branch.txt
index c7e2a65..3473b45 100644
--- a/lib/lp/translations/stories/productseries/xx-productseries-export-to-branch.txt
+++ b/lib/lp/translations/stories/productseries/xx-productseries-export-to-branch.txt
@@ -148,6 +148,7 @@ And of course, setting the translations branch requires edit privileges
 on the release series.
 
     >>> user_browser.open(link_page)
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    Unauthorized: ...
+    zope.security.interfaces.Unauthorized: ...
diff --git a/lib/lp/translations/stories/productseries/xx-productseries-translation-export.txt b/lib/lp/translations/stories/productseries/xx-productseries-translation-export.txt
index cdcc9d3..87e1f3e 100644
--- a/lib/lp/translations/stories/productseries/xx-productseries-translation-export.txt
+++ b/lib/lp/translations/stories/productseries/xx-productseries-translation-export.txt
@@ -34,9 +34,10 @@ not available to anonymous visitors.
 
     >>> anon_browser.open('http://translations.launchpad.test/evolution/trunk/')
     >>> anon_browser.getLink('download')
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    LinkNotFoundError
+    zope.testbrowser.browser.LinkNotFoundError
 
 
 == Making the request ==
@@ -95,6 +96,7 @@ link for "Download translations" is hidden.
 
     >>> user_browser.open('http://translations.launchpad.test/bzr/')
     >>> user_browser.getLink('download')
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    LinkNotFoundError
+    zope.testbrowser.browser.LinkNotFoundError
diff --git a/lib/lp/translations/stories/project/xx-project-translations.txt b/lib/lp/translations/stories/project/xx-project-translations.txt
index 7ad1bb3..a149e91 100644
--- a/lib/lp/translations/stories/project/xx-project-translations.txt
+++ b/lib/lp/translations/stories/project/xx-project-translations.txt
@@ -59,9 +59,10 @@ shouldn't appear in GNOME project translations page.
 
     >>> browser.open('http://translations.launchpad.test/gnome')
     >>> browser.getLink('alsa-utils')
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    LinkNotFoundError
+    zope.testbrowser.browser.LinkNotFoundError
 
 Let's confirm what we just stated.
 
@@ -72,9 +73,10 @@ Let's confirm what we just stated.
 alsa-utils does not belong to GNOME project.
 
     >>> browser.getLink('alsa-utils')
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    LinkNotFoundError
+    zope.testbrowser.browser.LinkNotFoundError
 
 It's using Launchpad Translations officially. And it has translations.
 
diff --git a/lib/lp/translations/stories/standalone/custom-language-codes.txt b/lib/lp/translations/stories/standalone/custom-language-codes.txt
index 34f104d..da03bda 100644
--- a/lib/lp/translations/stories/standalone/custom-language-codes.txt
+++ b/lib/lp/translations/stories/standalone/custom-language-codes.txt
@@ -149,16 +149,18 @@ This can be convenient for debugging.
 However all they get is a read-only version of the page.
 
     >>> user_browser.getLink("Add a custom language code").click()
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    LinkNotFoundError
+    zope.testbrowser.browser.LinkNotFoundError
 
 The page for adding custom language codes is not accessible to them.
 
     >>> user_browser.open(add_page)
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    Unauthorized: ...
+    zope.security.interfaces.Unauthorized: ...
 
 And naturally, if the owner creates a custom language code again, an
 unprivileged user can't remove it.
@@ -177,14 +179,16 @@ unprivileged user can't remove it.
 
     >>> user_browser.getLink("no").click()
     >>> user_browser.getLink("remove custom language code")
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    LinkNotFoundError
+    zope.testbrowser.browser.LinkNotFoundError
 
     >>> user_browser.open(remove_page)
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    Unauthorized: ...
+    zope.security.interfaces.Unauthorized: ...
 
 
 Source packages
diff --git a/lib/lp/translations/stories/standalone/xx-language.txt b/lib/lp/translations/stories/standalone/xx-language.txt
index 97cb4f0..63b80d7 100644
--- a/lib/lp/translations/stories/standalone/xx-language.txt
+++ b/lib/lp/translations/stories/standalone/xx-language.txt
@@ -55,9 +55,10 @@ the system detects it and warns the user.
 But, with a new language, it will succeed.
 
     >>> browser.open('http://translations.launchpad.test/+languages/foos')
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    NotFound:...
+    zope.publisher.interfaces.NotFound: ...
 
     >>> admin_browser.getControl('The ISO 639').value = 'foos'
     >>> admin_browser.getControl('English name').value = 'Foos'
@@ -75,15 +76,17 @@ A normal user will not be able to see or use the url to add languages.
     http://translations.launchpad.test/+languages
 
     >>> user_browser.getLink('Add new language')
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    LinkNotFoundError
+    zope.testbrowser.browser.LinkNotFoundError
 
     >>> user_browser.open(
     ...     'http://translations.launchpad.test/+languages/+add')
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    Unauthorized:...
+    zope.security.interfaces.Unauthorized: ...
 
 
 Searching for a language
@@ -201,15 +204,17 @@ Finally, there is the edit form to change language basic information.
 A plain user is not able to reach it.
 
     >>> user_browser.getLink('Administer')
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    LinkNotFoundError
+    zope.testbrowser.browser.LinkNotFoundError
 
     >>> user_browser.open(
     ...     'http://translations.launchpad.test/+languages/es/+admin')
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    Unauthorized:...
+    zope.security.interfaces.Unauthorized: ...
 
 An admin, though, will see the link and will be able to edit it.
 
@@ -304,6 +309,7 @@ That was a renaming action, which means that language code 'es' doesn't
 exist anymore.
 
     >>> browser.open('http://translations.launchpad.test/+languages/es')
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    NotFound:...
+    zope.publisher.interfaces.NotFound: ...
diff --git a/lib/lp/translations/stories/standalone/xx-licensing.txt b/lib/lp/translations/stories/standalone/xx-licensing.txt
index a69a74b..669f49c 100644
--- a/lib/lp/translations/stories/standalone/xx-licensing.txt
+++ b/lib/lp/translations/stories/standalone/xx-licensing.txt
@@ -97,14 +97,16 @@ no link to change Karl's licensing choice.
 
     >>> user_browser.open('http://translations.launchpad.test/~karl/')
     >>> user_browser.getLink('Translations licensing')
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    LinkNotFoundError
+    zope.testbrowser.browser.LinkNotFoundError
 
 Typing in the URL directly doesn't work either.
 
     >>> user_browser.open(
     ...     'http://translations.launchpad.test/~karl/+licensing')
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    Unauthorized: ...
+    zope.security.interfaces.Unauthorized: ...
diff --git a/lib/lp/translations/stories/standalone/xx-person-editlanguages.txt b/lib/lp/translations/stories/standalone/xx-person-editlanguages.txt
index 022536d..94c911f 100644
--- a/lib/lp/translations/stories/standalone/xx-person-editlanguages.txt
+++ b/lib/lp/translations/stories/standalone/xx-person-editlanguages.txt
@@ -128,9 +128,10 @@ The launchpad.AnyPerson permission means that when an anonymous user goes
 to that page, they'll be asked to login.
 
     >>> anon_browser.open('http://launchpad.test/+editmylanguages')
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    Unauthorized: ...
+    zope.security.interfaces.Unauthorized: ...
 
 But a logged in user will be sent straight to their /~user/+editlanguages
 page.
diff --git a/lib/lp/translations/stories/standalone/xx-pofile-export.txt b/lib/lp/translations/stories/standalone/xx-pofile-export.txt
index 7aa1661..364c90d 100644
--- a/lib/lp/translations/stories/standalone/xx-pofile-export.txt
+++ b/lib/lp/translations/stories/standalone/xx-pofile-export.txt
@@ -7,9 +7,10 @@ Not logged in users can't access the +export page.
     ...     'http://translations.launchpad.test/ubuntu/hoary'
     ...     '/+source/evolution/+pots/evolution-2.2/es/')
     >>> anon_browser.getLink('Download').click()
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    Unauthorized:...
+    zope.security.interfaces.Unauthorized: ...
 
 Logged in as a regular user, the +export page is accessible.
 
diff --git a/lib/lp/translations/stories/standalone/xx-pofile-translate-alternative-language.txt b/lib/lp/translations/stories/standalone/xx-pofile-translate-alternative-language.txt
index c2c1224..cfb9b5b 100644
--- a/lib/lp/translations/stories/standalone/xx-pofile-translate-alternative-language.txt
+++ b/lib/lp/translations/stories/standalone/xx-pofile-translate-alternative-language.txt
@@ -242,9 +242,10 @@ get an UnexpectedFormData exception:
     ...     'http://translations.launchpad.test/ubuntu/hoary/+source/'
     ...     'evolution/+pots/evolution-2.2/es/+translate'
     ...     '?field.alternative_language=ja&field.alternative_language=aj')
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    UnexpectedFormData: You specified...
+    lp.app.errors.UnexpectedFormData: You specified...
 
 
 Requests for a non-translatable alternative language
diff --git a/lib/lp/translations/stories/standalone/xx-pofile-translate.txt b/lib/lp/translations/stories/standalone/xx-pofile-translate.txt
index ccb3921..a7876ee 100644
--- a/lib/lp/translations/stories/standalone/xx-pofile-translate.txt
+++ b/lib/lp/translations/stories/standalone/xx-pofile-translate.txt
@@ -115,9 +115,10 @@ in the past.
     >>> browser.open(
     ...     'http://translations.launchpad.test/ubuntu/hoary/+source/'
     ...     'evolution/+pots/evolution-2.2/en/+translate')
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    NotFound: Object: ... name: u'en'
+    zope.publisher.interfaces.NotFound: Object: ... name: u'en'
 
 See xx-pofile-translate-alternative-language.txt for details about
 the 'make suggestions from' feature.
diff --git a/lib/lp/translations/stories/standalone/xx-potemplate-admin.txt b/lib/lp/translations/stories/standalone/xx-potemplate-admin.txt
index 59a9600..6b831d8 100644
--- a/lib/lp/translations/stories/standalone/xx-potemplate-admin.txt
+++ b/lib/lp/translations/stories/standalone/xx-potemplate-admin.txt
@@ -13,9 +13,10 @@ An unprivileged user cannot reach the POTemplate administration page.
     >>> user_browser.open(
     ...     'http://translations.launchpad.test/evolution/trunk/+pots/'
     ...     'evolution-2.2/+admin')
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    Unauthorized:...
+    zope.security.interfaces.Unauthorized: ...
 
 Jordi, a Rosetta expert, can.
 
diff --git a/lib/lp/translations/stories/standalone/xx-potemplate-edit.txt b/lib/lp/translations/stories/standalone/xx-potemplate-edit.txt
index d46ac09..be44260 100644
--- a/lib/lp/translations/stories/standalone/xx-potemplate-edit.txt
+++ b/lib/lp/translations/stories/standalone/xx-potemplate-edit.txt
@@ -11,9 +11,10 @@ An unpriviledged user cannot reach this page.
     >>> browser.open(
     ...     'http://translations.launchpad.test/evolution/trunk/+pots/'
     ...     'evolution-2.2/+edit')
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    Unauthorized:...
+    zope.security.interfaces.Unauthorized: ...
 
 In fact, the "Change details" option won't even appear for them.
 
@@ -21,9 +22,10 @@ In fact, the "Change details" option won't even appear for them.
     ...     'http://translations.launchpad.test/evolution/trunk/+pots/'
     ...     'evolution-2.2/')
     >>> browser.getLink('Change details').click()
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    LinkNotFoundError
+    zope.testbrowser.browser.LinkNotFoundError
 
 On the other hand, Rosetta expert (Jordi) can reach the POTemplate edit
 page.
diff --git a/lib/lp/translations/stories/standalone/xx-potemplate-export.txt b/lib/lp/translations/stories/standalone/xx-potemplate-export.txt
index 9f42e51..475a4c3 100644
--- a/lib/lp/translations/stories/standalone/xx-potemplate-export.txt
+++ b/lib/lp/translations/stories/standalone/xx-potemplate-export.txt
@@ -7,9 +7,10 @@ Not logged in users can't access the +export page.
   ...     'http://translations.launchpad.test/ubuntu/hoary'
   ...     '/+source/evolution/+pots/evolution-2.2/')
   >>> anon_browser.getLink('download').click()
+  ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
   Traceback (most recent call last):
   ...
-  LinkNotFoundError
+  zope.testbrowser.browser.LinkNotFoundError
 
 Logged in as a regular user, the +export page is accessible.
 
diff --git a/lib/lp/translations/stories/standalone/xx-potemplate-index.txt b/lib/lp/translations/stories/standalone/xx-potemplate-index.txt
index 8bf48de..a471655 100644
--- a/lib/lp/translations/stories/standalone/xx-potemplate-index.txt
+++ b/lib/lp/translations/stories/standalone/xx-potemplate-index.txt
@@ -282,14 +282,16 @@ administration or download/upload links.
     ...     'http://translations.launchpad.test/'
     ...     'ubuntu/hoary/+source/evolution/+pots/evolution-2.2')
     >>> anon_browser.getLink('upload')
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    LinkNotFoundError
+    zope.testbrowser.browser.LinkNotFoundError
 
     >>> anon_browser.getLink('download').click()
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    LinkNotFoundError
+    zope.testbrowser.browser.LinkNotFoundError
 
 As an authenticated user, you should see the download link,
 but not the one for uploading file to this potemplate.
@@ -298,9 +300,10 @@ but not the one for uploading file to this potemplate.
     ...     'http://translations.launchpad.test/'
     ...     'ubuntu/hoary/+source/evolution/+pots/evolution-2.2')
     >>> user_browser.getLink('upload')
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    LinkNotFoundError
+    zope.testbrowser.browser.LinkNotFoundError
 
     >>> user_browser.getLink('download').click()
     >>> print(user_browser.url)
diff --git a/lib/lp/translations/stories/standalone/xx-product-export.txt b/lib/lp/translations/stories/standalone/xx-product-export.txt
index 9291933..bad2d2a 100644
--- a/lib/lp/translations/stories/standalone/xx-product-export.txt
+++ b/lib/lp/translations/stories/standalone/xx-product-export.txt
@@ -48,9 +48,10 @@ Translations.
     >>> product.sync()
     >>> user_browser.open('http://translations.launchpad.test/evolution')
     >>> user_browser.getLink('download')
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    LinkNotFoundError
+    zope.testbrowser.browser.LinkNotFoundError
 
     >>> # Restore previous state for subsequent tests, and verify
     >>> product.translations_usage = ServiceUsage.LAUNCHPAD
@@ -67,9 +68,10 @@ Only logged-in users get the option to request downloads.
 
     >>> anon_browser.open('http://translations.launchpad.test/evolution/')
     >>> anon_browser.getLink('download').click()
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    LinkNotFoundError
+    zope.testbrowser.browser.LinkNotFoundError
 
 We can't see its placeholder in non-development mode:
 
@@ -88,6 +90,7 @@ We can't see its placeholder in non-development mode:
 Even "hacking the URL" to the download option will fail.
 
     >>> anon_browser.open(download_url)
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    Unauthorized: ...
+    zope.security.interfaces.Unauthorized: ...
diff --git a/lib/lp/translations/stories/standalone/xx-product-translations.txt b/lib/lp/translations/stories/standalone/xx-product-translations.txt
index c7b184d..6ae0603 100644
--- a/lib/lp/translations/stories/standalone/xx-product-translations.txt
+++ b/lib/lp/translations/stories/standalone/xx-product-translations.txt
@@ -85,22 +85,25 @@ are not being used, and provides access to help.
 It omits the registrant-only links ...
 
     >>> unprivileged.getLink(url='/gnomebaker/trunk/+translations-upload')
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    LinkNotFoundError
+    zope.testbrowser.browser.LinkNotFoundError
     >>> unprivileged.getLink(url='/gnomebaker/+packages')
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    LinkNotFoundError
+    zope.testbrowser.browser.LinkNotFoundError
 
 ... because you can't do those things.
 
     >>> unprivileged.open(
     ...     'http://translations.launchpad.test/gnomebaker/trunk/'
     ...     '+translations-upload')
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    Unauthorized:...
+    zope.security.interfaces.Unauthorized: ...
 
 
 If you're not logged in at all, you aren't shown the registrant
diff --git a/lib/lp/translations/stories/standalone/xx-series-templates.txt b/lib/lp/translations/stories/standalone/xx-series-templates.txt
index 882ecff..c2a7bcc 100644
--- a/lib/lp/translations/stories/standalone/xx-series-templates.txt
+++ b/lib/lp/translations/stories/standalone/xx-series-templates.txt
@@ -96,9 +96,10 @@ Administration page is inaccessible.
     ...     'http://translations.launchpad.test/ubuntu/hoary/+templates')
     >>> utc_browser.getLink(
     ...     url='+source/evolution/+pots/evolution-2.2/+admin')
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    LinkNotFoundError
+    zope.testbrowser.browser.LinkNotFoundError
 
 Trying to edit disabled templates brings them to the appropriate page.
 
@@ -115,9 +116,10 @@ Administration page is inaccessible.
     ...     'http://translations.launchpad.test/ubuntu/hoary/+templates')
     >>> utc_browser.getLink(
     ...     url='+source/evolution/+pots/disabled-template/+admin')
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    LinkNotFoundError
+    zope.testbrowser.browser.LinkNotFoundError
 
 
 Links to the templates
diff --git a/lib/lp/translations/stories/standalone/xx-translationmessage-translate.txt b/lib/lp/translations/stories/standalone/xx-translationmessage-translate.txt
index 34ff287..3c6b058 100644
--- a/lib/lp/translations/stories/standalone/xx-translationmessage-translate.txt
+++ b/lib/lp/translations/stories/standalone/xx-translationmessage-translate.txt
@@ -76,14 +76,16 @@ right ones.
     ...     'evolution/+pots/evolution-2.2/es/1/+translate')
 
     >>> browser.getLink('First')
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    LinkNotFoundError
+    zope.testbrowser.browser.LinkNotFoundError
 
     >>> browser.getLink('Prev')
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    LinkNotFoundError
+    zope.testbrowser.browser.LinkNotFoundError
 
     >>> next = browser.getLink('Next')
     >>> print(next.url)
@@ -131,14 +133,16 @@ And the last one.
     http://.../hoary/+source/evolution/+pots/evolution-2.2/es/21/+translate
 
     >>> browser.getLink('Next')
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    LinkNotFoundError
+    zope.testbrowser.browser.LinkNotFoundError
 
     >>> browser.getLink('Last')
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    LinkNotFoundError
+    zope.testbrowser.browser.LinkNotFoundError
 
 And the link to the IPOFile view should be there too:
 
@@ -361,9 +365,10 @@ UnexpectedFormData exception:
     ...  'http://translations.launchpad.test/ubuntu/hoary/+source/evolution/'
     ...  '+pots/evolution-2.2/es/14/+translate?field.alternative_language=ca&'
     ...  'field.alternative_language=es')
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    UnexpectedFormData: You specified...
+    lp.app.errors.UnexpectedFormData: You specified...
 
 Let's see what happens when we do a submission with a lock_timestamp
 older than the review date for current translation.
diff --git a/lib/lp/translations/stories/translationgroups/xx-change-translation-policy.txt b/lib/lp/translations/stories/translationgroups/xx-change-translation-policy.txt
index 866a42f..6a701fd 100644
--- a/lib/lp/translations/stories/translationgroups/xx-change-translation-policy.txt
+++ b/lib/lp/translations/stories/translationgroups/xx-change-translation-policy.txt
@@ -58,18 +58,20 @@ to translations policy page.
     >>> dtc_browser.open(
     ...     'http://translations.launchpad.test/chestii')
     >>> dtc_browser.getLink('Configure Translations')
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    LinkNotFoundError...
+    zope.testbrowser.browser.LinkNotFoundError
 
 An attempt to access the translations policy url will not be authorized.
 
     >>> browser.open(
     ...     'http://translations.launchpad.test/'
     ...     'chestii/+configure-translations')
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    Unauthorized...
+    zope.security.interfaces.Unauthorized: ...
 
 
 Translations policy for distributions
diff --git a/lib/lp/translations/stories/translationgroups/xx-translationgroups.txt b/lib/lp/translations/stories/translationgroups/xx-translationgroups.txt
index e73332c..6481fcf 100644
--- a/lib/lp/translations/stories/translationgroups/xx-translationgroups.txt
+++ b/lib/lp/translations/stories/translationgroups/xx-translationgroups.txt
@@ -20,17 +20,19 @@ page.
 
     >>> anon_browser.open(
     ...     'http://translations.launchpad.test/+groups/+new')
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    Unauthorized...
+    zope.security.interfaces.Unauthorized: ...
 
 Same for a regular, unprivileged user.
 
     >>> user_browser.open(
     ...     'http://translations.launchpad.test/+groups/+new')
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    Unauthorized...
+    zope.security.interfaces.Unauthorized: ...
 
 OK, best we try again, with administrator rights!
 
@@ -264,14 +266,16 @@ Other users cannot access this page, nor see the menu link to it.
 
     >>> user_browser.open(anon_browser.url)
     >>> user_browser.getLink('Configure Translations').click()
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    LinkNotFoundError
+    zope.testbrowser.browser.LinkNotFoundError
 
     >>> user_browser.open(ubuntu_owner_browser.url)
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    Unauthorized...
+    zope.security.interfaces.Unauthorized: ...
 
 Let's post to the form, setting the translation group to polyglot and
 closed permissions.
@@ -361,14 +365,16 @@ leads to.
 
     >>> user_browser.open(translations_page_url)
     >>> user_browser.getLink('Configure Translations').click()
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    LinkNotFoundError
+    zope.testbrowser.browser.LinkNotFoundError
 
     >>> user_browser.open(change_translators_url)
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    Unauthorized...
+    zope.security.interfaces.Unauthorized: ...
 
 Now let's post to the form. We should be redirected to the product page.
 
@@ -407,14 +413,16 @@ to access the page it leads to.
 
     >>> user_browser.open(translations_page_url)
     >>> user_browser.getLink('Change permissions').click()
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    LinkNotFoundError
+    zope.testbrowser.browser.LinkNotFoundError
 
     >>> user_browser.open(gnome_owner_browser.url)
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    Unauthorized...
+    zope.security.interfaces.Unauthorized: ...
 
 Let's post to the form, setting the translation group to polyglot and
 closed permissions.
@@ -606,9 +614,10 @@ Normal users, however, are not.
     'http://translations.launchpad.test/+groups/polyglot/'
 
     >>> user_browser.getLink('Appoint a new translation team')
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    LinkNotFoundError
+    zope.testbrowser.browser.LinkNotFoundError
 
     >>> user_browser.open(
     ...   'http://translations.launchpad.test/+groups/polyglot/')
@@ -616,9 +625,10 @@ Normal users, however, are not.
     'http://translations.launchpad.test/+groups/polyglot/'
 
     >>> user_browser.getLink('Change details').click()
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    LinkNotFoundError
+    zope.testbrowser.browser.LinkNotFoundError
 
 
 Change a translator in a translation group
@@ -690,9 +700,10 @@ instance Welsh (cy), the change will work.
 
     >>> admin_browser.open(
     ...     'http://translations.launchpad.test/+groups/polyglot/cy')
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    NotFound:...
+    zope.publisher.interfaces.NotFound: ...
 
     >>> browser.getControl('Language').value = ['cy']
     >>> browser.getControl('Change').click()
@@ -1081,9 +1092,10 @@ Try to get the page when unauthenticated.
     >>> browser.open(
     ...     'http://translations.launchpad.test/ubuntu/hoary/+source/' +
     ...         'evolution/+pots/evolution-2.2/af/+upload')
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    Unauthorized:...
+    zope.security.interfaces.Unauthorized: ...
 
 And now with valid credentials.
 
diff --git a/lib/lp/translations/stories/translations/xx-translations.txt b/lib/lp/translations/stories/translations/xx-translations.txt
index 3ae2b56..68424f7 100644
--- a/lib/lp/translations/stories/translations/xx-translations.txt
+++ b/lib/lp/translations/stories/translations/xx-translations.txt
@@ -100,9 +100,10 @@ page, and that it has all the data we are expecting, in terms of languages.
   >>> print(browser.getLink('Xhosa').url)
   http://translations.launchpad.test/ubuntu/hoary/+lang/xh
   >>> browser.getLink('Afrihili')
+  ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
   Traceback (most recent call last):
   ...
-  LinkNotFoundError
+  zope.testbrowser.browser.LinkNotFoundError
 
 We also should see any personal language that is not already translated, but
 which is in the personal pref list. In this example, we tell the system that
@@ -140,13 +141,15 @@ pofile) for evolution-2.2
 pmount and pkgconf-mozilla are not in this page, because it belongs to the next batch.
 
   >>> browser.getLink('pkgconf-mozilla')
+  ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
   Traceback (most recent call last):
   ...
-  LinkNotFoundError
+  zope.testbrowser.browser.LinkNotFoundError
   >>> browser.getLink('pmount')
+  ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
   Traceback (most recent call last):
   ...
-  LinkNotFoundError
+  zope.testbrowser.browser.LinkNotFoundError
 
 Let's go to next page.
 
diff --git a/lib/lp/translations/utilities/doc/gettext_mo_exporter.txt b/lib/lp/translations/utilities/doc/gettext_mo_exporter.txt
index ceb32d0..2544ded 100644
--- a/lib/lp/translations/utilities/doc/gettext_mo_exporter.txt
+++ b/lib/lp/translations/utilities/doc/gettext_mo_exporter.txt
@@ -129,6 +129,7 @@ get an export error exception:
     >>> mofile = compiler.compile(b'''
     ... blah
     ... ''')
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    UnknownTranslationExporterError: ...
+    lp.translations.interfaces.translationexporter.UnknownTranslationExporterError: ...
diff --git a/lib/lp/translations/utilities/doc/gettext_po_parser.txt b/lib/lp/translations/utilities/doc/gettext_po_parser.txt
index 678ada5..38264f1 100644
--- a/lib/lp/translations/utilities/doc/gettext_po_parser.txt
+++ b/lib/lp/translations/utilities/doc/gettext_po_parser.txt
@@ -15,49 +15,55 @@ PO files with empty headers are not allowed.
 
     >>> parser = POParser()
     >>> parser.parse('msgid "foo"\nmsgstr ""\n')
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    TranslationFormatSyntaxError:...
+    lp.translations.interfaces.translationimporter.TranslationFormatSyntaxError: ...
 
 PO files with context after msgids are reported as broken.
 
     >>> parser.parse('msgid ""\nmsgstr ""\n'
     ...              'msgid "blah"\nmsgctxt "foo"\nmsgstr "bar"\n')
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    TranslationFormatSyntaxError:...
+    lp.translations.interfaces.translationimporter.TranslationFormatSyntaxError: ...
 
 And a msgctxt followed by msgctxt is caught as well.
 
     >>> parser.parse('msgid ""\nmsgstr ""\n'
     ...              'msgctxt "foo"\nmsgctxt "foo1"\n'
     ...              'msgid "blah"\nmsgstr "bar"\n')
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    TranslationFormatSyntaxError:...
+    lp.translations.interfaces.translationimporter.TranslationFormatSyntaxError: ...
 
 When a string is followed by non-string, non-space data, it is caught
 as an error.
 
     >>> parser.parse('msgid ""\nmsgstr "something"\n'
     ...              '"foo"  whatever\n')
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    TranslationFormatSyntaxError:...Extra content found after string...
+    lp.translations.interfaces.translationimporter.TranslationFormatSyntaxError: ...Extra content found after string...
 
 Unrecognized escape sequences are caught as well.
 
     >>> parser.parse('msgid "\!"\nmsgstr ""\n')
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    TranslationFormatSyntaxError:...Unknown escape sequence...
+    lp.translations.interfaces.translationimporter.TranslationFormatSyntaxError: ...Unknown escape sequence...
 
 Unclosed strings (missing closing quotes) are caught.
 
     >>> parser.parse('msgid ""\nmsgstr "\n')
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    TranslationFormatSyntaxError:...String not terminated...
+    lp.translations.interfaces.translationimporter.TranslationFormatSyntaxError: ...String not terminated...
 
 
 == POHeader ==
@@ -316,10 +322,10 @@ errors occur:
     ... """
 
     >>> parser.parse(content + chunk2)
+    ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
     Traceback (most recent call last):
     ...
-    TranslationFormatInvalidInputError: Line 13:
-    Could not decode input from UTF-8
+    lp.translations.interfaces.translationimporter.TranslationFormatInvalidInputError: Line 13: Could not decode input from UTF-8
 
 
 == Evil Big5 Multibyte Sequences ==
diff --git a/lib/lp/translations/utilities/gettext_po_parser.py b/lib/lp/translations/utilities/gettext_po_parser.py
index 9245943..800a57c 100644
--- a/lib/lp/translations/utilities/gettext_po_parser.py
+++ b/lib/lp/translations/utilities/gettext_po_parser.py
@@ -744,9 +744,11 @@ class POParser(object):
           just recoded as Unicode so it's a TranslationFormatInvalidInputError
           >>> utf8_string = u'"view \\302\\253${version_title}\\302\\273"'
           >>> parser._parseQuotedString(utf8_string)
+          ... # doctest: +NORMALIZE_WHITESPACE
+          ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
           Traceback (most recent call last):
           ...
-          TranslationFormatInvalidInputError: Could not decode escaped string: (\302\253)
+          lp.translations.interfaces.translationimporter.TranslationFormatInvalidInputError: Could not decode escaped string: (\302\253)
 
           Now, we note the original encoding so we get the right Unicode
           string.
@@ -764,25 +766,33 @@ class POParser(object):
 
           >>> iso8859_1_string = u'"foo \\xf9"'
           >>> parser._parseQuotedString(iso8859_1_string)
+          ... # doctest: +NORMALIZE_WHITESPACE
+          ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
           Traceback (most recent call last):
           ...
-          TranslationFormatInvalidInputError: Could not decode escaped string as UTF-8: (\xf9)
+          lp.translations.interfaces.translationimporter.TranslationFormatInvalidInputError: Could not decode escaped string as UTF-8: (\xf9)
 
           An error will be raised if the entire string isn't contained in
           quotes properly:
 
           >>> parser._parseQuotedString(u'abc')
+          ... # doctest: +NORMALIZE_WHITESPACE
+          ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
           Traceback (most recent call last):
             ...
-          TranslationFormatSyntaxError: String is not quoted
+          lp.translations.interfaces.translationimporter.TranslationFormatSyntaxError: String is not quoted
           >>> parser._parseQuotedString(u'\"ab')
+          ... # doctest: +NORMALIZE_WHITESPACE
+          ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
           Traceback (most recent call last):
             ...
-          TranslationFormatSyntaxError: String not terminated
+          lp.translations.interfaces.translationimporter.TranslationFormatSyntaxError: String not terminated
           >>> parser._parseQuotedString(u'\"ab\"x')
+          ... # doctest: +NORMALIZE_WHITESPACE
+          ... # doctest: +IGNORE_EXCEPTION_MODULE_IN_PYTHON2
           Traceback (most recent call last):
             ...
-          TranslationFormatSyntaxError: Extra content found after string: (x)
+          lp.translations.interfaces.translationimporter.TranslationFormatSyntaxError: Extra content found after string: (x)
         """
         if self._escaped_line_break:
             # Continuing a line after an escaped newline.  Strip indentation.
diff --git a/lib/lp/translations/utilities/tests/test_gettext_po_parser.py b/lib/lp/translations/utilities/tests/test_gettext_po_parser.py
index 503f694..0ea27cd 100644
--- a/lib/lp/translations/utilities/tests/test_gettext_po_parser.py
+++ b/lib/lp/translations/utilities/tests/test_gettext_po_parser.py
@@ -5,6 +5,8 @@ import doctest
 import re
 import unittest
 
+from zope.testing.renormalizing import OutputChecker
+
 from lp.translations.interfaces.translationimporter import (
     TranslationFormatInvalidInputError,
     TranslationFormatSyntaxError,
@@ -449,7 +451,7 @@ class POBasicTestCase(unittest.TestCase):
 
 def test_suite():
     # Run gettext PO parser doc tests.
-    dt_suite = doctest.DocTestSuite(gettext_po_parser)
+    dt_suite = doctest.DocTestSuite(gettext_po_parser, checker=OutputChecker())
     loader = unittest.TestLoader()
     ut_suite = loader.loadTestsFromTestCase(POBasicTestCase)
     return unittest.TestSuite((ut_suite, dt_suite))