← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] ~cjwatson/launchpad:pyupgrade-py3-testing into launchpad:master

 

Colin Watson has proposed merging ~cjwatson/launchpad:pyupgrade-py3-testing into launchpad:master.

Commit message:
lp.{testing,testopenid,tests}: Apply "pyupgrade --py3-plus"

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/+git/launchpad/+merge/413954
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of ~cjwatson/launchpad:pyupgrade-py3-testing into launchpad:master.
diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs
index 7b39f1f..83958cd 100644
--- a/.git-blame-ignore-revs
+++ b/.git-blame-ignore-revs
@@ -44,3 +44,5 @@ f3f15787ebabe305fbf3e3ae6c0fd8ca7dfb4465
 2cda038936743a2f9271953c23b9fcc98968db63
 # apply pyupgrade --py3-plus to lp.soyuz
 21fb5364d9371c0ad2edf41fa6920c4e16ead2b0
+# apply pyupgrade --py3-plus to lp.{testing,testopenid,tests}
+9621eff0fd58287b254fb228c11948fca85d547c
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index d969ed2..cc22ee7 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -57,6 +57,9 @@ repos:
             |services
             |snappy
             |soyuz
+            |testing
+            |testopenid
+            |tests
           )/
 -   repo: https://github.com/PyCQA/isort
     rev: 5.9.2
diff --git a/lib/lp/testing/__init__.py b/lib/lp/testing/__init__.py
index 24c4eea..de3cfcf 100644
--- a/lib/lp/testing/__init__.py
+++ b/lib/lp/testing/__init__.py
@@ -681,7 +681,7 @@ class TestCase(testtools.TestCase, fixtures.TestWithFixtures):
         The config values will be restored during test tearDown.
         """
         name = self.factory.getUniqueString()
-        body = '\n'.join("%s: %s" % (k, v) for k, v in six.iteritems(kwargs))
+        body = '\n'.join("%s: %s" % (k, v) for k, v in kwargs.items())
         config.push(name, "\n[%s]\n%s\n" % (section, body))
         self.addCleanup(config.pop, name)
 
@@ -703,7 +703,7 @@ class TestCase(testtools.TestCase, fixtures.TestWithFixtures):
             self.addDetail('librarian-log', content)
 
     def setUp(self):
-        super(TestCase, self).setUp()
+        super().setUp()
         # Circular imports.
         from lp.testing.factory import ObjectFactory
         from lp.testing.layers import LibrarianLayer
@@ -822,7 +822,7 @@ class TestCase(testtools.TestCase, fixtures.TestWithFixtures):
 class TestCaseWithFactory(TestCase):
 
     def setUp(self, user=ANONYMOUS):
-        super(TestCaseWithFactory, self).setUp()
+        super().setUp()
         login(user)
         self.addCleanup(logout)
         from lp.testing.factory import LaunchpadObjectFactory
@@ -870,7 +870,7 @@ class TestCaseWithFactory(TestCase):
         :param branch_url: The URL to create the branch at.
         :param format: The format of branch to create.
         """
-        if format is not None and isinstance(format, six.string_types):
+        if format is not None and isinstance(format, str):
             format = format_registry.get(format)()
         return ControlDir.create_branch_convenience(branch_url, format=format)
 
@@ -958,7 +958,7 @@ class BrowserTestCase(TestCaseWithFactory):
 
     def setUp(self):
         """Provide useful defaults."""
