← Back to team overview

launchpad-reviewers team mailing list archive

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

 

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

Commit message:
lp.charms: Apply "pyupgrade --py3-plus"

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/+git/launchpad/+merge/413245
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of ~cjwatson/launchpad:pyupgrade-py3-charms into launchpad:master.
diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs
index 9a8f650..f2b6846 100644
--- a/.git-blame-ignore-revs
+++ b/.git-blame-ignore-revs
@@ -18,3 +18,5 @@ c348b945c29c723201380d9aca0e0c0298037c8c
 7f2f3372cfc3b59e805ddce7434757f068adcd26
 # apply pyupgrade --py3-plus to lp.buildmaster
 b6725842a2470e3927bb73bf400c4476a06ee3ba
+# apply pyupgrade --py3-plus to lp.charms
+474f07ab7c3f28d8b6b8e4f1bd4c56832cec3fab
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 8abd4c3..12d6018 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -47,6 +47,7 @@ repos:
             |blueprints
             |bugs
             |buildmaster
+            |charms
           )/
 -   repo: https://github.com/PyCQA/isort
     rev: 5.9.2
diff --git a/lib/lp/charms/adapters/buildarch.py b/lib/lp/charms/adapters/buildarch.py
index d0c8c8e..bcb7cad 100644
--- a/lib/lp/charms/adapters/buildarch.py
+++ b/lib/lp/charms/adapters/buildarch.py
@@ -11,8 +11,6 @@ from collections import (
     )
 import json
 
-import six
-
 from lp.services.helpers import english_list
 
 
@@ -24,7 +22,7 @@ class MissingPropertyError(CharmBasesParserError):
     """Error for when an expected property is not present in the YAML."""
 
     def __init__(self, prop):
