← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] ~cjwatson/launchpad:unsixify-trivial-strings into launchpad:master

 

Colin Watson has proposed merging ~cjwatson/launchpad:unsixify-trivial-strings into launchpad:master.

Commit message:
Avoid six.ensure_* in obvious cases

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

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

If we're dealing with something where it's obvious whether it's `bytes` or `str` (e.g. a literal or something that we just did an `isinstance` check on), then there's no need to use `six.ensure_*`; just use `.decode`/`.encode` directly.

`lp.testing.pages.http` accepts either `bytes` or `str`.
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of ~cjwatson/launchpad:unsixify-trivial-strings into launchpad:master.
diff --git a/lib/lp/archivepublisher/tests/archive-signing.rst b/lib/lp/archivepublisher/tests/archive-signing.rst
index 13511cc..dc8eba9 100644
--- a/lib/lp/archivepublisher/tests/archive-signing.rst
+++ b/lib/lp/archivepublisher/tests/archive-signing.rst
@@ -204,7 +204,7 @@ to test the export functions.
     ...         self.fingerprint = "fpr"
     ...
     ...     def export(self):
-    ...         return six.ensure_binary("Secret %s" % self.secret)
+    ...         return ("Secret %s" % self.secret).encode()
     ...
 
 exportSecretKey() raises an error if given a public key.
diff --git a/lib/lp/archivepublisher/tests/test_publisher.py b/lib/lp/archivepublisher/tests/test_publisher.py
index f3ac846..33fc909 100644
--- a/lib/lp/archivepublisher/tests/test_publisher.py
+++ b/lib/lp/archivepublisher/tests/test_publisher.py
@@ -3507,7 +3507,7 @@ class TestUpdateByHash(TestPublisherBase):
         for sourcename in ("foo", "bar", "baz"):
             self.getPubSource(
                 sourcename=sourcename,
-                filecontent=six.ensure_binary("Source: %s\n" % sourcename),
+                filecontent=("Source: %s\n" % sourcename).encode(),
             )
             self.runSteps(publisher, step_a=True, step_c=True, step_d=True)
             with open(suite_path("Release"), "rb") as f:
diff --git a/lib/lp/blueprints/browser/specification.py b/lib/lp/blueprints/browser/specification.py
index b43ef4a..1186390 100644
--- a/lib/lp/blueprints/browser/specification.py
+++ b/lib/lp/blueprints/browser/specification.py
@@ -41,7 +41,6 @@ from operator import attrgetter
 from subprocess import PIPE, Popen
 from typing import List
 
