← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] ~cjwatson/launchpad:remove-six-StringIO into launchpad:master

 

Colin Watson has proposed merging ~cjwatson/launchpad:remove-six-StringIO into launchpad:master.

Commit message:
Replace six.StringIO with io.StringIO

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/+git/launchpad/+merge/420640
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of ~cjwatson/launchpad:remove-six-StringIO into launchpad:master.
diff --git a/lib/devscripts/tests/test_sourcecode.py b/lib/devscripts/tests/test_sourcecode.py
index 5d5f429..40f5916 100644
--- a/lib/devscripts/tests/test_sourcecode.py
+++ b/lib/devscripts/tests/test_sourcecode.py
@@ -8,6 +8,7 @@ from __future__ import (
     print_function,
     )
 
+import io
 import os
 import shutil
 import tempfile
@@ -23,8 +24,6 @@ except ImportError:
     from bzrlib.tests import TestCase
     from bzrlib.transport import get_transport
 
-import six
-
 from devscripts import get_launchpad_root
 from devscripts.sourcecode import (
     find_branches,
@@ -38,7 +37,7 @@ class TestParseConfigFile(unittest.TestCase):
     """Tests for the config file parser."""
 
     def makeFile(self, contents):
-        return six.StringIO(contents)
+        return io.StringIO(contents)
 
     def test_empty(self):
         # Parsing an empty config file returns an empty sequence.
diff --git a/lib/lp/archivepublisher/model/ftparchive.py b/lib/lp/archivepublisher/model/ftparchive.py
index 1a5d8af..469b1cb 100644
--- a/lib/lp/archivepublisher/model/ftparchive.py
+++ b/lib/lp/archivepublisher/model/ftparchive.py
@@ -2,11 +2,11 @@
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 from collections import defaultdict
+import io
 import os
 import re
 import time
 
-import six
 from storm.expr import (
     Desc,
     Join,
@@ -771,7 +771,7 @@ class FTPArchiveHandler:
         released series, and in addition we exclude any suite not
         explicitly marked as dirty.
         """
-        apt_config = six.StringIO()
+        apt_config = io.StringIO()
         apt_config.write(CONFIG_HEADER % (self._config.archiveroot,
                                           self._config.overrideroot,
                                           self._config.cacheroot,
@@ -893,7 +893,7 @@ class FTPArchiveHandler:
         except OSError:
             pass
 
-        apt_config = six.StringIO()
+        apt_config = io.StringIO()
         apt_config.write(CONFIG_HEADER % (self._config.archiveroot,
                                           self._config.overrideroot,
                                           self._config.cacheroot,
diff --git a/lib/lp/blueprints/browser/sprint.py b/lib/lp/blueprints/browser/sprint.py
index 95bf105..796306b 100644
--- a/lib/lp/blueprints/browser/sprint.py
+++ b/lib/lp/blueprints/browser/sprint.py
@@ -24,10 +24,10 @@ __all__ = [
 
 from collections import defaultdict
 import csv
+import io
 
 from lazr.restful.utils import smartquote
 import pytz
-import six
 from zope.component import getUtility
 from zope.formlib.widget import CustomWidgetFactory
 from zope.formlib.widgets import TextAreaWidget
@@ -617,7 +617,7 @@ class SprintAttendeesCsvExportView(LaunchpadView):
         self.request.response.setHeader(
             'Content-disposition',
             'attachment; filename=%s-attendees.csv' % self.context.name)
-        output = six.StringIO()
+        output = io.StringIO()
         writer = csv.writer(output)
         writer.writerows(rows)
         return output.getvalue()
diff --git a/lib/lp/blueprints/stories/sprints/xx-sprints.txt b/lib/lp/blueprints/stories/sprints/xx-sprints.txt
index d1e78c7..efabff7 100644
--- a/lib/lp/blueprints/stories/sprints/xx-sprints.txt
+++ b/lib/lp/blueprints/stories/sprints/xx-sprints.txt
@@ -491,7 +491,8 @@ First, we add a couple of IRC nicknames for Carlos.
 The resulting CSV file lists physical attendance correctly.
 
     >>> import csv
-    >>> ubz_csv = list(csv.DictReader(six.StringIO(browser.contents)))
+    >>> import io
+    >>> ubz_csv = list(csv.DictReader(io.StringIO(browser.contents)))
     >>> [(row["Launchpad username"], row["Physically present"])
     ...     for row in ubz_csv]
     [('carlos', 'True'), ('salgado', 'False'), ('name12', 'True')]
@@ -522,7 +523,7 @@ as attending remotely.
     >>> browser.getControl('Register').click()
 
     >>> browser.getLink('Export attendees to CSV').click()
-    >>> ltsp_csv = list(csv.DictReader(six.StringIO(browser.contents)))
+    >>> ltsp_csv = list(csv.DictReader(io.StringIO(browser.contents)))
     >>> [(row["Launchpad username"], row["Physically present"])
     ...     for row in ltsp_csv]
     [('name12', 'False')]
diff --git a/lib/lp/code/model/branchjob.py b/lib/lp/code/model/branchjob.py
index 296521d..876c372 100644
--- a/lib/lp/code/model/branchjob.py
+++ b/lib/lp/code/model/branchjob.py
@@ -707,7 +707,7 @@ class RevisionsAddedJob(BranchJobDerived):
             authors = self.getAuthors(merged_revisions, graph)
             revision_set = RevisionSet()
             rev_authors = revision_set.acquireRevisionAuthors(authors)
-            outf = six.StringIO()
+            outf = io.StringIO()
             pretty_authors = []
             for rev_author in rev_authors.values():
                 if rev_author.person is None:
diff --git a/lib/lp/codehosting/vfs/tests/test_branchfs.py b/lib/lp/codehosting/vfs/tests/test_branchfs.py
index c32130e..471ad03 100644
--- a/lib/lp/codehosting/vfs/tests/test_branchfs.py
+++ b/lib/lp/codehosting/vfs/tests/test_branchfs.py
@@ -3,6 +3,7 @@
 
 """Tests for the branch filesystem."""
 
+import io
 import os
 import re
 import sys
@@ -1016,7 +1017,7 @@ class TestBranchChangedErrorHandling(TestCaseWithTransport, TestCase):
         self.disable_directory_isolation()
 
         # Trap stderr.
-        self.useFixture(MonkeyPatch('sys.stderr', six.StringIO()))
+        self.useFixture(MonkeyPatch('sys.stderr', io.StringIO()))
 
         # To record generated oopsids
         self.generated_oopsids = []
diff --git a/lib/lp/registry/scripts/distributionmirror_prober.py b/lib/lp/registry/scripts/distributionmirror_prober.py
index 00e1389..965b8a6 100644
--- a/lib/lp/registry/scripts/distributionmirror_prober.py
+++ b/lib/lp/registry/scripts/distributionmirror_prober.py
@@ -1125,7 +1125,7 @@ class DistroMirrorProber:
                 continue
 
             probed_mirrors.append(mirror)
-            logfile = six.StringIO()
+            logfile = io.StringIO()
             logfiles[mirror_id] = logfile
             prob_scheduled_calls = probe_function(
                 mirror, logfile, unchecked_keys, self.logger,
diff --git a/lib/lp/registry/tests/test_distributionmirror_prober.py b/lib/lp/registry/tests/test_distributionmirror_prober.py
index 8b9fd22..1decf83 100644
--- a/lib/lp/registry/tests/test_distributionmirror_prober.py
+++ b/lib/lp/registry/tests/test_distributionmirror_prober.py
@@ -5,6 +5,7 @@
 
 from datetime import datetime
 import http.client
+import io
 import logging
 import os
 import re
@@ -13,7 +14,6 @@ from textwrap import dedent
 from fixtures import MockPatchObject
 from lazr.uri import URI
 import responses
-import six
 from testtools.matchers import (
     ContainsDict,
     Equals,
@@ -879,7 +879,7 @@ class TestMirrorCDImageProberCallbacks(TestCaseWithFactory):
         mirror = removeSecurityProxy(
             self.factory.makeMirror(distroseries.distribution))
         callbacks = MirrorCDImageProberCallbacks(
-            mirror, distroseries, 'ubuntu', six.StringIO())
+            mirror, distroseries, 'ubuntu', io.StringIO())
         return callbacks
 
     def getLogger(self):
@@ -980,7 +980,7 @@ class TestArchiveMirrorProberCallbacks(TestCaseWithFactory):
         component = self.factory.makeComponent()
         callbacks = ArchiveMirrorProberCallbacks(
             mirror, distroseries, PackagePublishingPocket.RELEASE,
-            component, 'foo', six.StringIO())
+            component, 'foo', io.StringIO())
         return callbacks
 
     def test_failure_propagation(self):
@@ -1068,7 +1068,7 @@ class TestProbeFunctionSemaphores(TestCase):
         # Note that calling this function won't actually probe any mirrors; we
         # need to call reactor.run() to actually start the probing.
         with default_timeout(15.0):
-            probe_cdimage_mirror(mirror, six.StringIO(), [], logging, 100, 2)
+            probe_cdimage_mirror(mirror, io.StringIO(), [], logging, 100, 2)
         self.assertEqual(0, len(mirror.cdimage_series))
 
     def test_archive_mirror_probe_function(self):
@@ -1104,7 +1104,7 @@ class TestProbeFunctionSemaphores(TestCase):
         mirror2_host = URI(mirror2.base_url).host
         mirror3_host = URI(mirror3.base_url).host
 
-        probe_function(mirror1, six.StringIO(), [], logging, 100, 2)
+        probe_function(mirror1, io.StringIO(), [], logging, 100, 2)
         # Since we have a single mirror to probe we need to have a single
         # DeferredSemaphore with a limit of max_per_host_requests, to ensure we
         # don't issue too many simultaneous connections on that host.
@@ -1115,7 +1115,7 @@ class TestProbeFunctionSemaphores(TestCase):
         # overall number of requests.
         self.assertEqual(multi_lock.overall_lock.limit, max_requests)
 
-        probe_function(mirror2, six.StringIO(), [], logging, 100, 2)
+        probe_function(mirror2, io.StringIO(), [], logging, 100, 2)
         # Now we have two mirrors to probe, but they have the same hostname,
         # so we'll still have a single semaphore in host_semaphores.
         self.assertEqual(mirror2_host, mirror1_host)
@@ -1123,7 +1123,7 @@ class TestProbeFunctionSemaphores(TestCase):
         multi_lock = request_manager.host_locks[mirror2_host]
         self.assertEqual(multi_lock.host_lock.limit, max_per_host_requests)
 
-        probe_function(mirror3, six.StringIO(), [], logging, 100, 2)
+        probe_function(mirror3, io.StringIO(), [], logging, 100, 2)
         # This third mirror is on a separate host, so we'll have a second
         # semaphore added to host_semaphores.
         self.assertTrue(mirror3_host != mirror1_host)
@@ -1135,7 +1135,7 @@ class TestProbeFunctionSemaphores(TestCase):
         # proxy, we'll use the mirror's host as the key to find the semaphore
         # that should be used
         self.pushConfig('launchpad', http_proxy='http://squid.internal:3128/')
-        probe_function(mirror3, six.StringIO(), [], logging, 100, 2)
+        probe_function(mirror3, io.StringIO(), [], logging, 100, 2)
         self.assertEqual(len(request_manager.host_locks), 2)
 
 
@@ -1168,7 +1168,7 @@ class TestLoggingMixin(TestCase):
 
     def test_logMessage_output(self):
         logger = LoggingMixin()
-        logger.log_file = six.StringIO()
+        logger.log_file = io.StringIO()
         logger._getTime = self._fake_gettime
         logger.logMessage("Ubuntu Warty Released")
         logger.log_file.seek(0)
@@ -1179,7 +1179,7 @@ class TestLoggingMixin(TestCase):
 
     def test_logMessage_integration(self):
         logger = LoggingMixin()
-        logger.log_file = six.StringIO()
+        logger.log_file = io.StringIO()
         logger.logMessage("Probing...")
         logger.log_file.seek(0)
         message = logger.log_file.read()
diff --git a/lib/lp/registry/tests/test_prf_finder.py b/lib/lp/registry/tests/test_prf_finder.py
index 983f16e..85889a3 100644
--- a/lib/lp/registry/tests/test_prf_finder.py
+++ b/lib/lp/registry/tests/test_prf_finder.py
@@ -1,13 +1,13 @@
 # Copyright 2009-2018 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
+import io
 import logging
 import os
 import shutil
 import tempfile
 
 import responses
-import six
 from testtools import ExpectedException
 import transaction
 from zope.component import getUtility
@@ -389,7 +389,7 @@ class HandleReleaseTestCase(TestCase):
         # Test that handleRelease() handles the case where a version can't be
         # parsed from the url given.
         ztm = self.layer.txn
-        output = six.StringIO()
+        output = io.StringIO()
         logger = logging.getLogger()
         logger.setLevel(logging.INFO)
         logger.addHandler(logging.StreamHandler(output))
diff --git a/lib/lp/registry/tests/test_prf_walker.py b/lib/lp/registry/tests/test_prf_walker.py
index c0c88d9..678ff6f 100644
--- a/lib/lp/registry/tests/test_prf_walker.py
+++ b/lib/lp/registry/tests/test_prf_walker.py
@@ -3,10 +3,10 @@
 
 """Tests for lp.registry.scripts.productreleasefinder.walker."""
 
+import io
 import logging
 
 import responses
-import six
 
 from lp.registry.scripts.productreleasefinder.walker import (
     combine_url,
@@ -164,7 +164,7 @@ class WalkerBase_walk(TestCase):
             def close(self):
                 pass
 
-        log_output = six.StringIO()
+        log_output = io.StringIO()
         logger = logging.getLogger()
         self.addCleanup(logger.setLevel, logger.level)
         logger.setLevel(logging.DEBUG)
@@ -189,7 +189,7 @@ class WalkerBase_walk(TestCase):
             def close(self):
                 pass
 
-        log_output = six.StringIO()
+        log_output = io.StringIO()
         logger = logging.getLogger()
         self.addCleanup(logger.setLevel, logger.level)
         logger.setLevel(logging.DEBUG)
diff --git a/lib/lp/scripts/tests/test_garbo.py b/lib/lp/scripts/tests/test_garbo.py
index 2f6ff8e..1a26ca7 100644
--- a/lib/lp/scripts/tests/test_garbo.py
+++ b/lib/lp/scripts/tests/test_garbo.py
@@ -11,6 +11,7 @@ from datetime import (
     )
 from functools import partial
 import hashlib
+import io
 import logging
 import re
 from textwrap import dedent
@@ -457,7 +458,7 @@ class TestGarbo(FakeAdapterMixin, TestCaseWithFactory):
         self.runFrequently()
 
         # Capture garbo log output to tests can examine it.
-        self.log_buffer = six.StringIO()
+        self.log_buffer = io.StringIO()
         handler = logging.StreamHandler(self.log_buffer)
         self.log.addHandler(handler)
         self.addDetail('garbo-log', text_content(self.log_buffer.getvalue()))
diff --git a/lib/lp/scripts/utilities/warninghandler.py b/lib/lp/scripts/utilities/warninghandler.py
index c4f795a..3c1ac17 100644
--- a/lib/lp/scripts/utilities/warninghandler.py
+++ b/lib/lp/scripts/utilities/warninghandler.py
@@ -4,10 +4,10 @@
 """Handlers for warnings, to be installed when testing."""
 
 import inspect
+import io
 import sys
 import warnings
 
-import six
 # ViewPageTemplateFile has .filename.
 from zope.browserpage import ViewPageTemplateFile
 from zope.browserpage.simpleviewclass import simple
@@ -153,7 +153,7 @@ def launchpad_showwarning(message, category, filename, lineno, file=None,
                           line=None):
     if file is None:
         file = sys.stderr
-    stream = six.StringIO()
+    stream = io.StringIO()
     old_show_warning(message, category, filename, lineno, stream, line=line)
     warning_message = stream.getvalue()
     important_info = find_important_info()
diff --git a/lib/lp/services/job/tests/test_celeryjob.py b/lib/lp/services/job/tests/test_celeryjob.py
index 9d76ef5..1abc369 100644
--- a/lib/lp/services/job/tests/test_celeryjob.py
+++ b/lib/lp/services/job/tests/test_celeryjob.py
@@ -1,11 +1,11 @@
 # Copyright 2012-2020 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
+import io
 import sys
 from time import sleep
 
 from lazr.jobrunner.bin.clear_queues import clear_queues
-import six
 from testtools.content import text_content
 
 from lp.code.model.branchjob import BranchScanJob
@@ -148,8 +148,8 @@ class TestRunMissingJobs(TestCaseWithFactory):
         try:
             real_stdout = sys.stdout
             real_stderr = sys.stderr
-            sys.stdout = fake_stdout = six.StringIO()
-            sys.stderr = fake_stderr = six.StringIO()
+            sys.stdout = fake_stdout = io.StringIO()
+            sys.stderr = fake_stderr = io.StringIO()
             clear_queues(
                 ['script_name', '-c', 'lp.services.job.celeryconfig',
                  result_queue_name])
diff --git a/lib/lp/services/librarian/tests/test_smoketest.py b/lib/lp/services/librarian/tests/test_smoketest.py
index 4b1a5e0..6291f6a 100644
--- a/lib/lp/services/librarian/tests/test_smoketest.py
+++ b/lib/lp/services/librarian/tests/test_smoketest.py
@@ -7,7 +7,6 @@ from functools import partial
 import io
 
 from fixtures import MockPatch
-import six
 
 from lp.services.librarian.smoketest import (
     do_smoketest,
@@ -63,7 +62,7 @@ class SmokeTestTestCase(TestCaseWithFactory):
                 "lp.services.librarian.smoketest.urlopen", good_urlopen):
             self.assertEqual(
                 do_smoketest(self.fake_librarian, self.fake_librarian,
-                             output=six.StringIO()),
+                             output=io.StringIO()),
                 0)
 
     def test_bad_data(self):
@@ -72,7 +71,7 @@ class SmokeTestTestCase(TestCaseWithFactory):
         with MockPatch("lp.services.librarian.smoketest.urlopen", bad_urlopen):
             self.assertEqual(
                 do_smoketest(self.fake_librarian, self.fake_librarian,
-                             output=six.StringIO()),
+                             output=io.StringIO()),
                 1)
 
     def test_exception(self):
@@ -83,7 +82,7 @@ class SmokeTestTestCase(TestCaseWithFactory):
                 "lp.services.librarian.smoketest.urlopen", error_urlopen):
             self.assertEqual(
                 do_smoketest(self.fake_librarian, self.fake_librarian,
-                             output=six.StringIO()),
+                             output=io.StringIO()),
                 1)
 
     def test_explosive_errors(self):
@@ -96,4 +95,4 @@ class SmokeTestTestCase(TestCaseWithFactory):
                 self.assertRaises(
                     exception,
                     do_smoketest, self.fake_librarian, self.fake_librarian,
-                    output=six.StringIO())
+                    output=io.StringIO())
diff --git a/lib/lp/services/log/logger.py b/lib/lp/services/log/logger.py
index 0a15759..6e27b98 100644
--- a/lib/lp/services/log/logger.py
+++ b/lib/lp/services/log/logger.py
@@ -11,12 +11,11 @@ __all__ = [
     'PrefixFilter',
     ]
 
+import io
 import logging
 import sys
 import traceback
 
-import six
-
 from lp.services.log import loglevels
 
 
@@ -196,7 +195,7 @@ class BufferLogger(FakeLogger):
     # service.
 
     def __init__(self):
-        super().__init__(six.StringIO())
+        super().__init__(io.StringIO())
 
     def getLogBuffer(self):
         """Return the existing log messages."""
@@ -204,7 +203,7 @@ class BufferLogger(FakeLogger):
 
     def clearLogBuffer(self):
         """Clear out the existing log messages."""
-        self.output_file = six.StringIO()
+        self.output_file = io.StringIO()
 
     def getLogBufferAndClear(self):
         """Return the existing log messages and clear the buffer."""
diff --git a/lib/lp/services/mail/tests/test_dkim.py b/lib/lp/services/mail/tests/test_dkim.py
index abc9fa4..b0e2704 100644
--- a/lib/lp/services/mail/tests/test_dkim.py
+++ b/lib/lp/services/mail/tests/test_dkim.py
@@ -3,6 +3,7 @@
 
 """Test DKIM-signed messages"""
 
+import io
 import logging
 
 import dkim
@@ -68,7 +69,7 @@ class TestDKIM(TestCaseWithFactory):
     def setUp(self):
         # Login with admin roles as we aren't testing access here.
         TestCaseWithFactory.setUp(self, 'admin@xxxxxxxxxxxxx')
-        self._log_output = six.StringIO()
+        self._log_output = io.StringIO()
         handler = logging.StreamHandler(self._log_output)
         self.logger = logging.getLogger('mail-authenticate-dkim')
         self.logger.addHandler(handler)
diff --git a/lib/lp/services/profile/profile.py b/lib/lp/services/profile/profile.py
index cce5f39..ddd7496 100644
--- a/lib/lp/services/profile/profile.py
+++ b/lib/lp/services/profile/profile.py
@@ -13,6 +13,7 @@ from cProfile import Profile
 from datetime import datetime
 from functools import partial
 import heapq
+import io
 import os
 import pstats
 import re
@@ -21,7 +22,6 @@ import threading
 
 from breezy import lsprof
 import oops_datedir_repo.serializer_rfc822
-import six
 from zope.component import (
     adapter,
     getUtility,
@@ -344,7 +344,7 @@ def end_request(event):
             prof_stats.strip_dirs()
             for name in ('time', 'cumulative', 'calls'):
                 prof_stats.sort(name)
-                f = six.StringIO()
+                f = io.StringIO()
                 prof_stats.pprint(file=f)
                 template_context[name] = f.getvalue()
         template_context['profile_count'] = prof_stats.count
diff --git a/lib/lp/services/tests/test_looptuner.py b/lib/lp/services/tests/test_looptuner.py
index 2b34a67..498f74c 100644
--- a/lib/lp/services/tests/test_looptuner.py
+++ b/lib/lp/services/tests/test_looptuner.py
@@ -6,7 +6,8 @@
 These are the edge test cases that don't belong in the doctest.
 """
 
-import six
+import io
+
 from zope.interface import implementer
 
 from lp.services.log.logger import FakeLogger
@@ -57,7 +58,7 @@ class TestSomething(TestCase):
 
         Exception from cleanup raised.
         """
-        log_file = six.StringIO()
+        log_file = io.StringIO()
         loop = FailingLoop(fail_cleanup=True)
         tuner = LoopTuner(loop, 5, log=FakeLogger(log_file))
         self.assertRaises(CleanupException, tuner.run)
@@ -69,7 +70,7 @@ class TestSomething(TestCase):
         Exception from cleanup is logged.
         Original exception from main task is raised.
         """
-        log_file = six.StringIO()
+        log_file = io.StringIO()
         loop = FailingLoop(fail_main=True, fail_cleanup=True)
         tuner = LoopTuner(loop, 5, log=FakeLogger(log_file))
         self.assertRaises(MainException, tuner.run)
diff --git a/lib/lp/services/tests/test_stacktrace.py b/lib/lp/services/tests/test_stacktrace.py
index a702c07..aa76758 100644
--- a/lib/lp/services/tests/test_stacktrace.py
+++ b/lib/lp/services/tests/test_stacktrace.py
@@ -3,10 +3,10 @@
 
 """Test the stacktrace module."""
 
+import io
 import sys
 
 from fixtures import MonkeyPatch
-import six
 
 from lp.services import stacktrace
 from lp.testing import TestCase
@@ -149,7 +149,7 @@ class TestStacktrace(TestCase):
     def test_get_frame_data_supplement_bad_getInfo_with_traceback(self):
         def boo_hiss():
             raise ValueError()
-        stderr = six.StringIO()
+        stderr = io.StringIO()
         self.assertFalse(stacktrace.DEBUG_EXCEPTION_FORMATTER)
         stacktrace.DEBUG_EXCEPTION_FORMATTER = True
         try:
@@ -276,7 +276,7 @@ class TestStacktrace(TestCase):
     def test_format_list_extra_errors(self):
         extracted = stacktrace.extract_stack(get_frame(supplement=dict()))
         extracted[-1][-2]['warnings'] = object()  # This should never happen.
-        stderr = six.StringIO()
+        stderr = io.StringIO()
         self.assertFalse(stacktrace.DEBUG_EXCEPTION_FORMATTER)
         stacktrace.DEBUG_EXCEPTION_FORMATTER = True
         try:
@@ -289,30 +289,30 @@ class TestStacktrace(TestCase):
 
     def test_print_list_default(self):
         extracted = stacktrace.extract_stack(get_frame())
-        stderr = six.StringIO()
+        stderr = io.StringIO()
         with MonkeyPatch('sys.stderr', stderr):
             stacktrace.print_list(extracted)
         self.assertEndsWith(stderr.getvalue(), 'return sys._getframe()\n')
 
     def test_print_list_file(self):
         extracted = stacktrace.extract_stack(get_frame())
-        f = six.StringIO()
+        f = io.StringIO()
         stacktrace.print_list(extracted, file=f)
         self.assertEndsWith(f.getvalue(), 'return sys._getframe()\n')
 
     def test_print_stack_default(self):
-        stderr = six.StringIO()
+        stderr = io.StringIO()
         with MonkeyPatch('sys.stderr', stderr):
             stacktrace.print_stack()
         self.assertEndsWith(stderr.getvalue(), 'stacktrace.print_stack()\n')
 
     def test_print_stack_options(self):
-        f = six.StringIO()
+        f = io.StringIO()
         frame = get_frame()
         stacktrace.print_stack(f=frame, limit=100, file=f)
         self.assertEndsWith(f.getvalue(), 'return sys._getframe()\n')
         self.assertTrue(len(f.getvalue().split('\n')) > 4)
-        f = six.StringIO()
+        f = io.StringIO()
         stacktrace.print_stack(f=frame, limit=2, file=f)
         self.assertEqual(4, len(f.getvalue().strip().split('\n')))
         self.assertEndsWith(f.getvalue(), 'return sys._getframe()\n')
diff --git a/lib/lp/services/twistedsupport/__init__.py b/lib/lp/services/twistedsupport/__init__.py
index cf0eb1b..040686c 100644
--- a/lib/lp/services/twistedsupport/__init__.py
+++ b/lib/lp/services/twistedsupport/__init__.py
@@ -14,6 +14,7 @@ __all__ = [
 
 
 import functools
+import io
 from signal import (
     getsignal,
     SIGCHLD,
@@ -21,7 +22,6 @@ from signal import (
     )
 import sys
 
-import six
 from twisted.internet import (
     defer,
     reactor as default_reactor,
@@ -62,7 +62,7 @@ def suppress_stderr(function):
     @functools.wraps(function)
     def wrapper(*arguments, **keyword_arguments):
         saved_stderr = sys.stderr
-        ignored_stream = six.StringIO()
+        ignored_stream = io.StringIO()
         sys.stderr = ignored_stream
         d = defer.maybeDeferred(function, *arguments, **keyword_arguments)
         return d.addBoth(set_stderr, saved_stderr)
diff --git a/lib/lp/services/webapp/opstats.py b/lib/lp/services/webapp/opstats.py
index 4450db5..466d3bd 100644
--- a/lib/lp/services/webapp/opstats.py
+++ b/lib/lp/services/webapp/opstats.py
@@ -5,10 +5,9 @@
 
 __all__ = ["OpStats"]
 
+import io
 from time import time
 
-import six
-
 from lp.services.webapp import LaunchpadXMLRPCView
 
 
@@ -65,7 +64,7 @@ class OpStats(LaunchpadXMLRPCView):
 
     def __call__(self):
         now = time()
-        out = six.StringIO()
+        out = io.StringIO()
         for stat_key in sorted(OpStats.stats.keys()):
             print('%s:%d@%d' % (
                     # Make keys more cricket friendly
diff --git a/lib/lp/services/webapp/tests/test_statementtracer.py b/lib/lp/services/webapp/tests/test_statementtracer.py
index a78ef8a..476e69e 100644
--- a/lib/lp/services/webapp/tests/test_statementtracer.py
+++ b/lib/lp/services/webapp/tests/test_statementtracer.py
@@ -4,10 +4,10 @@
 """Tests for the LaunchpadStatementTracer."""
 
 from contextlib import contextmanager
+import io
 import sys
 
 from lazr.restful.utils import get_current_browser_request
-import six
 
 from lp.services.osutils import override_environ
 from lp.services.timeline.requesttimeline import get_request_timeline
@@ -23,7 +23,7 @@ from lp.testing.layers import DatabaseFunctionalLayer
 
 @contextmanager
 def stdout():
-    file = six.StringIO()
+    file = io.StringIO()
     original = sys.stdout
     sys.stdout = file
     try:
@@ -34,7 +34,7 @@ def stdout():
 
 @contextmanager
 def stderr():
-    file = six.StringIO()
+    file = io.StringIO()
     original = sys.stderr
     sys.stderr = file
     try:
diff --git a/lib/lp/soyuz/scripts/tests/test_ppareport.py b/lib/lp/soyuz/scripts/tests/test_ppareport.py
index a515216..f82eb1f 100644
--- a/lib/lp/soyuz/scripts/tests/test_ppareport.py
+++ b/lib/lp/soyuz/scripts/tests/test_ppareport.py
@@ -3,13 +3,12 @@
 
 """Tests for `PPAReportScript.` """
 
+import io
 import os
 import shutil
 import tempfile
 import unittest
 
-import six
-
 from lp.services.config import config
 from lp.services.log.logger import BufferLogger
 from lp.services.scripts.base import LaunchpadScriptFailure
@@ -70,7 +69,7 @@ class TestPPAReport(unittest.TestCase):
         if output is None:
 
             def set_test_output():
-                reporter.output = six.StringIO()
+                reporter.output = io.StringIO()
             reporter.setOutput = set_test_output
 
             reporter.closeOutput = FakeMethod()
diff --git a/lib/lp/soyuz/wsgi/tests/test_archiveauth.py b/lib/lp/soyuz/wsgi/tests/test_archiveauth.py
index fe2088f..94ebc77 100644
--- a/lib/lp/soyuz/wsgi/tests/test_archiveauth.py
+++ b/lib/lp/soyuz/wsgi/tests/test_archiveauth.py
@@ -4,12 +4,12 @@
 """Tests for the WSGI archive authorisation provider."""
 
 import crypt
+import io
 import os.path
 import subprocess
 import time
 
 from fixtures import MonkeyPatch
-import six
 import transaction
 
 from lp.services.config import config
@@ -39,7 +39,7 @@ class TestWSGIArchiveAuth(TestCaseWithFactory):
         self.resetLog()
 
     def resetLog(self):
-        self.wsgi_errors = six.StringIO()
+        self.wsgi_errors = io.StringIO()
 
     def assertLogs(self, message):
         self.assertEqual(
diff --git a/lib/lp/testing/__init__.py b/lib/lp/testing/__init__.py
index de3cfcf..bfc08cc 100644
--- a/lib/lp/testing/__init__.py
+++ b/lib/lp/testing/__init__.py
@@ -352,7 +352,7 @@ class StormStatementRecorder:
         stop_sql_logging()
 
     def __str__(self):
-        out = six.StringIO()
+        out = io.StringIO()
         print_queries(self.query_data, file=out)
         return out.getvalue()
 
@@ -665,7 +665,7 @@ class TestCase(testtools.TestCase, fixtures.TestWithFixtures):
     @contextmanager
     def expectedLog(self, regex):
         """Expect a log to be written that matches the regex."""
-        output = six.StringIO()
+        output = io.StringIO()
         handler = logging.StreamHandler(output)
         logger = logging.getLogger()
         logger.addHandler(handler)
diff --git a/lib/lp/testing/fixture.py b/lib/lp/testing/fixture.py
index 0a26c57..9effd7f 100644
--- a/lib/lp/testing/fixture.py
+++ b/lib/lp/testing/fixture.py
@@ -18,6 +18,7 @@ __all__ = [
     ]
 
 from configparser import ConfigParser
+import io
 import os.path
 import socket
 import time
@@ -32,7 +33,6 @@ from lazr.restful.utils import get_current_browser_request
 import oops
 import oops_amqp
 import pgbouncer.fixture
-import six
 from zope.component import (
     adapter,
     getGlobalSiteManager,
@@ -271,8 +271,8 @@ class CapturedOutput(Fixture):
 
     def __init__(self):
         super().__init__()
-        self.stdout = six.StringIO()
-        self.stderr = six.StringIO()
+        self.stdout = io.StringIO()
+        self.stderr = io.StringIO()
 
     def _setUp(self):
         self.useFixture(MonkeyPatch('sys.stdout', self.stdout))
diff --git a/lib/lp/translations/scripts/po_export_queue.py b/lib/lp/translations/scripts/po_export_queue.py
index 2ee4434..c3befa8 100644
--- a/lib/lp/translations/scripts/po_export_queue.py
+++ b/lib/lp/translations/scripts/po_export_queue.py
@@ -6,11 +6,11 @@ __all__ = [
     'process_queue',
     ]
 
+import io
 import os
 import traceback
 
 import psycopg2
-import six
 from zope.component import (
     getAdapter,
     getUtility,
@@ -344,7 +344,7 @@ class ExportResult:
     def addFailure(self):
         """Store an exception that broke the export."""
         # Get the trace back that produced this failure.
-        exception = six.StringIO()
+        exception = io.StringIO()
         traceback.print_exc(file=exception)
         exception.seek(0)
         # And store it.