← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] ~cjwatson/launchpad:black-code into launchpad:master

 

Colin Watson has proposed merging ~cjwatson/launchpad:black-code into launchpad:master.

Commit message:
lp.code: Apply black

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/+git/launchpad/+merge/427106
-- 
The attached diff has been truncated due to its size.
Your team Launchpad code reviewers is requested to review the proposed merge of ~cjwatson/launchpad:black-code into launchpad:master.
diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs
index 1a2bd19..1ccbf84 100644
--- a/.git-blame-ignore-revs
+++ b/.git-blame-ignore-revs
@@ -70,3 +70,5 @@ c606443bdb2f342593c9a7c9437cb70c01f85f29
 95cf83968d59453397dfbdcbe30556fc8004479a
 # apply black to lp.charms
 a6bed71f3d2fdbceae20c2d435c993e8bededdce
+# apply black to lp.code
+94d8e9842b7c92f3f9b7f514fb49ebdc9af7e413
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index d9fb758..c17b9a5 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -49,6 +49,7 @@ repos:
             |bugs
             |buildmaster
             |charms
+            |code
           )/
 -   repo: https://github.com/PyCQA/isort
     rev: 5.9.2
@@ -74,6 +75,7 @@ repos:
             |bugs
             |buildmaster
             |charms
+            |code
           )/
     -   id: isort
         alias: isort-black
@@ -89,6 +91,7 @@ repos:
             |bugs
             |buildmaster
             |charms
+            |code
           )/
 -   repo: https://github.com/PyCQA/flake8
     rev: 3.9.2
diff --git a/lib/lp/code/adapters/branch.py b/lib/lp/code/adapters/branch.py
index 614eb2d..fd62fa5 100644
--- a/lib/lp/code/adapters/branch.py
+++ b/lib/lp/code/adapters/branch.py
@@ -7,7 +7,7 @@ __all__ = [
     "BranchDelta",
     "BranchMergeProposalDelta",
     "BranchMergeProposalNoPreviewDiffDelta",
-    ]
+]
 
 from contextlib import contextmanager
 
@@ -17,13 +17,9 @@ from lazr.lifecycle.objectdelta import ObjectDelta
 from zope.event import notify
 from zope.interface import implementer
 
-from lp.code.interfaces.branch import (
-    IBranch,
-    IBranchDelta,
-    )
+from lp.code.interfaces.branch import IBranch, IBranchDelta
 from lp.code.interfaces.branchmergeproposal import IBranchMergeProposal
 
-
 # XXX: thumper 2006-12-20: This needs to be extended
 # to cover bugs and specs linked and unlinked, as
 # well as landing target when it is added to the UI
@@ -33,15 +29,23 @@ from lp.code.interfaces.branchmergeproposal import IBranchMergeProposal
 class BranchDelta:
     """See IBranchDelta."""
 
-    delta_values = ('name', 'title', 'url', 'lifecycle_status')
+    delta_values = ("name", "title", "url", "lifecycle_status")
 
-    new_values = ('summary', 'whiteboard')
+    new_values = ("summary", "whiteboard")
 
     interface = IBranch
 
-    def __init__(self, branch, user,
-                 name=None, title=None, summary=None, url=None,
-                 whiteboard=None, lifecycle_status=None):
+    def __init__(
+        self,
+        branch,
+        user,
+        name=None,
+        title=None,
+        summary=None,
+        url=None,
+        whiteboard=None,
+        lifecycle_status=None,
+    ):
         self.branch = branch
         self.user = user
 
@@ -78,15 +82,15 @@ class BranchMergeProposalDelta:
     """Represent changes made to a BranchMergeProposal."""
 
     delta_values = (
-        'registrant',
-        'queue_status',
-        )
+        "registrant",
+        "queue_status",
+    )
     new_values = (
-        'commit_message',
-        'whiteboard',
-        'description',
-        'preview_diff',
-        )
+        "commit_message",
+        "whiteboard",
+        "description",
+        "preview_diff",
+    )
     interface = IBranchMergeProposal
 
     def __init__(self, **kwargs):
@@ -127,11 +131,14 @@ class BranchMergeProposalDelta:
         merge_proposal_snapshot = klass.snapshot(merge_proposal)
         yield
         merge_proposal_delta = klass.construct(
-            merge_proposal_snapshot, merge_proposal)
+            merge_proposal_snapshot, merge_proposal
+        )
         if merge_proposal_delta is not None:
             merge_proposal_event = ObjectModifiedEvent(
-                merge_proposal, merge_proposal_snapshot,
-                list(vars(merge_proposal_delta)))
+                merge_proposal,
+                merge_proposal_snapshot,
+                list(vars(merge_proposal_delta)),
+            )
             notify(merge_proposal_event)
 
 
@@ -142,5 +149,7 @@ class BranchMergeProposalNoPreviewDiffDelta(BranchMergeProposalDelta):
     """
 
     new_values = tuple(
-        name for name in BranchMergeProposalDelta.new_values
-        if name != "preview_diff")
+        name
+        for name in BranchMergeProposalDelta.new_values
+        if name != "preview_diff"
+    )
diff --git a/lib/lp/code/adapters/branchcollection.py b/lib/lp/code/adapters/branchcollection.py
index cfc4d89..14900ec 100644
--- a/lib/lp/code/adapters/branchcollection.py
+++ b/lib/lp/code/adapters/branchcollection.py
@@ -4,14 +4,14 @@
 """Adapters for different objects to branch collections."""
 
 __all__ = [
-    'branch_collection_for_distribution',
-    'branch_collection_for_distro_series',
-    'branch_collection_for_person',
-    'branch_collection_for_product',
-    'branch_collection_for_project_group',
-    'branch_collection_for_source_package',
-    'branch_collection_for_distro_source_package',
-    ]
+    "branch_collection_for_distribution",
+    "branch_collection_for_distro_series",
+    "branch_collection_for_person",
+    "branch_collection_for_product",
+    "branch_collection_for_project_group",
+    "branch_collection_for_source_package",
+    "branch_collection_for_distro_source_package",
+]
 
 
 from zope.component import getUtility
@@ -59,4 +59,5 @@ def branch_collection_for_source_package(source_package):
 def branch_collection_for_distro_source_package(distro_source_package):
     """Adapt a distro_source_package to a branch collection."""
     return getUtility(IAllBranches).inDistributionSourcePackage(
-        distro_source_package)
+        distro_source_package
+    )
diff --git a/lib/lp/code/adapters/gitcollection.py b/lib/lp/code/adapters/gitcollection.py
index 0a7b1ab..c0bb456 100644
--- a/lib/lp/code/adapters/gitcollection.py
+++ b/lib/lp/code/adapters/gitcollection.py
@@ -4,16 +4,16 @@
 """Adapters for different objects to Git repository collections."""
 
 __all__ = [
-    'git_collection_for_distribution',
-    'git_collection_for_distro_source_package',
-    'git_collection_for_oci_project',
-    'git_collection_for_person',
-    'git_collection_for_person_distro_source_package',
-    'git_collection_for_person_oci_project',
-    'git_collection_for_person_product',
-    'git_collection_for_project',
-    'git_collection_for_project_group',
-    ]
+    "git_collection_for_distribution",
+    "git_collection_for_distro_source_package",
+    "git_collection_for_oci_project",
+    "git_collection_for_person",
+    "git_collection_for_person_distro_source_package",
+    "git_collection_for_person_oci_project",
+    "git_collection_for_person_product",
+    "git_collection_for_project",
+    "git_collection_for_project_group",
+]
 
 
 from zope.component import getUtility
@@ -39,7 +39,8 @@ def git_collection_for_distribution(distribution):
 def git_collection_for_distro_source_package(distro_source_package):
     """Adapt a distro_source_package to a Git repository collection."""
     return getUtility(IAllGitRepositories).inDistributionSourcePackage(
-        distro_source_package)
+        distro_source_package
+    )
 
 
 def git_collection_for_oci_project(oci_project):
@@ -64,13 +65,15 @@ def git_collection_for_person_distro_source_package(person_dsp):
     collection."""
     collection = getUtility(IAllGitRepositories).ownedBy(person_dsp.person)
     collection = collection.inDistributionSourcePackage(
-        person_dsp.distro_source_package)
+        person_dsp.distro_source_package
+    )
     return collection
 
 
 def git_collection_for_person_oci_project(person_oci_project):
     """Adapt a PersonOCIProject to a Git repository collection."""
     collection = getUtility(IAllGitRepositories).ownedBy(
-        person_oci_project.person)
+        person_oci_project.person
+    )
     collection = collection.inOCIProject(person_oci_project.oci_project)
     return collection
diff --git a/lib/lp/code/adapters/gitrepository.py b/lib/lp/code/adapters/gitrepository.py
index 085d51a..1687bf3 100644
--- a/lib/lp/code/adapters/gitrepository.py
+++ b/lib/lp/code/adapters/gitrepository.py
@@ -5,7 +5,7 @@
 
 __all__ = [
     "GitRepositoryDelta",
-    ]
+]
 
 from lazr.lifecycle.objectdelta import ObjectDelta
 from zope.interface import implementer
@@ -13,21 +13,22 @@ from zope.interface import implementer
 from lp.code.interfaces.gitrepository import (
     IGitRepository,
     IGitRepositoryDelta,
-    )
+)
 
 
 @implementer(IGitRepositoryDelta)
 class GitRepositoryDelta:
     """See `IGitRepositoryDelta`."""
 
-    delta_values = ('name', 'git_identity')
+    delta_values = ("name", "git_identity")
 
     new_values = ()
 
     interface = IGitRepository
 
-    def __init__(self, repository, user, name=None, git_identity=None,
-                 activities=None):
+    def __init__(
+        self, repository, user, name=None, git_identity=None, activities=None
+    ):
         self.repository = repository
         self.user = user
 
@@ -44,8 +45,11 @@ class GitRepositoryDelta:
         """
         delta = ObjectDelta(old_repository, new_repository)
         delta.recordNewAndOld(klass.delta_values)
-        activities = list(new_repository.getActivity(
-            changed_after=old_repository.date_last_modified))
+        activities = list(
+            new_repository.getActivity(
+                changed_after=old_repository.date_last_modified
+            )
+        )
         if delta.changes or activities:
             changes = delta.changes
             changes["repository"] = new_repository
diff --git a/lib/lp/code/adapters/revisioncache.py b/lib/lp/code/adapters/revisioncache.py
index 6cd8c08..7406adb 100644
--- a/lib/lp/code/adapters/revisioncache.py
+++ b/lib/lp/code/adapters/revisioncache.py
@@ -4,14 +4,14 @@
 """Adapters for different objects to a revision cache."""
 
 __all__ = [
-    'revision_cache_for_distribution',
-    'revision_cache_for_distro_series',
-    'revision_cache_for_person',
-    'revision_cache_for_product',
-    'revision_cache_for_project_group',
-    'revision_cache_for_source_package',
-    'revision_cache_for_distro_source_package',
-    ]
+    "revision_cache_for_distribution",
+    "revision_cache_for_distro_series",
+    "revision_cache_for_person",
+    "revision_cache_for_product",
+    "revision_cache_for_project_group",
+    "revision_cache_for_source_package",
+    "revision_cache_for_distro_source_package",
+]
 
 
 from zope.component import getUtility
@@ -52,4 +52,5 @@ def revision_cache_for_source_package(source_package):
 def revision_cache_for_distro_source_package(distro_source_package):
     """Adapt a distro_source_package to a revision cache."""
     return getUtility(IRevisionCache).inDistributionSourcePackage(
-        distro_source_package)
+        distro_source_package
+    )
diff --git a/lib/lp/code/adapters/tests/test_branch.py b/lib/lp/code/adapters/tests/test_branch.py
index b3292b5..4d83ca1 100644
--- a/lib/lp/code/adapters/tests/test_branch.py
+++ b/lib/lp/code/adapters/tests/test_branch.py
@@ -7,11 +7,7 @@ from lazr.lifecycle.event import ObjectModifiedEvent
 
 from lp.code.adapters.branch import BranchMergeProposalDelta
 from lp.code.enums import BranchMergeProposalStatus
-from lp.testing import (
-    EventRecorder,
-    login,
-    TestCase,
-    )
+from lp.testing import EventRecorder, TestCase, login
 from lp.testing.factory import LaunchpadObjectFactory
 from lp.testing.layers import LaunchpadFunctionalLayer
 
@@ -22,45 +18,52 @@ class TestBranchMergeProposalDelta(TestCase):
 
     def setUp(self):
         TestCase.setUp(self)
-        login('foo.bar@xxxxxxxxxxxxx')
+        login("foo.bar@xxxxxxxxxxxxx")
         self.factory = LaunchpadObjectFactory()
 
     def test_snapshot(self):
         """Test that the snapshot method produces a reasonable snapshot"""
         merge_proposal = self.factory.makeBranchMergeProposal()
-        merge_proposal.commit_message = 'foo'
-        merge_proposal.whiteboard = 'bar'
+        merge_proposal.commit_message = "foo"
+        merge_proposal.whiteboard = "bar"
         snapshot = BranchMergeProposalDelta.snapshot(merge_proposal)
-        self.assertEqual('foo', snapshot.commit_message)
-        self.assertEqual('bar', snapshot.whiteboard)
+        self.assertEqual("foo", snapshot.commit_message)
+        self.assertEqual("bar", snapshot.whiteboard)
 
     def test_noModification(self):
         """When there are no modifications, no delta should be returned."""
         merge_proposal = self.factory.makeBranchMergeProposal()
         old_merge_proposal = BranchMergeProposalDelta.snapshot(merge_proposal)
         delta = BranchMergeProposalDelta.construct(
-            old_merge_proposal, merge_proposal)
+            old_merge_proposal, merge_proposal
+        )
         assert delta is None
 
     def test_Modification(self):
         """When there are modifications, the delta reflects them."""
         registrant = self.factory.makePerson(
-            displayname='Baz Qux', email='baz.qux@xxxxxxxxxxx')
+            displayname="Baz Qux", email="baz.qux@xxxxxxxxxxx"
+        )
         merge_proposal = self.factory.makeBranchMergeProposal(
-            registrant=registrant)
+            registrant=registrant
+        )
         old_merge_proposal = BranchMergeProposalDelta.snapshot(merge_proposal)
-        merge_proposal.commit_message = 'Change foo into bar.'
-        merge_proposal.description = 'Set the description.'
+        merge_proposal.commit_message = "Change foo into bar."
+        merge_proposal.description = "Set the description."
         merge_proposal.markAsMerged()
         delta = BranchMergeProposalDelta.construct(
-            old_merge_proposal, merge_proposal)
+            old_merge_proposal, merge_proposal
+        )
         assert delta is not None
-        self.assertEqual('Change foo into bar.', delta.commit_message)
-        self.assertEqual('Set the description.', delta.description)
+        self.assertEqual("Change foo into bar.", delta.commit_message)
+        self.assertEqual("Set the description.", delta.description)
         self.assertEqual(
-            {'old': BranchMergeProposalStatus.WORK_IN_PROGRESS,
-            'new': BranchMergeProposalStatus.MERGED},
-            delta.queue_status)
+            {
+                "old": BranchMergeProposalStatus.WORK_IN_PROGRESS,
+                "new": BranchMergeProposalStatus.MERGED,
+            },
+            delta.queue_status,
+        )
 
     def test_monitor(self):
         """\
@@ -83,5 +86,5 @@ class TestBranchMergeProposalDelta(TestCase):
             self.assertIsInstance(event, ObjectModifiedEvent)
             self.assertEqual(merge_proposal, event.object)
             self.assertContentEqual(
-                ["commit_message", "whiteboard"],
-                event.edited_fields)
+                ["commit_message", "whiteboard"], event.edited_fields
+            )
diff --git a/lib/lp/code/adapters/tests/test_branchcollection.py b/lib/lp/code/adapters/tests/test_branchcollection.py
index ed66a77..a2fdbaa 100644
--- a/lib/lp/code/adapters/tests/test_branchcollection.py
+++ b/lib/lp/code/adapters/tests/test_branchcollection.py
@@ -26,6 +26,7 @@ class TestPersonProduct(TestCaseWithFactory):
         self.factory.makeProductBranch(product=product)
         self.factory.makeBranch(owner=person)
         person_product_branch = self.factory.makeProductBranch(
-            owner=person, product=product)
+            owner=person, product=product
+        )
         branches = IBranchCollection(person_product).getBranches()
         self.assertEqual([person_product_branch], [b for b in branches])
diff --git a/lib/lp/code/adapters/tests/test_gitrepository.py b/lib/lp/code/adapters/tests/test_gitrepository.py
index 9983274..94282d4 100644
--- a/lib/lp/code/adapters/tests/test_gitrepository.py
+++ b/lib/lp/code/adapters/tests/test_gitrepository.py
@@ -6,10 +6,7 @@ from testtools.matchers import MatchesStructure
 from zope.interface import providedBy
 
 from lp.code.adapters.gitrepository import GitRepositoryDelta
-from lp.testing import (
-    person_logged_in,
-    TestCaseWithFactory,
-    )
+from lp.testing import TestCaseWithFactory, person_logged_in
 from lp.testing.layers import LaunchpadFunctionalLayer
 
 
@@ -22,7 +19,8 @@ class TestGitRepositoryDelta(TestCaseWithFactory):
         repository = self.factory.makeGitRepository(name="foo")
         old_repository = Snapshot(repository, providing=providedBy(repository))
         delta = GitRepositoryDelta.construct(
-            old_repository, repository, repository.owner)
+            old_repository, repository, repository.owner
+        )
         self.assertIsNone(delta)
 
     def test_modification(self):
@@ -30,18 +28,23 @@ class TestGitRepositoryDelta(TestCaseWithFactory):
         owner = self.factory.makePerson(name="person")
         project = self.factory.makeProduct(name="project")
         repository = self.factory.makeGitRepository(
-            owner=owner, target=project, name="foo")
+            owner=owner, target=project, name="foo"
+        )
         old_repository = Snapshot(repository, providing=providedBy(repository))
         with person_logged_in(repository.owner):
             repository.setName("bar", repository.owner)
         delta = GitRepositoryDelta.construct(old_repository, repository, owner)
         self.assertIsNotNone(delta)
-        self.assertThat(delta, MatchesStructure.byEquality(
-            name={
-                "old": "foo",
-                "new": "bar",
+        self.assertThat(
+            delta,
+            MatchesStructure.byEquality(
+                name={
+                    "old": "foo",
+                    "new": "bar",
                 },
-            git_identity={
-                "old": "lp:~person/project/+git/foo",
-                "new": "lp:~person/project/+git/bar",
-                }))
+                git_identity={
+                    "old": "lp:~person/project/+git/foo",
+                    "new": "lp:~person/project/+git/bar",
+                },
+            ),
+        )
diff --git a/lib/lp/code/browser/bazaar.py b/lib/lp/code/browser/bazaar.py
index 3c98b5f..9a65327 100644
--- a/lib/lp/code/browser/bazaar.py
+++ b/lib/lp/code/browser/bazaar.py
@@ -4,9 +4,9 @@
 """View support classes for the bazaar application."""
 
 __all__ = [
-    'BazaarApplicationView',
-    'BazaarProductView',
-    ]
+    "BazaarApplicationView",
+    "BazaarProductView",
+]
 
 from datetime import datetime
 
@@ -14,25 +14,19 @@ import breezy
 from zope.component import getUtility
 
 from lp.code.enums import CodeImportReviewStatus
-from lp.code.interfaces.branch import (
-    IBranchCloud,
-    IBranchSet,
-    )
+from lp.code.interfaces.branch import IBranchCloud, IBranchSet
 from lp.code.interfaces.branchcollection import IAllBranches
 from lp.code.interfaces.codeimport import ICodeImportSet
 from lp.registry.interfaces.product import IProductSet
 from lp.services.config import config
 from lp.services.propertycache import cachedproperty
-from lp.services.webapp import (
-    canonical_url,
-    LaunchpadView,
-    )
+from lp.services.webapp import LaunchpadView, canonical_url
 from lp.services.webapp.authorization import precache_permission_for_objects
 
 
 class BazaarApplicationView(LaunchpadView):
 
-    page_title = 'Launchpad Branches'
+    page_title = "Launchpad Branches"
 
     @property
     def branch_count(self):
@@ -45,8 +39,11 @@ class BazaarApplicationView(LaunchpadView):
 
     @property
     def import_count(self):
-        return getUtility(ICodeImportSet).search(
-            review_status=CodeImportReviewStatus.REVIEWED).count()
+        return (
+            getUtility(ICodeImportSet)
+            .search(review_status=CodeImportReviewStatus.REVIEWED)
+            .count()
+        )
 
     @property
     def brz_version(self):
@@ -58,42 +55,55 @@ class BazaarApplicationView(LaunchpadView):
         # Until there is an API to do this nicely, shove the launchpad.view
         # permission into the request cache directly.
         precache_permission_for_objects(
-            self.request, 'launchpad.View', branches)
+            self.request, "launchpad.View", branches
+        )
         return branches
 
     @cachedproperty
     def recently_changed_branches(self):
         """Return the five most recently changed branches."""
         return self._precacheViewPermissions(
-            list(getUtility(IBranchSet).getRecentlyChangedBranches(
-                    5, visible_by_user=self.user)))
+            list(
+                getUtility(IBranchSet).getRecentlyChangedBranches(
+                    5, visible_by_user=self.user
+                )
+            )
+        )
 
     @cachedproperty
     def recently_imported_branches(self):
         """Return the five most recently imported branches."""
         return self._precacheViewPermissions(
-            list(getUtility(IBranchSet).getRecentlyImportedBranches(
-                    5, visible_by_user=self.user)))
+            list(
+                getUtility(IBranchSet).getRecentlyImportedBranches(
+                    5, visible_by_user=self.user
+                )
+            )
+        )
 
     @cachedproperty
     def recently_registered_branches(self):
         """Return the five most recently registered branches."""
         return self._precacheViewPermissions(
-            list(getUtility(IBranchSet).getRecentlyRegisteredBranches(
-                    5, visible_by_user=self.user)))
+            list(
+                getUtility(IBranchSet).getRecentlyRegisteredBranches(
+                    5, visible_by_user=self.user
+                )
+            )
+        )
 
     @cachedproperty
     def short_product_tag_cloud(self):
         """Show a preview of the product tag cloud."""
         return BazaarProductView(None, None).products(
-            num_products=config.launchpad.code_homepage_product_cloud_size)
+            num_products=config.launchpad.code_homepage_product_cloud_size
+        )
 
 
 class ProductInfo:
-
     def __init__(self, name, commits, author_count, size, elapsed):
         self.name = name
-        self.url = '/' + name
+        self.url = "/" + name
         self.commits = commits
         self.author_count = author_count
         self.size = size
@@ -132,8 +142,7 @@ class ProductInfo:
         elif self.elapsed_since_commit.days == 1:
             commit = "last commit one day old"
         else:
-            commit = (
-                "last commit %d days old" % self.elapsed_since_commit.days)
+            commit = "last commit %d days old" % self.elapsed_since_commit.days
         return "%s by %s, %s" % (size, who, commit)
 
 
@@ -151,7 +160,7 @@ class BazaarProjectsRedirect(LaunchpadView):
 class BazaarProductView(LaunchpadView):
     """Browser class for products gettable with Bazaar."""
 
-    page_title = 'Projects with active branches'
+    page_title = "Projects with active branches"
 
     def _make_distribution_map(self, values, percentile_map):
         """Given some values and a map of percentiles to other values, return
@@ -160,6 +169,7 @@ class BazaarProductView(LaunchpadView):
 
         There *must* be a percentile_map entry for 1.0.
         """
+
         def constrained_minimum(xs, a):
             """Return the smallest value of 'xs' strictly bigger than 'a'."""
             return min(x for x in xs if x > a)
@@ -179,20 +189,20 @@ class BazaarProductView(LaunchpadView):
         # is the first item of the tuple returned, and is guaranteed to be
         # unique by the sql query.
         product_info = sorted(
-            getUtility(IBranchCloud).getProductsWithInfo(num_products))
+            getUtility(IBranchCloud).getProductsWithInfo(num_products)
+        )
         if len(product_info) == 0:
             return
         now = datetime.today()
         counts = sorted(list(zip(*product_info))[1])
         size_mapping = {
-            0.2: 'smallest',
-            0.4: 'small',
-            0.6: 'medium',
-            0.8: 'large',
-            1.0: 'largest',
-            }
-        num_commits_to_size = self._make_distribution_map(
-            counts, size_mapping)
+            0.2: "smallest",
+            0.4: "small",
+            0.6: "medium",
+            0.8: "large",
+            1.0: "largest",
+        }
+        num_commits_to_size = self._make_distribution_map(counts, size_mapping)
 
         for name, commits, author_count, last_revision_date in product_info:
             size = num_commits_to_size[commits]
diff --git a/lib/lp/code/browser/branch.py b/lib/lp/code/browser/branch.py
index 0bdeb46..1442323 100644
--- a/lib/lp/code/browser/branch.py
+++ b/lib/lp/code/browser/branch.py
@@ -4,65 +4,51 @@
 """Branch views."""
 
 __all__ = [
-    'BranchBreadcrumb',
-    'BranchContextMenu',
-    'BranchDeletionView',
-    'BranchEditStatusView',
-    'BranchEditView',
-    'BranchEditWhiteboardView',
-    'BranchReviewerEditView',
-    'BranchMirrorStatusView',
-    'BranchMirrorMixin',
-    'BranchNavigation',
-    'BranchEditMenu',
-    'BranchUpgradeView',
-    'BranchURL',
-    'BranchView',
-    'CodeEditOwnerMixin',
-    'RegisterBranchMergeProposalView',
-    ]
+    "BranchBreadcrumb",
+    "BranchContextMenu",
+    "BranchDeletionView",
+    "BranchEditStatusView",
+    "BranchEditView",
+    "BranchEditWhiteboardView",
+    "BranchReviewerEditView",
+    "BranchMirrorStatusView",
+    "BranchMirrorMixin",
+    "BranchNavigation",
+    "BranchEditMenu",
+    "BranchUpgradeView",
+    "BranchURL",
+    "BranchView",
+    "CodeEditOwnerMixin",
+    "RegisterBranchMergeProposalView",
+]
 
 from datetime import datetime
 
+import pytz
+import simplejson
 from lazr.lifecycle.event import ObjectModifiedEvent
 from lazr.lifecycle.snapshot import Snapshot
 from lazr.restful.fields import Reference
-from lazr.restful.interface import (
-    copy_field,
-    use_template,
-    )
+from lazr.restful.interface import copy_field, use_template
 from lazr.uri import URI
-import pytz
-import simplejson
 from zope.component import getUtility
 from zope.event import notify
 from zope.formlib import form
 from zope.formlib.widget import CustomWidgetFactory
 from zope.formlib.widgets import TextAreaWidget
-from zope.interface import (
-    implementer,
-    Interface,
-    providedBy,
-    )
+from zope.interface import Interface, implementer, providedBy
 from zope.publisher.interfaces import NotFound
 from zope.publisher.interfaces.browser import IBrowserPublisher
-from zope.schema import (
-    Bool,
-    Choice,
-    Text,
-    )
-from zope.schema.vocabulary import (
-    SimpleTerm,
-    SimpleVocabulary,
-    )
+from zope.schema import Bool, Choice, Text
+from zope.schema.vocabulary import SimpleTerm, SimpleVocabulary
 
 from lp import _
 from lp.app.browser.informationtype import InformationTypePortletMixin
 from lp.app.browser.launchpadform import (
-    action,
     LaunchpadEditFormView,
     LaunchpadFormView,
-    )
+    action,
+)
 from lp.app.browser.lazrjs import EnumChoiceWidget
 from lp.app.enums import InformationType
 from lp.app.interfaces.launchpad import ILaunchpadCelebrities
@@ -72,14 +58,11 @@ from lp.app.widgets.suggestion import TargetBranchWidget
 from lp.blueprints.interfaces.specificationbranch import ISpecificationBranch
 from lp.bugs.interfaces.bug import IBugSet
 from lp.bugs.interfaces.bugbranch import IBugBranch
-from lp.bugs.interfaces.bugtask import (
-    IBugTaskSet,
-    UNRESOLVED_BUGTASK_STATUSES,
-    )
+from lp.bugs.interfaces.bugtask import UNRESOLVED_BUGTASK_STATUSES, IBugTaskSet
 from lp.bugs.interfaces.bugtasksearch import BugTaskSearchParams
 from lp.code.browser.branchmergeproposal import (
     latest_proposals_for_each_branch,
-    )
+)
 from lp.code.browser.branchref import BranchRef
 from lp.code.browser.codeimport import CodeImportTargetMixin
 from lp.code.browser.decorations import DecoratedBranch
@@ -92,11 +75,8 @@ from lp.code.errors import (
     BranchTargetError,
     CannotUpgradeBranch,
     InvalidBranchMergeProposal,
-    )
-from lp.code.interfaces.branch import (
-    IBranch,
-    IBranchSet,
-    )
+)
+from lp.code.interfaces.branch import IBranch, IBranchSet
 from lp.code.interfaces.branchcollection import IAllBranches
 from lp.code.interfaces.branchmergeproposal import IBranchMergeProposal
 from lp.code.interfaces.branchtarget import IBranchTarget
@@ -108,50 +88,41 @@ from lp.services import searchbuilder
 from lp.services.config import config
 from lp.services.database.constants import UTC_NOW
 from lp.services.features import getFeatureFlag
-from lp.services.feeds.browser import (
-    BranchFeedLink,
-    FeedsMixin,
-    )
-from lp.services.helpers import (
-    english_list,
-    truncate_text,
-    )
+from lp.services.feeds.browser import BranchFeedLink, FeedsMixin
+from lp.services.helpers import english_list, truncate_text
 from lp.services.job.interfaces.job import JobStatus
 from lp.services.propertycache import cachedproperty
 from lp.services.webapp import (
-    canonical_url,
     ContextMenu,
-    enabled_with_permission,
     LaunchpadView,
     Link,
     Navigation,
     NavigationMenu,
+    canonical_url,
+    enabled_with_permission,
     stepthrough,
     stepto,
-    )
+)
 from lp.services.webapp.authorization import (
     check_permission,
     precache_permission_for_objects,
-    )
+)
 from lp.services.webapp.breadcrumb import NameBreadcrumb
 from lp.services.webapp.escaping import structured
 from lp.services.webapp.interfaces import ICanonicalUrlData
 from lp.services.webapp.publisher import DataDownloadView
 from lp.services.webhooks.browser import WebhookTargetNavigationMixin
-from lp.snappy.browser.hassnaps import (
-    HasSnapsMenuMixin,
-    HasSnapsViewMixin,
-    )
+from lp.snappy.browser.hassnaps import HasSnapsMenuMixin, HasSnapsViewMixin
 from lp.translations.interfaces.translationtemplatesbuild import (
     ITranslationTemplatesBuildSource,
-    )
+)
 
 
 @implementer(ICanonicalUrlData)
 class BranchURL:
     """Branch URL creation rules."""
 
-    rootsite = 'code'
+    rootsite = "code"
     inside = None
 
     def __init__(self, branch):
@@ -163,7 +134,6 @@ class BranchURL:
 
 
 class BranchBreadcrumb(NameBreadcrumb):
-
     @property
     def inside(self):
         return self.context.target.components[-1]
@@ -229,6 +199,7 @@ class BranchNavigation(WebhookTargetNavigationMixin, Navigation):
     def traverse_translation_templates_build(self, id_string):
         """Traverses to a `TranslationTemplatesBuild`."""
         from lp.soyuz.browser.build import get_build_by_id_str
+
         ttb = get_build_by_id_str(ITranslationTemplatesBuildSource, id_string)
         if ttb is None or ttb.branch != self.context:
             return None
@@ -239,103 +210,117 @@ class BranchEditMenu(NavigationMenu):
     """Edit menu for IBranch."""
 
     usedfor = IBranch
-    facet = 'branches'
-    title = 'Edit branch'
-    links = (
-        'edit', 'reviewer', 'edit_whiteboard', 'webhooks', 'delete')
+    facet = "branches"
+    title = "Edit branch"
+    links = ("edit", "reviewer", "edit_whiteboard", "webhooks", "delete")
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def edit(self):
-        text = 'Change branch details'
-        return Link('+edit', text, icon='edit')
+        text = "Change branch details"
+        return Link("+edit", text, icon="edit")
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def delete(self):
-        text = 'Delete branch'
-        return Link('+delete', text, icon='trash-icon')
+        text = "Delete branch"
+        return Link("+delete", text, icon="trash-icon")
 
-    @enabled_with_permission('launchpad.AnyPerson')
+    @enabled_with_permission("launchpad.AnyPerson")
     def edit_whiteboard(self):
-        text = 'Edit whiteboard'
+        text = "Edit whiteboard"
         enabled = self.context.branch_type == BranchType.IMPORTED
-        return Link(
-            '+whiteboard', text, icon='edit', enabled=enabled)
+        return Link("+whiteboard", text, icon="edit", enabled=enabled)
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def reviewer(self):
-        text = 'Set branch reviewer'
-        return Link('+reviewer', text, icon='edit')
+        text = "Set branch reviewer"
+        return Link("+reviewer", text, icon="edit")
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def webhooks(self):
-        text = 'Manage webhooks'
+        text = "Manage webhooks"
         return Link(
-            '+webhooks', text, icon='edit',
-            enabled=bool(getFeatureFlag('webhooks.new.enabled')))
+            "+webhooks",
+            text,
+            icon="edit",
+            enabled=bool(getFeatureFlag("webhooks.new.enabled")),
+        )
 
 
 class BranchContextMenu(ContextMenu, HasRecipesMenuMixin, HasSnapsMenuMixin):
     """Context menu for branches."""
 
     usedfor = IBranch
-    facet = 'branches'
+    facet = "branches"
     links = [
-        'add_subscriber', 'browse_revisions', 'create_recipe', 'create_snap',
-        'link_bug', 'link_blueprint', 'register_merge', 'source',
-        'subscription', 'edit_status', 'edit_import', 'upgrade_branch',
-        'view_recipes', 'view_snaps', 'visibility']
+        "add_subscriber",
+        "browse_revisions",
+        "create_recipe",
+        "create_snap",
+        "link_bug",
+        "link_blueprint",
+        "register_merge",
+        "source",
+        "subscription",
+        "edit_status",
+        "edit_import",
+        "upgrade_branch",
+        "view_recipes",
+        "view_snaps",
+        "visibility",
+    ]
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def edit_status(self):
-        text = 'Change branch status'
-        return Link('+edit-status', text, icon='edit')
+        text = "Change branch status"
+        return Link("+edit-status", text, icon="edit")
 
-    @enabled_with_permission('launchpad.Moderate')
+    @enabled_with_permission("launchpad.Moderate")
     def visibility(self):
         """Return the 'Set information type' Link."""
-        text = 'Change information type'
-        return Link('+edit-information-type', text)
+        text = "Change information type"
+        return Link("+edit-information-type", text)
 
     def browse_revisions(self):
         """Return a link to the branch's revisions on codebrowse."""
-        text = 'All revisions'
+        text = "All revisions"
         enabled = self.context.code_is_browseable
-        url = self.context.getCodebrowseUrl('changes')
+        url = self.context.getCodebrowseUrl("changes")
         return Link(url, text, enabled=enabled)
 
-    @enabled_with_permission('launchpad.AnyPerson')
+    @enabled_with_permission("launchpad.AnyPerson")
     def subscription(self):
         if self.context.hasSubscription(self.user):
-            url = '+edit-subscription'
-            text = 'Edit your subscription'
-            icon = 'edit'
+            url = "+edit-subscription"
+            text = "Edit your subscription"
+            icon = "edit"
         else:
-            url = '+subscribe'
-            text = 'Subscribe yourself'
-            icon = 'add'
+            url = "+subscribe"
+            text = "Subscribe yourself"
+            icon = "add"
         return Link(url, text, icon=icon)
 
-    @enabled_with_permission('launchpad.AnyPerson')
+    @enabled_with_permission("launchpad.AnyPerson")
     def add_subscriber(self):
-        text = 'Subscribe someone else'
-        return Link('+addsubscriber', text, icon='add')
+        text = "Subscribe someone else"
+        return Link("+addsubscriber", text, icon="add")
 
     def register_merge(self):
-        text = 'Propose for merging'
+        text = "Propose for merging"
         enabled = (
-            self.context.target.supports_merge_proposals and
-            not self.context.branch_type == BranchType.IMPORTED)
-        return Link('+register-merge', text, icon='add', enabled=enabled)
+            self.context.target.supports_merge_proposals
+            and not self.context.branch_type == BranchType.IMPORTED
+        )
+        return Link("+register-merge", text, icon="add", enabled=enabled)
 
     def link_bug(self):
-        text = 'Link a bug report'
-        return Link('+linkbug', text, icon='add')
+        text = "Link a bug report"
+        return Link("+linkbug", text, icon="add")
 
     def link_blueprint(self):
         if list(self.context.getSpecificationLinks(self.user)):
-            text = 'Link to another blueprint'
+            text = "Link to another blueprint"
         else:
-            text = 'Link to a blueprint'
+            text = "Link to a blueprint"
         # XXX: JonathanLange 2009-05-13 spec=package-branches: Actually,
         # distroseries can also have blueprints, so it makes sense to
         # associate package-branches with them.
@@ -343,33 +328,35 @@ class BranchContextMenu(ContextMenu, HasRecipesMenuMixin, HasSnapsMenuMixin):
         # Since the blueprints are only related to products, there is no
         # point showing this link if the branch is junk.
         enabled = self.context.product is not None
-        return Link('+linkblueprint', text, icon='add', enabled=enabled)
+        return Link("+linkblueprint", text, icon="add", enabled=enabled)
 
     def source(self):
         """Return a link to the branch's file listing on codebrowse."""
-        text = 'Browse the code'
+        text = "Browse the code"
         enabled = self.context.code_is_browseable
-        url = self.context.getCodebrowseUrl('files')
-        return Link(url, text, icon='info', enabled=enabled)
+        url = self.context.getCodebrowseUrl("files")
+        return Link(url, text, icon="info", enabled=enabled)
 
     def edit_import(self):
-        text = 'Edit import source or review import'
+        text = "Edit import source or review import"
         enabled = (
-            self.context.branch_type == BranchType.IMPORTED and
-            check_permission('launchpad.Edit', self.context.code_import))
-        return Link('+edit-import', text, icon='edit', enabled=enabled)
+            self.context.branch_type == BranchType.IMPORTED
+            and check_permission("launchpad.Edit", self.context.code_import)
+        )
+        return Link("+edit-import", text, icon="edit", enabled=enabled)
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def upgrade_branch(self):
         enabled = self.context.needs_upgrading
         return Link(
-            '+upgrade', 'Upgrade this branch', icon='edit', enabled=enabled)
+            "+upgrade", "Upgrade this branch", icon="edit", enabled=enabled
+        )
 
     def create_recipe(self):
         # You can't create a recipe for a private branch.
         enabled = not self.context.private
-        text = 'Create packaging recipe'
-        return Link('+new-recipe', text, enabled=enabled, icon='add')
+        text = "Create packaging recipe"
+        return Link("+new-recipe", text, enabled=enabled, icon="add")
 
 
 class BranchMirrorMixin:
@@ -384,7 +371,7 @@ class BranchMirrorMixin:
         branch = self.branch
 
         # If the user has edit permissions, then show the actual location.
-        if branch.url is None or check_permission('launchpad.Edit', branch):
+        if branch.url is None or check_permission("launchpad.Edit", branch):
             return branch.url
 
         # XXX: Tim Penhey, 2008-05-30, bug 235916
@@ -392,23 +379,27 @@ class BranchMirrorMixin:
         # specifying whether or not they want the mirror location
         # hidden or not.  Given that this is a database patch,
         # it isn't going to happen today.
-        hosts = config.codehosting.private_mirror_hosts.split(',')
+        hosts = config.codehosting.private_mirror_hosts.split(",")
         private_mirror_hosts = [name.strip() for name in hosts]
 
         uri = URI(branch.url)
         for private_host in private_mirror_hosts:
             if uri.underDomain(private_host):
-                return '<private server>'
+                return "<private server>"
 
         return branch.url
 
 
-class BranchView(InformationTypePortletMixin, FeedsMixin, BranchMirrorMixin,
-                 LaunchpadView, HasSnapsViewMixin, CodeImportTargetMixin):
+class BranchView(
+    InformationTypePortletMixin,
+    FeedsMixin,
+    BranchMirrorMixin,
+    LaunchpadView,
+    HasSnapsViewMixin,
+    CodeImportTargetMixin,
+):
 
-    feed_types = (
-        BranchFeedLink,
-        )
+    feed_types = (BranchFeedLink,)
 
     @property
     def page_title(self):
@@ -426,7 +417,8 @@ class BranchView(InformationTypePortletMixin, FeedsMixin, BranchMirrorMixin,
         authorised_people = [self.branch.owner]
         if self.user is not None:
             precache_permission_for_objects(
-                self.request, "launchpad.LimitedView", authorised_people)
+                self.request, "launchpad.LimitedView", authorised_people
+            )
         # Replace our context with a decorated branch, if it is not already
         # decorated.
         if not isinstance(self.context, DecoratedBranch):
@@ -457,10 +449,11 @@ class BranchView(InformationTypePortletMixin, FeedsMixin, BranchMirrorMixin,
     def has_metadata(self):
         """Return whether there is branch metadata to display."""
         return (
-            self.context.branch_format or
-            self.context.repository_format or
-            self.context.control_format or
-            self.context.stacked_on)
+            self.context.branch_format
+            or self.context.repository_format
+            or self.context.control_format
+            or self.context.stacked_on
+        )
 
     @property
     def is_empty_directory(self):
@@ -492,12 +485,14 @@ class BranchView(InformationTypePortletMixin, FeedsMixin, BranchMirrorMixin,
         branch = self.context
         if branch.branch_type != BranchType.HOSTED:
             return False
-        return check_permission('launchpad.Edit', branch)
+        return check_permission("launchpad.Edit", branch)
 
     def user_can_download(self):
         """Whether the user can download this branch."""
-        return (self.context.branch_type != BranchType.REMOTE and
-                self.context.revision_count > 0)
+        return (
+            self.context.branch_type != BranchType.REMOTE
+            and self.context.revision_count > 0
+        )
 
     @cachedproperty
     def landing_targets(self):
@@ -515,8 +510,11 @@ class BranchView(InformationTypePortletMixin, FeedsMixin, BranchMirrorMixin,
     def landing_candidates(self):
         """Return a decorated list of landing candidates."""
         candidates = self.context.getPrecachedLandingCandidates(self.user)
-        return [proposal for proposal in candidates
-                if check_permission('launchpad.View', proposal)]
+        return [
+            proposal
+            for proposal in candidates
+            if check_permission("launchpad.View", proposal)
+        ]
 
     @property
     def recipes_link(self):
@@ -524,17 +522,18 @@ class BranchView(InformationTypePortletMixin, FeedsMixin, BranchMirrorMixin,
         count = self.context.recipes.count()
         if count == 0:
             # Nothing to link to.
-            return 'No recipes using this branch.'
+            return "No recipes using this branch."
         elif count == 1:
             # Link to the single recipe.
             return structured(
                 '<a href="%s">1 recipe</a> using this branch.',
-                canonical_url(self.context.recipes.one())).escapedtext
+                canonical_url(self.context.recipes.one()),
+            ).escapedtext
         else:
             # Link to a recipe listing.
             return structured(
-                '<a href="+recipes">%s recipes</a> using this branch.',
-                count).escapedtext
+                '<a href="+recipes">%s recipes</a> using this branch.', count
+            ).escapedtext
 
     @property
     def is_imported(self):
@@ -553,11 +552,11 @@ class BranchView(InformationTypePortletMixin, FeedsMixin, BranchMirrorMixin,
     def _getBranchCountText(self, count):
         """Help to show user friendly text."""
         if count == 0:
-            return 'No branches'
+            return "No branches"
         elif count == 1:
-            return '1 branch'
+            return "1 branch"
         else:
-            return '%s branches' % count
+            return "%s branches" % count
 
     @cachedproperty
     def dependent_branch_count_text(self):
@@ -571,15 +570,21 @@ class BranchView(InformationTypePortletMixin, FeedsMixin, BranchMirrorMixin,
 
     @cachedproperty
     def dependent_branches(self):
-        return [branch for branch in self.context.dependent_branches
-                if check_permission('launchpad.View', branch)]
+        return [
+            branch
+            for branch in self.context.dependent_branches
+            if check_permission("launchpad.View", branch)
+        ]
 
     @cachedproperty
     def no_merges(self):
         """Return true if there are no pending merges"""
-        return (len(self.landing_targets) +
-                len(self.landing_candidates) +
-                len(self.dependent_branches) == 0)
+        return (
+            len(self.landing_targets)
+            + len(self.landing_candidates)
+            + len(self.dependent_branches)
+            == 0
+        )
 
     @property
     def show_rescan_link(self):
@@ -598,14 +603,14 @@ class BranchView(InformationTypePortletMixin, FeedsMixin, BranchMirrorMixin,
             status_filter = searchbuilder.any(*UNRESOLVED_BUGTASK_STATUSES)
         else:
             status_filter = None
-        return list(self.context.getLinkedBugTasks(
-            self.user, status_filter))
+        return list(self.context.getLinkedBugTasks(self.user, status_filter))
 
     @cachedproperty
     def revision_info(self):
         collection = getUtility(IAllBranches).visibleByUser(self.user)
         return collection.getExtendedRevisionDetails(
-            self.user, self.context.latest_revisions)
+            self.user, self.context.latest_revisions
+        )
 
     @property
     def show_merge_links(self):
@@ -631,8 +636,11 @@ class BranchView(InformationTypePortletMixin, FeedsMixin, BranchMirrorMixin,
     def status_widget(self):
         """The config to configure the ChoiceSource JS widget."""
         return EnumChoiceWidget(
-            self.context.branch, IBranch['lifecycle_status'],
-            header='Change status to', css_class_prefix='branchstatus')
+            self.context.branch,
+            IBranch["lifecycle_status"],
+            header="Change status to",
+            css_class_prefix="branchstatus",
+        )
 
     @property
     def spec_links(self):
@@ -645,7 +653,7 @@ class BranchRescanView(LaunchpadEditFormView):
 
     field_names = []
 
-    @action('Rescan', name='rescan')
+    @action("Rescan", name="rescan")
     def rescan(self, action, data):
         self.context.unscan(rescan=True)
         self.request.response.addNotification("Branch scan scheduled")
@@ -679,17 +687,19 @@ class BranchEditFormView(LaunchpadEditFormView):
                 InformationType.USERDATA,
                 InformationType.PROPRIETARY,
                 InformationType.EMBARGOED,
-                )
+            )
 
             # XXX Once Branch Visibility Policies are removed, we only want to
             # show Private (USERDATA) if the branch is linked to such a bug.
             hidden_types = (
                 # InformationType.USERDATA,
-                )
+            )
             if set(allowed_types).intersection(hidden_types):
                 params = BugTaskSearchParams(
-                    user=self.user, linked_branches=self.context.id,
-                    information_type=hidden_types)
+                    user=self.user,
+                    linked_branches=self.context.id,
+                    information_type=hidden_types,
+                )
                 if not getUtility(IBugTaskSet).searchBugIds(params).is_empty():
                     shown_types += hidden_types
 
@@ -711,29 +721,40 @@ class BranchEditFormView(LaunchpadEditFormView):
             attribute, but in this case we actually want the user to be
             able to edit it.
             """
-            use_template(IBranch, include=[
-                'name',
-                'url',
-                'description',
-                'lifecycle_status',
-                'whiteboard',
-                ])
+
+            use_template(
+                IBranch,
+                include=[
+                    "name",
+                    "url",
+                    "description",
+                    "lifecycle_status",
+                    "whiteboard",
+                ],
+            )
             information_type = copy_field(
-                IBranch['information_type'], readonly=False,
-                vocabulary=InformationTypeVocabulary(types=info_types))
-            reviewer = copy_field(IBranch['reviewer'], required=True)
-            owner = copy_field(IBranch['owner'], readonly=False)
+                IBranch["information_type"],
+                readonly=False,
+                vocabulary=InformationTypeVocabulary(types=info_types),
+            )
+            reviewer = copy_field(IBranch["reviewer"], required=True)
+            owner = copy_field(IBranch["owner"], readonly=False)
             target = Reference(
-                title=_('Branch target'), required=True,
+                title=_("Branch target"),
+                required=True,
                 schema=IBranchTarget,
-                description=_('The project (if any) this branch pertains to. '
-                    'If no project is specified, then it is a personal '
-                    'branch'))
+                description=_(
+                    "The project (if any) this branch pertains to. "
+                    "If no project is specified, then it is a personal "
+                    "branch"
+                ),
+            )
+
         return BranchEditSchema
 
     @property
     def page_title(self):
-        return 'Edit %s' % self.context.displayname
+        return "Edit %s" % self.context.displayname
 
     @property
     def label(self):
@@ -744,8 +765,11 @@ class BranchEditFormView(LaunchpadEditFormView):
         """See `LaunchpadFormView`"""
         return {self.schema: self.context}
 
-    @action('Change Branch', name='change',
-        failure=LaunchpadFormView.ajax_failure_handler)
+    @action(
+        "Change Branch",
+        name="change",
+        failure=LaunchpadFormView.ajax_failure_handler,
+    )
     def change_action(self, action, data):
         # If the owner or product has changed, add an explicit notification.
         # We take our own snapshot here to make sure that the snapshot records
@@ -754,50 +778,58 @@ class BranchEditFormView(LaunchpadEditFormView):
         # sent in updateContextFromData.
         changed = False
         branch_before_modification = Snapshot(
-            self.context, providing=providedBy(self.context))
-        if 'owner' in data:
-            new_owner = data.pop('owner')
+            self.context, providing=providedBy(self.context)
+        )
+        if "owner" in data:
+            new_owner = data.pop("owner")
             if new_owner != self.context.owner:
                 self.context.setOwner(new_owner, self.user)
                 changed = True
                 self.request.response.addNotification(
                     "The branch owner has been changed to %s (%s)"
-                    % (new_owner.displayname, new_owner.name))
-        if 'private' in data:
+                    % (new_owner.displayname, new_owner.name)
+                )
+        if "private" in data:
             # Read only for display.
-            data.pop('private')
+            data.pop("private")
         # We must process information type before target so that the any new
         # information type is valid for the target.
-        if 'information_type' in data:
-            information_type = data.pop('information_type')
+        if "information_type" in data:
+            information_type = data.pop("information_type")
             self.context.transitionToInformationType(
-                information_type, self.user)
-        if 'target' in data:
-            target = data.pop('target')
-            existing_junk = self.context.target.name == '+junk'
-            same_junk_status = target == '+junk' and existing_junk
-            if target == '+junk':
+                information_type, self.user
+            )
+        if "target" in data:
+            target = data.pop("target")
+            existing_junk = self.context.target.name == "+junk"
+            same_junk_status = target == "+junk" and existing_junk
+            if target == "+junk":
                 target = None
             if not same_junk_status or (
-                target is not None and target != self.context.target):
+                target is not None and target != self.context.target
+            ):
                 try:
                     self.context.setTarget(self.user, project=target)
                 except BranchTargetError as e:
-                    self.setFieldError('target', e.args[0])
+                    self.setFieldError("target", e.args[0])
                     return
 
                 changed = True
                 if target:
                     self.request.response.addNotification(
                         "The branch target has been changed to %s (%s)"
-                        % (target.displayname, target.name))
+                        % (target.displayname, target.name)
+                    )
                 else:
                     self.request.response.addNotification(
                         "This branch is now a personal branch for %s (%s)"
-                        % (self.context.owner.displayname,
-                            self.context.owner.name))
-        if 'reviewer' in data:
-            reviewer = data.pop('reviewer')
+                        % (
+                            self.context.owner.displayname,
+                            self.context.owner.name,
+                        )
+                    )
+        if "reviewer" in data:
+            reviewer = data.pop("reviewer")
             if reviewer != self.context.code_reviewer:
                 if reviewer == self.context.owner:
                     # Clear the reviewer if set to the same as the owner.
@@ -813,15 +845,19 @@ class BranchEditFormView(LaunchpadEditFormView):
             # Notify the object has changed with the snapshot that was taken
             # earler.
             field_names = [
-                form_field.__name__ for form_field in self.form_fields]
-            notify(ObjectModifiedEvent(
-                self.context, branch_before_modification, field_names))
+                form_field.__name__ for form_field in self.form_fields
+            ]
+            notify(
+                ObjectModifiedEvent(
+                    self.context, branch_before_modification, field_names
+                )
+            )
             # Only specify that the context was modified if there
             # was in fact a change.
             self.context.date_last_modified = UTC_NOW
 
         if self.request.is_ajax:
-            return ''
+            return ""
 
     @property
     def next_url(self):
@@ -838,19 +874,19 @@ class BranchEditFormView(LaunchpadEditFormView):
 class BranchEditWhiteboardView(BranchEditFormView):
     """A view for editing the whiteboard only."""
 
-    field_names = ['whiteboard']
+    field_names = ["whiteboard"]
 
 
 class BranchEditStatusView(BranchEditFormView):
     """A view for editing the lifecycle status only."""
 
-    field_names = ['lifecycle_status']
+    field_names = ["lifecycle_status"]
 
 
 class BranchEditInformationTypeView(BranchEditFormView):
     """A view for editing the information type only."""
 
-    field_names = ['information_type']
+    field_names = ["information_type"]
 
 
 class BranchMirrorStatusView(LaunchpadFormView):
@@ -873,8 +909,9 @@ class BranchMirrorStatusView(LaunchpadFormView):
             return False
         else:
             celebs = getUtility(ILaunchpadCelebrities)
-            return (self.user.inTeam(self.context.owner) or
-                    self.user.inTeam(celebs.admin))
+            return self.user.inTeam(self.context.owner) or self.user.inTeam(
+                celebs.admin
+            )
 
     @property
     def mirror_of_ssh(self):
@@ -882,7 +919,7 @@ class BranchMirrorStatusView(LaunchpadFormView):
         if not self.context.url:
             return False  # not a mirror branch
         uri = URI(self.context.url)
-        return uri.scheme in ('sftp', 'bzr+ssh')
+        return uri.scheme in ("sftp", "bzr+ssh")
 
     @property
     def in_mirror_queue(self):
@@ -907,8 +944,9 @@ class BranchMirrorStatusView(LaunchpadFormView):
         message = self.context.mirror_status_message
         if len(message) <= self.MAXIMUM_STATUS_MESSAGE_LENGTH:
             return message
-        return truncate_text(
-            message, self.MAXIMUM_STATUS_MESSAGE_LENGTH) + ' ...'
+        return (
+            truncate_text(message, self.MAXIMUM_STATUS_MESSAGE_LENGTH) + " ..."
+        )
 
     @property
     def show_mirror_failure(self):
@@ -923,7 +961,7 @@ class BranchMirrorStatusView(LaunchpadFormView):
     def next_url(self):
         return canonical_url(self.context)
 
-    @action('Try again', name='try-again')
+    @action("Try again", name="try-again")
     def retry(self, action, data):
         self.context.requestMirror()
 
@@ -936,7 +974,7 @@ class BranchDeletionView(LaunchpadFormView):
 
     @property
     def page_title(self):
-        return 'Delete branch %s' % self.context.displayname
+        return "Delete branch %s" % self.context.displayname
 
     label = page_title
 
@@ -948,8 +986,9 @@ class BranchDeletionView(LaunchpadFormView):
         """
         reqs = []
         for item, (operation, reason) in self.context.deletionRequirements(
-                eager_load=True).items():
-            allowed = check_permission('launchpad.Edit', item)
+            eager_load=True
+        ).items():
+            allowed = check_permission("launchpad.Edit", item)
             reqs.append((item, operation, reason, allowed))
         return reqs
 
@@ -961,9 +1000,9 @@ class BranchDeletionView(LaunchpadFormView):
     def stacked_branches_text(self):
         """Cache a count of the branches stacked on this."""
         if self.stacked_branches_count == 1:
-            return _('branch')
+            return _("branch")
         else:
-            return _('branches')
+            return _("branches")
 
     def all_permitted(self):
         """Return True if all deletion requirements are permitted, else False.
@@ -973,11 +1012,24 @@ class BranchDeletionView(LaunchpadFormView):
         # Not permitted if there are any branches stacked on this.
         if self.stacked_branches_count > 0:
             return False
-        return len([item for item, action, reason, allowed in
-            self.display_deletion_requirements if not allowed]) == 0
+        return (
+            len(
+                [
+                    item
+                    for item, action, reason, allowed in (
+                        self.display_deletion_requirements
+                    )
+                    if not allowed
+                ]
+            )
+            == 0
+        )
 
-    @action('Delete', name='delete_branch',
-            condition=lambda x, y: x.all_permitted())
+    @action(
+        "Delete",
+        name="delete_branch",
+        condition=lambda x, y: x.all_permitted(),
+    )
     def delete_branch_action(self, action, data):
         branch = self.context
         if self.all_permitted():
@@ -989,7 +1041,8 @@ class BranchDeletionView(LaunchpadFormView):
             self.request.response.addNotification(message)
         else:
             self.request.response.addNotification(
-                "This branch cannot be deleted.")
+                "This branch cannot be deleted."
+            )
             self.next_url = canonical_url(branch)
 
     @property
@@ -999,19 +1052,24 @@ class BranchDeletionView(LaunchpadFormView):
         The keys are 'delete' and 'alter'; the values are dicts of
         'item', 'reason' and 'allowed'.
         """
-        row_dict = {'delete': [], 'alter': [], 'break_link': []}
-        for item, operation, reason, allowed in (
-            self.display_deletion_requirements):
+        row_dict = {"delete": [], "alter": [], "break_link": []}
+        for (
+            item,
+            operation,
+            reason,
+            allowed,
+        ) in self.display_deletion_requirements:
             if IBugBranch.providedBy(item):
-                operation = 'break_link'
+                operation = "break_link"
             elif ISpecificationBranch.providedBy(item):
-                operation = 'break_link'
+                operation = "break_link"
             elif IProductSeries.providedBy(item):
-                operation = 'break_link'
-            row = {'item': item,
-                   'reason': reason,
-                   'allowed': allowed,
-                  }
+                operation = "break_link"
+            row = {
+                "item": item,
+                "reason": reason,
+                "allowed": allowed,
+            }
             row_dict[operation].append(row)
         return row_dict
 
@@ -1028,7 +1086,7 @@ class BranchUpgradeView(LaunchpadFormView):
 
     @property
     def page_title(self):
-        return 'Upgrade branch %s' % self.context.displayname
+        return "Upgrade branch %s" % self.context.displayname
 
     @property
     def next_url(self):
@@ -1036,7 +1094,7 @@ class BranchUpgradeView(LaunchpadFormView):
 
     cancel_url = next_url
 
-    @action('Upgrade', name='upgrade_branch')
+    @action("Upgrade", name="upgrade_branch")
     def upgrade_branch_action(self, action, data):
         try:
             self.context.requestUpgrade(self.user)
@@ -1052,16 +1110,20 @@ class CodeEditOwnerMixin:
         # If the user can administer the relevant object type, then they
         # should be able to assign the ownership of the object to any valid
         # person or team.
-        if check_permission('launchpad.Admin', self.context):
-            owner_field = self.schema['owner']
+        if check_permission("launchpad.Admin", self.context):
+            owner_field = self.schema["owner"]
             any_owner_choice = Choice(
-                __name__='owner', title=owner_field.title,
+                __name__="owner",
+                title=owner_field.title,
                 description=self.any_owner_description,
-                required=True, vocabulary='ValidPersonOrTeam')
+                required=True,
+                vocabulary="ValidPersonOrTeam",
+            )
             any_owner_field = form.Fields(
-                any_owner_choice, render_context=self.render_context)
+                any_owner_choice, render_context=self.render_context
+            )
             # Replace the normal owner field with a more permissive vocab.
-            self.form_fields = self.form_fields.omit('owner')
+            self.form_fields = self.form_fields.omit("owner")
             self.form_fields = any_owner_field + self.form_fields
         else:
             # For normal users, there is an edge case with package branches
@@ -1071,18 +1133,23 @@ class CodeEditOwnerMixin:
             if not self.user.inTeam(self.context.owner):
                 vocab = UserTeamsParticipationPlusSelfVocabulary()
                 owner = self.context.owner
-                terms = [SimpleTerm(
-                    owner, owner.name, owner.unique_displayname)]
+                terms = [
+                    SimpleTerm(owner, owner.name, owner.unique_displayname)
+                ]
                 terms.extend([term for term in vocab])
-                owner_field = self.schema['owner']
+                owner_field = self.schema["owner"]
                 owner_choice = Choice(
-                    __name__='owner', title=owner_field.title,
+                    __name__="owner",
+                    title=owner_field.title,
                     description=owner_field.description,
-                    required=True, vocabulary=SimpleVocabulary(terms))
+                    required=True,
+                    vocabulary=SimpleVocabulary(terms),
+                )
                 new_owner_field = form.Fields(
-                    owner_choice, render_context=self.render_context)
+                    owner_choice, render_context=self.render_context
+                )
                 # Replace the normal owner field with a more permissive vocab.
-                self.form_fields = self.form_fields.omit('owner')
+                self.form_fields = self.form_fields.omit("owner")
                 self.form_fields = new_owner_field + self.form_fields
 
 
@@ -1091,12 +1158,12 @@ class BranchEditView(CodeEditOwnerMixin, BranchEditFormView):
 
     @property
     def field_names(self):
-        field_names = ['owner', 'name']
+        field_names = ["owner", "name"]
         if not self.context.sourcepackagename:
-            field_names.append('target')
-        field_names.extend([
-            'information_type', 'url', 'description',
-            'lifecycle_status'])
+            field_names.append("target")
+        field_names.extend(
+            ["information_type", "url", "description", "lifecycle_status"]
+        )
         return field_names
 
     custom_widget_target = BranchTargetWidget
@@ -1105,15 +1172,16 @@ class BranchEditView(CodeEditOwnerMixin, BranchEditFormView):
 
     any_owner_description = _(
         "As an administrator you are able to assign this branch to any "
-        "person or team.")
+        "person or team."
+    )
 
     def setUpFields(self):
         super().setUpFields()
         branch = self.context
         if branch.branch_type in (BranchType.HOSTED, BranchType.IMPORTED):
-            self.form_fields = self.form_fields.omit('url')
+            self.form_fields = self.form_fields.omit("url")
 
-    def _setBranchExists(self, existing_branch, field_name='name'):
+    def _setBranchExists(self, existing_branch, field_name="name"):
         owner = existing_branch.owner
         if owner == self.user:
             prefix = "You already have"
@@ -1121,43 +1189,49 @@ class BranchEditView(CodeEditOwnerMixin, BranchEditFormView):
             prefix = "%s already has" % owner.displayname
         message = structured(
             "%s a branch for <em>%s</em> called <em>%s</em>.",
-            prefix, existing_branch.target.displayname,
-            existing_branch.name)
+            prefix,
+            existing_branch.target.displayname,
+            existing_branch.name,
+        )
         self.setFieldError(field_name, message)
 
     def validate(self, data):
         # Check that we're not moving a team branch to the +junk
         # pseudo project.
-        if 'name' in data:
+        if "name" in data:
             # Only validate if the name has changed or the owner has changed.
-            owner = data['owner']
-            if ((data['name'] != self.context.name) or
-                (owner != self.context.owner)):
+            owner = data["owner"]
+            if (data["name"] != self.context.name) or (
+                owner != self.context.owner
+            ):
                 # We only allow moving within the same branch target for now.
                 namespace = self.context.target.getNamespace(owner)
                 try:
                     namespace.validateMove(
-                        self.context, self.user, name=data['name'])
+                        self.context, self.user, name=data["name"]
+                    )
                 except BranchCreationForbidden:
                     self.addError(
-                        "%s is not allowed to own branches in %s." % (
-                        owner.displayname, self.context.target.displayname))
+                        "%s is not allowed to own branches in %s."
+                        % (owner.displayname, self.context.target.displayname)
+                    )
                 except BranchExists as e:
                     self._setBranchExists(e.existing_branch)
 
         # If the branch is a MIRRORED branch, then the url
         # must be supplied, and if HOSTED the url must *not*
         # be supplied.
-        url = data.get('url')
+        url = data.get("url")
         if self.context.branch_type == BranchType.MIRRORED:
             if url is None:
                 # If the url is not set due to url validation errors,
                 # there will be an error set for it.
-                error = self.getFieldError('url')
+                error = self.getFieldError("url")
                 if not error:
                     self.setFieldError(
-                        'url',
-                        'Branch URLs are required for Mirrored branches.')
+                        "url",
+                        "Branch URLs are required for Mirrored branches.",
+                    )
         else:
             # We don't care about whether the URL is set for REMOTE branches,
             # and the URL field is not shown for IMPORT or HOSTED branches.
@@ -1167,64 +1241,82 @@ class BranchEditView(CodeEditOwnerMixin, BranchEditFormView):
 class BranchReviewerEditView(BranchEditFormView):
     """The view to set the review team."""
 
-    field_names = ['reviewer']
+    field_names = ["reviewer"]
 
     @property
     def initial_values(self):
-        return {'reviewer': self.context.code_reviewer}
+        return {"reviewer": self.context.code_reviewer}
 
 
 class RegisterProposalSchema(Interface):
     """The schema to define the form for registering a new merge proposal."""
+
     target_branch = Choice(
-        title=_('Target branch'),
-        vocabulary='Branch', required=True, readonly=True,
+        title=_("Target branch"),
+        vocabulary="Branch",
+        required=True,
+        readonly=True,
         description=_(
-            "The branch that the source branch will be merged into."))
+            "The branch that the source branch will be merged into."
+        ),
+    )
 
     prerequisite_branch = Choice(
-        title=_('Prerequisite branch'),
-        vocabulary='Branch', required=False, readonly=False,
+        title=_("Prerequisite branch"),
+        vocabulary="Branch",
+        required=False,
+        readonly=False,
         description=_(
-            'A branch that should be merged before this one.  (Its changes'
-            ' will not be shown in the diff.)'))
+            "A branch that should be merged before this one.  (Its changes"
+            " will not be shown in the diff.)"
+        ),
+    )
 
     comment = Text(
-        title=_('Description of the change'), required=False,
-        description=_('Describe what changes your branch introduces, '
-                      'what bugs it fixes, or what features it implements. '
-                      'Ideally include rationale and how to test. '
-                      'You do not need to repeat information from the commit '
-                      'message here.'))
+        title=_("Description of the change"),
+        required=False,
+        description=_(
+            "Describe what changes your branch introduces, "
+            "what bugs it fixes, or what features it implements. "
+            "Ideally include rationale and how to test. "
+            "You do not need to repeat information from the commit "
+            "message here."
+        ),
+    )
 
-    reviewer = copy_field(
-        ICodeReviewVoteReference['reviewer'], required=False)
+    reviewer = copy_field(ICodeReviewVoteReference["reviewer"], required=False)
 
     review_type = copy_field(
-        ICodeReviewVoteReference['review_type'],
-        description='Lowercase keywords describing the type of review you '
-                    'would like to be performed.')
+        ICodeReviewVoteReference["review_type"],
+        description="Lowercase keywords describing the type of review you "
+        "would like to be performed.",
+    )
 
-    commit_message = IBranchMergeProposal['commit_message']
+    commit_message = IBranchMergeProposal["commit_message"]
 
     needs_review = Bool(
-        title=_("Needs review"), required=True, default=True,
-        description=_(
-            "Is the proposal ready for review now?"))
+        title=_("Needs review"),
+        required=True,
+        default=True,
+        description=_("Is the proposal ready for review now?"),
+    )
 
 
 class RegisterBranchMergeProposalView(LaunchpadFormView):
     """The view to register new branch merge proposals."""
+
     schema = RegisterProposalSchema
     for_input = True
 
     custom_widget_target_branch = TargetBranchWidget
     custom_widget_commit_message = CustomWidgetFactory(
-        TextAreaWidget, cssClass='comment-text')
+        TextAreaWidget, cssClass="comment-text"
+    )
     custom_widget_comment = CustomWidgetFactory(
-        TextAreaWidget, cssClass='comment-text')
+        TextAreaWidget, cssClass="comment-text"
+    )
 
-    page_title = label = 'Propose branch for merging'
+    page_title = label = "Propose branch for merging"
 
     @property
     def cancel_url(self):
@@ -1233,63 +1325,77 @@ class RegisterBranchMergeProposalView(LaunchpadFormView):
     def initialize(self):
         """Show a 404 if the branch target doesn't support proposals."""
         if not self.context.target.supports_merge_proposals:
-            raise NotFound(self.context, '+register-merge')
+            raise NotFound(self.context, "+register-merge")
         LaunchpadFormView.initialize(self)
 
-    @action('Propose Merge', name='register',
-        failure=LaunchpadFormView.ajax_failure_handler)
+    @action(
+        "Propose Merge",
+        name="register",
+        failure=LaunchpadFormView.ajax_failure_handler,
+    )
     def register_action(self, action, data):
         """Register the new branch merge proposal."""
 
         registrant = self.user
         source_branch = self.context
-        target_branch = data['target_branch']
-        prerequisite_branch = data.get('prerequisite_branch')
+        target_branch = data["target_branch"]
+        prerequisite_branch = data.get("prerequisite_branch")
 
         review_requests = []
-        reviewer = data.get('reviewer')
-        review_type = data.get('review_type')
+        reviewer = data.get("reviewer")
+        review_type = data.get("review_type")
         if reviewer is None:
             reviewer = target_branch.code_reviewer
         if reviewer is not None:
             review_requests.append((reviewer, review_type))
 
-        branch_names = [branch.unique_name
-                        for branch in [source_branch, target_branch]]
+        branch_names = [
+            branch.unique_name for branch in [source_branch, target_branch]
+        ]
         visibility_info = getUtility(IBranchSet).getBranchVisibilityInfo(
-            self.user, reviewer, branch_names)
-        visible_branches = list(visibility_info['visible_branches'])
+            self.user, reviewer, branch_names
+        )
+        visible_branches = list(visibility_info["visible_branches"])
         if self.request.is_ajax and len(visible_branches) < 2:
             self.request.response.setStatus(400, "Branch Visibility")
-            self.request.response.setHeader(
-                'Content-Type', 'application/json')
-            return simplejson.dumps({
-                'person_name': visibility_info['person_name'],
-                'branches_to_check': branch_names,
-                'visible_branches': visible_branches,
-            })
+            self.request.response.setHeader("Content-Type", "application/json")
+            return simplejson.dumps(
+                {
+                    "person_name": visibility_info["person_name"],
+                    "branches_to_check": branch_names,
+                    "visible_branches": visible_branches,
+                }
+            )
 
         try:
             proposal = source_branch.addLandingTarget(
-                registrant=registrant, merge_target=target_branch,
+                registrant=registrant,
+                merge_target=target_branch,
                 merge_prerequisite=prerequisite_branch,
-                needs_review=data['needs_review'],
-                description=data.get('comment'),
+                needs_review=data["needs_review"],
+                description=data.get("comment"),
                 review_requests=review_requests,
-                commit_message=data.get('commit_message'))
+                commit_message=data.get("commit_message"),
+            )
             if len(visible_branches) < 2:
-                invisible_branches = [branch.unique_name
-                            for branch in [source_branch, target_branch]
-                            if branch.unique_name not in visible_branches]
+                invisible_branches = [
+                    branch.unique_name
+                    for branch in [source_branch, target_branch]
+                    if branch.unique_name not in visible_branches
+                ]
                 self.request.response.addNotification(
-                    'To ensure visibility, %s is now subscribed to: %s'
-                    % (visibility_info['person_name'],
-                       english_list(invisible_branches)))
+                    "To ensure visibility, %s is now subscribed to: %s"
+                    % (
+                        visibility_info["person_name"],
+                        english_list(invisible_branches),
+                    )
+                )
             # Success so we do a client redirect to the new mp page.
             if self.request.is_ajax:
                 self.request.response.setStatus(201)
                 self.request.response.setHeader(
-                    'Location', canonical_url(proposal))
+                    "Location", canonical_url(proposal)
+                )
                 return None
             else:
                 self.next_url = canonical_url(proposal)
@@ -1298,7 +1404,7 @@ class RegisterBranchMergeProposalView(LaunchpadFormView):
 
     def validate(self, data):
         source_branch = self.context
-        target_branch = data.get('target_branch')
+        target_branch = data.get("target_branch")
 
         # Make sure that the target branch is different from the context.
         if target_branch is None:
@@ -1308,15 +1414,17 @@ class RegisterBranchMergeProposalView(LaunchpadFormView):
             pass
         elif source_branch == target_branch:
             self.setFieldError(
-                'target_branch',
-                "The target branch cannot be the same as the source branch.")
+                "target_branch",
+                "The target branch cannot be the same as the source branch.",
+            )
         else:
             # Make sure that the target_branch is in the same project.
             if not target_branch.isBranchMergeable(source_branch):
                 self.setFieldError(
-                    'target_branch',
-                    "This branch is not mergeable into %s." %
-                    target_branch.bzr_identity)
+                    "target_branch",
+                    "This branch is not mergeable into %s."
+                    % target_branch.bzr_identity,
+                )
 
 
 @implementer(IBrowserPublisher)
diff --git a/lib/lp/code/browser/branchlisting.py b/lib/lp/code/browser/branchlisting.py
index e4c0c47..f3cb9bd 100644
--- a/lib/lp/code/browser/branchlisting.py
+++ b/lib/lp/code/browser/branchlisting.py
@@ -4,58 +4,46 @@
 """Base class view for branch listings."""
 
 __all__ = [
-    'BranchBadges',
-    'BranchListingView',
-    'DistributionBranchListingView',
-    'DistributionSourcePackageBranchesView',
-    'DistroSeriesBranchListingView',
-    'GroupedDistributionSourcePackageBranchesView',
-    'PersonBranchesMenu',
-    'PersonBranchesView',
-    'PersonCodeSummaryView',
-    'PersonTeamBranchesView',
-    'ProductBranchListingView',
-    'ProductBranchesMenu',
-    'ProductBranchesView',
-    'ProjectBranchesView',
-    'RecentlyChangedBranchesView',
-    'RecentlyImportedBranchesView',
-    'RecentlyRegisteredBranchesView',
-    'SourcePackageBranchesView',
-    ]
+    "BranchBadges",
+    "BranchListingView",
+    "DistributionBranchListingView",
+    "DistributionSourcePackageBranchesView",
+    "DistroSeriesBranchListingView",
+    "GroupedDistributionSourcePackageBranchesView",
+    "PersonBranchesMenu",
+    "PersonBranchesView",
+    "PersonCodeSummaryView",
+    "PersonTeamBranchesView",
+    "ProductBranchListingView",
+    "ProductBranchesMenu",
+    "ProductBranchesView",
+    "ProjectBranchesView",
+    "RecentlyChangedBranchesView",
+    "RecentlyImportedBranchesView",
+    "RecentlyRegisteredBranchesView",
+    "SourcePackageBranchesView",
+]
 
 from operator import attrgetter
 from urllib.parse import parse_qs
 
 from lazr.delegates import delegate_to
-from lazr.enum import (
-    EnumeratedType,
-    Item,
-    )
+from lazr.enum import EnumeratedType, Item
 from storm.expr import Desc
 from zope.browserpage import ViewPageTemplateFile
 from zope.component import getUtility
 from zope.formlib import form
-from zope.interface import (
-    implementer,
-    Interface,
-    )
+from zope.interface import Interface, implementer
 from zope.schema import Choice
 
 from lp import _
-from lp.app.browser.badge import (
-    Badge,
-    HasBadgeBase,
-    )
+from lp.app.browser.badge import Badge, HasBadgeBase
 from lp.app.browser.launchpadform import LaunchpadFormView
-from lp.app.enums import (
-    PRIVATE_INFORMATION_TYPES,
-    ServiceUsage,
-    )
+from lp.app.enums import PRIVATE_INFORMATION_TYPES, ServiceUsage
 from lp.app.widgets.itemswidgets import LaunchpadDropdownWidget
 from lp.blueprints.interfaces.specificationbranch import (
     ISpecificationBranchSet,
-    )
+)
 from lp.bugs.interfaces.bugbranch import IBugBranchSet
 from lp.code.browser.branch import BranchMirrorMixin
 from lp.code.browser.branchmergeproposallisting import ActiveReviewsView
@@ -65,14 +53,14 @@ from lp.code.enums import (
     BranchLifecycleStatusFilter,
     BranchListingSort,
     BranchType,
-    )
+)
 from lp.code.interfaces.branch import (
-    BzrIdentityMixin,
     DEFAULT_BRANCH_STATUS_IN_LISTING,
+    BzrIdentityMixin,
     IBranch,
     IBranchBatchNavigator,
     IBranchListingQueryOptimiser,
-    )
+)
 from lp.code.interfaces.branchcollection import IAllBranches
 from lp.code.interfaces.branchnamespace import IBranchNamespacePolicy
 from lp.code.interfaces.branchtarget import IBranchTarget
@@ -81,19 +69,16 @@ from lp.code.interfaces.revision import IRevisionSet
 from lp.code.interfaces.revisioncache import IRevisionCache
 from lp.code.interfaces.seriessourcepackagebranch import (
     IFindOfficialBranchLinks,
-    )
+)
 from lp.registry.browser.product import (
     ProductDownloadFileMixin,
     SortSeriesMixin,
-    )
-from lp.registry.interfaces.person import (
-    IPerson,
-    IPersonSet,
-    )
+)
+from lp.registry.interfaces.person import IPerson, IPersonSet
 from lp.registry.interfaces.personproduct import (
     IPersonProduct,
     IPersonProductFactory,
-    )
+)
 from lp.registry.interfaces.product import IProduct
 from lp.registry.interfaces.series import SeriesStatus
 from lp.registry.interfaces.sourcepackage import ISourcePackageFactory
@@ -108,17 +93,13 @@ from lp.services.feeds.browser import (
     ProductRevisionsFeedLink,
     ProjectBranchesFeedLink,
     ProjectRevisionsFeedLink,
-    )
+)
 from lp.services.propertycache import cachedproperty
-from lp.services.webapp import (
-    ApplicationMenu,
-    canonical_url,
-    Link,
-    )
+from lp.services.webapp import ApplicationMenu, Link, canonical_url
 from lp.services.webapp.authorization import (
     check_permission,
     precache_permission_for_objects,
-    )
+)
 from lp.services.webapp.batching import TableBatchNavigator
 from lp.services.webapp.publisher import LaunchpadView
 
@@ -133,13 +114,14 @@ class BranchBadges(HasBadgeBase):
     def getBadge(self, badge_name):
         """See `IHasBadges`."""
         if badge_name == "warning":
-            return Badge('/@@/warning', '/@@/warning-large', '',
-                         'Branch has errors')
+            return Badge(
+                "/@@/warning", "/@@/warning-large", "", "Branch has errors"
+            )
         else:
             return super().getBadge(badge_name)
 
 
-@delegate_to(IBranch, context='context')
+@delegate_to(IBranch, context="context")
 class BranchListingItem(BzrIdentityMixin, BranchBadges):
     """A decorated branch.
 
@@ -148,9 +130,16 @@ class BranchListingItem(BzrIdentityMixin, BranchBadges):
     prefetched by the view and decorate the branch.
     """
 
-    def __init__(self, branch, last_commit, show_bug_badge,
-                 show_blueprint_badge, show_mp_badge,
-                 associated_product_series, suite_source_packages):
+    def __init__(
+        self,
+        branch,
+        last_commit,
+        show_bug_badge,
+        show_blueprint_badge,
+        show_mp_badge,
+        associated_product_series,
+        suite_source_packages,
+    ):
         BranchBadges.__init__(self, branch)
         self.last_commit = last_commit
         self.show_bug_badge = show_bug_badge
@@ -169,8 +158,11 @@ class BranchListingItem(BzrIdentityMixin, BranchBadges):
 
     @property
     def active_series(self):
-        return [series for series in self.associated_product_series
-                if series.status != SeriesStatus.OBSOLETE]
+        return [
+            series
+            for series in self.associated_product_series
+            if series.status != SeriesStatus.OBSOLETE
+        ]
 
     def isBugBadgeVisible(self):
         return self.show_bug_badge
@@ -201,33 +193,40 @@ class BranchListingItem(BzrIdentityMixin, BranchBadges):
     @property
     def revision_codebrowse_link(self):
         return self.context.getCodebrowseUrl(
-            'revision', str(self.context.revision_count))
+            "revision", str(self.context.revision_count)
+        )
 
     def __repr__(self):
         # For testing purposes.
-        return '<BranchListingItem %r (%d)>' % (self.unique_name, self.id)
+        return "<BranchListingItem %r (%d)>" % (self.unique_name, self.id)
 
 
 class PersonBranchCategory(EnumeratedType):
     """Choices for filtering lists of branches related to people."""
 
-    OWNED = Item("""
+    OWNED = Item(
+        """
         Owned
 
         Show branches owned by the person or team.
-        """)
+        """
+    )
 
-    SUBSCRIBED = Item("""
+    SUBSCRIBED = Item(
+        """
         Subscribed
 
         Show branches subscribed to by the person or team.
-        """)
+        """
+    )
 
-    REGISTERED = Item("""
+    REGISTERED = Item(
+        """
         Registered
 
         Show branches registered by the person or team.
-        """)
+        """
+    )
 
 
 class IBranchListingFilter(Interface):
@@ -235,28 +234,36 @@ class IBranchListingFilter(Interface):
 
     # Stats and status attributes
     lifecycle = Choice(
-        title=_('Lifecycle Filter'), vocabulary=BranchLifecycleStatusFilter,
+        title=_("Lifecycle Filter"),
+        vocabulary=BranchLifecycleStatusFilter,
         default=BranchLifecycleStatusFilter.CURRENT,
         description=_(
-        "The author's assessment of the branch's maturity. "
-        " Mature: recommend for production use."
-        " Development: useful work that is expected to be merged eventually."
-        " Experimental: not recommended for merging yet, and maybe ever."
-        " Merged: integrated into mainline, of historical interest only."
-        " Abandoned: no longer considered relevant by the author."
-        " New: unspecified maturity."))
+            "The author's assessment of the branch's maturity. "
+            " Mature: recommend for production use."
+            " Development: useful work that is expected to be merged "
+            "eventually."
+            " Experimental: not recommended for merging yet, and maybe ever."
+            " Merged: integrated into mainline, of historical interest only."
+            " Abandoned: no longer considered relevant by the author."
+            " New: unspecified maturity."
+        ),
+    )
 
     sort_by = Choice(
-        title=_('ordered by'), vocabulary=BranchListingSort,
-        default=BranchListingSort.LIFECYCLE)
+        title=_("ordered by"),
+        vocabulary=BranchListingSort,
+        default=BranchListingSort.LIFECYCLE,
+    )
 
 
 class IPersonBranchListingFilter(IBranchListingFilter):
     """The schema for the branch listing filtering/ordering form for people."""
 
     category = Choice(
-        title=_('Category'), vocabulary=PersonBranchCategory,
-        default=PersonBranchCategory.OWNED)
+        title=_("Category"),
+        vocabulary=PersonBranchCategory,
+        default=PersonBranchCategory.OWNED,
+    )
 
 
 class BranchListingItemsMixin:
@@ -286,7 +293,8 @@ class BranchListingItemsMixin:
     def product_series_map(self):
         """Return a map from branch id to a list of product series."""
         series_resultset = self._query_optimiser.getProductSeriesForBranches(
-            self._visible_branch_ids)
+            self._visible_branch_ids
+        )
         result = {}
         for series in series_resultset:
             # Some products may be proprietary or embargoed, and users
@@ -317,7 +325,8 @@ class BranchListingItemsMixin:
         """Return a map from branch id to a list of package links."""
         query_optimiser = self._query_optimiser
         links = query_optimiser.getOfficialSourcePackageLinksForBranches(
-            self._visible_branch_ids)
+            self._visible_branch_ids
+        )
         result = {}
         for link in links:
             result.setdefault(link.branch.id, []).append(link)
@@ -328,9 +337,11 @@ class BranchListingItemsMixin:
 
         If there is more than one, they are sorted by pocket.
         """
-        links = [link.suite_sourcepackage for link in
-                 self.official_package_links_map.get(branch.id, [])]
-        return sorted(links, key=attrgetter('pocket'))
+        links = [
+            link.suite_sourcepackage
+            for link in self.official_package_links_map.get(branch.id, [])
+        ]
+        return sorted(links, key=attrgetter("pocket"))
 
     def getDistroDevelSeries(self, distribution):
         """distribution.currentseries hits the DB every time so cache it."""
@@ -344,15 +355,20 @@ class BranchListingItemsMixin:
     @cachedproperty
     def branch_ids_with_bug_links(self):
         """Return a set of branch ids that should show bug badges."""
-        return set(getUtility(IBugBranchSet).getBranchesWithVisibleBugs(
-            self.visible_branches_for_view, self.view_user))
+        return set(
+            getUtility(IBugBranchSet).getBranchesWithVisibleBugs(
+                self.visible_branches_for_view, self.view_user
+            )
+        )
 
     @cachedproperty
     def branch_ids_with_spec_links(self):
         """Return a set of branch ids that should show blueprint badges."""
         spec_branches = getUtility(
-            ISpecificationBranchSet).getSpecificationBranchesForBranches(
-            self.visible_branches_for_view, self.view_user)
+            ISpecificationBranchSet
+        ).getSpecificationBranchesForBranches(
+            self.visible_branches_for_view, self.view_user
+        )
         return {spec_branch.branch.id for spec_branch in spec_branches}
 
     @cachedproperty
@@ -372,24 +388,28 @@ class BranchListingItemsMixin:
         """Return a set of branch ids that should show blueprint badges."""
         revisionset = getUtility(IRevisionSet)
         revisions = revisionset.getTipRevisionsForBranches(
-            self.visible_branches_for_view)
+            self.visible_branches_for_view
+        )
         if revisions is None:
             revisions = []
 
         # Key the revisions by revision id.
         revision_map = {
-            revision.revision_id: revision for revision in revisions}
+            revision.revision_id: revision for revision in revisions
+        }
 
         # Cache display information for authors of branches' respective
         # last revisions.
         getUtility(IPersonSet).getPrecachedPersonsFromIDs(
             [revision.revision_author.person_id for revision in revisions],
-            need_icon=True)
+            need_icon=True,
+        )
 
         # Return a dict keyed on branch id.
         return {
             branch.id: revision_map.get(branch.last_scanned_id)
-            for branch in self.visible_branches_for_view}
+            for branch in self.visible_branches_for_view
+        }
 
     def _createItem(self, branch):
         last_commit = self.tip_revisions[branch.id]
@@ -399,8 +419,14 @@ class BranchListingItemsMixin:
         associated_product_series = self.getProductSeries(branch)
         suite_source_packages = self.getSuiteSourcePackages(branch)
         return BranchListingItem(
-            branch, last_commit, show_bug_badge, show_blueprint_badge,
-            show_mp_badge, associated_product_series, suite_source_packages)
+            branch,
+            last_commit,
+            show_bug_badge,
+            show_blueprint_badge,
+            show_mp_badge,
+            associated_product_series,
+            suite_source_packages,
+        )
 
     def decoratedBranches(self, branches):
         """Return the decorated branches for the branches passed in."""
@@ -408,15 +434,19 @@ class BranchListingItemsMixin:
 
 
 @implementer(IBranchBatchNavigator)
-class BranchListingBatchNavigator(TableBatchNavigator,
-                                  BranchListingItemsMixin):
+class BranchListingBatchNavigator(
+    TableBatchNavigator, BranchListingItemsMixin
+):
     """Batch up the branch listings."""
 
     def __init__(self, view):
         TableBatchNavigator.__init__(
-            self, view.getVisibleBranchesForUser(), view.request,
+            self,
+            view.getVisibleBranchesForUser(),
+            view.request,
             columns_to_show=view.extra_columns,
-            size=config.launchpad.branchlisting_batch_size)
+            size=config.launchpad.branchlisting_batch_size,
+        )
         BranchListingItemsMixin.__init__(self, view.user)
         self.view = view
         self.column_count = 4 + len(view.extra_columns)
@@ -429,7 +459,7 @@ class BranchListingBatchNavigator(TableBatchNavigator,
     def visible_branches_for_view(self):
         branches = list(self.currentBatch())
         request = self.view.request
-        precache_permission_for_objects(request, 'launchpad.View', branches)
+        precache_permission_for_objects(request, "launchpad.View", branches)
         return branches
 
     @cachedproperty
@@ -450,8 +480,9 @@ class BranchListingBatchNavigator(TableBatchNavigator,
 
 class BranchListingView(LaunchpadFormView, FeedsMixin):
     """A base class for views of branch listings."""
+
     schema = IBranchListingFilter
-    field_names = ['lifecycle', 'sort_by']
+    field_names = ["lifecycle", "sort_by"]
     development_focus_branch = None
     show_set_development_focus = False
     custom_widget_lifecycle = LaunchpadDropdownWidget
@@ -486,16 +517,17 @@ class BranchListingView(LaunchpadFormView, FeedsMixin):
         ProductRevisionsFeedLink,
         PersonBranchesFeedLink,
         PersonRevisionsFeedLink,
-        )
+    )
 
     table_only_template = ViewPageTemplateFile(
-        '../templates/branches-table-include.pt')
+        "../templates/branches-table-include.pt"
+    )
 
     @property
     def template(self):
-        query_string = self.request.get('QUERY_STRING') or ''
+        query_string = self.request.get("QUERY_STRING") or ""
         query_params = parse_qs(query_string)
-        render_table_only = 'batch_request' in query_params
+        render_table_only = "batch_request" in query_params
         if render_table_only:
             return self.table_only_template
         else:
@@ -503,11 +535,11 @@ class BranchListingView(LaunchpadFormView, FeedsMixin):
 
     @property
     def initial_values(self):
-        return {'lifecycle': BranchLifecycleStatusFilter.CURRENT}
+        return {"lifecycle": BranchLifecycleStatusFilter.CURRENT}
 
     @cachedproperty
     def selected_lifecycle_status(self):
-        widget = self.widgets['lifecycle']
+        widget = self.widgets["lifecycle"]
 
         if widget.hasValidInput():
             lifecycle_filter = widget.getInputValue()
@@ -519,7 +551,7 @@ class BranchListingView(LaunchpadFormView, FeedsMixin):
         elif lifecycle_filter == BranchLifecycleStatusFilter.CURRENT:
             return DEFAULT_BRANCH_STATUS_IN_LISTING
         else:
-            return (BranchLifecycleStatus.items[lifecycle_filter.name], )
+            return (BranchLifecycleStatus.items[lifecycle_filter.name],)
 
     def branches(self):
         """All branches related to this target, sorted for display."""
@@ -554,8 +586,9 @@ class BranchListingView(LaunchpadFormView, FeedsMixin):
         # the whole collection count (it might be expensive to compute if the
         # total number of branches is huge).
         return (
-            len(self.branches().visible_branches_for_view) == 0 and
-            not self.branch_count)
+            len(self.branches().visible_branches_for_view) == 0
+            and not self.branch_count
+        )
 
     def _branches(self, lifecycle_status, sort_by=None):
         """Return a sequence of branches.
@@ -579,19 +612,23 @@ class BranchListingView(LaunchpadFormView, FeedsMixin):
     def no_branch_message(self):
         """This may also be overridden in derived classes to provide
         context relevant messages if there are no branches returned."""
-        if (self.selected_lifecycle_status is not None
-            and self.hasAnyBranchesVisibleByUser()):
+        if (
+            self.selected_lifecycle_status is not None
+            and self.hasAnyBranchesVisibleByUser()
+        ):
             message = (
-                'There are branches related to %s but none of them match the '
-                'current filter criteria for this page. '
-                'Try filtering on "Any Status".')
+                "There are branches related to %s but none of them match the "
+                "current filter criteria for this page. "
+                'Try filtering on "Any Status".'
+            )
         else:
             message = (
-                'There are no branches related to %s '
-                'in Launchpad today. You can use Launchpad as a registry for '
-                'Bazaar branches, and encourage broader community '
-                'participation in your project using '
-                'distributed version control.')
+                "There are no branches related to %s "
+                "in Launchpad today. You can use Launchpad as a registry for "
+                "Bazaar branches, and encourage broader community "
+                "participation in your project using "
+                "distributed version control."
+            )
         return message % self.context.displayname
 
     @property
@@ -605,53 +642,63 @@ class BranchListingView(LaunchpadFormView, FeedsMixin):
         """
         # This is pretty painful.
         # First we find the items which are not excluded for this view.
-        vocab_items = [item for item in BranchListingSort.items.items
-                       if item not in self.no_sort_by]
+        vocab_items = [
+            item
+            for item in BranchListingSort.items.items
+            if item not in self.no_sort_by
+        ]
         # Finding the value of the lifecycle_filter widget is awkward as we do
         # this when the widgets are being set up.  We go digging in the
         # request.
-        lifecycle_field = IBranchListingFilter['lifecycle']
-        name = self.prefix + '.' + lifecycle_field.__name__
+        lifecycle_field = IBranchListingFilter["lifecycle"]
+        name = self.prefix + "." + lifecycle_field.__name__
         form_value = self.request.form.get(name)
         if form_value is not None:
             try:
                 status_filter = BranchLifecycleStatusFilter.getTermByToken(
-                    form_value).value
+                    form_value
+                ).value
             except LookupError:
                 # We explicitly support bogus values in field.lifecycle --
                 # they are treated the same as "CURRENT", which includes more
                 # than one lifecycle.
                 pass
             else:
-                if status_filter not in (BranchLifecycleStatusFilter.ALL,
-                                         BranchLifecycleStatusFilter.CURRENT):
+                if status_filter not in (
+                    BranchLifecycleStatusFilter.ALL,
+                    BranchLifecycleStatusFilter.CURRENT,
+                ):
                     vocab_items.remove(BranchListingSort.LIFECYCLE)
         return vocab_items
 
     @property
     def sort_by_field(self):
         """The zope.schema field for the 'sort_by' widget."""
-        orig_field = IBranchListingFilter['sort_by']
+        orig_field = IBranchListingFilter["sort_by"]
         values = self.branch_listing_sort_values
-        return Choice(__name__=orig_field.__name__,
-                      title=orig_field.title,
-                      required=True, values=values, default=values[0])
+        return Choice(
+            __name__=orig_field.__name__,
+            title=orig_field.title,
+            required=True,
+            values=values,
+            default=values[0],
+        )
 
     @property
     def sort_by(self):
         """The value of the `sort_by` widget, or None if none was present."""
-        widget = self.widgets['sort_by']
+        widget = self.widgets["sort_by"]
         if widget.hasValidInput():
             return widget.getInputValue()
         else:
             # If a derived view has specified a default sort_by, use that.
-            return self.initial_values.get('sort_by')
+            return self.initial_values.get("sort_by")
 
     def setUpWidgets(self, context=None):
         """Set up the 'sort_by' widget with only the applicable choices."""
         fields = []
         for field_name in self.field_names:
-            if field_name == 'sort_by':
+            if field_name == "sort_by":
                 field = form.FormField(self.sort_by_field)
             else:
                 field = self.form_fields[field_name]
@@ -688,12 +735,13 @@ class BranchListingView(LaunchpadFormView, FeedsMixin):
 class NoContextBranchListingView(BranchListingView):
     """A branch listing that has no associated product or person."""
 
-    field_names = ['lifecycle']
-    no_sort_by = (BranchListingSort.DEFAULT, )
+    field_names = ["lifecycle"]
+    no_sort_by = (BranchListingSort.DEFAULT,)
 
     no_branch_message = (
-        'There are no branches that match the current status filter.')
-    extra_columns = ('author', 'product', 'date_created')
+        "There are no branches that match the current status filter."
+    )
+    extra_columns = ("author", "product", "date_created")
 
     def _branches(self, lifecycle_status):
         """Return a sequence of branches.
@@ -707,17 +755,19 @@ class NoContextBranchListingView(BranchListingView):
             collection = collection.withLifecycleStatus(*lifecycle_status)
         collection = collection.visibleByUser(self.user)
         return collection.getBranches(eager_load=False).order_by(
-            self._branch_order)
+            self._branch_order
+        )
 
 
 class RecentlyRegisteredBranchesView(NoContextBranchListingView):
     """A batched view of branches orded by registration date."""
 
-    page_title = 'Recently registered branches'
+    page_title = "Recently registered branches"
 
     @property
     def _branch_order(self):
         from lp.code.model.branch import Branch
+
         return Desc(Branch.date_created), Desc(Branch.id)
 
     def _getCollection(self):
@@ -727,41 +777,47 @@ class RecentlyRegisteredBranchesView(NoContextBranchListingView):
 class RecentlyImportedBranchesView(NoContextBranchListingView):
     """A batched view of imported branches ordered by last modifed time."""
 
-    page_title = 'Recently imported branches'
-    extra_columns = ('product', 'date_created')
+    page_title = "Recently imported branches"
+    extra_columns = ("product", "date_created")
 
     @property
     def _branch_order(self):
         from lp.code.model.branch import Branch
+
         return Desc(Branch.date_last_modified), Desc(Branch.id)
 
     def _getCollection(self):
-        return (getUtility(IAllBranches)
-                .withBranchType(BranchType.IMPORTED)
-                .scanned())
+        return (
+            getUtility(IAllBranches)
+            .withBranchType(BranchType.IMPORTED)
+            .scanned()
+        )
 
 
 class RecentlyChangedBranchesView(NoContextBranchListingView):
     """Batched view of non-imported branches ordered by last modified time."""
 
-    page_title = 'Recently changed branches'
+    page_title = "Recently changed branches"
 
     @property
     def _branch_order(self):
         from lp.code.model.branch import Branch
+
         return Desc(Branch.date_last_modified), Desc(Branch.id)
 
     def _getCollection(self):
-        return (getUtility(IAllBranches)
-                .withBranchType(BranchType.HOSTED, BranchType.MIRRORED)
-                .scanned())
+        return (
+            getUtility(IAllBranches)
+            .withBranchType(BranchType.HOSTED, BranchType.MIRRORED)
+            .scanned()
+        )
 
 
 class PersonBranchesMenu(ApplicationMenu):
 
     usedfor = IPerson
-    facet = 'branches'
-    links = ['branches', 'active_reviews', 'source_package_recipes', 'snaps']
+    facet = "branches"
+    links = ["branches", "active_reviews", "source_package_recipes", "snaps"]
 
     @property
     def person(self):
@@ -773,26 +829,27 @@ class PersonBranchesMenu(ApplicationMenu):
         return self.context
 
     def branches(self):
-        return Link(
-            canonical_url(self.context, rootsite='code'), 'Branches')
+        return Link(canonical_url(self.context, rootsite="code"), "Branches")
 
     def active_reviews(self):
-        return Link('+activereviews', 'Active reviews')
+        return Link("+activereviews", "Active reviews")
 
     def source_package_recipes(self):
         return Link(
-            '+recipes', 'Source package recipes',
-            enabled=IPerson.providedBy(self.context))
+            "+recipes",
+            "Source package recipes",
+            enabled=IPerson.providedBy(self.context),
+        )
 
     def snaps(self):
         enabled = IPerson.providedBy(self.context)
-        return Link('+snaps', 'Snap packages', enabled=enabled)
+        return Link("+snaps", "Snap packages", enabled=enabled)
 
 
 class PersonProductBranchesMenu(PersonBranchesMenu):
 
     usedfor = IPersonProduct
-    links = ['branches', 'active_reviews', 'source_package_recipes', 'snaps']
+    links = ["branches", "active_reviews", "source_package_recipes", "snaps"]
 
     @property
     def person(self):
@@ -814,13 +871,13 @@ class PersonBaseBranchListingView(BranchListingView):
     @property
     def initial_values(self):
         values = super().initial_values
-        values['sort_by'] = BranchListingSort.MOST_RECENTLY_CHANGED_FIRST
+        values["sort_by"] = BranchListingSort.MOST_RECENTLY_CHANGED_FIRST
         return values
 
     def _getCollection(self):
         category = PersonBranchCategory.OWNED
-        if self.widgets['category'].hasValidInput():
-            category = self.widgets['category'].getInputValue()
+        if self.widgets["category"].hasValidInput():
+            category = self.widgets["category"].getInputValue()
         if category == PersonBranchCategory.OWNED:
             return getUtility(IAllBranches).ownedBy(self.person)
         elif category == PersonBranchCategory.SUBSCRIBED:
@@ -833,7 +890,7 @@ class PersonBranchesView(PersonBaseBranchListingView):
     """View for branch listing for a person's branches."""
 
     schema = IPersonBranchListingFilter
-    field_names = ['category', 'lifecycle', 'sort_by']
+    field_names = ["category", "lifecycle", "sort_by"]
     custom_widget_category = LaunchpadDropdownWidget
 
     no_sort_by = (BranchListingSort.DEFAULT, BranchListingSort.OWNER)
@@ -841,16 +898,19 @@ class PersonBranchesView(PersonBaseBranchListingView):
 
     @property
     def no_branch_message(self):
-        if (self.selected_lifecycle_status is not None
-            and self.hasAnyBranchesVisibleByUser()):
+        if (
+            self.selected_lifecycle_status is not None
+            and self.hasAnyBranchesVisibleByUser()
+        ):
             message = (
-                'There are branches related to %s but none of them match the '
-                'current filter criteria for this page. '
-                'Try filtering on "Any Status".')
+                "There are branches related to %s but none of them match the "
+                "current filter criteria for this page. "
+                'Try filtering on "Any Status".'
+            )
         else:
             message = (
-                'There are no branches related to %s '
-                'in Launchpad today.')
+                "There are no branches related to %s " "in Launchpad today."
+            )
         return message % self.context.displayname
 
 
@@ -858,8 +918,10 @@ class PersonProductBranchesView(PersonBranchesView):
     """Branch listing for a person's branches of a product."""
 
     no_sort_by = (
-        BranchListingSort.DEFAULT, BranchListingSort.OWNER,
-        BranchListingSort.PRODUCT)
+        BranchListingSort.DEFAULT,
+        BranchListingSort.OWNER,
+        BranchListingSort.PRODUCT,
+    )
 
     @property
     def person(self):
@@ -868,22 +930,26 @@ class PersonProductBranchesView(PersonBranchesView):
 
     @property
     def label(self):
-        return 'Branches of %s' % self.context.product.displayname
+        return "Branches of %s" % self.context.product.displayname
 
     @property
     def no_branch_message(self):
         """Provide a more appropriate message for no branches."""
-        if (self.selected_lifecycle_status is not None
-            and self.hasAnyBranchesVisibleByUser()):
+        if (
+            self.selected_lifecycle_status is not None
+            and self.hasAnyBranchesVisibleByUser()
+        ):
             message = (
-                'There are branches of %s for %s but none of them '
-                'match the current filter criteria for this page. '
-                'Try filtering on "Any Status".')
+                "There are branches of %s for %s but none of them "
+                "match the current filter criteria for this page. "
+                'Try filtering on "Any Status".'
+            )
         else:
-            message = (
-                'There are no branches of %s for %s in Launchpad today.')
+            message = "There are no branches of %s for %s in Launchpad today."
         return message % (
-            self.context.product.displayname, self.context.person.displayname)
+            self.context.product.displayname,
+            self.context.person.displayname,
+        )
 
     def _getCollection(self):
         coll = super()._getCollection()
@@ -904,7 +970,7 @@ class PersonTeamBranchesView(LaunchpadView):
         want a particular url formatter for a PersonProduct, we have the url
         separately.
         """
-        return {'team': team, 'url_provider': team}
+        return {"team": team, "url_provider": team}
 
     @property
     def person(self):
@@ -913,8 +979,11 @@ class PersonTeamBranchesView(LaunchpadView):
     @cachedproperty
     def teams_with_branches(self):
         teams = self._getCollection().getTeamsWithBranches(self.person)
-        return [self._createItem(team) for team in teams
-                if check_permission('launchpad.View', team)]
+        return [
+            self._createItem(team)
+            for team in teams
+            if check_permission("launchpad.View", team)
+        ]
 
 
 class PersonProductTeamBranchesView(PersonTeamBranchesView):
@@ -922,15 +991,20 @@ class PersonProductTeamBranchesView(PersonTeamBranchesView):
 
     def _getCollection(self):
         """Use a collection restricted on on the product."""
-        return getUtility(IAllBranches).visibleByUser(self.user).inProduct(
-                self.context.product)
+        return (
+            getUtility(IAllBranches)
+            .visibleByUser(self.user)
+            .inProduct(self.context.product)
+        )
 
     def _createItem(self, team):
         """Return a tuple of the team, and the thing to get the URL from."""
         return {
-            'team': team,
-            'url_provider': getUtility(IPersonProductFactory).create(
-                team, self.context.product)}
+            "team": team,
+            "url_provider": getUtility(IPersonProductFactory).create(
+                team, self.context.product
+            ),
+        }
 
     @property
     def person(self):
@@ -953,14 +1027,14 @@ class PersonProductCodeSummaryView(PersonCodeSummaryView):
 class ProductBranchesMenu(ApplicationMenu):
 
     usedfor = IProduct
-    facet = 'branches'
+    facet = "branches"
     links = [
-        'active_reviews',
-        'code_import',
-        ]
+        "active_reviews",
+        "code_import",
+    ]
     extra_attributes = [
-        'active_review_count',
-        ]
+        "active_review_count",
+    ]
 
     @cachedproperty
     def active_review_count(self):
@@ -970,21 +1044,20 @@ class ProductBranchesMenu(ApplicationMenu):
 
     def active_reviews(self):
         text = get_plural_text(
-            self.active_review_count,
-            'Active review',
-            'Active reviews')
-        return Link('+activereviews', text, site='code')
+            self.active_review_count, "Active review", "Active reviews"
+        )
+        return Link("+activereviews", text, site="code")
 
     def code_import(self):
-        text = 'Import a branch'
-        return Link('+new-import', text, icon='add', site='code')
+        text = "Import a branch"
+        return Link("+new-import", text, icon="add", site="code")
 
 
 class ProductBranchListingView(BranchListingView):
     """A base class for product branch listings."""
 
     show_series_links = True
-    no_sort_by = (BranchListingSort.PRODUCT, )
+    no_sort_by = (BranchListingSort.PRODUCT,)
 
     def _getCollection(self):
         return getUtility(IAllBranches).inProduct(self.context)
@@ -994,27 +1067,31 @@ class ProductBranchListingView(BranchListingView):
         dev_focus_branch = self.context.development_focus.branch
         if dev_focus_branch is None:
             return None
-        elif check_permission('launchpad.View', dev_focus_branch):
+        elif check_permission("launchpad.View", dev_focus_branch):
             return dev_focus_branch
         else:
             return None
 
     @property
     def no_branch_message(self):
-        if (self.selected_lifecycle_status is not None
-            and self.hasAnyBranchesVisibleByUser()):
+        if (
+            self.selected_lifecycle_status is not None
+            and self.hasAnyBranchesVisibleByUser()
+        ):
             message = (
-                'There are branches registered for %s '
-                'but none of them match the current filter criteria '
-                'for this page. Try filtering on "Any Status".')
+                "There are branches registered for %s "
+                "but none of them match the current filter criteria "
+                'for this page. Try filtering on "Any Status".'
+            )
         else:
             message = (
-                'There are no branches registered for %s '
-                'in Launchpad today. We recommend you visit '
-                'www.bazaar-vcs.org '
-                'for more information about how you can use the Bazaar '
-                'revision control system to improve community participation '
-                'in this project.')
+                "There are no branches registered for %s "
+                "in Launchpad today. We recommend you visit "
+                "www.bazaar-vcs.org "
+                "for more information about how you can use the Bazaar "
+                "revision control system to improve community participation "
+                "in this project."
+            )
         return message % self.context.displayname
 
     def can_configure_branches(self):
@@ -1022,8 +1099,9 @@ class ProductBranchListingView(BranchListingView):
         return check_permission("launchpad.Edit", self.context)
 
 
-class ProductBranchStatisticsView(BranchCountSummaryView,
-                                  ProductBranchListingView):
+class ProductBranchStatisticsView(
+    BranchCountSummaryView, ProductBranchListingView
+):
     """Portlet containing branch statistics."""
 
     @property
@@ -1037,9 +1115,12 @@ class ProductBranchStatisticsView(BranchCountSummaryView,
         return text.capitalize()
 
 
-class ProductBranchSummaryView(ProductBranchListingView, SortSeriesMixin,
-                               ProductDownloadFileMixin, BranchMirrorMixin):
-
+class ProductBranchSummaryView(
+    ProductBranchListingView,
+    SortSeriesMixin,
+    ProductDownloadFileMixin,
+    BranchMirrorMixin,
+):
     def initialize(self):
         ProductBranchListingView.initialize(self)
         revision_cache = getUtility(IRevisionCache)
@@ -1064,20 +1145,23 @@ class ProductBranchSummaryView(ProductBranchListingView, SortSeriesMixin,
         """The owners of branches."""
         # Listify the owners, there really shouldn't be that many for any
         # one project.
-        return list(getUtility(IPersonSet).getPeopleWithBranches(
-            product=self.context))
+        return list(
+            getUtility(IPersonSet).getPeopleWithBranches(product=self.context)
+        )
 
     @cachedproperty
     def person_owner_count(self):
         """The number of individual people who own branches."""
-        return len([person for person in self._branch_owners
-                    if not person.is_team])
+        return len(
+            [person for person in self._branch_owners if not person.is_team]
+        )
 
     @cachedproperty
     def team_owner_count(self):
         """The number of teams who own branches."""
-        return len([person for person in self._branch_owners
-                    if person.is_team])
+        return len(
+            [person for person in self._branch_owners if person.is_team]
+        )
 
     @property
     def has_development_focus_branch(self):
@@ -1086,34 +1170,40 @@ class ProductBranchSummaryView(ProductBranchListingView, SortSeriesMixin,
 
     @property
     def branch_text(self):
-        return get_plural_text(self.branch_count, _('branch'), _('branches'))
+        return get_plural_text(self.branch_count, _("branch"), _("branches"))
 
     @property
     def person_text(self):
         return get_plural_text(
-            self.person_owner_count, _('person'), _('people'))
+            self.person_owner_count, _("person"), _("people")
+        )
 
     @property
     def team_text(self):
-        return get_plural_text(self.team_owner_count, _('team'), _('teams'))
+        return get_plural_text(self.team_owner_count, _("team"), _("teams"))
 
     @property
     def commit_text(self):
-        return get_plural_text(self.commit_count, _('commit'), _('commits'))
+        return get_plural_text(self.commit_count, _("commit"), _("commits"))
 
     @property
     def committer_text(self):
-        return get_plural_text(self.committer_count, _('person'), _('people'))
+        return get_plural_text(self.committer_count, _("person"), _("people"))
 
     @property
     def external_visible(self):
         return (
             self.context.codehosting_usage == ServiceUsage.EXTERNAL
-            and self.branch)
+            and self.branch
+        )
 
 
-class ProductBranchesView(ProductBranchListingView, SortSeriesMixin,
-                          ProductDownloadFileMixin, BranchMirrorMixin):
+class ProductBranchesView(
+    ProductBranchListingView,
+    SortSeriesMixin,
+    ProductDownloadFileMixin,
+    BranchMirrorMixin,
+):
     """Initial view for products on the code virtual host."""
 
     show_set_development_focus = True
@@ -1122,9 +1212,9 @@ class ProductBranchesView(ProductBranchListingView, SortSeriesMixin,
     @property
     def initial_values(self):
         return {
-            'lifecycle': BranchLifecycleStatusFilter.CURRENT,
-            'sort_by': BranchListingSort.DEFAULT,
-            }
+            "lifecycle": BranchLifecycleStatusFilter.CURRENT,
+            "sort_by": BranchListingSort.DEFAULT,
+        }
 
     def _getSeriesBranches(self):
         """Get the series branches for the product, dev focus first."""
@@ -1138,22 +1228,26 @@ class ProductBranchesView(ProductBranchListingView, SortSeriesMixin,
             if self.selected_lifecycle_status is None:
                 return True
             else:
-                return (branch.lifecycle_status in
-                    self.selected_lifecycle_status)
+                return (
+                    branch.lifecycle_status in self.selected_lifecycle_status
+                )
+
         # The series will always have at least one series, that of the
         # development focus.
         dev_focus_branch = sorted_series[0].branch
-        if not check_permission('launchpad.View', dev_focus_branch):
+        if not check_permission("launchpad.View", dev_focus_branch):
             dev_focus_branch = None
         result = []
         if dev_focus_branch is not None and show_branch(dev_focus_branch):
             result.append(dev_focus_branch)
         for series in sorted_series[1:]:
             branch = series.branch
-            if (branch is not None and
-                branch not in result and
-                check_permission('launchpad.View', branch) and
-                show_branch(branch)):
+            if (
+                branch is not None
+                and branch not in result
+                and check_permission("launchpad.View", branch)
+                and show_branch(branch)
+            ):
                 result.append(branch)
         return result
 
@@ -1163,7 +1257,8 @@ class ProductBranchesView(ProductBranchListingView, SortSeriesMixin,
         series_branches = self._getSeriesBranches()
         branch_query = super()._branches(
             self.selected_lifecycle_status,
-            sort_by=BranchListingSort.MOST_RECENTLY_CHANGED_FIRST)
+            sort_by=BranchListingSort.MOST_RECENTLY_CHANGED_FIRST,
+        )
         # We don't want the initial branch listing to be batched, so only get
         # the batch size - the number of series_branches.
         batch_size = config.launchpad.branchlisting_batch_size
@@ -1171,8 +1266,10 @@ class ProductBranchesView(ProductBranchListingView, SortSeriesMixin,
         # We want to make sure that the series branches do not appear
         # in our branch list.
         branches = [
-            branch for branch in branch_query[:max_branches_from_query]
-            if branch not in series_branches]
+            branch
+            for branch in branch_query[:max_branches_from_query]
+            if branch not in series_branches
+        ]
         series_branches.extend(branches)
         return series_branches
 
@@ -1192,8 +1289,8 @@ class ProductBranchesView(ProductBranchListingView, SortSeriesMixin,
 class ProjectBranchesView(BranchListingView):
     """View for branch listing for a project."""
 
-    no_sort_by = (BranchListingSort.DEFAULT, )
-    extra_columns = ('author', 'product')
+    no_sort_by = (BranchListingSort.DEFAULT,)
+    extra_columns = ("author", "product")
     show_series_links = True
 
     def _getCollection(self):
@@ -1201,20 +1298,24 @@ class ProjectBranchesView(BranchListingView):
 
     @property
     def no_branch_message(self):
-        if (self.selected_lifecycle_status is not None
-            and self.hasAnyBranchesVisibleByUser()):
+        if (
+            self.selected_lifecycle_status is not None
+            and self.hasAnyBranchesVisibleByUser()
+        ):
             message = (
-                'There are branches registered for %s '
-                'but none of them match the current filter criteria '
-                'for this page. Try filtering on "Any Status".')
+                "There are branches registered for %s "
+                "but none of them match the current filter criteria "
+                'for this page. Try filtering on "Any Status".'
+            )
         else:
             message = (
-                'There are no branches registered for %s '
-                'in Launchpad today. We recommend you visit '
-                'www.bazaar-vcs.org '
-                'for more information about how you can use the Bazaar '
-                'revision control system to improve community participation '
-                'in this project group.')
+                "There are no branches registered for %s "
+                "in Launchpad today. We recommend you visit "
+                "www.bazaar-vcs.org "
+                "for more information about how you can use the Bazaar "
+                "revision control system to improve community participation "
+                "in this project group."
+            )
         return message % self.context.displayname
 
 
@@ -1226,7 +1327,7 @@ class BaseSourcePackageBranchesView(BranchListingView):
     @property
     def initial_values(self):
         values = super().initial_values
-        values['sort_by'] = BranchListingSort.MOST_RECENTLY_CHANGED_FIRST
+        values["sort_by"] = BranchListingSort.MOST_RECENTLY_CHANGED_FIRST
         return values
 
 
@@ -1237,7 +1338,8 @@ class DistributionSourcePackageBranchesView(BaseSourcePackageBranchesView):
 
     def _getCollection(self):
         return getUtility(IAllBranches).inDistributionSourcePackage(
-            self.context)
+            self.context
+        )
 
 
 class DistributionBranchListingView(BaseSourcePackageBranchesView):
@@ -1254,14 +1356,15 @@ class DistroSeriesBranchListingView(BaseSourcePackageBranchesView):
 
     @property
     def label(self):
-        return 'Branches for %s' % self.context.displayname
+        return "Branches for %s" % self.context.displayname
 
     def _getCollection(self):
         return getUtility(IAllBranches).inDistroSeries(self.context)
 
 
-class GroupedDistributionSourcePackageBranchesView(LaunchpadView,
-                                                   BranchListingItemsMixin):
+class GroupedDistributionSourcePackageBranchesView(
+    LaunchpadView, BranchListingItemsMixin
+):
     """A view that groups branches into distro series."""
 
     def __init__(self, context, request):
@@ -1270,15 +1373,19 @@ class GroupedDistributionSourcePackageBranchesView(LaunchpadView,
 
     def getBranchCollection(self):
         """See `BranchListingItemsMixin`."""
-        return getUtility(IAllBranches).inDistributionSourcePackage(
-            self.context).visibleByUser(self.user)
+        return (
+            getUtility(IAllBranches)
+            .inDistributionSourcePackage(self.context)
+            .visibleByUser(self.user)
+        )
 
     def _getBranchDict(self):
         """Return a dict of branches grouped by distroseries."""
         branches = {}
         # We're only interested in active branches.
         collection = self.getBranchCollection().withLifecycleStatus(
-            *DEFAULT_BRANCH_STATUS_IN_LISTING)
+            *DEFAULT_BRANCH_STATUS_IN_LISTING
+        )
         for branch in collection.getBranches(eager_load=False):
             branches.setdefault(branch.distroseries, []).append(branch)
         return branches
@@ -1295,8 +1402,10 @@ class GroupedDistributionSourcePackageBranchesView(LaunchpadView,
         # Remember it is possible that the linked branch is not visible by the
         # user.  Unlikely, but possible.
         visible_links = [
-            link for link in links
-            if check_permission('launchpad.View', link.branch)]
+            link
+            for link in links
+            if check_permission("launchpad.View", link.branch)
+        ]
         # Sort into distroseries.
         distro_links = {}
         for link in visible_links:
@@ -1305,7 +1414,7 @@ class GroupedDistributionSourcePackageBranchesView(LaunchpadView,
         # is linked to more than one pocket.  Best here means smaller value.
         official_branches = {}
         for key, value in distro_links.items():
-            ordered = sorted(value, key=attrgetter('pocket'))
+            ordered = sorted(value, key=attrgetter("pocket"))
             seen_branches = set()
             branches = []
             for link in ordered:
@@ -1320,9 +1429,10 @@ class GroupedDistributionSourcePackageBranchesView(LaunchpadView,
         # Sort the branches by the last modified date, and ignore any that are
         # official.
         ordered_branches = sorted(
-            (branch for branch in branches
-             if branch not in official_branches),
-            key=attrgetter('date_last_modified'), reverse=True)
+            (branch for branch in branches if branch not in official_branches),
+            key=attrgetter("date_last_modified"),
+            reverse=True,
+        )
         num_branches = len(ordered_branches)
         num_official = len(official_branches)
         # We want to show at most five branches, with (at most) the most
@@ -1349,7 +1459,8 @@ class GroupedDistributionSourcePackageBranchesView(LaunchpadView,
             if series in all_branches:
                 branches, more_count = self._getSeriesBranches(
                     official_branches.get(series, []),
-                    all_branches.get(series, []))
+                    all_branches.get(series, []),
+                )
                 series_branches[series] = (branches, more_count)
         return series_branches
 
@@ -1400,18 +1511,22 @@ class GroupedDistributionSourcePackageBranchesView(LaunchpadView,
             if series in series_branches_map:
                 branches, more_count = series_branches_map[series]
                 sourcepackage = sp_factory.new(
-                    self.context.sourcepackagename, series)
+                    self.context.sourcepackagename, series
+                )
                 num_branches = len(branches) + more_count
                 num_branches_text = get_plural_text(
-                    num_branches, "branch", "branches")
+                    num_branches, "branch", "branches"
+                )
                 count_string = "%s %s" % (num_branches, num_branches_text)
                 result.append(
-                    {'distroseries': series,
-                     'branches': self.decoratedBranches(branches),
-                     'more-branch-count': more_count,
-                     'package': sourcepackage,
-                     'total-count-string': count_string,
-                     })
+                    {
+                        "distroseries": series,
+                        "branches": self.decoratedBranches(branches),
+                        "more-branch-count": more_count,
+                        "package": sourcepackage,
+                        "total-count-string": count_string,
+                    }
+                )
         return result
 
     @property
@@ -1421,10 +1536,9 @@ class GroupedDistributionSourcePackageBranchesView(LaunchpadView,
 
 
 class SourcePackageBranchesView(BranchListingView):
-
     @property
     def label(self):
-        return 'Branches for %s' % self.context.distroseries.displayname
+        return "Branches for %s" % self.context.distroseries.displayname
 
     # XXX: JonathanLange 2009-03-03 spec=package-branches: This page has no
     # menu yet -- do we need one?
@@ -1452,9 +1566,9 @@ class SourcePackageBranchesView(BranchListingView):
             if not series.active:
                 continue
             if distribution.currentseries == series:
-                dev_focus_css = 'sourcepackage-dev-focus'
+                dev_focus_css = "sourcepackage-dev-focus"
             else:
-                dev_focus_css = 'sourcepackage-not-dev-focus'
+                dev_focus_css = "sourcepackage-not-dev-focus"
             package = SourcePackage(our_sourcepackagename, series)
             # XXX: JonathanLange 2009-05-13 bug=376295: This approach is
             # inefficient. We should instead do something like:
@@ -1469,10 +1583,12 @@ class SourcePackageBranchesView(BranchListingView):
             # generally less than 5.
             num_branches = self._numBranchesInPackage(package)
             num_branches_text = get_plural_text(
-                num_branches, "branch", "branches")
+                num_branches, "branch", "branches"
+            )
             yield dict(
                 series_name=series.displayname,
                 package=package,
-                num_branches='%s %s' % (num_branches, num_branches_text),
+                num_branches="%s %s" % (num_branches, num_branches_text),
                 dev_focus_css=dev_focus_css,
-                linked=(series != our_series))
+                linked=(series != our_series),
+            )
diff --git a/lib/lp/code/browser/branchmergeproposal.py b/lib/lp/code/browser/branchmergeproposal.py
index fcaddeb..c93790c 100644
--- a/lib/lp/code/browser/branchmergeproposal.py
+++ b/lib/lp/code/browser/branchmergeproposal.py
@@ -4,73 +4,52 @@
 """Views, navigation and actions for BranchMergeProposals."""
 
 __all__ = [
-    'BranchMergeCandidateView',
-    'BranchMergeProposalActionNavigationMenu',
-    'BranchMergeProposalAddVoteView',
-    'BranchMergeProposalChangeStatusView',
-    'BranchMergeProposalCommitMessageEditView',
-    'BranchMergeProposalContextMenu',
-    'BranchMergeProposalDeleteView',
-    'BranchMergeProposalDescriptionEditView',
-    'BranchMergeProposalEditMenu',
-    'BranchMergeProposalEditView',
-    'BranchMergeProposalNavigation',
-    'BranchMergeProposalMergedView',
-    'BranchMergeProposalRequestReviewView',
-    'BranchMergeProposalResubmitView',
-    'BranchMergeProposalSubscribersView',
-    'BranchMergeProposalView',
-    'BranchMergeProposalVoteView',
-    'latest_proposals_for_each_branch',
-    ]
+    "BranchMergeCandidateView",
+    "BranchMergeProposalActionNavigationMenu",
+    "BranchMergeProposalAddVoteView",
+    "BranchMergeProposalChangeStatusView",
+    "BranchMergeProposalCommitMessageEditView",
+    "BranchMergeProposalContextMenu",
+    "BranchMergeProposalDeleteView",
+    "BranchMergeProposalDescriptionEditView",
+    "BranchMergeProposalEditMenu",
+    "BranchMergeProposalEditView",
+    "BranchMergeProposalNavigation",
+    "BranchMergeProposalMergedView",
+    "BranchMergeProposalRequestReviewView",
+    "BranchMergeProposalResubmitView",
+    "BranchMergeProposalSubscribersView",
+    "BranchMergeProposalView",
+    "BranchMergeProposalVoteView",
+    "latest_proposals_for_each_branch",
+]
 
-from functools import wraps
 import operator
-from urllib.parse import (
-    urlsplit,
-    urlunsplit,
-    )
+from functools import wraps
+from urllib.parse import urlsplit, urlunsplit
 
+import simplejson
 from lazr.delegates import delegate_to
 from lazr.restful.interface import copy_field
-from lazr.restful.interfaces import (
-    IJSONRequestCache,
-    IWebServiceClientRequest,
-    )
-import simplejson
-from zope.component import (
-    adapter,
-    getMultiAdapter,
-    getUtility,
-    )
+from lazr.restful.interfaces import IJSONRequestCache, IWebServiceClientRequest
+from zope.component import adapter, getMultiAdapter, getUtility
 from zope.formlib import form
 from zope.formlib.widget import CustomWidgetFactory
 from zope.formlib.widgets import TextAreaWidget
-from zope.interface import (
-    implementer,
-    Interface,
-    )
-from zope.schema import (
-    Bool,
-    Choice,
-    Int,
-    Text,
-    )
-from zope.schema.vocabulary import (
-    SimpleTerm,
-    SimpleVocabulary,
-    )
+from zope.interface import Interface, implementer
+from zope.schema import Bool, Choice, Int, Text
+from zope.schema.vocabulary import SimpleTerm, SimpleVocabulary
 
 from lp import _
 from lp.app.browser.launchpadform import (
-    action,
     LaunchpadEditFormView,
     LaunchpadFormView,
-    )
+    action,
+)
 from lp.app.browser.lazrjs import (
     TextAreaEditorWidget,
     vocabulary_to_choice_edit_items,
-    )
+)
 from lp.app.browser.tales import DateTimeFormatterAPI
 from lp.code.adapters.branch import BranchMergeProposalNoPreviewDiffDelta
 from lp.code.browser.codereviewcomment import CodeReviewDisplayComment
@@ -82,7 +61,7 @@ from lp.code.enums import (
     BranchType,
     CodeReviewNotificationLevel,
     CodeReviewVote,
-    )
+)
 from lp.code.errors import (
     BranchMergeProposalExists,
     ClaimReviewFailed,
@@ -90,53 +69,47 @@ from lp.code.errors import (
     GitRepositoryScanFault,
     InvalidBranchMergeProposal,
     WrongBranchMergeProposal,
-    )
+)
 from lp.code.interfaces.branch import IBranch
 from lp.code.interfaces.branchmergeproposal import IBranchMergeProposal
 from lp.code.interfaces.codereviewcomment import ICodeReviewComment
 from lp.code.interfaces.codereviewinlinecomment import (
     ICodeReviewInlineCommentSet,
-    )
+)
 from lp.code.interfaces.codereviewvote import ICodeReviewVoteReference
 from lp.code.interfaces.diff import IPreviewDiff
 from lp.registry.interfaces.person import IPersonSet
 from lp.services.comments.interfaces.conversation import (
     IComment,
     IConversation,
-    )
+)
 from lp.services.config import config
 from lp.services.features import getFeatureFlag
 from lp.services.job.interfaces.job import JobStatus
 from lp.services.librarian.interfaces.client import LibrarianServerError
-from lp.services.propertycache import (
-    cachedproperty,
-    get_property_cache,
-    )
+from lp.services.propertycache import cachedproperty, get_property_cache
 from lp.services.scripts import log
-from lp.services.timeout import (
-    reduced_timeout,
-    TimeoutError,
-    )
+from lp.services.timeout import TimeoutError, reduced_timeout
 from lp.services.webapp import (
-    canonical_url,
     ContextMenu,
-    enabled_with_permission,
     LaunchpadView,
     Link,
     Navigation,
+    canonical_url,
+    enabled_with_permission,
     stepthrough,
-    )
+)
 from lp.services.webapp.authorization import check_permission
 from lp.services.webapp.breadcrumb import Breadcrumb
 from lp.services.webapp.escaping import structured
 from lp.services.webapp.interfaces import ILaunchBag
 from lp.services.webapp.menu import NavigationMenu
 
-
 GIT_HOSTING_ERROR_MSG = (
     "There was an error fetching revisions from git servers. "
     "Please try again in a few minutes. If the problem persists, "
-    "<a href='/launchpad/+addquestion'>contact Launchpad support</a>.")
+    "<a href='/launchpad/+addquestion'>contact Launchpad support</a>."
+)
 
 
 def latest_proposals_for_each_branch(proposals):
@@ -147,7 +120,7 @@ def latest_proposals_for_each_branch(proposals):
     targets = {}
     for proposal in proposals:
         # Don't show the proposal if the user can't see it.
-        if not check_permission('launchpad.View', proposal):
+        if not check_permission("launchpad.View", proposal):
             continue
         # Only show the most recent proposal for any given target.
         date_created = proposal.date_created
@@ -158,7 +131,9 @@ def latest_proposals_for_each_branch(proposals):
 
     return sorted(
         (proposal for proposal, date_created in targets.values()),
-        key=operator.attrgetter('date_created'), reverse=True)
+        key=operator.attrgetter("date_created"),
+        reverse=True,
+    )
 
 
 class BranchMergeProposalBreadcrumb(Breadcrumb):
@@ -166,7 +141,7 @@ class BranchMergeProposalBreadcrumb(Breadcrumb):
 
     @property
     def text(self):
-        return 'Merge into %s' % self.context.merge_target.name
+        return "Merge into %s" % self.context.merge_target.name
 
     @property
     def inside(self):
@@ -175,10 +150,12 @@ class BranchMergeProposalBreadcrumb(Breadcrumb):
 
 def notify(func):
     """Decorate a view method to send a notification."""
+
     @wraps(func)
     def decorator(view, *args, **kwargs):
         with BranchMergeProposalNoPreviewDiffDelta.monitor(view.context):
             return func(view, *args, **kwargs)
+
     return decorator
 
 
@@ -188,15 +165,14 @@ class BranchMergeCandidateView(LaunchpadView):
     def friendly_text(self):
         """Prints friendly text for a branch."""
         friendly_texts = {
-            BranchMergeProposalStatus.WORK_IN_PROGRESS: 'On hold',
-            BranchMergeProposalStatus.NEEDS_REVIEW: 'Ready for review',
-            BranchMergeProposalStatus.CODE_APPROVED: 'Approved',
-            BranchMergeProposalStatus.REJECTED: 'Rejected',
-            BranchMergeProposalStatus.MERGED: 'Merged',
-            BranchMergeProposalStatus.MERGE_FAILED:
-                'Approved [Merge Failed]',
-            BranchMergeProposalStatus.QUEUED: 'Queued',
-            BranchMergeProposalStatus.SUPERSEDED: 'Superseded',
+            BranchMergeProposalStatus.WORK_IN_PROGRESS: "On hold",
+            BranchMergeProposalStatus.NEEDS_REVIEW: "Ready for review",
+            BranchMergeProposalStatus.CODE_APPROVED: "Approved",
+            BranchMergeProposalStatus.REJECTED: "Rejected",
+            BranchMergeProposalStatus.MERGED: "Merged",
+            BranchMergeProposalStatus.MERGE_FAILED: "Approved [Merge Failed]",
+            BranchMergeProposalStatus.QUEUED: "Queued",
+            BranchMergeProposalStatus.SUPERSEDED: "Superseded",
         }
         return friendly_texts[self.context.queue_status]
 
@@ -206,14 +182,16 @@ class BranchMergeCandidateView(LaunchpadView):
 
         Only set if the status is approved or rejected.
         """
-        result = ''
+        result = ""
         if self.context.queue_status in (
             BranchMergeProposalStatus.CODE_APPROVED,
-            BranchMergeProposalStatus.REJECTED):
+            BranchMergeProposalStatus.REJECTED,
+        ):
             formatter = DateTimeFormatterAPI(self.context.date_reviewed)
-            result = '%s %s' % (
+            result = "%s %s" % (
                 self.context.reviewer.displayname,
-                formatter.displaydate())
+                formatter.displaydate(),
+            )
         return result
 
     @property
@@ -226,37 +204,36 @@ class BranchMergeCandidateView(LaunchpadView):
 class BranchMergeProposalMenuMixin:
     """Mixin class for merge proposal menus."""
 
-    @enabled_with_permission('launchpad.AnyPerson')
+    @enabled_with_permission("launchpad.AnyPerson")
     def add_comment(self):
-        return Link('+comment', 'Add a review or comment', icon='add')
+        return Link("+comment", "Add a review or comment", icon="add")
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def edit(self):
-        text = 'Edit details'
+        text = "Edit details"
         enabled = self.context.isMergable()
-        return Link('+edit', text, icon='edit', enabled=enabled)
+        return Link("+edit", text, icon="edit", enabled=enabled)
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def set_description(self):
-        text = 'Set description'
-        return Link('+edit-description', text, icon='add')
+        text = "Set description"
+        return Link("+edit-description", text, icon="add")
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def set_commit_message(self):
-        text = 'Set commit message'
+        text = "Set commit message"
         enabled = self.context.isMergable()
-        return Link('+edit-commit-message', text, icon='add',
-                    enabled=enabled)
+        return Link("+edit-commit-message", text, icon="add", enabled=enabled)
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def edit_status(self):
-        text = 'Change status'
-        return Link('+edit-status', text, icon='edit')
+        text = "Change status"
+        return Link("+edit-status", text, icon="edit")
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def delete(self):
-        text = 'Delete proposal to merge'
-        return Link('+delete', text, icon='trash-icon')
+        text = "Delete proposal to merge"
+        return Link("+delete", text, icon="trash-icon")
 
     def _enabledForStatus(self, next_state):
         """True if the next_state is a valid transition for the current user.
@@ -270,75 +247,75 @@ class BranchMergeProposalMenuMixin:
         else:
             return bmp.isValidTransition(next_state, self.user)
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def request_review(self):
-        text = 'Request a review'
+        text = "Request a review"
         enabled = self._enabledForStatus(
-            BranchMergeProposalStatus.NEEDS_REVIEW)
-        if (self.context.queue_status ==
-            BranchMergeProposalStatus.NEEDS_REVIEW):
+            BranchMergeProposalStatus.NEEDS_REVIEW
+        )
+        if self.context.queue_status == BranchMergeProposalStatus.NEEDS_REVIEW:
             enabled = True
             if len(self.context.votes) > 0:
-                text = 'Request another review'
-        return Link('+request-review', text, icon='add', enabled=enabled)
+                text = "Request another review"
+        return Link("+request-review", text, icon="add", enabled=enabled)
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def merge(self):
-        text = 'Mark as merged'
-        enabled = self._enabledForStatus(
-            BranchMergeProposalStatus.MERGED)
-        return Link('+merged', text, enabled=enabled)
+        text = "Mark as merged"
+        enabled = self._enabledForStatus(BranchMergeProposalStatus.MERGED)
+        return Link("+merged", text, enabled=enabled)
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def update_merge_revno(self):
         if IBranch.providedBy(self.context.merge_target):
-            text = 'Update revision number'
+            text = "Update revision number"
         else:
-            text = 'Update revision ID'
-        return Link('+merged', text)
+            text = "Update revision ID"
+        return Link("+merged", text)
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def resubmit(self):
-        text = 'Resubmit proposal'
-        enabled = self._enabledForStatus(
-            BranchMergeProposalStatus.SUPERSEDED)
-        return Link('+resubmit', text, enabled=enabled, icon='edit')
+        text = "Resubmit proposal"
+        enabled = self._enabledForStatus(BranchMergeProposalStatus.SUPERSEDED)
+        return Link("+resubmit", text, enabled=enabled, icon="edit")
 
     def link_bug(self):
-        text = 'Link a bug report'
-        return Link('+linkbug', text, icon='add')
+        text = "Link a bug report"
+        return Link("+linkbug", text, icon="add")
 
 
-class BranchMergeProposalEditMenu(NavigationMenu,
-                                  BranchMergeProposalMenuMixin):
+class BranchMergeProposalEditMenu(
+    NavigationMenu, BranchMergeProposalMenuMixin
+):
     """Edit menu for Branch Merge Proposals."""
 
     usedfor = IBranchMergeProposal
-    title = 'Edit Proposal'
-    facet = 'branches'
-    links = ['resubmit', 'delete']
+    title = "Edit Proposal"
+    facet = "branches"
+    links = ["resubmit", "delete"]
 
     @property
     def branch_merge_proposal(self):
         return self.context
 
 
-class BranchMergeProposalContextMenu(ContextMenu,
-                                     BranchMergeProposalMenuMixin):
+class BranchMergeProposalContextMenu(
+    ContextMenu, BranchMergeProposalMenuMixin
+):
     """Context menu for merge proposals."""
 
     usedfor = IBranchMergeProposal
     links = [
-        'add_comment',
-        'set_commit_message',
-        'set_description',
-        'edit_status',
-        'link_bug',
-        'merge',
-        'request_review',
-        'resubmit',
-        'update_merge_revno',
-        ]
+        "add_comment",
+        "set_commit_message",
+        "set_description",
+        "edit_status",
+        "link_bug",
+        "merge",
+        "request_review",
+        "resubmit",
+        "update_merge_revno",
+    ]
 
     @property
     def branch_merge_proposal(self):
@@ -349,13 +326,14 @@ class IBranchMergeProposalActionMenu(Interface):
     """A marker interface for the global action navigation menu."""
 
 
-class BranchMergeProposalActionNavigationMenu(NavigationMenu,
-                                              BranchMergeProposalMenuMixin):
+class BranchMergeProposalActionNavigationMenu(
+    NavigationMenu, BranchMergeProposalMenuMixin
+):
     """A sub-menu for acting upon a Product."""
 
     usedfor = IBranchMergeProposalActionMenu
-    facet = 'branches'
-    links = ('resubmit', 'delete')
+    facet = "branches"
+    links = ("resubmit", "delete")
 
     @property
     def branch_merge_proposal(self):
@@ -365,6 +343,7 @@ class BranchMergeProposalActionNavigationMenu(NavigationMenu,
 
 class UnmergedRevisionsMixin:
     """Provides the methods needed to show unmerged revisions."""
+
     # This is set at self.unlanded_revisions, and should be used at view level
     # as self.commit_infos_message (see git-macros.pt).
     _unlanded_revisions_message = None
@@ -372,17 +351,20 @@ class UnmergedRevisionsMixin:
     @cachedproperty
     def unlanded_revisions(self):
         """Return the unlanded revisions from the source branch."""
-        self._unlanded_revisions_message = ''
+        self._unlanded_revisions_message = ""
         with reduced_timeout(1.0, webapp_max=5.0):
             try:
                 return self.context.getUnlandedSourceBranchRevisions()
             except TimeoutError:
                 log.exception(
                     "Timeout fetching unlanded source revisions for merge "
-                    "proposal %s (%s => %s)" % (
+                    "proposal %s (%s => %s)"
+                    % (
                         self.context.id,
                         self.context.merge_source.identity,
-                        self.context.merge_target.identity))
+                        self.context.merge_target.identity,
+                    )
+                )
                 return []
             except GitRepositoryScanFault as e:
                 log.exception("Error scanning git repository: %s" % e)
@@ -425,12 +407,14 @@ class BranchMergeProposalRevisionIdMixin:
             return revision_id
         else:
             branch_revision = source_branch.getBranchRevision(
-                revision_id=revision_id)
+                revision_id=revision_id
+            )
             if branch_revision is None:
                 return "no longer in the source branch."
             elif branch_revision.sequence is None:
                 return (
-                    "no longer in the revision history of the source branch.")
+                    "no longer in the revision history of the source branch."
+                )
             else:
                 return branch_revision.sequence
 
@@ -438,7 +422,8 @@ class BranchMergeProposalRevisionIdMixin:
     def reviewed_revision_number(self):
         """Return the number of the reviewed revision."""
         return self._getRevisionNumberForRevisionId(
-            self.context.reviewed_revision_id)
+            self.context.reviewed_revision_id
+        )
 
 
 class BranchMergeProposalNavigation(Navigation):
@@ -446,7 +431,7 @@ class BranchMergeProposalNavigation(Navigation):
 
     usedfor = IBranchMergeProposal
 
-    @stepthrough('reviews')
+    @stepthrough("reviews")
     def traverse_review(self, id):
         """Navigate to a CodeReviewVoteReference through its BMP."""
         try:
@@ -458,7 +443,7 @@ class BranchMergeProposalNavigation(Navigation):
         except WrongBranchMergeProposal:
             return None
 
-    @stepthrough('comments')
+    @stepthrough("comments")
     def traverse_comment(self, id):
         try:
             id = int(id)
@@ -486,7 +471,7 @@ class BranchMergeProposalNavigation(Navigation):
         except (DiffNotFound, WrongBranchMergeProposal):
             return None
 
-    @stepthrough('+review')
+    @stepthrough("+review")
     def review(self, id):
         """Step to the CodeReviewVoteReference."""
         try:
@@ -514,7 +499,7 @@ class ClaimButton(Interface):
 
 
 class BranchMergeProposalStatusMixin:
-    '''A mixin for generating status vocabularies.'''
+    """A mixin for generating status vocabularies."""
 
     def _createStatusVocabulary(self):
         # Create the vocabulary that is used for the status widget.
@@ -524,7 +509,7 @@ class BranchMergeProposalStatusMixin:
             BranchMergeProposalStatus.CODE_APPROVED,
             BranchMergeProposalStatus.REJECTED,
             BranchMergeProposalStatus.MERGED,
-            )
+        )
         terms = []
         for status in possible_next_states:
             if not self.context.isValidTransition(status, self.user):
@@ -549,7 +534,7 @@ class DiffRenderingMixin:
     @property
     def diff_available(self):
         """Is the preview diff available from the librarian?"""
-        if getattr(self, '_diff_available', None) is None:
+        if getattr(self, "_diff_available", None) is None:
             # Load the cache so that the answer is known.
             self.preview_diff_text
         return self._diff_available
@@ -565,9 +550,9 @@ class DiffRenderingMixin:
             diff = preview_diff.text
         except (LookupError, LibrarianServerError):
             self._diff_available = False
-            diff = ''
+            diff = ""
         # Strip off the trailing carriage returns.
-        return diff.rstrip('\n')
+        return diff.rstrip("\n")
 
     @cachedproperty
     def diff_oversized(self):
@@ -582,7 +567,7 @@ class DiffRenderingMixin:
         if preview_diff is None:
             return False
         diff_text = self.preview_diff_text
-        return diff_text.count('\n') >= config.diff.max_format_lines
+        return diff_text.count("\n") >= config.diff.max_format_lines
 
 
 class ICodeReviewNewRevisions(IComment):
@@ -631,11 +616,14 @@ class CodeReviewNewRevisions:
 
 
 @implementer(IBranchMergeProposalActionMenu)
-class BranchMergeProposalView(LaunchpadFormView, UnmergedRevisionsMixin,
-                              BranchMergeProposalRevisionIdMixin,
-                              BranchMergeProposalStatusMixin,
-                              DiffRenderingMixin,
-                              HasRevisionStatusReportsMixin):
+class BranchMergeProposalView(
+    LaunchpadFormView,
+    UnmergedRevisionsMixin,
+    BranchMergeProposalRevisionIdMixin,
+    BranchMergeProposalStatusMixin,
+    DiffRenderingMixin,
+    HasRevisionStatusReportsMixin,
+):
     """A basic view used for the index page."""
 
     schema = ClaimButton
@@ -643,22 +631,26 @@ class BranchMergeProposalView(LaunchpadFormView, UnmergedRevisionsMixin,
     def initialize(self):
         super().initialize()
         cache = IJSONRequestCache(self.request)
-        cache.objects['branch_name'] = self.context.merge_source.name
-        if (IBranch.providedBy(self.context.merge_source) and
-                getFeatureFlag("code.bzr.diff.disable_proxy")):
+        cache.objects["branch_name"] = self.context.merge_source.name
+        if IBranch.providedBy(self.context.merge_source) and getFeatureFlag(
+            "code.bzr.diff.disable_proxy"
+        ):
             # This fallback works for public branches, but not private ones.
-            cache.objects['branch_diff_link'] = (
-                'https://%s/+loggerhead/%s/diff/' % (
-                    config.launchpad.code_domain,
-                    self.context.source_branch.unique_name))
+            cache.objects[
+                "branch_diff_link"
+            ] = "https://%s/+loggerhead/%s/diff/"; % (
+                config.launchpad.code_domain,
+                self.context.source_branch.unique_name,
+            )
         else:
-            cache.objects['branch_diff_link'] = (
-                canonical_url(self.context.parent) + '/+diff/')
+            cache.objects["branch_diff_link"] = (
+                canonical_url(self.context.parent) + "/+diff/"
+            )
 
-    @action('Claim', name='claim')
+    @action("Claim", name="claim")
     def claim_action(self, action, data):
         """Claim this proposal."""
-        request = self.context.getVoteReference(data['review_id'])
+        request = self.context.getVoteReference(data["review_id"])
         if request is not None:
             try:
                 request.claimReview(self.user)
@@ -669,7 +661,7 @@ class BranchMergeProposalView(LaunchpadFormView, UnmergedRevisionsMixin,
     @property
     def comment_location(self):
         """Location of page for commenting on this proposal."""
-        return canonical_url(self.context, view_name='+comment')
+        return canonical_url(self.context, view_name="+comment")
 
     @property
     def source_git_ssh_url(self):
@@ -687,15 +679,18 @@ class BranchMergeProposalView(LaunchpadFormView, UnmergedRevisionsMixin,
         merge_proposal = self.context
         with reduced_timeout(1.0, webapp_max=5.0):
             try:
-                self._conversation_message = ''
+                self._conversation_message = ""
                 groups = list(merge_proposal.getRevisionsSinceReviewStart())
             except TimeoutError:
                 log.exception(
                     "Timeout fetching revisions since review start for "
-                    "merge proposal %s (%s => %s)" % (
+                    "merge proposal %s (%s => %s)"
+                    % (
                         merge_proposal.id,
                         merge_proposal.merge_source.identity,
-                        merge_proposal.merge_target.identity))
+                        merge_proposal.merge_target.identity,
+                    )
+                )
                 groups = []
             except GitRepositoryScanFault as e:
                 log.exception("Error scanning git repository: %s" % e)
@@ -707,13 +702,18 @@ class BranchMergeProposalView(LaunchpadFormView, UnmergedRevisionsMixin,
         user = getUtility(ILaunchBag).user
         strip_invisible = not merge_proposal.userCanSetCommentVisibility(user)
         comments = []
-        if (getFeatureFlag('code.incremental_diffs.enabled') and
-                merge_proposal.source_branch is not None):
+        if (
+            getFeatureFlag("code.incremental_diffs.enabled")
+            and merge_proposal.source_branch is not None
+        ):
             # XXX cjwatson 2016-05-09: Implement for Git.
             ranges = [
-                (revisions[0].revision.getLefthandParent(),
-                 revisions[-1].revision)
-                for revisions in groups]
+                (
+                    revisions[0].revision.getLefthandParent(),
+                    revisions[-1].revision,
+                )
+                for revisions in groups
+            ]
             diffs = merge_proposal.getIncrementalDiffs(ranges)
         else:
             diffs = [None] * len(groups)
@@ -723,16 +723,19 @@ class BranchMergeProposalView(LaunchpadFormView, UnmergedRevisionsMixin,
             else:
                 last_date_created = revisions[-1]["author_date"]
             newrevs = CodeReviewNewRevisions(
-                revisions, last_date_created, source, diff)
+                revisions, last_date_created, source, diff
+            )
             comments.append(newrevs)
         while merge_proposal is not None:
             from_superseded = merge_proposal != self.context
             comments.extend(
                 CodeReviewDisplayComment(
-                    comment, from_superseded, limit_length=True)
-                for comment in merge_proposal.all_comments)
+                    comment, from_superseded, limit_length=True
+                )
+                for comment in merge_proposal.all_comments
+            )
             merge_proposal = merge_proposal.supersedes
-        comments = sorted(comments, key=operator.attrgetter('date'))
+        comments = sorted(comments, key=operator.attrgetter("date"))
         if strip_invisible:
             comments = [c for c in comments if c.visible or c.author == user]
         self._populate_previewdiffs(comments)
@@ -751,25 +754,31 @@ class BranchMergeProposalView(LaunchpadFormView, UnmergedRevisionsMixin,
 
         Only operated on objects providing `ICodeReviewComment`.
         """
-        comments = [comment for comment in comments
-                    if ICodeReviewComment.providedBy(comment)]
+        comments = [
+            comment
+            for comment in comments
+            if ICodeReviewComment.providedBy(comment)
+        ]
         cric_set = getUtility(ICodeReviewInlineCommentSet)
         relations = cric_set.getPreviewDiffsForComments(comments)
         for comment in comments:
-            get_property_cache(
-                comment).previewdiff_id = relations.get(comment.id)
+            get_property_cache(comment).previewdiff_id = relations.get(
+                comment.id
+            )
 
     @property
     def label(self):
         return "Merge %s into %s" % (
             self.context.merge_source.identity,
-            self.context.merge_target.identity)
+            self.context.merge_target.identity,
+        )
 
     @property
     def pending_diff(self):
         return (
-            self.context.next_preview_diff_job is not None or
-            self.context.merge_source.pending_updates)
+            self.context.next_preview_diff_job is not None
+            or self.context.merge_source.pending_updates
+        )
 
     @cachedproperty
     def preview_diff(self):
@@ -791,7 +800,8 @@ class BranchMergeProposalView(LaunchpadFormView, UnmergedRevisionsMixin,
             # blueprints.
             return []
         return list(
-            self.context.source_branch.getSpecificationLinks(self.user))
+            self.context.source_branch.getSpecificationLinks(self.user)
+        )
 
     @cachedproperty
     def linked_bugtasks(self):
@@ -809,10 +819,11 @@ class BranchMergeProposalView(LaunchpadFormView, UnmergedRevisionsMixin,
     def description_html(self):
         """The description as widget HTML."""
         mp = self.context
-        description = IBranchMergeProposal['description']
+        description = IBranchMergeProposal["description"]
         title = "Description of the change"
         return TextAreaEditorWidget(
-            mp, description, title, edit_view='+edit-description')
+            mp, description, title, edit_view="+edit-description"
+        )
 
     @property
     def opengraph_description(self):
@@ -831,23 +842,28 @@ class BranchMergeProposalView(LaunchpadFormView, UnmergedRevisionsMixin,
     def commit_message_html(self):
         """The commit message as widget HTML."""
         mp = self.context
-        commit_message = IBranchMergeProposal['commit_message']
+        commit_message = IBranchMergeProposal["commit_message"]
         title = "Commit message"
         return TextAreaEditorWidget(
-            mp, commit_message, title, edit_view='+edit-commit-message')
+            mp, commit_message, title, edit_view="+edit-commit-message"
+        )
 
     @property
     def status_config(self):
         """The config to configure the ChoiceSource JS widget."""
-        return simplejson.dumps({
-            'status_widget_items': vocabulary_to_choice_edit_items(
-                self._createStatusVocabulary(),
-                css_class_prefix='mergestatus'),
-            'status_value': self.context.queue_status.title,
-            'source_revid': self.source_revid,
-            'user_can_edit_status': check_permission(
-                'launchpad.Edit', self.context),
-            })
+        return simplejson.dumps(
+            {
+                "status_widget_items": vocabulary_to_choice_edit_items(
+                    self._createStatusVocabulary(),
+                    css_class_prefix="mergestatus",
+                ),
+                "status_value": self.context.queue_status.title,
+                "source_revid": self.source_revid,
+                "user_can_edit_status": check_permission(
+                    "launchpad.Edit", self.context
+                ),
+            }
+        )
 
     @property
     def show_diff_update_link(self):
@@ -874,7 +890,7 @@ class BranchMergeProposalRescanView(LaunchpadEditFormView):
 
     field_names = []
 
-    @action('Rescan', name='rescan')
+    @action("Rescan", name="rescan")
     def rescan(self, action, data):
         source_job = self.context.merge_source.getLatestScanJob()
         target_job = self.context.merge_target.getLatestScanJob()
@@ -897,19 +913,21 @@ class DecoratedCodeReviewVoteReference:
         CodeReviewVote.NEEDS_INFO: CodeReviewVote.NEEDS_INFO.title,
         CodeReviewVote.NEEDS_FIXING: CodeReviewVote.NEEDS_FIXING.title,
         CodeReviewVote.NEEDS_RESUBMITTING: (
-            CodeReviewVote.NEEDS_RESUBMITTING.title),
-        }
+            CodeReviewVote.NEEDS_RESUBMITTING.title
+        ),
+    }
 
     def __init__(self, context, user, users_vote):
         self.context = context
-        self.can_change_review = (user == context.reviewer)
+        self.can_change_review = user == context.reviewer
         if user is None:
             self.user_can_review = False
         else:
             # The user cannot review for a requested team review if the user
             # has already reviewed this proposal.
-            self.user_can_review = (self.can_change_review or
-                 (user.inTeam(context.reviewer) and (users_vote is None)))
+            self.user_can_review = self.can_change_review or (
+                user.inTeam(context.reviewer) and (users_vote is None)
+            )
         if context.reviewer == user:
             self.user_can_claim = False
         else:
@@ -921,10 +939,11 @@ class DecoratedCodeReviewVoteReference:
 
     @cachedproperty
     def trusted(self):
-        """ Is the person a trusted reviewer."""
+        """Is the person a trusted reviewer."""
         proposal = self.context.branch_merge_proposal
         return proposal.merge_target.isPersonTrustedReviewer(
-            self.context.reviewer)
+            self.context.reviewer
+        )
 
     @property
     def show_date_requested(self):
@@ -940,7 +959,7 @@ class DecoratedCodeReviewVoteReference:
     def review_type_str(self):
         """We want '' not 'None' if no type set."""
         if self.context.review_type is None:
-            return ''
+            return ""
         return self.context.review_type
 
     @property
@@ -969,12 +988,13 @@ class BranchMergeProposalVoteView(LaunchpadView):
         # the branch is not in a final state.  We want to show the table as
         # the menu link is now shown in the table itself.
         can_request_review = (
-            check_permission('launchpad.Edit', self.context) and
-            self.context.isMergable())
+            check_permission("launchpad.Edit", self.context)
+            and self.context.isMergable()
+        )
 
         # Show the table if there are review to show, or the user can review,
         # or if the user can request a review.
-        return (len(self.reviews) > 0 or can_request_review)
+        return len(self.reviews) > 0 or can_request_review
 
     @cachedproperty
     def reviews(self):
@@ -983,32 +1003,37 @@ class BranchMergeProposalVoteView(LaunchpadView):
         # This would use getUsersVoteReference, but we need to
         # be able to cache the property. We don't need to normalize
         # the review types.
-        users_vote = sorted((uv for uv in self.context.votes
-                             if uv.reviewer == self.user),
-                            key=operator.attrgetter('date_created'))
+        users_vote = sorted(
+            (uv for uv in self.context.votes if uv.reviewer == self.user),
+            key=operator.attrgetter("date_created"),
+        )
         final_vote = users_vote[0] if users_vote else None
-        return [DecoratedCodeReviewVoteReference(vote, self.user, final_vote)
-                for vote in self.context.votes
-                if check_permission('launchpad.LimitedView', vote.reviewer)]
+        return [
+            DecoratedCodeReviewVoteReference(vote, self.user, final_vote)
+            for vote in self.context.votes
+            if check_permission("launchpad.LimitedView", vote.reviewer)
+        ]
 
     @cachedproperty
     def current_reviews(self):
         """The current votes ordered by vote then date."""
         # We want the reviews in a specific order.
         # Disapprovals first, then approvals, then abstentions.
-        reviews = [review for review in self.reviews
-                   if review.comment is not None]
-        return sorted(reviews, key=operator.attrgetter('date_of_comment'),
-                      reverse=True)
+        reviews = [
+            review for review in self.reviews if review.comment is not None
+        ]
+        return sorted(
+            reviews, key=operator.attrgetter("date_of_comment"), reverse=True
+        )
 
     @cachedproperty
     def requested_reviews(self):
         """Reviews requested but not yet done."""
-        reviews = [review for review in self.reviews
-                   if review.comment is None]
+        reviews = [review for review in self.reviews if review.comment is None]
         # Now sort so the most recently created is first.
-        return sorted(reviews, key=operator.attrgetter('date_created'),
-                      reverse=True)
+        return sorted(
+            reviews, key=operator.attrgetter("date_created"), reverse=True
+        )
 
 
 class BranchMergeProposalScheduleUpdateDiffView(LaunchpadEditFormView):
@@ -1017,7 +1042,7 @@ class BranchMergeProposalScheduleUpdateDiffView(LaunchpadEditFormView):
 
     field_names = []
 
-    @action('Update', name='update')
+    @action("Update", name="update")
     def update(self, action, data):
         self.context.scheduleDiffUpdates()
         self.request.response.addNotification("Diff update scheduled")
@@ -1027,12 +1052,13 @@ class BranchMergeProposalScheduleUpdateDiffView(LaunchpadEditFormView):
 class IReviewRequest(Interface):
     """Schema for requesting a review."""
 
-    reviewer = copy_field(ICodeReviewVoteReference['reviewer'])
+    reviewer = copy_field(ICodeReviewVoteReference["reviewer"])
 
     review_type = copy_field(
-        ICodeReviewVoteReference['review_type'],
-        description='Lowercase keywords describing the type of review you '
-                    'would like to be performed.')
+        ICodeReviewVoteReference["review_type"],
+        description="Lowercase keywords describing the type of review you "
+        "would like to be performed.",
+    )
 
 
 class BranchMergeProposalRequestReviewView(LaunchpadEditFormView):
@@ -1044,7 +1070,7 @@ class BranchMergeProposalRequestReviewView(LaunchpadEditFormView):
     @property
     def initial_values(self):
         """Force the non-BMP values to None."""
-        return {'reviewer': None, 'review_type': None}
+        return {"reviewer": None, "review_type": None}
 
     @property
     def adapters(self):
@@ -1054,8 +1080,9 @@ class BranchMergeProposalRequestReviewView(LaunchpadEditFormView):
     @property
     def is_needs_review(self):
         """Return True if queue status is NEEDS_REVIEW."""
-        return (self.context.queue_status ==
-            BranchMergeProposalStatus.NEEDS_REVIEW)
+        return (
+            self.context.queue_status == BranchMergeProposalStatus.NEEDS_REVIEW
+        )
 
     @property
     def next_url(self):
@@ -1067,25 +1094,28 @@ class BranchMergeProposalRequestReviewView(LaunchpadEditFormView):
         """Request a `review_type` review from `candidate` and email them."""
         self.context.nominateReviewer(candidate, self.user, review_type)
 
-    @action('Request Review', name='review')
+    @action("Request Review", name="review")
     @notify
     def review_action(self, action, data):
         """Set 'Needs review' status, nominate reviewers, send emails."""
         self.context.requestReview()
-        candidate = data.pop('reviewer', None)
-        review_type = data.pop('review_type', None)
+        candidate = data.pop("reviewer", None)
+        review_type = data.pop("review_type", None)
         if candidate is not None:
             self.requestReview(candidate, review_type)
 
     def validate(self, data):
         """Ensure that the proposal is in an appropriate state."""
         if not self.context.isMergable():
-            self.addError("The merge proposal is not an a valid state to "
-                          "mark as 'Needs review'.")
+            self.addError(
+                "The merge proposal is not an a valid state to "
+                "mark as 'Needs review'."
+            )
 
 
-class MergeProposalEditView(LaunchpadEditFormView,
-                            BranchMergeProposalRevisionIdMixin):
+class MergeProposalEditView(
+    LaunchpadEditFormView, BranchMergeProposalRevisionIdMixin
+):
     """A base class for merge proposal edit views."""
 
     def initialize(self):
@@ -1098,15 +1128,18 @@ class MergeProposalEditView(LaunchpadEditFormView,
 class ResubmitSchema(IBranchMergeProposal):
 
     break_link = Bool(
-        title='Start afresh',
+        title="Start afresh",
         description=(
-            'Do not show old conversation and do not link to superseded'
-            ' proposal.'),
-        default=False,)
+            "Do not show old conversation and do not link to superseded"
+            " proposal."
+        ),
+        default=False,
+    )
 
 
-class BranchMergeProposalResubmitView(LaunchpadFormView,
-                                      UnmergedRevisionsMixin):
+class BranchMergeProposalResubmitView(
+    LaunchpadFormView, UnmergedRevisionsMixin
+):
     """The view to resubmit a proposal to merge."""
 
     schema = ResubmitSchema
@@ -1114,11 +1147,14 @@ class BranchMergeProposalResubmitView(LaunchpadFormView,
     page_title = label = "Resubmit proposal to merge"
 
     custom_widget_source_git_ref = CustomWidgetFactory(
-        GitRefWidget, require_branch=True)
+        GitRefWidget, require_branch=True
+    )
     custom_widget_target_git_ref = CustomWidgetFactory(
-        GitRefWidget, require_branch=True)
+        GitRefWidget, require_branch=True
+    )
     custom_widget_prerequisite_git_ref = CustomWidgetFactory(
-        GitRefWidget, require_branch=True)
+        GitRefWidget, require_branch=True
+    )
 
     def initialize(self):
         self.cancel_url = canonical_url(self.context)
@@ -1128,52 +1164,63 @@ class BranchMergeProposalResubmitView(LaunchpadFormView,
     def field_names(self):
         if IBranch.providedBy(self.context.merge_source):
             field_names = [
-                'source_branch',
-                'target_branch',
-                'prerequisite_branch',
-                ]
+                "source_branch",
+                "target_branch",
+                "prerequisite_branch",
+            ]
         else:
             field_names = [
-                'source_git_ref',
-                'target_git_ref',
-                'prerequisite_git_ref',
-                ]
-        field_names.extend([
-            'description',
-            'commit_message',
-            'break_link',
-            ])
+                "source_git_ref",
+                "target_git_ref",
+                "prerequisite_git_ref",
+            ]
+        field_names.extend(
+            [
+                "description",
+                "commit_message",
+                "break_link",
+            ]
+        )
         return field_names
 
     @property
     def initial_values(self):
         UNSET = object()
-        items = ((key, getattr(self.context, key, UNSET)) for key in
-                  self.field_names if key != 'break_link')
+        items = (
+            (key, getattr(self.context, key, UNSET))
+            for key in self.field_names
+            if key != "break_link"
+        )
         return dict(item for item in items if item[1] is not UNSET)
 
-    @action('Resubmit', name='resubmit')
+    @action("Resubmit", name="resubmit")
     @notify
     def resubmit_action(self, action, data):
         """Resubmit this proposal."""
         try:
             if IBranch.providedBy(self.context.merge_source):
-                merge_source = data['source_branch']
-                merge_target = data['target_branch']
-                merge_prerequisite = data['prerequisite_branch']
+                merge_source = data["source_branch"]
+                merge_target = data["target_branch"]
+                merge_prerequisite = data["prerequisite_branch"]
             else:
-                merge_source = data['source_git_ref']
-                merge_target = data['target_git_ref']
-                merge_prerequisite = data.get('prerequisite_git_ref')
+                merge_source = data["source_git_ref"]
+                merge_target = data["target_git_ref"]
+                merge_prerequisite = data.get("prerequisite_git_ref")
             proposal = self.context.resubmit(
-                self.user, merge_source, merge_target, merge_prerequisite,
-                data['commit_message'], data['description'],
-                data['break_link'])
+                self.user,
+                merge_source,
+                merge_target,
+                merge_prerequisite,
+                data["commit_message"],
+                data["description"],
+                data["break_link"],
+            )
         except BranchMergeProposalExists as e:
             message = structured(
                 'Cannot resubmit because <a href="%(url)s">a similar merge'
-                ' proposal</a> is already active.',
-                url=canonical_url(e.existing_proposal))
+                " proposal</a> is already active.",
+                url=canonical_url(e.existing_proposal),
+            )
             self.request.response.addErrorNotification(message)
             self.next_url = canonical_url(self.context)
             return None
@@ -1186,11 +1233,12 @@ class BranchMergeProposalResubmitView(LaunchpadFormView,
 
 class BranchMergeProposalEditView(MergeProposalEditView):
     """The view to control the editing of merge proposals."""
+
     schema = IBranchMergeProposal
     page_title = label = "Edit branch merge proposal"
     field_names = ["commit_message", "whiteboard"]
 
-    @action('Update', name='update')
+    @action("Update", name="update")
     def update_action(self, action, data):
         """Update the whiteboard and go back to the source branch."""
         self.updateContextFromData(data)
@@ -1202,9 +1250,9 @@ class BranchMergeProposalCommitMessageEditView(MergeProposalEditView):
     schema = IBranchMergeProposal
     label = "Edit merge proposal commit message"
     page_title = label
-    field_names = ['commit_message']
+    field_names = ["commit_message"]
 
-    @action('Update', name='update')
+    @action("Update", name="update")
     def update_action(self, action, data):
         """Update the commit message."""
         self.updateContextFromData(data)
@@ -1216,9 +1264,9 @@ class BranchMergeProposalDescriptionEditView(MergeProposalEditView):
     schema = IBranchMergeProposal
     label = "Edit merge proposal description"
     page_title = label
-    field_names = ['description']
+    field_names = ["description"]
 
-    @action('Update', name='update')
+    @action("Update", name="update")
     def update_action(self, action, data):
         """Update the commit message."""
         self.updateContextFromData(data)
@@ -1226,9 +1274,10 @@ class BranchMergeProposalDescriptionEditView(MergeProposalEditView):
 
 class BranchMergeProposalDeleteView(MergeProposalEditView):
     """The view to control the deletion of merge proposals."""
+
     schema = IBranchMergeProposal
     field_names = []
-    page_title = label = 'Delete proposal to merge branch'
+    page_title = label = "Delete proposal to merge branch"
 
     def initialize(self):
         # Store the source branch for `next_url` to make sure that
@@ -1237,7 +1286,7 @@ class BranchMergeProposalDeleteView(MergeProposalEditView):
         self.merge_source = self.context.merge_source
         super().initialize()
 
-    @action('Delete proposal', name='delete')
+    @action("Delete proposal", name="delete")
     def delete_action(self, action, data):
         """Delete the merge proposal and go back to the source branch."""
         self.context.deleteProposal()
@@ -1247,6 +1296,7 @@ class BranchMergeProposalDeleteView(MergeProposalEditView):
 
 class BranchMergeProposalMergedView(LaunchpadEditFormView):
     """The view to mark a merge proposal as merged."""
+
     schema = IBranchMergeProposal
     page_title = label = "Edit branch merge proposal"
     for_input = True
@@ -1267,13 +1317,13 @@ class BranchMergeProposalMergedView(LaunchpadEditFormView):
                 revno = self.context.merged_revno
             else:
                 revno = self.context.merge_target.revision_count
-            return {'merged_revno': revno}
+            return {"merged_revno": revno}
         else:
             if self.context.merged_revision_id is not None:
                 revision_id = self.context.merged_revision_id
             else:
                 revision_id = self.context.merge_target.commit_sha1
-            return {'merged_revision_id': revision_id}
+            return {"merged_revision_id": revision_id}
 
     @property
     def next_url(self):
@@ -1281,31 +1331,34 @@ class BranchMergeProposalMergedView(LaunchpadEditFormView):
 
     cancel_url = next_url
 
-    @action('Mark as Merged', name='mark_merged')
+    @action("Mark as Merged", name="mark_merged")
     @notify
     def mark_merged_action(self, action, data):
         """Update the whiteboard and go back to the source branch."""
         if IBranch.providedBy(self.context.merge_target):
-            kwargs = {'merged_revno': data['merged_revno']}
+            kwargs = {"merged_revno": data["merged_revno"]}
         else:
-            kwargs = {'merged_revision_id': data['merged_revision_id']}
+            kwargs = {"merged_revision_id": data["merged_revision_id"]}
         if self.context.queue_status == BranchMergeProposalStatus.MERGED:
             self.context.markAsMerged(**kwargs)
             self.request.response.addNotification(
-                'The proposal\'s merged revision has been updated.')
+                "The proposal's merged revision has been updated."
+            )
         else:
             self.context.markAsMerged(merge_reporter=self.user, **kwargs)
             self.request.response.addNotification(
-                'The proposal has now been marked as merged.')
+                "The proposal has now been marked as merged."
+            )
 
     def validate(self, data):
         # Ensure a positive integer value.
-        revno = data.get('merged_revno')
+        revno = data.get("merged_revno")
         if revno is not None:
             if revno <= 0:
                 self.setFieldError(
-                    'merged_revno',
-                    'Revision numbers must be positive integers.')
+                    "merged_revno",
+                    "Revision numbers must be positive integers.",
+                )
 
 
 class BranchMergeProposalSubscribersView(LaunchpadView):
@@ -1324,7 +1377,8 @@ class BranchMergeProposalSubscribersView(LaunchpadView):
         # only once, and for the most detailed subscription from the source
         # and target branches.
         self._status_subscribers = (
-            self._status_subscribers - self._full_subscribers)
+            self._status_subscribers - self._full_subscribers
+        )
 
     def _add_subscribers_for_branch(self, branch):
         """Add the subscribers to the subscription sets for the branch."""
@@ -1343,13 +1397,15 @@ class BranchMergeProposalSubscribersView(LaunchpadView):
     def full_subscribers(self):
         """A list of full subscribers ordered by displayname."""
         return sorted(
-            self._full_subscribers, key=operator.attrgetter('displayname'))
+            self._full_subscribers, key=operator.attrgetter("displayname")
+        )
 
     @cachedproperty
     def status_subscribers(self):
         """A list of full subscribers ordered by displayname."""
         return sorted(
-            self._status_subscribers, key=operator.attrgetter('displayname'))
+            self._status_subscribers, key=operator.attrgetter("displayname")
+        )
 
     @property
     def has_subscribers(self):
@@ -1357,8 +1413,9 @@ class BranchMergeProposalSubscribersView(LaunchpadView):
         return len(self.full_subscribers) + len(self.status_subscribers)
 
 
-class BranchMergeProposalChangeStatusView(MergeProposalEditView,
-                                          BranchMergeProposalStatusMixin):
+class BranchMergeProposalChangeStatusView(
+    MergeProposalEditView, BranchMergeProposalStatusMixin
+):
 
     page_title = label = "Change merge proposal status"
     schema = IBranchMergeProposal
@@ -1367,16 +1424,20 @@ class BranchMergeProposalChangeStatusView(MergeProposalEditView,
     def setUpFields(self):
         MergeProposalEditView.setUpFields(self)
         # Add the custom restricted queue status widget.
-        status_field = self.schema['queue_status']
+        status_field = self.schema["queue_status"]
 
         status_choice = Choice(
-                __name__='queue_status', title=status_field.title,
-                required=True, vocabulary=self._createStatusVocabulary())
+            __name__="queue_status",
+            title=status_field.title,
+            required=True,
+            vocabulary=self._createStatusVocabulary(),
+        )
         status_field = form.Fields(
-            status_choice, render_context=self.render_context)
+            status_choice, render_context=self.render_context
+        )
         self.form_fields = status_field + self.form_fields
 
-    @action('Change Status', name='update')
+    @action("Change Status", name="update")
     @notify
     def update_action(self, action, data):
         """Update the status."""
@@ -1384,38 +1445,41 @@ class BranchMergeProposalChangeStatusView(MergeProposalEditView,
         curr_status = self.context.queue_status
         # Assume for now that the queue_status in the data is a valid
         # transition from where we are.
-        rev_id = self.request.form['revno']
-        new_status = data['queue_status']
+        rev_id = self.request.form["revno"]
+        new_status = data["queue_status"]
         # Don't do anything if the user hasn't changed the status.
         if new_status == curr_status:
             return
 
-        assert new_status != BranchMergeProposalStatus.SUPERSEDED, (
-            'Superseded is done via an action, not by setting status.')
+        assert (
+            new_status != BranchMergeProposalStatus.SUPERSEDED
+        ), "Superseded is done via an action, not by setting status."
         self.context.setStatus(new_status, self.user, rev_id)
 
 
 class IAddVote(Interface):
     """Interface for use as a schema for CodeReviewComment forms."""
 
-    vote = copy_field(ICodeReviewComment['vote'], required=True)
+    vote = copy_field(ICodeReviewComment["vote"], required=True)
 
     review_type = copy_field(
-        ICodeReviewVoteReference['review_type'],
-        description='Lowercase keywords describing the type of review you '
-                     'are performing.')
+        ICodeReviewVoteReference["review_type"],
+        description="Lowercase keywords describing the type of review you "
+        "are performing.",
+    )
 
-    comment = Text(title=_('Comment'), required=False)
+    comment = Text(title=_("Comment"), required=False)
 
 
 class BranchMergeProposalAddVoteView(LaunchpadFormView):
     """View for adding a CodeReviewComment."""
 
     schema = IAddVote
-    field_names = ['vote', 'review_type', 'comment']
+    field_names = ["vote", "review_type", "comment"]
 
     custom_widget_comment = CustomWidgetFactory(
-        TextAreaWidget, cssClass='comment-text')
+        TextAreaWidget, cssClass="comment-text"
+    )
 
     @cachedproperty
     def initial_values(self):
@@ -1423,11 +1487,11 @@ class BranchMergeProposalAddVoteView(LaunchpadFormView):
         # Look to see if there is a vote reference already for the user.
         if self.users_vote_ref is None:
             # Look at the request to see if there is something there.
-            review_type = self.request.form.get('review_type', '')
+            review_type = self.request.form.get("review_type", "")
         else:
             review_type = self.users_vote_ref.review_type
         # We'll be positive here and default the vote to approve.
-        return {'vote': CodeReviewVote.APPROVE, 'review_type': review_type}
+        return {"vote": CodeReviewVote.APPROVE, "review_type": review_type}
 
     def initialize(self):
         """Get the users existing vote reference."""
@@ -1441,7 +1505,7 @@ class BranchMergeProposalAddVoteView(LaunchpadFormView):
 
         if self.user is None:
             # Anonymous users are not valid voters.
-            raise AssertionError('Invalid voter')
+            raise AssertionError("Invalid voter")
         super().initialize()
 
     def setUpFields(self):
@@ -1449,26 +1513,28 @@ class BranchMergeProposalAddVoteView(LaunchpadFormView):
         self.reviewer = self.user.name
         # claim_review is set in situations where a user is reviewing on
         # behalf of a team.
-        claim_review = self.request.form.get('claim')
+        claim_review = self.request.form.get("claim")
         if claim_review and self.users_vote_ref is None:
             team = getUtility(IPersonSet).getByName(claim_review)
             if team is not None and self.user.inTeam(team):
                 # If the review type is None, then don't show the field.
                 self.reviewer = team.name
-                if self.initial_values['review_type'] == '':
-                    self.form_fields = self.form_fields.omit('review_type')
+                if self.initial_values["review_type"] == "":
+                    self.form_fields = self.form_fields.omit("review_type")
                 else:
                     # Disable the review_type field
-                    self.form_fields['review_type'].for_display = True
+                    self.form_fields["review_type"].for_display = True
 
     @property
     def label(self):
         """The pagetitle and heading."""
         return "Review merge proposal for %s" % (
-            self.context.merge_source.identity)
+            self.context.merge_source.identity
+        )
+
     page_title = label
 
-    @action('Save Review', name='vote')
+    @action("Save Review", name="vote")
     def vote_action(self, action, data):
         """Create the comment."""
         # Get the review type from the data dict.  If the setUpFields set the
@@ -1476,18 +1542,23 @@ class BranchMergeProposalAddVoteView(LaunchpadFormView):
         # the data dict.  If this is the case, get the review_type from the
         # hidden field that we so cunningly added to the form.
         review_type = data.get(
-            'review_type', self.request.form.get('review_type'))
+            "review_type", self.request.form.get("review_type")
+        )
         # Translate the request parameter back into what our object model
         # needs.
-        if review_type == '':
+        if review_type == "":
             review_type = None
         # Get the reviewer from the hidden input.
-        reviewer_name = self.request.form.get('reviewer')
+        reviewer_name = self.request.form.get("reviewer")
         reviewer = getUtility(IPersonSet).getByName(reviewer_name)
-        if (reviewer.is_team and self.user.inTeam(reviewer) and
-            self.users_vote_ref is None):
+        if (
+            reviewer.is_team
+            and self.user.inTeam(reviewer)
+            and self.users_vote_ref is None
+        ):
             vote_ref = self.context.getUsersVoteReference(
-                reviewer, review_type)
+                reviewer, review_type
+            )
             if vote_ref is not None:
                 # Claim this vote reference, i.e. say that the individual
                 # self. user is doing this review ond behalf of the 'reviewer'
@@ -1495,8 +1566,12 @@ class BranchMergeProposalAddVoteView(LaunchpadFormView):
                 vote_ref.claimReview(self.user)
 
         self.context.createComment(
-            self.user, subject=None, content=data['comment'],
-            vote=data['vote'], review_type=review_type)
+            self.user,
+            subject=None,
+            content=data["comment"],
+            vote=data["vote"],
+            review_type=review_type,
+        )
 
     @property
     def next_url(self):
@@ -1523,6 +1598,5 @@ class PreviewDiffHTMLRepresentation:
 
     def __call__(self):
         """Render `BugBranch` as XHTML using the webservice."""
-        diff_view = getMultiAdapter(
-            (self.diff, self.request), name="+diff")
+        diff_view = getMultiAdapter((self.diff, self.request), name="+diff")
         return diff_view()
diff --git a/lib/lp/code/browser/branchmergeproposallisting.py b/lib/lp/code/browser/branchmergeproposallisting.py
index 5a219da..d98b467 100644
--- a/lib/lp/code/browser/branchmergeproposallisting.py
+++ b/lib/lp/code/browser/branchmergeproposallisting.py
@@ -4,60 +4,52 @@
 """Base class view for branch merge proposal listings."""
 
 __all__ = [
-    'ActiveReviewsView',
-    'BranchActiveReviewsView',
-    'BranchDependentMergesView',
-    'BranchMergeProposalListingItem',
-    'BranchMergeProposalListingView',
-    'PersonActiveReviewsView',
-    'PersonProductActiveReviewsView',
-    ]
+    "ActiveReviewsView",
+    "BranchActiveReviewsView",
+    "BranchDependentMergesView",
+    "BranchMergeProposalListingItem",
+    "BranchMergeProposalListingView",
+    "PersonActiveReviewsView",
+    "PersonProductActiveReviewsView",
+]
 
 from operator import attrgetter
 
 from lazr.delegates import delegate_to
-from lazr.enum import (
-    EnumeratedType,
-    Item,
-    use_template,
-    )
+from lazr.enum import EnumeratedType, Item, use_template
 from zope.component import getUtility
-from zope.interface import (
-    implementer,
-    Interface,
-    )
+from zope.interface import Interface, implementer
 from zope.schema import Choice
 
 from lp import _
 from lp.app.browser.launchpadform import LaunchpadFormView
 from lp.app.interfaces.launchpad import IHeadingContext
 from lp.app.widgets.itemswidgets import LaunchpadDropdownWidget
-from lp.code.enums import (
-    BranchMergeProposalStatus,
-    CodeReviewVote,
-    )
+from lp.code.enums import BranchMergeProposalStatus, CodeReviewVote
 from lp.code.interfaces.branchmergeproposal import (
     BRANCH_MERGE_PROPOSAL_FINAL_STATES,
     IBranchMergeProposal,
     IBranchMergeProposalGetter,
     IBranchMergeProposalListingBatchNavigator,
-    )
+)
 from lp.code.interfaces.hasbranches import IHasMergeProposals
 from lp.services.config import config
-from lp.services.propertycache import (
-    cachedproperty,
-    get_property_cache,
-    )
+from lp.services.propertycache import cachedproperty, get_property_cache
 from lp.services.webapp.authorization import check_permission
 from lp.services.webapp.batching import TableBatchNavigator
 
 
-@delegate_to(IBranchMergeProposal, context='context')
+@delegate_to(IBranchMergeProposal, context="context")
 class BranchMergeProposalListingItem:
     """A branch merge proposal that knows summary values for comments."""
 
-    def __init__(self, branch_merge_proposal, summary, proposal_reviewer,
-                 vote_references=None):
+    def __init__(
+        self,
+        branch_merge_proposal,
+        summary,
+        proposal_reviewer,
+        vote_references=None,
+    ):
         self.context = branch_merge_proposal
         self.summary = summary
         self.proposal_reviewer = proposal_reviewer
@@ -83,9 +75,12 @@ class BranchMergeProposalListingItem:
                 for ref in self.vote_references:
                     if ref.comment is not None and ref.comment.vote == vote:
                         reviewers.append(ref.reviewer.unique_displayname)
-                yield {'name': vote.name, 'title': vote.title,
-                       'count': vote_count,
-                       'reviewers': ', '.join(sorted(reviewers))}
+                yield {
+                    "name": vote.name,
+                    "title": vote.title,
+                    "count": vote_count,
+                    "reviewers": ", ".join(sorted(reviewers)),
+                }
 
     @property
     def vote_type_count(self):
@@ -96,7 +91,7 @@ class BranchMergeProposalListingItem:
     @property
     def comment_count(self):
         """The number of comments (that aren't votes)."""
-        return self.summary['comment_count']
+        return self.summary["comment_count"]
 
     @property
     def has_no_activity(self):
@@ -135,9 +130,11 @@ class BranchMergeProposalListingBatchNavigator(TableBatchNavigator):
 
     def __init__(self, view):
         super().__init__(
-            view.getVisibleProposalsForUser(), view.request,
+            view.getVisibleProposalsForUser(),
+            view.request,
             columns_to_show=view.extra_columns,
-            size=config.launchpad.branchlisting_batch_size)
+            size=config.launchpad.branchlisting_batch_size,
+        )
         self.view = view
 
     @cachedproperty
@@ -149,13 +146,15 @@ class BranchMergeProposalListingBatchNavigator(TableBatchNavigator):
         """A dict of proposals to counts of votes and comments."""
         utility = getUtility(IBranchMergeProposalGetter)
         return utility.getVoteSummariesForProposals(
-            self._proposals_for_current_batch)
+            self._proposals_for_current_batch
+        )
 
     def _createItem(self, proposal):
         """Create the listing item for the proposal."""
         summary = self._vote_summaries[proposal]
-        return BranchMergeProposalListingItem(proposal, summary,
-            proposal_reviewer=self.view.getUserFromContext())
+        return BranchMergeProposalListingItem(
+            proposal, summary, proposal_reviewer=self.view.getUserFromContext()
+        )
 
     @cachedproperty
     def proposals(self):
@@ -173,11 +172,20 @@ class BranchMergeProposalListingBatchNavigator(TableBatchNavigator):
 
 class FilterableStatusValues(EnumeratedType):
     """Selectable values for filtering the merge proposal listings."""
+
     use_template(BranchMergeProposalStatus)
 
     sort_order = (
-        'ALL', 'WORK_IN_PROGRESS', 'NEEDS_REVIEW', 'CODE_APPROVED',
-        'REJECTED', 'MERGED', 'MERGE_FAILED', 'QUEUED', 'SUPERSEDED')
+        "ALL",
+        "WORK_IN_PROGRESS",
+        "NEEDS_REVIEW",
+        "CODE_APPROVED",
+        "REJECTED",
+        "MERGED",
+        "MERGE_FAILED",
+        "QUEUED",
+        "SUPERSEDED",
+    )
 
     ALL = Item("Any status")
 
@@ -187,21 +195,23 @@ class BranchMergeProposalFilterSchema(Interface):
 
     # Stats and status attributes
     status = Choice(
-        title=_('Status'), vocabulary=FilterableStatusValues,
-        default=FilterableStatusValues.ALL,)
+        title=_("Status"),
+        vocabulary=FilterableStatusValues,
+        default=FilterableStatusValues.ALL,
+    )
 
 
 class BranchMergeProposalListingView(LaunchpadFormView):
     """A base class for views of branch merge proposal listings."""
 
     schema = BranchMergeProposalFilterSchema
-    field_names = ['status']
+    field_names = ["status"]
     custom_widget_status = LaunchpadDropdownWidget
 
     extra_columns = []
     _queue_status = None
 
-    page_title = 'Merge proposals'
+    page_title = "Merge proposals"
 
     @property
     def label(self):
@@ -212,12 +222,12 @@ class BranchMergeProposalListingView(LaunchpadFormView):
 
     @property
     def initial_values(self):
-        return {'status': FilterableStatusValues.ALL}
+        return {"status": FilterableStatusValues.ALL}
 
     @cachedproperty
     def status_value(self):
         """The effective value of the status widget."""
-        widget = self.widgets['status']
+        widget = self.widgets["status"]
         if widget.hasValidInput():
             return widget.getInputValue()
         else:
@@ -229,7 +239,7 @@ class BranchMergeProposalListingView(LaunchpadFormView):
         if self.status_value == FilterableStatusValues.ALL:
             return BranchMergeProposalStatus.items
         else:
-            return (BranchMergeProposalStatus.items[self.status_value.name], )
+            return (BranchMergeProposalStatus.items[self.status_value.name],)
 
     @property
     def proposals(self):
@@ -243,7 +253,8 @@ class BranchMergeProposalListingView(LaunchpadFormView):
     def getVisibleProposalsForUser(self):
         """Branch merge proposals that are visible by the logged in user."""
         return IHasMergeProposals(self.context).getMergeProposals(
-            self.status_filter, self.user, eager_load=True)
+            self.status_filter, self.user, eager_load=True
+        )
 
     @cachedproperty
     def proposal_count(self):
@@ -257,13 +268,15 @@ class BranchMergeProposalListingView(LaunchpadFormView):
             return "%s has no merge proposals." % self.context.displayname
         else:
             return "%s has no merge proposals with status: %s" % (
-                self.context.displayname, self.status_value.title)
+                self.context.displayname,
+                self.status_value.title,
+            )
 
 
 class BranchDependentMergesView(BranchMergeProposalListingView):
     """Branch merge proposals that list this branch as a prerequisite."""
 
-    page_title = 'Dependent merge proposals'
+    page_title = "Dependent merge proposals"
 
     @property
     def label(self):
@@ -272,7 +285,8 @@ class BranchDependentMergesView(BranchMergeProposalListingView):
     def getVisibleProposalsForUser(self):
         """See `BranchMergeProposalListingView`."""
         return self.context.getDependentMergeProposals(
-            self.status_filter, self.user, eager_load=True)
+            self.status_filter, self.user, eager_load=True
+        )
 
 
 class ActiveReviewsView(BranchMergeProposalListingView):
@@ -283,21 +297,24 @@ class ActiveReviewsView(BranchMergeProposalListingView):
     show_diffs = False
 
     # The grouping classifications.
-    APPROVED = 'approved'
-    TO_DO = 'to_do'
-    ARE_DOING = 'are_doing'
-    CAN_DO = 'can_do'
-    MINE = 'mine'
-    OTHER = 'other'
-    WIP = 'wip'
+    APPROVED = "approved"
+    TO_DO = "to_do"
+    ARE_DOING = "are_doing"
+    CAN_DO = "can_do"
+    MINE = "mine"
+    OTHER = "other"
+    WIP = "wip"
 
     def getProposals(self):
         """Get the proposals for the view."""
         return self.context.getMergeProposals(
             status=(
                 BranchMergeProposalStatus.CODE_APPROVED,
-                BranchMergeProposalStatus.NEEDS_REVIEW),
-            visible_by_user=self.user, eager_load=True)
+                BranchMergeProposalStatus.NEEDS_REVIEW,
+            ),
+            visible_by_user=self.user,
+            eager_load=True,
+        )
 
     def _getReviewGroup(self, proposal, votes, reviewer):
         """One of APPROVED, MINE, TO_DO, CAN_DO, ARE_DOING, OTHER or WIP.
@@ -328,10 +345,13 @@ class ActiveReviewsView(BranchMergeProposalListingView):
         if proposal.queue_status == bmp_status.WORK_IN_PROGRESS:
             return self.WIP
 
-        if (reviewer is not None and
-            (proposal.merge_source.owner == reviewer or
-             (reviewer.inTeam(proposal.merge_source.owner) and
-              proposal.registrant == reviewer))):
+        if reviewer is not None and (
+            proposal.merge_source.owner == reviewer
+            or (
+                reviewer.inTeam(proposal.merge_source.owner)
+                and proposal.registrant == reviewer
+            )
+        ):
             return self.MINE
 
         result = self.OTHER
@@ -367,15 +387,18 @@ class ActiveReviewsView(BranchMergeProposalListingView):
         for proposal in proposals:
             proposal_votes = all_votes[proposal]
             review_group = self._getReviewGroup(
-                proposal, proposal_votes, reviewer)
+                proposal, proposal_votes, reviewer
+            )
             self.review_groups.setdefault(review_group, []).append(
                 BranchMergeProposalListingItem(
-                    proposal, vote_summaries[proposal], None, proposal_votes))
+                    proposal, vote_summaries[proposal], None, proposal_votes
+                )
+            )
             if proposal.preview_diff is not None:
                 self.show_diffs = True
         # Sort each collection...
         for group in self.review_groups.values():
-            group.sort(key=attrgetter('sort_key'))
+            group.sort(key=attrgetter("sort_key"))
         get_property_cache(self).proposal_count = len(proposals)
 
     @cachedproperty
@@ -383,17 +406,18 @@ class ActiveReviewsView(BranchMergeProposalListingView):
         """Return a dict of headings for the groups."""
         reviewer = self._getReviewer()
         headings = {
-            self.APPROVED: 'Approved reviews ready to land',
-            self.TO_DO: 'Reviews you have to do',
-            self.ARE_DOING: 'Reviews you are doing',
-            self.CAN_DO: 'Requested reviews you can do',
-            self.MINE: 'Reviews you are waiting on',
-            self.OTHER: 'Other reviews you are not actively reviewing',
-            self.WIP: 'Work in progress'}
+            self.APPROVED: "Approved reviews ready to land",
+            self.TO_DO: "Reviews you have to do",
+            self.ARE_DOING: "Reviews you are doing",
+            self.CAN_DO: "Requested reviews you can do",
+            self.MINE: "Reviews you are waiting on",
+            self.OTHER: "Other reviews you are not actively reviewing",
+            self.WIP: "Work in progress",
+        }
         if reviewer is None:
             # If there is no reviewer, then there will be no TO_DO, ARE_DOING,
             # CAN_DO or MINE, and we are not in a person context.
-            headings[self.OTHER] = 'Reviews requested or in progress'
+            headings[self.OTHER] = "Reviews requested or in progress"
         elif self.user is not None and self.user.inTeam(reviewer):
             # The user is either looking at their own person review page, or a
             # reviews for a team that they are a member of.  The default
@@ -402,18 +426,20 @@ class ActiveReviewsView(BranchMergeProposalListingView):
         elif reviewer.is_team:
             # Looking at a person team page.
             name = reviewer.displayname
-            headings[self.CAN_DO] = 'Reviews %s can do' % name
+            headings[self.CAN_DO] = "Reviews %s can do" % name
             headings[self.OTHER] = (
-                'Reviews %s is not actively reviewing' % name)
+                "Reviews %s is not actively reviewing" % name
+            )
         else:
             # A user is looking at someone elses personal review page.
             name = reviewer.displayname
-            headings[self.TO_DO] = 'Reviews %s has to do' % name
-            headings[self.ARE_DOING] = 'Reviews %s is doing' % name
-            headings[self.CAN_DO] = 'Reviews %s can do' % name
-            headings[self.MINE] = 'Reviews %s is waiting on' % name
+            headings[self.TO_DO] = "Reviews %s has to do" % name
+            headings[self.ARE_DOING] = "Reviews %s is doing" % name
+            headings[self.CAN_DO] = "Reviews %s can do" % name
+            headings[self.MINE] = "Reviews %s is waiting on" % name
             headings[self.OTHER] = (
-                'Reviews %s is not actively reviewing' % name)
+                "Reviews %s is not actively reviewing" % name
+            )
         return headings
 
     @property
@@ -428,12 +454,17 @@ class BranchActiveReviewsView(ActiveReviewsView):
     def getProposals(self):
         """See `ActiveReviewsView`."""
         non_final = tuple(
-            set(BranchMergeProposalStatus.items) -
-            set(BRANCH_MERGE_PROPOSAL_FINAL_STATES))
+            set(BranchMergeProposalStatus.items)
+            - set(BRANCH_MERGE_PROPOSAL_FINAL_STATES)
+        )
         candidates = self.context.getMergeProposals(
-            status=non_final, eager_load=True, visible_by_user=self.user)
-        return [proposal for proposal in candidates
-                if check_permission('launchpad.View', proposal)]
+            status=non_final, eager_load=True, visible_by_user=self.user
+        )
+        return [
+            proposal
+            for proposal in candidates
+            if check_permission("launchpad.View", proposal)
+        ]
 
 
 class PersonActiveReviewsView(ActiveReviewsView):
@@ -447,8 +478,12 @@ class PersonActiveReviewsView(ActiveReviewsView):
         return self._getReviewer().getOwnedAndRequestedReviews(
             status=(
                 BranchMergeProposalStatus.CODE_APPROVED,
-                BranchMergeProposalStatus.NEEDS_REVIEW),
-            visible_by_user=self.user, project=project, eager_load=True)
+                BranchMergeProposalStatus.NEEDS_REVIEW,
+            ),
+            visible_by_user=self.user,
+            project=project,
+            eager_load=True,
+        )
 
 
 class PersonProductActiveReviewsView(PersonActiveReviewsView):
@@ -456,8 +491,10 @@ class PersonProductActiveReviewsView(PersonActiveReviewsView):
 
     @property
     def label(self):
-        return '%s for %s' % (
-            self.page_title, self.context.product.displayname)
+        return "%s for %s" % (
+            self.page_title,
+            self.context.product.displayname,
+        )
 
     def _getReviewer(self):
         return self.context.person
@@ -469,4 +506,6 @@ class PersonProductActiveReviewsView(PersonActiveReviewsView):
     def no_proposal_message(self):
         """Shown when there is no table to show."""
         return "%s has no active code reviews for %s." % (
-            self.context.person.displayname, self.context.product.displayname)
+            self.context.person.displayname,
+            self.context.product.displayname,
+        )
diff --git a/lib/lp/code/browser/branchref.py b/lib/lp/code/browser/branchref.py
index f26fba8..5d440ca 100644
--- a/lib/lp/code/browser/branchref.py
+++ b/lib/lp/code/browser/branchref.py
@@ -3,25 +3,18 @@
 
 """Browser code used to implement virtual '.bzr' directories."""
 
-__all__ = [
-    'BranchRef'
-    ]
+__all__ = ["BranchRef"]
 
 from zope.interface import implementer
 from zope.publisher.interfaces.browser import IBrowserPublisher
 
 from lp.code.interfaces.branchref import IBranchRef
 from lp.services.config import config
-from lp.services.webapp import (
-    Navigation,
-    stepthrough,
-    stepto,
-    )
+from lp.services.webapp import Navigation, stepthrough, stepto
 
 
 @implementer(IBranchRef)
 class BranchRef:
-
     def __init__(self, branch):
         self.branch = branch
 
@@ -35,28 +28,30 @@ class BranchRef:
 # Synthesising a branch reference provides the desired behaviour with
 # current Bazaar releases, however.
 
+
 class BranchRefNavigation(Navigation):
 
     usedfor = IBranchRef
 
-    @stepto('branch-format')
+    @stepto("branch-format")
     def branch_format(self):
-        return StaticContentView('Bazaar-NG meta directory, format 1\n')
+        return StaticContentView("Bazaar-NG meta directory, format 1\n")
 
-    @stepthrough('branch')
+    @stepthrough("branch")
     def traverse_branch(self, name):
-        if name == 'format':
-            return StaticContentView('Bazaar-NG Branch Reference Format 1\n')
-        elif name == 'location':
-            return StaticContentView(config.codehosting.supermirror_root +
-                                     self.context.branch.unique_name)
+        if name == "format":
+            return StaticContentView("Bazaar-NG Branch Reference Format 1\n")
+        elif name == "location":
+            return StaticContentView(
+                config.codehosting.supermirror_root
+                + self.context.branch.unique_name
+            )
         else:
             return None
 
 
 @implementer(IBrowserPublisher)
 class StaticContentView:
-
     def __init__(self, contents):
         self.contents = contents
 
diff --git a/lib/lp/code/browser/branchsubscription.py b/lib/lp/code/browser/branchsubscription.py
index b90a040..2881b03 100644
--- a/lib/lp/code/browser/branchsubscription.py
+++ b/lib/lp/code/browser/branchsubscription.py
@@ -2,32 +2,29 @@
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 __all__ = [
-    'BranchPortletSubscribersContent',
-    'BranchSubscriptionAddOtherView',
-    'BranchSubscriptionAddView',
-    'BranchSubscriptionEditOwnView',
-    'BranchSubscriptionEditView',
-    ]
+    "BranchPortletSubscribersContent",
+    "BranchSubscriptionAddOtherView",
+    "BranchSubscriptionAddView",
+    "BranchSubscriptionEditOwnView",
+    "BranchSubscriptionEditView",
+]
 
 from zope.component import getUtility
 
 from lp.app.browser.launchpadform import (
-    action,
     LaunchpadEditFormView,
     LaunchpadFormView,
-    )
+    action,
+)
 from lp.app.interfaces.services import IService
 from lp.code.enums import BranchSubscriptionNotificationLevel
 from lp.code.interfaces.branchsubscription import IBranchSubscription
 from lp.registry.interfaces.person import IPersonSet
-from lp.services.webapp import (
-    canonical_url,
-    LaunchpadView,
-    )
+from lp.services.webapp import LaunchpadView, canonical_url
 from lp.services.webapp.authorization import (
     check_permission,
     precache_permission_for_objects,
-    )
+)
 from lp.services.webapp.escaping import structured
 
 
@@ -42,20 +39,28 @@ class BranchPortletSubscribersContent(LaunchpadView):
         # the expense of running several complex SQL queries.
         subscriptions = list(self.context.subscriptions)
         person_ids = [sub.person_id for sub in subscriptions]
-        list(getUtility(IPersonSet).getPrecachedPersonsFromIDs(
-            person_ids, need_validity=True))
+        list(
+            getUtility(IPersonSet).getPrecachedPersonsFromIDs(
+                person_ids, need_validity=True
+            )
+        )
         if self.user is not None:
             subscribers = [
-                subscription.person for subscription in subscriptions]
+                subscription.person for subscription in subscriptions
+            ]
             precache_permission_for_objects(
-                self.request, "launchpad.LimitedView", subscribers)
+                self.request, "launchpad.LimitedView", subscribers
+            )
 
         visible_subscriptions = [
-            subscription for subscription in subscriptions
-            if check_permission('launchpad.LimitedView', subscription.person)]
+            subscription
+            for subscription in subscriptions
+            if check_permission("launchpad.LimitedView", subscription.person)
+        ]
         return sorted(
             visible_subscriptions,
-            key=lambda subscription: subscription.person.displayname)
+            key=lambda subscription: subscription.person.displayname,
+        )
 
 
 class _BranchSubscriptionView(LaunchpadFormView):
@@ -63,11 +68,12 @@ class _BranchSubscriptionView(LaunchpadFormView):
     """Contains the common functionality of the Add and Edit views."""
 
     schema = IBranchSubscription
-    field_names = ['notification_level', 'max_diff_lines', 'review_level']
+    field_names = ["notification_level", "max_diff_lines", "review_level"]
 
     LEVELS_REQUIRING_LINES_SPECIFICATION = (
         BranchSubscriptionNotificationLevel.DIFFSONLY,
-        BranchSubscriptionNotificationLevel.FULL)
+        BranchSubscriptionNotificationLevel.FULL,
+    )
 
     @property
     def user_is_subscribed(self):
@@ -82,17 +88,21 @@ class _BranchSubscriptionView(LaunchpadFormView):
 
     cancel_url = next_url
 
-    def add_notification_message(self, initial, notification_level,
-                                 max_diff_lines, review_level):
+    def add_notification_message(
+        self, initial, notification_level, max_diff_lines, review_level
+    ):
         if notification_level in self.LEVELS_REQUIRING_LINES_SPECIFICATION:
-            lines_message = '<li>%s</li>' % max_diff_lines.description
+            lines_message = "<li>%s</li>" % max_diff_lines.description
         else:
-            lines_message = ''
+            lines_message = ""
 
-        format_str = '%%s<ul><li>%%s</li>%s<li>%%s</li></ul>' % lines_message
+        format_str = "%%s<ul><li>%%s</li>%s<li>%%s</li></ul>" % lines_message
         message = structured(
-            format_str, initial, notification_level.description,
-            review_level.description)
+            format_str,
+            initial,
+            notification_level.description,
+            review_level.description,
+        )
         self.request.response.addNotification(message)
 
     def optional_max_diff_lines(self, notification_level, max_diff_lines):
@@ -112,31 +122,39 @@ class BranchSubscriptionAddView(_BranchSubscriptionView):
         # subscribed before continuing.
         if self.context.hasSubscription(self.user):
             self.request.response.addNotification(
-                'You are already subscribed to this branch.')
+                "You are already subscribed to this branch."
+            )
         else:
-            notification_level = data['notification_level']
+            notification_level = data["notification_level"]
             max_diff_lines = self.optional_max_diff_lines(
-                notification_level, data['max_diff_lines'])
-            review_level = data['review_level']
+                notification_level, data["max_diff_lines"]
+            )
+            review_level = data["review_level"]
 
             self.context.subscribe(
-                self.user, notification_level, max_diff_lines, review_level,
-                self.user)
+                self.user,
+                notification_level,
+                max_diff_lines,
+                review_level,
+                self.user,
+            )
 
             self.add_notification_message(
-                'You have subscribed to this branch with: ',
-                notification_level, max_diff_lines, review_level)
+                "You have subscribed to this branch with: ",
+                notification_level,
+                max_diff_lines,
+                review_level,
+            )
 
 
 class BranchSubscriptionEditOwnView(_BranchSubscriptionView):
-
     @property
     def label(self):
         return "Edit subscription to branch"
 
     @property
     def page_title(self):
-        return 'Edit subscription to branch %s' % self.context.displayname
+        return "Edit subscription to branch %s" % self.context.displayname
 
     @property
     def initial_values(self):
@@ -145,29 +163,33 @@ class BranchSubscriptionEditOwnView(_BranchSubscriptionView):
             # This is the case of URL hacking or stale page.
             return {}
         else:
-            return {'notification_level': subscription.notification_level,
-                    'max_diff_lines': subscription.max_diff_lines,
-                    'review_level': subscription.review_level}
+            return {
+                "notification_level": subscription.notification_level,
+                "max_diff_lines": subscription.max_diff_lines,
+                "review_level": subscription.review_level,
+            }
 
     @action("Change")
     def change_details(self, action, data):
         # Be proactive in the checking to catch the stale post problem.
         if self.context.hasSubscription(self.user):
             subscription = self.context.getSubscription(self.user)
-            subscription.notification_level = data['notification_level']
+            subscription.notification_level = data["notification_level"]
             subscription.max_diff_lines = self.optional_max_diff_lines(
-                subscription.notification_level,
-                data['max_diff_lines'])
-            subscription.review_level = data['review_level']
+                subscription.notification_level, data["max_diff_lines"]
+            )
+            subscription.review_level = data["review_level"]
 
             self.add_notification_message(
-                'Subscription updated to: ',
+                "Subscription updated to: ",
                 subscription.notification_level,
                 subscription.max_diff_lines,
-                subscription.review_level)
+                subscription.review_level,
+            )
         else:
             self.request.response.addNotification(
-                'You are not subscribed to this branch.')
+                "You are not subscribed to this branch."
+            )
 
     @action("Unsubscribe")
     def unsubscribe(self, action, data):
@@ -175,17 +197,23 @@ class BranchSubscriptionEditOwnView(_BranchSubscriptionView):
         if self.context.hasSubscription(self.user):
             self.context.unsubscribe(self.user, self.user)
             self.request.response.addNotification(
-                "You have unsubscribed from this branch.")
+                "You have unsubscribed from this branch."
+            )
         else:
             self.request.response.addNotification(
-                'You are not subscribed to this branch.')
+                "You are not subscribed to this branch."
+            )
 
 
 class BranchSubscriptionAddOtherView(_BranchSubscriptionView):
     """View used to subscribe someone other than the current user."""
 
     field_names = [
-        'person', 'notification_level', 'max_diff_lines', 'review_level']
+        "person",
+        "notification_level",
+        "max_diff_lines",
+        "review_level",
+    ]
     for_input = True
 
     # Since we are subscribing other people, the current user
@@ -195,37 +223,51 @@ class BranchSubscriptionAddOtherView(_BranchSubscriptionView):
     page_title = label = "Subscribe to branch"
 
     def validate(self, data):
-        if 'person' in data:
-            person = data['person']
+        if "person" in data:
+            person = data["person"]
             subscription = self.context.getSubscription(person)
             if subscription is None and not self.context.userCanBeSubscribed(
-                person):
-                self.setFieldError('person', "Open and delegated teams "
-                "cannot be subscribed to private branches.")
+                person
+            ):
+                self.setFieldError(
+                    "person",
+                    "Open and delegated teams "
+                    "cannot be subscribed to private branches.",
+                )
 
     @action("Subscribe", name="subscribe_action")
     def subscribe_action(self, action, data):
         """Subscribe the specified user to the branch."""
-        notification_level = data['notification_level']
+        notification_level = data["notification_level"]
         max_diff_lines = self.optional_max_diff_lines(
-            notification_level, data['max_diff_lines'])
-        review_level = data['review_level']
-        person = data['person']
+            notification_level, data["max_diff_lines"]
+        )
+        review_level = data["review_level"]
+        person = data["person"]
         subscription = self.context.getSubscription(person)
         if subscription is None:
             self.context.subscribe(
-                person, notification_level, max_diff_lines, review_level,
-                self.user)
+                person,
+                notification_level,
+                max_diff_lines,
+                review_level,
+                self.user,
+            )
             self.add_notification_message(
-                '%s has been subscribed to this branch with: '
-                % person.displayname, notification_level, max_diff_lines,
-                review_level)
+                "%s has been subscribed to this branch with: "
+                % person.displayname,
+                notification_level,
+                max_diff_lines,
+                review_level,
+            )
         else:
             self.add_notification_message(
-                '%s was already subscribed to this branch with: '
+                "%s was already subscribed to this branch with: "
                 % person.displayname,
-                subscription.notification_level, subscription.max_diff_lines,
-                review_level)
+                subscription.notification_level,
+                subscription.max_diff_lines,
+                review_level,
+            )
 
 
 class BranchSubscriptionEditView(LaunchpadEditFormView):
@@ -235,12 +277,13 @@ class BranchSubscriptionEditView(LaunchpadEditFormView):
     through the branch action item to edit the user's own subscription.
     This is the only current way to edit a team branch subscription.
     """
+
     schema = IBranchSubscription
-    field_names = ['notification_level', 'max_diff_lines', 'review_level']
+    field_names = ["notification_level", "max_diff_lines", "review_level"]
 
     @property
     def page_title(self):
-        return 'Edit subscription to branch %s' % self.branch.displayname
+        return "Edit subscription to branch %s" % self.branch.displayname
 
     @property
     def label(self):
@@ -262,16 +305,17 @@ class BranchSubscriptionEditView(LaunchpadEditFormView):
         self.branch.unsubscribe(self.person, self.user)
         self.request.response.addNotification(
             "%s has been unsubscribed from this branch."
-            % self.person.displayname)
+            % self.person.displayname
+        )
 
     @property
     def next_url(self):
         url = canonical_url(self.branch)
         # If the subscriber can no longer see the branch, redirect them away.
-        service = getUtility(IService, 'sharing')
+        service = getUtility(IService, "sharing")
         branches = service.getVisibleArtifacts(
-            self.person, branches=[self.branch],
-            ignore_permissions=True)["branches"]
+            self.person, branches=[self.branch], ignore_permissions=True
+        )["branches"]
         if not branches:
             url = canonical_url(self.branch.target)
         return url
diff --git a/lib/lp/code/browser/cibuild.py b/lib/lp/code/browser/cibuild.py
index e03d7b7..187f5ef 100644
--- a/lib/lp/code/browser/cibuild.py
+++ b/lib/lp/code/browser/cibuild.py
@@ -7,23 +7,20 @@ __all__ = [
     "CIBuildContextMenu",
     "CIBuildNavigation",
     "CIBuildView",
-    ]
+]
 
 from zope.interface import Interface
 
-from lp.app.browser.launchpadform import (
-    action,
-    LaunchpadFormView,
-    )
+from lp.app.browser.launchpadform import LaunchpadFormView, action
 from lp.code.interfaces.cibuild import ICIBuild
 from lp.services.librarian.browser import FileNavigationMixin
 from lp.services.webapp import (
-    canonical_url,
     ContextMenu,
-    enabled_with_permission,
     Link,
     Navigation,
-    )
+    canonical_url,
+    enabled_with_permission,
+)
 from lp.soyuz.interfaces.binarypackagebuild import IBuildRescoreForm
 
 
@@ -43,20 +40,29 @@ class CIBuildContextMenu(ContextMenu):
     @enabled_with_permission("launchpad.Edit")
     def retry(self):
         return Link(
-            "+retry", "Retry this build", icon="retry",
-            enabled=self.context.can_be_retried)
+            "+retry",
+            "Retry this build",
+            icon="retry",
+            enabled=self.context.can_be_retried,
+        )
 
     @enabled_with_permission("launchpad.Edit")
     def cancel(self):
         return Link(
-            "+cancel", "Cancel build", icon="remove",
-            enabled=self.context.can_be_cancelled)
+            "+cancel",
+            "Cancel build",
+            icon="remove",
+            enabled=self.context.can_be_cancelled,
+        )
 
     @enabled_with_permission("launchpad.Admin")
     def rescore(self):
         return Link(
-            "+rescore", "Rescore build", icon="edit",
-            enabled=self.context.can_be_rescored)
+            "+rescore",
+            "Rescore build",
+            icon="edit",
+            enabled=self.context.can_be_rescored,
+        )
 
 
 class CIBuildView(LaunchpadFormView):
@@ -83,6 +89,7 @@ class CIBuildRetryView(LaunchpadFormView):
     @property
     def cancel_url(self):
         return canonical_url(self.context)
+
     next_url = cancel_url
 
     @action("Retry build", name="retry")
@@ -90,7 +97,8 @@ class CIBuildRetryView(LaunchpadFormView):
         """Retry the build."""
         if not self.context.can_be_retried:
             self.request.response.addErrorNotification(
-                "Build cannot be retried")
+                "Build cannot be retried"
+            )
         else:
             self.context.retry()
             self.request.response.addInfoNotification("Build has been queued")
@@ -109,6 +117,7 @@ class CIBuildCancelView(LaunchpadFormView):
     @property
     def cancel_url(self):
         return canonical_url(self.context)
+
     next_url = cancel_url
 
     @action("Cancel build", name="cancel")
@@ -128,12 +137,14 @@ class CIBuildRescoreView(LaunchpadFormView):
         if self.context.can_be_rescored:
             return super().__call__()
         self.request.response.addWarningNotification(
-            "Cannot rescore this build because it is not queued.")
+            "Cannot rescore this build because it is not queued."
+        )
         self.request.response.redirect(canonical_url(self.context))
 
     @property
     def cancel_url(self):
         return canonical_url(self.context)
+
     next_url = cancel_url
 
     @action("Rescore build", name="rescore")
diff --git a/lib/lp/code/browser/codeimport.py b/lib/lp/code/browser/codeimport.py
index 33a2cd4..7856269 100644
--- a/lib/lp/code/browser/codeimport.py
+++ b/lib/lp/code/browser/codeimport.py
@@ -4,30 +4,24 @@
 """Browser views for CodeImports."""
 
 __all__ = [
-    'CodeImportEditView',
-    'CodeImportMachineView',
-    'CodeImportNameValidationMixin',
-    'CodeImportNewView',
-    'CodeImportSetBreadcrumb',
-    'CodeImportSetNavigation',
-    'CodeImportSetView',
-    'CodeImportTargetMixin',
-    'RequestImportView',
-    'TryImportAgainView',
-    'validate_import_url',
-    ]
+    "CodeImportEditView",
+    "CodeImportMachineView",
+    "CodeImportNameValidationMixin",
+    "CodeImportNewView",
+    "CodeImportSetBreadcrumb",
+    "CodeImportSetNavigation",
+    "CodeImportSetView",
+    "CodeImportTargetMixin",
+    "RequestImportView",
+    "TryImportAgainView",
+    "validate_import_url",
+]
 
 from textwrap import dedent
 from urllib.parse import urlparse
 
-from lazr.restful.interface import (
-    copy_field,
-    use_template,
-    )
-from zope.component import (
-    getUtility,
-    queryAdapter,
-    )
+from lazr.restful.interface import copy_field, use_template
+from zope.component import getUtility, queryAdapter
 from zope.formlib import form
 from zope.formlib.interfaces import IInputWidget
 from zope.formlib.utility import setUpWidget
@@ -38,63 +32,46 @@ from zope.security.interfaces import Unauthorized
 from zope.traversing.interfaces import IPathAdapter
 
 from lp import _
-from lp.app.browser.launchpadform import (
-    action,
-    LaunchpadFormView,
-    )
+from lp.app.browser.launchpadform import LaunchpadFormView, action
 from lp.app.errors import NotFoundError
 from lp.app.widgets.itemswidgets import (
     LaunchpadDropdownWidget,
     LaunchpadRadioWidget,
-    )
-from lp.app.widgets.textwidgets import (
-    StrippedTextWidget,
-    URIWidget,
-    )
+)
+from lp.app.widgets.textwidgets import StrippedTextWidget, URIWidget
 from lp.code.enums import (
+    NON_CVS_RCS_TYPES,
     BranchSubscriptionDiffSize,
     BranchSubscriptionNotificationLevel,
     CodeImportResultStatus,
     CodeImportReviewStatus,
     CodeReviewNotificationLevel,
-    NON_CVS_RCS_TYPES,
     RevisionControlSystems,
     TargetRevisionControlSystems,
-    )
+)
 from lp.code.errors import (
     BranchExists,
     CodeImportAlreadyRequested,
     CodeImportAlreadyRunning,
     CodeImportNotInReviewedState,
     GitRepositoryExists,
-    )
-from lp.code.interfaces.branch import (
-    IBranch,
-    user_has_special_branch_access,
-    )
+)
+from lp.code.interfaces.branch import IBranch, user_has_special_branch_access
 from lp.code.interfaces.branchnamespace import (
-    get_branch_namespace,
     IBranchNamespacePolicy,
-    )
-from lp.code.interfaces.codeimport import (
-    ICodeImport,
-    ICodeImportSet,
-    )
+    get_branch_namespace,
+)
+from lp.code.interfaces.codeimport import ICodeImport, ICodeImportSet
 from lp.code.interfaces.codeimportmachine import ICodeImportMachineSet
 from lp.code.interfaces.gitnamespace import (
-    get_git_namespace,
     IGitNamespacePolicy,
-    )
+    get_git_namespace,
+)
 from lp.registry.interfaces.product import IProduct
 from lp.services.beautifulsoup import BeautifulSoup
 from lp.services.fields import URIField
 from lp.services.propertycache import cachedproperty
-from lp.services.webapp import (
-    canonical_url,
-    LaunchpadView,
-    Navigation,
-    stepto,
-    )
+from lp.services.webapp import LaunchpadView, Navigation, canonical_url, stepto
 from lp.services.webapp.authorization import check_permission
 from lp.services.webapp.batching import BatchNavigator
 from lp.services.webapp.breadcrumb import Breadcrumb
@@ -103,16 +80,18 @@ from lp.services.webapp.escaping import structured
 
 class CodeImportSetNavigation(Navigation):
     """Navigation methods for IBuilder."""
+
     usedfor = ICodeImportSet
 
-    @stepto('+machines')
+    @stepto("+machines")
     def bugs(self):
         return getUtility(ICodeImportMachineSet)
 
 
 class CodeImportSetBreadcrumb(Breadcrumb):
     """Builds a breadcrumb for an `ICodeImportSet`."""
-    text = 'Code Import System'
+
+    text = "Code Import System"
 
 
 class DropdownWidgetWithAny(LaunchpadDropdownWidget):
@@ -122,7 +101,8 @@ class DropdownWidgetWithAny(LaunchpadDropdownWidget):
     associated value is None or not supplied, which is not what we want on
     this page.
     """
-    _messageNoValue = _('Any')
+
+    _messageNoValue = _("Any")
 
 
 class CodeImportSetView(LaunchpadView):
@@ -131,26 +111,31 @@ class CodeImportSetView(LaunchpadView):
     We present the CodeImportSet as a list of all imports.
     """
 
-    page_title = 'Code Imports'
+    page_title = "Code Imports"
 
     def initialize(self):
         """See `LaunchpadView.initialize`."""
         review_status_field = copy_field(
-            ICodeImport['review_status'], required=False, default=None)
+            ICodeImport["review_status"], required=False, default=None
+        )
         self.review_status_widget = CustomWidgetFactory(DropdownWidgetWithAny)
-        setUpWidget(self, 'review_status', review_status_field, IInputWidget)
+        setUpWidget(self, "review_status", review_status_field, IInputWidget)
 
         rcs_type_field = copy_field(
-            ICodeImport['rcs_type'], required=False, default=None)
+            ICodeImport["rcs_type"], required=False, default=None
+        )
         self.rcs_type_widget = CustomWidgetFactory(DropdownWidgetWithAny)
-        setUpWidget(self, 'rcs_type', rcs_type_field, IInputWidget)
+        setUpWidget(self, "rcs_type", rcs_type_field, IInputWidget)
 
         target_rcs_type_field = copy_field(
-            ICodeImport['target_rcs_type'], required=False, default=None)
+            ICodeImport["target_rcs_type"], required=False, default=None
+        )
         self.target_rcs_type_widget = CustomWidgetFactory(
-            DropdownWidgetWithAny)
+            DropdownWidgetWithAny
+        )
         setUpWidget(
-            self, 'target_rcs_type', target_rcs_type_field, IInputWidget)
+            self, "target_rcs_type", target_rcs_type_field, IInputWidget
+        )
 
         # status should be None if either (a) there were no query arguments
         # supplied, i.e. the user browsed directly to this page (this is when
@@ -169,8 +154,10 @@ class CodeImportSetView(LaunchpadView):
             target_rcs_type = self.target_rcs_type_widget.getInputValue()
 
         imports = self.context.search(
-            review_status=review_status, rcs_type=rcs_type,
-            target_rcs_type=target_rcs_type)
+            review_status=review_status,
+            rcs_type=rcs_type,
+            target_rcs_type=target_rcs_type,
+        )
 
         self.batchnav = BatchNavigator(imports, self.request)
 
@@ -181,9 +168,11 @@ class CodeImportBaseView(LaunchpadFormView):
     schema = ICodeImport
 
     custom_widget_cvs_root = CustomWidgetFactory(
-        StrippedTextWidget, displayWidth=50)
+        StrippedTextWidget, displayWidth=50
+    )
     custom_widget_cvs_module = CustomWidgetFactory(
-        StrippedTextWidget, displayWidth=20)
+        StrippedTextWidget, displayWidth=20
+    )
     custom_widget_url = CustomWidgetFactory(URIWidget, displayWidth=50)
 
     @cachedproperty
@@ -211,33 +200,43 @@ class CodeImportBaseView(LaunchpadFormView):
         """If the user has specified cvs, then we need to make
         sure that there isn't already an import with those values."""
         if cvs_root is None:
-            self.setSecondaryFieldError(
-                'cvs_root', 'Enter a CVS root.')
+            self.setSecondaryFieldError("cvs_root", "Enter a CVS root.")
         if cvs_module is None:
-            self.setSecondaryFieldError(
-                'cvs_module', 'Enter a CVS module.')
+            self.setSecondaryFieldError("cvs_module", "Enter a CVS module.")
 
         if cvs_root and cvs_module:
             code_import = getUtility(ICodeImportSet).getByCVSDetails(
-                cvs_root, cvs_module)
-            if (code_import is not None and
-                code_import != existing_import):
-                self.addError(structured("""
+                cvs_root, cvs_module
+            )
+            if code_import is not None and code_import != existing_import:
+                self.addError(
+                    structured(
+                        """
                     Those CVS details are already specified for
                     the imported branch <a href="%s">%s</a>.""",
-                    canonical_url(code_import.target),
-                    code_import.target.unique_name))
-
-    def _validateURL(self, url, rcs_type, target_rcs_type,
-                     existing_import=None, field_name='url'):
+                        canonical_url(code_import.target),
+                        code_import.target.unique_name,
+                    )
+                )
+
+    def _validateURL(
+        self,
+        url,
+        rcs_type,
+        target_rcs_type,
+        existing_import=None,
+        field_name="url",
+    ):
         """If the user has specified a url, we need to make sure that there
         isn't already an import with that url."""
         if url is None:
             self.setSecondaryFieldError(
-                field_name, 'Enter the URL of a foreign VCS branch.')
+                field_name, "Enter the URL of a foreign VCS branch."
+            )
         else:
             reason = validate_import_url(
-                url, rcs_type, target_rcs_type, existing_import)
+                url, rcs_type, target_rcs_type, existing_import
+            )
             if reason:
                 self.setFieldError(field_name, reason)
 
@@ -248,81 +247,98 @@ class CodeImportNameValidationMixin:
     def _setBranchExists(self, existing_branch, field_name):
         self.setFieldError(
             field_name,
-            structured(dedent("""
+            structured(
+                dedent(
+                    """
             There is already an existing import for
             <a href="%(product_url)s">%(product_name)s</a>
             with the name of
-            <a href="%(branch_url)s">%(branch_name)s</a>."""),
-                       product_url=canonical_url(existing_branch.target),
-                       product_name=existing_branch.target.name,
-                       branch_url=canonical_url(existing_branch),
-                       branch_name=existing_branch.name))
+            <a href="%(branch_url)s">%(branch_name)s</a>."""
+                ),
+                product_url=canonical_url(existing_branch.target),
+                product_name=existing_branch.target.name,
+                branch_url=canonical_url(existing_branch),
+                branch_name=existing_branch.name,
+            ),
+        )
 
 
 class NewCodeImportForm(Interface):
     """The fields presented on the form for editing a code import."""
 
-    use_template(IBranch, ['owner'])
-    use_template(ICodeImport, ['rcs_type', 'cvs_root', 'cvs_module'])
+    use_template(IBranch, ["owner"])
+    use_template(ICodeImport, ["rcs_type", "cvs_root", "cvs_module"])
 
     svn_branch_url = URIField(
-        title=_("Branch URL"), required=False,
+        title=_("Branch URL"),
+        required=False,
         description=_(
             "The URL of a Subversion branch, starting with svn:// or "
             "http(s)://.   You can include a username and password as part "
-            "of the url, but this will be displayed on the branch page."),
+            "of the url, but this will be displayed on the branch page."
+        ),
         allowed_schemes=["http", "https", "svn"],
         allow_userinfo=True,
         allow_port=True,
         allow_query=False,
         allow_fragment=False,
-        trailing_slash=False)
+        trailing_slash=False,
+    )
 
     git_repo_url = URIField(
-        title=_("Repo URL"), required=False,
+        title=_("Repo URL"),
+        required=False,
         description=_(
             "The URL of the Git repository.  For imports to Bazaar, the "
             "HEAD branch will be imported by default, but you can import "
             "different branches by appending ',branch=$name' to the URL.  "
-            "For imports to Git, the entire repository will be imported."),
+            "For imports to Git, the entire repository will be imported."
+        ),
         allowed_schemes=["git", "http", "https"],
         allow_userinfo=True,
         allow_port=True,
         allow_query=False,
         allow_fragment=False,
-        trailing_slash=False)
+        trailing_slash=False,
+    )
 
     git_target_rcs_type = Choice(
         title=_("Target version control system"),
         description=_(
             "The version control system that the source code should be "
-            "imported into on the Launchpad side."),
-        required=False, vocabulary=TargetRevisionControlSystems)
+            "imported into on the Launchpad side."
+        ),
+        required=False,
+        vocabulary=TargetRevisionControlSystems,
+    )
 
     bzr_branch_url = URIField(
-        title=_("Branch URL"), required=False,
+        title=_("Branch URL"),
+        required=False,
         description=_("The URL of the Bazaar branch."),
         allowed_schemes=["http", "https", "bzr", "ftp"],
         allow_userinfo=True,
         allow_port=True,
-        allow_query=False,     # Query makes no sense in Bazaar
+        allow_query=False,  # Query makes no sense in Bazaar
         allow_fragment=False,  # Fragment makes no sense in Bazaar
-        trailing_slash=False)
+        trailing_slash=False,
+    )
 
     branch_name = copy_field(
-        IBranch['name'],
-        __name__='branch_name',
-        title=_('Name'),
+        IBranch["name"],
+        __name__="branch_name",
+        title=_("Name"),
         description=_(
             "This will be used in the branch or repository URL to identify "
-            "the import.  Examples: main, trunk."),
-        )
+            "the import.  Examples: main, trunk."
+        ),
+    )
 
     product = Choice(
-        title=_('Project'),
+        title=_("Project"),
         description=_("The Project to associate the code import with."),
         vocabulary="Product",
-        )
+    )
 
 
 class CodeImportNewView(CodeImportBaseView, CodeImportNameValidationMixin):
@@ -337,11 +353,11 @@ class CodeImportNewView(CodeImportBaseView, CodeImportNameValidationMixin):
     @property
     def initial_values(self):
         return {
-            'owner': self.user,
-            'rcs_type': RevisionControlSystems.BZR,
-            'branch_name': 'trunk',
-            'git_target_rcs_type': TargetRevisionControlSystems.BZR,
-            }
+            "owner": self.user,
+            "rcs_type": RevisionControlSystems.BZR,
+            "branch_name": "trunk",
+            "git_target_rcs_type": TargetRevisionControlSystems.BZR,
+        }
 
     @property
     def context_is_product(self):
@@ -349,35 +365,40 @@ class CodeImportNewView(CodeImportBaseView, CodeImportNameValidationMixin):
 
     @property
     def label(self):
-        label = 'Request a code import'
+        label = "Request a code import"
         if self.context_is_product:
-            label += ' for %s' % self.context.displayname
+            label += " for %s" % self.context.displayname
         return label
 
     @property
     def cancel_url(self):
         """Cancel should take the user back to the root site."""
-        return '/'
+        return "/"
 
     def setUpFields(self):
         CodeImportBaseView.setUpFields(self)
         if self.context_is_product:
-            self.form_fields = self.form_fields.omit('product')
+            self.form_fields = self.form_fields.omit("product")
 
         # If the user can administer branches, then they should be able to
         # assign the ownership of the branch to any valid person or team.
         if user_has_special_branch_access(self.user):
-            owner_field = self.schema['owner']
+            owner_field = self.schema["owner"]
             any_owner_choice = Choice(
-                __name__='owner', title=owner_field.title,
+                __name__="owner",
+                title=owner_field.title,
                 description=_(
                     "As an administrator you are able to reassign this "
-                    "branch to any person or team."),
-                required=True, vocabulary='ValidPersonOrTeam')
+                    "branch to any person or team."
+                ),
+                required=True,
+                vocabulary="ValidPersonOrTeam",
+            )
             any_owner_field = form.Fields(
-                any_owner_choice, render_context=self.render_context)
+                any_owner_choice, render_context=self.render_context
+            )
             # Replace the normal owner field with a more permissive vocab.
-            self.form_fields = self.form_fields.omit('owner')
+            self.form_fields = self.form_fields.omit("owner")
             self.form_fields = any_owner_field + self.form_fields
 
     def setUpWidgets(self):
@@ -385,17 +406,17 @@ class CodeImportNewView(CodeImportBaseView, CodeImportNameValidationMixin):
 
         # Extract the radio buttons from the rcs_type widget, so we can
         # display them separately in the form.
-        soup = BeautifulSoup(self.widgets['rcs_type']())
-        fields = soup.find_all('input')
-        [cvs_button, svn_button, git_button, bzr_button,
-            empty_marker] = [
-                field for field in fields
-                if field.get('value') in [
-                     'CVS', 'BZR_SVN', 'GIT', 'BZR', '1']]
-        bzr_button['onclick'] = 'updateWidgets()'
-        cvs_button['onclick'] = 'updateWidgets()'
-        svn_button['onclick'] = 'updateWidgets()'
-        git_button['onclick'] = 'updateWidgets()'
+        soup = BeautifulSoup(self.widgets["rcs_type"]())
+        fields = soup.find_all("input")
+        [cvs_button, svn_button, git_button, bzr_button, empty_marker] = [
+            field
+            for field in fields
+            if field.get("value") in ["CVS", "BZR_SVN", "GIT", "BZR", "1"]
+        ]
+        bzr_button["onclick"] = "updateWidgets()"
+        cvs_button["onclick"] = "updateWidgets()"
+        svn_button["onclick"] = "updateWidgets()"
+        git_button["onclick"] = "updateWidgets()"
         # The following attributes are used only in the page template.
         self.rcs_type_cvs = str(cvs_button)
         self.rcs_type_svn = str(svn_button)
@@ -404,54 +425,57 @@ class CodeImportNewView(CodeImportBaseView, CodeImportNameValidationMixin):
         self.rcs_type_emptymarker = str(empty_marker)
         # This widget is only conditionally required in the rcs_type == GIT
         # case, but we still don't want a "(nothing selected)" item.
-        self.widgets['git_target_rcs_type']._displayItemForMissingValue = False
+        self.widgets["git_target_rcs_type"]._displayItemForMissingValue = False
 
     def _getImportLocation(self, data):
         """Return the import location based on type."""
-        rcs_type = data['rcs_type']
+        rcs_type = data["rcs_type"]
         if rcs_type == RevisionControlSystems.CVS:
-            return data.get('cvs_root'), data.get('cvs_module'), None
+            return data.get("cvs_root"), data.get("cvs_module"), None
         elif rcs_type == RevisionControlSystems.BZR_SVN:
-            return None, None, data.get('svn_branch_url')
+            return None, None, data.get("svn_branch_url")
         elif rcs_type == RevisionControlSystems.GIT:
-            return None, None, data.get('git_repo_url')
+            return None, None, data.get("git_repo_url")
         elif rcs_type == RevisionControlSystems.BZR:
-            return None, None, data.get('bzr_branch_url')
+            return None, None, data.get("bzr_branch_url")
         else:
             raise AssertionError(
-                'Unexpected revision control type %r.' % rcs_type)
+                "Unexpected revision control type %r." % rcs_type
+            )
 
     def _create_import(self, data, status):
         """Create the code import."""
         product = self.getProduct(data)
         cvs_root, cvs_module, url = self._getImportLocation(data)
-        if data['rcs_type'] == RevisionControlSystems.GIT:
+        if data["rcs_type"] == RevisionControlSystems.GIT:
             target_rcs_type = data.get(
-                'git_target_rcs_type', TargetRevisionControlSystems.BZR)
+                "git_target_rcs_type", TargetRevisionControlSystems.BZR
+            )
         else:
             target_rcs_type = TargetRevisionControlSystems.BZR
         return getUtility(ICodeImportSet).new(
             registrant=self.user,
-            owner=data['owner'],
+            owner=data["owner"],
             context=product,
-            branch_name=data['branch_name'],
-            rcs_type=data['rcs_type'],
+            branch_name=data["branch_name"],
+            rcs_type=data["rcs_type"],
             target_rcs_type=target_rcs_type,
             url=url,
             cvs_root=cvs_root,
             cvs_module=cvs_module,
-            review_status=status)
+            review_status=status,
+        )
 
-    @action(_('Request Import'), name='request_import')
+    @action(_("Request Import"), name="request_import")
     def request_import_action(self, action, data):
         """Create the code_import, and subscribe the user to the branch."""
         try:
             code_import = self._create_import(data, None)
         except BranchExists as e:
-            self._setBranchExists(e.existing_branch, 'branch_name')
+            self._setBranchExists(e.existing_branch, "branch_name")
             return
         except GitRepositoryExists as e:
-            self._setBranchExists(e.existing_repository, 'branch_name')
+            self._setBranchExists(e.existing_repository, "branch_name")
             return
 
         # Subscribe the user.
@@ -460,32 +484,37 @@ class CodeImportNewView(CodeImportBaseView, CodeImportNameValidationMixin):
             BranchSubscriptionNotificationLevel.FULL,
             BranchSubscriptionDiffSize.NODIFF,
             CodeReviewNotificationLevel.NOEMAIL,
-            self.user)
+            self.user,
+        )
 
         self.next_url = canonical_url(code_import.target)
 
-        self.request.response.addNotification("""
-            New code import created. The code import will start shortly.""")
+        self.request.response.addNotification(
+            """
+            New code import created. The code import will start shortly."""
+        )
 
     def getProduct(self, data):
         """If the context is a product, use that, otherwise get from data."""
         if self.context_is_product:
             return self.context
         else:
-            return data.get('product')
+            return data.get("product")
 
     def validate_widgets(self, data, names=None):
         """See `LaunchpadFormView`."""
-        self.widgets['git_target_rcs_type'].context.required = (
-            data.get('rcs_type') == RevisionControlSystems.GIT)
+        self.widgets["git_target_rcs_type"].context.required = (
+            data.get("rcs_type") == RevisionControlSystems.GIT
+        )
         super().validate_widgets(data, names=names)
 
     def validate(self, data):
         """See `LaunchpadFormView`."""
-        rcs_type = data['rcs_type']
+        rcs_type = data["rcs_type"]
         if rcs_type == RevisionControlSystems.GIT:
             target_rcs_type = data.get(
-                'git_target_rcs_type', TargetRevisionControlSystems.BZR)
+                "git_target_rcs_type", TargetRevisionControlSystems.BZR
+            )
         else:
             target_rcs_type = TargetRevisionControlSystems.BZR
 
@@ -493,7 +522,7 @@ class CodeImportNewView(CodeImportBaseView, CodeImportNameValidationMixin):
         # for the specified namespace.
         product = self.getProduct(data)
         # 'owner' in data may be None if it failed validation.
-        owner = data.get('owner')
+        owner = data.get("owner")
         if product is not None and owner is not None:
             if target_rcs_type == TargetRevisionControlSystems.BZR:
                 namespace = get_branch_namespace(owner, product)
@@ -505,38 +534,49 @@ class CodeImportNewView(CodeImportBaseView, CodeImportNameValidationMixin):
                 can_create = policy.canCreateRepositories(self.user)
             if not can_create:
                 self.setFieldError(
-                    'product',
+                    "product",
                     "You are not allowed to register imports for %s."
-                    % product.displayname)
+                    % product.displayname,
+                )
 
         # Make sure fields for unselected revision control systems
         # are blanked out:
         if rcs_type == RevisionControlSystems.CVS:
-            self._validateCVS(data.get('cvs_root'), data.get('cvs_module'))
+            self._validateCVS(data.get("cvs_root"), data.get("cvs_module"))
         elif rcs_type == RevisionControlSystems.BZR_SVN:
             self._validateURL(
-                data.get('svn_branch_url'), rcs_type, target_rcs_type,
-                field_name='svn_branch_url')
+                data.get("svn_branch_url"),
+                rcs_type,
+                target_rcs_type,
+                field_name="svn_branch_url",
+            )
         elif rcs_type == RevisionControlSystems.GIT:
             self._validateURL(
-                data.get('git_repo_url'), rcs_type, target_rcs_type,
-                field_name='git_repo_url')
+                data.get("git_repo_url"),
+                rcs_type,
+                target_rcs_type,
+                field_name="git_repo_url",
+            )
         elif rcs_type == RevisionControlSystems.BZR:
             self._validateURL(
-                data.get('bzr_branch_url'), rcs_type, target_rcs_type,
-                field_name='bzr_branch_url')
+                data.get("bzr_branch_url"),
+                rcs_type,
+                target_rcs_type,
+                field_name="bzr_branch_url",
+            )
         else:
             raise AssertionError(
-                'Unexpected revision control type %r.' % rcs_type)
+                "Unexpected revision control type %r." % rcs_type
+            )
 
 
 class EditCodeImportForm(Interface):
     """The fields presented on the form for editing a code import."""
 
-    url = copy_field(ICodeImport['url'], readonly=False)
-    cvs_root = copy_field(ICodeImport['cvs_root'], readonly=False)
-    cvs_module = copy_field(ICodeImport['cvs_module'], readonly=False)
-    whiteboard = copy_field(IBranch['whiteboard'])
+    url = copy_field(ICodeImport["url"], readonly=False)
+    cvs_root = copy_field(ICodeImport["cvs_root"], readonly=False)
+    cvs_module = copy_field(ICodeImport["cvs_module"], readonly=False)
+    whiteboard = copy_field(IBranch["whiteboard"])
 
 
 def _makeEditAction(label, status, text):
@@ -550,8 +590,10 @@ def _makeEditAction(label, status, text):
         notifcation, if a change was made.
     """
     if status is not None:
+
         def condition(self, ignored):
             return self._showButtonForStatus(status)
+
     else:
         condition = None
 
@@ -561,22 +603,24 @@ def _makeEditAction(label, status, text):
             # Moderators can change everything in code import, including its
             # status.
             if status is not None:
-                data['review_status'] = status
+                data["review_status"] = status
             event = self.code_import.updateFromData(data, self.user)
             if event is not None:
                 self.request.response.addNotification(
-                    'The code import has been ' + text + '.')
+                    "The code import has been " + text + "."
+                )
         elif self._is_edit_user and "url" in data:
             # Edit users can only change URL
             event = self.code_import.updateURL(data["url"], self.user)
             if event is not None:
                 self.request.response.addNotification(
-                    'The code import URL has been updated.')
+                    "The code import URL has been updated."
+                )
         else:
-            self.request.response.addNotification('No changes made.')
-    name = label.lower().replace(' ', '_')
-    return form.Action(
-        label, name=name, success=success, condition=condition)
+            self.request.response.addNotification("No changes made.")
+
+    name = label.lower().replace(" ", "_")
+    return form.Action(label, name=name, success=success, condition=condition)
 
 
 class CodeImportEditView(CodeImportBaseView):
@@ -595,14 +639,16 @@ class CodeImportEditView(CodeImportBaseView):
     # Need this to render the context to prepopulate the form fields.
     # Added here as the base class isn't LaunchpadEditFormView.
     render_context = True
-    page_title = 'Edit import details'
+    page_title = "Edit import details"
     label = page_title
 
     @property
     def initial_values(self):
-        if (self.code_import.target_rcs_type ==
-                TargetRevisionControlSystems.BZR):
-            return {'whiteboard': self.context.whiteboard}
+        if (
+            self.code_import.target_rcs_type
+            == TargetRevisionControlSystems.BZR
+        ):
+            return {"whiteboard": self.context.whiteboard}
         else:
             return {}
 
@@ -628,49 +674,58 @@ class CodeImportEditView(CodeImportBaseView):
         # If the import is a Subversion import, then omit the CVS
         # fields, and vice versa.
         if self.code_import.rcs_type == RevisionControlSystems.CVS:
-            self.form_fields = self.form_fields.omit('url')
+            self.form_fields = self.form_fields.omit("url")
         elif self.code_import.rcs_type in NON_CVS_RCS_TYPES:
-            self.form_fields = self.form_fields.omit('cvs_root', 'cvs_module')
+            self.form_fields = self.form_fields.omit("cvs_root", "cvs_module")
         else:
-            raise AssertionError('Unknown rcs_type for code import.')
+            raise AssertionError("Unknown rcs_type for code import.")
 
-        if (self.code_import.target_rcs_type !=
-                TargetRevisionControlSystems.BZR):
-            self.form_fields = self.form_fields.omit('whiteboard')
+        if (
+            self.code_import.target_rcs_type
+            != TargetRevisionControlSystems.BZR
+        ):
+            self.form_fields = self.form_fields.omit("whiteboard")
 
     def _showButtonForStatus(self, status):
         """If the status is different, and the user is super, show button."""
-        return (self._is_moderator_user and
-                self.code_import.review_status != status)
+        return (
+            self._is_moderator_user
+            and self.code_import.review_status != status
+        )
 
     actions = form.Actions(
-        _makeEditAction(_('Update'), None, 'updated'),
+        _makeEditAction(_("Update"), None, "updated"),
         _makeEditAction(
-            _('Approve'), CodeImportReviewStatus.REVIEWED,
-            'approved'),
+            _("Approve"), CodeImportReviewStatus.REVIEWED, "approved"
+        ),
         _makeEditAction(
-            _('Mark Invalid'), CodeImportReviewStatus.INVALID,
-            'set as invalid'),
+            _("Mark Invalid"), CodeImportReviewStatus.INVALID, "set as invalid"
+        ),
         _makeEditAction(
-            _('Suspend'), CodeImportReviewStatus.SUSPENDED,
-            'suspended'),
+            _("Suspend"), CodeImportReviewStatus.SUSPENDED, "suspended"
+        ),
         _makeEditAction(
-            _('Mark Failing'), CodeImportReviewStatus.FAILING,
-            'marked as failing'),
-        )
+            _("Mark Failing"),
+            CodeImportReviewStatus.FAILING,
+            "marked as failing",
+        ),
+    )
 
     def validate(self, data):
         """See `LaunchpadFormView`."""
         if self.code_import.rcs_type == RevisionControlSystems.CVS:
             self._validateCVS(
-                data.get('cvs_root'), data.get('cvs_module'),
-                self.code_import)
+                data.get("cvs_root"), data.get("cvs_module"), self.code_import
+            )
         elif self.code_import.rcs_type in NON_CVS_RCS_TYPES:
             self._validateURL(
-                data.get('url'), self.code_import.rcs_type,
-                self.code_import.target_rcs_type, self.code_import)
+                data.get("url"),
+                self.code_import.rcs_type,
+                self.code_import.target_rcs_type,
+                self.code_import,
+            )
         else:
-            raise AssertionError('Unknown rcs_type for code import.')
+            raise AssertionError("Unknown rcs_type for code import.")
 
 
 class CodeImportMachineView(LaunchpadView):
@@ -686,11 +741,13 @@ class CodeImportMachineView(LaunchpadView):
 
 def validate_import_url(url, rcs_type, target_rcs_type, existing_import=None):
     """Validate the given import URL."""
-    if (rcs_type.name == target_rcs_type.name and
-            urlparse(url).netloc.endswith('launchpad.net')):
+    if rcs_type.name == target_rcs_type.name and urlparse(url).netloc.endswith(
+        "launchpad.net"
+    ):
         return (
             "You cannot create same-VCS imports for branches or repositories "
-            "that are hosted by Launchpad.")
+            "that are hosted by Launchpad."
+        )
     code_import = getUtility(ICodeImportSet).getByURL(url, target_rcs_type)
     if code_import is not None:
         if existing_import and code_import == existing_import:
@@ -701,8 +758,11 @@ def validate_import_url(url, rcs_type, target_rcs_type, existing_import=None):
             target_type = "repository"
         return structured(
             "This foreign branch URL is already specified for the imported "
-            "%s <a href='%s'>%s</a>.", target_type,
-            canonical_url(code_import.target), code_import.target.unique_name)
+            "%s <a href='%s'>%s</a>.",
+            target_type,
+            canonical_url(code_import.target),
+            code_import.target.unique_name,
+        )
 
 
 class CodeImportTargetMixin:
@@ -751,25 +811,32 @@ class RequestImportView(LaunchpadFormView):
     def next_url(self):
         return canonical_url(self.context)
 
-    @action('Import Now', name='request')
+    @action("Import Now", name="request")
     def request_import_action(self, action, data):
         try:
             self.context.code_import.requestImport(
-                self.user, error_if_already_requested=True)
+                self.user, error_if_already_requested=True
+            )
             self.request.response.addNotification(
-                "Import will run as soon as possible.")
+                "Import will run as soon as possible."
+            )
         except CodeImportNotInReviewedState:
             self.request.response.addNotification(
-                "This import is no longer being updated automatically.")
+                "This import is no longer being updated automatically."
+            )
         except CodeImportAlreadyRunning:
             self.request.response.addNotification(
-                "The import is already running.")
+                "The import is already running."
+            )
         except CodeImportAlreadyRequested as e:
             user = e.requesting_user
-            adapter = queryAdapter(user, IPathAdapter, 'fmt')
+            adapter = queryAdapter(user, IPathAdapter, "fmt")
             self.request.response.addNotification(
-                structured("The import has already been requested by %s." %
-                           adapter.link(None)))
+                structured(
+                    "The import has already been requested by %s."
+                    % adapter.link(None)
+                )
+            )
 
     @property
     def prefix(self):
@@ -794,17 +861,21 @@ class TryImportAgainView(LaunchpadFormView):
     def next_url(self):
         return canonical_url(self.context)
 
-    @action('Try Again', name='tryagain')
+    @action("Try Again", name="tryagain")
     def request_try_again(self, action, data):
-        if (self.context.code_import.review_status !=
-            CodeImportReviewStatus.FAILING):
+        if (
+            self.context.code_import.review_status
+            != CodeImportReviewStatus.FAILING
+        ):
             self.request.response.addNotification(
                 "The import is now %s."
-                % self.context.code_import.review_status.name)
+                % self.context.code_import.review_status.name
+            )
         else:
             self.context.code_import.tryFailingImportAgain(self.user)
             self.request.response.addNotification(
-                "Import will be tried again as soon as possible.")
+                "Import will be tried again as soon as possible."
+            )
 
     @property
     def prefix(self):
diff --git a/lib/lp/code/browser/codeimportmachine.py b/lib/lp/code/browser/codeimportmachine.py
index 428e422..5b84c73 100644
--- a/lib/lp/code/browser/codeimportmachine.py
+++ b/lib/lp/code/browser/codeimportmachine.py
@@ -4,12 +4,12 @@
 """Browser views for CodeImportMachines."""
 
 __all__ = [
-    'CodeImportMachineBreadcrumb',
-    'CodeImportMachineSetBreadcrumb',
-    'CodeImportMachineSetNavigation',
-    'CodeImportMachineSetView',
-    'CodeImportMachineView',
-    ]
+    "CodeImportMachineBreadcrumb",
+    "CodeImportMachineSetBreadcrumb",
+    "CodeImportMachineSetNavigation",
+    "CodeImportMachineSetView",
+    "CodeImportMachineView",
+]
 
 
 from lazr.delegates import delegate_to
@@ -18,24 +18,17 @@ from zope.interface import Interface
 from zope.schema import TextLine
 
 from lp import _
-from lp.app.browser.launchpadform import (
-    action,
-    LaunchpadFormView,
-    )
+from lp.app.browser.launchpadform import LaunchpadFormView, action
 from lp.code.enums import (
     CodeImportJobState,
     CodeImportMachineOfflineReason,
     CodeImportMachineState,
-    )
+)
 from lp.code.interfaces.codeimportevent import ICodeImportEvent
 from lp.code.interfaces.codeimportjob import ICodeImportJobSet
 from lp.code.interfaces.codeimportmachine import ICodeImportMachineSet
 from lp.services.propertycache import cachedproperty
-from lp.services.webapp import (
-    canonical_url,
-    LaunchpadView,
-    Navigation,
-    )
+from lp.services.webapp import LaunchpadView, Navigation, canonical_url
 from lp.services.webapp.breadcrumb import Breadcrumb
 
 
@@ -49,6 +42,7 @@ class CodeImportMachineBreadcrumb(Breadcrumb):
 
 class CodeImportMachineSetNavigation(Navigation):
     """Navigation methods for ICodeImportMachineSet."""
+
     usedfor = ICodeImportMachineSet
 
     def traverse(self, hostname):
@@ -58,7 +52,8 @@ class CodeImportMachineSetNavigation(Navigation):
 
 class CodeImportMachineSetBreadcrumb(Breadcrumb):
     """Builds a breadcrumb for an `ICodeImportMachineSet`."""
-    text = 'Machines'
+
+    text = "Machines"
 
 
 class CodeImportMachineSetView(LaunchpadView):
@@ -75,31 +70,42 @@ class CodeImportMachineSetView(LaunchpadView):
     @property
     def pending_imports(self):
         """Get the number of imports that are pending."""
-        return getUtility(ICodeImportJobSet).getJobsInState(
-            CodeImportJobState.PENDING).count()
+        return (
+            getUtility(ICodeImportJobSet)
+            .getJobsInState(CodeImportJobState.PENDING)
+            .count()
+        )
 
     @property
     def scheduled_imports(self):
         """Get the number of imports that are scheduled."""
-        return getUtility(ICodeImportJobSet).getJobsInState(
-            CodeImportJobState.SCHEDULED).count()
+        return (
+            getUtility(ICodeImportJobSet)
+            .getJobsInState(CodeImportJobState.SCHEDULED)
+            .count()
+        )
 
     @property
     def running_imports(self):
         """Get the number of imports that are running."""
-        return getUtility(ICodeImportJobSet).getJobsInState(
-            CodeImportJobState.RUNNING).count()
+        return (
+            getUtility(ICodeImportJobSet)
+            .getJobsInState(CodeImportJobState.RUNNING)
+            .count()
+        )
 
 
 class UpdateMachineStateForm(Interface):
     """An interface to allow the user to enter a reason for quiescing."""
 
     reason = TextLine(
-        title=_('Reason'), required=False, description=_(
-            "Why the machine state is changing."))
+        title=_("Reason"),
+        required=False,
+        description=_("Why the machine state is changing."),
+    )
 
 
-@delegate_to(ICodeImportEvent, context='event')
+@delegate_to(ICodeImportEvent, context="event")
 class DecoratedEvent:
     """A CodeImportEvent with cached items."""
 
@@ -118,7 +124,7 @@ class CodeImportMachineView(LaunchpadFormView):
     schema = UpdateMachineStateForm
 
     # The default reason is always the empty string.
-    initial_values = {'reason': ''}
+    initial_values = {"reason": ""}
 
     @property
     def page_title(self):
@@ -144,27 +150,37 @@ class CodeImportMachineView(LaunchpadFormView):
 
         The next_state is stored in the data dict of the action.
         """
-        next_state = action.data['next_state']
+        next_state = action.data["next_state"]
         if next_state == CodeImportMachineState.QUIESCING:
             return self.context.state == CodeImportMachineState.ONLINE
         else:
             return self.context.state != next_state
 
-    @action('Set Online', name='set_online',
-            data={'next_state': CodeImportMachineState.ONLINE},
-            condition=_canChangeToState)
+    @action(
+        "Set Online",
+        name="set_online",
+        data={"next_state": CodeImportMachineState.ONLINE},
+        condition=_canChangeToState,
+    )
     def set_online_action(self, action, data):
-        self.context.setOnline(self.user, data['reason'])
+        self.context.setOnline(self.user, data["reason"])
 
-    @action('Set Offline', name='set_offline',
-            data={'next_state': CodeImportMachineState.OFFLINE},
-            condition=_canChangeToState)
+    @action(
+        "Set Offline",
+        name="set_offline",
+        data={"next_state": CodeImportMachineState.OFFLINE},
+        condition=_canChangeToState,
+    )
     def set_offline_action(self, action, data):
         self.context.setOffline(
-            CodeImportMachineOfflineReason.STOPPED, self.user, data['reason'])
-
-    @action('Set Quiescing', name='set_quiescing',
-            data={'next_state': CodeImportMachineState.QUIESCING},
-            condition=_canChangeToState)
+            CodeImportMachineOfflineReason.STOPPED, self.user, data["reason"]
+        )
+
+    @action(
+        "Set Quiescing",
+        name="set_quiescing",
+        data={"next_state": CodeImportMachineState.QUIESCING},
+        condition=_canChangeToState,
+    )
     def set_quiescing_action(self, action, data):
-        self.context.setQuiescing(self.user, data['reason'])
+        self.context.setQuiescing(self.user, data["reason"])
diff --git a/lib/lp/code/browser/codereviewcomment.py b/lib/lp/code/browser/codereviewcomment.py
index a8a480c..01cdc12 100644
--- a/lib/lp/code/browser/codereviewcomment.py
+++ b/lib/lp/code/browser/codereviewcomment.py
@@ -2,70 +2,52 @@
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 __all__ = [
-    'CodeReviewCommentAddView',
-    'CodeReviewCommentContextMenu',
-    'CodeReviewCommentView',
-    'CodeReviewDisplayComment',
-    ]
+    "CodeReviewCommentAddView",
+    "CodeReviewCommentContextMenu",
+    "CodeReviewCommentView",
+    "CodeReviewDisplayComment",
+]
 
 from lazr.delegates import delegate_to
 from lazr.restful.interface import copy_field
 from zope.component import getUtility
 from zope.formlib.widget import CustomWidgetFactory
-from zope.formlib.widgets import (
-    DropdownWidget,
-    TextAreaWidget,
-    TextWidget,
-    )
-from zope.interface import (
-    implementer,
-    Interface,
-    )
-from zope.schema import (
-    Object,
-    Text,
-    )
+from zope.formlib.widgets import DropdownWidget, TextAreaWidget, TextWidget
+from zope.interface import Interface, implementer
+from zope.schema import Object, Text
 
 from lp import _
-from lp.app.browser.launchpadform import (
-    action,
-    LaunchpadFormView,
-    )
+from lp.app.browser.launchpadform import LaunchpadFormView, action
 from lp.code.interfaces.codereviewcomment import ICodeReviewComment
 from lp.code.interfaces.codereviewinlinecomment import (
     ICodeReviewInlineCommentSet,
-    )
+)
 from lp.code.interfaces.codereviewvote import ICodeReviewVoteReference
 from lp.services.comments.browser.comment import download_body
 from lp.services.comments.browser.messagecomment import MessageComment
 from lp.services.comments.interfaces.conversation import IComment
 from lp.services.config import config
 from lp.services.librarian.interfaces import ILibraryFileAlias
-from lp.services.messages.interfaces.message import (
-    IMessage,
-    IMessageEdit,
-    )
-from lp.services.propertycache import (
-    cachedproperty,
-    get_property_cache,
-    )
+from lp.services.messages.interfaces.message import IMessage, IMessageEdit
+from lp.services.propertycache import cachedproperty, get_property_cache
 from lp.services.webapp import (
-    canonical_url,
     ContextMenu,
     LaunchpadView,
     Link,
     Navigation,
+    canonical_url,
     stepthrough,
-    )
+)
 from lp.services.webapp.authorization import check_permission
 from lp.services.webapp.interfaces import ILaunchBag
 
 
 class CodeReviewCommentNavigation(Navigation):
     """Navigation for the `ICodeReviewComment`."""
+
     usedfor = ICodeReviewComment
 
-    @stepthrough('revisions')
+    @stepthrough("revisions")
     def traverse_revisions(self, revision):
         try:
             revision = int(revision)
@@ -76,12 +58,13 @@ class CodeReviewCommentNavigation(Navigation):
 
 class ICodeReviewDisplayComment(IComment, ICodeReviewComment):
     """Marker interface for displaying code review comments."""
-    message = Object(schema=IMessage, title=_('The message.'))
+
+    message = Object(schema=IMessage, title=_("The message."))
 
 
 @implementer(ICodeReviewDisplayComment)
-@delegate_to(ICodeReviewComment, context='comment')
-@delegate_to(IMessageEdit, context='message')
+@delegate_to(ICodeReviewComment, context="comment")
+@delegate_to(IMessageEdit, context="message")
 class CodeReviewDisplayComment(MessageComment):
     """A code review comment or activity or both.
 
@@ -111,13 +94,14 @@ class CodeReviewDisplayComment(MessageComment):
     def extra_css_class(self):
         css_classes = super().extra_css_class.split()
         if self.from_superseded:
-            css_classes.append('from-superseded')
-        return ' '.join(css_classes)
+            css_classes.append("from-superseded")
+        return " ".join(css_classes)
 
     @cachedproperty
     def previewdiff_id(self):
         inline_comment = getUtility(
-            ICodeReviewInlineCommentSet).getByReviewComment(self.comment)
+            ICodeReviewInlineCommentSet
+        ).getByReviewComment(self.comment)
         if inline_comment is not None:
             return inline_comment.previewdiff_id
         return None
@@ -143,7 +127,7 @@ class CodeReviewDisplayComment(MessageComment):
 
     @property
     def download_url(self):
-        return canonical_url(self.comment, view_name='+download')
+        return canonical_url(self.comment, view_name="+download")
 
     @cachedproperty
     def show_spam_controls(self):
@@ -160,15 +144,15 @@ class CodeReviewCommentContextMenu(ContextMenu):
     """Context menu for branches."""
 
     usedfor = ICodeReviewComment
-    links = ['reply']
+    links = ["reply"]
 
     def reply(self):
         enabled = self.context.branch_merge_proposal.isMergable()
-        return Link('+reply', 'Reply', icon='add', enabled=enabled)
+        return Link("+reply", "Reply", icon="add", enabled=enabled)
 
 
 @implementer(ILibraryFileAlias)
-@delegate_to(ILibraryFileAlias, context='alias')
+@delegate_to(ILibraryFileAlias, context="alias")
 class DiffAttachment:
     """An attachment that we are going to display."""
 
@@ -188,11 +172,11 @@ class DiffAttachment:
     def diff_text(self):
         """Get the text and attempt to decode it."""
         try:
-            diff = self.text.decode('utf-8')
+            diff = self.text.decode("utf-8")
         except UnicodeDecodeError:
-            diff = self.text.decode('windows-1252', 'replace')
+            diff = self.text.decode("windows-1252", "replace")
         # Strip off the trailing carriage returns.
-        return diff.rstrip('\n')
+        return diff.rstrip("\n")
 
 
 class CodeReviewCommentView(LaunchpadView):
@@ -211,11 +195,12 @@ class CodeReviewCommentView(LaunchpadView):
 
     def download(self):
         return download_body(
-            CodeReviewDisplayComment(self.context), self.request)
+            CodeReviewDisplayComment(self.context), self.request
+        )
 
     @property
     def can_edit(self):
-        return check_permission('launchpad.Edit', self.context.message)
+        return check_permission("launchpad.Edit", self.context.message)
 
     # Should the comment be shown in full?
     full_comment = True
@@ -224,7 +209,6 @@ class CodeReviewCommentView(LaunchpadView):
 
 
 class CodeReviewCommentIndexView(CodeReviewCommentView):
-
     def __call__(self):
         """View redirects to +download if comment is too long to render."""
         if self.comment.too_long_to_render:
@@ -235,14 +219,15 @@ class CodeReviewCommentIndexView(CodeReviewCommentView):
 class IEditCodeReviewComment(Interface):
     """Interface for use as a schema for CodeReviewComment forms."""
 
-    vote = copy_field(ICodeReviewComment['vote'], required=False)
+    vote = copy_field(ICodeReviewComment["vote"], required=False)
 
     review_type = copy_field(
-        ICodeReviewVoteReference['review_type'],
-        description='Lowercase keywords describing the type of review you '
-                    'are performing.')
+        ICodeReviewVoteReference["review_type"],
+        description="Lowercase keywords describing the type of review you "
+        "are performing.",
+    )
 
-    comment = Text(title=_('Comment'), required=False)
+    comment = Text(title=_("Comment"), required=False)
 
 
 class CodeReviewCommentAddView(LaunchpadFormView):
@@ -250,17 +235,19 @@ class CodeReviewCommentAddView(LaunchpadFormView):
 
     class MyDropWidget(DropdownWidget):
         "Override the default none-selected display name to -Select-."
-        _messageNoValue = 'Comment only'
+        _messageNoValue = "Comment only"
 
     schema = IEditCodeReviewComment
 
     custom_widget_review_type = CustomWidgetFactory(
-        TextWidget, displayWidth=15)
+        TextWidget, displayWidth=15
+    )
     custom_widget_comment = CustomWidgetFactory(
-        TextAreaWidget, cssClass='comment-text')
+        TextAreaWidget, cssClass="comment-text"
+    )
     custom_widget_vote = MyDropWidget
 
-    page_title = 'Reply to code review comment'
+    page_title = "Reply to code review comment"
 
     @property
     def initial_values(self):
@@ -272,8 +259,8 @@ class CodeReviewCommentAddView(LaunchpadFormView):
         if self.is_reply:
             comment = self.reply_to.as_quoted_email
         else:
-            comment = ''
-        return {'comment': comment}
+            comment = ""
+        return {"comment": comment}
 
     @property
     def is_reply(self):
@@ -296,14 +283,19 @@ class CodeReviewCommentAddView(LaunchpadFormView):
         else:
             return None
 
-    @action('Save Comment', name='add')
+    @action("Save Comment", name="add")
     def add_action(self, action, data):
         """Create the comment..."""
-        vote = data.get('vote')
-        review_type = data.get('review_type')
+        vote = data.get("vote")
+        review_type = data.get("review_type")
         self.branch_merge_proposal.createComment(
-            self.user, subject=None, content=data['comment'],
-            parent=self.reply_to, vote=vote, review_type=review_type)
+            self.user,
+            subject=None,
+            content=data["comment"],
+            parent=self.reply_to,
+            vote=vote,
+            review_type=review_type,
+        )
 
     @property
     def next_url(self):
diff --git a/lib/lp/code/browser/codereviewvote.py b/lib/lp/code/browser/codereviewvote.py
index 3d3d9c2..3996bb7 100644
--- a/lib/lp/code/browser/codereviewvote.py
+++ b/lib/lp/code/browser/codereviewvote.py
@@ -6,14 +6,8 @@
 from zope.interface import Interface
 
 from lp import _
-from lp.app.browser.launchpadform import (
-    action,
-    LaunchpadFormView,
-    )
-from lp.code.errors import (
-    ReviewNotPending,
-    UserHasExistingReview,
-    )
+from lp.app.browser.launchpadform import LaunchpadFormView, action
+from lp.code.errors import ReviewNotPending, UserHasExistingReview
 from lp.services.fields import PublicPersonChoice
 from lp.services.webapp import canonical_url
 
@@ -21,9 +15,12 @@ from lp.services.webapp import canonical_url
 class ReassignSchema(Interface):
     """Schema to use when reassigning the reviewer for a requested review."""
 
-    reviewer = PublicPersonChoice(title=_('Reviewer'), required=True,
-            description=_('A person who you want to review this.'),
-            vocabulary='ValidBranchReviewer')
+    reviewer = PublicPersonChoice(
+        title=_("Reviewer"),
+        required=True,
+        description=_("A person who you want to review this."),
+        vocabulary="ValidBranchReviewer",
+    )
 
 
 class CodeReviewVoteReassign(LaunchpadFormView):
@@ -31,7 +28,7 @@ class CodeReviewVoteReassign(LaunchpadFormView):
 
     schema = ReassignSchema
 
-    page_title = label = 'Reassign review request'
+    page_title = label = "Reassign review request"
 
     @property
     def next_url(self):
@@ -39,14 +36,14 @@ class CodeReviewVoteReassign(LaunchpadFormView):
 
     cancel_url = next_url
 
-    @action('Reassign', name='reassign')
+    @action("Reassign", name="reassign")
     def reassign_action(self, action, data):
         """Use the form data to change the review request reviewer."""
-        self.context.reassignReview(data['reviewer'])
+        self.context.reassignReview(data["reviewer"])
 
     def validate(self, data):
         """Make sure that the reassignment can happen."""
-        reviewer = data.get('reviewer')
+        reviewer = data.get("reviewer")
         if reviewer is not None:
             try:
                 self.context.validateReasignReview(reviewer)
diff --git a/lib/lp/code/browser/decorations.py b/lib/lp/code/browser/decorations.py
index d1110e5..3fadbfe 100644
--- a/lib/lp/code/browser/decorations.py
+++ b/lib/lp/code/browser/decorations.py
@@ -4,23 +4,20 @@
 """Decorated model objects used in the browser code."""
 
 __all__ = [
-    'DecoratedBranch',
-    ]
+    "DecoratedBranch",
+]
 
 from lazr.delegates import delegate_to
 from zope.interface import implementer
 
 from lp.app.interfaces.informationtype import IInformationType
 from lp.app.interfaces.launchpad import IPrivacy
-from lp.code.interfaces.branch import (
-    BzrIdentityMixin,
-    IBranch,
-    )
+from lp.code.interfaces.branch import BzrIdentityMixin, IBranch
 from lp.services.propertycache import cachedproperty
 
 
 @implementer(IPrivacy)
-@delegate_to(IBranch, IInformationType, context='branch')
+@delegate_to(IBranch, IInformationType, context="branch")
 class DecoratedBranch(BzrIdentityMixin):
     """Wrap a number of the branch accessors to cache results.
 
@@ -54,8 +51,9 @@ class DecoratedBranch(BzrIdentityMixin):
         """A simple property to see if there are any series links."""
         # True if linked to a product series or suite source package.
         return (
-            len(self.associated_product_series) > 0 or
-            len(self.suite_source_packages) > 0)
+            len(self.associated_product_series) > 0
+            or len(self.suite_source_packages) > 0
+        )
 
     def associatedProductSeries(self):
         """Override the IBranch.associatedProductSeries."""
diff --git a/lib/lp/code/browser/diff.py b/lib/lp/code/browser/diff.py
index d97ff97..0e4fd79 100644
--- a/lib/lp/code/browser/diff.py
+++ b/lib/lp/code/browser/diff.py
@@ -4,8 +4,8 @@
 """Display classes relating to diff objects of one sort or another."""
 
 __all__ = [
-    'PreviewDiffFormatterAPI',
-    ]
+    "PreviewDiffFormatterAPI",
+]
 
 
 from lp import _
@@ -24,13 +24,11 @@ class PreviewDiffNavigation(Navigation, FileNavigationMixin):
 
 
 class DiffFormatterAPI(ObjectFormatterAPI):
-
     def _get_url(self, librarian_alias):
         return librarian_alias.getURL()
 
     def url(self, view_name=None, rootsite=None):
-        """Use the url of the librarian file containing the diff.
-        """
+        """Use the url of the librarian file containing the diff."""
         librarian_alias = self._context.diff_text
         if librarian_alias is None:
             return None
@@ -50,51 +48,59 @@ class DiffFormatterAPI(ObjectFormatterAPI):
             that name on this object.
         """
         diff = self._context
-        conflict_text = ''
+        conflict_text = ""
         if diff.has_conflicts:
-            conflict_text = _(' (has conflicts)')
+            conflict_text = _(" (has conflicts)")
 
-        count_text = ''
+        count_text = ""
         added = diff.added_lines_count
         removed = diff.removed_lines_count
-        if (added is not None and removed is not None):
-            count_text = ' (+%d/-%d)' % (added, removed)
+        if added is not None and removed is not None:
+            count_text = " (+%d/-%d)" % (added, removed)
 
-        file_text = ''
+        file_text = ""
         diffstat = diff.diffstat
         if diffstat is not None:
             file_count = len(diffstat)
             basic_file_text = get_plural_text(
-                file_count, _('%d file modified'), _('%d files modified'))
+                file_count, _("%d file modified"), _("%d files modified")
+            )
             basic_file_text = basic_file_text % file_count
-            diffstat_text = '<br/>'.join(
-                structured('%s (+%s/-%s)', path, added, removed).escapedtext
-                for path, (added, removed) in sorted(diffstat.items()))
+            diffstat_text = "<br/>".join(
+                structured("%s (+%s/-%s)", path, added, removed).escapedtext
+                for path, (added, removed) in sorted(diffstat.items())
+            )
             file_text = (
-                '<div class="collapsible"><span>%s</span><div>%s</div></div>' %
-                (basic_file_text, diffstat_text))
+                '<div class="collapsible"><span>%s</span><div>%s</div></div>'
+                % (basic_file_text, diffstat_text)
+            )
 
         args = {
-            'line_count': _('%s lines') % diff.diff_lines_count,
-            'conflict_text': conflict_text,
-            'count_text': count_text,
-            'url': self.url(view_name),
-            }
+            "line_count": _("%s lines") % diff.diff_lines_count,
+            "conflict_text": conflict_text,
+            "count_text": count_text,
+            "url": self.url(view_name),
+        }
         # Under normal circumstances, there will be an associated file,
         # however if the diff is empty, then there is no alias to link to.
-        if args['url'] is None:
+        if args["url"] is None:
             return structured(
-                '<span class="empty-diff">'
-                '%(line_count)s</span>', **args).escapedtext
+                '<span class="empty-diff">' "%(line_count)s</span>", **args
+            ).escapedtext
         else:
-            return structured(
-                '<a href="%(url)s" class="diff-link">'
-                '%(line_count)s%(count_text)s%(conflict_text)s'
-                '</a>', **args).escapedtext + file_text
+            return (
+                structured(
+                    '<a href="%(url)s" class="diff-link">'
+                    "%(line_count)s%(count_text)s%(conflict_text)s"
+                    "</a>",
+                    **args,
+                ).escapedtext
+                + file_text
+            )
 
 
 class PreviewDiffFormatterAPI(DiffFormatterAPI):
     """Formatter for preview diffs."""
 
     def _get_url(self, library_):
-        return canonical_url(self._context) + '/+files/preview.diff'
+        return canonical_url(self._context) + "/+files/preview.diff"
diff --git a/lib/lp/code/browser/gitlisting.py b/lib/lp/code/browser/gitlisting.py
index 5a9c092..f5415f1 100644
--- a/lib/lp/code/browser/gitlisting.py
+++ b/lib/lp/code/browser/gitlisting.py
@@ -4,15 +4,12 @@
 """View classes for Git repository listings."""
 
 __all__ = [
-    'PersonTargetGitListingView',
-    'TargetGitListingView',
-    ]
+    "PersonTargetGitListingView",
+    "TargetGitListingView",
+]
 
 from zope.component import getUtility
-from zope.interface import (
-    implementer,
-    Interface,
-    )
+from zope.interface import Interface, implementer
 
 from lp.app.enums import PRIVATE_INFORMATION_TYPES
 from lp.code.browser.gitrepository import GitRefBatchNavigator
@@ -20,13 +17,13 @@ from lp.code.enums import GitListingSort
 from lp.code.interfaces.branchcollection import IBranchCollection
 from lp.code.interfaces.gitcollection import IGitCollection
 from lp.code.interfaces.gitnamespace import (
-    get_git_namespace,
     IGitNamespacePolicy,
-    )
+    get_git_namespace,
+)
 from lp.code.interfaces.gitrepository import IGitRepositorySet
 from lp.registry.interfaces.persondistributionsourcepackage import (
     IPersonDistributionSourcePackage,
-    )
+)
 from lp.registry.interfaces.personociproject import IPersonOCIProject
 from lp.registry.interfaces.personproduct import IPersonProduct
 from lp.services.config import config
@@ -44,20 +41,22 @@ class IGitRepositoryBatchNavigator(Interface):
 class GitRepositoryBatchNavigator(TableBatchNavigator):
     """Batch up Git repository listings."""
 
-    variable_name_prefix = 'repo'
+    variable_name_prefix = "repo"
 
     def __init__(self, view, repo_collection):
         super().__init__(
             repo_collection.getRepositories(
                 eager_load=True,
-                sort_by=GitListingSort.MOST_RECENTLY_CHANGED_FIRST),
-            view.request, size=config.launchpad.branchlisting_batch_size)
+                sort_by=GitListingSort.MOST_RECENTLY_CHANGED_FIRST,
+            ),
+            view.request,
+            size=config.launchpad.branchlisting_batch_size,
+        )
         self.view = view
         self.column_count = 2
 
 
 class BaseGitListingView(LaunchpadView):
-
     @property
     def target(self):
         raise NotImplementedError()
@@ -112,7 +111,7 @@ class BaseGitListingView(LaunchpadView):
 
 class TargetGitListingView(BaseGitListingView):
 
-    page_title = 'Git'
+    page_title = "Git"
 
     @property
     def target(self):
@@ -120,11 +119,10 @@ class TargetGitListingView(BaseGitListingView):
 
     @cachedproperty
     def default_git_repository(self):
-        repo = getUtility(IGitRepositorySet).getDefaultRepository(
-            self.context)
+        repo = getUtility(IGitRepositorySet).getDefaultRepository(self.context)
         if repo is None:
             return None
-        elif check_permission('launchpad.View', repo):
+        elif check_permission("launchpad.View", repo):
             return repo
         else:
             return None
@@ -132,11 +130,11 @@ class TargetGitListingView(BaseGitListingView):
 
 class PersonTargetGitListingView(BaseGitListingView):
 
-    page_title = 'Git'
+    page_title = "Git"
 
     @property
     def label(self):
-        return 'Git repositories for %s' % self.target.displayname
+        return "Git repositories for %s" % self.target.displayname
 
     @property
     def target(self):
@@ -152,10 +150,11 @@ class PersonTargetGitListingView(BaseGitListingView):
     @cachedproperty
     def default_git_repository(self):
         repo = getUtility(IGitRepositorySet).getDefaultRepositoryForOwner(
-            self.context.person, self.target)
+            self.context.person, self.target
+        )
         if repo is None:
             return None
-        elif check_permission('launchpad.View', repo):
+        elif check_permission("launchpad.View", repo):
             return repo
         else:
             return None
@@ -168,7 +167,8 @@ class OCIProjectGitListingView(TargetGitListingView):
 
 
 class PersonDistributionSourcePackageGitListingView(
-        PersonTargetGitListingView):
+    PersonTargetGitListingView
+):
 
     # PersonDistributionSourcePackage:+branches doesn't exist.
     show_bzr_link = False
@@ -182,6 +182,6 @@ class PersonOCIProjectGitListingView(PersonTargetGitListingView):
 
 class PlainGitListingView(BaseGitListingView):
 
-    page_title = 'Git'
+    page_title = "Git"
     target = None
     default_git_repository = None
diff --git a/lib/lp/code/browser/gitref.py b/lib/lp/code/browser/gitref.py
index bc72f3a..df8c704 100644
--- a/lib/lp/code/browser/gitref.py
+++ b/lib/lp/code/browser/gitref.py
@@ -4,17 +4,13 @@
 """Git reference views."""
 
 __all__ = [
-    'GitRefContextMenu',
-    'GitRefRegisterMergeProposalView',
-    'GitRefView',
-    ]
+    "GitRefContextMenu",
+    "GitRefRegisterMergeProposalView",
+    "GitRefView",
+]
 
 import json
-from urllib.parse import (
-    quote_plus,
-    urlsplit,
-    urlunsplit,
-    )
+from urllib.parse import quote_plus, urlsplit, urlunsplit
 
 from breezy import urlutils
 from lazr.restful.interface import copy_field
@@ -23,74 +19,60 @@ from zope.formlib.widget import CustomWidgetFactory
 from zope.formlib.widgets import TextAreaWidget
 from zope.interface import Interface
 from zope.publisher.interfaces import NotFound
-from zope.schema import (
-    Bool,
-    Text,
-    )
+from zope.schema import Bool, Text
 
 from lp import _
-from lp.app.browser.launchpadform import (
-    action,
-    LaunchpadFormView,
-    )
+from lp.app.browser.launchpadform import LaunchpadFormView, action
 from lp.charms.browser.hascharmrecipes import (
     HasCharmRecipesMenuMixin,
     HasCharmRecipesViewMixin,
-    )
+)
 from lp.code.browser.branchmergeproposal import (
     latest_proposals_for_each_branch,
-    )
+)
 from lp.code.browser.revisionstatus import HasRevisionStatusReportsMixin
 from lp.code.browser.sourcepackagerecipelisting import HasRecipesMenuMixin
 from lp.code.browser.widgets.gitref import GitRefWidget
 from lp.code.enums import GitRepositoryType
-from lp.code.errors import (
-    GitRepositoryScanFault,
-    InvalidBranchMergeProposal,
-    )
+from lp.code.errors import GitRepositoryScanFault, InvalidBranchMergeProposal
 from lp.code.interfaces.branchmergeproposal import IBranchMergeProposal
 from lp.code.interfaces.codereviewvote import ICodeReviewVoteReference
 from lp.code.interfaces.gitref import IGitRef
 from lp.code.interfaces.gitrepository import (
     ContributorGitIdentity,
     IGitRepositorySet,
-    )
+)
 from lp.registry.interfaces.person import IPerson
 from lp.services.config import config
 from lp.services.helpers import english_list
 from lp.services.propertycache import cachedproperty
 from lp.services.scripts import log
-from lp.services.webapp import (
-    canonical_url,
-    ContextMenu,
-    LaunchpadView,
-    Link,
-    )
+from lp.services.webapp import ContextMenu, LaunchpadView, Link, canonical_url
 from lp.services.webapp.authorization import check_permission
 from lp.services.webapp.escaping import structured
-from lp.snappy.browser.hassnaps import (
-    HasSnapsMenuMixin,
-    HasSnapsViewMixin,
-    )
+from lp.snappy.browser.hassnaps import HasSnapsMenuMixin, HasSnapsViewMixin
 
 
 class GitRefContextMenu(
-        ContextMenu, HasRecipesMenuMixin, HasSnapsMenuMixin,
-        HasCharmRecipesMenuMixin):
+    ContextMenu,
+    HasRecipesMenuMixin,
+    HasSnapsMenuMixin,
+    HasCharmRecipesMenuMixin,
+):
     """Context menu for Git references."""
 
     usedfor = IGitRef
-    facet = 'branches'
+    facet = "branches"
     links = [
-        'browse_commits',
-        'create_charm_recipe',
-        'create_recipe',
-        'create_snap',
-        'register_merge',
-        'source',
-        'view_charm_recipes',
-        'view_recipes',
-        ]
+        "browse_commits",
+        "create_charm_recipe",
+        "create_recipe",
+        "create_snap",
+        "register_merge",
+        "source",
+        "view_charm_recipes",
+        "view_recipes",
+    ]
 
     def source(self):
         """Return a link to the branch's browsing interface."""
@@ -103,13 +85,14 @@ class GitRefContextMenu(
         text = "All commits"
         url = "%s/log/?h=%s" % (
             self.context.repository.getCodebrowseUrl(),
-            quote_plus(self.context.name.encode("UTF-8")))
+            quote_plus(self.context.name.encode("UTF-8")),
+        )
         return Link(url, text)
 
     def register_merge(self):
-        text = 'Propose for merging'
+        text = "Propose for merging"
         enabled = self.context.namespace.supports_merge_proposals
-        return Link('+register-merge', text, icon='add', enabled=enabled)
+        return Link("+register-merge", text, icon="add", enabled=enabled)
 
     def create_recipe(self):
         # You can't create a recipe for a reference in a private repository.
@@ -118,8 +101,12 @@ class GitRefContextMenu(
         return Link("+new-recipe", text, enabled=enabled, icon="add")
 
 
-class GitRefView(LaunchpadView, HasSnapsViewMixin, HasCharmRecipesViewMixin,
-                 HasRevisionStatusReportsMixin):
+class GitRefView(
+    LaunchpadView,
+    HasSnapsViewMixin,
+    HasCharmRecipesViewMixin,
+    HasRevisionStatusReportsMixin,
+):
 
     # This is set at self.commit_infos, and should be accessed by the view
     # as self.commit_info_message.
@@ -146,9 +133,11 @@ class GitRefView(LaunchpadView, HasSnapsViewMixin, HasCharmRecipesViewMixin,
         contributor = ContributorGitIdentity(
             owner=self.user,
             target=self.context.repository.target,
-            repository=self.context.repository)
+            repository=self.context.repository,
+        )
         base_url = urlutils.join(
-            config.codehosting.git_ssh_root, contributor.shortened_path)
+            config.codehosting.git_ssh_root, contributor.shortened_path
+        )
         url = list(urlsplit(base_url))
         url[1] = "{}@{}".format(self.user.name, url[1])
         return urlunsplit(url)
@@ -157,9 +146,9 @@ class GitRefView(LaunchpadView, HasSnapsViewMixin, HasCharmRecipesViewMixin,
     def user_can_push(self):
         """Whether the user can push to this branch."""
         return (
-            self.context.repository.repository_type ==
-                GitRepositoryType.HOSTED and
-            check_permission("launchpad.Edit", self.context))
+            self.context.repository.repository_type == GitRepositoryType.HOSTED
+            and check_permission("launchpad.Edit", self.context)
+        )
 
     @property
     def show_merge_links(self):
@@ -194,7 +183,8 @@ class GitRefView(LaunchpadView, HasSnapsViewMixin, HasCharmRecipesViewMixin,
         if IPerson.providedBy(self.context.namespace.target):
             messages.append(
                 "You will only be able to propose a merge to another personal "
-                "repository with the same name.")
+                "repository with the same name."
+            )
         return messages
 
     @cachedproperty
@@ -207,17 +197,20 @@ class GitRefView(LaunchpadView, HasSnapsViewMixin, HasCharmRecipesViewMixin,
     def landing_candidates(self):
         """Return a decorated list of landing candidates."""
         candidates = self.context.getPrecachedLandingCandidates(self.user)
-        return [proposal for proposal in candidates
-                if check_permission("launchpad.View", proposal)]
+        return [
+            proposal
+            for proposal in candidates
+            if check_permission("launchpad.View", proposal)
+        ]
 
     def _getBranchCountText(self, count):
         """Help to show user friendly text."""
         if count == 0:
-            return 'No branches'
+            return "No branches"
         elif count == 1:
-            return '1 branch'
+            return "1 branch"
         else:
-            return '%s branches' % count
+            return "%s branches" % count
 
     @cachedproperty
     def landing_candidate_count_text(self):
@@ -225,8 +218,11 @@ class GitRefView(LaunchpadView, HasSnapsViewMixin, HasCharmRecipesViewMixin,
 
     @cachedproperty
     def dependent_landings(self):
-        return [proposal for proposal in self.context.dependent_landings
-                if check_permission("launchpad.View", proposal)]
+        return [
+            proposal
+            for proposal in self.context.dependent_landings
+            if check_permission("launchpad.View", proposal)
+        ]
 
     @cachedproperty
     def dependent_landing_count_text(self):
@@ -235,21 +231,27 @@ class GitRefView(LaunchpadView, HasSnapsViewMixin, HasCharmRecipesViewMixin,
     @cachedproperty
     def commit_infos(self):
         try:
-            self._commit_info_message = ''
+            self._commit_info_message = ""
             return self.context.getLatestCommits(
-                extended_details=True, user=self.user, handle_timeout=True,
-                logger=log)
+                extended_details=True,
+                user=self.user,
+                handle_timeout=True,
+                logger=log,
+            )
         except GitRepositoryScanFault as e:
             log.error("There was an error fetching git commit info: %s" % e)
             self._commit_info_message = (
                 "There was an error while fetching commit information from "
                 "code hosting service. Please try again in a few minutes. "
                 'If the problem persists, <a href="/launchpad/+addquestion">'
-                "contact Launchpad support</a>.")
+                "contact Launchpad support</a>."
+            )
             return []
         except Exception as e:
-            log.error("There was an error scanning %s: (%s) %s" %
-                      (self.context, e.__class__, e))
+            log.error(
+                "There was an error scanning %s: (%s) %s"
+                % (self.context, e.__class__, e)
+            )
             raise
 
     def commit_infos_message(self):
@@ -265,54 +267,66 @@ class GitRefView(LaunchpadView, HasSnapsViewMixin, HasCharmRecipesViewMixin,
         count = self.context.recipes.count()
         if count == 0:
             # Nothing to link to.
-            return 'No recipes using this branch.'
+            return "No recipes using this branch."
         elif count == 1:
             # Link to the single recipe.
             return structured(
                 '<a href="%s">1 recipe</a> using this branch.',
-                canonical_url(self.context.recipes.one())).escapedtext
+                canonical_url(self.context.recipes.one()),
+            ).escapedtext
         else:
             # Link to a recipe listing.
             return structured(
-                '<a href="+recipes">%s recipes</a> using this branch.',
-                count).escapedtext
+                '<a href="+recipes">%s recipes</a> using this branch.', count
+            ).escapedtext
 
 
 class GitRefRegisterMergeProposalSchema(Interface):
     """The schema to define the form for registering a new merge proposal."""
 
     target_git_ref = copy_field(
-        IBranchMergeProposal['target_git_ref'], required=True)
+        IBranchMergeProposal["target_git_ref"], required=True
+    )
 
     prerequisite_git_ref = copy_field(
-        IBranchMergeProposal['prerequisite_git_ref'], required=False,
-        description=_("If the source branch is based on a different branch, "
-                      "you can add this as a prerequisite. "
-                      "The changes from that branch will not show "
-                      "in the diff."))
+        IBranchMergeProposal["prerequisite_git_ref"],
+        required=False,
+        description=_(
+            "If the source branch is based on a different branch, "
+            "you can add this as a prerequisite. "
+            "The changes from that branch will not show "
+            "in the diff."
+        ),
+    )
 
     comment = Text(
-        title=_('Description of the change'), required=False,
-        description=_('Describe what changes your branch introduces, '
-                      'what bugs it fixes, or what features it implements. '
-                      'Ideally include rationale and how to test. '
-                      'You do not need to repeat information from the commit '
-                      'message here.'))
+        title=_("Description of the change"),
+        required=False,
+        description=_(
+            "Describe what changes your branch introduces, "
+            "what bugs it fixes, or what features it implements. "
+            "Ideally include rationale and how to test. "
+            "You do not need to repeat information from the commit "
+            "message here."
+        ),
+    )
 
-    reviewer = copy_field(
-        ICodeReviewVoteReference['reviewer'], required=False)
+    reviewer = copy_field(ICodeReviewVoteReference["reviewer"], required=False)
 
     review_type = copy_field(
-        ICodeReviewVoteReference['review_type'],
-        description='Lowercase keywords describing the type of review you '
-                    'would like to be performed.')
+        ICodeReviewVoteReference["review_type"],
+        description="Lowercase keywords describing the type of review you "
+        "would like to be performed.",
+    )
 
-    commit_message = IBranchMergeProposal['commit_message']
+    commit_message = IBranchMergeProposal["commit_message"]
 
     needs_review = Bool(
-        title=_("Needs review"), required=True, default=True,
-        description=_(
-            "Is the proposal ready for review now?"))
+        title=_("Needs review"),
+        required=True,
+        default=True,
+        description=_("Is the proposal ready for review now?"),
+    )
 
 
 class GitRefRegisterMergeProposalView(LaunchpadFormView):
@@ -322,15 +336,19 @@ class GitRefRegisterMergeProposalView(LaunchpadFormView):
     for_input = True
 
     custom_widget_target_git_ref = CustomWidgetFactory(
-        GitRefWidget, require_branch=True)
+        GitRefWidget, require_branch=True
+    )
     custom_widget_prerequisite_git_ref = CustomWidgetFactory(
-        GitRefWidget, require_branch=True)
+        GitRefWidget, require_branch=True
+    )
     custom_widget_commit_message = CustomWidgetFactory(
-        TextAreaWidget, cssClass='comment-text')
+        TextAreaWidget, cssClass="comment-text"
+    )
     custom_widget_comment = CustomWidgetFactory(
-        TextAreaWidget, cssClass='comment-text')
+        TextAreaWidget, cssClass="comment-text"
+    )
 
-    page_title = label = 'Propose for merging'
+    page_title = label = "Propose for merging"
 
     @property
     def cancel_url(self):
@@ -339,87 +357,103 @@ class GitRefRegisterMergeProposalView(LaunchpadFormView):
     def initialize(self):
         """Show a 404 if the repository namespace doesn't support proposals."""
         if not self.context.namespace.supports_merge_proposals:
-            raise NotFound(self.context, '+register-merge')
+            raise NotFound(self.context, "+register-merge")
         super().initialize()
 
     def setUpWidgets(self, context=None):
         super().setUpWidgets(context=context)
 
-        if not self.widgets['target_git_ref'].hasInput():
+        if not self.widgets["target_git_ref"].hasInput():
             if self.context.repository.namespace.has_defaults:
                 repo_set = getUtility(IGitRepositorySet)
                 default_repo = repo_set.getDefaultRepository(
-                    self.context.repository.target)
+                    self.context.repository.target
+                )
             else:
                 default_repo = None
             if not default_repo:
                 default_repo = self.context.repository
             if default_repo.default_branch:
                 default_ref = default_repo.getRefByPath(
-                    default_repo.default_branch)
+                    default_repo.default_branch
+                )
                 with_path = True
             else:
                 default_ref = self.context
                 with_path = False
             self.widgets["target_git_ref"].setRenderedValue(
-                default_ref, with_path=with_path)
+                default_ref, with_path=with_path
+            )
 
-    @action('Propose Merge', name='register',
-            failure=LaunchpadFormView.ajax_failure_handler)
+    @action(
+        "Propose Merge",
+        name="register",
+        failure=LaunchpadFormView.ajax_failure_handler,
+    )
     def register_action(self, action, data):
         """Register the new merge proposal."""
 
         registrant = self.user
         source_ref = self.context
-        target_ref = data['target_git_ref']
-        prerequisite_ref = data.get('prerequisite_git_ref')
+        target_ref = data["target_git_ref"]
+        prerequisite_ref = data.get("prerequisite_git_ref")
 
         review_requests = []
-        reviewer = data.get('reviewer')
-        review_type = data.get('review_type')
+        reviewer = data.get("reviewer")
+        review_type = data.get("review_type")
         if reviewer is None:
             reviewer = target_ref.code_reviewer
         if reviewer is not None:
             review_requests.append((reviewer, review_type))
 
         repository_names = [
-            ref.repository.unique_name for ref in (source_ref, target_ref)]
+            ref.repository.unique_name for ref in (source_ref, target_ref)
+        ]
         repository_set = getUtility(IGitRepositorySet)
         visibility_info = repository_set.getRepositoryVisibilityInfo(
-            self.user, reviewer, repository_names)
-        visible_repositories = list(visibility_info['visible_repositories'])
+            self.user, reviewer, repository_names
+        )
+        visible_repositories = list(visibility_info["visible_repositories"])
         if self.request.is_ajax and len(visible_repositories) < 2:
             self.request.response.setStatus(400, "Repository Visibility")
-            self.request.response.setHeader(
-                'Content-Type', 'application/json')
-            return json.dumps({
-                'person_name': visibility_info['person_name'],
-                'repositories_to_check': repository_names,
-                'visible_repositories': visible_repositories,
-            })
+            self.request.response.setHeader("Content-Type", "application/json")
+            return json.dumps(
+                {
+                    "person_name": visibility_info["person_name"],
+                    "repositories_to_check": repository_names,
+                    "visible_repositories": visible_repositories,
+                }
+            )
 
         try:
             proposal = source_ref.addLandingTarget(
-                registrant=registrant, merge_target=target_ref,
+                registrant=registrant,
+                merge_target=target_ref,
                 merge_prerequisite=prerequisite_ref,
-                needs_review=data['needs_review'],
-                description=data.get('comment'),
+                needs_review=data["needs_review"],
+                description=data.get("comment"),
                 review_requests=review_requests,
-                commit_message=data.get('commit_message'))
+                commit_message=data.get("commit_message"),
+            )
             if len(visible_repositories) < 2:
                 invisible_repositories = [
                     ref.repository.unique_name
                     for ref in (source_ref, target_ref)
-                    if ref.repository.unique_name not in visible_repositories]
+                    if ref.repository.unique_name not in visible_repositories
+                ]
                 self.request.response.addNotification(
-                    'To ensure visibility, %s is now subscribed to: %s'
-                    % (visibility_info['person_name'],
-                       english_list(invisible_repositories)))
+                    "To ensure visibility, %s is now subscribed to: %s"
+                    % (
+                        visibility_info["person_name"],
+                        english_list(invisible_repositories),
+                    )
+                )
             # Success so we do a client redirect to the new mp page.
             if self.request.is_ajax:
                 self.request.response.setStatus(201)
                 self.request.response.setHeader(
-                    'Location', canonical_url(proposal))
+                    "Location", canonical_url(proposal)
+                )
                 return None
             else:
                 self.next_url = canonical_url(proposal)
@@ -427,31 +461,36 @@ class GitRefRegisterMergeProposalView(LaunchpadFormView):
             self.addError(str(error))
 
     def _validateRef(self, data, name):
-        ref = data['{}_git_ref'.format(name)]
+        ref = data["{}_git_ref".format(name)]
         if ref == self.context:
             self.setFieldError(
-                '%s_git_ref' % name,
+                "%s_git_ref" % name,
                 "The %s repository and path together cannot be the same "
-                "as the source repository and path." % name)
+                "as the source repository and path." % name,
+            )
         return ref.repository
 
     def validate(self, data):
         source_ref = self.context
         # The existence of target_git_repository is handled by the form
         # machinery.
-        if data.get('target_git_ref') is not None:
-            target_repository = self._validateRef(data, 'target')
+        if data.get("target_git_ref") is not None:
+            target_repository = self._validateRef(data, "target")
             if not target_repository.isRepositoryMergeable(
-                    source_ref.repository):
+                source_ref.repository
+            ):
                 self.setFieldError(
-                    'target_git_ref',
-                    "%s is not mergeable into this repository." %
-                    source_ref.repository.identity)
-        if data.get('prerequisite_git_ref') is not None:
-            prerequisite_repository = self._validateRef(data, 'prerequisite')
+                    "target_git_ref",
+                    "%s is not mergeable into this repository."
+                    % source_ref.repository.identity,
+                )
+        if data.get("prerequisite_git_ref") is not None:
+            prerequisite_repository = self._validateRef(data, "prerequisite")
             if not target_repository.isRepositoryMergeable(
-                    prerequisite_repository):
+                prerequisite_repository
+            ):
                 self.setFieldError(
-                    'prerequisite_git_ref',
-                    "This repository is not mergeable into %s." %
-                    target_repository.identity)
+                    "prerequisite_git_ref",
+                    "This repository is not mergeable into %s."
+                    % target_repository.identity,
+                )
diff --git a/lib/lp/code/browser/gitrepository.py b/lib/lp/code/browser/gitrepository.py
index 96a6ce0..8fa17b5 100644
--- a/lib/lp/code/browser/gitrepository.py
+++ b/lib/lp/code/browser/gitrepository.py
@@ -4,101 +4,84 @@
 """Git repository views."""
 
 __all__ = [
-    'GitRefBatchNavigator',
-    'GitRepositoriesBreadcrumb',
-    'GitRepositoryActivityView',
-    'GitRepositoryBreadcrumb',
-    'GitRepositoryContextMenu',
-    'GitRepositoryDeletionView',
-    'GitRepositoryEditInformationTypeView',
-    'GitRepositoryEditMenu',
-    'GitRepositoryEditReviewerView',
-    'GitRepositoryEditView',
-    'GitRepositoryNavigation',
-    'GitRepositoryPermissionsView',
-    'GitRepositoryURL',
-    'GitRepositoryView',
-    ]
+    "GitRefBatchNavigator",
+    "GitRepositoriesBreadcrumb",
+    "GitRepositoryActivityView",
+    "GitRepositoryBreadcrumb",
+    "GitRepositoryContextMenu",
+    "GitRepositoryDeletionView",
+    "GitRepositoryEditInformationTypeView",
+    "GitRepositoryEditMenu",
+    "GitRepositoryEditReviewerView",
+    "GitRepositoryEditView",
+    "GitRepositoryNavigation",
+    "GitRepositoryPermissionsView",
+    "GitRepositoryURL",
+    "GitRepositoryView",
+]
 
 import base64
 import binascii
 from collections import defaultdict
-from urllib.parse import (
-    urlsplit,
-    urlunsplit,
-    )
+from urllib.parse import urlsplit, urlunsplit
 
 from breezy import urlutils
 from lazr.lifecycle.event import ObjectModifiedEvent
 from lazr.lifecycle.snapshot import Snapshot
-from lazr.restful.interface import (
-    copy_field,
-    use_template,
-    )
+from lazr.restful.interface import copy_field, use_template
 from zope.component import getUtility
 from zope.event import notify
 from zope.formlib import form
 from zope.formlib.form import FormFields
 from zope.formlib.textwidgets import IntWidget
 from zope.formlib.widget import CustomWidgetFactory
-from zope.interface import (
-    implementer,
-    Interface,
-    providedBy,
-    )
+from zope.interface import Interface, implementer, providedBy
 from zope.publisher.interfaces.browser import IBrowserPublisher
-from zope.schema import (
-    Bool,
-    Choice,
-    Int,
-    )
+from zope.schema import Bool, Choice, Int
 from zope.schema.vocabulary import (
-    getVocabularyRegistry,
     SimpleTerm,
     SimpleVocabulary,
-    )
+    getVocabularyRegistry,
+)
 from zope.security.interfaces import Unauthorized
 
 from lp import _
 from lp.app.browser.informationtype import InformationTypePortletMixin
 from lp.app.browser.launchpadform import (
-    action,
     LaunchpadEditFormView,
     LaunchpadFormView,
-    )
-from lp.app.errors import (
-    NotFoundError,
-    UnexpectedFormData,
-    )
+    action,
+)
+from lp.app.errors import NotFoundError, UnexpectedFormData
 from lp.app.vocabularies import InformationTypeVocabulary
 from lp.app.widgets.itemswidgets import LaunchpadRadioWidgetWithDescription
 from lp.charms.browser.hascharmrecipes import HasCharmRecipesViewMixin
 from lp.code.browser.branch import CodeEditOwnerMixin
 from lp.code.browser.branchmergeproposal import (
     latest_proposals_for_each_branch,
-    )
+)
 from lp.code.browser.codeimport import CodeImportTargetMixin
 from lp.code.browser.sourcepackagerecipelisting import HasRecipesMenuMixin
 from lp.code.browser.widgets.gitgrantee import (
     GitGranteeDisplayWidget,
     GitGranteeField,
     GitGranteeWidget,
-    )
+)
 from lp.code.browser.widgets.gitrepositorytarget import (
     GitRepositoryTargetDisplayWidget,
     GitRepositoryTargetWidget,
-    )
+)
 from lp.code.enums import (
     GitGranteeType,
     GitRepositoryStatus,
     GitRepositoryType,
-    )
+)
 from lp.code.errors import (
     GitDefaultConflict,
     GitRepositoryCreationForbidden,
     GitRepositoryExists,
     GitTargetError,
-    )
+)
 from lp.code.interfaces.cibuild import ICIBuildSet
 from lp.code.interfaces.gitnamespace import get_git_namespace
 from lp.code.interfaces.gitref import IGitRefBatchNavigator
@@ -106,16 +89,13 @@ from lp.code.interfaces.gitrepository import (
     ContributorGitIdentity,
     IGitRepository,
     IGitRepositorySet,
-    )
+)
 from lp.code.interfaces.revisionstatus import (
     IRevisionStatusArtifactSet,
     IRevisionStatusReportSet,
-    )
+)
 from lp.code.vocabularies.gitrule import GitPermissionsVocabulary
-from lp.registry.interfaces.person import (
-    IPerson,
-    IPersonSet,
-    )
+from lp.registry.interfaces.person import IPerson, IPersonSet
 from lp.registry.vocabularies import UserTeamsParticipationPlusSelfVocabulary
 from lp.services.config import config
 from lp.services.database.constants import UTC_NOW
@@ -124,20 +104,20 @@ from lp.services.fields import UniqueField
 from lp.services.job.interfaces.job import JobStatus
 from lp.services.propertycache import cachedproperty
 from lp.services.webapp import (
-    canonical_url,
     ContextMenu,
-    enabled_with_permission,
     LaunchpadView,
     Link,
     Navigation,
     NavigationMenu,
+    canonical_url,
+    enabled_with_permission,
     stepthrough,
     stepto,
-    )
+)
 from lp.services.webapp.authorization import (
     check_permission,
     precache_permission_for_objects,
-    )
+)
 from lp.services.webapp.batching import TableBatchNavigator
 from lp.services.webapp.breadcrumb import Breadcrumb
 from lp.services.webapp.escaping import structured
@@ -148,8 +128,7 @@ from lp.services.webhooks.browser import WebhookTargetNavigationMixin
 from lp.snappy.browser.hassnaps import HasSnapsViewMixin
 from lp.soyuz.browser.build import get_build_by_id_str
 
-
-GIT_REPOSITORY_FORK_ENABLED = 'gitrepository.fork.enabled'
+GIT_REPOSITORY_FORK_ENABLED = "gitrepository.fork.enabled"
 
 
 @implementer(ICanonicalUrlData)
@@ -181,7 +160,6 @@ class GitRepositoriesBreadcrumb(Breadcrumb):
 
 
 class GitRepositoryBreadcrumb(Breadcrumb):
-
     @property
     def text(self):
         return self.context.git_identity
@@ -195,27 +173,26 @@ class GitRepositoryNavigation(WebhookTargetNavigationMixin, Navigation):
 
     usedfor = IGitRepository
 
-    @stepthrough('+status')
+    @stepthrough("+status")
     def traverse_status(self, id):
         try:
             report_id = int(id)
         except ValueError:
             raise NotFoundError(report_id)
-        report = getUtility(
-            IRevisionStatusReportSet).getByID(report_id)
+        report = getUtility(IRevisionStatusReportSet).getByID(report_id)
         if report is None:
             raise NotFoundError(report_id)
         return report
 
-    @stepthrough('+artifact')
+    @stepthrough("+artifact")
     def traverse_artifact(self, id):
         try:
             artifact_id = int(id)
         except ValueError:
             raise NotFoundError(artifact_id)
-        artifact = getUtility(
-            IRevisionStatusArtifactSet).getByRepositoryAndID(
-                self.context, artifact_id)
+        artifact = getUtility(IRevisionStatusArtifactSet).getByRepositoryAndID(
+            self.context, artifact_id
+        )
         if artifact is None:
             raise NotFoundError(artifact_id)
         return artifact
@@ -294,7 +271,7 @@ class GitRepositoryEditMenu(NavigationMenu):
         "access_tokens",
         "webhooks",
         "delete",
-        ]
+    ]
 
     @enabled_with_permission("launchpad.Edit")
     def edit(self):
@@ -325,8 +302,11 @@ class GitRepositoryEditMenu(NavigationMenu):
     def webhooks(self):
         text = "Manage webhooks"
         return Link(
-            "+webhooks", text, icon="edit",
-            enabled=bool(getFeatureFlag('webhooks.new.enabled')))
+            "+webhooks",
+            text,
+            icon="edit",
+            enabled=bool(getFeatureFlag("webhooks.new.enabled")),
+        )
 
     @enabled_with_permission("launchpad.Edit")
     def delete(self):
@@ -340,8 +320,14 @@ class GitRepositoryContextMenu(ContextMenu, HasRecipesMenuMixin):
     usedfor = IGitRepository
     facet = "branches"
     links = [
-        "add_subscriber", "create_recipe", "edit_import", "source",
-        "subscription", "view_recipes", "visibility"]
+        "add_subscriber",
+        "create_recipe",
+        "edit_import",
+        "source",
+        "subscription",
+        "view_recipes",
+        "visibility",
+    ]
 
     @enabled_with_permission("launchpad.AnyPerson")
     def subscription(self):
@@ -375,8 +361,9 @@ class GitRepositoryContextMenu(ContextMenu, HasRecipesMenuMixin):
     def edit_import(self):
         text = "Edit import source or review import"
         enabled = (
-            self.context.repository_type == GitRepositoryType.IMPORTED and
-            check_permission("launchpad.Edit", self.context.code_import))
+            self.context.repository_type == GitRepositoryType.IMPORTED
+            and check_permission("launchpad.Edit", self.context.code_import)
+        )
         return Link("+edit-import", text, icon="edit", enabled=enabled)
 
     def create_recipe(self):
@@ -393,8 +380,10 @@ class GitRefBatchNavigator(TableBatchNavigator):
     def __init__(self, view, context):
         self.context = context
         super().__init__(
-            self.context.branches_by_date, view.request,
-            size=config.launchpad.branchlisting_batch_size)
+            self.context.branches_by_date,
+            view.request,
+            size=config.launchpad.branchlisting_batch_size,
+        )
         self.view = view
         self.column_count = 3
 
@@ -409,10 +398,13 @@ class GitRefBatchNavigator(TableBatchNavigator):
             return "listing sortable"
 
 
-class GitRepositoryView(InformationTypePortletMixin, LaunchpadView,
-                        HasSnapsViewMixin, HasCharmRecipesViewMixin,
-                        CodeImportTargetMixin):
-
+class GitRepositoryView(
+    InformationTypePortletMixin,
+    LaunchpadView,
+    HasSnapsViewMixin,
+    HasCharmRecipesViewMixin,
+    CodeImportTargetMixin,
+):
     @property
     def page_title(self):
         return self.context.display_name
@@ -428,7 +420,8 @@ class GitRepositoryView(InformationTypePortletMixin, LaunchpadView,
         authorised_people = [self.context.owner]
         if self.user is not None:
             precache_permission_for_objects(
-                self.request, "launchpad.LimitedView", authorised_people)
+                self.request, "launchpad.LimitedView", authorised_people
+            )
 
     @property
     def git_ssh_url(self):
@@ -446,9 +439,11 @@ class GitRepositoryView(InformationTypePortletMixin, LaunchpadView,
         contributor = ContributorGitIdentity(
             owner=self.user,
             target=self.context.target,
-            repository=self.context)
+            repository=self.context,
+        )
         base_url = urlutils.join(
-            config.codehosting.git_ssh_root, contributor.shortened_path)
+            config.codehosting.git_ssh_root, contributor.shortened_path
+        )
         url = list(urlsplit(base_url))
         url[1] = "{}@{}".format(self.user.name, url[1])
         return urlunsplit(url)
@@ -457,8 +452,9 @@ class GitRepositoryView(InformationTypePortletMixin, LaunchpadView,
     def user_can_push(self):
         """Whether the user can push to this branch."""
         return (
-            self.context.repository_type == GitRepositoryType.HOSTED and
-            check_permission("launchpad.Edit", self.context))
+            self.context.repository_type == GitRepositoryType.HOSTED
+            and check_permission("launchpad.Edit", self.context)
+        )
 
     def branches(self):
         """All branches in this repository, sorted for display."""
@@ -470,17 +466,19 @@ class GitRepositoryView(InformationTypePortletMixin, LaunchpadView,
         count = self.context.recipes.count()
         if count == 0:
             # Nothing to link to.
-            return 'No recipes using this repository.'
+            return "No recipes using this repository."
         elif count == 1:
             # Link to the single recipe.
             return structured(
                 '<a href="%s">1 recipe</a> using this repository.',
-                canonical_url(self.context.recipes.one())).escapedtext
+                canonical_url(self.context.recipes.one()),
+            ).escapedtext
         else:
             # Link to a recipe listing.
             return structured(
                 '<a href="+recipes">%s recipes</a> using this repository.',
-                count).escapedtext
+                count,
+            ).escapedtext
 
     @property
     def is_imported(self):
@@ -490,17 +488,20 @@ class GitRepositoryView(InformationTypePortletMixin, LaunchpadView,
     @cachedproperty
     def landing_candidates(self):
         candidates = self.context.getPrecachedLandingCandidates(self.user)
-        return [proposal for proposal in candidates
-                if check_permission("launchpad.View", proposal)]
+        return [
+            proposal
+            for proposal in candidates
+            if check_permission("launchpad.View", proposal)
+        ]
 
     def _getBranchCountText(self, count):
         """Help to show user friendly text."""
         if count == 0:
-            return 'No branches'
+            return "No branches"
         elif count == 1:
-            return '1 branch'
+            return "1 branch"
         else:
-            return '%s branches' % count
+            return "%s branches" % count
 
     @cachedproperty
     def landing_candidate_count_text(self):
@@ -510,7 +511,8 @@ class GitRepositoryView(InformationTypePortletMixin, LaunchpadView,
     def landing_targets(self):
         """Return a filtered list of active landing targets."""
         targets = self.context.getPrecachedLandingTargets(
-            self.user, only_active=True)
+            self.user, only_active=True
+        )
         return latest_proposals_for_each_branch(targets)
 
     @property
@@ -541,7 +543,7 @@ class GitRepositoryView(InformationTypePortletMixin, LaunchpadView,
 
     @property
     def fork_url(self):
-        return canonical_url(self.context, view_name='+fork')
+        return canonical_url(self.context, view_name="+fork")
 
 
 class GitRepositoryForkView(LaunchpadEditFormView):
@@ -558,26 +560,30 @@ class GitRepositoryForkView(LaunchpadEditFormView):
     def setUpFields(self):
         super().setUpFields()
         owner_field = Choice(
-            vocabulary='AllUserTeamsParticipationPlusSelf',
-            title='Fork to the following owner', required=True,
-            __name__='owner')
+            vocabulary="AllUserTeamsParticipationPlusSelf",
+            title="Fork to the following owner",
+            required=True,
+            __name__="owner",
+        )
         self.form_fields += FormFields(owner_field)
 
     @property
     def initial_values(self):
-        return {'owner': self.user}
+        return {"owner": self.user}
 
     def validate(self, data):
         new_owner = data.get("owner")
         if not new_owner or not self.user.inTeam(new_owner):
             self.setFieldError(
                 "owner",
-                "You should select a valid user to fork the repository.")
+                "You should select a valid user to fork the repository.",
+            )
 
-    @action('Fork it', name='fork')
+    @action("Fork it", name="fork")
     def fork(self, action, data):
         forked = getUtility(IGitRepositorySet).fork(
-            self.context, self.user, data.get("owner"))
+            self.context, self.user, data.get("owner")
+        )
         self.request.response.addNotification("Repository forked.")
         self.next_url = canonical_url(forked)
 
@@ -592,7 +598,7 @@ class GitRepositoryRescanView(LaunchpadEditFormView):
 
     field_names = []
 
-    @action('Rescan', name='rescan')
+    @action("Rescan", name="rescan")
     def rescan(self, action, data):
         self.context.rescan()
         self.request.response.addNotification("Repository scan scheduled")
@@ -624,14 +630,18 @@ class GitRepositoryEditFormView(LaunchpadEditFormView):
             This is necessary to make various fields editable that are not
             normally editable through the interface.
             """
+
             use_template(IGitRepository, include=["default_branch"])
             information_type = copy_field(
-                IGitRepository["information_type"], readonly=False,
-                vocabulary=InformationTypeVocabulary(types=info_types))
+                IGitRepository["information_type"],
+                readonly=False,
+                vocabulary=InformationTypeVocabulary(types=info_types),
+            )
             name = copy_field(IGitRepository["name"], readonly=False)
             owner = copy_field(IGitRepository["owner"], readonly=False)
             owner_default = copy_field(
-                IGitRepository["owner_default"], readonly=False)
+                IGitRepository["owner_default"], readonly=False
+            )
             reviewer = copy_field(IGitRepository["reviewer"], required=True)
             target = copy_field(IGitRepository["target"], readonly=False)
 
@@ -650,8 +660,11 @@ class GitRepositoryEditFormView(LaunchpadEditFormView):
         """See `LaunchpadFormView`."""
         return {self.schema: self.context}
 
-    @action("Change Git Repository", name="change",
-            failure=LaunchpadFormView.ajax_failure_handler)
+    @action(
+        "Change Git Repository",
+        name="change",
+        failure=LaunchpadFormView.ajax_failure_handler,
+    )
     def change_action(self, action, data):
         # If the owner has changed, add an explicit notification.  We take
         # our own snapshot here to make sure that the snapshot records
@@ -660,7 +673,8 @@ class GitRepositoryEditFormView(LaunchpadEditFormView):
         # updateContextFromData.
         changed = False
         repository_before_modification = Snapshot(
-            self.context, providing=providedBy(self.context))
+            self.context, providing=providedBy(self.context)
+        )
         if "name" in data:
             name = data.pop("name")
             if name != self.context.name:
@@ -672,12 +686,14 @@ class GitRepositoryEditFormView(LaunchpadEditFormView):
                 self.context.setOwner(owner, self.user)
                 changed = True
                 self.request.response.addNotification(
-                    "The repository owner has been changed to %s (%s)" %
-                    (owner.displayname, owner.name))
+                    "The repository owner has been changed to %s (%s)"
+                    % (owner.displayname, owner.name)
+                )
         if "information_type" in data:
             information_type = data.pop("information_type")
             self.context.transitionToInformationType(
-                information_type, self.user)
+                information_type, self.user
+            )
         if "target" in data:
             target = data.pop("target")
             if target is None:
@@ -692,11 +708,13 @@ class GitRepositoryEditFormView(LaunchpadEditFormView):
                 if IPerson.providedBy(target):
                     self.request.response.addNotification(
                         "This repository is now a personal repository for %s "
-                        "(%s)" % (target.displayname, target.name))
+                        "(%s)" % (target.displayname, target.name)
+                    )
                 else:
                     self.request.response.addNotification(
-                        "The repository target has been changed to %s (%s)" %
-                        (target.displayname, target.name))
+                        "The repository target has been changed to %s (%s)"
+                        % (target.displayname, target.name)
+                    )
         if "reviewer" in data:
             reviewer = data.pop("reviewer")
             if reviewer != self.context.code_reviewer:
@@ -708,8 +726,10 @@ class GitRepositoryEditFormView(LaunchpadEditFormView):
                 changed = True
         if "owner_default" in data:
             owner_default = data.pop("owner_default")
-            if (self.context.namespace.has_defaults and
-                    owner_default != self.context.owner_default):
+            if (
+                self.context.namespace.has_defaults
+                and owner_default != self.context.owner_default
+            ):
                 self.context.setOwnerDefault(owner_default)
 
         if self.updateContextFromData(data, notify_modified=False):
@@ -719,9 +739,13 @@ class GitRepositoryEditFormView(LaunchpadEditFormView):
             # Notify that the object has changed with the snapshot that was
             # taken earlier.
             field_names = [
-                form_field.__name__ for form_field in self.form_fields]
-            notify(ObjectModifiedEvent(
-                self.context, repository_before_modification, field_names))
+                form_field.__name__ for form_field in self.form_fields
+            ]
+            notify(
+                ObjectModifiedEvent(
+                    self.context, repository_before_modification, field_names
+                )
+            )
             # Only specify that the context was modified if there
             # was in fact a change.
             self.context.date_last_modified = UTC_NOW
@@ -767,13 +791,14 @@ class GitRepositoryEditView(CodeEditOwnerMixin, GitRepositoryEditFormView):
         "information_type",
         "owner_default",
         "default_branch",
-        ]
+    ]
 
     custom_widget_information_type = LaunchpadRadioWidgetWithDescription
 
     any_owner_description = _(
         "As an administrator you are able to assign this repository to any "
-        "person or team.")
+        "person or team."
+    )
 
     def setUpFields(self):
         super().setUpFields()
@@ -784,13 +809,18 @@ class GitRepositoryEditView(CodeEditOwnerMixin, GitRepositoryEditFormView):
         if check_permission("launchpad.Admin", repository):
             owner_field = self.schema["owner"]
             any_owner_choice = Choice(
-                __name__="owner", title=owner_field.title,
+                __name__="owner",
+                title=owner_field.title,
                 description=_(
                     "As an administrator you are able to assign this "
-                    "repository to any person or team."),
-                required=True, vocabulary="ValidPersonOrTeam")
+                    "repository to any person or team."
+                ),
+                required=True,
+                vocabulary="ValidPersonOrTeam",
+            )
             any_owner_field = form.Fields(
-                any_owner_choice, render_context=self.render_context)
+                any_owner_choice, render_context=self.render_context
+            )
             # Replace the normal owner field with a more permissive vocab.
             self.form_fields = self.form_fields.omit("owner")
             self.form_fields = any_owner_field + self.form_fields
@@ -802,16 +832,21 @@ class GitRepositoryEditView(CodeEditOwnerMixin, GitRepositoryEditFormView):
             if not self.user.inTeam(self.context.owner):
                 vocab = UserTeamsParticipationPlusSelfVocabulary()
                 owner = self.context.owner
-                terms = [SimpleTerm(
-                    owner, owner.name, owner.unique_displayname)]
+                terms = [
+                    SimpleTerm(owner, owner.name, owner.unique_displayname)
+                ]
                 terms.extend([term for term in vocab])
                 owner_field = self.schema["owner"]
                 owner_choice = Choice(
-                    __name__="owner", title=owner_field.title,
+                    __name__="owner",
+                    title=owner_field.title,
                     description=owner_field.description,
-                    required=True, vocabulary=SimpleVocabulary(terms))
+                    required=True,
+                    vocabulary=SimpleVocabulary(terms),
+                )
                 new_owner_field = form.Fields(
-                    owner_choice, render_context=self.render_context)
+                    owner_choice, render_context=self.render_context
+                )
                 # Replace the normal owner field with a more permissive vocab.
                 self.form_fields = self.form_fields.omit("owner")
                 self.form_fields = new_owner_field + self.form_fields
@@ -828,9 +863,10 @@ class GitRepositoryEditView(CodeEditOwnerMixin, GitRepositoryEditFormView):
         if self.context.target_default:
             self.widgets["target"].hint = (
                 "This is the default repository for this target, so it "
-                "cannot be moved to another target.")
+                "cannot be moved to another target."
+            )
         if self.context.default_branch:
-            self.widgets['default_branch'].context.required = True
+            self.widgets["default_branch"].context.required = True
 
     def _setRepositoryExists(self, existing_repository, field_name="name"):
         owner = existing_repository.owner
@@ -840,8 +876,10 @@ class GitRepositoryEditView(CodeEditOwnerMixin, GitRepositoryEditFormView):
             prefix = "%s already has" % owner.displayname
         message = structured(
             "%s a repository for <em>%s</em> called <em>%s</em>.",
-            prefix, existing_repository.target.displayname,
-            existing_repository.name)
+            prefix,
+            existing_repository.target.displayname,
+            existing_repository.name,
+        )
         self.setFieldError(field_name, message)
 
     def validate(self, data):
@@ -851,34 +889,44 @@ class GitRepositoryEditView(CodeEditOwnerMixin, GitRepositoryEditFormView):
             target = data["target"]
             if target is None:
                 target = owner
-            if (name != self.context.name or
-                    owner != self.context.owner or
-                    target != self.context.target):
+            if (
+                name != self.context.name
+                or owner != self.context.owner
+                or target != self.context.target
+            ):
                 namespace = get_git_namespace(target, owner)
                 try:
                     namespace.validateMove(self.context, self.user, name=name)
                 except GitRepositoryCreationForbidden:
                     self.addError(
-                        "%s is not allowed to own Git repositories in %s." %
-                        (owner.displayname, target.displayname))
+                        "%s is not allowed to own Git repositories in %s."
+                        % (owner.displayname, target.displayname)
+                    )
                 except GitRepositoryExists as e:
                     self._setRepositoryExists(e.existing_repository)
                 except GitDefaultConflict as e:
                     self.addError(str(e))
-        if (self.context.target_default and "target" in data and
-                data["target"] != self.context.target):
+        if (
+            self.context.target_default
+            and "target" in data
+            and data["target"] != self.context.target
+        ):
             self.setFieldError(
                 "target",
                 "The default repository for a target cannot be moved to "
-                "another target.")
+                "another target.",
+            )
         if "default_branch" in data:
             default_branch = data["default_branch"]
-            if (default_branch is not None and
-                    self.context.getRefByPath(default_branch) is None):
+            if (
+                default_branch is not None
+                and self.context.getRefByPath(default_branch) is None
+            ):
                 self.setFieldError(
                     "default_branch",
                     "This repository does not contain a reference named "
-                    "'%s'." % default_branch)
+                    "'%s'." % default_branch,
+                )
 
 
 @implementer(IBrowserPublisher)
@@ -911,14 +959,18 @@ def encode_form_field_id(value):
     We use a modified version of base32 which fits into CSS identifiers and
     so doesn't cause FormattersAPI.zope_css_id to do unhelpful things.
     """
-    return base64.b32encode(
-        value.encode("UTF-8")).decode("UTF-8").replace("=", "_")
+    return (
+        base64.b32encode(value.encode("UTF-8"))
+        .decode("UTF-8")
+        .replace("=", "_")
+    )
 
 
 def decode_form_field_id(encoded):
     """Inverse of `encode_form_field_id`."""
-    return base64.b32decode(
-        encoded.replace("_", "=").encode("UTF-8")).decode("UTF-8")
+    return base64.b32decode(encoded.replace("_", "=").encode("UTF-8")).decode(
+        "UTF-8"
+    )
 
 
 class GitRulePatternField(UniqueField):
@@ -942,8 +994,9 @@ class GitRulePatternField(UniqueField):
     def unchanged(self, input):
         """See `UniqueField`."""
         return (
-            self.rule is not None and
-            self.ref_prefix + input == self.rule.ref_pattern)
+            self.rule is not None
+            and self.ref_prefix + input == self.rule.ref_pattern
+        )
 
     def set(self, object, value):
         """See `IField`."""
@@ -975,21 +1028,27 @@ class GitRepositoryPermissionsView(LaunchpadFormView):
     @property
     def branch_rules(self):
         return [
-            rule for rule in self.rules
-            if rule.ref_pattern.startswith(self.heads_prefix)]
+            rule
+            for rule in self.rules
+            if rule.ref_pattern.startswith(self.heads_prefix)
+        ]
 
     @property
     def tag_rules(self):
         return [
-            rule for rule in self.rules
-            if rule.ref_pattern.startswith(self.tags_prefix)]
+            rule
+            for rule in self.rules
+            if rule.ref_pattern.startswith(self.tags_prefix)
+        ]
 
     @property
     def other_rules(self):
         return [
-            rule for rule in self.rules
-            if not rule.ref_pattern.startswith(self.heads_prefix) and
-               not rule.ref_pattern.startswith(self.tags_prefix)]
+            rule
+            for rule in self.rules
+            if not rule.ref_pattern.startswith(self.heads_prefix)
+            and not rule.ref_pattern.startswith(self.tags_prefix)
+        ]
 
     def _getRuleGrants(self, rule):
         def grantee_key(grant):
@@ -1004,7 +1063,7 @@ class GitRepositoryPermissionsView(LaunchpadFormView):
         """Parse a pattern into a prefix and the displayed portion."""
         for prefix in (self.heads_prefix, self.tags_prefix):
             if ref_pattern.startswith(prefix):
-                return prefix, ref_pattern[len(prefix):]
+                return prefix, ref_pattern[len(prefix) :]
         return "", ref_pattern
 
     def _getFieldName(self, name, ref_pattern, grantee=None):
@@ -1030,7 +1089,8 @@ class GitRepositoryPermissionsView(LaunchpadFormView):
         field_bits = field_name.split(".")
         if len(field_bits) < 2:
             raise UnexpectedFormData(
-                "Cannot parse field name: %s" % field_name)
+                "Cannot parse field name: %s" % field_name
+            )
         field_type = field_bits[0]
         try:
             ref_pattern = decode_form_field_id(field_bits[1])
@@ -1038,7 +1098,8 @@ class GitRepositoryPermissionsView(LaunchpadFormView):
         # but binascii.Error on Python 3.
         except (TypeError, binascii.Error):
             raise UnexpectedFormData(
-                "Cannot parse field name: %s" % field_name)
+                "Cannot parse field name: %s" % field_name
+            )
         if len(field_bits) > 2:
             grantee_id = field_bits[2]
             if grantee_id.startswith("_"):
@@ -1069,7 +1130,8 @@ class GitRepositoryPermissionsView(LaunchpadFormView):
             # This should never happen, because GitPermissionsVocabulary
             # adds a custom term for the context grant if necessary.
             raise AssertionError(
-                "Could not find GitPermissions term for %r" % grant)
+                "Could not find GitPermissions term for %r" % grant
+            )
 
     def setUpFields(self):
         """See `LaunchpadFormView`."""
@@ -1084,7 +1146,7 @@ class GitRepositoryPermissionsView(LaunchpadFormView):
             self.heads_prefix: "can_push",
             self.tags_prefix: "can_create",
             "": "can_push",
-            }
+        }
 
         for rule_index, rule in enumerate(self.rules):
             # Remove the usual branch/tag prefixes from patterns.  The full
@@ -1094,71 +1156,113 @@ class GitRepositoryPermissionsView(LaunchpadFormView):
             position_fields.append(
                 Int(
                     __name__=self._getFieldName("position", ref_pattern),
-                    required=True, readonly=False, default=rule_index + 1))
+                    required=True,
+                    readonly=False,
+                    default=rule_index + 1,
+                )
+            )
             pattern_fields.append(
                 GitRulePatternField(
                     __name__=self._getFieldName("pattern", ref_pattern),
-                    required=True, readonly=False, ref_prefix=ref_prefix,
-                    rule=rule, default=short_pattern))
+                    required=True,
+                    readonly=False,
+                    ref_prefix=ref_prefix,
+                    rule=rule,
+                    default=short_pattern,
+                )
+            )
             delete_fields.append(
                 Bool(
                     __name__=self._getFieldName("delete", ref_pattern),
-                    readonly=False, default=False))
+                    readonly=False,
+                    default=False,
+                )
+            )
             for grant in self._getRuleGrants(rule):
                 grantee = grant.combined_grantee
                 readonly_grantee_fields.append(
                     GitGranteeField(
                         __name__=self._getFieldName(
-                            "grantee", ref_pattern, grantee),
-                        required=False, readonly=True, default=grantee,
-                        rule=rule))
+                            "grantee", ref_pattern, grantee
+                        ),
+                        required=False,
+                        readonly=True,
+                        default=grantee,
+                        rule=rule,
+                    )
+                )
                 permissions_fields.append(
                     Choice(
                         __name__=self._getFieldName(
-                            "permissions", ref_pattern, grantee),
+                            "permissions", ref_pattern, grantee
+                        ),
                         source=GitPermissionsVocabulary(grant),
                         readonly=False,
-                        default=self._getPermissionsTerm(grant).value))
+                        default=self._getPermissionsTerm(grant).value,
+                    )
+                )
                 delete_fields.append(
                     Bool(
                         __name__=self._getFieldName(
-                            "delete", ref_pattern, grantee),
-                        readonly=False, default=False))
+                            "delete", ref_pattern, grantee
+                        ),
+                        readonly=False,
+                        default=False,
+                    )
+                )
             grantee_fields.append(
                 GitGranteeField(
                     __name__=self._getFieldName("grantee", ref_pattern),
-                    required=False, readonly=False, rule=rule))
+                    required=False,
+                    readonly=False,
+                    rule=rule,
+                )
+            )
             permissions_vocabulary = GitPermissionsVocabulary(rule)
             permissions_fields.append(
                 Choice(
-                    __name__=self._getFieldName(
-                        "permissions", ref_pattern),
-                    source=permissions_vocabulary, readonly=False,
+                    __name__=self._getFieldName("permissions", ref_pattern),
+                    source=permissions_vocabulary,
+                    readonly=False,
                     default=permissions_vocabulary.getTermByToken(
-                        default_permissions_by_prefix[ref_prefix]).value))
+                        default_permissions_by_prefix[ref_prefix]
+                    ).value,
+                )
+            )
         for ref_prefix in (self.heads_prefix, self.tags_prefix):
             position_fields.append(
                 Int(
                     __name__=self._getFieldName("new-position", ref_prefix),
-                    required=False, readonly=True))
+                    required=False,
+                    readonly=True,
+                )
+            )
             pattern_fields.append(
                 GitRulePatternField(
                     __name__=self._getFieldName("new-pattern", ref_prefix),
-                    required=False, readonly=False, ref_prefix=ref_prefix))
+                    required=False,
+                    readonly=False,
+                    ref_prefix=ref_prefix,
+                )
+            )
 
         self.form_fields = (
             form.FormFields(
                 *position_fields,
-                custom_widget=CustomWidgetFactory(IntWidget, displayWidth=2)) +
-            form.FormFields(*pattern_fields) +
-            form.FormFields(*delete_fields) +
-            form.FormFields(
+                custom_widget=CustomWidgetFactory(IntWidget, displayWidth=2),
+            )
+            + form.FormFields(*pattern_fields)
+            + form.FormFields(*delete_fields)
+            + form.FormFields(
                 *readonly_grantee_fields,
-                custom_widget=CustomWidgetFactory(GitGranteeDisplayWidget)) +
-            form.FormFields(
+                custom_widget=CustomWidgetFactory(GitGranteeDisplayWidget),
+            )
+            + form.FormFields(
                 *grantee_fields,
-                custom_widget=CustomWidgetFactory(GitGranteeWidget)) +
-            form.FormFields(*permissions_fields))
+                custom_widget=CustomWidgetFactory(GitGranteeWidget),
+            )
+            + form.FormFields(*permissions_fields)
+        )
 
     def setUpWidgets(self, context=None):
         """See `LaunchpadFormView`."""
@@ -1174,63 +1278,75 @@ class GitRepositoryPermissionsView(LaunchpadFormView):
     def getRuleWidgets(self, rule):
         widgets_by_name = {widget.name: widget for widget in self.widgets}
         ref_pattern = rule.ref_pattern
-        position_field_name = (
-            "field." + self._getFieldName("position", ref_pattern))
-        pattern_field_name = (
-            "field." + self._getFieldName("pattern", ref_pattern))
-        delete_field_name = (
-            "field." + self._getFieldName("delete", ref_pattern))
+        position_field_name = "field." + self._getFieldName(
+            "position", ref_pattern
+        )
+        pattern_field_name = "field." + self._getFieldName(
+            "pattern", ref_pattern
+        )
+        delete_field_name = "field." + self._getFieldName(
+            "delete", ref_pattern
+        )
         grant_widgets = []
         for grant in self._getRuleGrants(rule):
             grantee = grant.combined_grantee
-            grantee_field_name = (
-                "field." + self._getFieldName("grantee", ref_pattern, grantee))
-            permissions_field_name = (
-                "field." +
-                self._getFieldName("permissions", ref_pattern, grantee))
-            delete_grant_field_name = (
-                "field." + self._getFieldName("delete", ref_pattern, grantee))
-            grant_widgets.append({
-                "grantee": widgets_by_name[grantee_field_name],
-                "permissions": widgets_by_name[permissions_field_name],
-                "delete": widgets_by_name[delete_grant_field_name],
-                })
-        new_grantee_field_name = (
-            "field." + self._getFieldName("grantee", ref_pattern))
-        new_permissions_field_name = (
-            "field." + self._getFieldName("permissions", ref_pattern))
+            grantee_field_name = "field." + self._getFieldName(
+                "grantee", ref_pattern, grantee
+            )
+            permissions_field_name = "field." + self._getFieldName(
+                "permissions", ref_pattern, grantee
+            )
+            delete_grant_field_name = "field." + self._getFieldName(
+                "delete", ref_pattern, grantee
+            )
+            grant_widgets.append(
+                {
+                    "grantee": widgets_by_name[grantee_field_name],
+                    "permissions": widgets_by_name[permissions_field_name],
+                    "delete": widgets_by_name[delete_grant_field_name],
+                }
+            )
+        new_grantee_field_name = "field." + self._getFieldName(
+            "grantee", ref_pattern
+        )
+        new_permissions_field_name = "field." + self._getFieldName(
+            "permissions", ref_pattern
+        )
         new_grant_widgets = {
             "grantee": widgets_by_name[new_grantee_field_name],
             "permissions": widgets_by_name[new_permissions_field_name],
-            }
+        }
         return {
             "position": widgets_by_name[position_field_name],
             "pattern": widgets_by_name[pattern_field_name],
             "delete": widgets_by_name.get(delete_field_name),
             "grants": grant_widgets,
             "new_grant": new_grant_widgets,
-            }
+        }
 
     def getNewRuleWidgets(self, ref_prefix):
         widgets_by_name = {widget.name: widget for widget in self.widgets}
-        new_position_field_name = (
-            "field." + self._getFieldName("new-position", ref_prefix))
-        new_pattern_field_name = (
-            "field." + self._getFieldName("new-pattern", ref_prefix))
+        new_position_field_name = "field." + self._getFieldName(
+            "new-position", ref_prefix
+        )
+        new_pattern_field_name = "field." + self._getFieldName(
+            "new-pattern", ref_prefix
+        )
         return {
             "position": widgets_by_name[new_position_field_name],
             "pattern": widgets_by_name[new_pattern_field_name],
-            }
+        }
 
     def parseData(self, data):
         """Rearrange form data to make it easier to process."""
         parsed_data = {
             "rules": {},
             "grants": defaultdict(list),
-            }
+        }
 
         for field_name in sorted(
-                name for name in data if name.split(".")[0] == "pattern"):
+            name for name in data if name.split(".")[0] == "pattern"
+        ):
             _, ref_pattern, _ = self._parseFieldName(field_name)
             prefix, _ = self._parseRefPattern(ref_pattern)
             position_field_name = self._getFieldName("position", ref_pattern)
@@ -1241,15 +1357,19 @@ class GitRepositoryPermissionsView(LaunchpadFormView):
             else:
                 position = max(0, data[position_field_name] - 1)
                 action = "change"
-            parsed_data["rules"].setdefault(ref_pattern, {
-                "position": position,
-                "pattern": ref_pattern,
-                "new_pattern": prefix + data[field_name],
-                "action": action,
-                })
+            parsed_data["rules"].setdefault(
+                ref_pattern,
+                {
+                    "position": position,
+                    "pattern": ref_pattern,
+                    "new_pattern": prefix + data[field_name],
+                    "action": action,
+                },
+            )
 
         for field_name in sorted(
-                name for name in data if name.split(".")[0] == "new-pattern"):
+            name for name in data if name.split(".")[0] == "new-pattern"
+        ):
             _, prefix, _ = self._parseFieldName(field_name)
             position_field_name = self._getFieldName("position", prefix)
             if not data[field_name]:
@@ -1258,18 +1378,23 @@ class GitRepositoryPermissionsView(LaunchpadFormView):
                 position = max(0, data[position_field_name] - 1)
             else:
                 position = None
-            parsed_data["rules"].setdefault(prefix, {
-                "position": position,
-                "pattern": prefix + data[field_name],
-                "action": "add",
-                })
+            parsed_data["rules"].setdefault(
+                prefix,
+                {
+                    "position": position,
+                    "pattern": prefix + data[field_name],
+                    "action": "add",
+                },
+            )
 
         for field_name in sorted(
-                name for name in data if name.split(".")[0] == "permissions"):
+            name for name in data if name.split(".")[0] == "permissions"
+        ):
             _, ref_pattern, grantee = self._parseFieldName(field_name)
             grantee_field_name = self._getFieldName("grantee", ref_pattern)
             delete_field_name = self._getFieldName(
-                "delete", ref_pattern, grantee)
+                "delete", ref_pattern, grantee
+            )
             if grantee is None:
                 grantee = data.get(grantee_field_name)
                 if grantee is None:
@@ -1279,11 +1404,13 @@ class GitRepositoryPermissionsView(LaunchpadFormView):
                 action = "delete"
             else:
                 action = "change"
-            parsed_data["grants"][ref_pattern].append({
-                "grantee": grantee,
-                "permissions": data[field_name],
-                "action": action,
-                })
+            parsed_data["grants"][ref_pattern].append(
+                {
+                    "grantee": grantee,
+                    "permissions": data[field_name],
+                    "action": action,
+                }
+            )
 
         return parsed_data
 
@@ -1293,7 +1420,8 @@ class GitRepositoryPermissionsView(LaunchpadFormView):
         rule_map = {rule.ref_pattern: rule for rule in self.repository.rules}
         grant_map = {
             (grant.rule.ref_pattern, grant.combined_grantee): grant
-            for grant in self.repository.grants}
+            for grant in self.repository.grants
+        }
 
         # Patterns must be processed in rule order so that position changes
         # work in a reasonably natural way.  Process new rules last.
@@ -1307,19 +1435,23 @@ class GitRepositoryPermissionsView(LaunchpadFormView):
                 # already been deleted by somebody else.
                 ordered_rules.append((ref_pattern, parsed_rule, rule.position))
         ordered_rules.sort(
-            key=lambda item: (item[1]["action"] != "add", item[2], item[0]))
+            key=lambda item: (item[1]["action"] != "add", item[2], item[0])
+        )
 
         for ref_pattern, parsed_rule, position in ordered_rules:
             rule = rule_map.get(parsed_rule["pattern"])
             action = parsed_rule["action"]
             if action not in ("add", "change", "delete"):
                 raise AssertionError(
-                    "unknown action: %s" % parsed_rule["action"])
+                    "unknown action: %s" % parsed_rule["action"]
+                )
 
             if action == "add" and rule is None:
                 rule = repository.addRule(
-                    parsed_rule["pattern"], self.user,
-                    position=parsed_rule["position"])
+                    parsed_rule["pattern"],
+                    self.user,
+                    position=parsed_rule["position"],
+                )
                 if ref_pattern == self.tags_prefix:
                     # Tags are a special case: on creation, they
                     # automatically get a grant of create permissions to
@@ -1327,11 +1459,14 @@ class GitRepositoryPermissionsView(LaunchpadFormView):
                     # ability of the repository owner to push protected
                     # references).
                     rule.addGrant(
-                        GitGranteeType.REPOSITORY_OWNER, self.user,
-                        can_create=True)
+                        GitGranteeType.REPOSITORY_OWNER,
+                        self.user,
+                        can_create=True,
+                    )
             elif action == "change" and rule is not None:
                 self.repository.moveRule(
-                    rule, parsed_rule["position"], self.user)
+                    rule, parsed_rule["position"], self.user
+                )
                 if parsed_rule["new_pattern"] != rule.ref_pattern:
                     with notify_modified(rule, ["ref_pattern"]):
                         rule.ref_pattern = parsed_rule["new_pattern"]
@@ -1340,13 +1475,19 @@ class GitRepositoryPermissionsView(LaunchpadFormView):
                 rule_map[parsed_rule["pattern"]] = None
             else:
                 raise AssertionError(
-                    "unknown action: %s" % parsed_rule["action"])
+                    "unknown action: %s" % parsed_rule["action"]
+                )
 
         for ref_pattern, parsed_grants in sorted(
-                parsed_data["grants"].items()):
+            parsed_data["grants"].items()
+        ):
             if ref_pattern not in rule_map:
-                self.addError(structured(
-                    "Cannot edit grants for nonexistent rule %s", ref_pattern))
+                self.addError(
+                    structured(
+                        "Cannot edit grants for nonexistent rule %s",
+                        ref_pattern,
+                    )
+                )
                 return
             rule = rule_map.get(ref_pattern)
             if rule is None:
@@ -1358,21 +1499,27 @@ class GitRepositoryPermissionsView(LaunchpadFormView):
                 action = parsed_grant["action"]
                 if action not in ("add", "change", "delete"):
                     raise AssertionError(
-                        "unknown action: %s" % parsed_rule["action"])
+                        "unknown action: %s" % parsed_rule["action"]
+                    )
 
                 if action == "add" and grant is None:
                     rule.addGrant(
-                        parsed_grant["grantee"], self.user,
-                        permissions=parsed_grant["permissions"])
-                elif (action in ("add", "change") and grant is not None and
-                      parsed_grant["permissions"] != grant.permissions):
+                        parsed_grant["grantee"],
+                        self.user,
+                        permissions=parsed_grant["permissions"],
+                    )
+                elif (
+                    action in ("add", "change")
+                    and grant is not None
+                    and parsed_grant["permissions"] != grant.permissions
+                ):
                     # Make the requested changes.  This can happen in the
                     # add case if somebody else added the grant since the
                     # form was last rendered, in which case updating it with
                     # the permissions from this request seems best.
                     with notify_modified(
-                            grant,
-                            ["can_create", "can_push", "can_force_push"]):
+                        grant, ["can_create", "can_push", "can_force_push"]
+                    ):
                         grant.permissions = parsed_grant["permissions"]
                 elif action == "delete" and grant is not None:
                     grant.destroySelf(self.user)
@@ -1385,7 +1532,8 @@ class GitRepositoryPermissionsView(LaunchpadFormView):
             self.updateRepositoryFromData(self.repository, parsed_data)
 
         self.request.response.addNotification(
-            "Saved permissions for %s" % self.context.identity)
+            "Saved permissions for %s" % self.context.identity
+        )
         self.next_url = canonical_url(self.context, view_name="+permissions")
 
 
@@ -1408,7 +1556,8 @@ class GitRepositoryDeletionView(LaunchpadFormView):
         """
         reqs = []
         for item, (operation, reason) in self.context.getDeletionRequirements(
-                eager_load=True).items():
+            eager_load=True
+        ).items():
             allowed = check_permission("launchpad.Edit", item)
             reqs.append((item, operation, reason, allowed))
         return reqs
@@ -1418,11 +1567,24 @@ class GitRepositoryDeletionView(LaunchpadFormView):
 
         Uses display_deletion_requirements as its source data.
         """
-        return len([item for item, action, reason, allowed in
-            self.display_deletion_requirements if not allowed]) == 0
-
-    @action("Delete", name="delete_repository",
-            condition=lambda x, _: x.all_permitted())
+        return (
+            len(
+                [
+                    item
+                    for item, action, reason, allowed in (
+                        self.display_deletion_requirements
+                    )
+                    if not allowed
+                ]
+            )
+            == 0
+        )
+
+    @action(
+        "Delete",
+        name="delete_repository",
+        condition=lambda x, _: x.all_permitted(),
+    )
     def delete_repository_action(self, action, data):
         repository = self.context
         if self.all_permitted():
@@ -1434,7 +1596,8 @@ class GitRepositoryDeletionView(LaunchpadFormView):
             self.request.response.addNotification(message)
         else:
             self.request.response.addNotification(
-                "This repository cannot be deleted.")
+                "This repository cannot be deleted."
+            )
             self.next_url = canonical_url(repository)
 
     @property
@@ -1445,12 +1608,17 @@ class GitRepositoryDeletionView(LaunchpadFormView):
         "item", "reason" and "allowed".
         """
         row_dict = {"delete": [], "alter": []}
-        for item, operation, reason, allowed in (
-            self.display_deletion_requirements):
-            row = {"item": item,
-                   "reason": reason,
-                   "allowed": allowed,
-                  }
+        for (
+            item,
+            operation,
+            reason,
+            allowed,
+        ) in self.display_deletion_requirements:
+            row = {
+                "item": item,
+                "reason": reason,
+                "allowed": allowed,
+            }
             row_dict[operation].append(row)
         return row_dict
 
@@ -1474,10 +1642,10 @@ class GitRepositoryActivityView(LaunchpadView):
     def displayPermissions(self, values):
         """Assemble a human readable list from the permissions changes."""
         permissions = []
-        if values.get('can_create'):
-            permissions.append('create')
-        if values.get('can_push'):
-            permissions.append('push')
-        if values.get('can_force_push'):
-            permissions.append('force-push')
-        return ', '.join(permissions)
+        if values.get("can_create"):
+            permissions.append("create")
+        if values.get("can_push"):
+            permissions.append("push")
+        if values.get("can_force_push"):
+            permissions.append("force-push")
+        return ", ".join(permissions)
diff --git a/lib/lp/code/browser/gitsubscription.py b/lib/lp/code/browser/gitsubscription.py
index e4af854..c6f2b45 100644
--- a/lib/lp/code/browser/gitsubscription.py
+++ b/lib/lp/code/browser/gitsubscription.py
@@ -2,32 +2,29 @@
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 __all__ = [
-    'GitRepositoryPortletSubscribersContent',
-    'GitSubscriptionAddOtherView',
-    'GitSubscriptionAddView',
-    'GitSubscriptionEditOwnView',
-    'GitSubscriptionEditView',
-    ]
+    "GitRepositoryPortletSubscribersContent",
+    "GitSubscriptionAddOtherView",
+    "GitSubscriptionAddView",
+    "GitSubscriptionEditOwnView",
+    "GitSubscriptionEditView",
+]
 
 from zope.component import getUtility
 
 from lp.app.browser.launchpadform import (
-    action,
     LaunchpadEditFormView,
     LaunchpadFormView,
-    )
+    action,
+)
 from lp.app.interfaces.services import IService
 from lp.code.enums import BranchSubscriptionNotificationLevel
 from lp.code.interfaces.gitsubscription import IGitSubscription
 from lp.registry.interfaces.person import IPersonSet
-from lp.services.webapp import (
-    canonical_url,
-    LaunchpadView,
-    )
+from lp.services.webapp import LaunchpadView, canonical_url
 from lp.services.webapp.authorization import (
     check_permission,
     precache_permission_for_objects,
-    )
+)
 from lp.services.webapp.escaping import structured
 
 
@@ -42,31 +39,40 @@ class GitRepositoryPortletSubscribersContent(LaunchpadView):
         # need the expense of running several complex SQL queries.
         subscriptions = list(self.context.subscriptions)
         person_ids = [sub.person_id for sub in subscriptions]
-        list(getUtility(IPersonSet).getPrecachedPersonsFromIDs(
-            person_ids, need_validity=True))
+        list(
+            getUtility(IPersonSet).getPrecachedPersonsFromIDs(
+                person_ids, need_validity=True
+            )
+        )
         if self.user is not None:
             subscribers = [
-                subscription.person for subscription in subscriptions]
+                subscription.person for subscription in subscriptions
+            ]
             precache_permission_for_objects(
-                self.request, "launchpad.LimitedView", subscribers)
+                self.request, "launchpad.LimitedView", subscribers
+            )
 
         visible_subscriptions = [
-            subscription for subscription in subscriptions
-            if check_permission("launchpad.LimitedView", subscription.person)]
+            subscription
+            for subscription in subscriptions
+            if check_permission("launchpad.LimitedView", subscription.person)
+        ]
         return sorted(
             visible_subscriptions,
-            key=lambda subscription: subscription.person.displayname)
+            key=lambda subscription: subscription.person.displayname,
+        )
 
 
 class _GitSubscriptionView(LaunchpadFormView):
     """Contains the common functionality of the Add and Edit views."""
 
     schema = IGitSubscription
-    field_names = ['notification_level', 'max_diff_lines', 'review_level']
+    field_names = ["notification_level", "max_diff_lines", "review_level"]
 
     LEVELS_REQUIRING_LINES_SPECIFICATION = (
         BranchSubscriptionNotificationLevel.DIFFSONLY,
-        BranchSubscriptionNotificationLevel.FULL)
+        BranchSubscriptionNotificationLevel.FULL,
+    )
 
     @property
     def user_is_subscribed(self):
@@ -81,8 +87,9 @@ class _GitSubscriptionView(LaunchpadFormView):
 
     cancel_url = next_url
 
-    def add_notification_message(self, initial, notification_level,
-                                 max_diff_lines, review_level):
+    def add_notification_message(
+        self, initial, notification_level, max_diff_lines, review_level
+    ):
         if notification_level in self.LEVELS_REQUIRING_LINES_SPECIFICATION:
             lines_message = "<li>%s</li>" % max_diff_lines.description
         else:
@@ -90,8 +97,11 @@ class _GitSubscriptionView(LaunchpadFormView):
 
         format_str = "%%s<ul><li>%%s</li>%s<li>%%s</li></ul>" % lines_message
         message = structured(
-            format_str, initial, notification_level.description,
-            review_level.description)
+            format_str,
+            initial,
+            notification_level.description,
+            review_level.description,
+        )
         self.request.response.addNotification(message)
 
     def optional_max_diff_lines(self, notification_level, max_diff_lines):
@@ -111,24 +121,32 @@ class GitSubscriptionAddView(_GitSubscriptionView):
         # subscribed before continuing.
         if self.context.hasSubscription(self.user):
             self.request.response.addNotification(
-                "You are already subscribed to this repository.")
+                "You are already subscribed to this repository."
+            )
         else:
             notification_level = data["notification_level"]
             max_diff_lines = self.optional_max_diff_lines(
-                notification_level, data["max_diff_lines"])
+                notification_level, data["max_diff_lines"]
+            )
             review_level = data["review_level"]
 
             self.context.subscribe(
-                self.user, notification_level, max_diff_lines, review_level,
-                self.user)
+                self.user,
+                notification_level,
+                max_diff_lines,
+                review_level,
+                self.user,
+            )
 
             self.add_notification_message(
                 "You have subscribed to this repository with: ",
-                notification_level, max_diff_lines, review_level)
+                notification_level,
+                max_diff_lines,
+                review_level,
+            )
 
 
 class GitSubscriptionEditOwnView(_GitSubscriptionView):
-
     @property
     def label(self):
         return "Edit subscription to repository"
@@ -144,9 +162,11 @@ class GitSubscriptionEditOwnView(_GitSubscriptionView):
             # This is the case of URL hacking or stale page.
             return {}
         else:
-            return {"notification_level": subscription.notification_level,
-                    "max_diff_lines": subscription.max_diff_lines,
-                    "review_level": subscription.review_level}
+            return {
+                "notification_level": subscription.notification_level,
+                "max_diff_lines": subscription.max_diff_lines,
+                "review_level": subscription.review_level,
+            }
 
     @action("Change")
     def change_details(self, action, data):
@@ -155,18 +175,20 @@ class GitSubscriptionEditOwnView(_GitSubscriptionView):
             subscription = self.context.getSubscription(self.user)
             subscription.notification_level = data["notification_level"]
             subscription.max_diff_lines = self.optional_max_diff_lines(
-                subscription.notification_level,
-                data["max_diff_lines"])
+                subscription.notification_level, data["max_diff_lines"]
+            )
             subscription.review_level = data["review_level"]
 
             self.add_notification_message(
                 "Subscription updated to: ",
                 subscription.notification_level,
                 subscription.max_diff_lines,
-                subscription.review_level)
+                subscription.review_level,
+            )
         else:
             self.request.response.addNotification(
-                "You are not subscribed to this repository.")
+                "You are not subscribed to this repository."
+            )
 
     @action("Unsubscribe")
     def unsubscribe(self, action, data):
@@ -174,17 +196,23 @@ class GitSubscriptionEditOwnView(_GitSubscriptionView):
         if self.context.hasSubscription(self.user):
             self.context.unsubscribe(self.user, self.user)
             self.request.response.addNotification(
-                "You have unsubscribed from this repository.")
+                "You have unsubscribed from this repository."
+            )
         else:
             self.request.response.addNotification(
-                "You are not subscribed to this repository.")
+                "You are not subscribed to this repository."
+            )
 
 
 class GitSubscriptionAddOtherView(_GitSubscriptionView):
     """View used to subscribe someone other than the current user."""
 
     field_names = [
-        "person", "notification_level", "max_diff_lines", "review_level"]
+        "person",
+        "notification_level",
+        "max_diff_lines",
+        "review_level",
+    ]
     for_input = True
 
     # Since we are subscribing other people, the current user
@@ -198,35 +226,47 @@ class GitSubscriptionAddOtherView(_GitSubscriptionView):
             person = data["person"]
             subscription = self.context.getSubscription(person)
             if subscription is None and not self.context.userCanBeSubscribed(
-                person):
+                person
+            ):
                 self.setFieldError(
                     "person",
                     "Open and delegated teams cannot be subscribed to "
-                    "private repositories.")
+                    "private repositories.",
+                )
 
     @action("Subscribe", name="subscribe_action")
     def subscribe_action(self, action, data):
         """Subscribe the specified user to the repository."""
         notification_level = data["notification_level"]
         max_diff_lines = self.optional_max_diff_lines(
-            notification_level, data["max_diff_lines"])
+            notification_level, data["max_diff_lines"]
+        )
         review_level = data["review_level"]
         person = data["person"]
         subscription = self.context.getSubscription(person)
         if subscription is None:
             self.context.subscribe(
-                person, notification_level, max_diff_lines, review_level,
-                self.user)
+                person,
+                notification_level,
+                max_diff_lines,
+                review_level,
+                self.user,
+            )
             self.add_notification_message(
                 "%s has been subscribed to this repository with: "
-                % person.displayname, notification_level, max_diff_lines,
-                review_level)
+                % person.displayname,
+                notification_level,
+                max_diff_lines,
+                review_level,
+            )
         else:
             self.add_notification_message(
                 "%s was already subscribed to this repository with: "
                 % person.displayname,
-                subscription.notification_level, subscription.max_diff_lines,
-                review_level)
+                subscription.notification_level,
+                subscription.max_diff_lines,
+                review_level,
+            )
 
 
 class GitSubscriptionEditView(LaunchpadEditFormView):
@@ -236,18 +276,21 @@ class GitSubscriptionEditView(LaunchpadEditFormView):
     through the repository action item to edit the user's own subscription.
     This is the only current way to edit a team repository subscription.
     """
+
     schema = IGitSubscription
     field_names = ["notification_level", "max_diff_lines", "review_level"]
 
     @property
     def page_title(self):
         return (
-            "Edit subscription to repository %s" % self.repository.displayname)
+            "Edit subscription to repository %s" % self.repository.displayname
+        )
 
     @property
     def label(self):
         return (
-            "Edit subscription to repository for %s" % self.person.displayname)
+            "Edit subscription to repository for %s" % self.person.displayname
+        )
 
     def initialize(self):
         self.repository = self.context.repository
@@ -265,7 +308,8 @@ class GitSubscriptionEditView(LaunchpadEditFormView):
         self.repository.unsubscribe(self.person, self.user)
         self.request.response.addNotification(
             "%s has been unsubscribed from this repository."
-            % self.person.displayname)
+            % self.person.displayname
+        )
 
     @property
     def next_url(self):
@@ -274,8 +318,10 @@ class GitSubscriptionEditView(LaunchpadEditFormView):
         # away.
         service = getUtility(IService, "sharing")
         repositories = service.getVisibleArtifacts(
-            self.person, gitrepositories=[self.repository],
-            ignore_permissions=True)["gitrepositories"]
+            self.person,
+            gitrepositories=[self.repository],
+            ignore_permissions=True,
+        )["gitrepositories"]
         if not repositories:
             url = canonical_url(self.repository.target)
         return url
diff --git a/lib/lp/code/browser/revisionstatus.py b/lib/lp/code/browser/revisionstatus.py
index 39574eb..5d5a839 100644
--- a/lib/lp/code/browser/revisionstatus.py
+++ b/lib/lp/code/browser/revisionstatus.py
@@ -15,7 +15,6 @@ class RevisionStatusArtifactNavigation(Navigation, FileNavigationMixin):
 
 
 class HasRevisionStatusReportsMixin:
-
     def getStatusReports(self, commit_sha1):
         reports = self.context.getStatusReports(commit_sha1)
         return BatchNavigator(reports, self.request)
@@ -24,25 +23,26 @@ class HasRevisionStatusReportsMixin:
         """Show an appropriate icon at the top of the report."""
         icon_template = (
             '<img width="14" height="14" alt="%(title)s" '
-            'title="%(title)s" src="%(src)s" />')
+            'title="%(title)s" src="%(src)s" />'
+        )
         reports = repository.getStatusReports(commit_sha1)
         if all(
-                report.result == RevisionStatusResult.SKIPPED
-                for report in reports):
+            report.result == RevisionStatusResult.SKIPPED for report in reports
+        ):
             title = "Skipped"
             source = "/@@/yes-gray"
         elif all(
-                report.result in (
-                    RevisionStatusResult.SUCCEEDED,
-                    RevisionStatusResult.SKIPPED)
-                for report in reports):
+            report.result
+            in (RevisionStatusResult.SUCCEEDED, RevisionStatusResult.SKIPPED)
+            for report in reports
+        ):
             title = "Succeeded"
             source = "/@@/yes"
         elif any(
-                report.result in (
-                    RevisionStatusResult.FAILED,
-                    RevisionStatusResult.CANCELLED)
-                for report in reports):
+            report.result
+            in (RevisionStatusResult.FAILED, RevisionStatusResult.CANCELLED)
+            for report in reports
+        ):
             title = "Failed"
             source = "/@@/no"
         else:
diff --git a/lib/lp/code/browser/sourcepackagerecipe.py b/lib/lp/code/browser/sourcepackagerecipe.py
index eed6d6f..89fa7dc 100644
--- a/lib/lp/code/browser/sourcepackagerecipe.py
+++ b/lib/lp/code/browser/sourcepackagerecipe.py
@@ -4,32 +4,29 @@
 """SourcePackageRecipe views."""
 
 __all__ = [
-    'SourcePackageRecipeAddView',
-    'SourcePackageRecipeContextMenu',
-    'SourcePackageRecipeEditView',
-    'SourcePackageRecipeNavigationMenu',
-    'SourcePackageRecipeRequestBuildsView',
-    'SourcePackageRecipeRequestDailyBuildView',
-    'SourcePackageRecipeView',
-    ]
+    "SourcePackageRecipeAddView",
+    "SourcePackageRecipeContextMenu",
+    "SourcePackageRecipeEditView",
+    "SourcePackageRecipeNavigationMenu",
+    "SourcePackageRecipeRequestBuildsView",
+    "SourcePackageRecipeRequestDailyBuildView",
+    "SourcePackageRecipeView",
+]
 
 import itertools
 
+import simplejson
 from breezy.plugins.builder.recipe import (
     ForbiddenInstructionError,
     RecipeParseError,
-    )
+)
 from lazr.lifecycle.event import ObjectModifiedEvent
 from lazr.lifecycle.snapshot import Snapshot
-from lazr.restful.interface import (
-    copy_field,
-    use_template,
-    )
+from lazr.restful.interface import copy_field, use_template
 from lazr.restful.interfaces import (
     IFieldHTMLRenderer,
     IWebServiceClientRequest,
-    )
-import simplejson
+)
 from storm.locals import Store
 from zope import component
 from zope.browserpage import ViewPageTemplateFile
@@ -37,47 +34,34 @@ from zope.component import getUtility
 from zope.event import notify
 from zope.formlib import form
 from zope.formlib.widget import Widget
-from zope.interface import (
-    implementer,
-    Interface,
-    providedBy,
-    )
+from zope.interface import Interface, implementer, providedBy
 from zope.publisher.interfaces import IView
-from zope.schema import (
-    Choice,
-    Field,
-    List,
-    Text,
-    TextLine,
-    )
+from zope.schema import Choice, Field, List, Text, TextLine
 from zope.schema.interfaces import ICollection
-from zope.schema.vocabulary import (
-    SimpleTerm,
-    SimpleVocabulary,
-    )
+from zope.schema.vocabulary import SimpleTerm, SimpleVocabulary
 
 from lp import _
 from lp.app.browser.launchpadform import (
-    action,
-    has_structured_doc,
     LaunchpadEditFormView,
     LaunchpadFormView,
+    action,
+    has_structured_doc,
     render_radio_widget_part,
-    )
+)
 from lp.app.browser.lazrjs import (
     BooleanChoiceWidget,
     InlineEditPickerWidget,
     InlinePersonEditPickerWidget,
     TextAreaEditorWidget,
     TextLineEditorWidget,
-    )
+)
 from lp.app.browser.tales import format_link
 from lp.app.interfaces.launchpad import ILaunchpadCelebrities
 from lp.app.validators.name import name_validator
 from lp.app.widgets.itemswidgets import (
     LabeledMultiCheckBoxWidget,
     LaunchpadRadioWidget,
-    )
+)
 from lp.app.widgets.suggestion import RecipeOwnerWidget
 from lp.code.errors import (
     BuildAlreadyPending,
@@ -86,49 +70,48 @@ from lp.code.errors import (
     PrivateBranchRecipe,
     PrivateGitRepositoryRecipe,
     TooNewRecipeFormat,
-    )
+)
 from lp.code.interfaces.branch import IBranch
 from lp.code.interfaces.branchtarget import IBranchTarget
 from lp.code.interfaces.gitref import IGitRef
 from lp.code.interfaces.gitrepository import IGitRepository
 from lp.code.interfaces.sourcepackagerecipe import (
+    MINIMAL_RECIPE_TEXT_BZR,
+    MINIMAL_RECIPE_TEXT_GIT,
     IRecipeBranchSource,
     ISourcePackageRecipe,
     ISourcePackageRecipeSource,
-    MINIMAL_RECIPE_TEXT_BZR,
-    MINIMAL_RECIPE_TEXT_GIT,
-    )
+)
 from lp.code.vocabularies.sourcepackagerecipe import BuildableDistroSeries
 from lp.registry.interfaces.series import SeriesStatus
 from lp.services.fields import PersonChoice
 from lp.services.propertycache import cachedproperty
 from lp.services.webapp import (
-    canonical_url,
     ContextMenu,
-    enabled_with_permission,
     LaunchpadView,
     Link,
     NavigationMenu,
+    canonical_url,
+    enabled_with_permission,
     structured,
-    )
+)
 from lp.services.webapp.authorization import check_permission
-from lp.services.webapp.breadcrumb import (
-    Breadcrumb,
-    NameBreadcrumb,
-    )
+from lp.services.webapp.breadcrumb import Breadcrumb, NameBreadcrumb
 from lp.soyuz.interfaces.archive import ArchiveDisabled
 from lp.soyuz.model.archive import validate_ppa
 
 
 class SourcePackageRecipeBreadcrumb(NameBreadcrumb):
-
     @property
     def inside(self):
         return Breadcrumb(
             self.context.owner,
             url=canonical_url(
-                self.context.owner, view_name="+recipes", rootsite="code"),
-            text="Recipes", inside=self.context.owner)
+                self.context.owner, view_name="+recipes", rootsite="code"
+            ),
+            text="Recipes",
+            inside=self.context.owner,
+        )
 
 
 class SourcePackageRecipeNavigationMenu(NavigationMenu):
@@ -136,17 +119,17 @@ class SourcePackageRecipeNavigationMenu(NavigationMenu):
 
     usedfor = ISourcePackageRecipe
 
-    facet = 'branches'
+    facet = "branches"
 
-    links = ('edit', 'delete')
+    links = ("edit", "delete")
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def edit(self):
-        return Link('+edit', 'Edit recipe', icon='edit')
+        return Link("+edit", "Edit recipe", icon="edit")
 
-    @enabled_with_permission('launchpad.Delete')
+    @enabled_with_permission("launchpad.Delete")
     def delete(self):
-        return Link('+delete', 'Delete recipe', icon='trash-icon')
+        return Link("+delete", "Delete recipe", icon="trash-icon")
 
 
 class SourcePackageRecipeContextMenu(ContextMenu):
@@ -154,30 +137,39 @@ class SourcePackageRecipeContextMenu(ContextMenu):
 
     usedfor = ISourcePackageRecipe
 
-    facet = 'branches'
+    facet = "branches"
 
-    links = ('request_builds', 'request_daily_build',)
+    links = (
+        "request_builds",
+        "request_daily_build",
+    )
 
     def request_builds(self):
         """Provide a link for requesting builds of a recipe."""
-        return Link('+request-builds', 'Request build(s)', icon='add')
+        return Link("+request-builds", "Request build(s)", icon="add")
 
     def request_daily_build(self):
         """Provide a link for requesting a daily build of a recipe."""
         recipe = self.context
         ppa = recipe.daily_build_archive
-        if (ppa is None or not ppa.enabled or not recipe.build_daily or not
-            recipe.is_stale or not recipe.distroseries):
+        if (
+            ppa is None
+            or not ppa.enabled
+            or not recipe.build_daily
+            or not recipe.is_stale
+            or not recipe.distroseries
+        ):
             show_request_build = False
         else:
             has_upload = ppa.checkArchivePermission(recipe.owner)
             show_request_build = has_upload
 
-        show_request_build = (show_request_build and
-            check_permission('launchpad.Edit', recipe))
+        show_request_build = show_request_build and check_permission(
+            "launchpad.Edit", recipe
+        )
         return Link(
-                '+request-daily-build', 'Build now',
-                enabled=show_request_build)
+            "+request-daily-build", "Build now", enabled=show_request_build
+        )
 
 
 class SourcePackageRecipeView(LaunchpadView):
@@ -190,22 +182,29 @@ class SourcePackageRecipeView(LaunchpadView):
             self.request.response.addWarningNotification(
                 structured(
                     "Daily builds for this recipe will <strong>not</strong> "
-                    "occur.<br/><br/>There is no PPA."))
+                    "occur.<br/><br/>There is no PPA."
+                )
+            )
         elif self.dailyBuildWithoutUploadPermission():
             self.request.response.addWarningNotification(
                 structured(
                     "Daily builds for this recipe will <strong>not</strong> "
                     "occur.<br/><br/>The owner of the recipe (%s) does not "
                     "have permission to upload packages into the daily "
-                    "build PPA (%s)" % (
+                    "build PPA (%s)"
+                    % (
                         format_link(recipe.owner),
-                        format_link(recipe.daily_build_archive))))
+                        format_link(recipe.daily_build_archive),
+                    )
+                )
+            )
 
     @property
     def page_title(self):
-        return "%(name)s\'s %(recipe_name)s recipe" % {
-            'name': self.context.owner.displayname,
-            'recipe_name': self.context.name}
+        return "%(name)s's %(recipe_name)s recipe" % {
+            "name": self.context.owner.displayname,
+            "recipe_name": self.context.name,
+        }
 
     label = page_title
 
@@ -228,116 +227,130 @@ class SourcePackageRecipeView(LaunchpadView):
     @property
     def person_picker(self):
         field = copy_field(
-            ISourcePackageRecipe['owner'],
-            vocabularyName='UserTeamsParticipationPlusSelfSimpleDisplay')
+            ISourcePackageRecipe["owner"],
+            vocabularyName="UserTeamsParticipationPlusSelfSimpleDisplay",
+        )
         return InlinePersonEditPickerWidget(
-            self.context, field,
+            self.context,
+            field,
             format_link(self.context.owner),
-            header='Change owner',
-            step_title='Select a new owner')
+            header="Change owner",
+            step_title="Select a new owner",
+        )
 
     @property
     def archive_picker(self):
-        field = ISourcePackageEditSchema['daily_build_archive']
+        field = ISourcePackageEditSchema["daily_build_archive"]
         return InlineEditPickerWidget(
-            self.context, field,
+            self.context,
+            field,
             format_link(self.context.daily_build_archive),
-            header='Change daily build archive',
-            step_title='Select a PPA')
+            header="Change daily build archive",
+            step_title="Select a PPA",
+        )
 
     @property
     def recipe_text_widget(self):
         """The recipe text as widget HTML."""
-        recipe_text = ISourcePackageRecipe['recipe_text']
+        recipe_text = ISourcePackageRecipe["recipe_text"]
         return TextAreaEditorWidget(self.context, recipe_text, title="")
 
     @property
     def daily_build_widget(self):
         return BooleanChoiceWidget(
-            self.context, ISourcePackageRecipe['build_daily'],
-            tag='span',
-            false_text='Built on request',
-            true_text='Built daily',
-            header='Change build schedule')
+            self.context,
+            ISourcePackageRecipe["build_daily"],
+            tag="span",
+            false_text="Built on request",
+            true_text="Built daily",
+            header="Change build schedule",
+        )
 
     @property
     def description_widget(self):
         """The description as a widget."""
-        description = ISourcePackageRecipe['description']
-        return TextAreaEditorWidget(
-            self.context, description, title="")
+        description = ISourcePackageRecipe["description"]
+        return TextAreaEditorWidget(self.context, description, title="")
 
     @property
     def name_widget(self):
-        name = ISourcePackageRecipe['name']
+        name = ISourcePackageRecipe["name"]
         title = "Edit the recipe name"
         return TextLineEditorWidget(
-            self.context, name, title, 'h1', max_width='95%',
-            truncate_lines=1)
+            self.context, name, title, "h1", max_width="95%", truncate_lines=1
+        )
 
     @property
     def distroseries_widget(self):
         from lp.app.browser.lazrjs import InlineMultiCheckboxWidget
-        field = ISourcePackageEditSchema['distroseries']
+
+        field = ISourcePackageEditSchema["distroseries"]
         return InlineMultiCheckboxWidget(
             self.context,
             field,
             attribute_type="reference",
-            vocabulary='BuildableDistroSeries',
+            vocabulary="BuildableDistroSeries",
             label="Distribution series:",
             label_tag="dt",
             header="Change default distribution series:",
             empty_display_value="None",
             selected_items=sorted(
-                self.context.distroseries, key=lambda ds: ds.displayname),
+                self.context.distroseries, key=lambda ds: ds.displayname
+            ),
             items_tag="dd",
-            )
+        )
 
 
-@component.adapter(ISourcePackageRecipe, ICollection,
-                   IWebServiceClientRequest)
+@component.adapter(ISourcePackageRecipe, ICollection, IWebServiceClientRequest)
 @implementer(IFieldHTMLRenderer)
 def distroseries_renderer(context, field, request):
     """Render a distroseries collection as a set of links."""
 
     def render(value):
         distroseries = sorted(
-            context.distroseries, key=lambda ds: ds.displayname)
+            context.distroseries, key=lambda ds: ds.displayname
+        )
         if not distroseries:
-            return 'None'
+            return "None"
         html = "<ul>"
-        html += ''.join(
-            ["<li>%s</li>" % format_link(series) for series in distroseries])
+        html += "".join(
+            ["<li>%s</li>" % format_link(series) for series in distroseries]
+        )
         html += "</ul>"
         return html
+
     return render
 
 
 def builds_for_recipe(recipe):
-        """A list of interesting builds.
+    """A list of interesting builds.
 
-        All pending builds are shown, as well as 1-5 recent builds.
-        Recent builds are ordered by date finished (if completed) or
-        date_started (if date finished is not set due to an error building or
-        other circumstance which resulted in the build not being completed).
-        This allows started but unfinished builds to show up in the view but
-        be discarded as more recent builds become available.
+    All pending builds are shown, as well as 1-5 recent builds.
+    Recent builds are ordered by date finished (if completed) or
+    date_started (if date finished is not set due to an error building or
+    other circumstance which resulted in the build not being completed).
+    This allows started but unfinished builds to show up in the view but
+    be discarded as more recent builds become available.
 
-        Builds that the user does not have permission to see are excluded.
-        """
-        builds = [build for build in recipe.pending_builds
-            if check_permission('launchpad.View', build)]
-        for build in recipe.completed_builds:
-            if not check_permission('launchpad.View', build):
-                continue
-            builds.append(build)
-            if len(builds) >= 5:
-                break
-        return builds
-
-
-def new_builds_notification_text(builds, already_pending=None,
-                                 contains_unbuildable=False):
+    Builds that the user does not have permission to see are excluded.
+    """
+    builds = [
+        build
+        for build in recipe.pending_builds
+        if check_permission("launchpad.View", build)
+    ]
+    for build in recipe.completed_builds:
+        if not check_permission("launchpad.View", build):
+            continue
+        builds.append(build)
+        if len(builds) >= 5:
+            break
+    return builds
+
+
+def new_builds_notification_text(
+    builds, already_pending=None, contains_unbuildable=False
+):
     nr_builds = len(builds)
     if not nr_builds:
         builds_text = "All requested recipe builds are already queued."
@@ -348,8 +361,10 @@ def new_builds_notification_text(builds, already_pending=None,
     if nr_builds > 0 and already_pending:
         builds_text = "<p>%s</p>%s" % (builds_text, already_pending)
     if contains_unbuildable:
-        builds_text = ("%s<p>The recipe contains an obsolete distroseries, "
-            "which has been skipped.</p>" % builds_text)
+        builds_text = (
+            "%s<p>The recipe contains an obsolete distroseries, "
+            "which has been skipped.</p>" % builds_text
+        )
     return structured(builds_text)
 
 
@@ -362,32 +377,39 @@ class SourcePackageRecipeRequestBuildsView(LaunchpadFormView):
 
         The distroseries function as defaults for requesting a build.
         """
-        initial_values = {'distroseries': self.context.distroseries}
+        initial_values = {"distroseries": self.context.distroseries}
         if self.context.daily_build_archive and check_permission(
-            'launchpad.Append', self.context.daily_build_archive):
-            initial_values['archive'] = self.context.daily_build_archive
+            "launchpad.Append", self.context.daily_build_archive
+        ):
+            initial_values["archive"] = self.context.daily_build_archive
         return initial_values
 
     class schema(Interface):
         """Schema for requesting a build."""
+
         archive = Choice(
-            vocabulary='TargetPPAs', title='Archive', required=False)
+            vocabulary="TargetPPAs", title="Archive", required=False
+        )
         distroseries = List(
-            Choice(vocabulary='BuildableDistroSeries'),
-            title='Distribution series')
+            Choice(vocabulary="BuildableDistroSeries"),
+            title="Distribution series",
+        )
 
     custom_widget_distroseries = LabeledMultiCheckBoxWidget
 
     def validate(self, data):
-        if not data['archive']:
+        if not data["archive"]:
             self.setFieldError(
-                'archive', "You must specify the archive to build into.")
+                "archive", "You must specify the archive to build into."
+            )
             return
-        distros = data.get('distroseries', [])
+        distros = data.get("distroseries", [])
         if not len(distros):
-            self.setFieldError('distroseries',
+            self.setFieldError(
+                "distroseries",
                 "You need to specify at least one distro series for which "
-                "to build.")
+                "to build.",
+            )
             return
 
     def requestBuild(self, data):
@@ -399,31 +421,36 @@ class SourcePackageRecipeRequestBuildsView(LaunchpadFormView):
         """
         informational = {}
         builds = []
-        for distroseries in data['distroseries']:
+        for distroseries in data["distroseries"]:
             try:
                 build = self.context.requestBuild(
-                    data['archive'], self.user, distroseries, manual=True)
+                    data["archive"], self.user, distroseries, manual=True
+                )
                 builds.append(build)
             except BuildAlreadyPending as e:
                 existing_message = informational.get("already_pending")
                 if existing_message:
                     new_message = existing_message[:-1] + (
-                                    ", and %s." % e.distroseries)
+                        ", and %s." % e.distroseries
+                    )
                 else:
-                    new_message = ("An identical build is "
-                                "already pending for %s." % e.distroseries)
+                    new_message = (
+                        "An identical build is "
+                        "already pending for %s." % e.distroseries
+                    )
                 informational["already_pending"] = new_message
 
         return builds, informational
 
 
 class SourcePackageRecipeRequestBuildsHtmlView(
-        SourcePackageRecipeRequestBuildsView):
+    SourcePackageRecipeRequestBuildsView
+):
     """Supports HTML form recipe build requests."""
 
     @property
     def title(self):
-        return 'Request builds for %s' % self.context.name
+        return "Request builds for %s" % self.context.name
 
     label = title
 
@@ -431,25 +458,33 @@ class SourcePackageRecipeRequestBuildsHtmlView(
     def cancel_url(self):
         return canonical_url(self.context)
 
-    @action('Request builds', name='request')
+    @action("Request builds", name="request")
     def request_action(self, action, data):
         builds, informational = self.requestBuild(data)
         self.next_url = self.cancel_url
         already_pending = informational.get("already_pending")
         notification_text = new_builds_notification_text(
-            builds, already_pending)
+            builds, already_pending
+        )
         self.request.response.addNotification(notification_text)
 
 
 class SourcePackageRecipeRequestBuildsAjaxView(
-        SourcePackageRecipeRequestBuildsView):
+    SourcePackageRecipeRequestBuildsView
+):
     """Supports AJAX form recipe build requests."""
 
-    def _process_error(self, data=None, builds=None, informational=None,
-                       errors=None, reason="Validation"):
+    def _process_error(
+        self,
+        data=None,
+        builds=None,
+        informational=None,
+        errors=None,
+        reason="Validation",
+    ):
         """Set up the response and json data to return to the caller."""
         self.request.response.setStatus(200, reason)
-        self.request.response.setHeader('Content-type', 'application/json')
+        self.request.response.setHeader("Content-type", "application/json")
         return_data = dict(builds=builds, errors=errors)
         if informational:
             return_data.update(informational)
@@ -458,12 +493,12 @@ class SourcePackageRecipeRequestBuildsAjaxView(
     def failure(self, action, data, errors):
         """Called by the form if validate() finds any errors.
 
-           We simply convert the errors to json and return that data to the
-           caller for display to the user.
+        We simply convert the errors to json and return that data to the
+        caller for display to the user.
         """
         return self._process_error(data=data, errors=self.widget_errors)
 
-    @action('Request builds', name='request', failure=failure)
+    @action("Request builds", name="request", failure=failure)
     def request_action(self, action, data):
         """User action for requesting a number of builds.
 
@@ -484,8 +519,11 @@ class SourcePackageRecipeRequestBuildsAjaxView(
             if len(builds):
                 builds_html = self.render()
             return self._process_error(
-                data=data, builds=builds_html, informational=informational,
-                reason="Request Build")
+                data=data,
+                builds=builds_html,
+                informational=informational,
+                reason="Request Build",
+            )
 
     @property
     def builds(self):
@@ -506,13 +544,13 @@ class SourcePackageRecipeRequestDailyBuildView(LaunchpadFormView):
 
     def initialize(self):
         super().initialize()
-        if self.request.method == 'GET':
+        if self.request.method == "GET":
             self.request.response.redirect(canonical_url(self.context))
 
     class schema(Interface):
         """Schema for requesting a build."""
 
-    @action('Build now', name='build')
+    @action("Build now", name="build")
     def build_action(self, action, data):
         recipe = self.context
         try:
@@ -524,15 +562,19 @@ class SourcePackageRecipeRequestDailyBuildView(LaunchpadFormView):
 
         if self.request.is_ajax:
             template = ViewPageTemplateFile(
-                    "../templates/sourcepackagerecipe-builds.pt")
+                "../templates/sourcepackagerecipe-builds.pt"
+            )
             return template(self)
         else:
             contains_unbuildable = recipe.containsUnbuildableSeries(
-                recipe.daily_build_archive)
+                recipe.daily_build_archive
+            )
             self.next_url = canonical_url(recipe)
             self.request.response.addNotification(
                 new_builds_notification_text(
-                    builds, contains_unbuildable=contains_unbuildable))
+                    builds, contains_unbuildable=contains_unbuildable
+                )
+            )
 
     @property
     def builds(self):
@@ -542,60 +584,81 @@ class SourcePackageRecipeRequestDailyBuildView(LaunchpadFormView):
 class ISourcePackageEditSchema(Interface):
     """Schema for adding or editing a recipe."""
 
-    use_template(ISourcePackageRecipe, include=[
-        'name',
-        'description',
-        'owner',
-        'build_daily',
-        'distroseries',
-        ])
-    daily_build_archive = Choice(vocabulary='TargetPPAs',
-        title='Daily build archive',
+    use_template(
+        ISourcePackageRecipe,
+        include=[
+            "name",
+            "description",
+            "owner",
+            "build_daily",
+            "distroseries",
+        ],
+    )
+    daily_build_archive = Choice(
+        vocabulary="TargetPPAs",
+        title="Daily build archive",
         description=(
-            'If built daily, this is the archive where the package '
-            'will be uploaded.'))
+            "If built daily, this is the archive where the package "
+            "will be uploaded."
+        ),
+    )
     recipe_text = has_structured_doc(
         Text(
-            title='Recipe text', required=True,
+            title="Recipe text",
+            required=True,
             description="""The text of the recipe.
                 <a href="/+help-code/recipe-syntax.html" target="help"
                   >Syntax help&nbsp;
                   <span class="sprite maybe action-icon">
                     Help
                   </span></a>
-               """))
+               """,
+        )
+    )
 
 
-EXISTING_PPA = 'existing-ppa'
-CREATE_NEW = 'create-new'
+EXISTING_PPA = "existing-ppa"
+CREATE_NEW = "create-new"
 
 
-USE_ARCHIVE_VOCABULARY = SimpleVocabulary((
-    SimpleTerm(EXISTING_PPA, EXISTING_PPA, _("Use an existing PPA")),
-    SimpleTerm(
-        CREATE_NEW, CREATE_NEW, _("Create a new PPA for this recipe")),
-    ))
+USE_ARCHIVE_VOCABULARY = SimpleVocabulary(
+    (
+        SimpleTerm(EXISTING_PPA, EXISTING_PPA, _("Use an existing PPA")),
+        SimpleTerm(
+            CREATE_NEW, CREATE_NEW, _("Create a new PPA for this recipe")
+        ),
+    )
+)
 
 
 class ISourcePackageAddSchema(ISourcePackageEditSchema):
 
-    daily_build_archive = Choice(vocabulary='TargetPPAs',
-        title='Daily build archive', required=False,
+    daily_build_archive = Choice(
+        vocabulary="TargetPPAs",
+        title="Daily build archive",
+        required=False,
         description=(
-            'If built daily, this is the archive where the package '
-            'will be uploaded.'))
+            "If built daily, this is the archive where the package "
+            "will be uploaded."
+        ),
+    )
 
     use_ppa = Choice(
-        title=_('Which PPA'),
+        title=_("Which PPA"),
         vocabulary=USE_ARCHIVE_VOCABULARY,
         description=_("Which PPA to use..."),
-        required=True)
+        required=True,
+    )
 
     ppa_name = TextLine(
-            title=_("New PPA name"), required=False,
-            constraint=name_validator,
-            description=_("A new PPA with this name will be created for "
-                          "the owner of the recipe ."))
+        title=_("New PPA name"),
+        required=False,
+        constraint=name_validator,
+        description=_(
+            "A new PPA with this name will be created for "
+            "the owner of the recipe ."
+        ),
+    )
 
 
 class ErrorHandled(Exception):
@@ -606,15 +669,17 @@ class RecipeTextValidatorMixin:
     """Class to validate that the Source Package Recipe text is valid."""
 
     def validate(self, data):
-        if data['build_daily']:
-            if len(data['distroseries']) == 0:
+        if data["build_daily"]:
+            if len(data["distroseries"]) == 0:
                 self.setFieldError(
-                    'distroseries',
-                    'You must specify at least one series for daily builds.')
+                    "distroseries",
+                    "You must specify at least one series for daily builds.",
+                )
         try:
             self.error_handler(
                 getUtility(IRecipeBranchSource).getParsedRecipe,
-                data['recipe_text'])
+                data["recipe_text"],
+            )
         except ErrorHandled:
             pass
 
@@ -623,23 +688,30 @@ class RecipeTextValidatorMixin:
             return callable(*args)
         except TooNewRecipeFormat:
             self.setFieldError(
-                'recipe_text',
-                'The recipe format version specified is not available.')
+                "recipe_text",
+                "The recipe format version specified is not available.",
+            )
         except ForbiddenInstructionError as e:
             self.setFieldError(
-                'recipe_text',
-                'The recipe instruction "%s" is not permitted here.' %
-                e.instruction_name)
+                "recipe_text",
+                'The recipe instruction "%s" is not permitted here.'
+                % e.instruction_name,
+            )
         except NoSuchBranch as e:
             self.setFieldError(
-                'recipe_text', '%s is not a branch on Launchpad.' % e.name)
+                "recipe_text", "%s is not a branch on Launchpad." % e.name
+            )
         except NoSuchGitRepository as e:
             self.setFieldError(
-                'recipe_text',
-                '%s is not a Git repository on Launchpad.' % e.name)
-        except (RecipeParseError, PrivateBranchRecipe,
-                PrivateGitRepositoryRecipe) as e:
-            self.setFieldError('recipe_text', str(e))
+                "recipe_text",
+                "%s is not a Git repository on Launchpad." % e.name,
+            )
+        except (
+            RecipeParseError,
+            PrivateBranchRecipe,
+            PrivateGitRepositoryRecipe,
+        ) as e:
+            self.setFieldError("recipe_text", str(e))
         raise ErrorHandled()
 
 
@@ -648,7 +720,8 @@ class RelatedBranchesWidget(Widget):
     """A widget to render the related branches for a recipe."""
 
     __call__ = ViewPageTemplateFile(
-        '../templates/sourcepackagerecipe-related-branches.pt')
+        "../templates/sourcepackagerecipe-related-branches.pt"
+    )
 
     related_package_branch_info = []
     related_series_branch_info = []
@@ -657,9 +730,8 @@ class RelatedBranchesWidget(Widget):
         return True
 
     def setRenderedValue(self, value):
-        self.related_package_branch_info = (
-            value['related_package_branch_info'])
-        self.related_series_branch_info = value['related_series_branch_info']
+        self.related_package_branch_info = value["related_package_branch_info"]
+        self.related_series_branch_info = value["related_series_branch_info"]
 
 
 class RecipeRelatedBranchesMixin(LaunchpadFormView):
@@ -672,16 +744,19 @@ class RecipeRelatedBranchesMixin(LaunchpadFormView):
 
         Adds a related branches field to the form.
         """
-        self.form_fields += form.Fields(Field(__name__='related_branches'))
-        self.widget_errors['related_branches'] = ''
+        self.form_fields += form.Fields(Field(__name__="related_branches"))
+        self.widget_errors["related_branches"] = ""
 
     def setUpWidgets(self, context=None):
         # Adds a new related branches widget.
         super().setUpWidgets(context)
-        self.widgets['related_branches'].display_label = False
-        self.widgets['related_branches'].setRenderedValue(dict(
-            related_package_branch_info=self.related_package_branch_info,
-            related_series_branch_info=self.related_series_branch_info))
+        self.widgets["related_branches"].display_label = False
+        self.widgets["related_branches"].setRenderedValue(
+            dict(
+                related_package_branch_info=self.related_package_branch_info,
+                related_series_branch_info=self.related_series_branch_info,
+            )
+        )
 
     @cachedproperty
     def related_series_branch_info(self):
@@ -689,7 +764,8 @@ class RecipeRelatedBranchesMixin(LaunchpadFormView):
         if IBranch.providedBy(branch_to_check):
             branch_target = IBranchTarget(branch_to_check.target)
             return branch_target.getRelatedSeriesBranchInfo(
-                branch_to_check, limit_results=5)
+                branch_to_check, limit_results=5
+            )
 
     @cachedproperty
     def related_package_branch_info(self):
@@ -697,14 +773,16 @@ class RecipeRelatedBranchesMixin(LaunchpadFormView):
         if IBranch.providedBy(branch_to_check):
             branch_target = IBranchTarget(branch_to_check.target)
             return branch_target.getRelatedPackageBranchInfo(
-                branch_to_check, limit_results=5)
+                branch_to_check, limit_results=5
+            )
 
 
-class SourcePackageRecipeAddView(RecipeRelatedBranchesMixin,
-                                 RecipeTextValidatorMixin, LaunchpadFormView):
+class SourcePackageRecipeAddView(
+    RecipeRelatedBranchesMixin, RecipeTextValidatorMixin, LaunchpadFormView
+):
     """View for creating Source Package Recipes."""
 
-    title = label = 'Create a new source package recipe'
+    title = label = "Create a new source package recipe"
 
     schema = ISourcePackageAddSchema
     custom_widget_distroseries = LabeledMultiCheckBoxWidget
@@ -713,16 +791,18 @@ class SourcePackageRecipeAddView(RecipeRelatedBranchesMixin,
 
     def initialize(self):
         super().initialize()
-        widget = self.widgets['use_ppa']
+        widget = self.widgets["use_ppa"]
         current_value = widget._getFormValue()
         self.use_ppa_existing = render_radio_widget_part(
-            widget, EXISTING_PPA, current_value)
+            widget, EXISTING_PPA, current_value
+        )
         self.use_ppa_new = render_radio_widget_part(
-            widget, CREATE_NEW, current_value)
-        archive_widget = self.widgets['daily_build_archive']
+            widget, CREATE_NEW, current_value
+        )
+        archive_widget = self.widgets["daily_build_archive"]
         self.show_ppa_chooser = len(archive_widget.vocabulary) > 0
         if not self.show_ppa_chooser:
-            self.widgets['ppa_name'].setRenderedValue('ppa')
+            self.widgets["ppa_name"].setRenderedValue("ppa")
         # Force there to be no '(nothing selected)' item in the select.
         # We do this as the input isn't listed as 'required' otherwise
         # the validator gets all confused when we want to create a new
@@ -732,7 +812,7 @@ class SourcePackageRecipeAddView(RecipeRelatedBranchesMixin,
     def setUpFields(self):
         super().setUpFields()
         # Ensure distro series widget allows input
-        self.form_fields['distroseries'].for_input = True
+        self.form_fields["distroseries"].for_input = True
 
     def getBranch(self):
         """The branch or repository on which the recipe is built."""
@@ -742,12 +822,17 @@ class SourcePackageRecipeAddView(RecipeRelatedBranchesMixin,
         """A generator of recipe names."""
         # +junk-daily doesn't make a very good recipe name, so use the
         # branch name in that case; similarly for personal Git repositories.
-        if ((IBranch.providedBy(self.context) and
-                self.context.target.allow_recipe_name_from_target) or
-            ((IGitRepository.providedBy(self.context) or
-              IGitRef.providedBy(self.context)) and
-                self.context.namespace.allow_recipe_name_from_target)):
-            branch_target_name = self.context.target.name.split('/')[-1]
+        if (
+            IBranch.providedBy(self.context)
+            and self.context.target.allow_recipe_name_from_target
+        ) or (
+            (
+                IGitRepository.providedBy(self.context)
+                or IGitRef.providedBy(self.context)
+            )
+            and self.context.namespace.allow_recipe_name_from_target
+        ):
+            branch_target_name = self.context.target.name.split("/")[-1]
         else:
             branch_target_name = self.context.name
         yield "%s-daily" % branch_target_name
@@ -765,54 +850,70 @@ class SourcePackageRecipeAddView(RecipeRelatedBranchesMixin,
     @property
     def initial_values(self):
         distroseries = BuildableDistroSeries.findSeries(self.user)
-        series = [series for series in distroseries if series.status in (
-                SeriesStatus.CURRENT, SeriesStatus.DEVELOPMENT)]
+        series = [
+            series
+            for series in distroseries
+            if series.status
+            in (SeriesStatus.CURRENT, SeriesStatus.DEVELOPMENT)
+        ]
         if IBranch.providedBy(self.context):
             recipe_text = MINIMAL_RECIPE_TEXT_BZR % self.context.identity
         elif IGitRepository.providedBy(self.context):
             default_ref = None
             if self.context.default_branch is not None:
                 default_ref = self.context.getRefByPath(
-                    self.context.default_branch)
+                    self.context.default_branch
+                )
             if default_ref is not None:
                 branch_name = default_ref.name
             else:
                 branch_name = "ENTER-BRANCH-NAME"
             recipe_text = MINIMAL_RECIPE_TEXT_GIT % (
-                self.context.identity, branch_name)
+                self.context.identity,
+                branch_name,
+            )
         elif IGitRef.providedBy(self.context):
             recipe_text = MINIMAL_RECIPE_TEXT_GIT % (
-                self.context.repository.identity, self.context.name)
+                self.context.repository.identity,
+                self.context.name,
+            )
         else:
             raise AssertionError("Unsupported context: %r" % (self.context,))
         return {
-            'name': self._find_unused_name(self.user),
-            'recipe_text': recipe_text,
-            'owner': self.user,
-            'distroseries': series,
-            'build_daily': True,
-            'use_ppa': EXISTING_PPA,
-            }
+            "name": self._find_unused_name(self.user),
+            "recipe_text": recipe_text,
+            "owner": self.user,
+            "distroseries": series,
+            "build_daily": True,
+            "use_ppa": EXISTING_PPA,
+        }
 
     @property
     def cancel_url(self):
         return canonical_url(self.context)
 
-    @action('Create Recipe', name='create')
+    @action("Create Recipe", name="create")
     def request_action(self, action, data):
-        owner = data['owner']
-        if data['use_ppa'] == CREATE_NEW:
-            ppa_name = data.get('ppa_name', None)
+        owner = data["owner"]
+        if data["use_ppa"] == CREATE_NEW:
+            ppa_name = data.get("ppa_name", None)
             ppa = owner.createPPA(
-                getUtility(ILaunchpadCelebrities).ubuntu, ppa_name)
+                getUtility(ILaunchpadCelebrities).ubuntu, ppa_name
+            )
         else:
-            ppa = data['daily_build_archive']
+            ppa = data["daily_build_archive"]
         try:
             source_package_recipe = self.error_handler(
                 getUtility(ISourcePackageRecipeSource).new,
-                self.user, owner, data['name'],
-                data['recipe_text'], data['description'],
-                data['distroseries'], ppa, data['build_daily'])
+                self.user,
+                owner,
+                data["name"],
+                data["recipe_text"],
+                data["description"],
+                data["distroseries"],
+                ppa,
+                data["build_daily"],
+            )
             Store.of(source_package_recipe).flush()
         except ErrorHandled:
             return
@@ -821,30 +922,33 @@ class SourcePackageRecipeAddView(RecipeRelatedBranchesMixin,
 
     def validate(self, data):
         super().validate(data)
-        name = data.get('name', None)
-        owner = data.get('owner', None)
+        name = data.get("name", None)
+        owner = data.get("owner", None)
         if name and owner:
             SourcePackageRecipeSource = getUtility(ISourcePackageRecipeSource)
             if SourcePackageRecipeSource.exists(owner, name):
                 self.setFieldError(
-                    'name',
-                    'There is already a recipe owned by %s with this name.' %
-                        owner.displayname)
-        if data['use_ppa'] == CREATE_NEW:
-            ppa_name = data.get('ppa_name', None)
+                    "name",
+                    "There is already a recipe owned by %s with this name."
+                    % owner.displayname,
+                )
+        if data["use_ppa"] == CREATE_NEW:
+            ppa_name = data.get("ppa_name", None)
             if ppa_name is None:
                 self.setFieldError(
-                    'ppa_name', 'You need to specify a name for the PPA.')
+                    "ppa_name", "You need to specify a name for the PPA."
+                )
             else:
                 error = validate_ppa(
-                    owner, getUtility(ILaunchpadCelebrities).ubuntu, ppa_name)
+                    owner, getUtility(ILaunchpadCelebrities).ubuntu, ppa_name
+                )
                 if error is not None:
-                    self.setFieldError('ppa_name', error)
+                    self.setFieldError("ppa_name", error)
 
 
-class SourcePackageRecipeEditView(RecipeRelatedBranchesMixin,
-                                  RecipeTextValidatorMixin,
-                                  LaunchpadEditFormView):
+class SourcePackageRecipeEditView(
+    RecipeRelatedBranchesMixin, RecipeTextValidatorMixin, LaunchpadEditFormView
+):
     """View for editing Source Package Recipes."""
 
     def getBranch(self):
@@ -853,7 +957,8 @@ class SourcePackageRecipeEditView(RecipeRelatedBranchesMixin,
 
     @property
     def title(self):
-        return 'Edit %s source package recipe' % self.context.name
+        return "Edit %s source package recipe" % self.context.name
+
     label = title
 
     schema = ISourcePackageEditSchema
@@ -863,52 +968,60 @@ class SourcePackageRecipeEditView(RecipeRelatedBranchesMixin,
         super().setUpFields()
 
         # Ensure distro series widget allows input
-        self.form_fields['distroseries'].for_input = True
+        self.form_fields["distroseries"].for_input = True
 
-        if check_permission('launchpad.Admin', self.context):
+        if check_permission("launchpad.Admin", self.context):
             # Exclude the PPA archive dropdown.
-            self.form_fields = self.form_fields.omit('daily_build_archive')
+            self.form_fields = self.form_fields.omit("daily_build_archive")
 
-            owner_field = self.schema['owner']
+            owner_field = self.schema["owner"]
             any_owner_choice = PersonChoice(
-                __name__='owner', title=owner_field.title,
-                description=("As an administrator you are able to assign"
-                             " this recipe to any person or team."),
-                required=True, vocabulary='ValidPersonOrTeam')
+                __name__="owner",
+                title=owner_field.title,
+                description=(
+                    "As an administrator you are able to assign"
+                    " this recipe to any person or team."
+                ),
+                required=True,
+                vocabulary="ValidPersonOrTeam",
+            )
             any_owner_field = form.Fields(
-                any_owner_choice, render_context=self.render_context)
+                any_owner_choice, render_context=self.render_context
+            )
             # Replace the normal owner field with a more permissive vocab.
-            self.form_fields = self.form_fields.omit('owner')
+            self.form_fields = self.form_fields.omit("owner")
             self.form_fields = any_owner_field + self.form_fields
 
     @property
     def initial_values(self):
         return {
-            'distroseries': self.context.distroseries,
-            'recipe_text': self.context.recipe_text,
-            }
+            "distroseries": self.context.distroseries,
+            "recipe_text": self.context.recipe_text,
+        }
 
     @property
     def cancel_url(self):
         return canonical_url(self.context)
 
-    @action('Update Recipe', name='update')
+    @action("Update Recipe", name="update")
     def request_action(self, action, data):
         changed = False
         recipe_before_modification = Snapshot(
-            self.context, providing=providedBy(self.context))
+            self.context, providing=providedBy(self.context)
+        )
 
-        recipe_text = data.pop('recipe_text')
+        recipe_text = data.pop("recipe_text")
         try:
             recipe = self.error_handler(
-                getUtility(IRecipeBranchSource).getParsedRecipe, recipe_text)
+                getUtility(IRecipeBranchSource).getParsedRecipe, recipe_text
+            )
             if self.context.builder_recipe != recipe:
                 self.error_handler(self.context.setRecipeText, recipe_text)
                 changed = True
         except ErrorHandled:
             return
 
-        distros = data.pop('distroseries')
+        distros = data.pop("distroseries")
         if distros != self.context.distroseries:
             self.context.distroseries.clear()
             for distroseries_item in distros:
@@ -920,9 +1033,13 @@ class SourcePackageRecipeEditView(RecipeRelatedBranchesMixin,
 
         if changed:
             field_names = [
-                form_field.__name__ for form_field in self.form_fields]
-            notify(ObjectModifiedEvent(
-                self.context, recipe_before_modification, field_names))
+                form_field.__name__ for form_field in self.form_fields
+            ]
+            notify(
+                ObjectModifiedEvent(
+                    self.context, recipe_before_modification, field_names
+                )
+            )
 
         self.next_url = canonical_url(self.context)
 
@@ -933,24 +1050,25 @@ class SourcePackageRecipeEditView(RecipeRelatedBranchesMixin,
 
     def validate(self, data):
         super().validate(data)
-        name = data.get('name', None)
-        owner = data.get('owner', None)
+        name = data.get("name", None)
+        owner = data.get("owner", None)
         if name and owner:
             SourcePackageRecipeSource = getUtility(ISourcePackageRecipeSource)
             if SourcePackageRecipeSource.exists(owner, name):
                 recipe = owner.getRecipe(name)
                 if recipe != self.context:
                     self.setFieldError(
-                        'name',
-                        'There is already a recipe owned by %s with this '
-                        'name.' % owner.displayname)
+                        "name",
+                        "There is already a recipe owned by %s with this "
+                        "name." % owner.displayname,
+                    )
 
 
 class SourcePackageRecipeDeleteView(LaunchpadFormView):
-
     @property
     def title(self):
-        return 'Delete %s source package recipe' % self.context.name
+        return "Delete %s source package recipe" % self.context.name
+
     label = title
 
     class schema(Interface):
@@ -964,6 +1082,6 @@ class SourcePackageRecipeDeleteView(LaunchpadFormView):
     def next_url(self):
         return canonical_url(self.context.owner)
 
-    @action('Delete recipe', name='delete')
+    @action("Delete recipe", name="delete")
     def request_action(self, action, data):
         self.context.destroySelf()
diff --git a/lib/lp/code/browser/sourcepackagerecipebuild.py b/lib/lp/code/browser/sourcepackagerecipebuild.py
index 8817d7e..4a675b1 100644
--- a/lib/lp/code/browser/sourcepackagerecipebuild.py
+++ b/lib/lp/code/browser/sourcepackagerecipebuild.py
@@ -4,37 +4,31 @@
 """SourcePackageRecipeBuild views."""
 
 __all__ = [
-    'SourcePackageRecipeBuildContextMenu',
-    'SourcePackageRecipeBuildNavigation',
-    'SourcePackageRecipeBuildView',
-    'SourcePackageRecipeBuildCancelView',
-    'SourcePackageRecipeBuildRescoreView',
-    ]
+    "SourcePackageRecipeBuildContextMenu",
+    "SourcePackageRecipeBuildNavigation",
+    "SourcePackageRecipeBuildView",
+    "SourcePackageRecipeBuildCancelView",
+    "SourcePackageRecipeBuildRescoreView",
+]
 
 from zope.interface import Interface
 from zope.schema import Int
 
-from lp.app.browser.launchpadform import (
-    action,
-    LaunchpadFormView,
-    )
-from lp.buildmaster.enums import (
-    BuildQueueStatus,
-    BuildStatus,
-    )
+from lp.app.browser.launchpadform import LaunchpadFormView, action
+from lp.buildmaster.enums import BuildQueueStatus, BuildStatus
 from lp.code.interfaces.sourcepackagerecipebuild import (
     ISourcePackageRecipeBuild,
-    )
+)
 from lp.services.librarian.browser import FileNavigationMixin
 from lp.services.propertycache import cachedproperty
 from lp.services.webapp import (
-    canonical_url,
     ContextMenu,
-    enabled_with_permission,
     LaunchpadView,
     Link,
     Navigation,
-    )
+    canonical_url,
+    enabled_with_permission,
+)
 
 
 class SourcePackageRecipeBuildNavigation(Navigation, FileNavigationMixin):
@@ -47,21 +41,27 @@ class SourcePackageRecipeBuildContextMenu(ContextMenu):
 
     usedfor = ISourcePackageRecipeBuild
 
-    facet = 'branches'
+    facet = "branches"
 
-    links = ('cancel', 'rescore')
+    links = ("cancel", "rescore")
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def cancel(self):
         return Link(
-            '+cancel', 'Cancel build', icon='remove',
-            enabled=self.context.can_be_cancelled)
+            "+cancel",
+            "Cancel build",
+            icon="remove",
+            enabled=self.context.can_be_cancelled,
+        )
 
-    @enabled_with_permission('launchpad.Admin')
+    @enabled_with_permission("launchpad.Admin")
     def rescore(self):
         return Link(
-            '+rescore', 'Rescore build', icon='edit',
-            enabled=self.context.can_be_rescored)
+            "+rescore",
+            "Rescore build",
+            icon="edit",
+            enabled=self.context.can_be_rescored,
+        )
 
 
 class SourcePackageRecipeBuildView(LaunchpadView):
@@ -70,21 +70,23 @@ class SourcePackageRecipeBuildView(LaunchpadView):
     @property
     def status(self):
         """A human-friendly status string."""
-        if (self.context.status == BuildStatus.NEEDSBUILD
-            and self.eta is None):
-            return 'No suitable builders'
+        if self.context.status == BuildStatus.NEEDSBUILD and self.eta is None:
+            return "No suitable builders"
         return {
-            BuildStatus.NEEDSBUILD: 'Pending build',
-            BuildStatus.UPLOADING: 'Build uploading',
-            BuildStatus.FULLYBUILT: 'Successful build',
+            BuildStatus.NEEDSBUILD: "Pending build",
+            BuildStatus.UPLOADING: "Build uploading",
+            BuildStatus.FULLYBUILT: "Successful build",
             BuildStatus.MANUALDEPWAIT: (
-                'Could not build because of missing dependencies'),
+                "Could not build because of missing dependencies"
+            ),
             BuildStatus.CHROOTWAIT: (
-                'Could not build because of chroot problem'),
+                "Could not build because of chroot problem"
+            ),
             BuildStatus.SUPERSEDED: (
-                'Could not build because source package was superseded'),
-            BuildStatus.FAILEDTOUPLOAD: 'Could not be uploaded correctly',
-            }.get(self.context.status, self.context.status.title)
+                "Could not build because source package was superseded"
+            ),
+            BuildStatus.FAILEDTOUPLOAD: "Could not be uploaded correctly",
+        }.get(self.context.status, self.context.status.title)
 
     @cachedproperty
     def eta(self):
@@ -134,9 +136,10 @@ class SourcePackageRecipeBuildCancelView(LaunchpadFormView):
     @property
     def cancel_url(self):
         return canonical_url(self.context)
+
     next_url = cancel_url
 
-    @action('Cancel build', name='cancel')
+    @action("Cancel build", name="cancel")
     def request_action(self, action, data):
         """Cancel the build."""
         self.context.cancel()
@@ -147,9 +150,12 @@ class SourcePackageRecipeBuildRescoreView(LaunchpadFormView):
 
     class schema(Interface):
         """Schema for deleting a build."""
+
         score = Int(
-            title='Score', required=True,
-            description='The score of the recipe.')
+            title="Score",
+            required=True,
+            description="The score of the recipe.",
+        )
 
     page_title = label = "Rescore build"
 
@@ -157,19 +163,21 @@ class SourcePackageRecipeBuildRescoreView(LaunchpadFormView):
         if self.context.buildqueue_record is not None:
             return super().__call__()
         self.request.response.addWarningNotification(
-            'Cannot rescore this build because it is not queued.')
+            "Cannot rescore this build because it is not queued."
+        )
         self.request.response.redirect(canonical_url(self.context))
 
     @property
     def cancel_url(self):
         return canonical_url(self.context)
+
     next_url = cancel_url
 
-    @action('Rescore build', name='rescore')
+    @action("Rescore build", name="rescore")
     def request_action(self, action, data):
         """Rescore the build."""
-        self.context.buildqueue_record.lastscore = int(data['score'])
+        self.context.buildqueue_record.lastscore = int(data["score"])
 
     @property
     def initial_values(self):
-        return {'score': str(self.context.buildqueue_record.lastscore)}
+        return {"score": str(self.context.buildqueue_record.lastscore)}
diff --git a/lib/lp/code/browser/sourcepackagerecipelisting.py b/lib/lp/code/browser/sourcepackagerecipelisting.py
index 4b743cf..622723d 100644
--- a/lib/lp/code/browser/sourcepackagerecipelisting.py
+++ b/lib/lp/code/browser/sourcepackagerecipelisting.py
@@ -4,32 +4,30 @@
 """Base class view for sourcepackagerecipe listings."""
 
 __all__ = [
-    'BranchRecipeListingView',
-    'HasRecipesMenuMixin',
-    'PersonRecipeListingView',
-    'ProductRecipeListingView',
-    ]
+    "BranchRecipeListingView",
+    "HasRecipesMenuMixin",
+    "PersonRecipeListingView",
+    "ProductRecipeListingView",
+]
 
 
 from lp.code.browser.decorations import DecoratedBranch
 from lp.code.interfaces.branch import IBranch
 from lp.services.feeds.browser import FeedsMixin
-from lp.services.webapp import (
-    LaunchpadView,
-    Link,
-    )
+from lp.services.webapp import LaunchpadView, Link
 
 
 class HasRecipesMenuMixin:
     """A mixin for context menus for objects that implement IHasRecipes."""
 
     def view_recipes(self):
-        text = 'View source package recipes'
+        text = "View source package recipes"
         enabled = False
         if self.context.recipes.count():
             enabled = True
         return Link(
-            '+recipes', text, icon='info', enabled=enabled, site='code')
+            "+recipes", text, icon="info", enabled=enabled, site="code"
+        )
 
 
 class RecipeListingView(LaunchpadView, FeedsMixin):
@@ -41,8 +39,9 @@ class RecipeListingView(LaunchpadView, FeedsMixin):
 
     @property
     def page_title(self):
-        return 'Source Package Recipes for %(display_name)s' % {
-            'display_name': self.context.display_name}
+        return "Source Package Recipes for %(display_name)s" % {
+            "display_name": self.context.display_name
+        }
 
 
 class BranchRecipeListingView(RecipeListingView):
@@ -53,8 +52,9 @@ class BranchRecipeListingView(RecipeListingView):
         super().initialize()
         # Replace our context with a decorated branch, if it is not already
         # decorated.
-        if (IBranch.providedBy(self.context) and
-                not isinstance(self.context, DecoratedBranch)):
+        if IBranch.providedBy(self.context) and not isinstance(
+            self.context, DecoratedBranch
+        ):
             self.context = DecoratedBranch(self.context)
 
 
diff --git a/lib/lp/code/browser/summary.py b/lib/lp/code/browser/summary.py
index fdfb1e1..ccdd742 100644
--- a/lib/lp/code/browser/summary.py
+++ b/lib/lp/code/browser/summary.py
@@ -4,8 +4,8 @@
 """View classes for branch summaries."""
 
 __all__ = [
-    'BranchCountSummaryView',
-    ]
+    "BranchCountSummaryView",
+]
 
 
 from lp import _
@@ -25,7 +25,8 @@ class BranchCountSummaryView(LaunchpadView):
         """Return the branch collection for this context."""
         collection = IBranchCollection(self.context).visibleByUser(self.user)
         collection = collection.withLifecycleStatus(
-            *DEFAULT_BRANCH_STATUS_IN_LISTING)
+            *DEFAULT_BRANCH_STATUS_IN_LISTING
+        )
         return collection
 
     @cachedproperty
@@ -62,21 +63,23 @@ class BranchCountSummaryView(LaunchpadView):
     @property
     def branch_text(self):
         return get_plural_text(
-            self.branch_count, _('active branch'), _('active branches'))
+            self.branch_count, _("active branch"), _("active branches")
+        )
 
     @property
     def person_text(self):
         return get_plural_text(
-            self.person_owner_count, _('person'), _('people'))
+            self.person_owner_count, _("person"), _("people")
+        )
 
     @property
     def team_text(self):
-        return get_plural_text(self.team_owner_count, _('team'), _('teams'))
+        return get_plural_text(self.team_owner_count, _("team"), _("teams"))
 
     @property
     def commit_text(self):
-        return get_plural_text(self.commit_count, _('commit'), _('commits'))
+        return get_plural_text(self.commit_count, _("commit"), _("commits"))
 
     @property
     def committer_text(self):
-        return get_plural_text(self.committer_count, _('person'), _('people'))
+        return get_plural_text(self.committer_count, _("person"), _("people"))
diff --git a/lib/lp/code/browser/tests/test_bazaar.py b/lib/lp/code/browser/tests/test_bazaar.py
index e6d67d5..1b593be 100644
--- a/lib/lp/code/browser/tests/test_bazaar.py
+++ b/lib/lp/code/browser/tests/test_bazaar.py
@@ -9,12 +9,7 @@ from lp.app.enums import InformationType
 from lp.code.browser.bazaar import BazaarApplicationView
 from lp.services.webapp.authorization import check_permission
 from lp.services.webapp.servers import LaunchpadTestRequest
-from lp.testing import (
-    ANONYMOUS,
-    login,
-    login_person,
-    TestCaseWithFactory,
-    )
+from lp.testing import ANONYMOUS, TestCaseWithFactory, login, login_person
 from lp.testing.layers import DatabaseFunctionalLayer
 
 
@@ -34,40 +29,44 @@ class TestBazaarViewPreCacheLaunchpadPermissions(TestCaseWithFactory):
         # Create a some private branches (stacked and unstacked) that the
         # logged in user would not normally see.
         private_branch = self.factory.makeAnyBranch(
-            information_type=InformationType.USERDATA)
+            information_type=InformationType.USERDATA
+        )
         self.factory.makeAnyBranch(stacked_on=private_branch)
         branch = self.factory.makeAnyBranch()
-        recent_branches = self.getViewBranches('recently_registered_branches')
+        recent_branches = self.getViewBranches("recently_registered_branches")
         self.assertEqual(branch, recent_branches[0])
-        self.assertTrue(check_permission('launchpad.View', branch))
+        self.assertTrue(check_permission("launchpad.View", branch))
 
     def makeBranchScanned(self, branch):
         """Make the branch appear scanned."""
         revision = self.factory.makeRevision()
         # Login an administrator so they can update the branch's details.
-        login('admin@xxxxxxxxxxxxx')
+        login("admin@xxxxxxxxxxxxx")
         branch.updateScannedDetails(revision, 1)
 
     def test_recently_changed(self):
         # Create a some private branches (stacked and unstacked) that the
         # logged in user would not normally see.
         private_branch = self.factory.makeAnyBranch(
-            information_type=InformationType.USERDATA)
+            information_type=InformationType.USERDATA
+        )
         stacked_private_branch = self.factory.makeAnyBranch(
-            stacked_on=private_branch)
+            stacked_on=private_branch
+        )
         branch = self.factory.makeAnyBranch()
         self.makeBranchScanned(stacked_private_branch)
         self.makeBranchScanned(branch)
-        recent_branches = self.getViewBranches('recently_changed_branches')
+        recent_branches = self.getViewBranches("recently_changed_branches")
         self.assertEqual(branch, recent_branches[0])
-        self.assertTrue(check_permission('launchpad.View', branch))
+        self.assertTrue(check_permission("launchpad.View", branch))
 
     def test_recently_imported(self):
         # Create an import branch that is stacked on a private branch that the
         # logged in user would not normally see.  This would never happen in
         # reality, but hey, lets test the function actually works.
         private_branch = self.factory.makeAnyBranch(
-            information_type=InformationType.USERDATA)
+            information_type=InformationType.USERDATA
+        )
         # A new code import needs a real user as the sender for the outgoing
         # email.
         login_person(self.factory.makePerson())
@@ -79,6 +78,6 @@ class TestBazaarViewPreCacheLaunchpadPermissions(TestCaseWithFactory):
         branch = code_import.branch
         self.makeBranchScanned(stacked_private_branch)
         self.makeBranchScanned(branch)
-        recent_branches = self.getViewBranches('recently_imported_branches')
+        recent_branches = self.getViewBranches("recently_imported_branches")
         self.assertEqual(branch, recent_branches[0])
-        self.assertTrue(check_permission('launchpad.View', branch))
+        self.assertTrue(check_permission("launchpad.View", branch))
diff --git a/lib/lp/code/browser/tests/test_branch.py b/lib/lp/code/browser/tests/test_branch.py
index ac77d79..e8d914e 100644
--- a/lib/lp/code/browser/tests/test_branch.py
+++ b/lib/lp/code/browser/tests/test_branch.py
@@ -6,8 +6,8 @@
 from datetime import datetime
 from textwrap import dedent
 
-from fixtures import FakeLogger
 import pytz
+from fixtures import FakeLogger
 from storm.store import Store
 from testtools.matchers import Equals
 from zope.component import getUtility
@@ -19,15 +19,11 @@ from lp.app.enums import InformationType
 from lp.app.interfaces.launchpad import ILaunchpadCelebrities
 from lp.app.interfaces.services import IService
 from lp.bugs.interfaces.bugtask import (
-    BugTaskStatus,
     UNRESOLVED_BUGTASK_STATUSES,
-    )
+    BugTaskStatus,
+)
 from lp.code.browser.branch import BranchMirrorStatusView
-from lp.code.bzr import (
-    BranchFormat,
-    ControlFormat,
-    RepositoryFormat,
-    )
+from lp.code.bzr import BranchFormat, ControlFormat, RepositoryFormat
 from lp.code.enums import BranchType
 from lp.code.model.branchjob import BranchScanJob
 from lp.code.tests.helpers import BranchHostingFixture
@@ -44,32 +40,22 @@ from lp.services.webapp.publisher import canonical_url
 from lp.services.webapp.servers import LaunchpadTestRequest
 from lp.testing import (
     BrowserTestCase,
+    StormStatementRecorder,
+    TestCaseWithFactory,
     login,
     login_person,
     logout,
     person_logged_in,
-    StormStatementRecorder,
-    TestCaseWithFactory,
-    )
-from lp.testing.layers import (
-    DatabaseFunctionalLayer,
-    LaunchpadFunctionalLayer,
-    )
-from lp.testing.matchers import (
-    BrowsesWithQueryLimit,
-    Contains,
-    HasQueryCount,
-    )
+)
+from lp.testing.layers import DatabaseFunctionalLayer, LaunchpadFunctionalLayer
+from lp.testing.matchers import BrowsesWithQueryLimit, Contains, HasQueryCount
 from lp.testing.pages import (
     extract_text,
     find_tag_by_id,
     setupBrowser,
     setupBrowserForUser,
-    )
-from lp.testing.views import (
-    create_initialized_view,
-    create_view,
-    )
+)
+from lp.testing.views import create_initialized_view, create_view
 
 
 class TestBranchMirrorHidden(TestCaseWithFactory):
@@ -80,10 +66,14 @@ class TestBranchMirrorHidden(TestCaseWithFactory):
     def setUp(self):
         super().setUp()
         config.push(
-            "test", dedent("""\
+            "test",
+            dedent(
+                """\
                 [codehosting]
                 private_mirror_hosts: private.example.com
-                """))
+                """
+            ),
+        )
 
     def tearDown(self):
         config.pop("test")
@@ -93,17 +83,20 @@ class TestBranchMirrorHidden(TestCaseWithFactory):
         # A branch from a normal location is fine.
         branch = self.factory.makeAnyBranch(
             branch_type=BranchType.MIRRORED,
-            url="http://example.com/good/mirror";)
-        view = create_initialized_view(branch, '+index')
+            url="http://example.com/good/mirror";,
+        )
+        view = create_initialized_view(branch, "+index")
         self.assertTrue(view.user is None)
         self.assertEqual(
-            "http://example.com/good/mirror";, view.mirror_location)
+            "http://example.com/good/mirror";, view.mirror_location
+        )
 
     def testLocationlessRemoteBranch(self):
         # A branch from a normal location is fine.
         branch = self.factory.makeAnyBranch(
-            branch_type=BranchType.REMOTE, url=None)
-        view = create_initialized_view(branch, '+index')
+            branch_type=BranchType.REMOTE, url=None
+        )
+        view = create_initialized_view(branch, "+index")
         self.assertTrue(view.user is None)
         self.assertIs(None, view.mirror_location)
 
@@ -112,8 +105,9 @@ class TestBranchMirrorHidden(TestCaseWithFactory):
         # anonymous browsers.
         branch = self.factory.makeAnyBranch(
             branch_type=BranchType.MIRRORED,
-            url="http://private.example.com/bzr-mysql/mysql-5.0";)
-        view = create_initialized_view(branch, '+index')
+            url="http://private.example.com/bzr-mysql/mysql-5.0";,
+        )
+        view = create_initialized_view(branch, "+index")
         self.assertTrue(view.user is None)
         self.assertEqual("<private server>", view.mirror_location)
 
@@ -123,14 +117,17 @@ class TestBranchMirrorHidden(TestCaseWithFactory):
         owner = self.factory.makePerson(email="eric@xxxxxxxxxxx")
         branch = self.factory.makeAnyBranch(
             branch_type=BranchType.MIRRORED,
-            owner=owner, url="http://private.example.com/bzr-mysql/mysql-5.0";)
+            owner=owner,
+            url="http://private.example.com/bzr-mysql/mysql-5.0";,
+        )
         # Now log in the owner.
-        login('eric@xxxxxxxxxxx')
-        view = create_initialized_view(branch, '+index')
+        login("eric@xxxxxxxxxxx")
+        view = create_initialized_view(branch, "+index")
         self.assertEqual(view.user, owner)
         self.assertEqual(
             "http://private.example.com/bzr-mysql/mysql-5.0";,
-            view.mirror_location)
+            view.mirror_location,
+        )
 
     def testHiddenBranchAsOtherLoggedInUser(self):
         # A branch location with a defined private host is hidden from other
@@ -138,11 +135,13 @@ class TestBranchMirrorHidden(TestCaseWithFactory):
         owner = self.factory.makePerson(email="eric@xxxxxxxxxxx")
         other = self.factory.makePerson(email="other@xxxxxxxxxxx")
         branch = self.factory.makeAnyBranch(
-            branch_type=BranchType.MIRRORED, owner=owner,
-            url="http://private.example.com/bzr-mysql/mysql-5.0";)
+            branch_type=BranchType.MIRRORED,
+            owner=owner,
+            url="http://private.example.com/bzr-mysql/mysql-5.0";,
+        )
         # Now log in the other person.
-        login('other@xxxxxxxxxxx')
-        view = create_initialized_view(branch, '+index')
+        login("other@xxxxxxxxxxx")
+        view = create_initialized_view(branch, "+index")
         self.assertEqual(view.user, other)
         self.assertEqual("<private server>", view.mirror_location)
 
@@ -155,43 +154,50 @@ class TestBranchView(BrowserTestCase):
         """mirror_status_message is truncated if the text is overly long."""
         branch = self.factory.makeBranch(branch_type=BranchType.MIRRORED)
         branch.mirrorFailed(
-            "on quick brown fox the dog jumps to" *
-            BranchMirrorStatusView.MAXIMUM_STATUS_MESSAGE_LENGTH)
-        branch_view = create_view(branch, '+mirror-status')
+            "on quick brown fox the dog jumps to"
+            * BranchMirrorStatusView.MAXIMUM_STATUS_MESSAGE_LENGTH
+        )
+        branch_view = create_view(branch, "+mirror-status")
         self.assertEqual(
-            truncate_text(branch.mirror_status_message,
-                          branch_view.MAXIMUM_STATUS_MESSAGE_LENGTH) + ' ...',
-            branch_view.mirror_status_message)
+            truncate_text(
+                branch.mirror_status_message,
+                branch_view.MAXIMUM_STATUS_MESSAGE_LENGTH,
+            )
+            + " ...",
+            branch_view.mirror_status_message,
+        )
 
     def testMirrorStatusMessage(self):
         """mirror_status_message on the view is the same as on the branch."""
         branch = self.factory.makeBranch(branch_type=BranchType.MIRRORED)
         branch.mirrorFailed("This is a short error message.")
-        branch_view = create_view(branch, '+mirror-status')
+        branch_view = create_view(branch, "+mirror-status")
         self.assertTrue(
             len(branch.mirror_status_message)
             <= branch_view.MAXIMUM_STATUS_MESSAGE_LENGTH,
             "branch.mirror_status_message longer than expected: %r"
-            % (branch.mirror_status_message, ))
+            % (branch.mirror_status_message,),
+        )
         self.assertEqual(
-            branch.mirror_status_message, branch_view.mirror_status_message)
+            branch.mirror_status_message, branch_view.mirror_status_message
+        )
         self.assertEqual(
-            "This is a short error message.",
-            branch_view.mirror_status_message)
+            "This is a short error message.", branch_view.mirror_status_message
+        )
 
     def testShowMergeLinksOnManyBranchProject(self):
         # The merge links are shown on projects that have multiple branches.
-        product = self.factory.makeProduct(name='super-awesome-project')
+        product = self.factory.makeProduct(name="super-awesome-project")
         branch = self.factory.makeAnyBranch(product=product)
         self.factory.makeAnyBranch(product=product)
-        view = create_initialized_view(branch, '+index')
+        view = create_initialized_view(branch, "+index")
         self.assertTrue(view.show_merge_links)
 
     def testShowMergeLinksOnJunkBranch(self):
         # The merge links are not shown on junk branches because they do not
         # support merge proposals.
         junk_branch = self.factory.makeBranch(product=None)
-        view = create_initialized_view(junk_branch, '+index')
+        view = create_initialized_view(junk_branch, "+index")
         self.assertFalse(view.show_merge_links)
 
     def testShowMergeLinksOnSingleBranchProject(self):
@@ -199,91 +205,94 @@ class TestBranchView(BrowserTestCase):
         # only has one branch because it's pointless to propose it for merging
         # if there's nothing to merge into.
         branch = self.factory.makeAnyBranch()
-        view = create_initialized_view(branch, '+index')
+        view = create_initialized_view(branch, "+index")
         self.assertFalse(view.show_merge_links)
 
     def testNoProductSeriesPushingTranslations(self):
         # By default, a branch view shows no product series pushing
         # translations to the branch.
         branch = self.factory.makeBranch()
-        view = create_initialized_view(branch, '+index')
+        view = create_initialized_view(branch, "+index")
         self.assertEqual(list(view.translations_sources()), [])
 
     def testProductSeriesPushingTranslations(self):
         # If a product series exports its translations to the branch,
         # the view shows it.
         product = self.factory.makeProduct()
-        trunk = product.getSeries('trunk')
+        trunk = product.getSeries("trunk")
         branch = self.factory.makeBranch(owner=product.owner)
         removeSecurityProxy(trunk).translations_branch = branch
-        view = create_initialized_view(branch, '+index')
+        view = create_initialized_view(branch, "+index")
         self.assertEqual(list(view.translations_sources()), [trunk])
 
     def test_is_empty_directory(self):
         # Branches are considered empty until they get a control format.
         branch = self.factory.makeBranch()
-        view = create_initialized_view(branch, '+index')
+        view = create_initialized_view(branch, "+index")
         self.assertTrue(view.is_empty_directory)
         with person_logged_in(branch.owner):
             # Make it look as though the branch has been pushed.
             branch.branchChanged(
-                None, None, ControlFormat.BZR_METADIR_1, None, None)
+                None, None, ControlFormat.BZR_METADIR_1, None, None
+            )
         self.assertFalse(view.is_empty_directory)
 
     def test_empty_directories_use_existing(self):
         # Push example should include --use-existing-dir for empty directories.
         branch = self.factory.makeBranch(owner=self.user)
         text = self.getMainText(branch)
-        self.assertIn('push\n--use-existing-dir', text)
+        self.assertIn("push\n--use-existing-dir", text)
         with person_logged_in(self.user):
             # Make it look as though the branch has been pushed.
             branch.branchChanged(
-                None, None, ControlFormat.BZR_METADIR_1, None, None)
+                None, None, ControlFormat.BZR_METADIR_1, None, None
+            )
         text = self.getMainText(branch)
-        self.assertNotIn('push\n--use-existing-dir', text)
+        self.assertNotIn("push\n--use-existing-dir", text)
 
     def test_user_can_upload(self):
         # A user can upload if they have edit permissions.
         branch = self.factory.makeAnyBranch()
-        view = create_initialized_view(branch, '+index')
+        view = create_initialized_view(branch, "+index")
         login_person(branch.owner)
         self.assertTrue(view.user_can_upload)
 
     def test_user_can_upload_admins_can(self):
         # Admins can upload to any hosted branch.
         branch = self.factory.makeAnyBranch()
-        view = create_initialized_view(branch, '+index')
-        login('admin@xxxxxxxxxxxxx')
+        view = create_initialized_view(branch, "+index")
+        login("admin@xxxxxxxxxxxxx")
         self.assertTrue(view.user_can_upload)
 
     def test_user_can_upload_non_owner(self):
         # Someone not associated with the branch cannot upload
         branch = self.factory.makeAnyBranch()
-        view = create_initialized_view(branch, '+index')
+        view = create_initialized_view(branch, "+index")
         login_person(self.factory.makePerson())
         self.assertFalse(view.user_can_upload)
 
     def test_user_can_upload_mirrored(self):
         # Even the owner of a mirrored branch can't upload.
         branch = self.factory.makeAnyBranch(branch_type=BranchType.MIRRORED)
-        view = create_initialized_view(branch, '+index')
+        view = create_initialized_view(branch, "+index")
         login_person(branch.owner)
         self.assertFalse(view.user_can_upload)
 
     def test_recipes_link_no_recipes(self):
         # A branch with no recipes does not show a recipes link.
         branch = self.factory.makeAnyBranch()
-        view = create_initialized_view(branch, '+index')
-        self.assertEqual('No recipes using this branch.', view.recipes_link)
+        view = create_initialized_view(branch, "+index")
+        self.assertEqual("No recipes using this branch.", view.recipes_link)
 
     def test_recipes_link_one_recipe(self):
         # A branch with one recipe shows a link to that recipe.
         branch = self.factory.makeAnyBranch()
         recipe = self.factory.makeSourcePackageRecipe(branches=[branch])
-        view = create_initialized_view(branch, '+index')
+        view = create_initialized_view(branch, "+index")
         expected_link = (
-            '<a href="%s">1 recipe</a> using this branch.' %
-            canonical_url(recipe))
+            '<a href="%s">1 recipe</a> using this branch.'
+            % canonical_url(recipe)
+        )
         self.assertEqual(expected_link, view.recipes_link)
 
     def test_recipes_link_more_recipes(self):
@@ -291,16 +300,17 @@ class TestBranchView(BrowserTestCase):
         branch = self.factory.makeAnyBranch()
         self.factory.makeSourcePackageRecipe(branches=[branch])
         self.factory.makeSourcePackageRecipe(branches=[branch])
-        view = create_initialized_view(branch, '+index')
+        view = create_initialized_view(branch, "+index")
         self.assertEqual(
             '<a href="+recipes">2 recipes</a> using this branch.',
-            view.recipes_link)
+            view.recipes_link,
+        )
 
     def test_show_rescan_link(self):
         branch = self.factory.makeAnyBranch()
         job = BranchScanJob.create(branch)
         job.job._status = JobStatus.FAILED
-        view = create_initialized_view(branch, '+index')
+        view = create_initialized_view(branch, "+index")
         result = view.show_rescan_link
         self.assertTrue(result)
 
@@ -309,13 +319,13 @@ class TestBranchView(BrowserTestCase):
         job = BranchScanJob.create(branch)
         job.job._status = JobStatus.COMPLETED
         job.job.date_finished = UTC_NOW
-        view = create_initialized_view(branch, '+index')
+        view = create_initialized_view(branch, "+index")
         result = view.show_rescan_link
         self.assertFalse(result)
 
     def test_show_rescan_link_no_scan_jobs(self):
         branch = self.factory.makeAnyBranch()
-        view = create_initialized_view(branch, '+index')
+        view = create_initialized_view(branch, "+index")
         result = view.show_rescan_link
         self.assertFalse(result)
 
@@ -325,7 +335,7 @@ class TestBranchView(BrowserTestCase):
         job.job._status = JobStatus.FAILED
         job = BranchScanJob.create(branch)
         job.job._status = JobStatus.COMPLETED
-        view = create_initialized_view(branch, '+index')
+        view = create_initialized_view(branch, "+index")
         result = view.show_rescan_link
         self.assertTrue(result)
 
@@ -339,7 +349,7 @@ class TestBranchView(BrowserTestCase):
         branch = self.factory.makeAnyBranch()
         with person_logged_in(branch.owner):
             self._addBugLinks(branch)
-        view = create_initialized_view(branch, '+index')
+        view = create_initialized_view(branch, "+index")
         self.assertEqual(len(BugTaskStatus), len(view.linked_bugtasks))
         self.assertFalse(view.context.is_series_branch)
 
@@ -349,14 +359,16 @@ class TestBranchView(BrowserTestCase):
         branch = self.factory.makeAnyBranch()
         reporter = self.factory.makePerson()
         bug = self.factory.makeBug(
-            owner=reporter, information_type=InformationType.USERDATA)
+            owner=reporter, information_type=InformationType.USERDATA
+        )
         with person_logged_in(reporter):
             branch.linkBug(bug, reporter)
-            view = create_initialized_view(branch, '+index')
-            self.assertEqual([bug.id],
-                [task.bug.id for task in view.linked_bugtasks])
+            view = create_initialized_view(branch, "+index")
+            self.assertEqual(
+                [bug.id], [task.bug.id for task in view.linked_bugtasks]
+            )
         with person_logged_in(branch.owner):
-            view = create_initialized_view(branch, '+index')
+            view = create_initialized_view(branch, "+index")
             self.assertEqual([], view.linked_bugtasks)
 
     def test_linked_bugtasks_series_branch(self):
@@ -367,10 +379,9 @@ class TestBranchView(BrowserTestCase):
             product.development_focus.branch = branch
         with person_logged_in(branch.owner):
             self._addBugLinks(branch)
-        view = create_initialized_view(branch, '+index')
+        view = create_initialized_view(branch, "+index")
         for bugtask in view.linked_bugtasks:
-            self.assertTrue(
-                bugtask.status in UNRESOLVED_BUGTASK_STATUSES)
+            self.assertTrue(bugtask.status in UNRESOLVED_BUGTASK_STATUSES)
 
     def test_linked_bugs_nonseries_branch_query_scaling(self):
         # As we add linked bugs, the query count for a branch index page stays
@@ -392,7 +403,8 @@ class TestBranchView(BrowserTestCase):
         # As we add linked bugs, the query count for a branch index page stays
         # constant.
         product = self.factory.makeProduct(
-            branch_sharing_policy=BranchSharingPolicy.PUBLIC)
+            branch_sharing_policy=BranchSharingPolicy.PUBLIC
+        )
         branch = self.factory.makeProductBranch(product=product)
         browses_under_limit = BrowsesWithQueryLimit(54, branch.owner)
         with person_logged_in(product.owner):
@@ -415,7 +427,9 @@ class TestBranchView(BrowserTestCase):
                 author="Eric the Viking <eric@xxxxxxxxxxxxxxxxxxxxxxxx>",
                 log_body=(
                     "Testing the email address in revisions\n"
-                    "email Bob (bob@xxxxxxxxxxx) for details."))
+                    "email Bob (bob@xxxxxxxxxxx) for details."
+                ),
+            )
 
             branch_revision = branch.createBranchRevision(seq, revision)
             branch.updateScannedDetails(revision, seq)
@@ -428,7 +442,7 @@ class TestBranchView(BrowserTestCase):
         with person_logged_in(branch.owner):
             self._add_revisions(branch)
         browser = self.getUserBrowser(canonical_url(branch))
-        tag = find_tag_by_id(browser.contents, 'recent-revisions')
+        tag = find_tag_by_id(browser.contents, "recent-revisions")
         text = extract_text(tag)
         expected_text = """
             Recent revisions
@@ -451,7 +465,7 @@ class TestBranchView(BrowserTestCase):
         browser = setupBrowser()
         logout()
         browser.open(branch_url)
-        tag = find_tag_by_id(browser.contents, 'recent-revisions')
+        tag = find_tag_by_id(browser.contents, "recent-revisions")
         text = extract_text(tag)
         expected_text = """
             Recent revisions
@@ -471,20 +485,21 @@ class TestBranchView(BrowserTestCase):
         with person_logged_in(branch.owner):
             revisions = self._add_revisions(branch, 2)
             mp = self.factory.makeBranchMergeProposal(
-                target_branch=branch, registrant=branch.owner)
+                target_branch=branch, registrant=branch.owner
+            )
             mp.markAsMerged(merged_revno=revisions[0].sequence)
 
             # These values are extracted here and used below.
-            mp_url = canonical_url(mp, rootsite='code', force_local_path=True)
+            mp_url = canonical_url(mp, rootsite="code", force_local_path=True)
             branch_display_name = mp.source_branch.displayname
 
         browser = self.getUserBrowser(canonical_url(branch))
 
-        revision_content = find_tag_by_id(
-            browser.contents, 'recent-revisions')
+        revision_content = find_tag_by_id(browser.contents, "recent-revisions")
 
         text = extract_text(revision_content)
-        expected_text = """
+        expected_text = (
+            """
             Recent revisions
             .*
             2. By Eric the Viking &lt;eric@xxxxxxxxxxxxxxxxxxxxxxxx&gt;
@@ -496,12 +511,14 @@ class TestBranchView(BrowserTestCase):
             Testing the email address in revisions\n
             email Bob \\(bob@xxxxxxxxxxx\\) for details.
             Merged branch %s
-            """ % branch_display_name
+            """
+            % branch_display_name
+        )
 
         self.assertTextMatchesExpressionIgnoreWhitespace(expected_text, text)
 
-        links = revision_content.find_all('a')
-        self.assertEqual(mp_url, links[2]['href'])
+        links = revision_content.find_all("a")
+        self.assertEqual(mp_url, links[2]["href"])
 
     def test_recent_revisions_with_merge_proposals_and_bug_links(self):
         # Revisions which result from merging in a branch with a merge
@@ -512,7 +529,8 @@ class TestBranchView(BrowserTestCase):
         with person_logged_in(branch.owner):
             revisions = self._add_revisions(branch, 2)
             mp = self.factory.makeBranchMergeProposal(
-                target_branch=branch, registrant=branch.owner)
+                target_branch=branch, registrant=branch.owner
+            )
             mp.markAsMerged(merged_revno=revisions[0].sequence)
 
             # record linked bug info for use below
@@ -522,7 +540,8 @@ class TestBranchView(BrowserTestCase):
                 bug = self.factory.makeBug()
                 mp.source_branch.linkBug(bug, branch.owner)
                 linked_bug_urls.append(
-                    canonical_url(bug.default_bugtask, rootsite='bugs'))
+                    canonical_url(bug.default_bugtask, rootsite="bugs")
+                )
                 bug_text = "Bug #%s: %s" % (bug.id, bug.title)
                 linked_bug_text.append(bug_text)
 
@@ -534,8 +553,7 @@ class TestBranchView(BrowserTestCase):
 
         browser = self.getUserBrowser(canonical_url(branch))
 
-        revision_content = find_tag_by_id(
-            browser.contents, 'recent-revisions')
+        revision_content = find_tag_by_id(browser.contents, "recent-revisions")
 
         text = extract_text(revision_content)
         expected_text = """
@@ -551,15 +569,18 @@ class TestBranchView(BrowserTestCase):
             email Bob \\(bob@xxxxxxxxxxx\\) for details.
             Merged branch %s
             %s
-            """ % (branch_display_name, linked_bug_rendered_text)
+            """ % (
+            branch_display_name,
+            linked_bug_rendered_text,
+        )
 
         self.assertTextMatchesExpressionIgnoreWhitespace(expected_text, text)
 
-        links = revision_content.find_all('a')
-        self.assertEqual(mp_url, links[2]['href'])
-        self.assertEqual(branch_url, links[3]['href'])
-        self.assertEqual(linked_bug_urls[0], links[4]['href'])
-        self.assertEqual(linked_bug_urls[1], links[5]['href'])
+        links = revision_content.find_all("a")
+        self.assertEqual(mp_url, links[2]["href"])
+        self.assertEqual(branch_url, links[3]["href"])
+        self.assertEqual(linked_bug_urls[0], links[4]["href"])
+        self.assertEqual(linked_bug_urls[1], links[5]["href"])
 
     def test_view_for_user_with_artifact_grant(self):
         # Users with an artifact grant for a branch related to a private
@@ -567,15 +588,18 @@ class TestBranchView(BrowserTestCase):
         owner = self.factory.makePerson()
         user = self.factory.makePerson()
         product = self.factory.makeProduct(
-            owner=owner,
-            information_type=InformationType.PROPRIETARY)
+            owner=owner, information_type=InformationType.PROPRIETARY
+        )
         with person_logged_in(owner):
             product_name = product.name
             branch = self.factory.makeBranch(
-                product=product, owner=owner,
-                information_type=InformationType.PROPRIETARY)
-            getUtility(IService, 'sharing').ensureAccessGrants(
-                [user], owner, branches=[branch])
+                product=product,
+                owner=owner,
+                information_type=InformationType.PROPRIETARY,
+            )
+            getUtility(IService, "sharing").ensureAccessGrants(
+                [user], owner, branches=[branch]
+            )
         with person_logged_in(user):
             url = canonical_url(branch)
         # The main check: No Unauthorized error should be raised.
@@ -591,11 +615,13 @@ class TestBranchView(BrowserTestCase):
         source = self.factory.makeBranch(stacked_on=stacked, product=product)
         prereq = self.factory.makeBranch(product=product)
         self.factory.makeBranchMergeProposal(
-            source_branch=source, target_branch=branch,
-            prerequisite_branch=prereq)
+            source_branch=source,
+            target_branch=branch,
+            prerequisite_branch=prereq,
+        )
         Store.of(branch).flush()
         Store.of(branch).invalidate()
-        view = create_view(branch, '+index')
+        view = create_view(branch, "+index")
         with StormStatementRecorder() as recorder:
             view.landing_candidates
         self.assertThat(recorder, HasQueryCount(Equals(14)))
@@ -609,11 +635,13 @@ class TestBranchView(BrowserTestCase):
         target = self.factory.makeBranch(stacked_on=stacked, product=product)
         prereq = self.factory.makeBranch(product=product)
         self.factory.makeBranchMergeProposal(
-            source_branch=branch, target_branch=target,
-            prerequisite_branch=prereq)
+            source_branch=branch,
+            target_branch=target,
+            prerequisite_branch=prereq,
+        )
         Store.of(branch).flush()
         Store.of(branch).invalidate()
-        view = create_view(branch, '+index')
+        view = create_view(branch, "+index")
         with StormStatementRecorder() as recorder:
             view.landing_targets
         self.assertThat(recorder, HasQueryCount(Equals(13)))
@@ -625,7 +653,8 @@ class TestBranchView(BrowserTestCase):
         Store.of(branch).flush()
         Store.of(branch).invalidate()
         view = create_initialized_view(
-            branch, '+branch-portlet-subscriber-content')
+            branch, "+branch-portlet-subscriber-content"
+        )
         with StormStatementRecorder() as recorder:
             view.render()
         self.assertThat(recorder, HasQueryCount(Equals(6)))
@@ -636,7 +665,7 @@ class TestBranchView(BrowserTestCase):
             self.factory.makeBranchSubscription(branch=branch)
         Store.of(branch).flush()
         Store.of(branch).invalidate()
-        branch_url = canonical_url(branch, view_name='+index', rootsite='code')
+        branch_url = canonical_url(branch, view_name="+index", rootsite="code")
         browser = setupBrowser()
         logout()
         with StormStatementRecorder() as recorder:
@@ -653,10 +682,11 @@ class TestBranchRescanView(BrowserTestCase):
         job = BranchScanJob.create(branch)
         job.job._status = JobStatus.FAILED
         branch_url = canonical_url(
-            branch, view_name='+rescan', rootsite='code')
+            branch, view_name="+rescan", rootsite="code"
+        )
         browser = self.getUserBrowser(branch_url, user=branch.owner)
         browser.open(branch_url)
-        self.assertIn('schedule a rescan', browser.contents)
+        self.assertIn("schedule a rescan", browser.contents)
 
     def test_product_owner_can_see_rescan(self):
         project_owner = self.factory.makePerson()
@@ -665,23 +695,24 @@ class TestBranchRescanView(BrowserTestCase):
         job = BranchScanJob.create(branch)
         job.job._status = JobStatus.FAILED
         branch_url = canonical_url(
-            branch, view_name='+rescan', rootsite='code')
+            branch, view_name="+rescan", rootsite="code"
+        )
         browser = self.getUserBrowser(branch_url, user=project_owner)
         browser.open(branch_url)
-        self.assertIn('schedule a rescan', browser.contents)
+        self.assertIn("schedule a rescan", browser.contents)
 
     def test_other_user_can_not_see_rescan(self):
         branch = self.factory.makeAnyBranch()
         job = BranchScanJob.create(branch)
         job.job._status = JobStatus.FAILED
         branch_url = canonical_url(
-            branch, view_name='+rescan', rootsite='code')
-        self.assertRaises(
-            Unauthorized, self.getUserBrowser, branch_url)
+            branch, view_name="+rescan", rootsite="code"
+        )
+        self.assertRaises(Unauthorized, self.getUserBrowser, branch_url)
 
 
 class TestBranchViewPrivateArtifacts(BrowserTestCase):
-    """ Tests that branches with private team artifacts can be viewed.
+    """Tests that branches with private team artifacts can be viewed.
 
     A Branch may be associated with a private team as follows:
     - the owner is a private team
@@ -707,25 +738,27 @@ class TestBranchViewPrivateArtifacts(BrowserTestCase):
     def test_view_branch_with_private_owner(self):
         # A branch with a private owner is rendered.
         private_owner = self.factory.makeTeam(
-            displayname="PrivateTeam", visibility=PersonVisibility.PRIVATE)
+            displayname="PrivateTeam", visibility=PersonVisibility.PRIVATE
+        )
         with person_logged_in(private_owner):
             branch = self.factory.makeAnyBranch(owner=private_owner)
         # Ensure the branch owner is rendered.
-        url = canonical_url(branch, rootsite='code')
+        url = canonical_url(branch, rootsite="code")
         user = self.factory.makePerson()
         browser = self._getBrowser(user)
         browser.open(url)
         soup = BeautifulSoup(browser.contents)
-        self.assertIsNotNone(soup.find('a', text="PrivateTeam"))
+        self.assertIsNotNone(soup.find("a", text="PrivateTeam"))
 
     def test_view_private_branch_with_private_owner(self):
         # A private branch with a private owner is rendered.
         private_owner = self.factory.makeTeam(
-            displayname="PrivateTeam", visibility=PersonVisibility.PRIVATE)
+            displayname="PrivateTeam", visibility=PersonVisibility.PRIVATE
+        )
         with person_logged_in(private_owner):
             branch = self.factory.makeAnyBranch(owner=private_owner)
         # Ensure the branch owner is rendered.
-        url = canonical_url(branch, rootsite='code')
+        url = canonical_url(branch, rootsite="code")
         user = self.factory.makePerson()
         # Subscribe the user so they can see the branch.
         with person_logged_in(private_owner):
@@ -733,87 +766,97 @@ class TestBranchViewPrivateArtifacts(BrowserTestCase):
         browser = self._getBrowser(user)
         browser.open(url)
         soup = BeautifulSoup(browser.contents)
-        self.assertIsNotNone(soup.find('a', text="PrivateTeam"))
+        self.assertIsNotNone(soup.find("a", text="PrivateTeam"))
 
     def test_anonymous_view_branch_with_private_owner(self):
         # A branch with a private owner is not rendered for anon users.
         self.useFixture(FakeLogger())
         private_owner = self.factory.makeTeam(
-            visibility=PersonVisibility.PRIVATE)
+            visibility=PersonVisibility.PRIVATE
+        )
         with person_logged_in(private_owner):
             branch = self.factory.makeAnyBranch(owner=private_owner)
         # Viewing the branch results in an error.
-        url = canonical_url(branch, rootsite='code')
+        url = canonical_url(branch, rootsite="code")
         browser = self._getBrowser()
         self.assertRaises(NotFound, browser.open, url)
 
     def test_view_branch_with_private_subscriber(self):
         # A branch with a private subscriber is rendered.
         private_subscriber = self.factory.makeTeam(
-            name="privateteam", visibility=PersonVisibility.PRIVATE)
+            name="privateteam", visibility=PersonVisibility.PRIVATE
+        )
         branch = self.factory.makeAnyBranch()
         with person_logged_in(branch.owner):
             self.factory.makeBranchSubscription(
-                branch, private_subscriber, branch.owner)
+                branch, private_subscriber, branch.owner
+            )
         # Ensure the branch subscriber is rendered.
-        url = canonical_url(branch, rootsite='code')
+        url = canonical_url(branch, rootsite="code")
         user = self.factory.makePerson()
         browser = self._getBrowser(user)
         browser.open(url)
         soup = BeautifulSoup(browser.contents)
         self.assertIsNotNone(
-            soup.find('div', attrs={'id': 'subscriber-privateteam'}))
+            soup.find("div", attrs={"id": "subscriber-privateteam"})
+        )
 
     def test_anonymous_view_branch_with_private_subscriber(self):
         # Private branch subscribers are not rendered for anon users.
         private_subscriber = self.factory.makeTeam(
-            name="privateteam", visibility=PersonVisibility.PRIVATE)
+            name="privateteam", visibility=PersonVisibility.PRIVATE
+        )
         branch = self.factory.makeAnyBranch()
         with person_logged_in(private_subscriber):
             self.factory.makeBranchSubscription(
-                branch, private_subscriber, branch.owner)
+                branch, private_subscriber, branch.owner
+            )
         # Viewing the branch doesn't show the private subscriber.
-        url = canonical_url(branch, rootsite='code')
+        url = canonical_url(branch, rootsite="code")
         browser = self._getBrowser()
         browser.open(url)
         soup = BeautifulSoup(browser.contents)
         self.assertIsNone(
-            soup.find('div', attrs={'id': 'subscriber-privateteam'}))
+            soup.find("div", attrs={"id": "subscriber-privateteam"})
+        )
 
     def _createPrivateMergeProposalVotes(self):
         private_reviewer = self.factory.makeTeam(
-            name="privateteam", visibility=PersonVisibility.PRIVATE)
+            name="privateteam", visibility=PersonVisibility.PRIVATE
+        )
         product = self.factory.makeProduct()
         branch = self.factory.makeProductBranch(product=product)
         target_branch = self.factory.makeProductBranch(product=product)
         with person_logged_in(branch.owner):
             self.factory.makeBranchMergeProposal(
-                source_branch=branch, target_branch=target_branch,
-                reviewer=removeSecurityProxy(private_reviewer))
+                source_branch=branch,
+                target_branch=target_branch,
+                reviewer=removeSecurityProxy(private_reviewer),
+            )
         return branch
 
     def test_view_branch_with_private_reviewer(self):
         # A branch with a private reviewer is rendered.
         branch = self._createPrivateMergeProposalVotes()
         # Ensure the branch reviewers are rendered.
-        url = canonical_url(branch, rootsite='code')
+        url = canonical_url(branch, rootsite="code")
         user = self.factory.makePerson()
         browser = self._getBrowser(user)
         browser.open(url)
         soup = BeautifulSoup(browser.contents)
-        reviews_list = soup.find('dl', attrs={'class': 'reviews'})
-        self.assertIsNotNone(reviews_list.find('a', text='Privateteam'))
+        reviews_list = soup.find("dl", attrs={"class": "reviews"})
+        self.assertIsNotNone(reviews_list.find("a", text="Privateteam"))
 
     def test_anonymous_view_branch_with_private_reviewer(self):
         # A branch with a private reviewer is rendered.
         branch = self._createPrivateMergeProposalVotes()
         # Viewing the branch doesn't show the private reviewers.
-        url = canonical_url(branch, rootsite='code')
+        url = canonical_url(branch, rootsite="code")
         browser = self._getBrowser()
         browser.open(url)
         soup = BeautifulSoup(browser.contents)
-        reviews_list = soup.find('dl', attrs={'class': 'reviews'})
-        self.assertIsNone(reviews_list.find('a', text='Privateteam'))
+        reviews_list = soup.find("dl", attrs={"class": "reviews"})
+        self.assertIsNone(reviews_list.find("a", text="Privateteam"))
 
     def test_unsubscribe_private_branch(self):
         # Unsubscribing from a branch with a policy grant still allows the
@@ -822,21 +865,27 @@ class TestBranchViewPrivateArtifacts(BrowserTestCase):
         owner = self.factory.makePerson()
         subscriber = self.factory.makePerson()
         [ap] = getUtility(IAccessPolicySource).find(
-            [(product, InformationType.USERDATA)])
+            [(product, InformationType.USERDATA)]
+        )
         self.factory.makeAccessPolicyGrant(
-            policy=ap, grantee=subscriber, grantor=product.owner)
+            policy=ap, grantee=subscriber, grantor=product.owner
+        )
         branch = self.factory.makeBranch(
-            product=product, owner=owner,
-            information_type=InformationType.USERDATA)
+            product=product,
+            owner=owner,
+            information_type=InformationType.USERDATA,
+        )
         with person_logged_in(owner):
             self.factory.makeBranchSubscription(branch, subscriber, owner)
-            base_url = canonical_url(branch, rootsite='code')
-            expected_title = '%s : Code : %s' % (
-                branch.name, product.displayname)
-        url = '%s/+subscription/%s' % (base_url, subscriber.name)
+            base_url = canonical_url(branch, rootsite="code")
+            expected_title = "%s : Code : %s" % (
+                branch.name,
+                product.displayname,
+            )
+        url = "%s/+subscription/%s" % (base_url, subscriber.name)
         browser = self._getBrowser(user=subscriber)
         browser.open(url)
-        browser.getControl('Unsubscribe').click()
+        browser.getControl("Unsubscribe").click()
         self.assertEqual(base_url, browser.url)
         self.assertEqual(expected_title, browser.title)
 
@@ -847,17 +896,19 @@ class TestBranchViewPrivateArtifacts(BrowserTestCase):
         owner = self.factory.makePerson()
         subscriber = self.factory.makePerson()
         branch = self.factory.makeBranch(
-            product=product, owner=owner,
-            information_type=InformationType.USERDATA)
+            product=product,
+            owner=owner,
+            information_type=InformationType.USERDATA,
+        )
         with person_logged_in(owner):
             self.factory.makeBranchSubscription(branch, subscriber, owner)
-            base_url = canonical_url(branch, rootsite='code')
-            product_url = canonical_url(product, rootsite='code')
-        url = '%s/+subscription/%s' % (base_url, subscriber.name)
+            base_url = canonical_url(branch, rootsite="code")
+            product_url = canonical_url(product, rootsite="code")
+        url = "%s/+subscription/%s" % (base_url, subscriber.name)
         expected_title = "Code : %s" % product.displayname
         browser = self._getBrowser(user=subscriber)
         browser.open(url)
-        browser.getControl('Unsubscribe').click()
+        browser.getControl("Unsubscribe").click()
         self.assertEqual(product_url, browser.url)
         self.assertEqual(expected_title, browser.title)
 
@@ -872,28 +923,29 @@ class TestBranchReviewerEditView(TestCaseWithFactory):
         # the branch.
         branch = self.factory.makeAnyBranch()
         self.assertIs(None, branch.reviewer)
-        view = create_view(branch, '+reviewer')
-        self.assertEqual(branch.owner, view.initial_values['reviewer'])
+        view = create_view(branch, "+reviewer")
+        self.assertEqual(branch.owner, view.initial_values["reviewer"])
 
     def test_initial_reviewer_set(self):
         # If the reviewer has been set, it is shown as the initial value.
         branch = self.factory.makeAnyBranch()
         login_person(branch.owner)
         branch.reviewer = self.factory.makePerson()
-        view = create_view(branch, '+reviewer')
-        self.assertEqual(branch.reviewer, view.initial_values['reviewer'])
+        view = create_view(branch, "+reviewer")
+        self.assertEqual(branch.reviewer, view.initial_values["reviewer"])
 
     def test_set_reviewer(self):
         # Test setting the reviewer.
         branch = self.factory.makeAnyBranch()
         reviewer = self.factory.makePerson()
         login_person(branch.owner)
-        view = create_initialized_view(branch, '+reviewer')
-        view.change_action.success({'reviewer': reviewer})
+        view = create_initialized_view(branch, "+reviewer")
+        view.change_action.success({"reviewer": reviewer})
         self.assertEqual(reviewer, branch.reviewer)
         # Last modified has been updated.
         self.assertSqlAttributeEqualsDate(
-            branch, 'date_last_modified', UTC_NOW)
+            branch, "date_last_modified", UTC_NOW
+        )
 
     def test_set_reviewer_as_owner_clears_reviewer(self):
         # If the reviewer is set to be the branch owner, the review field is
@@ -901,12 +953,13 @@ class TestBranchReviewerEditView(TestCaseWithFactory):
         branch = self.factory.makeAnyBranch()
         login_person(branch.owner)
         branch.reviewer = self.factory.makePerson()
-        view = create_initialized_view(branch, '+reviewer')
-        view.change_action.success({'reviewer': branch.owner})
+        view = create_initialized_view(branch, "+reviewer")
+        view.change_action.success({"reviewer": branch.owner})
         self.assertIs(None, branch.reviewer)
         # Last modified has been updated.
         self.assertSqlAttributeEqualsDate(
-            branch, 'date_last_modified', UTC_NOW)
+            branch, "date_last_modified", UTC_NOW
+        )
 
     def test_set_reviewer_to_same_does_not_update_last_modified(self):
         # If the user has set the reviewer to be same and clicked on save,
@@ -914,8 +967,8 @@ class TestBranchReviewerEditView(TestCaseWithFactory):
         # modified is not updated.
         modified_date = datetime(2007, 1, 1, tzinfo=pytz.UTC)
         branch = self.factory.makeAnyBranch(date_created=modified_date)
-        view = create_initialized_view(branch, '+reviewer')
-        view.change_action.success({'reviewer': branch.owner})
+        view = create_initialized_view(branch, "+reviewer")
+        view.change_action.success({"reviewer": branch.owner})
         self.assertIs(None, branch.reviewer)
         # Last modified has not been updated.
         self.assertEqual(modified_date, branch.date_last_modified)
@@ -936,7 +989,8 @@ class TestBranchBzrIdentity(TestCaseWithFactory):
         login_person(product.owner)
         product.development_focus.branch = branch
         view = create_initialized_view(
-            branch.owner, '+branches', rootsite='code')
+            branch.owner, "+branches", rootsite="code"
+        )
         navigator = view.branches()
         [decorated_branch] = navigator.branches
         self.assertEqual("lp://dev/fooix", decorated_branch.bzr_identity)
@@ -952,7 +1006,7 @@ class TestBranchProposalsVisible(TestCaseWithFactory):
         # landing_target is available for the template rendering.
         bmp = self.factory.makeBranchMergeProposal()
         branch = bmp.source_branch
-        view = create_view(branch, '+index')
+        view = create_view(branch, "+index")
         self.assertFalse(view.no_merges)
         [target] = view.landing_targets
         # Check the ids as the target is a DecoratedMergeProposal.
@@ -962,9 +1016,10 @@ class TestBranchProposalsVisible(TestCaseWithFactory):
         # If the target is private, the landing targets should not include it.
         bmp = self.factory.makeBranchMergeProposal()
         branch = bmp.source_branch
-        removeSecurityProxy(bmp.target_branch).information_type = (
-            InformationType.USERDATA)
-        view = create_view(branch, '+index')
+        removeSecurityProxy(
+            bmp.target_branch
+        ).information_type = InformationType.USERDATA
+        view = create_view(branch, "+index")
         self.assertTrue(view.no_merges)
         self.assertEqual([], view.landing_targets)
 
@@ -973,7 +1028,7 @@ class TestBranchProposalsVisible(TestCaseWithFactory):
         # landing_candidate is available for the template rendering.
         bmp = self.factory.makeBranchMergeProposal()
         branch = bmp.target_branch
-        view = create_view(branch, '+index')
+        view = create_view(branch, "+index")
         self.assertFalse(view.no_merges)
         [candidate] = view.landing_candidates
         # Check the ids as the target is a DecoratedMergeProposal.
@@ -984,9 +1039,10 @@ class TestBranchProposalsVisible(TestCaseWithFactory):
         # it.
         bmp = self.factory.makeBranchMergeProposal()
         branch = bmp.target_branch
-        removeSecurityProxy(bmp.source_branch).information_type = (
-            InformationType.USERDATA)
-        view = create_view(branch, '+index')
+        removeSecurityProxy(
+            bmp.source_branch
+        ).information_type = InformationType.USERDATA
+        view = create_view(branch, "+index")
         self.assertTrue(view.no_merges)
         self.assertEqual([], view.landing_candidates)
 
@@ -995,7 +1051,7 @@ class TestBranchProposalsVisible(TestCaseWithFactory):
         # there are merges.
         branch = self.factory.makeProductBranch()
         bmp = self.factory.makeBranchMergeProposal(prerequisite_branch=branch)
-        view = create_view(branch, '+index')
+        view = create_view(branch, "+index")
         self.assertFalse(view.no_merges)
         [proposal] = view.dependent_branches
         self.assertEqual(bmp, proposal)
@@ -1005,9 +1061,10 @@ class TestBranchProposalsVisible(TestCaseWithFactory):
         # the target is private, then the dependent_branches are not shown.
         branch = self.factory.makeProductBranch()
         bmp = self.factory.makeBranchMergeProposal(prerequisite_branch=branch)
-        removeSecurityProxy(bmp.source_branch).information_type = (
-            InformationType.USERDATA)
-        view = create_view(branch, '+index')
+        removeSecurityProxy(
+            bmp.source_branch
+        ).information_type = InformationType.USERDATA
+        view = create_view(branch, "+index")
         self.assertTrue(view.no_merges)
         self.assertEqual([], view.dependent_branches)
 
@@ -1021,8 +1078,8 @@ class TestBranchEditView(TestCaseWithFactory):
         person = self.factory.makePerson()
         branch = self.factory.makePersonalBranch(owner=person)
         login_person(person)
-        view = create_initialized_view(branch, name='+edit')
-        self.assertEqual('personal', view.widgets['target'].default_option)
+        view = create_initialized_view(branch, name="+edit")
+        self.assertEqual("personal", view.widgets["target"].default_option)
 
     def test_branch_target_widget_renders_product(self):
         # The branch target widget renders correctly for a product branch.
@@ -1030,18 +1087,19 @@ class TestBranchEditView(TestCaseWithFactory):
         product = self.factory.makeProduct()
         branch = self.factory.makeProductBranch(product=product, owner=person)
         login_person(person)
-        view = create_initialized_view(branch, name='+edit')
-        self.assertEqual('product', view.widgets['target'].default_option)
+        view = create_initialized_view(branch, name="+edit")
+        self.assertEqual("product", view.widgets["target"].default_option)
         self.assertEqual(
-            product.name, view.widgets['target'].product_widget.selected_value)
+            product.name, view.widgets["target"].product_widget.selected_value
+        )
 
     def test_no_branch_target_widget_for_source_package_branch(self):
         # The branch target widget is not rendered for a package branch.
         person = self.factory.makePerson()
         branch = self.factory.makePackageBranch(owner=person)
         login_person(person)
-        view = create_initialized_view(branch, name='+edit')
-        self.assertIsNone(view.widgets.get('target'))
+        view = create_initialized_view(branch, name="+edit")
+        self.assertIsNone(view.widgets.get("target"))
 
     def test_branch_target_widget_saves_junk(self):
         # The branch target widget can retarget to a junk branch.
@@ -1050,34 +1108,36 @@ class TestBranchEditView(TestCaseWithFactory):
         branch = self.factory.makeProductBranch(product=product, owner=person)
         login_person(person)
         form = {
-            'field.target': 'personal',
-            'field.actions.change': 'Change Branch',
+            "field.target": "personal",
+            "field.actions.change": "Change Branch",
         }
-        view = create_initialized_view(branch, name='+edit', form=form)
+        view = create_initialized_view(branch, name="+edit", form=form)
         self.assertEqual(person, branch.target.context)
         self.assertEqual(1, len(view.request.response.notifications))
         self.assertEqual(
-            'This branch is now a personal branch for %s (%s)'
-                % (person.displayname, person.name),
-            view.request.response.notifications[0].message)
+            "This branch is now a personal branch for %s (%s)"
+            % (person.displayname, person.name),
+            view.request.response.notifications[0].message,
+        )
 
     def test_save_to_different_junk(self):
         # The branch target widget can retarget to a junk branch.
         person = self.factory.makePerson()
         branch = self.factory.makePersonalBranch(owner=person)
-        new_owner = self.factory.makeTeam(name='newowner', members=[person])
+        new_owner = self.factory.makeTeam(name="newowner", members=[person])
         login_person(person)
         form = {
-            'field.target': 'personal',
-            'field.owner': 'newowner',
-            'field.actions.change': 'Change Branch',
+            "field.target": "personal",
+            "field.owner": "newowner",
+            "field.actions.change": "Change Branch",
         }
-        view = create_initialized_view(branch, name='+edit', form=form)
+        view = create_initialized_view(branch, name="+edit", form=form)
         self.assertEqual(new_owner, branch.target.context)
         self.assertEqual(1, len(view.request.response.notifications))
         self.assertEqual(
-            'The branch owner has been changed to Newowner (newowner)',
-            view.request.response.notifications[0].message)
+            "The branch owner has been changed to Newowner (newowner)",
+            view.request.response.notifications[0].message,
+        )
 
     def test_branch_target_widget_saves_product(self):
         # The branch target widget can retarget to a product branch.
@@ -1086,16 +1146,17 @@ class TestBranchEditView(TestCaseWithFactory):
         product = self.factory.makeProduct()
         login_person(person)
         form = {
-            'field.target': 'product',
-            'field.target.product': product.name,
-            'field.actions.change': 'Change Branch',
+            "field.target": "product",
+            "field.target.product": product.name,
+            "field.actions.change": "Change Branch",
         }
-        view = create_initialized_view(branch, name='+edit', form=form)
+        view = create_initialized_view(branch, name="+edit", form=form)
         self.assertEqual(product, branch.target.context)
         self.assertEqual(
-            'The branch target has been changed to %s (%s)'
-                % (product.displayname, product.name),
-            view.request.response.notifications[0].message)
+            "The branch target has been changed to %s (%s)"
+            % (product.displayname, product.name),
+            view.request.response.notifications[0].message,
+        )
 
     def test_forbidden_target_is_error(self):
         # An error is displayed if a branch is saved with a target that is not
@@ -1103,18 +1164,24 @@ class TestBranchEditView(TestCaseWithFactory):
         owner = self.factory.makePerson()
         initial_target = self.factory.makeProduct()
         self.factory.makeProduct(
-            name="commercial", owner=owner,
-            branch_sharing_policy=BranchSharingPolicy.PROPRIETARY)
+            name="commercial",
+            owner=owner,
+            branch_sharing_policy=BranchSharingPolicy.PROPRIETARY,
+        )
         branch = self.factory.makeProductBranch(
-            owner=owner, product=initial_target,
-            information_type=InformationType.PUBLIC)
+            owner=owner,
+            product=initial_target,
+            information_type=InformationType.PUBLIC,
+        )
         browser = self.getUserBrowser(
-            canonical_url(branch) + '/+edit', user=owner)
+            canonical_url(branch) + "/+edit", user=owner
+        )
         browser.getControl(name="field.target.product").value = "commercial"
         browser.getControl("Change Branch").click()
         self.assertThat(
             browser.contents,
-            Contains('Public branches are not allowed for target Commercial.'))
+            Contains("Public branches are not allowed for target Commercial."),
+        )
         with person_logged_in(owner):
             self.assertEqual(initial_target, branch.target.context)
 
@@ -1126,7 +1193,8 @@ class TestBranchEditView(TestCaseWithFactory):
         admins = getUtility(ILaunchpadCelebrities).admin
         admin = admins.teamowner
         browser = self.getUserBrowser(
-            canonical_url(branch) + '/+edit', user=admin)
+            canonical_url(branch) + "/+edit", user=admin
+        )
         browser.getControl("Private", index=1).click()
         browser.getControl("Change Branch").click()
         with person_logged_in(person):
@@ -1138,13 +1206,17 @@ class TestBranchEditView(TestCaseWithFactory):
         owner = self.factory.makePerson()
         product = self.factory.makeProduct(owner=owner)
         stacked_on = self.factory.makeBranch(
-            product=product, owner=owner,
-            information_type=InformationType.USERDATA)
+            product=product,
+            owner=owner,
+            information_type=InformationType.USERDATA,
+        )
         branch = self.factory.makeBranch(
-            product=product, owner=owner, stacked_on=stacked_on)
+            product=product, owner=owner, stacked_on=stacked_on
+        )
         with person_logged_in(owner):
             browser = self.getUserBrowser(
-                canonical_url(branch) + '/+edit', user=owner)
+                canonical_url(branch) + "/+edit", user=owner
+            )
         self.assertRaises(LookupError, browser.getControl, "Information Type")
 
     def test_edit_view_ajax_render(self):
@@ -1153,21 +1225,28 @@ class TestBranchEditView(TestCaseWithFactory):
         person = self.factory.makePerson()
         branch = self.factory.makeProductBranch(owner=person)
 
-        extra = {'HTTP_X_REQUESTED_WITH': 'XMLHttpRequest'}
+        extra = {"HTTP_X_REQUESTED_WITH": "XMLHttpRequest"}
         request = LaunchpadTestRequest(
-            method='POST', form={
-                'field.actions.change': 'Change Branch',
-                'field.information_type': 'PUBLICSECURITY'},
-            **extra)
+            method="POST",
+            form={
+                "field.actions.change": "Change Branch",
+                "field.information_type": "PUBLICSECURITY",
+            },
+            **extra,
+        )
         with person_logged_in(person):
             view = create_initialized_view(
-                branch, name='+edit-information-type',
-                request=request, principal=person)
+                branch,
+                name="+edit-information-type",
+                request=request,
+                principal=person,
+            )
             request.traversed_objects = [person, branch.product, branch, view]
             result = view.render()
-            self.assertEqual('', result)
+            self.assertEqual("", result)
             self.assertEqual(
-                branch.information_type, InformationType.PUBLICSECURITY)
+                branch.information_type, InformationType.PUBLICSECURITY
+            )
 
 
 class TestBranchEditViewInformationTypes(TestCaseWithFactory):
@@ -1179,7 +1258,7 @@ class TestBranchEditViewInformationTypes(TestCaseWithFactory):
         if user is None:
             user = removeSecurityProxy(branch).owner
         with person_logged_in(user):
-            view = create_initialized_view(branch, '+edit', principal=user)
+            view = create_initialized_view(branch, "+edit", principal=user)
             self.assertContentEqual(types, view.getInformationTypesToShow())
 
     def test_public_branch(self):
@@ -1187,11 +1266,17 @@ class TestBranchEditViewInformationTypes(TestCaseWithFactory):
         # type except embargoed and proprietary.
         # The model doesn't enforce this, so it's just a UI thing.
         branch = self.factory.makeBranch(
-            information_type=InformationType.PUBLIC)
+            information_type=InformationType.PUBLIC
+        )
         self.assertShownTypes(
-            [InformationType.PUBLIC, InformationType.PUBLICSECURITY,
-             InformationType.PRIVATESECURITY, InformationType.USERDATA],
-            branch)
+            [
+                InformationType.PUBLIC,
+                InformationType.PUBLICSECURITY,
+                InformationType.PRIVATESECURITY,
+                InformationType.USERDATA,
+            ],
+            branch,
+        )
 
     def test_branch_with_disallowed_type(self):
         # We don't force branches with a disallowed type (eg. Proprietary on a
@@ -1200,25 +1285,35 @@ class TestBranchEditViewInformationTypes(TestCaseWithFactory):
         product = self.factory.makeProduct()
         self.factory.makeAccessPolicy(pillar=product)
         branch = self.factory.makeBranch(
-            product=product, information_type=InformationType.PROPRIETARY)
+            product=product, information_type=InformationType.PROPRIETARY
+        )
         self.assertShownTypes(
-            [InformationType.PUBLIC, InformationType.PUBLICSECURITY,
-             InformationType.PRIVATESECURITY, InformationType.USERDATA,
-             InformationType.PROPRIETARY], branch)
+            [
+                InformationType.PUBLIC,
+                InformationType.PUBLICSECURITY,
+                InformationType.PRIVATESECURITY,
+                InformationType.USERDATA,
+                InformationType.PROPRIETARY,
+            ],
+            branch,
+        )
 
     def test_stacked_on_private(self):
         # A branch stacked on a private branch has its choices limited
         # to the current type and the stacked-on type.
         product = self.factory.makeProduct()
         stacked_on_branch = self.factory.makeBranch(
-            product=product, information_type=InformationType.USERDATA)
+            product=product, information_type=InformationType.USERDATA
+        )
         branch = self.factory.makeBranch(
-            product=product, stacked_on=stacked_on_branch,
+            product=product,
+            stacked_on=stacked_on_branch,
             owner=product.owner,
-            information_type=InformationType.PRIVATESECURITY)
+            information_type=InformationType.PRIVATESECURITY,
+        )
         self.assertShownTypes(
-            [InformationType.PRIVATESECURITY, InformationType.USERDATA],
-            branch)
+            [InformationType.PRIVATESECURITY, InformationType.USERDATA], branch
+        )
 
     def test_branch_for_project_with_embargoed_and_proprietary(self):
         # Branches for commercial projects which have a policy of embargoed or
@@ -1228,12 +1323,16 @@ class TestBranchEditViewInformationTypes(TestCaseWithFactory):
         self.factory.makeCommercialSubscription(pillar=product)
         with person_logged_in(owner):
             product.setBranchSharingPolicy(
-                BranchSharingPolicy.EMBARGOED_OR_PROPRIETARY)
+                BranchSharingPolicy.EMBARGOED_OR_PROPRIETARY
+            )
             branch = self.factory.makeBranch(
-                product=product, owner=owner,
-                information_type=InformationType.PROPRIETARY)
+                product=product,
+                owner=owner,
+                information_type=InformationType.PROPRIETARY,
+            )
         self.assertShownTypes(
-            [InformationType.EMBARGOED, InformationType.PROPRIETARY], branch)
+            [InformationType.EMBARGOED, InformationType.PROPRIETARY], branch
+        )
 
     def test_branch_for_project_with_proprietary(self):
         # Branches for commercial projects which have a policy of proprietary
@@ -1244,8 +1343,10 @@ class TestBranchEditViewInformationTypes(TestCaseWithFactory):
         with person_logged_in(owner):
             product.setBranchSharingPolicy(BranchSharingPolicy.PROPRIETARY)
             branch = self.factory.makeBranch(
-                product=product, owner=owner,
-                information_type=InformationType.PROPRIETARY)
+                product=product,
+                owner=owner,
+                information_type=InformationType.PROPRIETARY,
+            )
         self.assertShownTypes([InformationType.PROPRIETARY], branch)
 
 
@@ -1256,17 +1357,20 @@ class TestBranchUpgradeView(TestCaseWithFactory):
     def test_upgrade_branch_action_cannot_upgrade(self):
         # A nice error is displayed if a branch cannot be upgraded.
         branch = self.factory.makePersonalBranch(
-        branch_format=BranchFormat.BZR_BRANCH_6,
-        repository_format=RepositoryFormat.BZR_CHK_2A)
+            branch_format=BranchFormat.BZR_BRANCH_6,
+            repository_format=RepositoryFormat.BZR_CHK_2A,
+        )
         login_person(branch.owner)
         self.addCleanup(logout)
         branch.requestUpgrade(branch.owner)
-        view = create_initialized_view(branch, '+upgrade')
+        view = create_initialized_view(branch, "+upgrade")
         view.upgrade_branch_action.success({})
         self.assertEqual(1, len(view.request.notifications))
         self.assertEqual(
-            'An upgrade is already in progress for branch %s.' %
-            branch.bzr_identity, view.request.notifications[0].message)
+            "An upgrade is already in progress for branch %s."
+            % branch.bzr_identity,
+            view.request.notifications[0].message,
+        )
 
 
 class TestBranchPrivacyPortlet(TestCaseWithFactory):
@@ -1277,20 +1381,23 @@ class TestBranchPrivacyPortlet(TestCaseWithFactory):
         # The privacy portlet shows the information_type.
         owner = self.factory.makePerson()
         branch = self.factory.makeBranch(
-            owner=owner, information_type=InformationType.USERDATA)
+            owner=owner, information_type=InformationType.USERDATA
+        )
         with person_logged_in(owner):
-            view = create_initialized_view(branch, '+portlet-privacy')
-            edit_url = '/' + branch.unique_name + '/+edit-information-type'
+            view = create_initialized_view(branch, "+portlet-privacy")
+            edit_url = "/" + branch.unique_name + "/+edit-information-type"
             soup = BeautifulSoup(view.render())
-        information_type = soup.find('strong')
-        description = soup.find('div', id='information-type-description')
+        information_type = soup.find("strong")
+        description = soup.find("div", id="information-type-description")
         self.assertEqual(
-            InformationType.USERDATA.title, information_type.decode_contents())
+            InformationType.USERDATA.title, information_type.decode_contents()
+        )
         self.assertTextMatchesExpressionIgnoreWhitespace(
-            InformationType.USERDATA.description,
-            description.decode_contents())
+            InformationType.USERDATA.description, description.decode_contents()
+        )
         self.assertIsNotNone(
-            soup.find('a', id='privacy-link', attrs={'href': edit_url}))
+            soup.find("a", id="privacy-link", attrs={"href": edit_url})
+        )
 
 
 class TestBranchDiffView(BrowserTestCase):
@@ -1308,7 +1415,8 @@ class TestBranchDiffView(BrowserTestCase):
         browser.open(branch_url + "/+diff/2/1")
         self.assertEqual(401, int(browser.headers["Status"].split(" ", 1)[0]))
         self.assertEqual(
-            "Proxying of branch diffs is disabled.\n", browser.contents)
+            "Proxying of branch diffs is disabled.\n", browser.contents
+        )
         self.assertEqual([], hosting_fixture.getDiff.calls)
 
     def test_render(self):
@@ -1316,17 +1424,18 @@ class TestBranchDiffView(BrowserTestCase):
         hosting_fixture = self.useFixture(BranchHostingFixture(diff=diff))
         person = self.factory.makePerson()
         branch = self.factory.makeBranch(owner=person, name="some-branch")
-        browser = self.getUserBrowser(
-            canonical_url(branch) + "/+diff/2/1")
+        browser = self.getUserBrowser(canonical_url(branch) + "/+diff/2/1")
         with person_logged_in(person):
             self.assertEqual(
                 [((branch.id, "2"), {"old": "1"})],
-                hosting_fixture.getDiff.calls)
+                hosting_fixture.getDiff.calls,
+            )
         self.assertEqual("text/x-patch", browser.headers["Content-Type"])
         self.assertEqual(str(len(diff)), browser.headers["Content-Length"])
         self.assertEqual(
             'attachment; filename="some-branch_1_2.diff"',
-            browser.headers["Content-Disposition"])
+            browser.headers["Content-Disposition"],
+        )
         self.assertEqual(diff, browser.contents)
 
     def test_security(self):
@@ -1336,17 +1445,21 @@ class TestBranchDiffView(BrowserTestCase):
         self.useFixture(BranchHostingFixture(diff=diff))
         person = self.factory.makePerson()
         project = self.factory.makeProduct(
-            owner=person, information_type=InformationType.PROPRIETARY)
+            owner=person, information_type=InformationType.PROPRIETARY
+        )
         with person_logged_in(person):
             branch = self.factory.makeBranch(
-                owner=person, product=project,
-                information_type=InformationType.PROPRIETARY)
+                owner=person,
+                product=project,
+                information_type=InformationType.PROPRIETARY,
+            )
             branch_url = canonical_url(branch)
         browser = self.getUserBrowser(branch_url + "/+diff/2/1", user=person)
         self.as