-        super(MissingPropertyError, self).__init__(
+        super().__init__(
             "Base specification is missing the {!r} property".format(prop))
         self.property = prop
 
@@ -37,19 +35,18 @@ class DuplicateRunOnError(CharmBasesParserError):
     """Error for when multiple `run-on`s include the same architecture."""
 
     def __init__(self, duplicates):
-        super(DuplicateRunOnError, self).__init__(
+        super().__init__(
             "{} {} present in the 'run-on' of multiple items".format(
                 english_list([str(d) for d in duplicates]),
                 "is" if len(duplicates) == 1 else "are"))
 
 
-@six.python_2_unicode_compatible
 class CharmBase:
     """A single base in charmcraft.yaml."""
 
     def __init__(self, name, channel, architectures=None):
         self.name = name
-        if not isinstance(channel, six.string_types):
+        if not isinstance(channel, str):
             raise BadPropertyError(
                 "Channel {!r} is not a string (missing quotes?)".format(
                     channel))
diff --git a/lib/lp/charms/browser/charmbase.py b/lib/lp/charms/browser/charmbase.py
index 706a404..d882be6 100644
--- a/lib/lp/charms/browser/charmbase.py
+++ b/lib/lp/charms/browser/charmbase.py
@@ -3,7 +3,6 @@
 
 """Views of bases for charms."""
 
-__metaclass__ = type
 __all__ = [
     "CharmBaseSetNavigation",
     ]
diff --git a/lib/lp/charms/browser/charmrecipe.py b/lib/lp/charms/browser/charmrecipe.py
index b09b778..675e517 100644
--- a/lib/lp/charms/browser/charmrecipe.py
+++ b/lib/lp/charms/browser/charmrecipe.py
@@ -356,12 +356,11 @@ class CharmRecipeAddView(CharmRecipeAuthorizeMixin, LaunchpadFormView):
         if self.widgets.get("store_upload") is not None:
             # Set widgets as required or optional depending on the
             # store_upload field.
-            super(CharmRecipeAddView, self).validate_widgets(
-                data, ["store_upload"])
+            super().validate_widgets(data, ["store_upload"])
             store_upload = data.get("store_upload", False)
             self.widgets["store_name"].context.required = store_upload
             self.widgets["store_channels"].context.required = store_upload
-        super(CharmRecipeAddView, self).validate_widgets(data, names=names)
+        super().validate_widgets(data, names=names)
 
     @action("Create charm recipe", name="create")
     def create_action(self, action, data):
@@ -387,7 +386,7 @@ class CharmRecipeAddView(CharmRecipeAuthorizeMixin, LaunchpadFormView):
             self.next_url = canonical_url(recipe)
 
     def validate(self, data):
-        super(CharmRecipeAddView, self).validate(data)
+        super().validate(data)
         owner = data.get("owner", None)
         if self.is_project_context:
             project = self.context
@@ -416,16 +415,14 @@ class BaseCharmRecipeEditView(
         if self.widgets.get("store_upload") is not None:
             # Set widgets as required or optional depending on the
             # store_upload field.
-            super(BaseCharmRecipeEditView, self).validate_widgets(
-                data, ["store_upload"])
+            super().validate_widgets(data, ["store_upload"])
             store_upload = data.get("store_upload", False)
             self.widgets["store_name"].context.required = store_upload
             self.widgets["store_channels"].context.required = store_upload
-        super(BaseCharmRecipeEditView, self).validate_widgets(
-            data, names=names)
+        super().validate_widgets(data, names=names)
 
     def validate(self, data):
-        super(BaseCharmRecipeEditView, self).validate(data)
+        super().validate(data)
         # These are the requirements for public snaps.
         if "owner" in data:
             owner = data.get("owner", self.context.owner)
@@ -511,7 +508,7 @@ class CharmRecipeEditView(BaseCharmRecipeEditView):
     custom_widget_store_channels = StoreChannelsWidget
 
     def validate(self, data):
-        super(CharmRecipeEditView, self).validate(data)
+        super().validate(data)
         owner = data.get("owner", None)
         project = data.get("project", None)
         name = data.get("name", None)
diff --git a/lib/lp/charms/browser/charmrecipebuild.py b/lib/lp/charms/browser/charmrecipebuild.py
index 971bd6b..4c6ae4e 100644
--- a/lib/lp/charms/browser/charmrecipebuild.py
+++ b/lib/lp/charms/browser/charmrecipebuild.py
@@ -163,7 +163,7 @@ class CharmRecipeBuildRescoreView(LaunchpadFormView):
 
     def __call__(self):
         if self.context.can_be_rescored:
-            return super(CharmRecipeBuildRescoreView, self).__call__()
+            return super().__call__()
         self.request.response.addWarningNotification(
             "Cannot rescore this build because it is not queued.")
         self.request.response.redirect(canonical_url(self.context))
diff --git a/lib/lp/charms/browser/charmrecipelisting.py b/lib/lp/charms/browser/charmrecipelisting.py
index d51008c..90fa225 100644
--- a/lib/lp/charms/browser/charmrecipelisting.py
+++ b/lib/lp/charms/browser/charmrecipelisting.py
@@ -38,7 +38,7 @@ class CharmRecipeListingView(LaunchpadView, FeedsMixin):
             "displayname": self.context.displayname}
 
     def initialize(self):
-        super(CharmRecipeListingView, self).initialize()
+        super().initialize()
         recipes = getUtility(ICharmRecipeSet).findByContext(
             self.context, visible_by_user=self.user)
         loader = partial(
diff --git a/lib/lp/charms/browser/tests/test_charmrecipe.py b/lib/lp/charms/browser/tests/test_charmrecipe.py
index 1439306..e37a38a 100644
--- a/lib/lp/charms/browser/tests/test_charmrecipe.py
+++ b/lib/lp/charms/browser/tests/test_charmrecipe.py
@@ -99,7 +99,7 @@ class TestCharmRecipeNavigation(TestCaseWithFactory):
     layer = DatabaseFunctionalLayer
 
     def setUp(self):
-        super(TestCharmRecipeNavigation, self).setUp()
+        super().setUp()
         self.useFixture(FeatureFixture({CHARM_RECIPE_ALLOW_CREATE: "on"}))
 
     def test_canonical_url(self):
@@ -124,7 +124,7 @@ class BaseTestCharmRecipeView(BrowserTestCase):
     layer = LaunchpadFunctionalLayer
 
     def setUp(self):
-        super(BaseTestCharmRecipeView, self).setUp()
+        super().setUp()
         self.useFixture(FeatureFixture({CHARM_RECIPE_ALLOW_CREATE: "on"}))
         self.useFixture(FakeLogger())
         self.snap_store_client = FakeMethod()
@@ -804,7 +804,7 @@ class TestCharmRecipeDeleteView(BaseTestCharmRecipeView):
 class TestCharmRecipeView(BaseTestCharmRecipeView):
 
     def setUp(self):
-        super(TestCharmRecipeView, self).setUp()
+        super().setUp()
         self.project = self.factory.makeProduct(
             name="test-project", displayname="Test Project")
         self.ubuntu = getUtility(ILaunchpadCelebrities).ubuntu
@@ -1011,7 +1011,7 @@ class TestCharmRecipeView(BaseTestCharmRecipeView):
 class TestCharmRecipeRequestBuildsView(BaseTestCharmRecipeView):
 
     def setUp(self):
-        super(TestCharmRecipeRequestBuildsView, self).setUp()
+        super().setUp()
         self.project = self.factory.makeProduct(
             name="test-project", displayname="Test Project")
         self.ubuntu = getUtility(ILaunchpadCelebrities).ubuntu
diff --git a/lib/lp/charms/browser/tests/test_charmrecipebuild.py b/lib/lp/charms/browser/tests/test_charmrecipebuild.py
index 588eae8..cd83dc7 100644
--- a/lib/lp/charms/browser/tests/test_charmrecipebuild.py
+++ b/lib/lp/charms/browser/tests/test_charmrecipebuild.py
@@ -48,7 +48,7 @@ class TestCanonicalUrlForCharmRecipeBuild(TestCaseWithFactory):
     layer = DatabaseFunctionalLayer
 
     def setUp(self):
-        super(TestCanonicalUrlForCharmRecipeBuild, self).setUp()
+        super().setUp()
         self.useFixture(FeatureFixture({CHARM_RECIPE_ALLOW_CREATE: "on"}))
 
     def test_canonical_url(self):
@@ -70,7 +70,7 @@ class TestCharmRecipeBuildView(TestCaseWithFactory):
     layer = LaunchpadFunctionalLayer
 
     def setUp(self):
-        super(TestCharmRecipeBuildView, self).setUp()
+        super().setUp()
         self.useFixture(FeatureFixture({CHARM_RECIPE_ALLOW_CREATE: "on"}))
 
     def test_files(self):
@@ -162,7 +162,7 @@ class TestCharmRecipeBuildOperations(BrowserTestCase):
     layer = DatabaseFunctionalLayer
 
     def setUp(self):
-        super(TestCharmRecipeBuildOperations, self).setUp()
+        super().setUp()
         self.useFixture(FeatureFixture({CHARM_RECIPE_ALLOW_CREATE: "on"}))
         self.useFixture(FakeLogger())
         self.build = self.factory.makeCharmRecipeBuild()
diff --git a/lib/lp/charms/browser/tests/test_hascharmrecipes.py b/lib/lp/charms/browser/tests/test_hascharmrecipes.py
index f53aa6b..e114153 100644
--- a/lib/lp/charms/browser/tests/test_hascharmrecipes.py
+++ b/lib/lp/charms/browser/tests/test_hascharmrecipes.py
@@ -44,7 +44,7 @@ class TestHasCharmRecipesView(WithScenarios, TestCaseWithFactory):
         ]
 
     def setUp(self):
-        super(TestHasCharmRecipesView, self).setUp()
+        super().setUp()
         self.useFixture(FeatureFixture({CHARM_RECIPE_ALLOW_CREATE: "on"}))
 
     def makeCharmRecipe(self, context):
@@ -91,7 +91,7 @@ class TestHasCharmRecipesMenu(WithScenarios, TestCaseWithFactory):
         ]
 
     def setUp(self):
-        super(TestHasCharmRecipesMenu, self).setUp()
+        super().setUp()
         self.useFixture(GitHostingFixture())
 
     def makeCharmRecipe(self, context):
diff --git a/lib/lp/charms/browser/widgets/charmrecipebuildchannels.py b/lib/lp/charms/browser/widgets/charmrecipebuildchannels.py
index e1b59cc..92af76e 100644
--- a/lib/lp/charms/browser/widgets/charmrecipebuildchannels.py
+++ b/lib/lp/charms/browser/widgets/charmrecipebuildchannels.py
@@ -35,7 +35,7 @@ class CharmRecipeBuildChannelsWidget(BrowserWidget, InputWidget):
     _widgets_set_up = False
 
     def __init__(self, context, request):
-        super(CharmRecipeBuildChannelsWidget, self).__init__(context, request)
+        super().__init__(context, request)
         self.hint = (
             "The channels to use for build tools when building the charm "
             "recipe.")
@@ -97,7 +97,7 @@ class CharmRecipeBuildChannelsWidget(BrowserWidget, InputWidget):
                 self.getInputValue()
         except InputErrors as error:
             self._error = error
-        return super(CharmRecipeBuildChannelsWidget, self).error()
+        return super().error()
 
     def __call__(self):
         """See `IBrowserWidget`."""
diff --git a/lib/lp/charms/browser/widgets/tests/test_charmrecipebuildchannelswidget.py b/lib/lp/charms/browser/widgets/tests/test_charmrecipebuildchannelswidget.py
index 834c801..a5c922b 100644
--- a/lib/lp/charms/browser/widgets/tests/test_charmrecipebuildchannelswidget.py
+++ b/lib/lp/charms/browser/widgets/tests/test_charmrecipebuildchannelswidget.py
@@ -28,7 +28,7 @@ class TestCharmRecipeBuildChannelsWidget(TestCaseWithFactory):
     layer = DatabaseFunctionalLayer
 
     def setUp(self):
-        super(TestCharmRecipeBuildChannelsWidget, self).setUp()
+        super().setUp()
         self.useFixture(FeatureFixture({CHARM_RECIPE_ALLOW_CREATE: "on"}))
         field = Dict(
             __name__="auto_build_channels",
diff --git a/lib/lp/charms/interfaces/charmbase.py b/lib/lp/charms/interfaces/charmbase.py
index 1677ceb..f22c521 100644
--- a/lib/lp/charms/interfaces/charmbase.py
+++ b/lib/lp/charms/interfaces/charmbase.py
@@ -3,7 +3,6 @@
 
 """Interfaces for bases for charms."""
 
-__metaclass__ = type
 __all__ = [
     "DuplicateCharmBase",
     "ICharmBase",
diff --git a/lib/lp/charms/interfaces/charmrecipe.py b/lib/lp/charms/interfaces/charmrecipe.py
index 5f95f7c..91daf3f 100644
--- a/lib/lp/charms/interfaces/charmrecipe.py
+++ b/lib/lp/charms/interfaces/charmrecipe.py
@@ -106,7 +106,7 @@ class CharmRecipeFeatureDisabled(Unauthorized):
     """Only certain users can create new charm recipes."""
 
     def __init__(self):
-        super(CharmRecipeFeatureDisabled, self).__init__(
+        super().__init__(
             "You do not have permission to create new charm recipes.")
 
 
@@ -115,7 +115,7 @@ class CharmRecipePrivateFeatureDisabled(Unauthorized):
     """Only certain users can create private charm recipes."""
 
     def __init__(self):
-        super(CharmRecipePrivateFeatureDisabled, self).__init__(
+        super().__init__(
             "You do not have permission to create private charm recipes.")
 
 
@@ -124,7 +124,7 @@ class DuplicateCharmRecipeName(Exception):
     """Raised for charm recipes with duplicate project/owner/name."""
 
     def __init__(self):
-        super(DuplicateCharmRecipeName, self).__init__(
+        super().__init__(
             "There is already a charm recipe with the same project, owner, "
             "and name.")
 
@@ -144,8 +144,7 @@ class NoSourceForCharmRecipe(Exception):
     """Charm recipes must have a source (Git branch)."""
 
     def __init__(self):
-        super(NoSourceForCharmRecipe, self).__init__(
-            "New charm recipes must have a Git branch.")
+        super().__init__("New charm recipes must have a Git branch.")
 
 
 @error_status(http.client.BAD_REQUEST)
@@ -158,7 +157,7 @@ class CharmRecipePrivacyMismatch(Exception):
     """Charm recipe privacy does not match its content."""
 
     def __init__(self, message=None):
-        super(CharmRecipePrivacyMismatch, self).__init__(
+        super().__init__(
             message or
             "Charm recipe contains private information and cannot be public.")
 
@@ -176,8 +175,7 @@ class MissingCharmcraftYaml(Exception):
     """The repository for this charm recipe does not have a charmcraft.yaml."""
 
     def __init__(self, branch_name):
-        super(MissingCharmcraftYaml, self).__init__(
-            "Cannot find charmcraft.yaml in %s" % branch_name)
+        super().__init__("Cannot find charmcraft.yaml in %s" % branch_name)
 
 
 class CannotFetchCharmcraftYaml(Exception):
@@ -193,7 +191,7 @@ class CharmRecipeBuildAlreadyPending(Exception):
     """A build was requested when an identical build was already pending."""
 
     def __init__(self):
-        super(CharmRecipeBuildAlreadyPending, self).__init__(
+        super().__init__(
             "An identical build of this charm recipe is already pending.")
 
 
@@ -202,7 +200,7 @@ class CharmRecipeBuildDisallowedArchitecture(Exception):
     """A build was requested for a disallowed architecture."""
 
     def __init__(self, das):
-        super(CharmRecipeBuildDisallowedArchitecture, self).__init__(
+        super().__init__(
             "This charm recipe is not allowed to build for %s/%s." %
             (das.distroseries.name, das.architecturetag))
 
diff --git a/lib/lp/charms/mail/charmrecipebuild.py b/lib/lp/charms/mail/charmrecipebuild.py
index ca10eac..bb811ad 100644
--- a/lib/lp/charms/mail/charmrecipebuild.py
+++ b/lib/lp/charms/mail/charmrecipebuild.py
@@ -90,15 +90,14 @@ class CharmRecipeBuildMailer(BaseMailer):
 
     def __init__(self, subject, template_name, recipients, from_address,
                  notification_type, build):
-        super(CharmRecipeBuildMailer, self).__init__(
+        super().__init__(
             subject, template_name, recipients, from_address,
             notification_type=notification_type)
         self.build = build
 
     def _getHeaders(self, email, recipient):
         """See `BaseMailer`."""
-        headers = super(CharmRecipeBuildMailer, self)._getHeaders(
-            email, recipient)
+        headers = super()._getHeaders(email, recipient)
         headers["X-Launchpad-Build-State"] = self.build.status.name
         return headers
 
@@ -110,8 +109,7 @@ class CharmRecipeBuildMailer(BaseMailer):
             error_message = ""
         else:
             error_message = upload_job.error_message or ""
-        params = super(CharmRecipeBuildMailer, self)._getTemplateParams(
-            email, recipient)
+        params = super()._getTemplateParams(email, recipient)
         params.update({
             "architecturetag": build.distro_arch_series.architecturetag,
             "build_duration": "",
diff --git a/lib/lp/charms/model/charmbase.py b/lib/lp/charms/model/charmbase.py
index 211c0c3..6546b26 100644
--- a/lib/lp/charms/model/charmbase.py
+++ b/lib/lp/charms/model/charmbase.py
@@ -3,7 +3,6 @@
 
 """Bases for charms."""
 
-__metaclass__ = type
 __all__ = [
     "CharmBase",
     ]
diff --git a/lib/lp/charms/model/charmrecipe.py b/lib/lp/charms/model/charmrecipe.py
index 2ee38d4..c124924 100644
--- a/lib/lp/charms/model/charmrecipe.py
+++ b/lib/lp/charms/model/charmrecipe.py
@@ -329,7 +329,7 @@ class CharmRecipe(StormBase, WebhookTargetMixin):
         """Construct a `CharmRecipe`."""
         if not getFeatureFlag(CHARM_RECIPE_ALLOW_CREATE):
             raise CharmRecipeFeatureDisabled()
-        super(CharmRecipe, self).__init__()
+        super().__init__()
 
         # Set this first for use by other validators.
         self.information_type = information_type
diff --git a/lib/lp/charms/model/charmrecipebuild.py b/lib/lp/charms/model/charmrecipebuild.py
index 666942e..cbcdd9e 100644
--- a/lib/lp/charms/model/charmrecipebuild.py
+++ b/lib/lp/charms/model/charmrecipebuild.py
@@ -152,7 +152,7 @@ class CharmRecipeBuild(PackageBuildMixin, StormBase):
                  store_upload_metadata=None, date_created=DEFAULT):
         """Construct a `CharmRecipeBuild`."""
         requester = build_request.requester
-        super(CharmRecipeBuild, self).__init__()
+        super().__init__()
         self.build_farm_job = build_farm_job
         self.build_request_id = build_request.id
         self.requester = requester
@@ -455,7 +455,7 @@ class CharmRecipeBuild(PackageBuildMixin, StormBase):
         with notify_modified(
                 self, edited_fields,
                 snapshot_names=("status", "revision_id")) as previous_obj:
-            super(CharmRecipeBuild, self).updateStatus(
+            super().updateStatus(
                 status, builder=builder, slave_status=slave_status,
                 date_started=date_started, date_finished=date_finished,
                 force_invalid_transition=force_invalid_transition)
@@ -576,6 +576,6 @@ class CharmFile(StormBase):
 
     def __init__(self, build, library_file):
         """Construct a `CharmFile`."""
-        super(CharmFile, self).__init__()
+        super().__init__()
         self.build = build
         self.library_file = library_file
diff --git a/lib/lp/charms/model/charmrecipebuildbehaviour.py b/lib/lp/charms/model/charmrecipebuildbehaviour.py
index b02c90e..cd44314 100644
--- a/lib/lp/charms/model/charmrecipebuildbehaviour.py
+++ b/lib/lp/charms/model/charmrecipebuildbehaviour.py
@@ -72,8 +72,7 @@ class CharmRecipeBuildBehaviour(BuilderProxyMixin, BuildFarmJobBehaviourBase):
         Return the extra arguments required by the slave for the given build.
         """
         build = self.build
-        args = yield super(CharmRecipeBuildBehaviour, self).extraBuildArgs(
-            logger=logger)
+        args = yield super().extraBuildArgs(logger=logger)
         yield self.addProxyArgs(args)
         args["name"] = build.recipe.store_name or build.recipe.name
         channels = build.channels or {}
diff --git a/lib/lp/charms/model/charmrecipebuildjob.py b/lib/lp/charms/model/charmrecipebuildjob.py
index dba0e4a..d3fdd5a 100644
--- a/lib/lp/charms/model/charmrecipebuildjob.py
+++ b/lib/lp/charms/model/charmrecipebuildjob.py
@@ -98,7 +98,7 @@ class CharmRecipeBuildJob(StormBase):
         :param metadata: The type-specific variables, as a JSON-compatible
             dict.
         """
-        super(CharmRecipeBuildJob, self).__init__()
+        super().__init__()
         self.job = Job(**job_args)
         self.build = build
         self.job_type = job_type
@@ -151,7 +151,7 @@ class CharmRecipeBuildJobDerived(
 
     def getOopsVars(self):
         """See `IRunnableJob`."""
-        oops_vars = super(CharmRecipeBuildJobDerived, self).getOopsVars()
+        oops_vars = super().getOopsVars()
         recipe = self.context.build.recipe
         oops_vars.extend([
             ("job_id", self.context.job.id),
@@ -271,7 +271,7 @@ class CharmhubUploadJob(CharmRecipeBuildJobDerived):
                       *args, **kwargs):
         edited_fields = set()
         with notify_modified(self.build, edited_fields) as before_modification:
-            getattr(super(CharmhubUploadJob, self), method_name)(
+            getattr(super(), method_name)(
                 *args, manage_transaction=manage_transaction, **kwargs)
             upload_status = self.build.store_upload_status
             if upload_status != before_modification.store_upload_status:
@@ -299,7 +299,7 @@ class CharmhubUploadJob(CharmRecipeBuildJobDerived):
 
     def getOopsVars(self):
         """See `IRunnableJob`."""
-        oops_vars = super(CharmhubUploadJob, self).getOopsVars()
+        oops_vars = super().getOopsVars()
         oops_vars.append(("error_detail", self.error_detail))
         return oops_vars
 
diff --git a/lib/lp/charms/model/charmrecipejob.py b/lib/lp/charms/model/charmrecipejob.py
index 649c965..61a4818 100644
--- a/lib/lp/charms/model/charmrecipejob.py
+++ b/lib/lp/charms/model/charmrecipejob.py
@@ -98,7 +98,7 @@ class CharmRecipeJob(StormBase):
         :param metadata: The type-specific variables, as a JSON-compatible
             dict.
         """
-        super(CharmRecipeJob, self).__init__()
+        super().__init__()
         self.job = Job(**job_args)
         self.recipe = recipe
         self.job_type = job_type
@@ -148,7 +148,7 @@ class CharmRecipeJobDerived(BaseRunnableJob, metaclass=EnumeratedSubclass):
 
     def getOopsVars(self):
         """See `IRunnableJob`."""
-        oops_vars = super(CharmRecipeJobDerived, self).getOopsVars()
+        oops_vars = super().getOopsVars()
         oops_vars.extend([
             ("job_id", self.context.job.id),
             ("job_type", self.context.job_type.title),
diff --git a/lib/lp/charms/tests/test_charmbase.py b/lib/lp/charms/tests/test_charmbase.py
index 42aa994..4d6d175 100644
--- a/lib/lp/charms/tests/test_charmbase.py
+++ b/lib/lp/charms/tests/test_charmbase.py
@@ -3,8 +3,6 @@
 
 """Test bases for charms."""
 
-__metaclass__ = type
-
 from testtools.matchers import (
     ContainsDict,
     Equals,
diff --git a/lib/lp/charms/tests/test_charmrecipe.py b/lib/lp/charms/tests/test_charmrecipe.py
index 280936e..2d1b8e6 100644
--- a/lib/lp/charms/tests/test_charmrecipe.py
+++ b/lib/lp/charms/tests/test_charmrecipe.py
@@ -152,7 +152,7 @@ class TestCharmRecipe(TestCaseWithFactory):
     layer = DatabaseFunctionalLayer
 
     def setUp(self):
-        super(TestCharmRecipe, self).setUp()
+        super().setUp()
         self.useFixture(FeatureFixture({CHARM_RECIPE_ALLOW_CREATE: "on"}))
 
     def test_implements_interfaces(self):
@@ -949,7 +949,7 @@ class TestCharmRecipeDeleteWithBuilds(TestCaseWithFactory):
     layer = LaunchpadFunctionalLayer
 
     def setUp(self):
-        super(TestCharmRecipeDeleteWithBuilds, self).setUp()
+        super().setUp()
         self.useFixture(FeatureFixture({CHARM_RECIPE_ALLOW_CREATE: "on"}))
 
     def test_delete_with_builds(self):
@@ -1032,7 +1032,7 @@ class TestCharmRecipeSet(TestCaseWithFactory):
     layer = DatabaseFunctionalLayer
 
     def setUp(self):
-        super(TestCharmRecipeSet, self).setUp()
+        super().setUp()
         self.useFixture(FeatureFixture({CHARM_RECIPE_ALLOW_CREATE: "on"}))
 
     def test_class_implements_interfaces(self):
diff --git a/lib/lp/charms/tests/test_charmrecipebuild.py b/lib/lp/charms/tests/test_charmrecipebuild.py
index d06dfe0..349f934 100644
--- a/lib/lp/charms/tests/test_charmrecipebuild.py
+++ b/lib/lp/charms/tests/test_charmrecipebuild.py
@@ -94,7 +94,7 @@ class TestCharmRecipeBuild(TestCaseWithFactory):
     layer = LaunchpadZopelessLayer
 
     def setUp(self):
-        super(TestCharmRecipeBuild, self).setUp()
+        super().setUp()
         self.useFixture(FeatureFixture({CHARM_RECIPE_ALLOW_CREATE: "on"}))
         self.build = self.factory.makeCharmRecipeBuild()
 
@@ -671,7 +671,7 @@ class TestCharmRecipeBuildSet(TestCaseWithFactory):
     layer = LaunchpadZopelessLayer
 
     def setUp(self):
-        super(TestCharmRecipeBuildSet, self).setUp()
+        super().setUp()
         self.useFixture(FeatureFixture({CHARM_RECIPE_ALLOW_CREATE: "on"}))
 
     def test_getByBuildFarmJob_works(self):
diff --git a/lib/lp/charms/tests/test_charmrecipebuildbehaviour.py b/lib/lp/charms/tests/test_charmrecipebuildbehaviour.py
index 097fb0d..92aa239 100644
--- a/lib/lp/charms/tests/test_charmrecipebuildbehaviour.py
+++ b/lib/lp/charms/tests/test_charmrecipebuildbehaviour.py
@@ -93,7 +93,7 @@ class TestCharmRecipeBuildBehaviourBase(TestCaseWithFactory):
 
     def setUp(self):
         self.useFixture(FeatureFixture({CHARM_RECIPE_ALLOW_CREATE: "on"}))
-        super(TestCharmRecipeBuildBehaviourBase, self).setUp()
+        super().setUp()
 
     def makeJob(self, distribution=None, **kwargs):
         """Create a sample `ICharmRecipeBuildBehaviour`."""
@@ -174,7 +174,7 @@ class TestAsyncCharmRecipeBuildBehaviour(
 
     @defer.inlineCallbacks
     def setUp(self):
-        super(TestAsyncCharmRecipeBuildBehaviour, self).setUp()
+        super().setUp()
         build_username = 'OCIBUILD-1'
         self.token = {'secret': uuid.uuid4().hex,
                       'username': build_username,
@@ -195,7 +195,7 @@ class TestAsyncCharmRecipeBuildBehaviour(
     def makeJob(self, **kwargs):
         # We need a builder in these tests, in order that requesting a proxy
         # token can piggyback on its reactor and pool.
-        job = super(TestAsyncCharmRecipeBuildBehaviour, self).makeJob(**kwargs)
+        job = super().makeJob(**kwargs)
         builder = MockBuilder()
         builder.processor = job.build.processor
         slave = self.useFixture(SlaveTestHelpers()).getClientSlave()
diff --git a/lib/lp/charms/tests/test_charmrecipejob.py b/lib/lp/charms/tests/test_charmrecipejob.py
index 67bac9a..360d466 100644
--- a/lib/lp/charms/tests/test_charmrecipejob.py
+++ b/lib/lp/charms/tests/test_charmrecipejob.py
@@ -51,7 +51,7 @@ class TestCharmRecipeJob(TestCaseWithFactory):
     layer = ZopelessDatabaseLayer
 
     def setUp(self):
-        super(TestCharmRecipeJob, self).setUp()
+        super().setUp()
         self.useFixture(FeatureFixture({CHARM_RECIPE_ALLOW_CREATE: "on"}))
 
     def test_provides_interface(self):
@@ -67,7 +67,7 @@ class TestCharmRecipeRequestBuildsJob(TestCaseWithFactory):
     layer = ZopelessDatabaseLayer
 
     def setUp(self):
-        super(TestCharmRecipeRequestBuildsJob, self).setUp()
+        super().setUp()
         self.useFixture(FeatureFixture({CHARM_RECIPE_ALLOW_CREATE: "on"}))
 
     def test_provides_interface(self):