← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] ~cjwatson/launchpad:py3-use-backslashreplace into launchpad:master

 

Colin Watson has proposed merging ~cjwatson/launchpad:py3-use-backslashreplace into launchpad:master.

Commit message:
Use backslashreplace helper everywhere

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

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

This does more finely-tuned string type conversion such that it works on both Python 2 and 3.
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of ~cjwatson/launchpad:py3-use-backslashreplace into launchpad:master.
diff --git a/lib/lp/answers/browser/tests/views.txt b/lib/lp/answers/browser/tests/views.txt
index 3a87546..55cb48f 100644
--- a/lib/lp/answers/browser/tests/views.txt
+++ b/lib/lp/answers/browser/tests/views.txt
@@ -760,7 +760,7 @@ searchResults() method. The returned results are batched.
     <lp.services.webapp.batching.BatchNavigator ...>
 
     >>> for question in questions.batch:
-    ...     print(question.title.encode('us-ascii', 'backslashreplace'))
+    ...     print(backslashreplace(question.title))
     Problema al recompilar kernel con soporte smp (doble-n\xfacleo)
     Continue playing after shutdown
     Play DVDs in Totem
diff --git a/lib/lp/answers/doc/notifications.txt b/lib/lp/answers/doc/notifications.txt
index 1aa97dd..eff0fd1 100644
--- a/lib/lp/answers/doc/notifications.txt
+++ b/lib/lp/answers/doc/notifications.txt
@@ -698,7 +698,7 @@ receive notifications related to it.
     ...     language=getUtility(ILanguageSet)['pt_BR'])
     >>> notifications = pop_questionemailjobs()
 