-import six
 from lazr.restful.interface import copy_field, use_template
 from lazr.restful.interfaces import (
     IFieldHTMLRenderer,
@@ -1491,7 +1490,7 @@ def to_DOT_ID(value):
 
     """
     if isinstance(value, bytes):
-        unitext = six.ensure_text(value, encoding="ascii")
+        unitext = value.decode(encoding="ascii")
     else:
         unitext = str(value)
     output = unitext.replace('"', '\\"')
diff --git a/lib/lp/bugs/stories/guided-filebug/xx-bug-reporting-tools.rst b/lib/lp/bugs/stories/guided-filebug/xx-bug-reporting-tools.rst
index 598d95d..5f36cf6 100644
--- a/lib/lp/bugs/stories/guided-filebug/xx-bug-reporting-tools.rst
+++ b/lib/lp/bugs/stories/guided-filebug/xx-bug-reporting-tools.rst
@@ -73,11 +73,9 @@ The most common case will be that the user is sent to the guided
     >>> filebug_url = "http://%s%s"; % (filebug_host, filebug_path)
     >>> contents = str(
     ...     http(
-    ...         six.ensure_binary(
-    ...             "GET %s HTTP/1.1\nHostname: %s\n"
-    ...             "Authorization: Basic test@xxxxxxxxxxxxx:test\n\n"
-    ...             % (filebug_path, filebug_host)
-    ...         )
+    ...         "GET %s HTTP/1.1\nHostname: %s\n"
+    ...         "Authorization: Basic test@xxxxxxxxxxxxx:test\n\n"
+    ...         % (filebug_path, filebug_host)
     ...     )
     ... )
 
diff --git a/lib/lp/code/model/gitref.py b/lib/lp/code/model/gitref.py
index eed9381..60ef93f 100644
--- a/lib/lp/code/model/gitref.py
+++ b/lib/lp/code/model/gitref.py
@@ -14,7 +14,6 @@ from urllib.parse import quote, quote_plus, urlsplit
 
 import pytz
 import requests
-import six
 from lazr.lifecycle.event import ObjectCreatedEvent
 from storm.expr import And, Or
 from storm.locals import DateTime, Int, Not, Reference, Store, Unicode
@@ -406,7 +405,7 @@ class GitRefMixin:
                 memcache_key += ":limit=%s" % limit
             if stop is not None:
                 memcache_key += ":stop=%s" % stop
-            memcache_key = six.ensure_binary(memcache_key)
+            memcache_key = memcache_key.encode()
 
             description = "log information for %s:%s" % (path, start)
             log = memcache_client.get_json(memcache_key, logger, description)
diff --git a/lib/lp/registry/browser/tests/test_distroseries.py b/lib/lp/registry/browser/tests/test_distroseries.py
index fd9a066..f3e256f 100644
--- a/lib/lp/registry/browser/tests/test_distroseries.py
+++ b/lib/lp/registry/browser/tests/test_distroseries.py
@@ -9,7 +9,6 @@ from datetime import timedelta
 from textwrap import TextWrapper
 from urllib.parse import urlencode, urlparse
 
-import six
 import soupmatchers
 from fixtures import FakeLogger
 from lazr.restful.interfaces import IJSONRequestCache
@@ -1022,7 +1021,7 @@ class TestDistroSeriesLocalDiffPerformance(
                     list(prepare_statements(rec2)),
                 )
                 for line in diff:
-                    yield six.ensure_binary("%s\n" % line)
+                    yield ("%s\n" % line).encode()
 
             return statement_diff
 
diff --git a/lib/lp/scripts/tests/test_garbo.py b/lib/lp/scripts/tests/test_garbo.py
index dce4882..eae9163 100644
--- a/lib/lp/scripts/tests/test_garbo.py
+++ b/lib/lp/scripts/tests/test_garbo.py
@@ -14,7 +14,6 @@ from datetime import datetime, timedelta
 from functools import partial
 from textwrap import dedent
 
-import six
 import transaction
 from psycopg2 import IntegrityError
 from pytz import UTC
@@ -1416,10 +1415,7 @@ class TestGarbo(FakeAdapterMixin, TestCaseWithFactory):
         naked_bug.heat_last_updated = old_update
         IPrimaryStore(FeatureFlag).add(
             FeatureFlag(
-                "default",
-                0,
-                "bugs.heat_updates.cutoff",
-                six.ensure_text(cutoff.isoformat()),
+                "default", 0, "bugs.heat_updates.cutoff", cutoff.isoformat()
             )
         )
         transaction.commit()
diff --git a/lib/lp/services/gpg/handler.py b/lib/lp/services/gpg/handler.py
index 821a05d..b00378a 100644
--- a/lib/lp/services/gpg/handler.py
+++ b/lib/lp/services/gpg/handler.py
@@ -23,7 +23,6 @@ from urllib.parse import urlencode
 import gpgme
 import pytz
 import requests
-import six
 from lazr.restful.utils import get_current_browser_request
 from zope.component import getUtility
 from zope.interface import implementer
@@ -472,7 +471,7 @@ class GPGHandler:
             del os.environ["GPG_AGENT_INFO"]
 
         def passphrase_cb(uid_hint, passphrase_info, prev_was_bad, fd):
-            os.write(fd, six.ensure_binary("%s\n" % password))
+            os.write(fd, ("%s\n" % password).encode())
 
         context.passphrase_cb = passphrase_cb
 
diff --git a/lib/lp/services/librarian/client.py b/lib/lp/services/librarian/client.py
index ddf1117..c8a803e 100644
--- a/lib/lp/services/librarian/client.py
+++ b/lib/lp/services/librarian/client.py
@@ -131,7 +131,7 @@ class FileUploadClient:
             self._checkError()
 
     def _sendHeader(self, name, value):
-        self._sendLine(six.ensure_binary("%s: %s" % (name, value)))
+        self._sendLine(("%s: %s" % (name, value)).encode())
 
     def addFile(
         self,
diff --git a/lib/lp/services/librarianserver/tests/test_doc.py b/lib/lp/services/librarianserver/tests/test_doc.py
index b9f7e67..4ae056a 100644
--- a/lib/lp/services/librarianserver/tests/test_doc.py
+++ b/lib/lp/services/librarianserver/tests/test_doc.py
@@ -7,8 +7,6 @@ Run the doctests and pagetests.
 
 import os
 
-import six
-
 from lp.services.librarianserver.libraryprotocol import FileUploadProtocol
 from lp.services.librarianserver.storage import WrongDatabaseError
 from lp.services.testing import build_test_suite
@@ -112,10 +110,7 @@ def upload_request(request):
     server.dataReceived(request.replace(b"\n", b"\r\n"))
 
     # Report on what happened
-    print(
-        "reply: %r"
-        % six.ensure_str(server.transport.bytesWritten.rstrip(b"\r\n"))
-    )
+    print("reply: %r" % server.transport.bytesWritten.rstrip(b"\r\n").decode())
 
     if server.transport.connectionLost:
         print("connection closed")
@@ -124,11 +119,7 @@ def upload_request(request):
     if mockFile is not None and mockFile.stored:
         print(
             "file '%s' stored as %s, contents: %r"
-            % (
-                mockFile.name,
-                mockFile.mimetype,
-                six.ensure_str(mockFile.bytes),
-            )
+            % (mockFile.name, mockFile.mimetype, mockFile.bytes.decode())
         )
 
     # Cleanup: remove the observer.
diff --git a/lib/lp/services/mail/doc/emailauthentication.rst b/lib/lp/services/mail/doc/emailauthentication.rst
index 7ddb3a4..94ba727 100644
--- a/lib/lp/services/mail/doc/emailauthentication.rst
+++ b/lib/lp/services/mail/doc/emailauthentication.rst
@@ -156,7 +156,7 @@ starts failing, Python is probably fixed, so the manual boundary parsing
 hack can be removed.
 
     >>> msg = read_test_message("signed_folded_header.txt")
-    >>> print(six.ensure_str(msg.parsed_bytes))
+    >>> print(msg.parsed_bytes.decode())
     ... # doctest: -NORMALIZE_WHITESPACE
     Date:...
     ...
diff --git a/lib/lp/services/mail/doc/signedmessage.rst b/lib/lp/services/mail/doc/signedmessage.rst
index e93b6d7..a18bb64 100644
--- a/lib/lp/services/mail/doc/signedmessage.rst
+++ b/lib/lp/services/mail/doc/signedmessage.rst
@@ -14,7 +14,7 @@ the attributes are correctly set.
     True
     >>> msg["To"]
     'someone'
-    >>> print(six.ensure_text(msg.parsed_bytes))
+    >>> print(msg.parsed_bytes.decode())
     To: someone
     <BLANKLINE>
     Hello.
diff --git a/lib/lp/services/mail/incoming.py b/lib/lp/services/mail/incoming.py
index 6519976..9f2c990 100644
--- a/lib/lp/services/mail/incoming.py
+++ b/lib/lp/services/mail/incoming.py
@@ -69,20 +69,20 @@ def canonicalise_line_endings(buf):
     >>> b = canonicalise_line_endings(b"\n\nfoo\nbar\rbaz\r\n")
     >>> isinstance(b, bytes)
     True
-    >>> six.ensure_str(b)
-    '\r\n\r\nfoo\r\nbar\r\nbaz\r\n'
+    >>> b
+    b'\r\n\r\nfoo\r\nbar\r\nbaz\r\n'
 
     >>> b = canonicalise_line_endings(b"\r\rfoo\r\nbar\rbaz\n")
     >>> isinstance(b, bytes)
     True
-    >>> six.ensure_str(b)
-    '\r\n\r\nfoo\r\nbar\r\nbaz\r\n'
+    >>> b
+    b'\r\n\r\nfoo\r\nbar\r\nbaz\r\n'
 
     >>> b = canonicalise_line_endings(b"\r\nfoo\r\nbar\nbaz\r")
     >>> isinstance(b, bytes)
     True
-    >>> six.ensure_str(b)
-    '\r\nfoo\r\nbar\r\nbaz\r\n'
+    >>> b
+    b'\r\nfoo\r\nbar\r\nbaz\r\n'
     """
     if non_canonicalised_line_endings.search(buf):
         buf = non_canonicalised_line_endings.sub(b"\r\n", buf)
diff --git a/lib/lp/services/testing/tests/test_parallel.py b/lib/lp/services/testing/tests/test_parallel.py
index 41636b7..0a3739c 100644
--- a/lib/lp/services/testing/tests/test_parallel.py
+++ b/lib/lp/services/testing/tests/test_parallel.py
@@ -8,7 +8,6 @@ import subprocess
 import tempfile
 from textwrap import dedent
 
-import six
 from fixtures import PopenFixture, TestWithFixtures
 from testtools import TestCase, TestResult
 
@@ -106,16 +105,14 @@ class TestUtilities(TestCase, TestWithFixtures):
             return {
                 "stdin": io.BytesIO(),
                 "stdout": io.BytesIO(
-                    six.ensure_binary(
-                        dedent(
-                            """\
+                    dedent(
+                        """\
                     test: quux
                     successful: quux
                     test: glom
                     successful: glom
                     """
-                        )
-                    )
+                    ).encode()
                 ),
             }
 
diff --git a/lib/lp/services/tokens.py b/lib/lp/services/tokens.py
index 551f61e..e4f92cf 100644
--- a/lib/lp/services/tokens.py
+++ b/lib/lp/services/tokens.py
@@ -9,8 +9,6 @@ __all__ = [
 
 import random
 
-import six
-
 
 def create_token(token_length):
     """Create a random token string.
@@ -20,8 +18,7 @@ def create_token(token_length):
     # Since tokens are, in general, user-visible, vowels are not included
     # below to prevent them from having curse/offensive words.
     characters = "0123456789bcdfghjklmnpqrstvwxzBCDFGHJKLMNPQRSTVWXZ"
-    token = "".join(
+    return "".join(
         random.SystemRandom().choice(characters)
         for count in range(token_length)
     )
-    return six.ensure_text(token)
diff --git a/lib/lp/services/utils.py b/lib/lp/services/utils.py
index dfa3f10..495be31 100644
--- a/lib/lp/services/utils.py
+++ b/lib/lp/services/utils.py
@@ -41,7 +41,6 @@ from textwrap import dedent
 from types import FunctionType
 
 import pytz
-import six
 from lazr.enum import BaseItem
 from twisted.python.util import mergeFunctionMetadata
 from zope.security.proxy import isinstance as zope_isinstance
@@ -123,7 +122,7 @@ def value_string(item):
     elif zope_isinstance(item, BaseItem):
         return item.title
     elif zope_isinstance(item, bytes):
-        return six.ensure_text(item)
+        return item.decode()
     else:
         return str(item)
 
diff --git a/lib/lp/services/verification/tests/logintoken.py b/lib/lp/services/verification/tests/logintoken.py
index a45f131..0836d27 100644
--- a/lib/lp/services/verification/tests/logintoken.py
+++ b/lib/lp/services/verification/tests/logintoken.py
@@ -6,8 +6,6 @@
 import email
 import re
 
-import six
-
 
 def get_token_url_from_email(email_msg):
     """Return the logintoken URL contained in the given email message."""
@@ -17,4 +15,4 @@ def get_token_url_from_email(email_msg):
 
 def get_token_url_from_bytes(buf):
     """Return the logintoken URL contained in the given byte string."""
-    return six.ensure_str(re.findall(rb"http.*/token/.*", buf)[0])
+    return re.findall(rb"http.*/token/.*", buf)[0].decode()
diff --git a/lib/lp/services/webapp/escaping.py b/lib/lp/services/webapp/escaping.py
index 5df92a3..ac188d5 100644
--- a/lib/lp/services/webapp/escaping.py
+++ b/lib/lp/services/webapp/escaping.py
@@ -7,7 +7,6 @@ __all__ = [
     "structured",
 ]
 
-import six
 from lazr.restful.utils import get_current_browser_request
 from zope.i18n import Message, translate
 from zope.interface import implementer
@@ -49,7 +48,7 @@ def html_escape(message):
         # first. See bug #54987.
         raw = translate_if_i18n(message)
         if isinstance(raw, bytes):
-            raw = six.ensure_text(raw)
+            raw = raw.decode()
         else:
             raw = str(raw)
         for needle, replacement in HTML_REPLACEMENTS:
@@ -90,7 +89,7 @@ class structured:
     def __init__(self, text, *reps, **kwreps):
         text = translate_if_i18n(text)
         if isinstance(text, bytes):
-            text = six.ensure_text(text)
+            text = text.decode()
         else:
             text = str(text)
         self.text = text
diff --git a/lib/lp/soyuz/stories/ppa/xx-ppa-files.rst b/lib/lp/soyuz/stories/ppa/xx-ppa-files.rst
index 525a1f1..7e0ce09 100644
--- a/lib/lp/soyuz/stories/ppa/xx-ppa-files.rst
+++ b/lib/lp/soyuz/stories/ppa/xx-ppa-files.rst
@@ -305,13 +305,11 @@ The 'No Privileges' user, the PPA owner, can download the DSC file.
 
     >>> print(
     ...     http(
-    ...         six.ensure_binary(
-    ...             r"""
+    ...         r"""
     ... GET %s HTTP/1.1
     ... Authorization: Basic no-priv@xxxxxxxxxxxxx:test
     ... """
-    ...             % (dsc_file_lp_url.replace("http://launchpad.test";, ""))
-    ...         )
+    ...         % (dsc_file_lp_url.replace("http://launchpad.test";, ""))
     ...     )
     ... )
     HTTP/1.1 303 See Other
@@ -333,13 +331,11 @@ Binary files are served via '+files' rather than '+sourcefiles'.
     zope.security.interfaces.Unauthorized
     >>> print(
     ...     http(
-    ...         six.ensure_binary(
-    ...             r"""
+    ...         r"""
     ... GET %s HTTP/1.1
     ... Authorization: Basic no-priv@xxxxxxxxxxxxx:test
     ... """
-    ...             % (deb_file_lp_url.replace("http://launchpad.test";, ""))
-    ...         )
+    ...         % (deb_file_lp_url.replace("http://launchpad.test";, ""))
     ...     )
     ... )
     HTTP/1.1 303 See Other
@@ -390,12 +386,10 @@ binaries across to no-priv's public ppa.
 
     >>> print(
     ...     http(
-    ...         six.ensure_binary(
-    ...             r"""
+    ...         r"""
     ... GET %s HTTP/1.1
     ... """
-    ...             % file_lp_url.replace("http://launchpad.test";, "")
-    ...         )
+    ...         % file_lp_url.replace("http://launchpad.test";, "")
     ...     )
     ... )
     HTTP/1.1 303 See Other
@@ -424,13 +418,11 @@ redirect to the files for the default named PPA.
 
     >>> print(
     ...     http(
-    ...         six.ensure_binary(
-    ...             r"""
+    ...         r"""
     ... GET %s HTTP/1.1
     ... """
-    ...             % file_lp_url_without_ppa_name.replace(
-    ...                 "http://launchpad.test";, ""
-    ...             )
+    ...         % file_lp_url_without_ppa_name.replace(
+    ...             "http://launchpad.test";, ""
     ...         )
     ...     )
     ... )  # noqa
@@ -446,13 +438,11 @@ The same redirection happens for +archive/+build/blah urls:
     ... )
     >>> print(
     ...     http(
-    ...         six.ensure_binary(
-    ...             r"""
+    ...         r"""
     ... GET %s HTTP/1.1
     ... """
-    ...             % buildlog_lp_url_without_ppa_name.replace(
-    ...                 "http://launchpad.test";, ""
-    ...             )
+    ...         % buildlog_lp_url_without_ppa_name.replace(
+    ...             "http://launchpad.test";, ""
     ...         )
     ...     )
     ... )
@@ -515,12 +505,10 @@ LP proxy URL a proper NotFound error is raised.
     http://launchpad.test/~no-priv/+archive/ubuntu/ppa/+sourcefiles/test-pkg/1.0/test-pkg_1.0.dsc
 
     >>> not_found_file = http(
-    ...     six.ensure_binary(
-    ...         r"""
+    ...     r"""
     ... GET %s HTTP/1.1
     ... """
-    ...         % file_lp_url.replace("http://launchpad.test";, "")
-    ...     )
+    ...     % file_lp_url.replace("http://launchpad.test";, "")
     ... )
 
 It results in a 404 response.
diff --git a/lib/lp/testing/gpgkeys/__init__.py b/lib/lp/testing/gpgkeys/__init__.py
index 46b9581..a7cd653 100644
--- a/lib/lp/testing/gpgkeys/__init__.py
+++ b/lib/lp/testing/gpgkeys/__init__.py
@@ -20,7 +20,6 @@ import os
 from io import BytesIO
 
 import gpgme
-import six
 from zope.component import getUtility
 
 from lp.registry.interfaces.gpg import IGPGKeySet
@@ -133,7 +132,7 @@ def decrypt_content(content, password):
     plain = BytesIO()
 
     def passphrase_cb(uid_hint, passphrase_info, prev_was_bad, fd):
-        os.write(fd, six.ensure_binary("%s\n" % password))
+        os.write(fd, ("%s\n" % password).encode())
 
     ctx.passphrase_cb = passphrase_cb
 
diff --git a/lib/lp/translations/doc/poimport-pofile-old-po-imported.rst b/lib/lp/translations/doc/poimport-pofile-old-po-imported.rst
index 059c172..6474ebc 100644
--- a/lib/lp/translations/doc/poimport-pofile-old-po-imported.rst
+++ b/lib/lp/translations/doc/poimport-pofile-old-po-imported.rst
@@ -64,7 +64,7 @@ We create the POFile object where we are going to attach the .po file.
 
 First, we do a valid import.
 
-    >>> pofile_contents = six.ensure_binary(
+    >>> pofile_contents = (
     ...     r"""
     ... msgid ""
     ... msgstr ""
@@ -79,7 +79,7 @@ First, we do a valid import.
     ... msgstr "blah"
     ... """
     ...     % datetime.datetime.now(UTC).isoformat()
-    ... )
+    ... ).encode()
     >>> by_maintainer = False
     >>> entry = translation_import_queue.addOrUpdateEntry(
     ...     pofile.path,
@@ -121,7 +121,7 @@ file we just imported.
 Now, we are going to import a .po file that has a 'PO-Revision-Date'
 field with a date older than a previous .po import.
 
-    >>> pofile_contents = six.ensure_binary(
+    >>> pofile_contents = (
     ...     r"""
     ... msgid ""
     ... msgstr ""
@@ -136,7 +136,7 @@ field with a date older than a previous .po import.
     ... msgstr "blah"
     ... """
     ...     % datetime.datetime.now(UTC).isoformat()
-    ... )
+    ... ).encode()
     >>> by_maintainer = False
     >>> entry = translation_import_queue.addOrUpdateEntry(
     ...     pofile.path,
diff --git a/lib/lp/translations/doc/poimport-pofile-syntax-error.rst b/lib/lp/translations/doc/poimport-pofile-syntax-error.rst
index 86d379b..9e9c10b 100644
--- a/lib/lp/translations/doc/poimport-pofile-syntax-error.rst
+++ b/lib/lp/translations/doc/poimport-pofile-syntax-error.rst
@@ -60,7 +60,7 @@ We create the POFile object where we are going to attach the .po file.
 Let's import a .po file that misses the '"' char after msgstr. That's a
 syntax error.
 
-    >>> pofile_contents = six.ensure_binary(
+    >>> pofile_contents = (
     ...     r"""
     ... msgid ""
     ... msgstr ""
@@ -75,7 +75,7 @@ syntax error.
     ... msgstr blah"
     ... """
     ...     % datetime.datetime.now(UTC).isoformat()
-    ... )
+    ... ).encode()
     >>> by_maintainer = False
     >>> entry = translation_import_queue.addOrUpdateEntry(
     ...     pofile.path,
@@ -213,7 +213,7 @@ In his rush to be the first Sumerian translator for Firefox, Mark
 submits a translation with a nonsensical plurals definition.
 
     >>> pofile = potemplate.newPOFile("sux")
-    >>> pofile_contents = six.ensure_binary(
+    >>> pofile_contents = (
     ...     r"""
     ... msgid ""
     ... msgstr ""
@@ -227,7 +227,7 @@ submits a translation with a nonsensical plurals definition.
     ... msgstr "bar"
     ... """
     ...     % datetime.datetime.now(UTC).isoformat()
-    ... )
+    ... ).encode()
     >>> entry = translation_import_queue.addOrUpdateEntry(
     ...     pofile.path,
     ...     pofile_contents,
@@ -266,7 +266,7 @@ Not enough forms
 Mark mistakenly attempts to import a translation with "zero" plural
 forms.  He receives an email notifying him of a syntax error.
 
-    >>> pofile_contents = six.ensure_binary(
+    >>> pofile_contents = (
     ...     r"""
     ... msgid ""
     ... msgstr ""
@@ -280,7 +280,7 @@ forms.  He receives an email notifying him of a syntax error.
     ... msgstr "bar"
     ... """
     ...     % datetime.datetime.now(UTC).isoformat()
-    ... )
+    ... ).encode()
     >>> entry = translation_import_queue.addOrUpdateEntry(
     ...     pofile.path,
     ...     pofile_contents,
@@ -313,7 +313,7 @@ forms.  He receives an email notifying him of a syntax error.
 On his next attempt, Mark accidentally types a negative number of plural
 forms.  The same error is given.
 
-    >>> pofile_contents = six.ensure_binary(
+    >>> pofile_contents = (
     ...     r"""
     ... msgid ""
     ... msgstr ""
@@ -327,7 +327,7 @@ forms.  The same error is given.
     ... msgstr "bar"
     ... """
     ...     % datetime.datetime.now(UTC).isoformat()
-    ... )
+    ... ).encode()
     >>> entry = translation_import_queue.addOrUpdateEntry(
     ...     pofile.path,
     ...     pofile_contents,
@@ -371,7 +371,7 @@ to get that information corrected if need be.
     >>> pofile = potemplate.newPOFile("ar")
 
     # PO file with nplurals=7, a value we can't handle.
-    >>> pofile_contents = six.ensure_binary(
+    >>> pofile_contents = (
     ...     r"""
     ... msgid ""
     ... msgstr ""
@@ -392,7 +392,7 @@ to get that information corrected if need be.
     ... msgstr[6] "barim %%d"
     ... """
     ...     % datetime.datetime.now(UTC).isoformat()
-    ... )
+    ... ).encode()
     >>> entry = translation_import_queue.addOrUpdateEntry(
     ...     pofile.path,
     ...     pofile_contents,
@@ -444,7 +444,7 @@ Once Mark has checked the language page and corrected the number of
 plural forms, the file imports just fine.
 
     # Same PO file as before, but with nplurals=6.
-    >>> pofile_contents = six.ensure_binary(
+    >>> pofile_contents = (
     ...     r"""
     ... msgid ""
     ... msgstr ""
@@ -464,7 +464,7 @@ plural forms, the file imports just fine.
     ... msgstr[5] "barorum %%d"
     ... """
     ...     % datetime.datetime.now(UTC).isoformat()
-    ... )
+    ... ).encode()
     >>> entry = translation_import_queue.addOrUpdateEntry(
     ...     pofile.path,
     ...     pofile_contents,
diff --git a/lib/lp/translations/doc/poimport.rst b/lib/lp/translations/doc/poimport.rst
index 31b7369..b0a15b3 100644
--- a/lib/lp/translations/doc/poimport.rst
+++ b/lib/lp/translations/doc/poimport.rst
@@ -59,7 +59,7 @@ And this is the POTemplate where the import will be done.
 
 This is the file that'll get imported.
 
-    >>> potemplate_contents = six.ensure_binary(
+    >>> potemplate_contents = (
     ...     r"""
     ... msgid ""
     ... msgstr ""
@@ -92,7 +92,7 @@ This is the file that'll get imported.
     ... msgstr ""
     ... """
     ...     % datetime.datetime.now(UTC).isoformat()
-    ... )  # noqa
+    ... ).encode()  # noqa
 
 We sometimes saw deadlocks as POFile statistics were updated after
 importing a template.  The operation would read all translation messages
@@ -239,7 +239,7 @@ Import With Errors
 Here are the contents of the file we'll be importing. It has some
 validation errors.
 
-    >>> pofile_with_errors = six.ensure_binary(
+    >>> pofile_with_errors = (
     ...     r"""
     ... msgid ""
     ... msgstr ""
@@ -273,7 +273,7 @@ validation errors.
     ... msgstr[3] "We have four! %%d"
     ... """
     ...     % datetime.datetime.now(UTC).isoformat()
-    ... )  # noqa
+    ... ).encode()  # noqa
 
 This is the dbschema that controls the validation of a translation.
 
@@ -456,7 +456,7 @@ instance) and they don't mean that any messages failed to import.
 
 For example, here's a gettext PO file with two headers.
 
-    >>> pofile_with_warning = six.ensure_binary(
+    >>> pofile_with_warning = (
     ...     r"""
     ... msgid ""
     ... msgstr ""
@@ -475,7 +475,7 @@ For example, here's a gettext PO file with two headers.
     ... msgstr "b"
     ... """
     ...     % datetime.datetime.now(UTC).isoformat()
-    ... )  # noqa
+    ... ).encode()  # noqa
     >>> eo_pofile = potemplate.newPOFile("eo")
     >>> warning_entry = translation_import_queue.addOrUpdateEntry(
     ...     "eo.po",
@@ -526,7 +526,7 @@ Import Without Errors
 Now, let's import one without errors. This file changes one translation
 and adds another one.
 
-    >>> pofile_without_errors = six.ensure_binary(
+    >>> pofile_without_errors = (
     ...     r"""
     ... msgid ""
     ... msgstr ""
@@ -547,7 +547,7 @@ and adds another one.
     ... msgstr "helpful@xxxxxxxxxxx"
     ... """
     ...     % datetime.datetime.now(UTC).isoformat()
-    ... )
+    ... ).encode()
     >>> entry = translation_import_queue.addOrUpdateEntry(
     ...     pofile.path,
     ...     pofile_without_errors,
@@ -701,7 +701,7 @@ plural forms).
 
 We'll import a POFile with 3 plural forms into this POFile:
 
-    >>> pofile_with_plurals = six.ensure_binary(
+    >>> pofile_with_plurals = (
     ...     r"""
     ... msgid ""
     ... msgstr ""
@@ -719,7 +719,7 @@ We'll import a POFile with 3 plural forms into this POFile:
     ... msgstr[2] "Third form %%d"
     ... """
     ...     % datetime.datetime.now(UTC).isoformat()
-    ... )  # noqa
+    ... ).encode()  # noqa
 
 We now import this POFile as this language's translation for the source
 package:
diff --git a/lib/lp/translations/doc/rosetta-karma.rst b/lib/lp/translations/doc/rosetta-karma.rst
index 91d255c..457dd91 100644
--- a/lib/lp/translations/doc/rosetta-karma.rst
+++ b/lib/lp/translations/doc/rosetta-karma.rst
@@ -130,7 +130,7 @@ Let's say that we have this .po file to import:
     >>> import datetime
     >>> import pytz
     >>> UTC = pytz.timezone("UTC")
-    >>> pofile_contents = six.ensure_binary(
+    >>> pofile_contents = (
     ...     r"""
     ... msgid ""
     ... msgstr ""
@@ -141,7 +141,7 @@ Let's say that we have this .po file to import:
     ... msgstr "bar"
     ... """
     ...     % datetime.datetime.now(UTC).isoformat()
-    ... )
+    ... ).encode()
     >>> potemplate = POTemplate.get(1)
     >>> pofile = potemplate.getPOFileByLang("es")
 
@@ -215,7 +215,7 @@ Tell the PO file to import from the file data it has.
 Now, the user is going to upload a local edition of the .po file. In this
 case, we will give karma *only* because the translation change.
 
-    >>> pofile_contents = six.ensure_binary(
+    >>> pofile_contents = (
     ...     r"""
     ... msgid ""
     ... msgstr ""
@@ -226,7 +226,7 @@ case, we will give karma *only* because the translation change.
     ... msgstr "bars"
     ... """
     ...     % datetime.datetime.now(UTC).isoformat()
-    ... )
+    ... ).encode()
 
 We attach the new file as not coming from upstream.
 
diff --git a/lib/lp/translations/doc/rosetta-poimport-script.rst b/lib/lp/translations/doc/rosetta-poimport-script.rst
index 4131971..bb4273c 100644
--- a/lib/lp/translations/doc/rosetta-poimport-script.rst
+++ b/lib/lp/translations/doc/rosetta-poimport-script.rst
@@ -32,7 +32,7 @@ the sampledata.
     None
 
     >>> pofile = potemplate.newPOFile("sr")
-    >>> pofile_content = six.ensure_binary(
+    >>> pofile_content = (
     ...     """
     ... msgid ""
     ... msgstr ""
@@ -45,7 +45,7 @@ the sampledata.
     ... msgstr "Bar"
     ... """
     ...     % datetime.datetime.now(UTC).isoformat()
-    ... )
+    ... ).encode()
 
 We clean the import queue.
 
diff --git a/lib/lp/translations/scripts/po_import.py b/lib/lp/translations/scripts/po_import.py
index ddbce5d..e7b2f3e 100644
--- a/lib/lp/translations/scripts/po_import.py
+++ b/lib/lp/translations/scripts/po_import.py
@@ -11,7 +11,6 @@ import sys
 from datetime import datetime, timedelta
 
 import pytz
-import six
 from zope.component import getUtility
 
 from lp.app.interfaces.launchpad import ILaunchpadCelebrities
@@ -79,9 +78,7 @@ class TranslationsImport(LaunchpadCronScript):
 
     def _registerFailure(self, entry, reason, traceback=False, abort=False):
         """Note that a queue entry is unusable in some way."""
-        reason_text = (
-            six.ensure_text(reason) if reason is bytes else str(reason)
-        )
+        reason_text = reason.decode() if reason is bytes else str(reason)
         entry.setStatus(
             RosettaImportStatus.FAILED,
             getUtility(ILaunchpadCelebrities).rosetta_experts,
diff --git a/lib/lp/translations/utilities/tests/test_gettext_po_exporter.py b/lib/lp/translations/utilities/tests/test_gettext_po_exporter.py
index 7e055d7..7780873 100644
--- a/lib/lp/translations/utilities/tests/test_gettext_po_exporter.py
+++ b/lib/lp/translations/utilities/tests/test_gettext_po_exporter.py
@@ -3,7 +3,6 @@
 
 from textwrap import dedent
 
-import six
 from zope.interface.verify import verifyObject
 
 from lp.services.helpers import test_diff
@@ -47,12 +46,11 @@ class GettextPOExporterTestCase(TestCaseWithFactory):
             return
 
         import_lines = [
-            six.ensure_text(line, errors="replace")
-            for line in import_file.split(b"\n")
+            line.decode(errors="replace") for line in import_file.split(b"\n")
         ]
         # Remove X-Launchpad-Export-Date line to prevent time bombs in tests.
         export_lines = [
-            six.ensure_text(line, errors="replace")
+            line.decode(errors="replace")
             for line in export_file.split(b"\n")
             if (
                 not line.startswith(b'"X-Launchpad-Export-Date:')
diff --git a/lib/lp/translations/utilities/tests/test_translation_importer.py b/lib/lp/translations/utilities/tests/test_translation_importer.py
index 03af93c..98925ae 100644
--- a/lib/lp/translations/utilities/tests/test_translation_importer.py
+++ b/lib/lp/translations/utilities/tests/test_translation_importer.py
@@ -5,7 +5,6 @@
 
 from io import BytesIO
 
-import six
 import transaction
 
 from lp.services.log.logger import DevNullLogger
@@ -262,7 +261,7 @@ class TranslationImporterTestCase(TestCaseWithFactory):
             pofile=pofile, potmsgset=potmsgset1
         )
 
-        text = six.ensure_binary(
+        text = (
             """
             msgid ""
             msgstr ""
@@ -275,7 +274,7 @@ class TranslationImporterTestCase(TestCaseWithFactory):
             msgstr "A translation."
         """
             % potmsgset2.msgid_singular.msgid
-        )
+        ).encode()
 
         entry = self.factory.makeTranslationImportQueueEntry(
             "foo.po",