-        super(BrowserTestCase, self).setUp()
+        super().setUp()
         self.user = self.factory.makePerson()
 
     def getViewBrowser(self, context, view_name=None, no_login=False,
@@ -1010,7 +1010,7 @@ class WebServiceTestCase(TestCaseWithFactory):
         return AppServerLayer
 
     def setUp(self):
-        super(WebServiceTestCase, self).setUp()
+        super().setUp()
         self.ws_version = 'devel'
         self.service = self.factory.makeLaunchpadService(
             version=self.ws_version)
@@ -1057,14 +1057,14 @@ class AbstractYUITestCase(TestCase):
             methodName = self._testMethodName
         else:
             assert methodName == self._testMethodName
-        super(AbstractYUITestCase, self).__init__(methodName)
+        super().__init__(methodName)
 
     def id(self):
         """Return an ID for this test based on the file path."""
         return os.path.relpath(self.test_path, config.root)
 
     def setUp(self):
-        super(AbstractYUITestCase, self).setUp()
+        super().setUp()
         # html5browser imports from the gir/pygtk stack which causes
         # twisted tests to break because of gtk's initialize.
         from lp.testing import html5browser
@@ -1227,8 +1227,7 @@ class RunIsolatedTest(testtools.RunTest):
             fdwrite = os.fdopen(pwrite, 'wb', 1)
             # Send results to the subunit stream client so that the parent
             # process can obtain the result.
-            super(RunIsolatedTest, self).run(
-                subunit.TestProtocolClient(fdwrite))
+            super().run(subunit.TestProtocolClient(fdwrite))
             fdwrite.flush()
             # See note above about flushing.
             sys.__stdout__.flush()
@@ -1373,7 +1372,7 @@ def normalize_whitespace(string):
     # whitespace is roughly 6 times faster than using an uncompiled
     # regex (for the expression \s+), and 4 times faster than a
     # compiled regex.
-    joiner = b" " if isinstance(string, bytes) else u" "
+    joiner = b" " if isinstance(string, bytes) else " "
     return joiner.join(string.split())
 
 
@@ -1403,7 +1402,7 @@ def map_branch_contents(branch):
     return contents
 
 
-def set_feature_flag(name, value, scope=u'default', priority=1):
+def set_feature_flag(name, value, scope='default', priority=1):
     """Set a feature flag to the specified value.
 
     In order to access the flag, use the feature_flags context manager or
@@ -1442,13 +1441,13 @@ def monkey_patch(context, **kwargs):
     """
     old_values = {}
     not_set = object()
-    for name, value in six.iteritems(kwargs):
+    for name, value in kwargs.items():
         old_values[name] = getattr(context, name, not_set)
         setattr(context, name, value)
     try:
         yield
     finally:
-        for name, value in six.iteritems(old_values):
+        for name, value in old_values.items():
             if value is not_set:
                 delattr(context, name)
             else:
@@ -1472,13 +1471,12 @@ class ExpectedException(TTExpectedException):
     """An ExpectedException that provides access to the caught exception."""
 
     def __init__(self, exc_type, value_re):
-        super(ExpectedException, self).__init__(exc_type, value_re)
+        super().__init__(exc_type, value_re)
         self.caught_exc = None
 
     def __exit__(self, exc_type, exc_value, traceback):
         self.caught_exc = exc_value
-        return super(ExpectedException, self).__exit__(
-            exc_type, exc_value, traceback)
+        return super().__exit__(exc_type, exc_value, traceback)
 
 
 def extract_lp_cache(text):
diff --git a/lib/lp/testing/_login.py b/lib/lp/testing/_login.py
index 439b8b0..a90b3ec 100644
--- a/lib/lp/testing/_login.py
+++ b/lib/lp/testing/_login.py
@@ -21,7 +21,6 @@ __all__ = [
 
 from contextlib import contextmanager
 
-import six
 from zope.component import getUtility
 from zope.security.management import (
     endInteraction,
@@ -69,7 +68,7 @@ def login(email, participation=None):
     setPrincipal(), otherwise it must allow setting its principal attribute.
     """
 
-    if not isinstance(email, six.string_types):
+    if not isinstance(email, str):
         raise ValueError("Expected email parameter to be a string.")
     participation = _test_login_impl(participation)
     setupInteractionByEmail(email, participation)
diff --git a/lib/lp/testing/_webservice.py b/lib/lp/testing/_webservice.py
index 4c9d1b6..c705f68 100644
--- a/lib/lp/testing/_webservice.py
+++ b/lib/lp/testing/_webservice.py
@@ -58,15 +58,15 @@ def oauth_access_token_for(consumer_name, person, permission, context=None):
 
     :return: A tuple of an OAuthAccessToken object and its secret.
     """
-    if isinstance(person, six.string_types):
+    if isinstance(person, str):
         # Look up a person by name.
         person = getUtility(IPersonSet).getByName(person)
-    if isinstance(context, six.string_types):
+    if isinstance(context, str):
         # Turn an OAuth context string into the corresponding object.
         # Avoid an import loop by importing from launchpad.browser here.
         from lp.services.oauth.browser import lookup_oauth_context
         context = lookup_oauth_context(context)
-    if isinstance(permission, six.string_types):
+    if isinstance(permission, str):
         # Look up a permission by its token string.
         permission = OAuthPermission.items[permission]
 
diff --git a/lib/lp/testing/browser.py b/lib/lp/testing/browser.py
index a315362..ed2f222 100644
--- a/lib/lp/testing/browser.py
+++ b/lib/lp/testing/browser.py
@@ -49,7 +49,7 @@ class Browser(_Browser):
             wsgi_app = AuthorizationMiddleware(
                 TransactionMiddleware(
                     TransparentProxy(client=client)))
-        super(Browser, self).__init__(url=url, wsgi_app=wsgi_app)
+        super().__init__(url=url, wsgi_app=wsgi_app)
 
     @property
     def vhost(self):
diff --git a/lib/lp/testing/factory.py b/lib/lp/testing/factory.py
index 05b92c4..7ba28f6 100644
--- a/lib/lp/testing/factory.py
+++ b/lib/lp/testing/factory.py
@@ -454,7 +454,7 @@ class ObjectFactory(metaclass=AutoDecorate(default_master_store)):
             don't care.
         :return: A hexadecimal string, with 'a'-'f' in lower case.
         """
-        hex_number = u'%x' % self.getUniqueInteger()
+        hex_number = '%x' % self.getUniqueInteger()
         if digits is not None:
             hex_number = hex_number.zfill(digits)
         return hex_number
@@ -634,7 +634,7 @@ class BareLaunchpadObjectFactory(ObjectFactory):
         if account_status == AccountStatus.PLACEHOLDER:
             # Placeholder people are pretty special, so just create and
             # bail out.
-            openid = self.getUniqueUnicode(u'%s-openid' % name)
+            openid = self.getUniqueUnicode('%s-openid' % name)
             person = getUtility(IPersonSet).createPlaceholderPerson(
                 openid, name)
             return person
@@ -800,7 +800,7 @@ class BareLaunchpadObjectFactory(ObjectFactory):
         """
         if owner is None:
             owner = self.makePerson()
-        elif isinstance(owner, six.string_types):
+        elif isinstance(owner, str):
             owner = getUtility(IPersonSet).getByName(owner)
         else:
             pass
@@ -1097,7 +1097,7 @@ class BareLaunchpadObjectFactory(ObjectFactory):
         if time_starts is None:
             time_starts = datetime(2009, 1, 1, tzinfo=pytz.UTC)
         time_ends = time_starts + timedelta(days=1)
-        time_zone = u'UTC'
+        time_zone = 'UTC'
         summary = self.getUniqueUnicode('summary')
         return getUtility(ISprintSet).new(
             owner=owner, name=name, title=title, time_zone=time_zone,
@@ -1195,7 +1195,7 @@ class BareLaunchpadObjectFactory(ObjectFactory):
         else:
             make_sourcepackagename = (
                 sourcepackagename is None or
-                isinstance(sourcepackagename, six.text_type))
+                isinstance(sourcepackagename, str))
             if make_sourcepackagename:
                 sourcepackagename = self.makeSourcePackageName(
                     sourcepackagename)
@@ -1613,7 +1613,7 @@ class BareLaunchpadObjectFactory(ObjectFactory):
             return ProxyFactory(
                 Diff.fromFile(BytesIO(diff_text), len(diff_text)))
 
-    def makePreviewDiff(self, conflicts=u'', merge_proposal=None,
+    def makePreviewDiff(self, conflicts='', merge_proposal=None,
                         date_created=None, size='small', git=False):
         diff = self.makeDiff(size)
         if merge_proposal is None:
@@ -1804,9 +1804,9 @@ class BareLaunchpadObjectFactory(ObjectFactory):
             paths = [self.getUniqueUnicode('refs/heads/path')]
         refs_info = {
             path: {
-                u"sha1": six.ensure_text(
+                "sha1": six.ensure_text(
                     hashlib.sha1(path.encode('utf-8')).hexdigest()),
-                u"type": GitObjectType.COMMIT,
+                "type": GitObjectType.COMMIT,
                 }
             for path in paths}
         refs_by_path = {
@@ -1823,7 +1823,7 @@ class BareLaunchpadObjectFactory(ObjectFactory):
             path = self.getUniqueUnicode('refs/heads/path')
         return getUtility(IGitRefRemoteSet).new(repository_url, path)
 
-    def makeGitRule(self, repository=None, ref_pattern=u"refs/heads/*",
+    def makeGitRule(self, repository=None, ref_pattern="refs/heads/*",
                     creator=None, position=None, **repository_kwargs):
         """Create a Git repository access rule."""
         if repository is None:
@@ -2128,7 +2128,7 @@ class BareLaunchpadObjectFactory(ObjectFactory):
         """
         if bug is None:
             bug = self.makeBug()
-        elif isinstance(bug, (six.integer_types, six.string_types)):
+        elif isinstance(bug, (int, str)):
             bug = getUtility(IBugSet).getByNameOrID(str(bug))
         if owner is None:
             owner = self.makePerson()
@@ -2163,7 +2163,7 @@ class BareLaunchpadObjectFactory(ObjectFactory):
         """
         if bug is None:
             bug = self.makeBug()
-        elif isinstance(bug, (six.integer_types, six.string_types)):
+        elif isinstance(bug, (int, str)):
             bug = getUtility(IBugSet).getByNameOrID(str(bug))
         if owner is None:
             owner = self.makePerson()
@@ -2362,7 +2362,7 @@ class BareLaunchpadObjectFactory(ObjectFactory):
                                   status=SpecificationWorkItemStatus.TODO,
                                   sequence=None):
         if title is None:
-            title = self.getUniqueString(u'title')
+            title = self.getUniqueString('title')
         if specification is None:
             product = None
             distribution = None
@@ -2510,7 +2510,7 @@ class BareLaunchpadObjectFactory(ObjectFactory):
             spn = self.getUniqueString()
         changelog = ''
         for version in versions:
-            entry = dedent(u'''\
+            entry = dedent('''\
             %s (%s) unstable; urgency=low
 
               * %s.
@@ -2936,7 +2936,7 @@ class BareLaunchpadObjectFactory(ObjectFactory):
         """
         if purpose is None:
             purpose = ArchivePurpose.PPA
-        elif isinstance(purpose, six.string_types):
+        elif isinstance(purpose, str):
             purpose = ArchivePurpose.items[purpose.upper()]
 
         if distribution is None:
@@ -3616,8 +3616,7 @@ class BareLaunchpadObjectFactory(ObjectFactory):
             SourcePackagePublishingHistory.
         """
         # Make sure we have a real sourcepackagename object.
-        if (sourcepackagename is None or
-            isinstance(sourcepackagename, six.string_types)):
+        if sourcepackagename is None or isinstance(sourcepackagename, str):
             sourcepackagename = self.getOrMakeSourcePackageName(
                 sourcepackagename)
         if distroseries is None:
@@ -3776,17 +3775,16 @@ class BareLaunchpadObjectFactory(ObjectFactory):
         if archive is None:
             archive = distroseries.main_archive
 
-        if (sourcepackagename is None or
-            isinstance(sourcepackagename, six.string_types)):
+        if sourcepackagename is None or isinstance(sourcepackagename, str):
             sourcepackagename = self.getOrMakeSourcePackageName(
                 sourcepackagename)
 
-        if (component is None or isinstance(component, six.string_types)):
+        if (component is None or isinstance(component, str)):
             component = self.makeComponent(component)
 
         if urgency is None:
             urgency = self.getAnySourcePackageUrgency()
-        elif isinstance(urgency, six.string_types):
+        elif isinstance(urgency, str):
             urgency = SourcePackageUrgency.items[urgency.upper()]
 
         section = self.makeSection(name=section_name)
@@ -3803,7 +3801,7 @@ class BareLaunchpadObjectFactory(ObjectFactory):
             creator = self.makePerson()
 
         if version is None:
-            version = six.text_type(self.getUniqueInteger()) + 'version'
+            version = str(self.getUniqueInteger()) + 'version'
 
         if copyright is None:
             copyright = self.getUniqueString()
@@ -3893,7 +3891,7 @@ class BareLaunchpadObjectFactory(ObjectFactory):
                 archive = source_package_release.upload_archive
         if pocket is None:
             pocket = PackagePublishingPocket.RELEASE
-        elif isinstance(pocket, six.string_types):
+        elif isinstance(pocket, str):
             pocket = PackagePublishingPocket.items[pocket.upper()]
 
         if source_package_release is None:
@@ -3973,12 +3971,12 @@ class BareLaunchpadObjectFactory(ObjectFactory):
 
         if pocket is None:
             pocket = self.getAnyPocket()
-        elif isinstance(pocket, six.string_types):
+        elif isinstance(pocket, str):
             pocket = PackagePublishingPocket.items[pocket.upper()]
 
         if status is None:
             status = PackagePublishingStatus.PENDING
-        elif isinstance(status, six.string_types):
+        elif isinstance(status, str):
             status = PackagePublishingStatus.items[status.upper()]
 
         if sourcepackagerelease is None:
@@ -4177,8 +4175,7 @@ class BareLaunchpadObjectFactory(ObjectFactory):
         """Make a `BinaryPackageRelease`."""
         if build is None:
             build = self.makeBinaryPackageBuild()
-        if (binarypackagename is None or
-            isinstance(binarypackagename, six.string_types)):
+        if binarypackagename is None or isinstance(binarypackagename, str):
             binarypackagename = self.getOrMakeBinaryPackageName(
                 binarypackagename)
         if version is None:
@@ -4187,9 +4184,9 @@ class BareLaunchpadObjectFactory(ObjectFactory):
             binpackageformat = BinaryPackageFormat.DEB
         if component is None:
             component = build.source_package_release.component
-        elif isinstance(component, six.text_type):
+        elif isinstance(component, str):
             component = getUtility(IComponentSet)[component]
-        if isinstance(section_name, six.string_types):
+        if isinstance(section_name, str):
             section_name = self.makeSection(section_name)
         section = section_name or build.source_package_release.section
         if priority is None:
@@ -4252,11 +4249,11 @@ class BareLaunchpadObjectFactory(ObjectFactory):
                        packages=(), distroseries=None, related_set=None):
         """Make an `IPackageset`."""
         if name is None:
-            name = self.getUniqueString(u'package-set-name')
+            name = self.getUniqueString('package-set-name')
         if description is None:
-            description = self.getUniqueString(u'package-set-description')
+            description = self.getUniqueString('package-set-description')
         if owner is None:
-            person = self.getUniqueString(u'package-set-owner')
+            person = self.getUniqueString('package-set-owner')
             owner = self.makePerson(name=person)
         if distroseries is None:
             distroseries = getUtility(IDistributionSet)[
@@ -4301,8 +4298,7 @@ class BareLaunchpadObjectFactory(ObjectFactory):
         if pocket is None:
             pocket = self.getAnyPocket()
         # Make sure we have a real sourcepackagename object.
-        if (sourcepackagename is None or
-            isinstance(sourcepackagename, six.string_types)):
+        if sourcepackagename is None or isinstance(sourcepackagename, str):
             sourcepackagename = self.getOrMakeSourcePackageName(
                 sourcepackagename)
         return ProxyFactory(
@@ -4311,8 +4307,7 @@ class BareLaunchpadObjectFactory(ObjectFactory):
     def makeDistributionSourcePackage(self, sourcepackagename=None,
                                       distribution=None, with_db=False):
         # Make sure we have a real sourcepackagename object.
-        if (sourcepackagename is None or
-            isinstance(sourcepackagename, six.string_types)):
+        if sourcepackagename is None or isinstance(sourcepackagename, str):
             sourcepackagename = self.getOrMakeSourcePackageName(
                 sourcepackagename)
         if distribution is None:
@@ -4462,8 +4457,7 @@ class BareLaunchpadObjectFactory(ObjectFactory):
         It doesn't actually run the job. It fakes it, and uses a fake
         librarian file so as to work without the librarian.
         """
-        blob = TemporaryBlobStorage(
-            uuid=six.text_type(uuid.uuid1()), file_alias=1)
+        blob = TemporaryBlobStorage(uuid=str(uuid.uuid1()), file_alias=1)
         job = getUtility(IProcessApportBlobJobSource).create(blob)
         job.job.start()
         removeSecurityProxy(job).metadata = {
@@ -4476,7 +4470,7 @@ class BareLaunchpadObjectFactory(ObjectFactory):
             person = self.makePerson()
         from lp.testing.layers import BaseLayer
         launchpad = launchpadlib_for(
-            u"test", person, service_root=BaseLayer.appserver_root_url("api"),
+            "test", person, service_root=BaseLayer.appserver_root_url("api"),
             version=version)
         login_person(person)
         return launchpad
@@ -4510,7 +4504,7 @@ class BareLaunchpadObjectFactory(ObjectFactory):
         if key is None:
             key = self.getUniqueUnicode("oauthconsumerkey")
         if secret is None:
-            secret = u''
+            secret = ''
         return getUtility(IOAuthConsumerSet).new(key, secret)
 
     def makeOAuthRequestToken(self, consumer=None, date_created=None,
@@ -4592,8 +4586,7 @@ class BareLaunchpadObjectFactory(ObjectFactory):
         """Create a new `PlainPackageCopyJob`."""
         if package_name is None and package_version is None:
             package_name = self.makeSourcePackageName().name
-            package_version = (
-                six.text_type(self.getUniqueInteger()) + 'version')
+            package_version = str(self.getUniqueInteger()) + 'version'
         if source_archive is None:
             source_archive = self.makeArchive()
         if target_archive is None:
@@ -4679,7 +4672,7 @@ class BareLaunchpadObjectFactory(ObjectFactory):
         return fileupload
 
     def makeCommercialSubscription(self, product, expired=False,
-                                   voucher_id=u'new'):
+                                   voucher_id='new'):
         """Create a commercial subscription for the given product."""
         if IStore(CommercialSubscription).find(
                 CommercialSubscription, product=product).one() is not None:
@@ -4696,7 +4689,7 @@ class BareLaunchpadObjectFactory(ObjectFactory):
             registrant=product.owner,
             purchaser=product.owner,
             sales_system_id=voucher_id,
-            whiteboard=u'')
+            whiteboard='')
         del get_property_cache(product).commercial_subscription
         return commercial_subscription
 
@@ -4717,7 +4710,7 @@ class BareLaunchpadObjectFactory(ObjectFactory):
         if distroseries is None:
             distroseries = self.makeDistroSeries()
         if name is None:
-            name = self.getUniqueString(u"livefs-name")
+            name = self.getUniqueString("livefs-name")
         if metadata is None:
             metadata = {}
         livefs = getUtility(ILiveFSSet).new(
@@ -4825,7 +4818,7 @@ class BareLaunchpadObjectFactory(ObjectFactory):
         if distroseries is _DEFAULT:
             distroseries = self.makeDistroSeries()
         if name is None:
-            name = self.getUniqueString(u"snap-name")
+            name = self.getUniqueString("snap-name")
         if branch is None and git_ref is None:
             branch = self.makeAnyBranch()
         if auto_build:
@@ -4941,7 +4934,7 @@ class BareLaunchpadObjectFactory(ObjectFactory):
         if registrant is None:
             registrant = self.makePerson()
         if name is None:
-            name = self.getUniqueString(u"snappy-series-name")
+            name = self.getUniqueString("snappy-series-name")
         if display_name is None:
             display_name = SPACE.join(
                 word.capitalize() for word in name.split('-'))
@@ -4965,21 +4958,21 @@ class BareLaunchpadObjectFactory(ObjectFactory):
         if registrant is None:
             registrant = self.makePerson()
         if name is None:
-            name = self.getUniqueString(u"snap-base-name")
+            name = self.getUniqueString("snap-base-name")
         if display_name is None:
             display_name = SPACE.join(
                 word.capitalize() for word in name.split('-'))
         if distro_series is None:
             distro_series = self.makeDistroSeries()
         if build_channels is None:
-            build_channels = {u"snapcraft": u"stable"}
+            build_channels = {"snapcraft": "stable"}
         return getUtility(ISnapBaseSet).new(
             registrant, name, display_name, distro_series, build_channels,
             processors=processors, date_created=date_created)
 
     def makeOCIProjectName(self, name=None):
         if name is None:
-            name = self.getUniqueString(u"oci-project-name")
+            name = self.getUniqueString("oci-project-name")
         return getUtility(IOCIProjectNameSet).getOrCreateByName(name)
 
     def makeOCIProject(self, registrant=None, pillar=None,
@@ -4992,7 +4985,7 @@ class BareLaunchpadObjectFactory(ObjectFactory):
             registrant = self.makePerson()
         if pillar is None:
             pillar = self.makeDistribution()
-        if ociprojectname is None or isinstance(ociprojectname, six.text_type):
+        if ociprojectname is None or isinstance(ociprojectname, str):
             ociprojectname = self.makeOCIProjectName(ociprojectname)
         return getUtility(IOCIProjectSet).new(
             registrant, pillar, ociprojectname, date_created=date_created,
@@ -5005,9 +4998,9 @@ class BareLaunchpadObjectFactory(ObjectFactory):
                              oci_project=None, **kwargs):
         """Make a new OCIProjectSeries attached to an OCIProject."""
         if name is None:
-            name = self.getUniqueString(u"oci-project-series-name")
+            name = self.getUniqueString("oci-project-series-name")
         if summary is None:
-            summary = self.getUniqueString(u"oci-project-series-summary")
+            summary = self.getUniqueString("oci-project-series-summary")
         if registrant is None:
             registrant = self.makePerson()
         if oci_project is None:
@@ -5022,23 +5015,23 @@ class BareLaunchpadObjectFactory(ObjectFactory):
                       information_type=InformationType.PUBLIC):
         """Make a new OCIRecipe."""
         if name is None:
-            name = self.getUniqueString(u"oci-recipe-name")
+            name = self.getUniqueString("oci-recipe-name")
         if registrant is None:
             registrant = self.makePerson()
         if description is None:
-            description = self.getUniqueString(u"oci-recipe-description")
+            description = self.getUniqueString("oci-recipe-description")
         if owner is None:
             owner = self.makeTeam(members=[registrant])
         if oci_project is None:
             oci_project = self.makeOCIProject()
         if git_ref is None:
             component = self.getUniqueUnicode()
-            paths = [u'refs/heads/{}-20.04'.format(component)]
+            paths = ['refs/heads/{}-20.04'.format(component)]
             [git_ref] = self.makeGitRefs(paths=paths)
         if build_file is None:
-            build_file = self.getUniqueUnicode(u"build_file_for")
+            build_file = self.getUniqueUnicode("build_file_for")
         if build_path is None:
-            build_path = self.getUniqueUnicode(u"build_path_for")
+            build_path = self.getUniqueUnicode("build_path_for")
         return getUtility(IOCIRecipeSet).new(
             name=name,
             registrant=registrant,
@@ -5148,7 +5141,7 @@ class BareLaunchpadObjectFactory(ObjectFactory):
         if registry_credentials is None:
             registry_credentials = self.makeOCIRegistryCredentials()
         if image_name is None:
-            image_name = self.getUniqueUnicode(u"oci-image-name")
+            image_name = self.getUniqueUnicode("oci-image-name")
         return getUtility(IOCIPushRuleSet).new(
             recipe=recipe,
             registry_credentials=registry_credentials,
@@ -5182,7 +5175,7 @@ class BareLaunchpadObjectFactory(ObjectFactory):
                 information_type=information_type,
                 branch_sharing_policy=branch_sharing_policy)
         if name is None:
-            name = self.getUniqueUnicode(u"charm-name")
+            name = self.getUniqueUnicode("charm-name")
         if git_ref is None:
             git_ref = self.makeGitRefs()[0]
         recipe = getUtility(ICharmRecipeSet).new(
@@ -5277,7 +5270,6 @@ unwrapped_types = frozenset({
     datetime,
     int,
     str,
-    six.text_type,
     })
 
 
@@ -5305,7 +5297,7 @@ class UnproxiedFactoryMethodWarning(UserWarning):
     """Raised when someone calls an unproxied factory method."""
 
     def __init__(self, method_name):
-        super(UnproxiedFactoryMethodWarning, self).__init__(
+        super().__init__(
             "PLEASE FIX: LaunchpadObjectFactory.%s returns an "
             "unproxied object." % (method_name, ))
 
@@ -5317,7 +5309,7 @@ class ShouldThisBeUsingRemoveSecurityProxy(UserWarning):
         message = (
             "removeSecurityProxy(%r) called. Is this correct? "
             "Either call it directly or fix the test." % obj)
-        super(ShouldThisBeUsingRemoveSecurityProxy, self).__init__(message)
+        super().__init__(message)
 
 
 class LaunchpadObjectFactory:
diff --git a/lib/lp/testing/fixture.py b/lib/lp/testing/fixture.py
index ce70144..0a26c57 100644
--- a/lib/lp/testing/fixture.py
+++ b/lib/lp/testing/fixture.py
@@ -68,7 +68,7 @@ class PGBouncerFixture(pgbouncer.fixture.PGBouncerFixture):
     """
 
     def __init__(self):
-        super(PGBouncerFixture, self).__init__()
+        super().__init__()
 
         # Known databases
         from lp.testing.layers import DatabaseLayer
@@ -97,7 +97,7 @@ class PGBouncerFixture(pgbouncer.fixture.PGBouncerFixture):
         self.admin_users = ['launchpad', 'pgbouncer', os.environ['USER']]
 
     def setUp(self):
-        super(PGBouncerFixture, self).setUp()
+        super().setUp()
 
         # reconnect_store cleanup added first so it is run last, after
         # the environment variables have been reset.
@@ -130,11 +130,11 @@ class PGBouncerFixture(pgbouncer.fixture.PGBouncerFixture):
     def start(self, retries=20, sleep=0.5):
         """Start PGBouncer, waiting for it to accept connections if neccesary.
         """
-        super(PGBouncerFixture, self).start()
+        super().start()
         for i in range(retries):
             try:
                 socket.create_connection((self.host, self.port))
-            except socket.error:
+            except OSError:
                 # Try again.
                 pass
             else:
@@ -147,8 +147,8 @@ class PGBouncerFixture(pgbouncer.fixture.PGBouncerFixture):
 class ZopeAdapterFixture(Fixture):
     """A fixture to register and unregister an adapter."""
 
-    def __init__(self, factory, required=None, provided=None, name=u"",
-                 info=u"", event=True):
+    def __init__(self, factory, required=None, provided=None, name="",
+                 info="", event=True):
         # We use some private functions from here since we need them to work
         # out how to query for existing adapters.  We could copy and paste
         # the code instead, but it doesn't seem worth it.
@@ -181,7 +181,7 @@ class ZopeEventHandlerFixture(Fixture):
     """A fixture that provides and then unprovides a Zope event handler."""
 
     def __init__(self, handler, required=None):
-        super(ZopeEventHandlerFixture, self).__init__()
+        super().__init__()
         self._handler = handler
         self._required = required
 
@@ -201,7 +201,7 @@ class ZopeViewReplacementFixture(Fixture):
     def __init__(self, name, context_interface,
                  request_interface=IDefaultBrowserLayer,
                  replacement=None):
-        super(ZopeViewReplacementFixture, self).__init__()
+        super().__init__()
         self.name = name
         self.context_interface = context_interface
         self.request_interface = request_interface
@@ -270,7 +270,7 @@ class CapturedOutput(Fixture):
     """A fixture that captures output to stdout and stderr."""
 
     def __init__(self):
-        super(CapturedOutput, self).__init__()
+        super().__init__()
         self.stdout = six.StringIO()
         self.stderr = six.StringIO()
 
diff --git a/lib/lp/testing/html5browser.py b/lib/lp/testing/html5browser.py
index e54e305..7230ab2 100644
--- a/lib/lp/testing/html5browser.py
+++ b/lib/lp/testing/html5browser.py
@@ -67,7 +67,7 @@ class Browser(WebKit.WebView):
     INCREMENTAL_TIMEOUT = None
 
     def __init__(self, show_window=False, hide_console_messages=True):
-        super(Browser, self).__init__()
+        super().__init__()
         self.show_window = show_window
         self.hide_console_messages = hide_console_messages
         self.browser_window = None
diff --git a/lib/lp/testing/karma.py b/lib/lp/testing/karma.py
index 0c0e75c..0393a4b 100644
--- a/lib/lp/testing/karma.py
+++ b/lib/lp/testing/karma.py
@@ -111,7 +111,7 @@ class KarmaAssignedEventListener(KarmaRecorder):
     """
 
     def __init__(self, show_person=False):
-        super(KarmaAssignedEventListener, self).__init__()
+        super().__init__()
         self.added_karma_actions = set()
         self.show_person = show_person
 
diff --git a/lib/lp/testing/keyserver/inprocess.py b/lib/lp/testing/keyserver/inprocess.py
index 70d6aae..932ff2e 100644
--- a/lib/lp/testing/keyserver/inprocess.py
+++ b/lib/lp/testing/keyserver/inprocess.py
@@ -67,7 +67,7 @@ class InProcessKeyServerFixture(Fixture):
         # fixtures.callmany.CallMany doesn't support cleanup functions that
         # return Deferred, so we have to do this manually.
         yield self._port.stopListening()
-        return super(InProcessKeyServerFixture, self).cleanUp(*args, **kwargs)
+        return super().cleanUp(*args, **kwargs)
 
     @property
     def url(self):
diff --git a/lib/lp/testing/layers.py b/lib/lp/testing/layers.py
index 3c60e5b..d27ca18 100644
--- a/lib/lp/testing/layers.py
+++ b/lib/lp/testing/layers.py
@@ -977,8 +977,7 @@ class TransactionMiddleware:
 
     def __call__(self, environ, start_response):
         transaction.commit()
-        for entry in self.app(environ, start_response):
-            yield entry
+        yield from self.app(environ, start_response)
 
 
 class RemoteAddrMiddleware:
@@ -1026,7 +1025,7 @@ class _FunctionalBrowserLayer(zope.testbrowser.wsgi.Layer, ZCMLFileLayer):
     fake_db = object()
 
     def __init__(self, *args, **kwargs):
-        super(_FunctionalBrowserLayer, self).__init__(*args, **kwargs)
+        super().__init__(*args, **kwargs)
         self.middlewares = [
             AuthorizationMiddleware,
             RemoteAddrMiddleware,
@@ -1036,7 +1035,7 @@ class _FunctionalBrowserLayer(zope.testbrowser.wsgi.Layer, ZCMLFileLayer):
             ]
 
     def setUp(self):
-        super(_FunctionalBrowserLayer, self).setUp()
+        super().setUp()
         # We don't use ZODB, but the webapp subscribes to IDatabaseOpened to
         # perform some post-configuration tasks, so emit that event
         # manually.
diff --git a/lib/lp/testing/matchers.py b/lib/lp/testing/matchers.py
index 84e26c9..2f5ffad 100644
--- a/lib/lp/testing/matchers.py
+++ b/lib/lp/testing/matchers.py
@@ -21,7 +21,6 @@ __all__ = [
     ]
 
 from lazr.lifecycle.snapshot import Snapshot
-import six
 from testtools import matchers
 from testtools.content import text_content
 from testtools.matchers import (
@@ -119,7 +118,7 @@ class DoesNotCorrectlyProvide(DoesNotProvide):
         :param extra: any extra information about the mismatch as a string,
             or None
         """
-        super(DoesNotCorrectlyProvide, self).__init__(obj, interface)
+        super().__init__(obj, interface)
         self.extra = extra
 
     def describe(self):
@@ -209,12 +208,12 @@ class _MismatchedQueryCount(Mismatch):
         result = []
         for query in collector.queries:
             start, stop, dbname, statement, backtrace = query
-            result.append(u'%d-%d@%s %s' % (
+            result.append('%d-%d@%s %s' % (
                 start, stop, dbname, statement.rstrip()))
-            result.append(u'-' * 70)
+            result.append('-' * 70)
             if backtrace is not None:
                 result.append(backtrace.rstrip())
-                result.append(u'.' * 70)
+                result.append('.' * 70)
         return text_content('\n'.join(result))
 
     def get_details(self):
@@ -402,7 +401,7 @@ class SoupMismatch(Mismatch):
         self.soup_content = soup_content
 
     def get_details(self):
-        return {'content': text_content(six.text_type(self.soup_content))}
+        return {'content': text_content(str(self.soup_content))}
 
 
 class MissingElement(SoupMismatch):
@@ -474,14 +473,14 @@ class EqualsIgnoringWhitespace(Equals):
     """
 
     def __init__(self, expected):
-        if isinstance(expected, (bytes, six.text_type)):
+        if isinstance(expected, (bytes, str)):
             expected = normalize_whitespace(expected)
-        super(EqualsIgnoringWhitespace, self).__init__(expected)
+        super().__init__(expected)
 
     def match(self, observed):
-        if isinstance(observed, (bytes, six.text_type)):
+        if isinstance(observed, (bytes, str)):
             observed = normalize_whitespace(observed)
-        return super(EqualsIgnoringWhitespace, self).match(observed)
+        return super().match(observed)
 
 
 class FileContainsBytes(FileContains):
diff --git a/lib/lp/testing/pages.py b/lib/lp/testing/pages.py
index 5c7a2fa..f28cf7a 100644
--- a/lib/lp/testing/pages.py
+++ b/lib/lp/testing/pages.py
@@ -87,9 +87,9 @@ from lp.testing.systemdocs import (
 
 
 SAMPLEDATA_ACCESS_SECRETS = {
-    u'salgado-read-nonprivate': u'secret',
-    u'salgado-change-anything': u'test',
-    u'nopriv-read-nonprivate': u'mystery',
+    'salgado-read-nonprivate': 'secret',
+    'salgado-change-anything': 'test',
+    'nopriv-read-nonprivate': 'mystery',
     }
 
 
@@ -141,7 +141,7 @@ class LaunchpadWebServiceCaller(WebServiceCaller):
         if oauth_consumer_key is not None and oauth_access_key is not None:
             if oauth_access_secret is None:
                 oauth_access_secret = SAMPLEDATA_ACCESS_SECRETS.get(
-                    oauth_access_key, u'')
+                    oauth_access_key, '')
             self.oauth_client = oauth1.Client(
                 oauth_consumer_key,
                 resource_owner_key=oauth_access_key,
@@ -343,11 +343,11 @@ ELEMENTS_INTRODUCING_NEWLINE = [
     'address', 'li', 'dt', 'dd', 'th', 'td', 'caption', 'br']
 
 
-NEWLINES_RE = re.compile(u'\n+')
+NEWLINES_RE = re.compile('\n+')
 LEADING_AND_TRAILING_SPACES_RE = re.compile(
-    u'(^[ \t]+)|([ \t]$)', re.MULTILINE)
-TABS_AND_SPACES_RE = re.compile(u'[ \t]+')
-NBSP_RE = re.compile(u' | |\xa0')
+    '(^[ \t]+)|([ \t]$)', re.MULTILINE)
+TABS_AND_SPACES_RE = re.compile('[ \t]+')
+NBSP_RE = re.compile(' | |\xa0')
 
 
 def extract_link_from_tag(tag, base=None):
@@ -390,7 +390,7 @@ def extract_text(content, extract_image_text=False, skip_tags=None,
         if type(node) in IGNORED_ELEMENTS:
             continue
         elif isinstance(node, CData):
-            result.append(six.text_type(node))
+            result.append(str(node))
         elif isinstance(node, NavigableString):
             result.append(node.format_string(node, formatter=formatter))
         else:
@@ -401,7 +401,7 @@ def extract_text(content, extract_image_text=False, skip_tags=None,
                 elif getattr(node, 'name', '') in skip_tags:
                     continue
                 if node.name.lower() in ELEMENTS_INTRODUCING_NEWLINE:
-                    result.append(u'\n')
+                    result.append('\n')
 
                 # If extract_image_text is True and the node is an
                 # image, try to find its title or alt attributes.
@@ -416,7 +416,7 @@ def extract_text(content, extract_image_text=False, skip_tags=None,
             # Process this node's children next.
             nodes[0:0] = list(node)
 
-    text = u''.join(result)
+    text = ''.join(result)
     text = NBSP_RE.sub(' ', text)
     text = TABS_AND_SPACES_RE.sub(' ', text)
     text = LEADING_AND_TRAILING_SPACES_RE.sub('', text)
@@ -647,8 +647,7 @@ class Browser(_Browser):
 
     def addHeader(self, key, value):
         """Make sure headers are native strings."""
-        super(Browser, self).addHeader(
-            wsgi_native_string(key), wsgi_native_string(value))
+        super().addHeader(wsgi_native_string(key), wsgi_native_string(value))
 
     def _getText(self, element):
         def get_strings(elem):
@@ -656,10 +655,10 @@ class Browser(_Browser):
                 if isinstance(descendant, (NavigableString, CData)):
                     yield descendant
                 elif isinstance(descendant, Tag) and descendant.name == 'img':
-                    yield u'%s[%s]' % (
-                        descendant.get('alt', u''), descendant.name.upper())
+                    yield '%s[%s]' % (
+                        descendant.get('alt', ''), descendant.name.upper())
 
-        return u''.join(list(get_strings(element)))
+        return ''.join(list(get_strings(element)))
 
     def getLink(self, text=None, url=None, id=None, index=0):
         """Search for both text nodes and image alt attributes."""
@@ -738,7 +737,7 @@ def safe_canonical_url(*args, **kwargs):
     return str(canonical_url(*args, **kwargs))
 
 
-def webservice_for_person(person, consumer_key=u'launchpad-library',
+def webservice_for_person(person, consumer_key='launchpad-library',
                           permission=OAuthPermission.READ_PUBLIC,
                           context=None, default_api_version=None):
     """Return a valid LaunchpadWebServiceCaller for the person.
diff --git a/lib/lp/testing/swift/fakeswift.py b/lib/lp/testing/swift/fakeswift.py
index f0134d8..69d9502 100644
--- a/lib/lp/testing/swift/fakeswift.py
+++ b/lib/lp/testing/swift/fakeswift.py
@@ -19,7 +19,6 @@ import os.path
 import time
 import uuid
 
-import six
 from twisted.web import (
     http,
     resource,
@@ -75,7 +74,7 @@ class FakeKeystone(resource.Resource):
             if token is not None and self._isValidToken(token, tenant_name):
                 return token
         else:
-            for id, token in six.iteritems(self.tokens):
+            for id, token in self.tokens.items():
                 if self._isValidToken(token, tenant_name):
                     return token
 
@@ -353,8 +352,7 @@ class SwiftContainer(resource.Resource):
 
     def iter_children(self):
         """Iterator that returns each child object."""
-        for key, val in self.container_children.items():
-            yield key, val
+        yield from self.container_children.items()
 
     def getChild(self, name, request):
         """Get the next object down the chain."""
diff --git a/lib/lp/testing/swift/fixture.py b/lib/lp/testing/swift/fixture.py
index facaf47..d0382d5 100644
--- a/lib/lp/testing/swift/fixture.py
+++ b/lib/lp/testing/swift/fixture.py
@@ -32,7 +32,7 @@ class SwiftFixture(TacTestFixture):
     daemon_port = None
 
     def __init__(self, old_instance=False):
-        super(SwiftFixture, self).__init__()
+        super().__init__()
         self.old_instance = old_instance
 
     def _getConfig(self, key):
@@ -53,7 +53,7 @@ class SwiftFixture(TacTestFixture):
                 config.root, 'logs', 'fakeswift-%s.pid' % self.daemon_port)
         assert self.daemon_port is not None
 
-        super(SwiftFixture, self).setUp(
+        super().setUp(
             spew, umask,
             os.path.join(config.root, 'bin', 'py'),
             os.path.join(config.root, 'bin', 'twistd'))
diff --git a/lib/lp/testing/swift/tests/test_fixture.py b/lib/lp/testing/swift/tests/test_fixture.py
index 431a6dd..091da15 100644
--- a/lib/lp/testing/swift/tests/test_fixture.py
+++ b/lib/lp/testing/swift/tests/test_fixture.py
@@ -30,7 +30,7 @@ class TestSwiftFixture(TestCase):
     layer = BaseLayer
 
     def setUp(self):
-        super(TestSwiftFixture, self).setUp()
+        super().setUp()
         self.swift_fixture = SwiftFixture()
         self.useFixture(self.swift_fixture)
         self.factory = ObjectFactory()
diff --git a/lib/lp/testing/systemdocs.py b/lib/lp/testing/systemdocs.py
index 4ea6d5f..99dba51 100644
--- a/lib/lp/testing/systemdocs.py
+++ b/lib/lp/testing/systemdocs.py
@@ -211,7 +211,7 @@ def stop():
         sys.stdout = old_stdout
 
 
-class PrettyPrinter(pprint.PrettyPrinter, object):
+class PrettyPrinter(pprint.PrettyPrinter):
     """A pretty-printer that formats text in the Python 3 style.
 
     This should only be used when the resulting ambiguities between str and
@@ -220,20 +220,19 @@ class PrettyPrinter(pprint.PrettyPrinter, object):
     """
 
     def format(self, obj, contexts, maxlevels, level):
-        if isinstance(obj, six.text_type):
+        if isinstance(obj, str):
             obj = obj.encode('unicode_escape').decode('ASCII')
             if "'" in obj and '"' not in obj:
                 return '"%s"' % obj, True, False
             else:
                 return "'%s'" % obj.replace("'", "\\'"), True, False
         else:
-            return super(PrettyPrinter, self).format(
-                obj, contexts, maxlevels, level)
+            return super().format(obj, contexts, maxlevels, level)
 
     # Disable wrapping of long strings on Python >= 3.5, which is unhelpful
     # in doctests.  There seems to be no reasonable public API for this.
     _dispatch = dict(pprint.PrettyPrinter._dispatch)
-    del _dispatch[six.text_type.__repr__]
+    del _dispatch[str.__repr__]
     del _dispatch[bytes.__repr__]
     del _dispatch[bytearray.__repr__]
 
diff --git a/lib/lp/testing/tests/test_factory.py b/lib/lp/testing/tests/test_factory.py
index 61010aa..4953455 100644
--- a/lib/lp/testing/tests/test_factory.py
+++ b/lib/lp/testing/tests/test_factory.py
@@ -628,20 +628,20 @@ class TestFactory(TestCaseWithFactory):
 
     # makeCVE
     def test_makeCVE_returns_cve(self):
-        cve = self.factory.makeCVE(sequence=u'2000-1234')
+        cve = self.factory.makeCVE(sequence='2000-1234')
         self.assertThat(cve, ProvidesAndIsProxied(ICve))
 
     def test_makeCVE_uses_sequence(self):
-        cve = self.factory.makeCVE(sequence=u'2000-1234')
-        self.assertEqual(u'2000-1234', cve.sequence)
+        cve = self.factory.makeCVE(sequence='2000-1234')
+        self.assertEqual('2000-1234', cve.sequence)
 
     def test_makeCVE_uses_description(self):
-        cve = self.factory.makeCVE(sequence=u'2000-1234', description=u'foo')
-        self.assertEqual(u'foo', cve.description)
+        cve = self.factory.makeCVE(sequence='2000-1234', description='foo')
+        self.assertEqual('foo', cve.description)
 
     def test_makeCVE_uses_cve_status(self):
         cve = self.factory.makeCVE(
-            sequence=u'2000-1234', cvestate=CveStatus.DEPRECATED)
+            sequence='2000-1234', cvestate=CveStatus.DEPRECATED)
         self.assertEqual(CveStatus.DEPRECATED, cve.status)
 
     # dir() support.
@@ -890,7 +890,7 @@ class IsSecurityProxiedOrHarmlessTests(TestCaseWithFactory):
     def test_is_security_proxied_or_harmless__unicode(self):
         # is_security_proxied_or_harmless() considers unicode objects
         # to be harmless.
-        self.assertTrue(is_security_proxied_or_harmless(u'abc'))
+        self.assertTrue(is_security_proxied_or_harmless('abc'))
 
     def test_is_security_proxied_or_harmless__proxied_object(self):
         # is_security_proxied_or_harmless() treats security proxied
diff --git a/lib/lp/testing/tests/test_fixture.py b/lib/lp/testing/tests/test_fixture.py
index f8592c7..db2887a 100644
--- a/lib/lp/testing/tests/test_fixture.py
+++ b/lib/lp/testing/tests/test_fixture.py
@@ -123,7 +123,7 @@ class TestZopeAdapterFixture(TestCase):
 
 
 @implementer(IMailDelivery)
-class DummyMailer(object):
+class DummyMailer:
 
     pass
 
@@ -332,7 +332,7 @@ class TestDisableTriggerFixture(TestCase):
     layer = ZopelessDatabaseLayer
 
     def setUp(self):
-        super(TestDisableTriggerFixture, self).setUp()
+        super().setUp()
         con_str = dbconfig.rw_main_primary + ' user=launchpad_main'
         con = psycopg2.connect(con_str)
         con.set_isolation_level(0)
diff --git a/lib/lp/testing/tests/test_html5browser.py b/lib/lp/testing/tests/test_html5browser.py
index bd64569..bc30aad 100644
--- a/lib/lp/testing/tests/test_html5browser.py
+++ b/lib/lp/testing/tests/test_html5browser.py
@@ -78,7 +78,7 @@ class BrowserTestCase(TestCase):
     """Verify Browser methods."""
 
     def setUp(self):
-        super(BrowserTestCase, self).setUp()
+        super().setUp()
         self.file = NamedTemporaryFile(
             mode='w+', prefix='html5browser_', suffix='.html')
         self.addCleanup(self.file.close)
diff --git a/lib/lp/testing/tests/test_layers_functional.py b/lib/lp/testing/tests/test_layers_functional.py
index 1b8c536..1787087 100644
--- a/lib/lp/testing/tests/test_layers_functional.py
+++ b/lib/lp/testing/tests/test_layers_functional.py
@@ -66,7 +66,7 @@ class BaseLayerIsolator(Fixture):
         :param with_persistent: If True LP_PERSISTENT_TEST_SERVICES will
             be enabled during setUp.
         """
-        super(BaseLayerIsolator, self).__init__()
+        super().__init__()
         self.with_persistent = with_persistent
 
     def _setUp(self):
@@ -92,7 +92,7 @@ class LayerFixture(Fixture):
 
         :param layer: The layer to use.
         """
-        super(LayerFixture, self).__init__()
+        super().__init__()
         self.layer = layer
 
     def _setUp(self):
@@ -198,7 +198,7 @@ class BaseTestCase(TestCase):
                     self.want_librarian_running,
                     'Librarian should not be running.'
                     )
-        except IOError:
+        except OSError:
             self.assertFalse(
                     self.want_librarian_running,
                     'Librarian should be running.'
@@ -496,7 +496,7 @@ class LayerProcessControllerTestCase(TestCase):
     layer = DatabaseLayer
 
     def tearDown(self):
-        super(LayerProcessControllerTestCase, self).tearDown()
+        super().tearDown()
         # Stop the app server.  It's okay if it isn't running.
         LayerProcessController.stopAppServer()
 
diff --git a/lib/lp/testing/tests/test_matchers.py b/lib/lp/testing/tests/test_matchers.py
index 30c0df1..9b04b5d 100644
--- a/lib/lp/testing/tests/test_matchers.py
+++ b/lib/lp/testing/tests/test_matchers.py
@@ -378,14 +378,14 @@ class EqualsIgnoringWhitespaceTests(TestCase):
             mismatch.describe())
 
     def test_match_unicode(self):
-        matcher = EqualsIgnoringWhitespace(u"one \t two \n \u1234  ")
-        self.assertIs(None, matcher.match(u" one \r two     \u1234 "))
+        matcher = EqualsIgnoringWhitespace("one \t two \n \u1234  ")
+        self.assertIs(None, matcher.match(" one \r two     \u1234 "))
 
     def test_mismatch_unicode(self):
-        matcher = EqualsIgnoringWhitespace(u"one \t two \n \u1234  ")
-        mismatch = matcher.match(u" one \r \u1234 ")
+        matcher = EqualsIgnoringWhitespace("one \t two \n \u1234  ")
+        mismatch = matcher.match(" one \r \u1234 ")
         self.assertEqual(
-            u"%r != %r" % (u"one \u1234", u"one two \u1234"),
+            "%r != %r" % ("one \u1234", "one two \u1234"),
             mismatch.describe())
 
     def test_match_non_string(self):
diff --git a/lib/lp/testing/tests/test_run_isolated_test.py b/lib/lp/testing/tests/test_run_isolated_test.py
index 363bca2..66a1cfe 100644
--- a/lib/lp/testing/tests/test_run_isolated_test.py
+++ b/lib/lp/testing/tests/test_run_isolated_test.py
@@ -101,13 +101,13 @@ class TestRunIsolatedTest(TestCase):
     @record_pid
     def __init__(self, method_name='runTest'):
         # Runs in the parent process.
-        super(TestRunIsolatedTest, self).__init__(method_name)
+        super().__init__(method_name)
         self.layer = TestRunIsolatedTestLayer()
 
     @record_pid
     def setUp(self):
         # Runs in the child process.
-        super(TestRunIsolatedTest, self).setUp()
+        super().setUp()
         self.assertNotEqual(
             self.layer.pid_in___init__, self.pid_in_setUp,
             "setUp() called in parent process.")
@@ -122,7 +122,7 @@ class TestRunIsolatedTest(TestCase):
     @record_pid
     def tearDown(self):
         # Runs in the child process.
-        super(TestRunIsolatedTest, self).tearDown()
+        super().tearDown()
         self.assertEqual(
             self.pid_in_setUp, self.pid_in_tearDown,
             "tearDown() not run in same process as setUp().")
diff --git a/lib/lp/testing/tests/test_sampledata.py b/lib/lp/testing/tests/test_sampledata.py
index e3699da..ac65bd1 100644
--- a/lib/lp/testing/tests/test_sampledata.py
+++ b/lib/lp/testing/tests/test_sampledata.py
@@ -21,13 +21,13 @@ class SampleDataTestCase(TestCase):
     layer = DatabaseLayer
 
     def setUp(self):
-        super(SampleDataTestCase, self).setUp()
+        super().setUp()
         self.pg_fixture = PgTestSetup(template='template1')
         self.pg_fixture.setUp()
 
     def tearDown(self):
         self.pg_fixture.tearDown()
-        super(SampleDataTestCase, self).tearDown()
+        super().tearDown()
 
     def test_testSampledata(self):
         """Test the sample data used by the test suite."""
diff --git a/lib/lp/testing/tests/test_testcase.py b/lib/lp/testing/tests/test_testcase.py
index 75f5c26..86b61ad 100644
--- a/lib/lp/testing/tests/test_testcase.py
+++ b/lib/lp/testing/tests/test_testcase.py
@@ -112,7 +112,7 @@ class TestRemoveLoggingHandlers(TestCase):
         self.logger.addHandler(logging.Handler())
         self.logger.addHandler(logging.Handler())
         # `TestCase.setUp()` removes the handlers just added.
-        super(TestRemoveLoggingHandlers, self).setUp()
+        super().setUp()
 
     def test_handlers_list_is_empty(self):
         # Ensure `TestCase.setUp()` correctly removed all logging handlers.
diff --git a/lib/lp/testing/tests/test_testing.py b/lib/lp/testing/tests/test_testing.py
index ec6154b..7a7e931 100644
--- a/lib/lp/testing/tests/test_testing.py
+++ b/lib/lp/testing/tests/test_testing.py
@@ -31,18 +31,18 @@ class TestFeatureFlags(TestCase):
         # test (other tests will re-add it). This prevents weird
         # interactions in a parallel test environment.
         uninstall_feature_controller()
-        self.assertRaises(AssertionError, set_feature_flag, u'name', u'value')
+        self.assertRaises(AssertionError, set_feature_flag, 'name', 'value')
 
     def test_flags_set_within_feature_flags_context(self):
         """In the feature_flags context, set/get works."""
         self.useContext(feature_flags())
-        set_feature_flag(u'name', u'value')
+        set_feature_flag('name', 'value')
         self.assertEqual('value', getFeatureFlag('name'))
 
     def test_flags_unset_outside_feature_flags_context(self):
         """get fails when used outside the feature_flags context."""
         with feature_flags():
-            set_feature_flag(u'name', u'value')
+            set_feature_flag('name', 'value')
         self.assertIs(None, getFeatureFlag('name'))
 
 
diff --git a/lib/lp/testing/xmlrpc.py b/lib/lp/testing/xmlrpc.py
index 82ac254..f795693 100644
--- a/lib/lp/testing/xmlrpc.py
+++ b/lib/lp/testing/xmlrpc.py
@@ -23,7 +23,7 @@ from lp.services.webapp.interaction import (
 from lp.testing.pages import http
 
 
-class _FakeSocket(object):
+class _FakeSocket:
     """Pretend to be a socket that has a makefile method.
 
     This is used because it is what http.client.HTTPResponse expects.
diff --git a/lib/lp/testing/yuixhr.py b/lib/lp/testing/yuixhr.py
index 7bb18a0..e438e15 100644
--- a/lib/lp/testing/yuixhr.py
+++ b/lib/lp/testing/yuixhr.py
@@ -249,7 +249,7 @@ class YUITestFixtureControllerView(LaunchpadView):
         """)
 
     def __init__(self, context, request):
-        super(YUITestFixtureControllerView, self).__init__(context, request)
+        super().__init__(context, request)
         self.names = []
         self.action = None
         self.fixtures = []
@@ -486,13 +486,13 @@ class YUIAppServerTestCase(AbstractYUITestCase):
         self.facet = facet
         # This needs to be done early so the "id" is set correctly.
         self.test_path = self.module_name.replace('.', '/')
-        super(YUIAppServerTestCase, self).__init__()
+        super().__init__()
 
     def setUp(self):
         config = LayerProcessController.appserver_config
         root_url = config.appserver_root_url(self.facet)
         self.html_uri = '%s/+yuitest/%s' % (root_url, self.test_path)
-        super(YUIAppServerTestCase, self).setUp()
+        super().setUp()
 
     runTest = AbstractYUITestCase.checkResults
 
diff --git a/lib/lp/testopenid/browser/server.py b/lib/lp/testopenid/browser/server.py
index bdf4774..04d0982 100644
--- a/lib/lp/testopenid/browser/server.py
+++ b/lib/lp/testopenid/browser/server.py
@@ -134,7 +134,7 @@ class OpenIDMixin:
     openid_request = None
 
     def __init__(self, context, request):
-        super(OpenIDMixin, self).__init__(context, request)
+        super().__init__(context, request)
         self.server_url = get_server_url()
         self.openid_server = Server(get_openid_store(), self.server_url)
 
@@ -304,7 +304,7 @@ class TestOpenIDLoginView(OpenIDMixin, LaunchpadFormView):
 
     def initialize(self):
         self.restoreRequestFromSession()
-        super(TestOpenIDLoginView, self).initialize()
+        super().initialize()
 
     def validate(self, data):
         """Check that the email address is valid for login."""
diff --git a/lib/lp/testopenid/interfaces/server.py b/lib/lp/testopenid/interfaces/server.py
index cb8b675..8930f00 100644
--- a/lib/lp/testopenid/interfaces/server.py
+++ b/lib/lp/testopenid/interfaces/server.py
@@ -23,7 +23,7 @@ class ITestOpenIDApplication(ILaunchpadApplication):
 
 
 class ITestOpenIDLoginForm(Interface):
-    email = TextLine(title=u'What is your email address?', required=True)
+    email = TextLine(title='What is your email address?', required=True)
 
 
 class ITestOpenIDPersistentIdentity(IOpenIDPersistentIdentity):
diff --git a/lib/lp/testopenid/testing/helpers.py b/lib/lp/testopenid/testing/helpers.py
index ded6f31..4d189e4 100644
--- a/lib/lp/testopenid/testing/helpers.py
+++ b/lib/lp/testopenid/testing/helpers.py
@@ -30,10 +30,10 @@ class EchoView(LaunchpadView):
 
     def render(self):
         out = io.StringIO()
-        print(u'Request method: %s' % self.request.method, file=out)
+        print('Request method: %s' % self.request.method, file=out)
         keys = sorted(self.request.form.keys())
         for key in keys:
-            print(u'%s:%s' % (key, self.request.form[key]), file=out)
+            print('%s:%s' % (key, self.request.form[key]), file=out)
         return out.getvalue()