-    >>> print(notifications[0].subject.encode('ASCII', 'backslashreplace'))
+    >>> print(backslashreplace(notifications[0].subject))
     [Question #...]: Abrir uma p\xe1gina que requer java quebra o firefox
 
 Similarly, when a question in a non-English language is modified or its
@@ -735,7 +735,7 @@ sent to the support list about that question:
     # Effectively replace u'\xe9' by '\\e9'.
 
     >>> def recode_text(notification):
-    ...     return notification.body.encode('ASCII', 'backslashreplace')
+    ...     return backslashreplace(notification.body)
 
     >>> notification_body = recode_text(notifications[1])
     >>> print(notification_body)
diff --git a/lib/lp/answers/stories/answer-contact-report.txt b/lib/lp/answers/stories/answer-contact-report.txt
index 9900bd2..5458b9a 100644
--- a/lib/lp/answers/stories/answer-contact-report.txt
+++ b/lib/lp/answers/stories/answer-contact-report.txt
@@ -27,7 +27,7 @@ they registered for.
 
     >>> content = find_tag_by_id(
     ...     anon_browser.contents, "direct-answer-contacts-for-list")
-    >>> print(extract_text(content).encode('ascii', 'backslashreplace'))
+    >>> print(backslashreplace(extract_text(content)))
     gnomebaker
     ...mozilla-firefox... package in Ubuntu
 
@@ -75,6 +75,6 @@ see the link for other users.
 
     >>> content = find_tag_by_id(
     ...     browser.contents, "direct-answer-contacts-for-list")
-    >>> print(extract_text(content).encode('ascii', 'backslashreplace'))
+    >>> print(backslashreplace(extract_text(content)))
     gnomebaker
     ...mozilla-firefox... package in Ubuntu
diff --git a/lib/lp/answers/stories/faq-browse-and-search.txt b/lib/lp/answers/stories/faq-browse-and-search.txt
index 8826620..2a35fbe 100644
--- a/lib/lp/answers/stories/faq-browse-and-search.txt
+++ b/lib/lp/answers/stories/faq-browse-and-search.txt
@@ -107,7 +107,7 @@ enter keywords that be used to filter the displayed FAQs.
 When no matches are found, a simple message is displayed:
 
     >>> message = find_main_content(browser.contents).find('p')
-    >>> print(extract_text(message).encode('ascii', 'backslashreplace'))
+    >>> print(backslashreplace(extract_text(message)))
     There are no FAQs for Ubuntu matching \u201ccrash on boot\u201d.
 
 Otherwise, the listing only contains the matching FAQs.
diff --git a/lib/lp/answers/stories/question-add.txt b/lib/lp/answers/stories/question-add.txt
index b8cfea0..ab30452 100644
--- a/lib/lp/answers/stories/question-add.txt
+++ b/lib/lp/answers/stories/question-add.txt
@@ -89,8 +89,7 @@ For now, use a closer search:
     u'http://answers.launchpad.test/ubuntu/+faq/...'
 
     >>> similar_questions = contents.find(id='similar-questions')
-    >>> print(extract_text(similar_questions).encode(
-    ...     'ascii', 'backslashreplace'))
+    >>> print(backslashreplace(extract_text(similar_questions)))
     8: Installation of Java Runtime Environment for Mozilla  (Answered)
         posted on ... in ...mozilla-firefox... package in Ubuntu
     >>> similar_questions.a['href']
diff --git a/lib/lp/answers/stories/question-overview.txt b/lib/lp/answers/stories/question-overview.txt
index 0a22246..701357b 100644
--- a/lib/lp/answers/stories/question-overview.txt
+++ b/lib/lp/answers/stories/question-overview.txt
@@ -169,7 +169,7 @@ question' button on their overview page.
 
     >>> questions = find_tag_by_id(
     ...     user_browser.contents, 'portlet-latest-questions')
-    >>> print(extract_text(questions).encode('ASCII', 'backslashreplace'))
+    >>> print(backslashreplace(extract_text(questions)))
     All questions
     Latest questions
     Problemas de Impress\xe3o no Firefox ...
diff --git a/lib/lp/answers/stories/this-is-a-faq.txt b/lib/lp/answers/stories/this-is-a-faq.txt
index ff6e5d9..1939f2c 100644
--- a/lib/lp/answers/stories/this-is-a-faq.txt
+++ b/lib/lp/answers/stories/this-is-a-faq.txt
@@ -134,9 +134,8 @@ A link to the FAQ appears under the question's description:
 
 The answer message was added to the question's discussion:
 
-    >>> print(extract_text(find_tags_by_class(
-    ...     user_browser.contents, 'boardCommentBody')[-1]).encode(
-    ...     'ascii', 'backslashreplace'))
+    >>> print(backslashreplace(extract_text(find_tags_by_class(
+    ...     user_browser.contents, 'boardCommentBody')[-1])))
     No Privileges Person suggests this article as an answer to your question:
     FAQ #10: \u201cHow do I install plugins...
 
@@ -183,9 +182,8 @@ submit the form again.
 
 The new message was added to the question:
 
-    >>> print(extract_text(find_tags_by_class(
-    ...     user_browser.contents, 'boardCommentBody')[-1]).encode(
-    ...     'ascii', 'backslashreplace'))
+    >>> print(backslashreplace(extract_text(find_tags_by_class(
+    ...     user_browser.contents, 'boardCommentBody')[-1])))
     Sorry, this document doesn't really answer your question.
 
 The link was also removed from the details portlet:
@@ -275,9 +273,8 @@ returned to the question page.
 
 The answer message was added to the question's discussion:
 
-    >>> print(extract_text(find_tags_by_class(
-    ...     owner_browser.contents, 'boardCommentBody')[-1]).encode(
-    ...     'ascii', 'backslashreplace'))
+    >>> print(backslashreplace(extract_text(find_tags_by_class(
+    ...     owner_browser.contents, 'boardCommentBody')[-1])))
     Read the Fine Answer:
     FAQ...: \u201cDisplaying SVG in Firefox\u201d.
 
@@ -391,9 +388,8 @@ discussion.
     Related FAQ:
     How can I play MP3/Divx/DVDs/Quicktime/Realmedia files ...
 
-    >>> print(extract_text(find_tags_by_class(
-    ...     user_browser.contents, 'boardCommentBody')[-1]).encode(
-    ...     'ascii', 'backslashreplace'))
+    >>> print(backslashreplace(extract_text(find_tags_by_class(
+    ...     user_browser.contents, 'boardCommentBody')[-1])))
     The FAQ mentions this:
     FAQ #6: ...How can I play MP3/Divx/DVDs/Quicktime/Realmedia files...
 
diff --git a/lib/lp/blueprints/model/specificationworkitem.py b/lib/lp/blueprints/model/specificationworkitem.py
index c3aa789..8490fc8 100644
--- a/lib/lp/blueprints/model/specificationworkitem.py
+++ b/lib/lp/blueprints/model/specificationworkitem.py
@@ -6,7 +6,6 @@ __all__ = [
     'SpecificationWorkItem',
     ]
 
-import six
 from storm.locals import (
     Bool,
     Int,
@@ -26,6 +25,7 @@ from lp.services.database.constants import DEFAULT
 from lp.services.database.datetimecol import UtcDateTimeCol
 from lp.services.database.enumcol import EnumCol
 from lp.services.database.stormbase import StormBase
+from lp.services.helpers import backslashreplace
 
 
 @implementer(ISpecificationWorkItem)
@@ -50,8 +50,7 @@ class SpecificationWorkItem(StormBase):
     deleted = Bool(allow_none=False, default=False)
 
     def __repr__(self):
-        title = six.ensure_str(
-            self.title, encoding='ASCII', errors='backslashreplace')
+        title = backslashreplace(self.title)
         assignee = getattr(self.assignee, 'name', None)
         return '<SpecificationWorkItem [%s] %s: %s of %s>' % (
             assignee, title, self.status.name, self.specification)
diff --git a/lib/lp/blueprints/stories/standalone/xx-overview.txt b/lib/lp/blueprints/stories/standalone/xx-overview.txt
index 51f6894..5012814 100644
--- a/lib/lp/blueprints/stories/standalone/xx-overview.txt
+++ b/lib/lp/blueprints/stories/standalone/xx-overview.txt
@@ -36,7 +36,7 @@ of blueprints attached to Mozilla Firefox by following the "Blueprints" tab:
     >>> user_browser.open('http://launchpad.test/firefox')
     >>> user_browser.getLink('Blueprints').click()
     >>> main = find_main_content(user_browser.contents)
-    >>> print(extract_text(main).encode('ascii', 'backslashreplace'))
+    >>> print(backslashreplace(extract_text(main)))
     Blueprints for Mozilla Firefox...
     Priority...Blueprint...Design  ...Delivery      ...
     High    ...svg-support  ...Approved...Beta Available...
@@ -52,7 +52,7 @@ the blueprints page for 1.0:
 
     >>> user_browser.open('http://blueprints.launchpad.test/firefox/1.0')
     >>> main = find_main_content(user_browser.contents)
-    >>> print(extract_text(main).encode('ascii', 'backslashreplace'))
+    >>> print(backslashreplace(extract_text(main)))
     Blueprints for 1.0
     Launchpad lets projects track the features they intend to implement...
 
@@ -65,7 +65,7 @@ Let's target an existing Mozilla Firefox blueprint to the 1.0 series:
     Support Native SVG Objects...
     >>> browser.getLink('Propose as goal').click()
     >>> main = find_main_content(browser.contents)
-    >>> print(extract_text(main).encode('ascii', 'backslashreplace'))
+    >>> print(backslashreplace(extract_text(main)))
     Target to a product series
     Support Native SVG Objects
     ...
@@ -93,7 +93,7 @@ Now we'll target our chosen blueprint to the new milestone:
     >>> browser.getLink('svg-support').click()
     >>> browser.getLink('Target milestone').click()
     >>> main = find_main_content(browser.contents)
-    >>> print(extract_text(main).encode('ascii', 'backslashreplace'))
+    >>> print(backslashreplace(extract_text(main)))
     Target to a milestone
     ...
     Support Native SVG Objects
@@ -112,7 +112,7 @@ blueprint. It also lists the milestone to which the blueprint is targeted:
 
     >>> user_browser.open('http://blueprints.launchpad.test/firefox/1.0')
     >>> main = find_main_content(user_browser.contents)
-    >>> print(extract_text(main).encode('ascii', 'backslashreplace'))
+    >>> print(backslashreplace(extract_text(main)))
     Blueprints for 1.0...
     Priority...Blueprint...Design  ...Delivery...Assignee...Milestone...
     High    ...svg-support  ...Approved...Beta    ...Carlos  ...1.0.9    ...
@@ -133,7 +133,7 @@ blueprints attached to Ubuntu Linux by following the "Blueprints" tab:
     >>> user_browser.open('http://launchpad.test/ubuntu')
     >>> user_browser.getLink('Blueprints').click()
     >>> main = find_main_content(user_browser.contents)
-    >>> print(extract_text(main).encode('ascii', 'backslashreplace'))
+    >>> print(backslashreplace(extract_text(main)))
     Blueprints for Ubuntu...
     Priority ...Blueprint        ...Design    ...Delivery...
     Undefined...media-integrity-check...Discussion...Unknown...
@@ -149,7 +149,7 @@ blueprints page for Grumpy:
 
     >>> user_browser.open('http://blueprints.launchpad.test/ubuntu/grumpy')
     >>> main = find_main_content(user_browser.contents)
-    >>> print(extract_text(main).encode('ascii', 'backslashreplace'))
+    >>> print(backslashreplace(extract_text(main)))
     Blueprints for Grumpy
     Launchpad lets projects track the features they intend to implement...
 
@@ -163,7 +163,7 @@ Let's target an existing Ubuntu blueprint to the Grumpy series:
     CD Media Integrity Check...
     >>> browser.getLink('Propose as goal').click()
     >>> main = find_main_content(browser.contents)
-    >>> print(extract_text(main).encode('ascii', 'backslashreplace'))
+    >>> print(backslashreplace(extract_text(main)))
     Target to a distribution series
     CD Media Integrity Check
     ...
@@ -208,7 +208,7 @@ blueprint. It also lists the milestone to which the blueprint is targeted:
 
     >>> user_browser.open('http://blueprints.launchpad.test/ubuntu/grumpy')
     >>> main = find_main_content(user_browser.contents)
-    >>> print(extract_text(main).encode('ascii', 'backslashreplace'))
+    >>> print(backslashreplace(extract_text(main)))
     Blueprints for Grumpy...
     Priority ...Blueprint        ...Design    ...Delivery...Milestone...
     Undefined...media-integrity-check...Discussion...Unknown ...drift-1  ...
diff --git a/lib/lp/bugs/stories/structural-subscriptions/xx-bug-subscriptions.txt b/lib/lp/bugs/stories/structural-subscriptions/xx-bug-subscriptions.txt
index de639aa..99e500b 100644
--- a/lib/lp/bugs/stories/structural-subscriptions/xx-bug-subscriptions.txt
+++ b/lib/lp/bugs/stories/structural-subscriptions/xx-bug-subscriptions.txt
@@ -42,9 +42,8 @@ Sample Person and the Landscape team are now subscribed.
 
     >>> browser.open(
     ... 'http://bugs.launchpad.test/ubuntu/+source/mozilla-firefox/+subscribe')
-    >>> print(extract_text(find_portlet(
-    ...     browser.contents, 'Subscribers')).encode(
-    ...     'ascii', 'backslashreplace'))
+    >>> print(backslashreplace(extract_text(find_portlet(
+    ...     browser.contents, 'Subscribers'))))
     Subscribers
       To all bugs in mozilla-firefox in Ubuntu:
         Foo Bar
diff --git a/lib/lp/registry/browser/tests/teammembership-views.txt b/lib/lp/registry/browser/tests/teammembership-views.txt
index ea06526..c5b03e3 100644
--- a/lib/lp/registry/browser/tests/teammembership-views.txt
+++ b/lib/lp/registry/browser/tests/teammembership-views.txt
@@ -72,5 +72,5 @@ is applied during the stepto traversal--it is not a named view in ZCML.
 The view provides page_title to create a breadcrumb that describes this
 use the TeamMembership.
 
-    >>> print view.page_title.encode('ascii', 'backslashreplace')
+    >>> print(backslashreplace(view.page_title))
     \u201cUs\u201d team invitation
diff --git a/lib/lp/registry/doc/sourcepackage.txt b/lib/lp/registry/doc/sourcepackage.txt
index c8a6508..c712f3b 100644
--- a/lib/lp/registry/doc/sourcepackage.txt
+++ b/lib/lp/registry/doc/sourcepackage.txt
@@ -218,7 +218,7 @@ method on a distribution:
     >>> print ubuntu_firefox.name
     mozilla-firefox
 
-    >>> print ubuntu_firefox.title.encode('ascii', 'backslashreplace')
+    >>> print(backslashreplace(ubuntu_firefox.title))
     mozilla-firefox package in Ubuntu
 
     >>> print ubuntu_firefox.displayname
diff --git a/lib/lp/registry/model/distribution.py b/lib/lp/registry/model/distribution.py
index 0db17cb..516b8be 100644
--- a/lib/lp/registry/model/distribution.py
+++ b/lib/lp/registry/model/distribution.py
@@ -156,7 +156,10 @@ from lp.services.database.stormexpr import (
     rank_by_fti,
     )
 from lp.services.features import getFeatureFlag
-from lp.services.helpers import shortlist
+from lp.services.helpers import (
+    backslashreplace,
+    shortlist,
+    )
 from lp.services.propertycache import (
     cachedproperty,
     get_property_cache,
@@ -269,8 +272,7 @@ class Distribution(SQLBase, BugTargetBase, MakesAnnouncements,
     redirect_default_traversal = BoolCol(notNull=False, default=False)
 
     def __repr__(self):
-        display_name = six.ensure_str(
-            self.display_name, encoding='ASCII', errors='backslashreplace')
+        display_name = backslashreplace(self.display_name)
         return "<%s '%s' (%s)>" % (
             self.__class__.__name__, display_name, self.name)
 
diff --git a/lib/lp/registry/model/person.py b/lib/lp/registry/model/person.py
index ed09f67..b5ba29a 100644
--- a/lib/lp/registry/model/person.py
+++ b/lib/lp/registry/model/person.py
@@ -261,6 +261,7 @@ from lp.services.database.sqlbase import (
 from lp.services.database.stormbase import StormBase
 from lp.services.database.stormexpr import fti_search
 from lp.services.helpers import (
+    backslashreplace,
     ensure_unicode,
     shortlist,
     )
@@ -547,8 +548,7 @@ class Person(
                      storm_validator=_validate_name)
 
     def __repr__(self):
-        displayname = six.ensure_str(
-            self.displayname, encoding='ASCII', errors='backslashreplace')
+        displayname = backslashreplace(self.displayname)
         return '<Person at 0x%x %s (%s)>' % (id(self), self.name, displayname)
 
     display_name = StringCol(dbName='displayname', notNull=True)
diff --git a/lib/lp/registry/stories/announcements/xx-announcements.txt b/lib/lp/registry/stories/announcements/xx-announcements.txt
index 610bfaf..64fe2e8 100644
--- a/lib/lp/registry/stories/announcements/xx-announcements.txt
+++ b/lib/lp/registry/stories/announcements/xx-announcements.txt
@@ -135,8 +135,7 @@ announcement so there is a "Latest news" portlet. Let's render the
 portlet, taking care not to render today's date which would timebomb our
 script.
 
-    >>> print(latest_news(priv_browser.contents).encode(
-    ...     'ascii', 'backslashreplace'))
+    >>> print(backslashreplace(latest_news(priv_browser.contents)))
     Announcements
     Apache announcement headline...
     Read all announcements
@@ -160,8 +159,7 @@ work too:
 
 And check out the results:
 
-    >>> print(latest_news(priv_browser.contents).encode(
-    ...     'ascii', 'backslashreplace'))
+    >>> print(backslashreplace(latest_news(priv_browser.contents)))
     Announcements
     Apache announcement headline ...
     Tomcat announcement headline on 2007-11-24 ...
diff --git a/lib/lp/registry/stories/productrelease/xx-productrelease-delete.txt b/lib/lp/registry/stories/productrelease/xx-productrelease-delete.txt
index 31563f9..f7f11b7 100644
--- a/lib/lp/registry/stories/productrelease/xx-productrelease-delete.txt
+++ b/lib/lp/registry/stories/productrelease/xx-productrelease-delete.txt
@@ -41,7 +41,7 @@ The 0.9.2 release has some files associated with it. Salgado reads that
 they will be deleted too.
 
     >>> text = extract_text(find_main_content(salgados_browser.contents))
-    >>> print(text.encode('ASCII', 'backslashreplace'))
+    >>> print(backslashreplace(text))
     Delete Mozilla Firefox 0.9.2 \u201cOne (secure) Tree Hill\u201d
     ...
     Are you sure you want to delete the 0.9.2 release of
diff --git a/lib/lp/services/database/doc/textsearching.txt b/lib/lp/services/database/doc/textsearching.txt
index 0cb4d67..8951f1b 100644
--- a/lib/lp/services/database/doc/textsearching.txt
+++ b/lib/lp/services/database/doc/textsearching.txt
@@ -106,11 +106,11 @@ The following examples show the text version of the query using
     ...         SQLBase._connection._connection.rollback()
     ...         raise
     ...     if uncompiled is not None:
-    ...         uncompiled = uncompiled.encode('US-ASCII', 'backslashreplace')
+    ...         uncompiled = backslashreplace(uncompiled)
     ...         uncompiled = uncompiled.replace(' ','')
     ...     if compiled is not None:
     ...         compiled = compiled.decode('UTF-8')
-    ...         compiled = compiled.encode('US-ASCII', 'backslashreplace')
+    ...         compiled = backslashreplace(compiled)
     ...     print('%s <=> %s' % (uncompiled, compiled))
     >>>
     >>> def search(text_to_search, search_phrase):
diff --git a/lib/lp/services/identity/model/account.py b/lib/lp/services/identity/model/account.py
index bfad289..a5d4a9d 100644
--- a/lib/lp/services/identity/model/account.py
+++ b/lib/lp/services/identity/model/account.py
@@ -24,6 +24,7 @@ from lp.services.database.interfaces import (
     IStore,
     )
 from lp.services.database.sqlbase import SQLBase
+from lp.services.helpers import backslashreplace
 from lp.services.identity.interfaces.account import (
     AccountCreationRationale,
     AccountStatus,
@@ -62,7 +63,7 @@ class Account(SQLBase):
         "Account.id", OpenIdIdentifier.account_id)
 
     def __repr__(self):
-        displayname = self.displayname.encode('ASCII', 'backslashreplace')
+        displayname = backslashreplace(self.displayname)
         return "<%s '%s' (%s)>" % (
             self.__class__.__name__, displayname, self.status)
 
diff --git a/lib/lp/testing/pages.py b/lib/lp/testing/pages.py
index e620147..4192057 100644
--- a/lib/lp/testing/pages.py
+++ b/lib/lp/testing/pages.py
@@ -62,6 +62,7 @@ from lp.services.beautifulsoup import (
     )
 from lp.services.config import config
 from lp.services.encoding import wsgi_native_string
+from lp.services.helpers import backslashreplace
 from lp.services.oauth.interfaces import (
     IOAuthConsumerSet,
     OAUTH_REALM,
@@ -541,7 +542,7 @@ def print_comments(page):
 def print_batch_header(soup):
     """Print the batch navigator header."""
     navigation = soup.find('td', {'class': 'batch-navigation-index'})
-    print(extract_text(navigation).encode('ASCII', 'backslashreplace'))
+    print(backslashreplace(extract_text(navigation)))
 
 
 def print_self_link_of_entries(json_body):
@@ -894,6 +895,7 @@ def setUpGlobs(test, future=False):
     test.globs['page_log_location'] = PageTestLayer.log_location
     test.globs['stop'] = stop
     test.globs['six'] = six
+    test.globs['backslashreplace'] = backslashreplace
 
     if future:
         import __future__
diff --git a/lib/lp/testing/systemdocs.py b/lib/lp/testing/systemdocs.py
index 706fa75..abc6239 100644
--- a/lib/lp/testing/systemdocs.py
+++ b/lib/lp/testing/systemdocs.py
@@ -32,6 +32,7 @@ from zope.testing.loggingsupport import Handler
 
 from lp.services.config import config
 from lp.services.database.sqlbase import flush_database_updates
+from lp.services.helpers import backslashreplace
 from lp.services.webapp.interfaces import ILaunchBag
 from lp.testing import (
     ANONYMOUS,
@@ -264,6 +265,7 @@ def setGlobs(test, future=False):
     test.globs['launchpadlib_credentials_for'] = launchpadlib_credentials_for
     test.globs['oauth_access_token_for'] = oauth_access_token_for
     test.globs['six'] = six
+    test.globs['backslashreplace'] = backslashreplace
 
     if future:
         import __future__
diff --git a/lib/lp/translations/interfaces/translationimporter.py b/lib/lp/translations/interfaces/translationimporter.py
index e0d67b8..7547050 100644
--- a/lib/lp/translations/interfaces/translationimporter.py
+++ b/lib/lp/translations/interfaces/translationimporter.py
@@ -23,6 +23,7 @@ from zope.schema import (
     TextLine,
     )
 
+from lp.services.helpers import backslashreplace
 from lp.translations.interfaces.translationcommonformat import (
     TranslationImportExportBaseException,
     )
@@ -60,7 +61,7 @@ class TranslationFormatBaseError(TranslationImportExportBaseException):
     def represent(self, default_message):
         """Return human-readable description of error location."""
         if self.filename is not None:
-            safe_filename = self.filename.encode("ascii", "backslashreplace")
+            safe_filename = backslashreplace(self.filename)
 
         if self.line_number is not None and self.line_number > 0:
             if self.filename is not None:
@@ -78,7 +79,7 @@ class TranslationFormatBaseError(TranslationImportExportBaseException):
             location_prefix = ""
 
         if self.message is not None:
-            text = self.message.encode("ascii", "backslashreplace")
+            text = backslashreplace(self.message)
         else:
             text = default_message
 
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 9846322..6d500ed 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
@@ -52,8 +52,7 @@ already included in the sample data.
     ...     """Print "x -> y of z results" batch navigator heading."""
     ...     heading = find_tags_by_class(
     ...         browser.contents, 'batch-navigation-index')[0]
-    ...     print(
-    ...         extract_text(heading).encode('us-ascii', 'backslashreplace'))
+    ...     print(backslashreplace(extract_text(heading)))
 
     >>> def print_dropdown(browser, name, index=0):
     ...     """Print contents of named dropdown."""
@@ -307,7 +306,7 @@ The only entry that shows up now is the one Carlos just uploaded.
 
     >>> def represent_queue_entry(entry_html):
     ...     text_contents = extract_text(entry_html)
-    ...     return text_contents.encode('ascii', 'backslashreplace')
+    ...     return backslashreplace(text_contents)
 
     >>> import_list = find_tag_by_id(
     ...     user_browser.contents, 'import-entries-list')
diff --git a/lib/lp/translations/stories/standalone/xx-pofile-details.txt b/lib/lp/translations/stories/standalone/xx-pofile-details.txt
index 888e2fb..8efea71 100644
--- a/lib/lp/translations/stories/standalone/xx-pofile-details.txt
+++ b/lib/lp/translations/stories/standalone/xx-pofile-details.txt
@@ -8,8 +8,8 @@ contributors.
     >>> anon_browser.open(
     ...     'http://translations.launchpad.test/evolution/trunk/+pots/'
     ...     'evolution-2.2/es/+details')
-    >>> print(extract_text(find_main_content(anon_browser.contents)).encode(
-    ...     'ascii', 'backslashreplace'))
+    >>> print(backslashreplace(extract_text(find_main_content(
+    ...     anon_browser.contents))))
     Details for Spanish translation
     ...
     Latest contributor:
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 070d946..c2c1224 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
@@ -89,8 +89,8 @@ The Spanish translations now show up as suggestions.  For example, where
 "cards" might translate to Catalan as "targetas," the Spanish equivalent is
 "tarjetas."
 
-    >>> print(extract_text(find_main_content(
-    ...     browser.contents)).encode('ASCII', 'backslashreplace'))
+    >>> print(backslashreplace(extract_text(find_main_content(
+    ...     browser.contents))))
     Translating into Catalan...
     ...
     English: cards
@@ -138,8 +138,8 @@ languages are defined.  Suggestions just work for anonymous users.
     ...     anon_browser.toStr('Spanish (es)')).selected = True
     >>> anon_browser.getControl('Change').click()
 
-    >>> print(extract_text(find_main_content(
-    ...     anon_browser.contents)).encode('ASCII', 'backslashreplace'))
+    >>> print(backslashreplace(extract_text(find_main_content(
+    ...     anon_browser.contents))))
     Browsing Catalan translation
     ...
     English: cards
diff --git a/lib/lp/translations/stories/standalone/xx-rosetta-sourcepackage-list.txt b/lib/lp/translations/stories/standalone/xx-rosetta-sourcepackage-list.txt
index e4f442c..cae0b43 100644
--- a/lib/lp/translations/stories/standalone/xx-rosetta-sourcepackage-list.txt
+++ b/lib/lp/translations/stories/standalone/xx-rosetta-sourcepackage-list.txt
@@ -16,8 +16,7 @@ IP address, since we'll use that later.
     'Hoary (5.04) : Translations : ...evolution...package : Ubuntu'
 
     >>> content = find_main_content(anon_browser.contents)
-    >>> print(extract_text(content.findAll('h1')[0]).encode(
-    ...     'ascii','backslashreplace'))
+    >>> print(backslashreplace(extract_text(content.findAll('h1')[0])))
     Translations for evolution in Ubuntu Hoary
 
 There are two templates for evolution in Ubuntu Hoary