← Back to team overview

launchpad-reviewers team mailing list archive

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

 

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

Commit message:
lp.registry: Apply black

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/+git/launchpad/+merge/427243
-- 
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-registry into launchpad:master.
diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs
index 1166ab6..fd2e058 100644
--- a/.git-blame-ignore-revs
+++ b/.git-blame-ignore-revs
@@ -78,3 +78,5 @@ ed7d7b97b8fb4ebe92799f922b0fa9c4bd1714e8
 1e6ead9387a1d073eea9271fe8dc646bb22fa3d2
 # apply black to lp.oci
 06b1048510a0b65bb0a6fc46d4e063510b52b5f6
+# apply black to lp.registry
+0a9f5f09fc0c930b73619e77524e60f2740595ed
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 43f0476..7db349d 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -53,6 +53,7 @@ repos:
             |codehosting
             |coop
             |oci
+            |registry
           )/
 -   repo: https://github.com/PyCQA/isort
     rev: 5.9.2
@@ -82,6 +83,7 @@ repos:
             |codehosting
             |coop
             |oci
+            |registry
           )/
     -   id: isort
         alias: isort-black
@@ -101,6 +103,7 @@ repos:
             |codehosting
             |coop
             |oci
+            |registry
           )/
 -   repo: https://github.com/PyCQA/flake8
     rev: 3.9.2
diff --git a/lib/lp/registry/adapters.py b/lib/lp/registry/adapters.py
index ef21952..de7ce44 100644
--- a/lib/lp/registry/adapters.py
+++ b/lib/lp/registry/adapters.py
@@ -4,11 +4,11 @@
 """Adapters for registry objects."""
 
 __all__ = [
-    'distroseries_to_distribution',
-    'PollSubset',
-    'productseries_to_product',
-    'sourcepackage_to_distribution',
-    ]
+    "distroseries_to_distribution",
+    "PollSubset",
+    "productseries_to_product",
+    "sourcepackage_to_distribution",
+]
 
 
 from zope.component import getUtility
@@ -22,7 +22,7 @@ from lp.registry.interfaces.poll import (
     IPollSubset,
     PollAlgorithm,
     PollStatus,
-    )
+)
 from lp.services.webapp.interfaces import ILaunchpadPrincipal
 
 
@@ -63,56 +63,85 @@ def person_from_principal(principal):
 class PollSubset:
     """Adapt an `IPoll` to an `IPollSubset`."""
 
-    title = 'Team polls'
+    title = "Team polls"
 
     def __init__(self, team=None):
         self.team = team
 
-    def new(self, name, title, proposition, dateopens, datecloses,
-            secrecy, allowspoilt, poll_type=PollAlgorithm.SIMPLE):
+    def new(
+        self,
+        name,
+        title,
+        proposition,
+        dateopens,
+        datecloses,
+        secrecy,
+        allowspoilt,
+        poll_type=PollAlgorithm.SIMPLE,
+    ):
         """See IPollSubset."""
-        assert self.team is not None, (
-            'team cannot be None to call this method.')
+        assert (
+            self.team is not None
+        ), "team cannot be None to call this method."
         return getUtility(IPollSet).new(
-            self.team, name, title, proposition, dateopens,
-            datecloses, secrecy, allowspoilt, poll_type)
+            self.team,
+            name,
+            title,
+            proposition,
+            dateopens,
+            datecloses,
+            secrecy,
+            allowspoilt,
+            poll_type,
+        )
 
     def getByName(self, name, default=None):
         """See IPollSubset."""
-        assert self.team is not None, (
-            'team cannot be None to call this method.')
+        assert (
+            self.team is not None
+        ), "team cannot be None to call this method."
         pollset = getUtility(IPollSet)
         return pollset.getByTeamAndName(self.team, name, default)
 
     def getAll(self):
         """See IPollSubset."""
-        assert self.team is not None, (
-            'team cannot be None to call this method.')
+        assert (
+            self.team is not None
+        ), "team cannot be None to call this method."
         return getUtility(IPollSet).findByTeam(self.team)
 
     def getOpenPolls(self, when=None):
         """See IPollSubset."""
-        assert self.team is not None, (
-            'team cannot be None to call this method.')
+        assert (
+            self.team is not None
+        ), "team cannot be None to call this method."
         return getUtility(IPollSet).findByTeam(
-            self.team, [PollStatus.OPEN],
-            order_by=PollSort.CLOSING, when=when)
+            self.team, [PollStatus.OPEN], order_by=PollSort.CLOSING, when=when
+        )
 
     def getClosedPolls(self, when=None):
         """See IPollSubset."""
-        assert self.team is not None, (
-            'team cannot be None to call this method.')
+        assert (
+            self.team is not None
+        ), "team cannot be None to call this method."
         return getUtility(IPollSet).findByTeam(
-            self.team, [PollStatus.CLOSED],
-            order_by=PollSort.CLOSING, when=when)
+            self.team,
+            [PollStatus.CLOSED],
+            order_by=PollSort.CLOSING,
+            when=when,
+        )
 
     def getNotYetOpenedPolls(self, when=None):
         """See IPollSubset."""
-        assert self.team is not None, (
-            'team cannot be None to call this method.')
+        assert (
+            self.team is not None
+        ), "team cannot be None to call this method."
         return getUtility(IPollSet).findByTeam(
-            self.team, [PollStatus.NOT_YET_OPENED],
-            order_by=PollSort.OPENING, when=when)
+            self.team,
+            [PollStatus.NOT_YET_OPENED],
+            order_by=PollSort.OPENING,
+            when=when,
+        )
 
 
 def productseries_to_product(productseries):
diff --git a/lib/lp/registry/browser/__init__.py b/lib/lp/registry/browser/__init__.py
index c9b6f86..403345b 100644
--- a/lib/lp/registry/browser/__init__.py
+++ b/lib/lp/registry/browser/__init__.py
@@ -4,42 +4,39 @@
 """Common registry browser helpers and mixins."""
 
 __all__ = [
-    'add_subscribe_link',
-    'BaseRdfView',
-    'get_status_counts',
-    'MilestoneOverlayMixin',
-    'RDFIndexView',
-    'RegistryEditFormView',
-    'RegistryDeleteViewMixin',
-    'StatusCount',
-    ]
+    "add_subscribe_link",
+    "BaseRdfView",
+    "get_status_counts",
+    "MilestoneOverlayMixin",
+    "RDFIndexView",
+    "RegistryEditFormView",
+    "RegistryDeleteViewMixin",
+    "StatusCount",
+]
 
 
-from operator import attrgetter
 import os
+from operator import attrgetter
 
 from storm.store import Store
 from zope.component import getUtility
 from zope.security.proxy import removeSecurityProxy
 
 from lp.app.browser.folder import ExportedFolder
-from lp.app.browser.launchpadform import (
-    action,
-    LaunchpadEditFormView,
-    )
+from lp.app.browser.launchpadform import LaunchpadEditFormView, action
 from lp.app.interfaces.launchpad import ILaunchpadCelebrities
 from lp.blueprints.interfaces.specificationworkitem import (
     ISpecificationWorkItemSet,
-    )
+)
 from lp.bugs.interfaces.bugtask import IBugTaskSet
 from lp.bugs.interfaces.bugtasksearch import BugTaskSearchParams
 from lp.registry.interfaces.productseries import IProductSeries
 from lp.registry.interfaces.series import SeriesStatus
 from lp.services.webapp.publisher import (
-    canonical_url,
     DataDownloadView,
     LaunchpadView,
-    )
+    canonical_url,
+)
 
 
 class StatusCount:
@@ -55,7 +52,7 @@ class StatusCount:
         self.count = count
 
 
-def get_status_counts(workitems, status_attr, key='sortkey'):
+def get_status_counts(workitems, status_attr, key="sortkey"):
     """Return a list StatusCounts summarising the workitem."""
     statuses = {}
     for workitem in workitems:
@@ -68,12 +65,13 @@ def get_status_counts(workitems, status_attr, key='sortkey'):
         statuses[status] += 1
     return [
         StatusCount(status, statuses[status])
-        for status in sorted(statuses, key=attrgetter(key))]
+        for status in sorted(statuses, key=attrgetter(key))
+    ]
 
 
 def add_subscribe_link(links):
     """Add the subscription-related links."""
-    links.extend(['subscribe_to_bug_mail', 'edit_bug_mail'])
+    links.extend(["subscribe_to_bug_mail", "edit_bug_mail"])
 
 
 class MilestoneOverlayMixin:
@@ -84,7 +82,7 @@ class MilestoneOverlayMixin:
     @property
     def milestone_form_uri(self):
         """URI for form displayed by the formoverlay widget."""
-        return canonical_url(self.context) + '/+addmilestone/++form++'
+        return canonical_url(self.context) + "/+addmilestone/++form++"
 
     @property
     def series_api_uri(self):
@@ -95,11 +93,11 @@ class MilestoneOverlayMixin:
     def milestone_table_class(self):
         """The milestone table will be hidden if there are no milestones."""
         if self.context.has_milestones:
-            return 'listing'
+            return "listing"
         else:
             # The page can remove the 'hidden' class to make the table
             # visible.
-            return 'listing hidden'
+            return "listing hidden"
 
     @property
     def milestone_row_uri_template(self):
@@ -108,17 +106,18 @@ class MilestoneOverlayMixin:
         else:
             pillar = self.context.distribution
         uri = canonical_url(pillar, path_only_if_possible=True)
-        return '%s/+milestone/{name}/+productseries-table-row' % uri
+        return "%s/+milestone/{name}/+productseries-table-row" % uri
 
     @property
     def register_milestone_script(self):
         """Return the script to enable milestone creation via AJAX."""
         uris = {
-            'series_api_uri': self.series_api_uri,
-            'milestone_form_uri': self.milestone_form_uri,
-            'milestone_row_uri': self.milestone_row_uri_template,
-            }
-        return """
+            "series_api_uri": self.series_api_uri,
+            "milestone_form_uri": self.milestone_form_uri,
+            "milestone_row_uri": self.milestone_row_uri_template,
+        }
+        return (
+            """
             LPJS.use(
                 'node', 'lp.registry.milestoneoverlay',
                 'lp.registry.milestonetable',
@@ -148,7 +147,9 @@ class MilestoneOverlayMixin:
                     Y.lp.registry.milestonetable.setup(table_config);
                 });
             });
-            """ % uris
+            """
+            % uris
+        )
 
 
 class RegistryDeleteViewMixin:
@@ -166,8 +167,8 @@ class RegistryDeleteViewMixin:
             params.setProductSeries(target)
         else:
             params = BugTaskSearchParams(
-                milestone=target, user=self.user,
-                ignore_privacy=ignore_privacy)
+                milestone=target, user=self.user, ignore_privacy=ignore_privacy
+            )
         bugtasks = getUtility(IBugTaskSet).search(params)
         return list(bugtasks)
 
@@ -221,9 +222,12 @@ class RegistryDeleteViewMixin:
         # Series are not deleted because some objects like translations are
         # problematic. The series is assigned to obsolete-junk. They must be
         # renamed to avoid name collision.
-        date_time = series.datecreated.strftime('%Y%m%d-%H%M%S')
-        series.name = '%s-%s-%s' % (
-            series.product.name, series.name, date_time)
+        date_time = series.datecreated.strftime("%Y%m%d-%H%M%S")
+        series.name = "%s-%s-%s" % (
+            series.product.name,
+            series.name,
+            date_time,
+        )
         series.status = SeriesStatus.OBSOLETE
         series.releasefileglob = None
         series.product = getUtility(ILaunchpadCelebrities).obsolete_junk
@@ -268,7 +272,7 @@ class RegistryEditFormView(LaunchpadEditFormView):
 
     next_url = cancel_url
 
-    @action("Change", name='change')
+    @action("Change", name="change")
     def change_action(self, action, data):
         self.updateContextFromData(data)
 
@@ -290,16 +294,19 @@ class BaseRdfView(DataDownloadView):
         As a side-effect, HTTP headers are set for the mime type
         and filename for download."""
         unicodedata = self.template()
-        encodeddata = unicodedata.encode('utf-8')
+        encodeddata = unicodedata.encode("utf-8")
         return encodeddata
 
 
 class RDFIndexView(LaunchpadView):
     """View for /rdf page."""
+
     page_title = label = "Launchpad RDF"
 
 
 class RDFFolder(ExportedFolder):
     """Export the Launchpad RDF schemas."""
+
     folder = os.path.join(
-        os.path.dirname(os.path.realpath(__file__)), '../rdfspec/')
+        os.path.dirname(os.path.realpath(__file__)), "../rdfspec/"
+    )
diff --git a/lib/lp/registry/browser/announcement.py b/lib/lp/registry/browser/announcement.py
index 7592db6..ef7957e 100644
--- a/lib/lp/registry/browser/announcement.py
+++ b/lib/lp/registry/browser/announcement.py
@@ -4,31 +4,22 @@
 """Announcement views."""
 
 __all__ = [
-    'AnnouncementAddView',
-    'AnnouncementRetargetView',
-    'AnnouncementPublishView',
-    'AnnouncementRetractView',
-    'AnnouncementDeleteView',
-    'AnnouncementEditView',
-    'AnnouncementSetView',
-    'HasAnnouncementsView',
-    'AnnouncementView',
-    ]
-
-from zope.interface import (
-    implementer,
-    Interface,
-    )
-from zope.schema import (
-    Choice,
-    TextLine,
-    )
+    "AnnouncementAddView",
+    "AnnouncementRetargetView",
+    "AnnouncementPublishView",
+    "AnnouncementRetractView",
+    "AnnouncementDeleteView",
+    "AnnouncementEditView",
+    "AnnouncementSetView",
+    "HasAnnouncementsView",
+    "AnnouncementView",
+]
+
+from zope.interface import Interface, implementer
+from zope.schema import Choice, TextLine
 
 from lp import _
-from lp.app.browser.launchpadform import (
-    action,
-    LaunchpadFormView,
-    )
+from lp.app.browser.launchpadform import LaunchpadFormView, action
 from lp.app.validators.url import valid_webref
 from lp.app.widgets.announcementdate import AnnouncementDateWidget
 from lp.registry.interfaces.announcement import IAnnouncement
@@ -37,70 +28,63 @@ from lp.services.feeds.browser import (
     AnnouncementsFeedLink,
     FeedsMixin,
     RootAnnouncementsFeedLink,
-    )
-from lp.services.fields import (
-    AnnouncementDate,
-    Summary,
-    Title,
-    )
+)
+from lp.services.fields import AnnouncementDate, Summary, Title
 from lp.services.propertycache import cachedproperty
 from lp.services.webapp.authorization import check_permission
 from lp.services.webapp.batching import BatchNavigator
 from lp.services.webapp.menu import (
-    enabled_with_permission,
     Link,
     NavigationMenu,
-    )
-from lp.services.webapp.publisher import (
-    canonical_url,
-    LaunchpadView,
-    )
+    enabled_with_permission,
+)
+from lp.services.webapp.publisher import LaunchpadView, canonical_url
 
 
 class AnnouncementMenuMixin:
     """A mixin of links common to many menus."""
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def edit(self):
-        text = 'Modify announcement'
-        return Link('+edit', text, icon='edit')
+        text = "Modify announcement"
+        return Link("+edit", text, icon="edit")
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def retarget(self):
-        text = 'Move announcement'
-        return Link('+retarget', text, icon='edit')
+        text = "Move announcement"
+        return Link("+retarget", text, icon="edit")
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def publish(self):
-        text = 'Publish announcement'
+        text = "Publish announcement"
         enabled = not self.context.published
-        return Link('+publish', text, icon='edit', enabled=enabled)
+        return Link("+publish", text, icon="edit", enabled=enabled)
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def retract(self):
-        text = 'Retract announcement'
+        text = "Retract announcement"
         enabled = self.context.published
-        return Link('+retract', text, icon='remove', enabled=enabled)
+        return Link("+retract", text, icon="remove", enabled=enabled)
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def delete(self):
-        text = 'Delete announcement'
-        return Link('+delete', text, icon='trash-icon')
+        text = "Delete announcement"
+        return Link("+delete", text, icon="trash-icon")
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def announce(self):
-        text = 'Make announcement'
-        summary = 'Create an item of news for this project'
-        return Link('+announce', text, summary, icon='add')
+        text = "Make announcement"
+        summary = "Create an item of news for this project"
+        return Link("+announce", text, summary, icon="add")
 
 
 class AnnouncementEditNavigationMenu(NavigationMenu, AnnouncementMenuMixin):
     """A sub-menu for different aspects of modifying an announcement."""
 
     usedfor = IAnnouncement
-    facet = 'overview'
-    title = 'Change announcement'
-    links = ['edit', 'retarget', 'publish', 'retract', 'delete']
+    facet = "overview"
+    title = "Change announcement"
+    links = ["edit", "retarget", "publish", "retract", "delete"]
 
 
 class IAnnouncementCreateMenu(Interface):
@@ -111,9 +95,9 @@ class AnnouncementCreateNavigationMenu(NavigationMenu, AnnouncementMenuMixin):
     """A sub-menu for different aspects of modifying an announcement."""
 
     usedfor = IAnnouncementCreateMenu
-    facet = 'overview'
-    title = 'Create announcement'
-    links = ['announce']
+    facet = "overview"
+    title = "Create announcement"
+    links = ["announce"]
 
 
 class AnnouncementFormMixin:
@@ -126,17 +110,21 @@ class AnnouncementFormMixin:
     @property
     def cancel_url(self):
         """The announcements URL."""
-        return canonical_url(self.context.target, view_name='+announcements')
+        return canonical_url(self.context.target, view_name="+announcements")
 
 
 class AddAnnouncementForm(Interface):
     """Form definition for the view which creates new Announcements."""
 
-    title = Title(title=_('Headline'), required=True)
-    summary = Summary(title=_('Summary'), required=True)
-    url = TextLine(title=_('URL'), required=False, constraint=valid_webref,
-        description=_("The web location of your announcement."))
-    publication_date = AnnouncementDate(title=_('Date'), required=True)
+    title = Title(title=_("Headline"), required=True)
+    summary = Summary(title=_("Summary"), required=True)
+    url = TextLine(
+        title=_("URL"),
+        required=False,
+        constraint=valid_webref,
+        description=_("The web location of your announcement."),
+    )
+    publication_date = AnnouncementDate(title=_("Date"), required=True)
 
 
 class AnnouncementAddView(LaunchpadFormView):
@@ -148,15 +136,16 @@ class AnnouncementAddView(LaunchpadFormView):
 
     custom_widget_publication_date = AnnouncementDateWidget
 
-    @action(_('Make announcement'), name='announce')
+    @action(_("Make announcement"), name="announce")
     def announce_action(self, action, data):
         """Registers a new announcement."""
         self.context.announce(
             user=self.user,
-            title=data.get('title'),
-            summary=data.get('summary'),
-            url=data.get('url'),
-            publication_date=data.get('publication_date'))
+            title=data.get("title"),
+            summary=data.get("summary"),
+            url=data.get("url"),
+            publication_date=data.get("publication_date"),
+        )
         self.next_url = canonical_url(self.context)
 
     @property
@@ -173,23 +162,29 @@ class AnnouncementEditView(AnnouncementFormMixin, LaunchpadFormView):
     """A view which allows you to edit the announcement."""
 
     schema = AddAnnouncementForm
-    field_names = ['title', 'summary', 'url', ]
-    page_title = 'Modify announcement'
+    field_names = [
+        "title",
+        "summary",
+        "url",
+    ]
+    page_title = "Modify announcement"
 
     @property
     def initial_values(self):
         return {
-            'title': self.context.title,
-            'summary': self.context.summary,
-            'url': self.context.url,
-            }
+            "title": self.context.title,
+            "summary": self.context.summary,
+            "url": self.context.url,
+        }
 
-    @action(_('Modify'), name='modify')
+    @action(_("Modify"), name="modify")
     def modify_action(self, action, data):
-        self.context.modify(title=data.get('title'),
-                            summary=data.get('summary'),
-                            url=data.get('url'))
-        self.next_url = canonical_url(self.context.target) + '/+announcements'
+        self.context.modify(
+            title=data.get("title"),
+            summary=data.get("summary"),
+            url=data.get("url"),
+        )
+        self.next_url = canonical_url(self.context.target) + "/+announcements"
 
 
 class AnnouncementRetargetForm(Interface):
@@ -198,89 +193,95 @@ class AnnouncementRetargetForm(Interface):
     target = Choice(
         title=_("For"),
         description=_("The project where this announcement is being made."),
-        required=True, vocabulary='DistributionOrProductOrProjectGroup')
+        required=True,
+        vocabulary="DistributionOrProductOrProjectGroup",
+    )
 
 
 class AnnouncementRetargetView(AnnouncementFormMixin, LaunchpadFormView):
     """A view to move an annoucement to another project."""
 
     schema = AnnouncementRetargetForm
-    field_names = ['target']
-    page_title = 'Move announcement'
+    field_names = ["target"]
+    page_title = "Move announcement"
 
     def validate(self, data):
         """Ensure that the person can publish announcement at the new
         target.
         """
 
-        target = data.get('target')
+        target = data.get("target")
 
         if target is None:
-            self.setFieldError('target',
+            self.setFieldError(
+                "target",
                 "There is no project with the name '%s'. "
-                "Please check that name and try again." %
-                self.request.form.get("field.target"))
+                "Please check that name and try again."
+                % self.request.form.get("field.target"),
+            )
             return
 
-        if not check_permission('launchpad.Edit', target):
-            self.setFieldError('target',
+        if not check_permission("launchpad.Edit", target):
+            self.setFieldError(
+                "target",
                 "You don't have permission to make announcements for "
-                "%s. Please check that name and try again." %
-                target.displayname)
+                "%s. Please check that name and try again."
+                % target.displayname,
+            )
             return
 
-    @action(_('Retarget'), name='retarget')
+    @action(_("Retarget"), name="retarget")
     def retarget_action(self, action, data):
-        target = data.get('target')
+        target = data.get("target")
         self.context.retarget(target)
-        self.next_url = canonical_url(self.context.target) + '/+announcements'
+        self.next_url = canonical_url(self.context.target) + "/+announcements"
 
 
 class AnnouncementPublishView(AnnouncementFormMixin, LaunchpadFormView):
     """A view to publish an annoucement."""
 
     schema = AddAnnouncementForm
-    field_names = ['publication_date']
-    page_title = 'Publish announcement'
+    field_names = ["publication_date"]
+    page_title = "Publish announcement"
 
     custom_widget_publication_date = AnnouncementDateWidget
 
-    @action(_('Publish'), name='publish')
+    @action(_("Publish"), name="publish")
     def publish_action(self, action, data):
-        publication_date = data['publication_date']
+        publication_date = data["publication_date"]
         self.context.setPublicationDate(publication_date)
-        self.next_url = canonical_url(self.context.target) + '/+announcements'
+        self.next_url = canonical_url(self.context.target) + "/+announcements"
 
 
 class AnnouncementRetractView(AnnouncementFormMixin, LaunchpadFormView):
     """A view to unpublish an announcement."""
 
     schema = IAnnouncement
-    page_title = 'Retract announcement'
+    page_title = "Retract announcement"
 
-    @action(_('Retract'), name='retract')
+    @action(_("Retract"), name="retract")
     def retract_action(self, action, data):
         self.context.retract()
-        self.next_url = canonical_url(self.context.target) + '/+announcements'
+        self.next_url = canonical_url(self.context.target) + "/+announcements"
 
 
 class AnnouncementDeleteView(AnnouncementFormMixin, LaunchpadFormView):
     """A view to delete an annoucement."""
 
     schema = IAnnouncement
-    page_title = 'Delete announcement'
+    page_title = "Delete announcement"
 
-    @action(_("Delete"), name="delete", validator='validate_cancel')
+    @action(_("Delete"), name="delete", validator="validate_cancel")
     def action_delete(self, action, data):
         self.context.destroySelf()
-        self.next_url = canonical_url(self.context.target) + '/+announcements'
+        self.next_url = canonical_url(self.context.target) + "/+announcements"
 
 
 @implementer(IAnnouncementCreateMenu)
 class HasAnnouncementsView(LaunchpadView, FeedsMixin):
     """A view class for pillars which have announcements."""
 
-    page_title = 'News and announcements'
+    page_title = "News and announcements"
     batch_size = config.launchpad.announcement_batch_size
 
     @cachedproperty
@@ -294,15 +295,19 @@ class HasAnnouncementsView(LaunchpadView, FeedsMixin):
 
     @cachedproperty
     def announcements(self):
-        published_only = not check_permission('launchpad.Edit', self.context)
+        published_only = not check_permission("launchpad.Edit", self.context)
         return self.context.getAnnouncements(
-                    limit=None, published_only=published_only)
+            limit=None, published_only=published_only
+        )
 
     @cachedproperty
     def latest_announcements(self):
-        published_only = not check_permission('launchpad.Edit', self.context)
-        return list(self.context.getAnnouncements(
-                    limit=5, published_only=published_only))
+        published_only = not check_permission("launchpad.Edit", self.context)
+        return list(
+            self.context.getAnnouncements(
+                limit=5, published_only=published_only
+            )
+        )
 
     @cachedproperty
     def has_announcements(self):
@@ -310,14 +315,15 @@ class HasAnnouncementsView(LaunchpadView, FeedsMixin):
 
     @cachedproperty
     def show_announcements(self):
-        return (len(self.latest_announcements) > 0
-            or check_permission('launchpad.Edit', self.context))
+        return len(self.latest_announcements) > 0 or check_permission(
+            "launchpad.Edit", self.context
+        )
 
     @cachedproperty
     def announcement_nav(self):
         return BatchNavigator(
-            self.announcements, self.request,
-            size=self.batch_size)
+            self.announcements, self.request, size=self.batch_size
+        )
 
 
 class AnnouncementSetView(HasAnnouncementsView):
@@ -326,12 +332,13 @@ class AnnouncementSetView(HasAnnouncementsView):
     All other feed links should be disabled on this page by
     overriding the feed_types class variable.
     """
+
     feed_types = (
         AnnouncementsFeedLink,
         RootAnnouncementsFeedLink,
-        )
+    )
 
-    page_title = 'Announcements from all projects hosted in Launchpad'
+    page_title = "Announcements from all projects hosted in Launchpad"
     label = page_title
 
 
diff --git a/lib/lp/registry/browser/branding.py b/lib/lp/registry/browser/branding.py
index 58008f5..8c53e1f 100644
--- a/lib/lp/registry/browser/branding.py
+++ b/lib/lp/registry/browser/branding.py
@@ -4,15 +4,12 @@
 """Browser views for items that can be displayed as images."""
 
 __all__ = [
-    'BrandingChangeView',
-    ]
+    "BrandingChangeView",
+]
 
 from zope.formlib.widget import CustomWidgetFactory
 
-from lp.app.browser.launchpadform import (
-    action,
-    LaunchpadEditFormView,
-    )
+from lp.app.browser.launchpadform import LaunchpadEditFormView, action
 from lp.app.widgets.image import ImageChangeWidget
 from lp.services.webapp import canonical_url
 
@@ -28,19 +25,24 @@ class BrandingChangeView(LaunchpadEditFormView):
 
     @property
     def label(self):
-        return ('Change the images used to represent %s in Launchpad'
-                % self.context.displayname)
+        return (
+            "Change the images used to represent %s in Launchpad"
+            % self.context.displayname
+        )
 
     page_title = "Change branding"
 
     custom_widget_icon = CustomWidgetFactory(
-        ImageChangeWidget, ImageChangeWidget.EDIT_STYLE)
+        ImageChangeWidget, ImageChangeWidget.EDIT_STYLE
+    )
     custom_widget_logo = CustomWidgetFactory(
-        ImageChangeWidget, ImageChangeWidget.EDIT_STYLE)
+        ImageChangeWidget, ImageChangeWidget.EDIT_STYLE
+    )
     custom_widget_mugshot = CustomWidgetFactory(
-        ImageChangeWidget, ImageChangeWidget.EDIT_STYLE)
+        ImageChangeWidget, ImageChangeWidget.EDIT_STYLE
+    )
 
-    @action("Change Branding", name='change')
+    @action("Change Branding", name="change")
     def change_action(self, action, data):
         self.updateContextFromData(data)
 
diff --git a/lib/lp/registry/browser/codeofconduct.py b/lib/lp/registry/browser/codeofconduct.py
index cda21f9..3c47070 100644
--- a/lib/lp/registry/browser/codeofconduct.py
+++ b/lib/lp/registry/browser/codeofconduct.py
@@ -4,48 +4,45 @@
 """View classes to handle signed Codes of Conduct."""
 
 __all__ = [
-    'AffirmCodeOfConductView',
-    'SignedCodeOfConductSetNavigation',
-    'CodeOfConductSetNavigation',
-    'CodeOfConductOverviewMenu',
-    'CodeOfConductSetOverviewMenu',
-    'SignedCodeOfConductSetOverviewMenu',
-    'SignedCodeOfConductOverviewMenu',
-    'CodeOfConductView',
-    'CodeOfConductDownloadView',
-    'CodeOfConductSetView',
-    'SignedCodeOfConductAddView',
-    'SignedCodeOfConductAckView',
-    'SignedCodeOfConductView',
-    'SignedCodeOfConductAdminView',
-    'SignedCodeOfConductActiveView',
-    'SignedCodeOfConductDeactiveView',
-    ]
+    "AffirmCodeOfConductView",
+    "SignedCodeOfConductSetNavigation",
+    "CodeOfConductSetNavigation",
+    "CodeOfConductOverviewMenu",
+    "CodeOfConductSetOverviewMenu",
+    "SignedCodeOfConductSetOverviewMenu",
+    "SignedCodeOfConductOverviewMenu",
+    "CodeOfConductView",
+    "CodeOfConductDownloadView",
+    "CodeOfConductSetView",
+    "SignedCodeOfConductAddView",
+    "SignedCodeOfConductAckView",
+    "SignedCodeOfConductView",
+    "SignedCodeOfConductAdminView",
+    "SignedCodeOfConductActiveView",
+    "SignedCodeOfConductDeactiveView",
+]
 
 from lazr.restful.interface import copy_field
 from zope.component import getUtility
 from zope.interface import Interface
 
 from lp import _
-from lp.app.browser.launchpadform import (
-    action,
-    LaunchpadFormView,
-    )
+from lp.app.browser.launchpadform import LaunchpadFormView, action
 from lp.registry.interfaces.codeofconduct import (
     ICodeOfConduct,
     ICodeOfConductConf,
     ICodeOfConductSet,
     ISignedCodeOfConduct,
     ISignedCodeOfConductSet,
-    )
+)
 from lp.services.webapp import (
     ApplicationMenu,
-    canonical_url,
-    enabled_with_permission,
     GetitemNavigation,
     LaunchpadView,
     Link,
-    )
+    canonical_url,
+    enabled_with_permission,
+)
 from lp.services.webapp.interfaces import ILaunchBag
 from lp.services.webapp.publisher import DataDownloadView
 
@@ -63,66 +60,68 @@ class CodeOfConductSetNavigation(GetitemNavigation):
 class CodeOfConductOverviewMenu(ApplicationMenu):
 
     usedfor = ICodeOfConduct
-    facet = 'overview'
-    links = ['sign', 'download']
+    facet = "overview"
+    links = ["sign", "download"]
 
     def sign(self):
-        text = 'Sign it'
-        if (self.context.current and
-            self.user and
-            not self.user.is_ubuntu_coc_signer):
+        text = "Sign it"
+        if (
+            self.context.current
+            and self.user
+            and not self.user.is_ubuntu_coc_signer
+        ):
             # Then...
             enabled = True
         else:
             enabled = False
-        return Link('+sign', text, enabled=enabled, icon='edit')
+        return Link("+sign", text, enabled=enabled, icon="edit")
 
     def download(self):
-        text = 'Download this version'
+        text = "Download this version"
         is_current = self.context.current
-        return Link('+download', text, enabled=is_current, icon='download')
+        return Link("+download", text, enabled=is_current, icon="download")
 
 
 class CodeOfConductSetOverviewMenu(ApplicationMenu):
 
     usedfor = ICodeOfConductSet
-    facet = 'overview'
-    links = ['admin']
+    facet = "overview"
+    links = ["admin"]
 
-    @enabled_with_permission('launchpad.Admin')
+    @enabled_with_permission("launchpad.Admin")
     def admin(self):
-        text = 'Administration console'
-        return Link('console', text, icon='edit')
+        text = "Administration console"
+        return Link("console", text, icon="edit")
 
 
 class SignedCodeOfConductSetOverviewMenu(ApplicationMenu):
 
     usedfor = ISignedCodeOfConductSet
-    facet = 'overview'
-    links = ['register']
+    facet = "overview"
+    links = ["register"]
 
     def register(self):
         text = "Register Someone's Signature"
-        return Link('+new', text, icon='add')
+        return Link("+new", text, icon="add")
 
 
 class SignedCodeOfConductOverviewMenu(ApplicationMenu):
 
     usedfor = ISignedCodeOfConduct
-    facet = 'overview'
-    links = ['activation', 'adminconsole']
+    facet = "overview"
+    links = ["activation", "adminconsole"]
 
     def activation(self):
         if self.context.active:
-            text = 'deactivate'
-            return Link('+deactivate', text, icon='edit')
+            text = "deactivate"
+            return Link("+deactivate", text, icon="edit")
         else:
-            text = 'activate'
-            return Link('+activate', text, icon='edit')
+            text = "activate"
+            return Link("+activate", text, icon="edit")
 
     def adminconsole(self):
-        text = 'Administration console'
-        return Link('../', text, icon='info')
+        text = "Administration console"
+        return Link("../", text, icon="info")
 
 
 class CodeOfConductView(LaunchpadView):
@@ -153,13 +152,13 @@ class CodeOfConductDownloadView(DataDownloadView):
     def filename(self):
         # Build a fancy filename:
         # - Use title with no spaces and append '.txt'
-        return self.context.title.replace(' ', '') + '.txt'
+        return self.context.title.replace(" ", "") + ".txt"
 
 
 class CodeOfConductSetView(LaunchpadView):
     """Simple view class for CoCSet page."""
 
-    page_title = 'Ubuntu Codes of Conduct'
+    page_title = "Ubuntu Codes of Conduct"
 
 
 class AffirmCodeOfConductView(LaunchpadFormView):
@@ -175,9 +174,11 @@ class AffirmCodeOfConductView(LaunchpadFormView):
 
         affirmed = copy_field(
             ISignedCodeOfConduct["affirmed"],
-            title=_("I agree to this Code of Conduct"), description="")
+            title=_("I agree to this Code of Conduct"),
+            description="",
+        )
 
-    field_names = ['affirmed']
+    field_names = ["affirmed"]
 
     @property
     def page_title(self):
@@ -187,28 +188,30 @@ class AffirmCodeOfConductView(LaunchpadFormView):
     def code_of_conduct(self):
         return self.context.content
 
-    @action('Affirm', name='affirm')
+    @action("Affirm", name="affirm")
     def affirm_action(self, action, data):
-        if data.get('affirmed'):
+        if data.get("affirmed"):
             signedcocset = getUtility(ISignedCodeOfConductSet)
             error_message = signedcocset.affirmAndStore(
-                self.user, self.context.content)
+                self.user, self.context.content
+            )
             if error_message:
                 self.addError(error_message)
                 return
-        self.next_url = canonical_url(self.user) + '/+codesofconduct'
+        self.next_url = canonical_url(self.user) + "/+codesofconduct"
 
 
 class SignedCodeOfConductAddView(LaunchpadFormView):
     """Add a new SignedCodeOfConduct Entry."""
+
     schema = ISignedCodeOfConduct
-    field_names = ['signedcode']
+    field_names = ["signedcode"]
 
     @property
     def page_title(self):
-        return 'Sign %s' % self.context.title
+        return "Sign %s" % self.context.title
 
-    @action('Continue', name='continue')
+    @action("Continue", name="continue")
     def continue_action(self, action, data):
         signedcode = data["signedcode"]
         signedcocset = getUtility(ISignedCodeOfConductSet)
@@ -219,7 +222,7 @@ class SignedCodeOfConductAddView(LaunchpadFormView):
         if error_message:
             self.addError(error_message)
             return
-        self.next_url = canonical_url(self.user) + '/+codesofconduct'
+        self.next_url = canonical_url(self.user) + "/+codesofconduct"
 
     @property
     def current(self):
@@ -231,9 +234,10 @@ class SignedCodeOfConductAddView(LaunchpadFormView):
 
 class SignedCodeOfConductAckView(LaunchpadFormView):
     """Acknowledge a Paper Submitted CoC."""
+
     schema = ISignedCodeOfConduct
-    field_names = ['owner']
-    label = 'Register a code of conduct signature'
+    field_names = ["owner"]
+    label = "Register a code of conduct signature"
     page_title = label
 
     @property
@@ -242,11 +246,12 @@ class SignedCodeOfConductAckView(LaunchpadFormView):
 
     cancel_url = next_url
 
-    @action('Register', name='add')
+    @action("Register", name="add")
     def createAndAdd(self, action, data):
         """Verify and Add the Acknowledge SignedCoC entry."""
         self.context.acknowledgeSignature(
-            user=data['owner'], recipient=self.user)
+            user=data["owner"], recipient=self.user
+        )
 
 
 class SignedCodeOfConductView(CodeOfConductView):
@@ -256,7 +261,7 @@ class SignedCodeOfConductView(CodeOfConductView):
 class SignedCodeOfConductAdminView(LaunchpadView):
     """Admin Console for SignedCodeOfConduct Entries."""
 
-    page_title = 'Administer Codes of Conduct'
+    page_title = "Administer Codes of Conduct"
 
     def __init__(self, context, request):
         self.context = context
@@ -266,26 +271,30 @@ class SignedCodeOfConductAdminView(LaunchpadView):
 
     def search(self):
         """Search Signed CoC by Owner Displayname"""
-        name = self.request.form.get('name')
-        searchfor = self.request.form.get('searchfor')
+        name = self.request.form.get("name")
+        searchfor = self.request.form.get("searchfor")
 
-        if (self.request.method != "POST" or
-            self.request.form.get("search") != "Search"):
+        if (
+            self.request.method != "POST"
+            or self.request.form.get("search") != "Search"
+        ):
             return
 
         # use utility to query on SignedCoCs
         sCoC_util = getUtility(ISignedCodeOfConductSet)
         self.results = list(
-            sCoC_util.searchByDisplayname(name, searchfor=searchfor))
+            sCoC_util.searchByDisplayname(name, searchfor=searchfor)
+        )
 
         return True
 
 
 class SignedCodeOfConductActiveView(LaunchpadFormView):
     """Active a SignedCodeOfConduct Entry."""
+
     schema = ISignedCodeOfConduct
-    field_names = ['admincomment']
-    label = 'Activate code of conduct signature'
+    field_names = ["admincomment"]
+    label = "Activate code of conduct signature"
     page_title = label
     state = True
 
@@ -296,24 +305,28 @@ class SignedCodeOfConductActiveView(LaunchpadFormView):
     cancel_url = next_url
 
     def _change(self, action, data):
-        admincomment = data['admincomment']
+        admincomment = data["admincomment"]
         sCoC_util = getUtility(ISignedCodeOfConductSet)
         sCoC_util.modifySignature(
-            sign_id=self.context.id, recipient=self.user,
-            admincomment=admincomment, state=self.state)
+            sign_id=self.context.id,
+            recipient=self.user,
+            admincomment=admincomment,
+            state=self.state,
+        )
         self.request.response.redirect(self.next_url)
 
-    @action('Activate', name='change')
+    @action("Activate", name="change")
     def activate(self, action, data):
         self._change(action, data)
 
 
 class SignedCodeOfConductDeactiveView(SignedCodeOfConductActiveView):
     """Deactivate a SignedCodeOfConduct Entry."""
-    label = 'Deactivate code of conduct signature'
+
+    label = "Deactivate code of conduct signature"
     page_title = label
     state = False
 
-    @action('Deactivate', name='change')
+    @action("Deactivate", name="change")
     def deactivate(self, action, data):
         self._change(action, data)
diff --git a/lib/lp/registry/browser/distribution.py b/lib/lp/registry/browser/distribution.py
index 436783f..7180941 100644
--- a/lib/lp/registry/browser/distribution.py
+++ b/lib/lp/registry/browser/distribution.py
@@ -4,41 +4,41 @@
 """Browser views for distributions."""
 
 __all__ = [
-    'DistributionAddView',
-    'DistributionAdminView',
-    'DistributionArchiveMirrorsRSSView',
-    'DistributionArchiveMirrorsView',
-    'DistributionArchivesView',
-    'DistributionChangeMembersView',
-    'DistributionChangeMirrorAdminView',
-    'DistributionChangeOCIProjectAdminView',
-    'DistributionChangeSecurityAdminView',
-    'DistributionCountryArchiveMirrorsView',
-    'DistributionDisabledMirrorsView',
-    'DistributionEditView',
-    'DistributionFacets',
-    'DistributionNavigation',
-    'DistributionPPASearchView',
-    'DistributionPackageSearchView',
-    'DistributionPendingReviewMirrorsView',
-    'DistributionPublisherConfigView',
-    'DistributionReassignmentView',
-    'DistributionSeriesView',
-    'DistributionDerivativesView',
-    'DistributionSeriesMirrorsRSSView',
-    'DistributionSeriesMirrorsView',
-    'DistributionSetActionNavigationMenu',
-    'DistributionSetBreadcrumb',
-    'DistributionSetContextMenu',
-    'DistributionSetNavigation',
-    'DistributionSetView',
-    'DistributionSpecificationsMenu',
-    'DistributionUnofficialMirrorsView',
-    'DistributionView',
-    ]
+    "DistributionAddView",
+    "DistributionAdminView",
+    "DistributionArchiveMirrorsRSSView",
+    "DistributionArchiveMirrorsView",
+    "DistributionArchivesView",
+    "DistributionChangeMembersView",
+    "DistributionChangeMirrorAdminView",
+    "DistributionChangeOCIProjectAdminView",
+    "DistributionChangeSecurityAdminView",
+    "DistributionCountryArchiveMirrorsView",
+    "DistributionDisabledMirrorsView",
+    "DistributionEditView",
+    "DistributionFacets",
+    "DistributionNavigation",
+    "DistributionPPASearchView",
+    "DistributionPackageSearchView",
+    "DistributionPendingReviewMirrorsView",
+    "DistributionPublisherConfigView",
+    "DistributionReassignmentView",
+    "DistributionSeriesView",
+    "DistributionDerivativesView",
+    "DistributionSeriesMirrorsRSSView",
+    "DistributionSeriesMirrorsView",
+    "DistributionSetActionNavigationMenu",
+    "DistributionSetBreadcrumb",
+    "DistributionSetContextMenu",
+    "DistributionSetNavigation",
+    "DistributionSetView",
+    "DistributionSpecificationsMenu",
+    "DistributionUnofficialMirrorsView",
+    "DistributionView",
+]
 
-from collections import defaultdict
 import datetime
+from collections import defaultdict
 
 from lazr.restful.utils import smartquote
 from zope.component import getUtility
@@ -55,10 +55,10 @@ from zope.security.interfaces import Unauthorized
 from lp.answers.browser.faqtarget import FAQTargetNavigationMixin
 from lp.answers.browser.questiontarget import QuestionTargetTraversalMixin
 from lp.app.browser.launchpadform import (
-    action,
     LaunchpadEditFormView,
     LaunchpadFormView,
-    )
+    action,
+)
 from lp.app.browser.lazrjs import InlinePersonEditPickerWidget
 from lp.app.browser.tales import format_link
 from lp.app.enums import PILLAR_INFORMATION_TYPES
@@ -68,77 +68,71 @@ from lp.app.widgets.image import ImageChangeWidget
 from lp.app.widgets.itemswidgets import (
     LabeledMultiCheckBoxWidget,
     LaunchpadRadioWidgetWithDescription,
-    )
+)
 from lp.archivepublisher.interfaces.publisherconfig import (
     IPublisherConfig,
     IPublisherConfigSet,
-    )
+)
 from lp.blueprints.browser.specificationtarget import (
     HasSpecificationsMenuMixin,
-    )
+)
 from lp.bugs.browser.bugtask import BugTargetTraversalMixin
 from lp.bugs.browser.structuralsubscription import (
-    expose_structural_subscription_data_to_js,
     StructuralSubscriptionMenuMixin,
     StructuralSubscriptionTargetTraversalMixin,
-    )
+    expose_structural_subscription_data_to_js,
+)
 from lp.buildmaster.interfaces.processor import IProcessorSet
 from lp.code.browser.vcslisting import TargetDefaultVCSNavigationMixin
-from lp.registry.browser import (
-    add_subscribe_link,
-    RegistryEditFormView,
-    )
+from lp.registry.browser import RegistryEditFormView, add_subscribe_link
 from lp.registry.browser.announcement import HasAnnouncementsView
 from lp.registry.browser.menu import (
     IRegistryCollectionNavigationMenu,
     RegistryCollectionActionMenuBase,
-    )
+)
 from lp.registry.browser.objectreassignment import ObjectReassignmentView
 from lp.registry.browser.pillar import (
     PillarBugsMenu,
     PillarNavigationMixin,
     PillarViewMixin,
-    )
+)
 from lp.registry.browser.widgets.ocicredentialswidget import (
     OCICredentialsWidget,
-    )
+)
 from lp.registry.enums import DistributionDefaultTraversalPolicy
 from lp.registry.interfaces.distribution import (
     IDistribution,
     IDistributionMirrorMenuMarker,
     IDistributionSet,
-    )
+)
 from lp.registry.interfaces.distributionmirror import (
     MirrorContent,
     MirrorSpeed,
-    )
+)
 from lp.registry.interfaces.ociproject import (
-    IOCIProjectSet,
     OCI_PROJECT_ALLOW_CREATE,
-    )
+    IOCIProjectSet,
+)
 from lp.registry.interfaces.series import SeriesStatus
 from lp.services.database.decoratedresultset import DecoratedResultSet
 from lp.services.features import getFeatureFlag
 from lp.services.feeds.browser import FeedsMixin
-from lp.services.geoip.helpers import (
-    ipaddress_from_request,
-    request_country,
-    )
+from lp.services.geoip.helpers import ipaddress_from_request, request_country
 from lp.services.helpers import english_list
 from lp.services.propertycache import cachedproperty
 from lp.services.webapp import (
     ApplicationMenu,
-    canonical_url,
     ContextMenu,
-    enabled_with_permission,
     LaunchpadView,
     Link,
     Navigation,
     NavigationMenu,
-    redirection,
     StandardLaunchpadFacets,
+    canonical_url,
+    enabled_with_permission,
+    redirection,
     stepthrough,
-    )
+)
 from lp.services.webapp.authorization import check_permission
 from lp.services.webapp.batching import BatchNavigator
 from lp.services.webapp.breadcrumb import Breadcrumb
@@ -150,59 +144,70 @@ from lp.soyuz.interfaces.archive import IArchiveSet
 
 
 class DistributionNavigation(
-    Navigation, BugTargetTraversalMixin, QuestionTargetTraversalMixin,
-    FAQTargetNavigationMixin, StructuralSubscriptionTargetTraversalMixin,
-    PillarNavigationMixin, TargetDefaultVCSNavigationMixin):
+    Navigation,
+    BugTargetTraversalMixin,
+    QuestionTargetTraversalMixin,
+    FAQTargetNavigationMixin,
+    StructuralSubscriptionTargetTraversalMixin,
+    PillarNavigationMixin,
+    TargetDefaultVCSNavigationMixin,
+):
 
     usedfor = IDistribution
 
-    @redirection('+source', status=301)
+    @redirection("+source", status=301)
     def redirect_source(self):
         return canonical_url(self.context)
 
-    @stepthrough('+mirror')
+    @stepthrough("+mirror")
     def traverse_mirrors(self, name):
         return self.context.getMirrorByName(name)
 
-    @stepthrough('+source')
+    @stepthrough("+source")
     def traverse_sources(self, name):
         dsp = self.context.getSourcePackage(name)
         policy = self.context.default_traversal_policy
-        if (policy == DistributionDefaultTraversalPolicy.SOURCE_PACKAGE and
-                not self.context.redirect_default_traversal):
+        if (
+            policy == DistributionDefaultTraversalPolicy.SOURCE_PACKAGE
+            and not self.context.redirect_default_traversal
+        ):
             return self.redirectSubTree(
-                canonical_url(dsp, request=self.request), status=303)
+                canonical_url(dsp, request=self.request), status=303
+            )
         else:
             return dsp
 
-    @stepthrough('+oci')
+    @stepthrough("+oci")
     def traverse_oci(self, name):
         oci_project = self.context.getOCIProject(name)
         policy = self.context.default_traversal_policy
-        if (policy == DistributionDefaultTraversalPolicy.OCI_PROJECT and
-                not self.context.redirect_default_traversal):
+        if (
+            policy == DistributionDefaultTraversalPolicy.OCI_PROJECT
+            and not self.context.redirect_default_traversal
+        ):
             return self.redirectSubTree(
-                canonical_url(oci_project, request=self.request), status=303)
+                canonical_url(oci_project, request=self.request), status=303
+            )
         else:
             return oci_project
 
-    @stepthrough('+milestone')
+    @stepthrough("+milestone")
     def traverse_milestone(self, name):
         return self.context.getMilestone(name)
 
-    @stepthrough('+announcement')
+    @stepthrough("+announcement")
     def traverse_announcement(self, name):
         return self.context.getAnnouncement(name)
 
-    @stepthrough('+spec')
+    @stepthrough("+spec")
     def traverse_spec(self, name):
         return self.context.getSpecification(name)
 
-    @stepthrough('+archive')
+    @stepthrough("+archive")
     def traverse_archive(self, name):
         return self.context.getArchive(name)
 
-    @stepthrough('+commercialsubscription')
+    @stepthrough("+commercialsubscription")
     def traverse_commercialsubscription(self, name):
         return self.context.commercial_subscription
 
@@ -213,21 +218,24 @@ class DistributionNavigation(
             resolved = self.context.resolveSeriesAlias(name)
             return resolved, True
 
-    @stepthrough('+series')
+    @stepthrough("+series")
     def traverse_series(self, name):
         series, redirect = self._resolveSeries(name)
         if not redirect:
             policy = self.context.default_traversal_policy
-            if (policy == DistributionDefaultTraversalPolicy.SERIES and
-                    not self.context.redirect_default_traversal):
+            if (
+                policy == DistributionDefaultTraversalPolicy.SERIES
+                and not self.context.redirect_default_traversal
+            ):
                 redirect = True
         if redirect:
             return self.redirectSubTree(
-                canonical_url(series, request=self.request), status=303)
+                canonical_url(series, request=self.request), status=303
+            )
         else:
             return series
 
-    @stepthrough('+vulnerability')
+    @stepthrough("+vulnerability")
     def traverse_vulnerability(self, id):
         try:
             id = int(id)
@@ -249,12 +257,14 @@ class DistributionNavigation(
             redirect = False
         else:
             raise AssertionError(
-                "Unknown default traversal policy %r" % policy)
+                "Unknown default traversal policy %r" % policy
+            )
         if obj is None:
             return None
         if redirect or self.context.redirect_default_traversal:
             return self.redirectSubTree(
-                canonical_url(obj, request=self.request), status=303)
+                canonical_url(obj, request=self.request), status=303
+            )
         else:
             return obj
 
@@ -278,37 +288,39 @@ class DistributionFacets(StandardLaunchpadFacets):
 
 class DistributionSetBreadcrumb(Breadcrumb):
     """Builds a breadcrumb for an `IDistributionSet`."""
-    text = 'Distributions'
+
+    text = "Distributions"
 
 
 class DistributionSetContextMenu(ContextMenu):
 
     usedfor = IDistributionSet
-    links = ['products', 'distributions', 'people', 'meetings']
+    links = ["products", "distributions", "people", "meetings"]
 
     def distributions(self):
-        return Link('/distros/', 'View distributions')
+        return Link("/distros/", "View distributions")
 
     def products(self):
-        return Link('/projects/', 'View projects')
+        return Link("/projects/", "View projects")
 
     def people(self):
-        return Link('/people/', 'View people')
+        return Link("/people/", "View people")
 
     def meetings(self):
-        return Link('/sprints/', 'View meetings')
+        return Link("/sprints/", "View meetings")
 
 
 class DistributionMirrorsNavigationMenu(NavigationMenu):
 
     usedfor = IDistributionMirrorMenuMarker
-    facet = 'overview'
-    links = ('cdimage_mirrors',
-             'archive_mirrors',
-             'disabled_mirrors',
-             'pending_review_mirrors',
-             'unofficial_mirrors',
-             )
+    facet = "overview"
+    links = (
+        "cdimage_mirrors",
+        "archive_mirrors",
+        "disabled_mirrors",
+        "pending_review_mirrors",
+        "unofficial_mirrors",
+    )
 
     @property
     def distribution(self):
@@ -319,54 +331,58 @@ class DistributionMirrorsNavigationMenu(NavigationMenu):
         return self.context.context
 
     def cdimage_mirrors(self):
-        text = 'CD mirrors'
-        return Link('+cdmirrors', text, icon='info')
+        text = "CD mirrors"
+        return Link("+cdmirrors", text, icon="info")
 
     def archive_mirrors(self):
-        text = 'Archive mirrors'
-        return Link('+archivemirrors', text, icon='info')
+        text = "Archive mirrors"
+        return Link("+archivemirrors", text, icon="info")
 
     def newmirror(self):
-        text = 'Register mirror'
-        return Link('+newmirror', text, icon='add')
+        text = "Register mirror"
+        return Link("+newmirror", text, icon="add")
 
     def _userCanSeeNonPublicMirrorListings(self):
         """Does the user have rights to see non-public mirrors listings?"""
         user = getUtility(ILaunchBag).user
-        return (self.distribution.supports_mirrors
-                and user is not None
-                and user.inTeam(self.distribution.mirror_admin))
+        return (
+            self.distribution.supports_mirrors
+            and user is not None
+            and user.inTeam(self.distribution.mirror_admin)
+        )
 
     def disabled_mirrors(self):
-        text = 'Disabled mirrors'
+        text = "Disabled mirrors"
         enabled = self._userCanSeeNonPublicMirrorListings()
-        return Link('+disabledmirrors', text, enabled=enabled, icon='info')
+        return Link("+disabledmirrors", text, enabled=enabled, icon="info")
 
     def pending_review_mirrors(self):
-        text = 'Pending-review mirrors'
+        text = "Pending-review mirrors"
         enabled = self._userCanSeeNonPublicMirrorListings()
         return Link(
-            '+pendingreviewmirrors', text, enabled=enabled, icon='info')
+            "+pendingreviewmirrors", text, enabled=enabled, icon="info"
+        )
 
     def unofficial_mirrors(self):
-        text = 'Unofficial mirrors'
+        text = "Unofficial mirrors"
         enabled = self._userCanSeeNonPublicMirrorListings()
-        return Link('+unofficialmirrors', text, enabled=enabled, icon='info')
+        return Link("+unofficialmirrors", text, enabled=enabled, icon="info")
 
 
 class DistributionLinksMixin(StructuralSubscriptionMenuMixin):
     """A mixin to provide common links to menus."""
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def edit(self):
-        text = 'Change details'
-        return Link('+edit', text, icon='edit')
+        text = "Change details"
+        return Link("+edit", text, icon="edit")
 
 
 class DistributionNavigationMenu(NavigationMenu, DistributionLinksMixin):
     """A menu of context actions."""
+
     usedfor = IDistribution
-    facet = 'overview'
+    facet = "overview"
 
     @enabled_with_permission("launchpad.Admin")
     def admin(self):
@@ -378,221 +394,232 @@ class DistributionNavigationMenu(NavigationMenu, DistributionLinksMixin):
         text = "Configure publisher"
         return Link("+pubconf", text, icon="edit")
 
-    @enabled_with_permission('launchpad.Driver')
+    @enabled_with_permission("launchpad.Driver")
     def sharing(self):
-        return Link('+sharing', 'Sharing', icon='edit')
+        return Link("+sharing", "Sharing", icon="edit")
 
     def new_oci_project(self):
-        text = 'Create an OCI project'
-        link = Link('+new-oci-project', text, icon='add')
-        link.enabled = (
-            bool(getFeatureFlag(OCI_PROJECT_ALLOW_CREATE))
-            and self.context.canAdministerOCIProjects(self.user))
+        text = "Create an OCI project"
+        link = Link("+new-oci-project", text, icon="add")
+        link.enabled = bool(
+            getFeatureFlag(OCI_PROJECT_ALLOW_CREATE)
+        ) and self.context.canAdministerOCIProjects(self.user)
         return link
 
     def search_oci_project(self):
         oci_projects = getUtility(IOCIProjectSet).findByPillarAndName(
-            self.context, '')
-        text = 'Search for OCI project'
-        link = Link('+search-oci-project', text, icon='info')
+            self.context, ""
+        )
+        text = "Search for OCI project"
+        link = Link("+search-oci-project", text, icon="info")
         link.enabled = not oci_projects.is_empty()
         return link
 
     @cachedproperty
     def links(self):
         return [
-            'edit', 'admin', 'pubconf', 'subscribe_to_bug_mail',
-            'edit_bug_mail', 'sharing', 'new_oci_project',
-            'search_oci_project']
+            "edit",
+            "admin",
+            "pubconf",
+            "subscribe_to_bug_mail",
+            "edit_bug_mail",
+            "sharing",
+            "new_oci_project",
+            "search_oci_project",
+        ]
 
 
 class DistributionOverviewMenu(ApplicationMenu, DistributionLinksMixin):
 
     usedfor = IDistribution
-    facet = 'overview'
+    facet = "overview"
     links = [
-        'edit',
-        'branding',
-        'driver',
-        'search',
-        'members',
-        'mirror_admin',
-        'oci_project_admin',
-        'security_admin',
-        'reassign',
-        'addseries',
-        'series',
-        'derivatives',
-        'milestones',
-        'top_contributors',
-        'builds',
-        'cdimage_mirrors',
-        'archive_mirrors',
-        'pending_review_mirrors',
-        'disabled_mirrors',
-        'unofficial_mirrors',
-        'newmirror',
-        'announce',
-        'announcements',
-        'ppas',
-        'configure_answers',
-        'configure_blueprints',
-        'configure_translations',
-        ]
+        "edit",
+        "branding",
+        "driver",
+        "search",
+        "members",
+        "mirror_admin",
+        "oci_project_admin",
+        "security_admin",
+        "reassign",
+        "addseries",
+        "series",
+        "derivatives",
+        "milestones",
+        "top_contributors",
+        "builds",
+        "cdimage_mirrors",
+        "archive_mirrors",
+        "pending_review_mirrors",
+        "disabled_mirrors",
+        "unofficial_mirrors",
+        "newmirror",
+        "announce",
+        "announcements",
+        "ppas",
+        "configure_answers",
+        "configure_blueprints",
+        "configure_translations",
+    ]
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def branding(self):
-        text = 'Change branding'
-        return Link('+branding', text, icon='edit')
+        text = "Change branding"
+        return Link("+branding", text, icon="edit")
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def driver(self):
-        text = 'Appoint driver'
-        summary = 'Someone with permission to set goals for all series'
-        return Link('+driver', text, summary, icon='edit')
+        text = "Appoint driver"
+        summary = "Someone with permission to set goals for all series"
+        return Link("+driver", text, summary, icon="edit")
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def reassign(self):
-        text = 'Change maintainer'
-        return Link('+reassign', text, icon='edit')
+        text = "Change maintainer"
+        return Link("+reassign", text, icon="edit")
 
     def newmirror(self):
-        text = 'Register a new mirror'
+        text = "Register a new mirror"
         enabled = self.context.supports_mirrors
-        return Link('+newmirror', text, enabled=enabled, icon='add')
+        return Link("+newmirror", text, enabled=enabled, icon="add")
 
     def top_contributors(self):
-        text = 'More contributors'
-        return Link('+topcontributors', text, icon='info')
+        text = "More contributors"
+        return Link("+topcontributors", text, icon="info")
 
     def cdimage_mirrors(self):
-        text = 'CD mirrors'
-        return Link('+cdmirrors', text, icon='info')
+        text = "CD mirrors"
+        return Link("+cdmirrors", text, icon="info")
 
     def archive_mirrors(self):
-        text = 'Archive mirrors'
-        return Link('+archivemirrors', text, icon='info')
+        text = "Archive mirrors"
+        return Link("+archivemirrors", text, icon="info")
 
     def _userCanSeeNonPublicMirrorListings(self):
         """Does the user have rights to see non-public mirrors listings?"""
         user = getUtility(ILaunchBag).user
-        return (self.context.supports_mirrors
-                and user is not None
-                and user.inTeam(self.context.mirror_admin))
+        return (
+            self.context.supports_mirrors
+            and user is not None
+            and user.inTeam(self.context.mirror_admin)
+        )
 
     def disabled_mirrors(self):
-        text = 'Disabled mirrors'
+        text = "Disabled mirrors"
         enabled = self._userCanSeeNonPublicMirrorListings()
-        return Link('+disabledmirrors', text, enabled=enabled, icon='info')
+        return Link("+disabledmirrors", text, enabled=enabled, icon="info")
 
     def pending_review_mirrors(self):
-        text = 'Pending-review mirrors'
+        text = "Pending-review mirrors"
         enabled = self._userCanSeeNonPublicMirrorListings()
         return Link(
-            '+pendingreviewmirrors', text, enabled=enabled, icon='info')
+            "+pendingreviewmirrors", text, enabled=enabled, icon="info"
+        )
 
     def unofficial_mirrors(self):
-        text = 'Unofficial mirrors'
+        text = "Unofficial mirrors"
         enabled = self._userCanSeeNonPublicMirrorListings()
-        return Link('+unofficialmirrors', text, enabled=enabled, icon='info')
+        return Link("+unofficialmirrors", text, enabled=enabled, icon="info")
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def members(self):
-        text = 'Change members team'
-        return Link('+selectmemberteam', text, icon='edit')
+        text = "Change members team"
+        return Link("+selectmemberteam", text, icon="edit")
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def mirror_admin(self):
-        text = 'Change mirror admins'
+        text = "Change mirror admins"
         enabled = self.context.supports_mirrors
-        return Link('+selectmirroradmins', text, enabled=enabled, icon='edit')
+        return Link("+selectmirroradmins", text, enabled=enabled, icon="edit")
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def oci_project_admin(self):
-        text = 'Change OCI project admins'
-        return Link('+select-oci-project-admins', text, icon='edit')
+        text = "Change OCI project admins"
+        return Link("+select-oci-project-admins", text, icon="edit")
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def security_admin(self):
-        text = 'Change security admins'
-        return Link('+select-security-admins', text, icon='edit')
+        text = "Change security admins"
+        return Link("+select-security-admins", text, icon="edit")
 
     def search(self):
-        text = 'Search packages'
-        return Link('+search', text, icon='search')
+        text = "Search packages"
+        return Link("+search", text, icon="search")
 
-    @enabled_with_permission('launchpad.Moderate')
+    @enabled_with_permission("launchpad.Moderate")
     def addseries(self):
-        text = 'Add series'
-        return Link('+addseries', text, icon='add')
+        text = "Add series"
+        return Link("+addseries", text, icon="add")
 
     def series(self):
-        text = 'All series'
-        return Link('+series', text, icon='info')
+        text = "All series"
+        return Link("+series", text, icon="info")
 
     def derivatives(self):
-        text = 'All derivatives'
-        return Link('+derivatives', text, icon='info')
+        text = "All derivatives"
+        return Link("+derivatives", text, icon="info")
 
     def milestones(self):
-        text = 'All milestones'
-        return Link('+milestones', text, icon='info')
+        text = "All milestones"
+        return Link("+milestones", text, icon="info")
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def announce(self):
-        text = 'Make announcement'
-        summary = 'Publish an item of news for this project'
-        return Link('+announce', text, summary, icon='add')
+        text = "Make announcement"
+        summary = "Publish an item of news for this project"
+        return Link("+announce", text, summary, icon="add")
 
     def announcements(self):
-        text = 'Read all announcements'
+        text = "Read all announcements"
         enabled = bool(self.context.getAnnouncements())
-        return Link('+announcements', text, icon='info', enabled=enabled)
+        return Link("+announcements", text, icon="info", enabled=enabled)
 
     def builds(self):
-        text = 'Builds'
-        return Link('+builds', text, icon='info')
+        text = "Builds"
+        return Link("+builds", text, icon="info")
 
     def ppas(self):
-        text = 'Personal Package Archives'
-        return Link('+ppas', text, icon='info')
+        text = "Personal Package Archives"
+        return Link("+ppas", text, icon="info")
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def configure_answers(self):
-        text = 'Configure support tracker'
-        summary = 'Allow users to ask questions on this project'
-        return Link('+edit', text, summary, icon='edit')
+        text = "Configure support tracker"
+        summary = "Allow users to ask questions on this project"
+        return Link("+edit", text, summary, icon="edit")
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def configure_blueprints(self):
-        text = 'Configure blueprints'
-        summary = 'Enable tracking of feature planning.'
-        return Link('+edit', text, summary, icon='edit')
+        text = "Configure blueprints"
+        summary = "Enable tracking of feature planning."
+        return Link("+edit", text, summary, icon="edit")
 
-    @enabled_with_permission('launchpad.TranslationsAdmin')
+    @enabled_with_permission("launchpad.TranslationsAdmin")
     def configure_translations(self):
-        text = 'Configure translations'
-        summary = 'Allow users to provide translations for this project.'
-        return Link('+configure-translations', text, summary, icon='edit')
+        text = "Configure translations"
+        summary = "Allow users to provide translations for this project."
+        return Link("+configure-translations", text, summary, icon="edit")
 
 
 class DistributionBugsMenu(PillarBugsMenu):
 
     usedfor = IDistribution
-    facet = 'bugs'
+    facet = "bugs"
 
     @property
     def links(self):
-        links = ['bugsupervisor', 'cve', 'filebug']
+        links = ["bugsupervisor", "cve", "filebug"]
         add_subscribe_link(links)
         return links
 
 
-class DistributionSpecificationsMenu(NavigationMenu,
-                                     HasSpecificationsMenuMixin):
+class DistributionSpecificationsMenu(
+    NavigationMenu, HasSpecificationsMenuMixin
+):
     usedfor = IDistribution
-    facet = 'specifications'
-    links = ['listall', 'doc', 'assignments', 'new', 'register_sprint']
+    facet = "specifications"
+    links = ["listall", "doc", "assignments", "new", "register_sprint"]
 
 
 class DistributionPackageSearchView(PackageSearchViewBase):
@@ -606,9 +633,9 @@ class DistributionPackageSearchView(PackageSearchViewBase):
         # default to searches on binary names, but allow the user to
         # select.
         if self.context.has_published_binaries:
-            self.search_type = self.request.get("search_type", 'binary')
+            self.search_type = self.request.get("search_type", "binary")
         else:
-            self.search_type = 'source'
+            self.search_type = "source"
 
     def contextSpecificSearch(self):
         """See `AbstractPackageSearchView`."""
@@ -617,7 +644,8 @@ class DistributionPackageSearchView(PackageSearchViewBase):
             return self.context.searchBinaryPackages(self.text)
         else:
             non_exact_matches = self.context.searchSourcePackageCaches(
-                self.text)
+                self.text
+            )
 
             # The searchBinaryPackageCaches() method returns tuples, so we
             # use the DecoratedResultSet here to just get the
@@ -626,7 +654,8 @@ class DistributionPackageSearchView(PackageSearchViewBase):
                 return cache_name_tuple[0]
 
             non_exact_matches = DecoratedResultSet(
-                non_exact_matches, tuple_to_package_cache)
+                non_exact_matches, tuple_to_package_cache
+            )
 
         return non_exact_matches.config(distinct=True)
 
@@ -653,13 +682,14 @@ class DistributionPackageSearchView(PackageSearchViewBase):
         """
         return "%s/+search?search_type=source&%s" % (
             canonical_url(self.context),
-            self.request.get('QUERY_STRING'),
-            )
+            self.request.get("QUERY_STRING"),
+        )
 
     @cachedproperty
     def exact_matches(self):
         return self.context.searchBinaryPackages(
-            self.text, exact_match=True).order_by('name')
+            self.text, exact_match=True
+        ).order_by("name")
 
     @property
     def has_exact_matches(self):
@@ -676,20 +706,20 @@ class DistributionPackageSearchView(PackageSearchViewBase):
 
         for package_cache in self.batchnav.currentBatch():
             names[package_cache.name] = self._listFirstFiveMatchingNames(
-                self.text, package_cache.binpkgnames)
+                self.text, package_cache.binpkgnames
+            )
 
         return names
 
     def _listFirstFiveMatchingNames(self, match_text, space_separated_list):
         """Returns a comma-separated list of the first five matching items"""
-        name_list = space_separated_list.split(' ')
+        name_list = space_separated_list.split(" ")
 
-        matching_names = [
-            name for name in name_list if match_text in name]
+        matching_names = [name for name in name_list if match_text in name]
 
         if len(matching_names) > 5:
             matching_names = matching_names[:5]
-            matching_names.append('...')
+            matching_names.append("...")
 
         return ", ".join(matching_names)
 
@@ -704,8 +734,9 @@ class DistributionPackageSearchView(PackageSearchViewBase):
             # create a list, convert the list to a set and back again:
             distroseries_list = [
                 pubrec.distroseries.name
-                    for pubrec in package.current_publishing_records
-                        if pubrec.distroseries.active]
+                for pubrec in package.current_publishing_records
+                if pubrec.distroseries.active
+            ]
             distroseries_list = list(set(distroseries_list))
 
             # Yay for alphabetical series names.
@@ -732,92 +763,110 @@ class DistributionView(PillarViewMixin, HasAnnouncementsView, FeedsMixin):
     def initialize(self):
         super().initialize()
         expose_structural_subscription_data_to_js(
-            self.context, self.request, self.user)
+            self.context, self.request, self.user
+        )
 
     @property
     def page_title(self):
-        return '%s in Launchpad' % self.context.displayname
+        return "%s in Launchpad" % self.context.displayname
 
     @property
     def maintainer_widget(self):
         return InlinePersonEditPickerWidget(
-            self.context, IDistribution['owner'],
+            self.context,
+            IDistribution["owner"],
             format_link(self.context.owner),
-            header='Change maintainer', edit_view='+reassign',
-            step_title='Select a new maintainer', show_create_team=True)
+            header="Change maintainer",
+            edit_view="+reassign",
+            step_title="Select a new maintainer",
+            show_create_team=True,
+        )
 
     @property
     def driver_widget(self):
-        if canWrite(self.context, 'driver'):
-            empty_value = 'Specify a driver'
+        if canWrite(self.context, "driver"):
+            empty_value = "Specify a driver"
         else:
-            empty_value = 'None'
+            empty_value = "None"
         return InlinePersonEditPickerWidget(
-            self.context, IDistribution['driver'],
+            self.context,
+            IDistribution["driver"],
             format_link(self.context.driver, empty_value=empty_value),
-            header='Change driver', edit_view='+driver',
+            header="Change driver",
+            edit_view="+driver",
             null_display_value=empty_value,
-            step_title='Select a new driver', show_create_team=True)
+            step_title="Select a new driver",
+            show_create_team=True,
+        )
 
     @property
     def members_widget(self):
-        if canWrite(self.context, 'members'):
-            empty_value = ' Specify the members team'
+        if canWrite(self.context, "members"):
+            empty_value = " Specify the members team"
         else:
-            empty_value = 'None'
+            empty_value = "None"
         return InlinePersonEditPickerWidget(
-            self.context, IDistribution['members'],
+            self.context,
+            IDistribution["members"],
             format_link(self.context.members, empty_value=empty_value),
-            header='Change the members team', edit_view='+selectmemberteam',
+            header="Change the members team",
+            edit_view="+selectmemberteam",
             null_display_value=empty_value,
-            step_title='Select a new members team')
+            step_title="Select a new members team",
+        )
 
     @property
     def mirror_admin_widget(self):
-        if canWrite(self.context, 'mirror_admin'):
-            empty_value = ' Specify a mirror administrator'
+        if canWrite(self.context, "mirror_admin"):
+            empty_value = " Specify a mirror administrator"
         else:
-            empty_value = 'None'
+            empty_value = "None"
         return InlinePersonEditPickerWidget(
-            self.context, IDistribution['mirror_admin'],
+            self.context,
+            IDistribution["mirror_admin"],
             format_link(self.context.mirror_admin, empty_value=empty_value),
-            header='Change the mirror administrator',
-            edit_view='+selectmirroradmins', null_display_value=empty_value,
-            step_title='Select a new mirror administrator')
+            header="Change the mirror administrator",
+            edit_view="+selectmirroradmins",
+            null_display_value=empty_value,
+            step_title="Select a new mirror administrator",
+        )
 
     @property
     def oci_project_admin_widget(self):
-        if canWrite(self.context, 'oci_project_admin'):
-            empty_value = ' Specify an OCI project administrator'
+        if canWrite(self.context, "oci_project_admin"):
+            empty_value = " Specify an OCI project administrator"
         else:
-            empty_value = 'None'
+            empty_value = "None"
         return InlinePersonEditPickerWidget(
-            self.context, IDistribution['oci_project_admin'],
+            self.context,
+            IDistribution["oci_project_admin"],
             format_link(
-                self.context.oci_project_admin, empty_value=empty_value),
-            header='Change the OCI project administrator',
-            edit_view='+select-oci-project-admins',
+                self.context.oci_project_admin, empty_value=empty_value
+            ),
+            header="Change the OCI project administrator",
+            edit_view="+select-oci-project-admins",
             null_display_value=empty_value,
-            step_title='Select a new OCI project administrator')
+            step_title="Select a new OCI project administrator",
+        )
 
     @property
     def security_admin_widget(self):
-        if canWrite(self.context, 'security_admin'):
-            empty_value = ' Specify a security administrator'
+        if canWrite(self.context, "security_admin"):
+            empty_value = " Specify a security administrator"
         else:
-            empty_value = 'None'
+            empty_value = "None"
 
         return InlinePersonEditPickerWidget(
             self.context,
-            IDistribution['security_admin'],
+            IDistribution["security_admin"],
             format_link(
                 self.context.security_admin,
                 empty_value=empty_value,
             ),
-            header='Change the security administrator',
-            edit_view='+select-security-admins',
+            header="Change the security administrator",
+            edit_view="+select-security-admins",
             null_display_value=empty_value,
-            step_title='Select a new security administrator'
+            step_title="Select a new security administrator",
         )
 
     def linkedMilestonesForSeries(self, series):
@@ -830,8 +879,9 @@ class DistributionView(PillarViewMixin, HasAnnouncementsView, FeedsMixin):
         linked_milestones = []
         for milestone in milestones:
             linked_milestones.append(
-                "<a href=%s>%s</a>" % (
-                    canonical_url(milestone), milestone.name))
+                "<a href=%s>%s</a>"
+                % (canonical_url(milestone), milestone.name)
+            )
 
         return english_list(linked_milestones)
 
@@ -849,15 +899,15 @@ class DistributionView(PillarViewMixin, HasAnnouncementsView, FeedsMixin):
         first two are allowed via the Launchpad.Edit permission.  The latter
         is allowed via Launchpad.Commercial.
         """
-        return (check_permission('launchpad.Edit', self.context) or
-                check_permission('launchpad.Commercial', self.context))
+        return check_permission(
+            "launchpad.Edit", self.context
+        ) or check_permission("launchpad.Commercial", self.context)
 
 
 class DistributionArchivesView(LaunchpadView):
-
     @property
     def page_title(self):
-        return '%s Copy Archives' % self.context.title
+        return "%s Copy Archives" % self.context.title
 
     @property
     def batchnav(self):
@@ -871,9 +921,12 @@ class DistributionArchivesView(LaunchpadView):
         The context may be an IDistroSeries or a users archives.
         """
         results = getUtility(IArchiveSet).getArchivesForDistribution(
-            self.context, purposes=[ArchivePurpose.COPY], user=self.user,
-            exclude_disabled=False)
-        return results.order_by('date_created DESC')
+            self.context,
+            purposes=[ArchivePurpose.COPY],
+            user=self.user,
+            exclude_disabled=False,
+        )
+        return results.order_by("date_created DESC")
 
 
 class DistributionPPASearchView(LaunchpadView):
@@ -882,7 +935,7 @@ class DistributionPPASearchView(LaunchpadView):
     page_title = "Personal Package Archives"
 
     def initialize(self):
-        self.name_filter = self.request.get('name_filter')
+        self.name_filter = self.request.get("name_filter")
         if isinstance(self.name_filter, list):
             # This happens if someone hand-hacks the URL so that it has
             # more than one name_filter field.  We could do something
@@ -890,11 +943,11 @@ class DistributionPPASearchView(LaunchpadView):
             # but we can acutally do better and join the terms supplied
             # instead.
             self.name_filter = " ".join(self.name_filter)
-        self.show_inactive = self.request.get('show_inactive')
+        self.show_inactive = self.request.get("show_inactive")
 
     @property
     def label(self):
-        return 'Personal Package Archives for %s' % self.context.title
+        return "Personal Package Archives for %s" % self.context.title
 
     @property
     def search_results(self):
@@ -905,11 +958,11 @@ class DistributionPPASearchView(LaunchpadView):
         # Preserve self.show_inactive state because it's used in the
         # template and build a boolean field to be passed for
         # searchPPAs.
-        show_inactive = (self.show_inactive == 'on')
+        show_inactive = self.show_inactive == "on"
 
         ppas = self.context.searchPPAs(
-            text=self.name_filter, show_inactive=show_inactive,
-            user=self.user)
+            text=self.name_filter, show_inactive=show_inactive, user=self.user
+        )
 
         self.batchnav = BatchNavigator(ppas, self.request)
         return self.batchnav.currentBatch()
@@ -923,14 +976,16 @@ class DistributionPPASearchView(LaunchpadView):
         """Return the last 5 sources publication in the context PPAs."""
         archive_set = getUtility(IArchiveSet)
         return archive_set.getLatestPPASourcePublicationsForDistribution(
-            distribution=self.context)
+            distribution=self.context
+        )
 
     @property
     def most_active_ppas(self):
         """Return the last 5 most active PPAs."""
         archive_set = getUtility(IArchiveSet)
         return archive_set.getMostActivePPAsForDistribution(
-            distribution=self.context)
+            distribution=self.context
+        )
 
 
 class DistributionSetActionNavigationMenu(RegistryCollectionActionMenuBase):
@@ -938,15 +993,18 @@ class DistributionSetActionNavigationMenu(RegistryCollectionActionMenuBase):
 
     usedfor = IDistributionSet
     links = [
-        'register_team', 'register_project', 'register_distribution',
-        'create_account']
+        "register_team",
+        "register_project",
+        "register_distribution",
+        "create_account",
+    ]
 
 
 @implementer(IRegistryCollectionNavigationMenu)
 class DistributionSetView(LaunchpadView):
     """View for /distros top level collection."""
 
-    page_title = 'Distributions registered in Launchpad'
+    page_title = "Distributions registered in Launchpad"
 
     @cachedproperty
     def count(self):
@@ -959,20 +1017,24 @@ class RequireVirtualizedBuildersMixin:
     def createRequireVirtualized(self):
         return form.Fields(
             Bool(
-                __name__='require_virtualized',
+                __name__="require_virtualized",
                 title="Require virtualized builders",
                 description=(
                     "Only build the distribution's packages on virtual "
-                    "builders."),
-                required=True))
+                    "builders."
+                ),
+                required=True,
+            )
+        )
 
     def updateRequireVirtualized(self, require_virtualized, archive):
         if archive.require_virtualized != require_virtualized:
             archive.require_virtualized = require_virtualized
 
 
-class DistributionAddView(LaunchpadFormView, RequireVirtualizedBuildersMixin,
-                          EnableProcessorsMixin):
+class DistributionAddView(
+    LaunchpadFormView, RequireVirtualizedBuildersMixin, EnableProcessorsMixin
+):
 
     schema = IDistribution
     label = "Register a new distribution"
@@ -987,7 +1049,7 @@ class DistributionAddView(LaunchpadFormView, RequireVirtualizedBuildersMixin,
         "blueprints_usage",
         "translations_usage",
         "answers_usage",
-        ]
+    ]
     custom_widget_require_virtualized = CheckBoxWidget
     custom_widget_processors = LabeledMultiCheckBoxWidget
 
@@ -999,9 +1061,9 @@ class DistributionAddView(LaunchpadFormView, RequireVirtualizedBuildersMixin,
     @property
     def initial_values(self):
         return {
-            'processors': getUtility(IProcessorSet).getAll(),
-            'require_virtualized': False,
-            }
+            "processors": getUtility(IProcessorSet).getAll(),
+            "require_virtualized": False,
+        }
 
     @property
     def cancel_url(self):
@@ -1015,62 +1077,69 @@ class DistributionAddView(LaunchpadFormView, RequireVirtualizedBuildersMixin,
         self.form_fields += self.createEnabledProcessors(
             getUtility(IProcessorSet).getAll(),
             "The architectures on which the distribution's main archive can "
-            "build.")
+            "build.",
+        )
 
-    @action("Save", name='save')
+    @action("Save", name="save")
     def save_action(self, action, data):
         distribution = getUtility(IDistributionSet).new(
-            name=data['name'],
-            display_name=data['display_name'],
-            title=data['display_name'],
-            summary=data['summary'],
-            description=data['description'],
-            domainname=data['domainname'],
-            members=data['members'],
+            name=data["name"],
+            display_name=data["display_name"],
+            title=data["display_name"],
+            summary=data["summary"],
+            description=data["description"],
+            domainname=data["domainname"],
+            members=data["members"],
             owner=self.user,
             registrant=self.user,
-            )
+        )
         archive = distribution.main_archive
-        self.updateRequireVirtualized(data['require_virtualized'], archive)
+        self.updateRequireVirtualized(data["require_virtualized"], archive)
         archive.setProcessors(
-            data['processors'], check_permissions=True, user=self.user)
+            data["processors"], check_permissions=True, user=self.user
+        )
 
         notify(ObjectCreatedEvent(distribution))
         self.next_url = canonical_url(distribution)
 
 
-class DistributionEditView(RegistryEditFormView,
-                           RequireVirtualizedBuildersMixin,
-                           EnableProcessorsMixin):
+class DistributionEditView(
+    RegistryEditFormView,
+    RequireVirtualizedBuildersMixin,
+    EnableProcessorsMixin,
+):
 
     schema = IDistribution
     field_names = [
-        'display_name',
-        'summary',
-        'description',
-        'bug_reporting_guidelines',
-        'bug_reported_acknowledgement',
-        'package_derivatives_email',
-        'icon',
-        'logo',
-        'mugshot',
-        'official_malone',
-        'enable_bug_expiration',
-        'blueprints_usage',
-        'translations_usage',
-        'answers_usage',
-        'translation_focus',
-        'default_traversal_policy',
-        'redirect_default_traversal',
-        'oci_registry_credentials',
-        ]
+        "display_name",
+        "summary",
+        "description",
+        "bug_reporting_guidelines",
+        "bug_reported_acknowledgement",
+        "package_derivatives_email",
+        "icon",
+        "logo",
+        "mugshot",
+        "official_malone",
+        "enable_bug_expiration",
+        "blueprints_usage",
+        "translations_usage",
+        "answers_usage",
+        "translation_focus",
+        "default_traversal_policy",
+        "redirect_default_traversal",
+        "oci_registry_credentials",
+    ]
 
     custom_widget_icon = CustomWidgetFactory(
-        ImageChangeWidget, ImageChangeWidget.EDIT_STYLE)
+        ImageChangeWidget, ImageChangeWidget.EDIT_STYLE
+    )
     custom_widget_logo = CustomWidgetFactory(
-        ImageChangeWidget, ImageChangeWidget.EDIT_STYLE)
+        ImageChangeWidget, ImageChangeWidget.EDIT_STYLE
+    )
     custom_widget_mugshot = CustomWidgetFactory(
-        ImageChangeWidget, ImageChangeWidget.EDIT_STYLE)
+        ImageChangeWidget, ImageChangeWidget.EDIT_STYLE
+    )
     custom_widget_require_virtualized = CheckBoxWidget
     custom_widget_processors = LabeledMultiCheckBoxWidget
     custom_widget_oci_registry_credentials = OCICredentialsWidget
@@ -1078,7 +1147,7 @@ class DistributionEditView(RegistryEditFormView,
     @property
     def label(self):
         """See `LaunchpadFormView`."""
-        return 'Change %s details' % self.context.displayname
+        return "Change %s details" % self.context.displayname
 
     def setUpFields(self):
         """See `LaunchpadFormView`."""
@@ -1087,52 +1156,56 @@ class DistributionEditView(RegistryEditFormView,
         self.form_fields += self.createEnabledProcessors(
             getUtility(IProcessorSet).getAll(),
             "The architectures on which the distribution's main archive can "
-            "build.")
+            "build.",
+        )
 
     @property
     def initial_values(self):
+        main_archive = self.context.main_archive
         return {
-            'require_virtualized':
-                self.context.main_archive.require_virtualized,
-            'processors': self.context.main_archive.processors
-            }
+            "require_virtualized": main_archive.require_virtualized,
+            "processors": main_archive.processors,
+        }
 
     def validate(self, data):
         """Constrain bug expiration to Launchpad Bugs tracker."""
         # enable_bug_expiration is disabled by JavaScript when official_malone
         # is set False. The contraint is enforced here in case the JavaScript
         # fails to load or activate.
-        official_malone = data.get('official_malone', False)
+        official_malone = data.get("official_malone", False)
         if not official_malone:
-            data['enable_bug_expiration'] = False
-        if 'processors' in data:
-            widget = self.widgets['processors']
+            data["enable_bug_expiration"] = False
+        if "processors" in data:
+            widget = self.widgets["processors"]
             for processor in self.context.main_archive.processors:
-                if processor not in data['processors']:
+                if processor not in data["processors"]:
                     if processor.name in widget.disabled_items:
                         # This processor is restricted and currently
                         # enabled.  Leave it untouched.
-                        data['processors'].append(processor)
+                        data["processors"].append(processor)
 
     def change_archive_fields(self, data):
         # Update context.main_archive.
-        new_require_virtualized = data.get('require_virtualized')
+        new_require_virtualized = data.get("require_virtualized")
         if new_require_virtualized is not None:
             self.updateRequireVirtualized(
-                new_require_virtualized, self.context.main_archive)
-            del(data['require_virtualized'])
-        new_processors = data.get('processors')
+                new_require_virtualized, self.context.main_archive
+            )
+            del data["require_virtualized"]
+        new_processors = data.get("processors")
         if new_processors is not None:
-            if (set(self.context.main_archive.processors) !=
-                    set(new_processors)):
+            if set(self.context.main_archive.processors) != set(
+                new_processors
+            ):
                 self.context.main_archive.setProcessors(
-                    new_processors, check_permissions=True, user=self.user)
-            del(data['processors'])
+                    new_processors, check_permissions=True, user=self.user
+                )
+            del data["processors"]
 
-    @action("Change", name='change')
+    @action("Change", name="change")
     def change_action(self, action, data):
         self.change_archive_fields(data)
-        new_credentials = data.pop('oci_registry_credentials', None)
+        new_credentials = data.pop("oci_registry_credentials", None)
         old_credentials = self.context.oci_registry_credentials
         if self.context.oci_registry_credentials != new_credentials:
             # Remove the old credentials as we're assigning new ones
@@ -1147,38 +1220,40 @@ class DistributionAdminView(LaunchpadEditFormView):
 
     schema = IDistribution
     field_names = [
-        'official_packages',
-        'supports_ppas',
-        'supports_mirrors',
-        'default_traversal_policy',
-        'redirect_default_traversal',
-        'information_type',
-        ]
+        "official_packages",
+        "supports_ppas",
+        "supports_mirrors",
+        "default_traversal_policy",
+        "redirect_default_traversal",
+        "information_type",
+    ]
 
     custom_widget_information_type = CustomWidgetFactory(
         LaunchpadRadioWidgetWithDescription,
-        vocabulary=InformationTypeVocabulary(types=PILLAR_INFORMATION_TYPES))
+        vocabulary=InformationTypeVocabulary(types=PILLAR_INFORMATION_TYPES),
+    )
 
     @property
     def label(self):
         """See `LaunchpadFormView`."""
-        return 'Administer %s' % self.context.displayname
+        return "Administer %s" % self.context.displayname
 
     def validate(self, data):
         super().validate(data)
-        information_type = data.get('information_type')
+        information_type = data.get("information_type")
         if information_type:
             errors = [
-                str(e) for e in self.context.checkInformationType(
-                    information_type)]
+                str(e)
+                for e in self.context.checkInformationType(information_type)
+            ]
             if len(errors) > 0:
-                self.setFieldError('information_type', ' '.join(errors))
+                self.setFieldError("information_type", " ".join(errors))
 
     @property
     def cancel_url(self):
         return canonical_url(self.context)
 
-    @action("Change", name='change')
+    @action("Change", name="change")
     def change_action(self, action, data):
         self.updateContextFromData(data)
         self.next_url = canonical_url(self.context)
@@ -1186,31 +1261,35 @@ class DistributionAdminView(LaunchpadEditFormView):
 
 class DistributionSeriesBaseView(LaunchpadView):
     """A base view to list distroseries."""
+
     @cachedproperty
     def styled_series(self):
         """A list of dicts; keys: series, css_class, is_development_focus"""
         all_series = []
         for series in self._displayed_series:
-            all_series.append({
-                'series': series,
-                'css_class': self.getCssClass(series),
-                })
+            all_series.append(
+                {
+                    "series": series,
+                    "css_class": self.getCssClass(series),
+                }
+            )
         return all_series
 
     def getCssClass(self, series):
         """The highlight, lowlight, or normal CSS class."""
         if series.status == SeriesStatus.DEVELOPMENT:
-            return 'highlight'
+            return "highlight"
         elif series.status == SeriesStatus.OBSOLETE:
-            return 'lowlight'
+            return "lowlight"
         else:
             # This is normal presentation.
-            return ''
+            return ""
 
 
 class DistributionSeriesView(DistributionSeriesBaseView):
     """A view to list the distribution series."""
-    label = 'Timeline'
+
+    label = "Timeline"
     show_add_series_link = True
     show_milestones_link = True
 
@@ -1221,7 +1300,8 @@ class DistributionSeriesView(DistributionSeriesBaseView):
 
 class DistributionDerivativesView(DistributionSeriesBaseView):
     """A view to list the distribution derivatives."""
-    label = 'Derivatives'
+
+    label = "Derivatives"
     show_add_series_link = False
     show_milestones_link = False
 
@@ -1232,8 +1312,9 @@ class DistributionDerivativesView(DistributionSeriesBaseView):
 
 class DistributionChangeMirrorAdminView(RegistryEditFormView):
     """A view to change the mirror administrator."""
+
     schema = IDistribution
-    field_names = ['mirror_admin']
+    field_names = ["mirror_admin"]
 
     @property
     def label(self):
@@ -1243,20 +1324,23 @@ class DistributionChangeMirrorAdminView(RegistryEditFormView):
 
 class DistributionChangeOCIProjectAdminView(RegistryEditFormView):
     """A view to change the OCI project administrator."""
+
     schema = IDistribution
-    field_names = ['oci_project_admin']
+    field_names = ["oci_project_admin"]
 
     @property
     def label(self):
         """See `LaunchpadFormView`."""
         return "Change the %s OCI project administrator" % (
-            self.context.displayname)
+            self.context.displayname
+        )
 
 
 class DistributionChangeSecurityAdminView(RegistryEditFormView):
     """A view to change the security administrator."""
+
     schema = IDistribution
-    field_names = ['security_admin']
+    field_names = ["security_admin"]
 
     @property
     def label(self):
@@ -1268,8 +1352,9 @@ class DistributionChangeSecurityAdminView(RegistryEditFormView):
 
 class DistributionChangeMembersView(RegistryEditFormView):
     """A view to change the members team."""
+
     schema = IDistribution
-    field_names = ['members']
+    field_names = ["members"]
 
     @property
     def label(self):
@@ -1289,27 +1374,30 @@ class DistributionCountryArchiveMirrorsView(LaunchpadView):
         request = self.request
         if not self.context.supports_mirrors:
             request.response.setStatus(404)
-            return ''
+            return ""
         ip_address = ipaddress_from_request(request)
         country = request_country(request)
         mirrors = self.context.getBestMirrorsForCountry(
-            country, MirrorContent.ARCHIVE)
+            country, MirrorContent.ARCHIVE
+        )
         body = "\n".join(mirror.base_url for mirror in mirrors)
-        request.response.setHeader('content-type', 'text/plain;charset=utf-8')
+        request.response.setHeader("content-type", "text/plain;charset=utf-8")
         if country is None:
-            country_name = 'Unknown'
+            country_name = "Unknown"
         else:
             country_name = country.name
-        request.response.setHeader('X-Generated-For-Country', country_name)
-        request.response.setHeader('X-Generated-For-IP', ip_address)
+        request.response.setHeader("X-Generated-For-Country", country_name)
+        request.response.setHeader("X-Generated-For-IP", ip_address)
         # XXX: Guilherme Salgado 2008-01-09 bug=173729: These are here only
         # for debugging.
         request.response.setHeader(
-            'X-REQUEST-HTTP_X_FORWARDED_FOR',
-            request.get('HTTP_X_FORWARDED_FOR'))
+            "X-REQUEST-HTTP_X_FORWARDED_FOR",
+            request.get("HTTP_X_FORWARDED_FOR"),
+        )
         request.response.setHeader(
-            'X-REQUEST-REMOTE_ADDR', request.get('REMOTE_ADDR'))
-        return body.encode('utf-8')
+            "X-REQUEST-REMOTE_ADDR", request.get("REMOTE_ADDR")
+        )
+        return body.encode("utf-8")
 
 
 @implementer(IDistributionMirrorMenuMarker)
@@ -1317,7 +1405,7 @@ class DistributionMirrorsView(LaunchpadView):
     show_freshness = True
     show_mirror_type = False
     description = None
-    page_title = 'Mirrors'
+    page_title = "Mirrors"
 
     @cachedproperty
     def mirror_count(self):
@@ -1363,13 +1451,13 @@ class DistributionMirrorsView(LaunchpadView):
             else:
                 # need to be made aware of new values in
                 # interfaces/distributionmirror.py MirrorSpeed
-                return 'Indeterminate'
+                return "Indeterminate"
         if throughput < 1000:
-            return str(throughput) + ' Kbps'
+            return str(throughput) + " Kbps"
         elif throughput < 1000000:
-            return str(throughput // 1000) + ' Mbps'
+            return str(throughput // 1000) + " Mbps"
         else:
-            return str(throughput // 1000000) + ' Gbps'
+            return str(throughput // 1000000) + " Gbps"
 
     @cachedproperty
     def total_throughput(self):
@@ -1385,18 +1473,24 @@ class DistributionMirrorsView(LaunchpadView):
         for mirror in self.mirrors:
             mirrors_by_country[mirror.country.name].append(mirror)
 
-        return [dict(country=country,
-                     mirrors=mirrors,
-                     number=len(mirrors),
-                     throughput=self._sum_throughput(mirrors))
-                for country, mirrors in sorted(mirrors_by_country.items())]
+        return [
+            dict(
+                country=country,
+                mirrors=mirrors,
+                number=len(mirrors),
+                throughput=self._sum_throughput(mirrors),
+            )
+            for country, mirrors in sorted(mirrors_by_country.items())
+        ]
 
 
 class DistributionArchiveMirrorsView(DistributionMirrorsView):
 
-    heading = 'Official Archive Mirrors'
-    description = ('These mirrors provide repositories and archives of all '
-                   'software for the distribution.')
+    heading = "Official Archive Mirrors"
+    description = (
+        "These mirrors provide repositories and archives of all "
+        "software for the distribution."
+    )
 
     @cachedproperty
     def mirrors(self):
@@ -1405,9 +1499,11 @@ class DistributionArchiveMirrorsView(DistributionMirrorsView):
 
 class DistributionSeriesMirrorsView(DistributionMirrorsView):
 
-    heading = 'Official CD Mirrors'
-    description = ('These mirrors offer ISO images which you can download '
-                   'and burn to CD to make installation disks.')
+    heading = "Official CD Mirrors"
+    description = (
+        "These mirrors offer ISO images which you can download "
+        "and burn to CD to make installation disks."
+    )
     show_freshness = False
 
     @cachedproperty
@@ -1423,15 +1519,16 @@ class DistributionMirrorsRSSBaseView(LaunchpadView):
 
     def render(self):
         self.request.response.setHeader(
-            'content-type', 'text/xml;charset=utf-8')
+            "content-type", "text/xml;charset=utf-8"
+        )
         body = LaunchpadView.render(self)
-        return body.encode('utf-8')
+        return body.encode("utf-8")
 
 
 class DistributionArchiveMirrorsRSSView(DistributionMirrorsRSSBaseView):
     """The RSS feed for archive mirrors."""
 
-    heading = 'Archive Mirrors'
+    heading = "Archive Mirrors"
 
     @cachedproperty
     def mirrors(self):
@@ -1441,7 +1538,7 @@ class DistributionArchiveMirrorsRSSView(DistributionMirrorsRSSBaseView):
 class DistributionSeriesMirrorsRSSView(DistributionMirrorsRSSBaseView):
     """The RSS feed for series mirrors."""
 
-    heading = 'CD Mirrors'
+    heading = "CD Mirrors"
 
     @cachedproperty
     def mirrors(self):
@@ -1449,7 +1546,6 @@ class DistributionSeriesMirrorsRSSView(DistributionMirrorsRSSBaseView):
 
 
 class DistributionMirrorsAdminView(DistributionMirrorsView):
-
     def initialize(self):
         """Raise an Unauthorized exception if the user is not a member of this
         distribution's mirror_admin team.
@@ -1460,12 +1556,12 @@ class DistributionMirrorsAdminView(DistributionMirrorsView):
         # that permission on a Distribution would be able to see them. That's
         # why we have to do the permission check here.
         if not (self.user and self.user.inTeam(self.context.mirror_admin)):
-            raise Unauthorized('Forbidden')
+            raise Unauthorized("Forbidden")
 
 
 class DistributionUnofficialMirrorsView(DistributionMirrorsAdminView):
 
-    heading = 'Unofficial Mirrors'
+    heading = "Unofficial Mirrors"
 
     @cachedproperty
     def mirrors(self):
@@ -1474,7 +1570,7 @@ class DistributionUnofficialMirrorsView(DistributionMirrorsAdminView):
 
 class DistributionPendingReviewMirrorsView(DistributionMirrorsAdminView):
 
-    heading = 'Pending-review mirrors'
+    heading = "Pending-review mirrors"
     show_mirror_type = True
     show_freshness = False
 
@@ -1485,7 +1581,7 @@ class DistributionPendingReviewMirrorsView(DistributionMirrorsAdminView):
 
 class DistributionDisabledMirrorsView(DistributionMirrorsAdminView):
 
-    heading = 'Disabled Mirrors'
+    heading = "Disabled Mirrors"
 
     @cachedproperty
     def mirrors(self):
@@ -1494,7 +1590,8 @@ class DistributionDisabledMirrorsView(DistributionMirrorsAdminView):
 
 class DistributionReassignmentView(ObjectReassignmentView):
     """View class for changing distribution maintainer."""
-    ownerOrMaintainerName = 'maintainer'
+
+    ownerOrMaintainerName = "maintainer"
 
 
 class DistributionPublisherConfigView(LaunchpadFormView):
@@ -1502,13 +1599,14 @@ class DistributionPublisherConfigView(LaunchpadFormView):
 
     It redirects to the main distroseries page after a successful edit.
     """
+
     schema = IPublisherConfig
-    field_names = ['root_dir', 'base_url', 'copy_base_url']
+    field_names = ["root_dir", "base_url", "copy_base_url"]
 
     @property
     def label(self):
         """See `LaunchpadFormView`."""
-        return 'Publisher configuration for %s' % self.context.title
+        return "Publisher configuration for %s" % self.context.title
 
     @property
     def page_title(self):
@@ -1523,8 +1621,9 @@ class DistributionPublisherConfigView(LaunchpadFormView):
     @property
     def initial_values(self):
         """If the config already exists, set up the fields with data."""
-        config = getUtility(
-            IPublisherConfigSet).getByDistribution(self.context)
+        config = getUtility(IPublisherConfigSet).getByDistribution(
+            self.context
+        )
         values = {}
         if config is not None:
             for name in self.field_names:
@@ -1536,16 +1635,19 @@ class DistributionPublisherConfigView(LaunchpadFormView):
     def save_action(self, action, data):
         """Update the context and redirect to its overview page."""
         config = getUtility(IPublisherConfigSet).getByDistribution(
-            self.context)
+            self.context
+        )
         if config is None:
             config = getUtility(IPublisherConfigSet).new(
                 distribution=self.context,
-                root_dir=data['root_dir'],
-                base_url=data['base_url'],
-                copy_base_url=data['copy_base_url'])
+                root_dir=data["root_dir"],
+                base_url=data["base_url"],
+                copy_base_url=data["copy_base_url"],
+            )
         else:
             form.applyChanges(config, self.form_fields, data, self.adapters)
 
         self.request.response.addInfoNotification(
-            'Your changes have been applied.')
+            "Your changes have been applied."
+        )
         self.next_url = canonical_url(self.context)
diff --git a/lib/lp/registry/browser/distributionmirror.py b/lib/lp/registry/browser/distributionmirror.py
index 6b77341..75aa827 100644
--- a/lib/lp/registry/browser/distributionmirror.py
+++ b/lib/lp/registry/browser/distributionmirror.py
@@ -2,16 +2,16 @@
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 __all__ = [
-    'DistributionMirrorEditView',
-    'DistributionMirrorOverviewMenu',
-    'DistributionMirrorAddView',
-    'DistributionMirrorView',
-    'DistributionMirrorReviewView',
-    'DistributionMirrorReassignmentView',
-    'DistributionMirrorDeleteView',
-    'DistributionMirrorProberLogView',
-    'DistributionMirrorBreadcrumb',
-    ]
+    "DistributionMirrorEditView",
+    "DistributionMirrorOverviewMenu",
+    "DistributionMirrorAddView",
+    "DistributionMirrorView",
+    "DistributionMirrorReviewView",
+    "DistributionMirrorReassignmentView",
+    "DistributionMirrorDeleteView",
+    "DistributionMirrorProberLogView",
+    "DistributionMirrorBreadcrumb",
+]
 
 from datetime import datetime
 
@@ -22,10 +22,10 @@ from zope.lifecycleevent import ObjectCreatedEvent
 
 from lp import _
 from lp.app.browser.launchpadform import (
-    action,
     LaunchpadEditFormView,
     LaunchpadFormView,
-    )
+    action,
+)
 from lp.archivepublisher.debversion import Version
 from lp.registry.browser.objectreassignment import ObjectReassignmentView
 from lp.registry.errors import InvalidMirrorReviewState
@@ -33,61 +33,61 @@ from lp.registry.interfaces.distribution import IDistributionMirrorMenuMarker
 from lp.registry.interfaces.distributionmirror import (
     IDistributionMirror,
     MirrorStatus,
-    )
+)
 from lp.services.propertycache import cachedproperty
 from lp.services.webapp import (
-    canonical_url,
-    enabled_with_permission,
     Link,
     NavigationMenu,
-    )
+    canonical_url,
+    enabled_with_permission,
+)
 from lp.services.webapp.batching import BatchNavigator
 from lp.services.webapp.breadcrumb import TitleBreadcrumb
 from lp.services.webapp.publisher import LaunchpadView
 from lp.soyuz.browser.sourceslist import (
     SourcesListEntries,
     SourcesListEntriesView,
-    )
+)
 
 
 class DistributionMirrorOverviewMenu(NavigationMenu):
 
     usedfor = IDistributionMirror
-    facet = 'overview'
-    links = ['proberlogs', 'edit', 'review', 'reassign', 'delete', 'resubmit']
+    facet = "overview"
+    links = ["proberlogs", "edit", "review", "reassign", "delete", "resubmit"]
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def edit(self):
-        text = 'Change details'
-        return Link('+edit', text, icon='edit')
+        text = "Change details"
+        return Link("+edit", text, icon="edit")
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def proberlogs(self):
-        text = 'Prober logs'
+        text = "Prober logs"
         enabled = self.context.last_probe_record is not None
-        return Link('+prober-logs', text, icon='info', enabled=enabled)
+        return Link("+prober-logs", text, icon="info", enabled=enabled)
 
-    @enabled_with_permission('launchpad.Admin')
+    @enabled_with_permission("launchpad.Admin")
     def delete(self):
         enabled = self.context.last_probe_record is None
-        text = 'Delete this mirror'
-        return Link('+delete', text, icon='remove', enabled=enabled)
+        text = "Delete this mirror"
+        return Link("+delete", text, icon="remove", enabled=enabled)
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def reassign(self):
-        text = 'Change owner'
-        return Link('+reassign', text, icon='edit')
+        text = "Change owner"
+        return Link("+reassign", text, icon="edit")
 
-    @enabled_with_permission('launchpad.Admin')
+    @enabled_with_permission("launchpad.Admin")
     def review(self):
-        text = 'Review mirror'
-        return Link('+review', text, icon='edit')
+        text = "Review mirror"
+        return Link("+review", text, icon="edit")
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def resubmit(self):
-        text = 'Resubmit for review'
+        text = "Resubmit for review"
         enabled = self.context.status == MirrorStatus.BROKEN
-        return Link('+resubmit', text, icon='edit', enabled=enabled)
+        return Link("+resubmit", text, icon="edit", enabled=enabled)
 
 
 class _FlavoursByDistroSeries:
@@ -105,12 +105,13 @@ class DistributionMirrorBreadcrumb(TitleBreadcrumb):
 
 
 class DistributionMirrorView(LaunchpadView):
-
     @property
     def page_title(self):
         """The HTML page title."""
-        values = dict(distribution=self.context.distribution.displayname,
-                      name=self.context.title)
+        values = dict(
+            distribution=self.context.distribution.displayname,
+            name=self.context.title,
+        )
         return '%(distribution)s mirror "%(name)s"' % values
 
     def initialize(self):
@@ -121,11 +122,12 @@ class DistributionMirrorView(LaunchpadView):
             series = arch_series.distro_arch_series.distroseries
             if series not in valid_series:
                 valid_series.append(series)
-        entries = SourcesListEntries(self.context.distribution,
-                                     self.context.base_url,
-                                     valid_series)
+        entries = SourcesListEntries(
+            self.context.distribution, self.context.base_url, valid_series
+        )
         self.sources_list_entries = SourcesListEntriesView(
-            entries, self.request, initially_without_selection=True)
+            entries, self.request, initially_without_selection=True
+        )
 
     @cachedproperty
     def probe_records(self):
@@ -136,15 +138,21 @@ class DistributionMirrorView(LaunchpadView):
     def summarized_arch_series(self):
         mirrors = self.context.getSummarizedMirroredArchSeries()
         return sorted(
-            mirrors, reverse=True,
+            mirrors,
+            reverse=True,
             key=lambda mirror: Version(
-                mirror.distro_arch_series.distroseries.version))
+                mirror.distro_arch_series.distroseries.version
+            ),
+        )
 
     @property
     def summarized_source_series(self):
         mirrors = self.context.getSummarizedMirroredSourceSeries()
-        return sorted(mirrors, reverse=True,
-                      key=lambda mirror: Version(mirror.distroseries.version))
+        return sorted(
+            mirrors,
+            reverse=True,
+            key=lambda mirror: Version(mirror.distroseries.version),
+        )
 
     def getCDImageMirroredFlavoursBySeries(self):
         """Return a list of _FlavoursByDistroSeries objects ordered
@@ -159,8 +167,11 @@ class DistributionMirrorView(LaunchpadView):
                 all_series[series] = flavours_by_series
             flavours_by_series.flavours.append(flavour)
         flavours_by_series = all_series.values()
-        return sorted(flavours_by_series, reverse=True,
-                      key=lambda item: Version(item.distroseries.version))
+        return sorted(
+            flavours_by_series,
+            reverse=True,
+            key=lambda item: Version(item.distroseries.version),
+        )
 
 
 class DistributionMirrorDeleteView(LaunchpadFormView):
@@ -171,7 +182,7 @@ class DistributionMirrorDeleteView(LaunchpadFormView):
     @property
     def label(self):
         """See `LaunchpadFormView`."""
-        return 'Delete mirror %s' % self.context.title
+        return "Delete mirror %s" % self.context.title
 
     @property
     def page_title(self):
@@ -185,14 +196,17 @@ class DistributionMirrorDeleteView(LaunchpadFormView):
         # and so we do this check here.
         if self.context.last_probe_record is not None:
             self.request.response.addInfoNotification(
-                "This mirror has been probed and thus can't be deleted.")
+                "This mirror has been probed and thus can't be deleted."
+            )
             self.next_url = canonical_url(self.context)
             return
 
-        self.next_url = canonical_url(self.context.distribution,
-            view_name='+pendingreviewmirrors')
+        self.next_url = canonical_url(
+            self.context.distribution, view_name="+pendingreviewmirrors"
+        )
         self.request.response.addInfoNotification(
-            "Mirror %s has been deleted." % self.context.title)
+            "Mirror %s has been deleted." % self.context.title
+        )
         self.context.destroySelf()
 
     @property
@@ -205,10 +219,18 @@ class DistributionMirrorDeleteView(LaunchpadFormView):
 class DistributionMirrorAddView(LaunchpadFormView):
     schema = IDistributionMirror
     field_names = [
-        "display_name", "description", "whiteboard", "https_base_url",
-        "http_base_url", "ftp_base_url", "rsync_base_url", "speed", "country",
-        "content", "official_candidate",
-        ]
+        "display_name",
+        "description",
+        "whiteboard",
+        "https_base_url",
+        "http_base_url",
+        "ftp_base_url",
+        "rsync_base_url",
+        "speed",
+        "country",
+        "content",
+        "official_candidate",
+    ]
     invariant_context = None
 
     @property
@@ -229,15 +251,19 @@ class DistributionMirrorAddView(LaunchpadFormView):
     @action(_("Register Mirror"), name="create")
     def create_action(self, action, data):
         mirror = self.context.newMirror(
-            owner=self.user, speed=data['speed'], country=data['country'],
-            content=data['content'], display_name=data['display_name'],
-            description=data['description'],
-            whiteboard=data['whiteboard'],
-            https_base_url=data['https_base_url'],
-            http_base_url=data['http_base_url'],
-            ftp_base_url=data['ftp_base_url'],
-            rsync_base_url=data['rsync_base_url'],
-            official_candidate=data['official_candidate'])
+            owner=self.user,
+            speed=data["speed"],
+            country=data["country"],
+            content=data["content"],
+            display_name=data["display_name"],
+            description=data["description"],
+            whiteboard=data["whiteboard"],
+            https_base_url=data["https_base_url"],
+            http_base_url=data["http_base_url"],
+            ftp_base_url=data["ftp_base_url"],
+            rsync_base_url=data["rsync_base_url"],
+            official_candidate=data["official_candidate"],
+        )
 
         self.next_url = canonical_url(mirror)
         notify(ObjectCreatedEvent(mirror))
@@ -246,12 +272,12 @@ class DistributionMirrorAddView(LaunchpadFormView):
 class DistributionMirrorReviewView(LaunchpadEditFormView):
 
     schema = IDistributionMirror
-    field_names = ['status', 'whiteboard']
+    field_names = ["status", "whiteboard"]
 
     @property
     def label(self):
         """See `LaunchpadFormView`."""
-        return 'Review mirror %s' % self.context.title
+        return "Review mirror %s" % self.context.title
 
     @property
     def page_title(self):
@@ -266,9 +292,9 @@ class DistributionMirrorReviewView(LaunchpadEditFormView):
     @action(_("Save"), name="save")
     def action_save(self, action, data):
         context = self.context
-        if data['status'] != context.status:
+        if data["status"] != context.status:
             context.reviewer = self.user
-            context.date_reviewed = datetime.now(pytz.timezone('UTC'))
+            context.date_reviewed = datetime.now(pytz.timezone("UTC"))
         self.updateContextFromData(data)
         self.next_url = canonical_url(context)
 
@@ -277,15 +303,24 @@ class DistributionMirrorEditView(LaunchpadEditFormView):
 
     schema = IDistributionMirror
     field_names = [
-        "name", "display_name", "description", "whiteboard",
-        "https_base_url", "http_base_url", "ftp_base_url", "rsync_base_url",
-        "speed", "country", "content", "official_candidate",
-        ]
+        "name",
+        "display_name",
+        "description",
+        "whiteboard",
+        "https_base_url",
+        "http_base_url",
+        "ftp_base_url",
+        "rsync_base_url",
+        "speed",
+        "country",
+        "content",
+        "official_candidate",
+    ]
 
     @property
     def label(self):
         """See `LaunchpadFormView`."""
-        return 'Edit mirror %s' % self.context.title
+        return "Edit mirror %s" % self.context.title
 
     @property
     def page_title(self):
@@ -311,7 +346,7 @@ class DistributionMirrorResubmitView(LaunchpadEditFormView):
     @property
     def label(self):
         """See `LaunchpadFormView`."""
-        return 'Resubmit mirror %s' % self.context.title
+        return "Resubmit mirror %s" % self.context.title
 
     page_title = label
 
@@ -322,12 +357,12 @@ class DistributionMirrorResubmitView(LaunchpadEditFormView):
         except InvalidMirrorReviewState:
             self.request.response.addInfoNotification(
                 "The mirror is not in the correct state"
-                " (broken) and cannot be resubmitted.")
+                " (broken) and cannot be resubmitted."
+            )
         self.next_url = canonical_url(self.context)
 
 
 class DistributionMirrorReassignmentView(ObjectReassignmentView):
-
     @property
     def contextName(self):
         return self.context.title
@@ -339,4 +374,4 @@ class DistributionMirrorProberLogView(DistributionMirrorView):
     @property
     def page_title(self):
         """The HTML page title."""
-        return '%s mirror prober logs' % self.context.title
+        return "%s mirror prober logs" % self.context.title
diff --git a/lib/lp/registry/browser/distributionsourcepackage.py b/lib/lp/registry/browser/distributionsourcepackage.py
index 1d09774..a55097b 100644
--- a/lib/lp/registry/browser/distributionsourcepackage.py
+++ b/lib/lp/registry/browser/distributionsourcepackage.py
@@ -2,41 +2,35 @@
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 __all__ = [
-    'DistributionSourcePackageAnswersMenu',
-    'DistributionSourcePackageBreadcrumb',
-    'DistributionSourcePackageChangelogView',
-    'DistributionSourcePackageEditView',
-    'DistributionSourcePackageFacets',
-    'DistributionSourcePackageHelpView',
-    'DistributionSourcePackageNavigation',
-    'DistributionSourcePackageOverviewMenu',
-    'DistributionSourcePackagePublishingHistoryView',
-    'DistributionSourcePackageURL',
-    'DistributionSourcePackageView',
-    'PublishingHistoryViewMixin',
-    ]
+    "DistributionSourcePackageAnswersMenu",
+    "DistributionSourcePackageBreadcrumb",
+    "DistributionSourcePackageChangelogView",
+    "DistributionSourcePackageEditView",
+    "DistributionSourcePackageFacets",
+    "DistributionSourcePackageHelpView",
+    "DistributionSourcePackageNavigation",
+    "DistributionSourcePackageOverviewMenu",
+    "DistributionSourcePackagePublishingHistoryView",
+    "DistributionSourcePackageURL",
+    "DistributionSourcePackageView",
+    "PublishingHistoryViewMixin",
+]
 
-from functools import cmp_to_key
 import itertools
 import operator
+from functools import cmp_to_key
 
 import apt_pkg
 from lazr.delegates import delegate_to
 from zope.component import getUtility
-from zope.interface import (
-    implementer,
-    Interface,
-    )
+from zope.interface import Interface, implementer
 
 from lp.answers.browser.questiontarget import (
     QuestionTargetAnswersMenu,
     QuestionTargetTraversalMixin,
-    )
+)
 from lp.answers.enums import QuestionStatus
-from lp.app.browser.launchpadform import (
-    action,
-    LaunchpadEditFormView,
-    )
+from lp.app.browser.launchpadform import LaunchpadEditFormView, action
 from lp.app.browser.stringformatter import extract_email_addresses
 from lp.app.browser.tales import CustomizableFormatter
 from lp.app.enums import ServiceUsage
@@ -44,10 +38,10 @@ from lp.app.interfaces.headings import IHeadingBreadcrumb
 from lp.app.interfaces.launchpad import IServiceUsage
 from lp.bugs.browser.bugtask import BugTargetTraversalMixin
 from lp.bugs.browser.structuralsubscription import (
-    expose_structural_subscription_data_to_js,
     StructuralSubscriptionMenuMixin,
     StructuralSubscriptionTargetTraversalMixin,
-    )
+    expose_structural_subscription_data_to_js,
+)
 from lp.bugs.interfaces.bugtask import BugTaskStatus
 from lp.bugs.interfaces.bugtasksearch import BugTaskSearchParams
 from lp.code.browser.vcslisting import TargetDefaultVCSNavigationMixin
@@ -56,41 +50,41 @@ from lp.registry.browser.pillar import PillarBugsMenu
 from lp.registry.enums import DistributionDefaultTraversalPolicy
 from lp.registry.interfaces.distributionsourcepackage import (
     IDistributionSourcePackage,
-    )
+)
 from lp.registry.interfaces.person import IPersonSet
 from lp.registry.interfaces.series import SeriesStatus
 from lp.services.database.decoratedresultset import DecoratedResultSet
 from lp.services.helpers import shortlist
 from lp.services.propertycache import cachedproperty
 from lp.services.webapp import (
-    canonical_url,
     Navigation,
-    redirection,
     StandardLaunchpadFacets,
-    )
+    canonical_url,
+    redirection,
+)
 from lp.services.webapp.batching import BatchNavigator
 from lp.services.webapp.breadcrumb import Breadcrumb
 from lp.services.webapp.interfaces import (
     ICanonicalUrlData,
     IMultiFacetedBreadcrumb,
-    )
+)
 from lp.services.webapp.menu import (
     ApplicationMenu,
-    enabled_with_permission,
     Link,
     NavigationMenu,
-    )
+    enabled_with_permission,
+)
 from lp.services.webapp.publisher import LaunchpadView
 from lp.services.webapp.sorting import sorted_dotted_numbers
 from lp.soyuz.browser.sourcepackagerelease import linkify_changelog
 from lp.soyuz.interfaces.archive import IArchiveSet
 from lp.soyuz.interfaces.distributionsourcepackagerelease import (
     IDistributionSourcePackageRelease,
-    )
+)
 from lp.soyuz.interfaces.packagediff import IPackageDiffSet
 from lp.translations.browser.customlanguagecode import (
     HasCustomLanguageCodesTraversalMixin,
-    )
+)
 
 
 @implementer(ICanonicalUrlData)
@@ -114,8 +108,10 @@ class DistributionSourcePackageURL:
     @property
     def path(self):
         policy = self.context.distribution.default_traversal_policy
-        if (policy == DistributionDefaultTraversalPolicy.SOURCE_PACKAGE and
-                not self.context.distribution.redirect_default_traversal):
+        if (
+            policy == DistributionDefaultTraversalPolicy.SOURCE_PACKAGE
+            and not self.context.distribution.redirect_default_traversal
+        ):
             return self.context.name
         else:
             return "+source/%s" % self.context.name
@@ -124,12 +120,12 @@ class DistributionSourcePackageURL:
 class DistributionSourcePackageFormatterAPI(CustomizableFormatter):
     """Adapt IDistributionSourcePackage objects to a formatted string."""
 
-    _link_permission = 'zope.Public'
-    _link_summary_template = '%(displayname)s'
+    _link_permission = "zope.Public"
+    _link_summary_template = "%(displayname)s"
 
     def _link_summary_values(self):
         displayname = self._context.displayname
-        return {'displayname': displayname}
+        return {"displayname": displayname}
 
 
 @implementer(IHeadingBreadcrumb, IMultiFacetedBreadcrumb)
@@ -138,32 +134,31 @@ class DistributionSourcePackageBreadcrumb(Breadcrumb):
 
     @property
     def text(self):
-        return '%s package' % self.context.sourcepackagename.name
+        return "%s package" % self.context.sourcepackagename.name
 
 
 class DistributionSourcePackageFacets(StandardLaunchpadFacets):
 
     usedfor = IDistributionSourcePackage
     enable_only = [
-        'overview',
-        'branches',
-        'bugs',
-        'translations',
-        'answers',
-        ]
+        "overview",
+        "branches",
+        "bugs",
+        "translations",
+        "answers",
+    ]
 
 
 class DistributionSourcePackageLinksMixin:
-
     def publishinghistory(self):
-        return Link('+publishinghistory', 'Show publishing history')
+        return Link("+publishinghistory", "Show publishing history")
 
-    @enabled_with_permission('launchpad.BugSupervisor')
+    @enabled_with_permission("launchpad.BugSupervisor")
     def edit(self):
         """Edit the details of this source package."""
         # This is titled "Edit bug reporting guidelines" because that
         # is the only editable property of a source package right now.
-        return Link('+edit', 'Configure bug tracker', icon='edit')
+        return Link("+edit", "Configure bug tracker", icon="edit")
 
     def new_bugs(self):
         base_path = "+bugs"
@@ -177,24 +172,26 @@ class DistributionSourcePackageLinksMixin:
 
 
 class DistributionSourcePackageOverviewMenu(
-    ApplicationMenu, DistributionSourcePackageLinksMixin):
+    ApplicationMenu, DistributionSourcePackageLinksMixin
+):
 
     usedfor = IDistributionSourcePackage
-    facet = 'overview'
-    links = ['new_bugs', 'open_questions']
+    facet = "overview"
+    links = ["new_bugs", "open_questions"]
 
 
 class DistributionSourcePackageBugsMenu(
     PillarBugsMenu,
     StructuralSubscriptionMenuMixin,
-    DistributionSourcePackageLinksMixin):
+    DistributionSourcePackageLinksMixin,
+):
 
     usedfor = IDistributionSourcePackage
-    facet = 'bugs'
+    facet = "bugs"
 
     @cachedproperty
     def links(self):
-        links = ['filebug']
+        links = ["filebug"]
         add_subscribe_link(links)
         return links
 
@@ -202,30 +199,34 @@ class DistributionSourcePackageBugsMenu(
 class DistributionSourcePackageAnswersMenu(QuestionTargetAnswersMenu):
 
     usedfor = IDistributionSourcePackage
-    facet = 'answers'
+    facet = "answers"
 
-    links = QuestionTargetAnswersMenu.links + ['gethelp']
+    links = QuestionTargetAnswersMenu.links + ["gethelp"]
 
     def gethelp(self):
-        return Link('+gethelp', 'Help and support options', icon='info')
+        return Link("+gethelp", "Help and support options", icon="info")
 
 
-class DistributionSourcePackageNavigation(Navigation,
-    BugTargetTraversalMixin, HasCustomLanguageCodesTraversalMixin,
-    QuestionTargetTraversalMixin, TargetDefaultVCSNavigationMixin,
-    StructuralSubscriptionTargetTraversalMixin):
+class DistributionSourcePackageNavigation(
+    Navigation,
+    BugTargetTraversalMixin,
+    HasCustomLanguageCodesTraversalMixin,
+    QuestionTargetTraversalMixin,
+    TargetDefaultVCSNavigationMixin,
+    StructuralSubscriptionTargetTraversalMixin,
+):
 
     usedfor = IDistributionSourcePackage
 
-    @redirection('+editbugcontact')
+    @redirection("+editbugcontact")
     def redirect_editbugcontact(self):
-        return '+subscribe'
+        return "+subscribe"
 
     def traverse(self, name):
         return self.context.getVersion(name)
 
 
-@delegate_to(IDistributionSourcePackageRelease, context='context')
+@delegate_to(IDistributionSourcePackageRelease, context="context")
 class DecoratedDistributionSourcePackageRelease:
     """A decorated DistributionSourcePackageRelease.
 
@@ -234,8 +235,13 @@ class DecoratedDistributionSourcePackageRelease:
     """
 
     def __init__(
-        self, distributionsourcepackagerelease, publishing_history,
-        package_diffs, person_data, user):
+        self,
+        distributionsourcepackagerelease,
+        publishing_history,
+        package_diffs,
+        person_data,
+        user,
+    ):
         self.context = distributionsourcepackagerelease
         self._publishing_history = publishing_history
         self._package_diffs = package_diffs
@@ -244,19 +250,20 @@ class DecoratedDistributionSourcePackageRelease:
 
     @property
     def publishing_history(self):
-        """ See `IDistributionSourcePackageRelease`."""
+        """See `IDistributionSourcePackageRelease`."""
         return self._publishing_history
 
     @property
     def package_diffs(self):
-        """ See `ISourcePackageRelease`."""
+        """See `ISourcePackageRelease`."""
         return self._package_diffs
 
     @property
     def change_summary(self):
-        """ See `ISourcePackageRelease`."""
+        """See `ISourcePackageRelease`."""
         return linkify_changelog(
-            self._user, self.context.change_summary, self._person_data)
+            self._user, self.context.change_summary, self._person_data
+        )
 
 
 class IDistributionSourcePackageActionMenu(Interface):
@@ -266,26 +273,28 @@ class IDistributionSourcePackageActionMenu(Interface):
 class DistributionSourcePackageActionMenu(
     NavigationMenu,
     StructuralSubscriptionMenuMixin,
-    DistributionSourcePackageLinksMixin):
+    DistributionSourcePackageLinksMixin,
+):
     """Action menu for distro source packages."""
+
     usedfor = IDistributionSourcePackageActionMenu
-    facet = 'overview'
-    title = 'Actions'
+    facet = "overview"
+    title = "Actions"
 
     @cachedproperty
     def links(self):
-        links = ['publishing_history', 'change_log']
+        links = ["publishing_history", "change_log"]
         add_subscribe_link(links)
-        links.append('edit')
+        links.append("edit")
         return links
 
     def publishing_history(self):
-        text = 'View full publishing history'
-        return Link('+publishinghistory', text, icon="info")
+        text = "View full publishing history"
+        return Link("+publishinghistory", text, icon="info")
 
     def change_log(self):
-        text = 'View full change log'
-        return Link('+changelog', text, icon="info")
+        text = "View full change log"
+        return Link("+changelog", text, icon="info")
 
 
 class DistributionSourcePackageBaseView(LaunchpadView):
@@ -297,52 +306,71 @@ class DistributionSourcePackageBaseView(LaunchpadView):
 
         def not_empty(text):
             return (
-                text is not None and isinstance(text, str)
-                and len(text.strip()) > 0)
+                text is not None
+                and isinstance(text, str)
+                and len(text.strip()) > 0
+            )
 
         def decorate(dspr_pubs):
             sprs = [dspr.sourcepackagerelease for (dspr, spphs) in dspr_pubs]
             # Preload email/person data only if user is logged on. In
             # the opposite case the emails in the changelog will be
             # obfuscated anyway and thus cause no database lookups.
-            the_changelog = '\n'.join(
-                [spr.changelog_entry for spr in sprs
-                if not_empty(spr.changelog_entry)])
+            the_changelog = "\n".join(
+                [
+                    spr.changelog_entry
+                    for spr in sprs
+                    if not_empty(spr.changelog_entry)
+                ]
+            )
             if self.user:
                 self._person_data = {
-                    email.email: person for (email, person) in
-                        getUtility(IPersonSet).getByEmails(
-                            extract_email_addresses(the_changelog),
-                            include_hidden=False)}
+                    email.email: person
+                    for (email, person) in getUtility(IPersonSet).getByEmails(
+                        extract_email_addresses(the_changelog),
+                        include_hidden=False,
+                    )
+                }
             else:
                 self._person_data = None
             # Collate diffs for relevant SourcePackageReleases
             pkg_diffs = getUtility(IPackageDiffSet).getDiffsToReleases(
-                sprs, preload_for_display=True)
+                sprs, preload_for_display=True
+            )
             spr_diffs = {}
             for spr, diffs in itertools.groupby(
-                    pkg_diffs, operator.attrgetter('to_source')):
+                pkg_diffs, operator.attrgetter("to_source")
+            ):
                 spr_diffs[spr] = list(diffs)
 
             return [
                 DecoratedDistributionSourcePackageRelease(
-                    dspr, spphs, spr_diffs.get(dspr.sourcepackagerelease, []),
-                    self._person_data, self.user)
-                for (dspr, spphs) in dspr_pubs]
+                    dspr,
+                    spphs,
+                    spr_diffs.get(dspr.sourcepackagerelease, []),
+                    self._person_data,
+                    self.user,
+                )
+                for (dspr, spphs) in dspr_pubs
+            ]
+
         return DecoratedResultSet(
             self.context.getReleasesAndPublishingHistory(),
-            bulk_decorator=decorate)
+            bulk_decorator=decorate,
+        )
 
 
 @implementer(IDistributionSourcePackageActionMenu)
-class DistributionSourcePackageView(DistributionSourcePackageBaseView,
-                                    LaunchpadView):
+class DistributionSourcePackageView(
+    DistributionSourcePackageBaseView, LaunchpadView
+):
     """View class for DistributionSourcePackage."""
 
     def initialize(self):
         super().initialize()
         expose_structural_subscription_data_to_js(
-            self.context, self.request, self.user)
+            self.context, self.request, self.user
+        )
 
     @property
     def label(self):
@@ -390,8 +418,10 @@ class DistributionSourcePackageView(DistributionSourcePackageBaseView,
         # three archives.
         archive_set = getUtility(IArchiveSet)
         publications = archive_set.getPublicationsInArchives(
-            self.context.sourcepackagename, top_three_archives,
-            distribution=self.context.distribution)
+            self.context.sourcepackagename,
+            top_three_archives,
+            distribution=self.context.distribution,
+        )
 
         # Collect the publishings for each archive
         archive_publishings = {}
@@ -408,26 +438,28 @@ class DistributionSourcePackageView(DistributionSourcePackageBaseView,
             # 'Jaunty (1.0.1b)' to the versions list.
             for pub in archive_publishings[archive]:
                 versions.append(
-                    "%s (%s)" % (
+                    "%s (%s)"
+                    % (
                         pub.distroseries.displayname,
                         pub.source_package_version,
-                        )
                     )
-            archive_versions.append({
-                'archive': archive,
-                'versions': ", ".join(versions),
-                })
+                )
+            archive_versions.append(
+                {
+                    "archive": archive,
+                    "versions": ", ".join(versions),
+                }
+            )
 
         return archive_versions
 
     @property
     def further_ppa_versions_url(self):
-        """Return the url used to find further PPA versions of this package.
-        """
+        """Return the url used to find further PPA versions of this package."""
         return "%s/+ppas?name_filter=%s" % (
             canonical_url(self.context.distribution),
             self.context.name,
-            )
+        )
 
     @cachedproperty
     def active_distroseries_packages(self):
@@ -453,7 +485,8 @@ class DistributionSourcePackageView(DistributionSourcePackageBaseView,
         for package in self.active_distroseries_packages:
             series.add(package.distroseries)
         result = sorted_dotted_numbers(
-            series, key=operator.attrgetter('version'))
+            series, key=operator.attrgetter("version")
+        )
         result.reverse()
         return result
 
@@ -465,7 +498,8 @@ class DistributionSourcePackageView(DistributionSourcePackageBaseView,
         publications = getUtility(IArchiveSet).getPublicationsInArchives(
             sourcepackage.sourcepackagename,
             sourcepackage.distribution.all_distro_archives,
-            distroseries=sourcepackage.distroseries)
+            distroseries=sourcepackage.distroseries,
+        )
         pocket_dict = {}
         for pub in shortlist(publications):
             version = pub.source_package_version
@@ -477,7 +511,8 @@ class DistributionSourcePackageView(DistributionSourcePackageBaseView,
         if len(self.active_series) == 0:
             return None
         return self.active_series[0].getSourcePackage(
-            self.context.sourcepackagename)
+            self.context.sourcepackagename
+        )
 
     @property
     def version_table(self):
@@ -493,51 +528,57 @@ class DistributionSourcePackageView(DistributionSourcePackageBaseView,
             # fill it in.
             show_set_upstream_link = (
                 packaging is None
-                and distroseries.status in (
+                and distroseries.status
+                in (
                     SeriesStatus.CURRENT,
                     SeriesStatus.DEVELOPMENT,
-                    )
                 )
+            )
             title_row = {
-                'blank_row': False,
-                'title_row': True,
-                'data_row': False,
-                'distroseries': distroseries,
-                'series_package': package,
-                'packaging': packaging,
-                'show_set_upstream_link': show_set_upstream_link,
-                }
+                "blank_row": False,
+                "title_row": True,
+                "data_row": False,
+                "distroseries": distroseries,
+                "series_package": package,
+                "packaging": packaging,
+                "show_set_upstream_link": show_set_upstream_link,
+            }
             rows.append(title_row)
 
             # After the title row, we list each package version that's
             # currently published, and which pockets it's published in.
             pocket_dict = self.published_by_version(package)
             for version in sorted(
-                    pocket_dict,
-                    key=cmp_to_key(apt_pkg.version_compare), reverse=True):
+                pocket_dict,
+                key=cmp_to_key(apt_pkg.version_compare),
+                reverse=True,
+            ):
                 most_recent_publication = pocket_dict[version][0]
                 date_published = most_recent_publication.datepublished
                 pockets = ", ".join(
-                    [pub.pocket.name for pub in pocket_dict[version]])
+                    [pub.pocket.name for pub in pocket_dict[version]]
+                )
                 row = {
-                    'blank_row': False,
-                    'title_row': False,
-                    'data_row': True,
-                    'version': version,
-                    'publication': most_recent_publication,
-                    'pockets': pockets,
-                    'component': most_recent_publication.component_name,
-                    'date_published': date_published,
-                    }
+                    "blank_row": False,
+                    "title_row": False,
+                    "data_row": True,
+                    "version": version,
+                    "publication": most_recent_publication,
+                    "pockets": pockets,
+                    "component": most_recent_publication.component_name,
+                    "date_published": date_published,
+                }
                 rows.append(row)
             # We need a blank row after each section, so the series
             # header row doesn't appear too close to the previous
             # section.
-            rows.append({
-                'blank_row': True,
-                'title_row': False,
-                'data_row': False,
-                })
+            rows.append(
+                {
+                    "blank_row": True,
+                    "title_row": False,
+                    "data_row": False,
+                }
+            )
 
         return rows
 
@@ -548,34 +589,37 @@ class DistributionSourcePackageView(DistributionSourcePackageBaseView,
 
     @cachedproperty
     def bugs_answers_usage(self):
-        """Return a  dict of uses_bugs, uses_answers, uses_both, uses_either.
-        """
+        """Return a dict of uses_bugs, uses_answers, uses_both, uses_either."""
         service_usage = IServiceUsage(self.context)
-        uses_bugs = (
-            service_usage.bug_tracking_usage == ServiceUsage.LAUNCHPAD)
+        uses_bugs = service_usage.bug_tracking_usage == ServiceUsage.LAUNCHPAD
         uses_answers = service_usage.answers_usage == ServiceUsage.LAUNCHPAD
         uses_both = uses_bugs and uses_answers
         uses_either = uses_bugs or uses_answers
         return dict(
-            uses_bugs=uses_bugs, uses_answers=uses_answers,
-            uses_both=uses_both, uses_either=uses_either)
+            uses_bugs=uses_bugs,
+            uses_answers=uses_answers,
+            uses_both=uses_both,
+            uses_either=uses_either,
+        )
 
     @cachedproperty
     def new_bugtasks_count(self):
         search_params = BugTaskSearchParams(
-            self.user, status=BugTaskStatus.NEW, omit_dupes=True)
+            self.user, status=BugTaskStatus.NEW, omit_dupes=True
+        )
         return self.context.searchTasks(search_params).count()
 
 
 class DistributionSourcePackageChangelogView(
-    DistributionSourcePackageBaseView, LaunchpadView):
+    DistributionSourcePackageBaseView, LaunchpadView
+):
     """View for presenting change logs for a `DistributionSourcePackage`."""
 
-    page_title = 'Change log'
+    page_title = "Change log"
 
     @property
     def label(self):
-        return 'Change log for %s' % self.context.title
+        return "Change log for %s" % self.context.title
 
     @cachedproperty
     def batchnav(self):
@@ -591,8 +635,11 @@ class PublishingHistoryViewMixin:
             ids.update((spph.removed_byID, spph.creatorID, spph.sponsorID))
         ids.discard(None)
         if ids:
-            list(getUtility(IPersonSet).getPrecachedPersonsFromIDs(
-                ids, need_validity=True))
+            list(
+                getUtility(IPersonSet).getPrecachedPersonsFromIDs(
+                    ids, need_validity=True
+                )
+            )
 
     @property
     def batchnav(self):
@@ -601,19 +648,22 @@ class PublishingHistoryViewMixin:
         return BatchNavigator(
             DecoratedResultSet(
                 self.context.publishing_history,
-                pre_iter_hook=self._preload_people),
-            self.request)
+                pre_iter_hook=self._preload_people,
+            ),
+            self.request,
+        )
 
 
 class DistributionSourcePackagePublishingHistoryView(
-        LaunchpadView, PublishingHistoryViewMixin):
+    LaunchpadView, PublishingHistoryViewMixin
+):
     """View for presenting `DistributionSourcePackage` publishing history."""
 
-    page_title = 'Publishing history'
+    page_title = "Publishing history"
 
     @property
     def label(self):
-        return 'Publishing history of %s' % self.context.title
+        return "Publishing history of %s" % self.context.title
 
 
 class DistributionSourcePackageEditView(LaunchpadEditFormView):
@@ -621,22 +671,22 @@ class DistributionSourcePackageEditView(LaunchpadEditFormView):
 
     schema = IDistributionSourcePackage
     field_names = [
-        'bug_reporting_guidelines',
-        'bug_reported_acknowledgement',
-        'enable_bugfiling_duplicate_search',
-        ]
+        "bug_reporting_guidelines",
+        "bug_reported_acknowledgement",
+        "enable_bugfiling_duplicate_search",
+    ]
 
     @property
     def label(self):
         """The form label."""
-        return 'Edit %s' % self.context.title
+        return "Edit %s" % self.context.title
 
     @property
     def page_title(self):
         """The page title."""
         return self.label
 
-    @action("Change", name='change')
+    @action("Change", name="change")
     def change_action(self, action, data):
         self.updateContextFromData(data)
 
@@ -650,4 +700,4 @@ class DistributionSourcePackageEditView(LaunchpadEditFormView):
 class DistributionSourcePackageHelpView(LaunchpadView):
     """A View to show Answers help."""
 
-    page_title = 'Help and support options'
+    page_title = "Help and support options"
diff --git a/lib/lp/registry/browser/distroseries.py b/lib/lp/registry/browser/distroseries.py
index ee34d1f..f1a25fe 100644
--- a/lib/lp/registry/browser/distroseries.py
+++ b/lib/lp/registry/browser/distroseries.py
@@ -4,20 +4,20 @@
 """View classes related to `IDistroSeries`."""
 
 __all__ = [
-    'DistroSeriesAddView',
-    'DistroSeriesAdminView',
-    'DistroSeriesBreadcrumb',
-    'DistroSeriesEditView',
-    'DistroSeriesInitializeView',
-    'DistroSeriesLocalDifferencesView',
-    'DistroSeriesMissingPackagesView',
-    'DistroSeriesNavigation',
-    'DistroSeriesPackageSearchView',
-    'DistroSeriesPackagesView',
-    'DistroSeriesUniquePackagesView',
-    'DistroSeriesURL',
-    'DistroSeriesView',
-    ]
+    "DistroSeriesAddView",
+    "DistroSeriesAdminView",
+    "DistroSeriesBreadcrumb",
+    "DistroSeriesEditView",
+    "DistroSeriesInitializeView",
+    "DistroSeriesLocalDifferencesView",
+    "DistroSeriesMissingPackagesView",
+    "DistroSeriesNavigation",
+    "DistroSeriesPackageSearchView",
+    "DistroSeriesPackagesView",
+    "DistroSeriesUniquePackagesView",
+    "DistroSeriesURL",
+    "DistroSeriesView",
+]
 
 import apt_pkg
 from lazr.restful.interface import copy_field
@@ -26,57 +26,44 @@ from zope.component import getUtility
 from zope.event import notify
 from zope.formlib import form
 from zope.formlib.widget import CustomWidgetFactory
-from zope.interface import (
-    implementer,
-    Interface,
-    )
+from zope.interface import Interface, implementer
 from zope.lifecycleevent import ObjectCreatedEvent
-from zope.schema import (
-    Choice,
-    List,
-    TextLine,
-    )
-from zope.schema.vocabulary import (
-    SimpleTerm,
-    SimpleVocabulary,
-    )
+from zope.schema import Choice, List, TextLine
+from zope.schema.vocabulary import SimpleTerm, SimpleVocabulary
 
 from lp import _
 from lp.app.browser.launchpadform import (
-    action,
     LaunchpadEditFormView,
     LaunchpadFormView,
-    )
+    action,
+)
 from lp.app.errors import NotFoundError
 from lp.app.widgets.itemswidgets import (
     LabeledMultiCheckBoxWidget,
     LaunchpadDropdownWidget,
     LaunchpadRadioWidget,
-    )
+)
 from lp.app.widgets.popup import PersonPickerWidget
 from lp.archivepublisher.interfaces.publisherconfig import IPublisherConfigSet
 from lp.blueprints.browser.specificationtarget import (
     HasSpecificationsMenuMixin,
-    )
+)
 from lp.bugs.browser.bugtask import BugTargetTraversalMixin
 from lp.bugs.browser.structuralsubscription import (
-    expose_structural_subscription_data_to_js,
     StructuralSubscriptionMenuMixin,
     StructuralSubscriptionTargetTraversalMixin,
-    )
-from lp.registry.browser import (
-    add_subscribe_link,
-    MilestoneOverlayMixin,
-    )
+    expose_structural_subscription_data_to_js,
+)
+from lp.registry.browser import MilestoneOverlayMixin, add_subscribe_link
 from lp.registry.enums import (
     DistributionDefaultTraversalPolicy,
     DistroSeriesDifferenceStatus,
     DistroSeriesDifferenceType,
-    )
+)
 from lp.registry.interfaces.distroseries import IDistroSeries
 from lp.registry.interfaces.distroseriesdifference import (
     IDistroSeriesDifferenceSource,
-    )
+)
 from lp.registry.interfaces.person import IPersonSet
 from lp.registry.interfaces.pocket import PackagePublishingPocket
 from lp.registry.interfaces.series import SeriesStatus
@@ -92,16 +79,16 @@ from lp.services.webapp.escaping import structured
 from lp.services.webapp.interfaces import ICanonicalUrlData
 from lp.services.webapp.menu import (
     ApplicationMenu,
-    enabled_with_permission,
     Link,
     NavigationMenu,
-    )
+    enabled_with_permission,
+)
 from lp.services.webapp.publisher import (
-    canonical_url,
     LaunchpadView,
+    canonical_url,
     stepthrough,
     stepto,
-    )
+)
 from lp.services.webapp.url import urlappend
 from lp.services.worlddata.helpers import browser_languages
 from lp.services.worlddata.interfaces.country import ICountry
@@ -111,22 +98,21 @@ from lp.soyuz.browser.packagesearch import PackageSearchViewBase
 from lp.soyuz.enums import PackageCopyPolicy
 from lp.soyuz.interfaces.distributionjob import (
     IDistroSeriesDifferenceJobSource,
-    )
+)
 from lp.soyuz.interfaces.packagecopyjob import IPlainPackageCopyJobSource
 from lp.soyuz.interfaces.packageset import IPackagesetSet
 from lp.soyuz.interfaces.queue import IPackageUploadSet
 from lp.soyuz.model.queue import PackageUploadQueue
 from lp.translations.browser.distroseries import (
     check_distroseries_translations_viewable,
-    )
-
+)
 
 # DistroSeries statuses that benefit from mass package upgrade support.
 UPGRADABLE_SERIES_STATUSES = [
     SeriesStatus.FUTURE,
     SeriesStatus.EXPERIMENTAL,
     SeriesStatus.DEVELOPMENT,
-    ]
+]
 
 
 def get_dsd_source():
@@ -155,24 +141,29 @@ class DistroSeriesURL:
     @property
     def path(self):
         policy = self.context.distribution.default_traversal_policy
-        if (policy == DistributionDefaultTraversalPolicy.SERIES and
-                not self.context.distribution.redirect_default_traversal):
+        if (
+            policy == DistributionDefaultTraversalPolicy.SERIES
+            and not self.context.distribution.redirect_default_traversal
+        ):
             return self.context.name
         else:
             return "+series/%s" % self.context.name
 
 
-class DistroSeriesNavigation(GetitemNavigation, BugTargetTraversalMixin,
-    StructuralSubscriptionTargetTraversalMixin):
+class DistroSeriesNavigation(
+    GetitemNavigation,
+    BugTargetTraversalMixin,
+    StructuralSubscriptionTargetTraversalMixin,
+):
 
     usedfor = IDistroSeries
 
-    @stepthrough('+lang')
+    @stepthrough("+lang")
     def traverse_lang(self, langcode):
         """Retrieve the DistroSeriesLanguage or a dummy if one it is None."""
         # We do not want users to see the 'en' pofile because
         # we store the messages we want to translate as English.
-        if langcode == 'en':
+        if langcode == "en":
             raise NotFoundError(langcode)
 
         langset = getUtility(ILanguageSet)
@@ -191,36 +182,36 @@ class DistroSeriesNavigation(GetitemNavigation, BugTargetTraversalMixin,
 
         return distroserieslang
 
-    @stepthrough('+source')
+    @stepthrough("+source")
     def source(self, name):
         return self.context.getSourcePackage(name)
 
     # sabdfl 17/10/05 please keep this old location here for
     # LaunchpadIntegration on Breezy, unless you can figure out how to
     # redirect to the newer +source, defined above
-    @stepthrough('+sources')
+    @stepthrough("+sources")
     def sources(self, name):
         return self.context.getSourcePackage(name)
 
-    @stepthrough('+package')
+    @stepthrough("+package")
     def package(self, name):
         return self.context.getBinaryPackage(name)
 
-    @stepto('+latest-full-language-pack')
+    @stepto("+latest-full-language-pack")
     def latest_full_language_pack(self):
         if self.context.last_full_language_pack_exported is None:
             return None
         else:
             return self.context.last_full_language_pack_exported.file
 
-    @stepto('+latest-delta-language-pack')
+    @stepto("+latest-delta-language-pack")
     def redirect_latest_delta_language_pack(self):
         if self.context.last_delta_language_pack_exported is None:
             return None
         else:
             return self.context.last_delta_language_pack_exported.file
 
-    @stepthrough('+upload')
+    @stepthrough("+upload")
     def traverse_queue(self, id):
         try:
             queue_id = int(id)
@@ -241,117 +232,127 @@ class DistroSeriesBreadcrumb(Breadcrumb):
 
 
 class DistroSeriesOverviewMenu(
-    ApplicationMenu, StructuralSubscriptionMenuMixin):
+    ApplicationMenu, StructuralSubscriptionMenuMixin
+):
 
     usedfor = IDistroSeries
-    facet = 'overview'
+    facet = "overview"
 
     @property
     def links(self):
-        links = ['edit',
-                 'driver',
-                 'answers',
-                 'packaging',
-                 'needs_packaging',
-                 'builds',
-                 'queue',
-                 'add_port',
-                 'create_milestone',
-                 'initseries',
-                 ]
+        links = [
+            "edit",
+            "driver",
+            "answers",
+            "packaging",
+            "needs_packaging",
+            "builds",
+            "queue",
+            "add_port",
+            "create_milestone",
+            "initseries",
+        ]
         add_subscribe_link(links)
-        links.append('admin')
+        links.append("admin")
         return links
 
-    @enabled_with_permission('launchpad.Admin')
+    @enabled_with_permission("launchpad.Admin")
     def edit(self):
-        text = 'Change details'
-        return Link('+edit', text, icon='edit')
+        text = "Change details"
+        return Link("+edit", text, icon="edit")
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def driver(self):
-        text = 'Appoint driver'
-        summary = 'Someone with permission to set goals for this series'
-        return Link('+driver', text, summary, icon='edit')
+        text = "Appoint driver"
+        summary = "Someone with permission to set goals for this series"
+        return Link("+driver", text, summary, icon="edit")
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def create_milestone(self):
-        text = 'Create milestone'
-        summary = 'Register a new milestone for this series'
-        return Link('+addmilestone', text, summary, icon='add')
+        text = "Create milestone"
+        summary = "Register a new milestone for this series"
+        return Link("+addmilestone", text, summary, icon="add")
 
     def packaging(self):
-        text = 'All upstream links'
-        summary = 'A listing of source packages and their upstream projects'
-        return Link('+packaging', text, summary=summary, icon='info')
+        text = "All upstream links"
+        summary = "A listing of source packages and their upstream projects"
+        return Link("+packaging", text, summary=summary, icon="info")
 
     def needs_packaging(self):
-        text = 'Needs upstream links'
-        summary = 'A listing of source packages without upstream projects'
-        return Link('+needs-packaging', text, summary=summary, icon='info')
+        text = "Needs upstream links"
+        summary = "A listing of source packages without upstream projects"
+        return Link("+needs-packaging", text, summary=summary, icon="info")
 
     # A search link isn't needed because the distro series overview
     # has a search form.
     def answers(self):
-        text = 'Ask a question'
-        url = canonical_url(self.context.distribution) + '/+addquestion'
-        return Link(url, text, icon='add')
+        text = "Ask a question"
+        url = canonical_url(self.context.distribution) + "/+addquestion"
+        return Link(url, text, icon="add")
 
-    @enabled_with_permission('launchpad.Admin')
+    @enabled_with_permission("launchpad.Admin")
     def add_port(self):
-        text = 'Add architecture'
-        return Link('+addport', text, icon='add')
+        text = "Add architecture"
+        return Link("+addport", text, icon="add")
 
-    @enabled_with_permission('launchpad.Moderate')
+    @enabled_with_permission("launchpad.Moderate")
     def admin(self):
-        text = 'Administer'
-        return Link('+admin', text, icon='edit')
+        text = "Administer"
+        return Link("+admin", text, icon="edit")
 
     def builds(self):
-        text = 'Show builds'
-        return Link('+builds', text, icon='info')
+        text = "Show builds"
+        return Link("+builds", text, icon="info")
 
     def queue(self):
-        text = 'Show uploads'
-        return Link('+queue', text, icon='info')
+        text = "Show uploads"
+        return Link("+queue", text, icon="info")
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def initseries(self):
         enabled = (
-             not self.context.isInitializing() and
-             not self.context.isInitialized())
-        text = 'Initialize series'
-        return Link('+initseries', text, icon='edit', enabled=enabled)
+            not self.context.isInitializing()
+            and not self.context.isInitialized()
+        )
+        text = "Initialize series"
+        return Link("+initseries", text, icon="edit", enabled=enabled)
 
 
 class DistroSeriesBugsMenu(ApplicationMenu, StructuralSubscriptionMenuMixin):
 
     usedfor = IDistroSeries
-    facet = 'bugs'
+    facet = "bugs"
 
     @property
     def links(self):
-        links = ['cve',
-                 'nominations',
-                 ]
+        links = [
+            "cve",
+            "nominations",
+        ]
         add_subscribe_link(links)
         return links
 
     def cve(self):
-        return Link('+cve', 'CVE reports', icon='cve')
+        return Link("+cve", "CVE reports", icon="cve")
 
     def nominations(self):
-        return Link('+nominations', 'Review nominations', icon='bug')
+        return Link("+nominations", "Review nominations", icon="bug")
 
 
-class DistroSeriesSpecificationsMenu(NavigationMenu,
-                                     HasSpecificationsMenuMixin):
+class DistroSeriesSpecificationsMenu(
+    NavigationMenu, HasSpecificationsMenuMixin
+):
 
     usedfor = IDistroSeries
-    facet = 'specifications'
+    facet = "specifications"
     links = [
-        'listall', 'listdeclined', 'assignments', 'setgoals',
-        'new', 'register_sprint']
+        "listall",
+        "listdeclined",
+        "assignments",
+        "setgoals",
+        "new",
+        "register_sprint",
+    ]
 
 
 class DistroSeriesPackageSearchView(PackageSearchViewBase):
@@ -361,7 +362,7 @@ class DistroSeriesPackageSearchView(PackageSearchViewBase):
         """See `AbstractPackageSearchView`."""
         return self.context.searchPackages(self.text)
 
-    label = 'Search packages'
+    label = "Search packages"
 
 
 class SeriesStatusMixin:
@@ -379,35 +380,43 @@ class SeriesStatusMixin:
             SeriesStatus.CURRENT,
             SeriesStatus.SUPPORTED,
             SeriesStatus.OBSOLETE,
-            )
+        )
 
         if self.context.status not in stable_status:
-            terms = [status for status in SeriesStatus.items
-                     if status not in stable_status]
+            terms = [
+                status
+                for status in SeriesStatus.items
+                if status not in stable_status
+            ]
             terms.append(SeriesStatus.CURRENT)
         else:
             terms = stable_status
 
         status_vocabulary = SimpleVocabulary(
-            [SimpleTerm(item, item.name, item.title) for item in terms])
+            [SimpleTerm(item, item.name, item.title) for item in terms]
+        )
 
         return form.Fields(
-            Choice(__name__='status',
-                   title=_('Status'),
-                   default=self.context.status,
-                   vocabulary=status_vocabulary,
-                   description=_("Select the distroseries status."),
-                   required=True))
+            Choice(
+                __name__="status",
+                title=_("Status"),
+                default=self.context.status,
+                vocabulary=status_vocabulary,
+                description=_("Select the distroseries status."),
+                required=True,
+            )
+        )
 
     def updateDateReleased(self, status):
         """Update the datereleased field if the status is set to CURRENT."""
-        if (self.context.datereleased is None and
-            status == SeriesStatus.CURRENT):
+        if (
+            self.context.datereleased is None
+            and status == SeriesStatus.CURRENT
+        ):
             self.context.datereleased = UTC_NOW
 
 
 class DerivedDistroSeriesMixin:
-
     @cachedproperty
     def has_unique_parent(self):
         return len(self.context.getParentSeries()) == 1
@@ -425,13 +434,12 @@ class DerivedDistroSeriesMixin:
 
     def getParentName(self, multiple_parent_default=None):
         if self.has_unique_parent:
-            return ("parent series '%s'" %
-                self.unique_parent.displayname)
+            return "parent series '%s'" % self.unique_parent.displayname
         else:
             if multiple_parent_default is not None:
                 return multiple_parent_default
             else:
-                return 'a parent series'
+                return "a parent series"
 
 
 def word_differences_count(count):
@@ -442,22 +450,26 @@ def word_differences_count(count):
     return get_plural_text(count, "%d package", "%d packages") % count
 
 
-class DistroSeriesView(LaunchpadView, MilestoneOverlayMixin,
-                       DerivedDistroSeriesMixin):
-
+class DistroSeriesView(
+    LaunchpadView, MilestoneOverlayMixin, DerivedDistroSeriesMixin
+):
     def initialize(self):
         super().initialize()
-        self.displayname = '%s %s' % (
+        self.displayname = "%s %s" % (
             self.context.distribution.displayname,
-            self.context.version)
+            self.context.version,
+        )
         expose_structural_subscription_data_to_js(
-            self.context, self.request, self.user)
+            self.context, self.request, self.user
+        )
 
     @property
     def page_title(self):
         """Return the HTML page title."""
-        return '%s %s in Launchpad' % (
-        self.context.distribution.title, self.context.version)
+        return "%s %s in Launchpad" % (
+            self.context.distribution.title,
+            self.context.version,
+        )
 
     def requestCountry(self):
         return ICountry(self.request, None)
@@ -472,9 +484,10 @@ class DistroSeriesView(LaunchpadView, MilestoneOverlayMixin,
         permitted; we redirect to the distribution's file
         """
         distro_url = canonical_url(
-            self.context.distribution, view_name='+filebug')
-        if self.request.form.get('no-redirect') is not None:
-            distro_url += '?no-redirect'
+            self.context.distribution, view_name="+filebug"
+        )
+        if self.request.form.get("no-redirect") is not None:
+            distro_url += "?no-redirect"
         return self.request.response.redirect(distro_url)
 
     @cachedproperty
@@ -513,24 +526,27 @@ class DistroSeriesView(LaunchpadView, MilestoneOverlayMixin,
             attention?  If not, count all that can be viewed.
         """
         if needing_attention_only:
-            status = (DistroSeriesDifferenceStatus.NEEDS_ATTENTION, )
+            status = (DistroSeriesDifferenceStatus.NEEDS_ATTENTION,)
         else:
             status = None
 
         differences = get_dsd_source().getForDistroSeries(
-            self.context, difference_type=difference_type, status=status)
+            self.context, difference_type=difference_type, status=status
+        )
         return differences.count()
 
     @cachedproperty
     def num_version_differences_needing_attention(self):
         return self.countDifferences(
-            DistroSeriesDifferenceType.DIFFERENT_VERSIONS)
+            DistroSeriesDifferenceType.DIFFERENT_VERSIONS
+        )
 
     @cachedproperty
     def num_version_differences(self):
         return self.countDifferences(
             DistroSeriesDifferenceType.DIFFERENT_VERSIONS,
-            needing_attention_only=False)
+            needing_attention_only=False,
+        )
 
     def wordVersionDifferences(self):
         return word_differences_count(self.num_version_differences)
@@ -538,7 +554,8 @@ class DistroSeriesView(LaunchpadView, MilestoneOverlayMixin,
     @cachedproperty
     def num_differences_in_parent(self):
         return self.countDifferences(
-            DistroSeriesDifferenceType.MISSING_FROM_DERIVED_SERIES)
+            DistroSeriesDifferenceType.MISSING_FROM_DERIVED_SERIES
+        )
 
     def wordDifferencesInParent(self):
         return word_differences_count(self.num_differences_in_parent)
@@ -546,7 +563,8 @@ class DistroSeriesView(LaunchpadView, MilestoneOverlayMixin,
     @cachedproperty
     def num_differences_in_child(self):
         return self.countDifferences(
-            DistroSeriesDifferenceType.UNIQUE_TO_DERIVED_SERIES)
+            DistroSeriesDifferenceType.UNIQUE_TO_DERIVED_SERIES
+        )
 
     def wordDifferencesInChild(self):
         return word_differences_count(self.num_differences_in_child)
@@ -566,24 +584,25 @@ class DistroSeriesView(LaunchpadView, MilestoneOverlayMixin,
     @cachedproperty
     def link_to_version_diffs_needing_attention(self):
         """Return URL for +localpackagediffs page."""
-        return canonical_url(self.context, view_name='+localpackagediffs')
+        return canonical_url(self.context, view_name="+localpackagediffs")
 
     @property
     def link_to_all_version_diffs(self):
         """Return URL for +localdiffs page for all statuses."""
         return (
             "%s?field.package_type=all"
-            % self.link_to_version_diffs_needing_attention)
+            % self.link_to_version_diffs_needing_attention
+        )
 
     @property
     def link_to_differences_in_parent(self):
         """Return URL for +missingpackages page."""
-        return canonical_url(self.context, view_name='+missingpackages')
+        return canonical_url(self.context, view_name="+missingpackages")
 
     @property
     def link_to_differences_in_child(self):
         """Return URL for +uniquepackages page."""
-        return canonical_url(self.context, view_name='+uniquepackages')
+        return canonical_url(self.context, view_name="+uniquepackages")
 
 
 class DistroSeriesEditView(LaunchpadEditFormView, SeriesStatusMixin):
@@ -591,14 +610,15 @@ class DistroSeriesEditView(LaunchpadEditFormView, SeriesStatusMixin):
 
     It redirects to the main distroseries page after a successful edit.
     """
+
     schema = IDistroSeries
-    field_names = ['display_name', 'title', 'summary', 'description']
+    field_names = ["display_name", "title", "summary", "description"]
     custom_widget_status = LaunchpadDropdownWidget
 
     @property
     def label(self):
         """See `LaunchpadFormView`."""
-        return 'Edit %s details' % self.context.title
+        return "Edit %s details" % self.context.title
 
     @property
     def page_title(self):
@@ -618,22 +638,23 @@ class DistroSeriesEditView(LaunchpadEditFormView, SeriesStatusMixin):
         """
         LaunchpadEditFormView.setUpFields(self)
         self.series_are_harmless = (
-            not self.context.distribution.official_packages)
-        self.has_admin = check_permission('launchpad.Admin', self.context)
+            not self.context.distribution.official_packages
+        )
+        self.has_admin = check_permission("launchpad.Admin", self.context)
         if self.has_admin or self.series_are_harmless:
             # The user is an admin or damage to the series can't break
             # archives.
-            self.form_fields = (
-                self.form_fields + self.createStatusField())
+            self.form_fields = self.form_fields + self.createStatusField()
 
     @action("Change")
     def change_action(self, action, data):
         """Update the context and redirects to its overviw page."""
         if self.has_admin or self.series_are_harmless:
-            self.updateDateReleased(data.get('status'))
+            self.updateDateReleased(data.get("status"))
         self.updateContextFromData(data)
         self.request.response.addInfoNotification(
-            'Your changes have been applied.')
+            "Your changes have been applied."
+        )
         self.next_url = canonical_url(self.context)
 
 
@@ -642,15 +663,20 @@ class DistroSeriesAdminView(LaunchpadEditFormView, SeriesStatusMixin):
 
     It redirects to the main distroseries page after a successful edit.
     """
+
     schema = IDistroSeries
     field_names = [
-        'name', 'version', 'changeslist', 'inherit_overrides_from_parents']
+        "name",
+        "version",
+        "changeslist",
+        "inherit_overrides_from_parents",
+    ]
     custom_widget_status = LaunchpadDropdownWidget
 
     @property
     def label(self):
         """See `LaunchpadFormView`."""
-        return 'Administer %s' % self.context.title
+        return "Administer %s" % self.context.title
 
     @property
     def page_title(self):
@@ -669,8 +695,7 @@ class DistroSeriesAdminView(LaunchpadEditFormView, SeriesStatusMixin):
         'status' field. See `createStatusField` method.
         """
         LaunchpadEditFormView.setUpFields(self)
-        self.form_fields = (
-            self.form_fields + self.createStatusField())
+        self.form_fields = self.form_fields + self.createStatusField()
 
     @action("Change")
     def change_action(self, action, data):
@@ -679,50 +704,56 @@ class DistroSeriesAdminView(LaunchpadEditFormView, SeriesStatusMixin):
         Also, set 'datereleased' when a unstable distroseries is made
         CURRENT.
         """
-        self.updateDateReleased(data.get('status'))
+        self.updateDateReleased(data.get("status"))
         self.updateContextFromData(data)
 
         self.request.response.addInfoNotification(
-            'Your changes have been applied.')
+            "Your changes have been applied."
+        )
         self.next_url = canonical_url(self.context)
 
 
 class IDistroSeriesAddForm(Interface):
 
     name = copy_field(
-        IDistroSeries["name"], description=_(
-            "The name of this series as used for URLs."))
+        IDistroSeries["name"],
+        description=_("The name of this series as used for URLs."),
+    )
 
     version = copy_field(
-        IDistroSeries["version"], description=_(
-            "The version of the new series."))
+        IDistroSeries["version"],
+        description=_("The version of the new series."),
+    )
 
     display_name = copy_field(
-        IDistroSeries["display_name"], description=_(
-            "The name of the new series as it would "
-            "appear in a paragraph."))
+        IDistroSeries["display_name"],
+        description=_(
+            "The name of the new series as it would " "appear in a paragraph."
+        ),
+    )
 
     summary = copy_field(IDistroSeries["summary"])
 
 
 class DistroSeriesAddView(LaunchpadFormView):
     """A view to create an `IDistroSeries`."""
+
     schema = IDistroSeriesAddForm
     field_names = [
-        'name',
-        'version',
-        'display_name',
-        'summary',
-        ]
+        "name",
+        "version",
+        "display_name",
+        "summary",
+    ]
 
     help_links = {
         "name": "/+help-registry/distribution-add-series.html#codename",
-        }
+    }
 
-    label = 'Add a series'
+    label = "Add a series"
     page_title = label
 
-    @action(_('Add Series'), name='create')
+    @action(_("Add Series"), name="create")
     def createAndAdd(self, action, data):
         """Create and add a new Distribution Series"""
         # 'series' is a cached property so this won't issue 2 queries.
@@ -732,14 +763,15 @@ class DistroSeriesAddView(LaunchpadFormView):
             previous_series = None
         # previous_series will be None if there isn't one.
         distroseries = self.context.newSeries(
-            name=data['name'],
-            display_name=data['display_name'],
-            title=data['display_name'],
-            summary=data['summary'],
+            name=data["name"],
+            display_name=data["display_name"],
+            title=data["display_name"],
+            summary=data["summary"],
             description="",
-            version=data['version'],
+            version=data["version"],
             previous_series=previous_series,
-            registrant=self.user)
+            registrant=self.user,
+        )
         notify(ObjectCreatedEvent(distroseries))
         self.next_url = canonical_url(distroseries)
         return distroseries
@@ -753,11 +785,10 @@ def seriesToVocab(series):
     # Simple helper function to format series data into a dict:
     # {'value':series_id, 'api_uri': api_uri, 'title': series_title}.
     return {
-        'value': series.id,
-        'title': '%s: %s'
-            % (series.distribution.displayname, series.title),
-        'api_uri': canonical_url(
-            series, path_only_if_possible=True)}
+        "value": series.id,
+        "title": "%s: %s" % (series.distribution.displayname, series.title),
+        "api_uri": canonical_url(series, path_only_if_possible=True),
+    }
 
 
 class EmptySchema(Interface):
@@ -768,7 +799,7 @@ class DistroSeriesInitializeView(LaunchpadFormView):
     """A view to initialize an `IDistroSeries`."""
 
     schema = EmptySchema
-    label = 'Initialize series'
+    label = "Initialize series"
     page_title = label
 
     def initialize(self):
@@ -776,27 +807,31 @@ class DistroSeriesInitializeView(LaunchpadFormView):
         cache = IJSONRequestCache(self.request).objects
         distribution = self.context.distribution
         is_first_derivation = not distribution.has_published_sources
-        cache['is_first_derivation'] = is_first_derivation
-        if (not is_first_derivation and
-            self.context.previous_series is not None):
-            cache['previous_series'] = seriesToVocab(
-                self.context.previous_series)
+        cache["is_first_derivation"] = is_first_derivation
+        if (
+            not is_first_derivation
+            and self.context.previous_series is not None
+        ):
+            cache["previous_series"] = seriesToVocab(
+                self.context.previous_series
+            )
             previous_parents = self.context.previous_series.getParentSeries()
-            cache['previous_parents'] = [
-                seriesToVocab(series) for series in previous_parents]
+            cache["previous_parents"] = [
+                seriesToVocab(series) for series in previous_parents
+            ]
 
-    @action("Initialize Series", name='initialize')
+    @action("Initialize Series", name="initialize")
     def submit(self, action, data):
         """Stub for the Javascript in the page to use."""
 
     @cachedproperty
     def show_derivation_form(self):
         return (
-            not self.show_previous_series_empty_message and
-            not self.show_already_initializing_message and
-            not self.show_already_initialized_message and
-            not self.show_no_publisher_message
-            )
+            not self.show_previous_series_empty_message
+            and not self.show_already_initializing_message
+            and not self.show_already_initialized_message
+            and not self.show_no_publisher_message
+        )
 
     @cachedproperty
     def show_previous_series_empty_message(self):
@@ -804,8 +839,9 @@ class DistroSeriesInitializeView(LaunchpadFormView):
         # The distribution already has initialized series and this
         # distroseries has no previous_series.
         return (
-            self.context.distribution.has_published_sources and
-            self.context.previous_series is None)
+            self.context.distribution.has_published_sources
+            and self.context.previous_series is None
+        )
 
     @cachedproperty
     def show_already_initialized_message(self):
@@ -832,39 +868,39 @@ class DistroSeriesInitializeView(LaunchpadFormView):
 class DistroSeriesPackagesView(LaunchpadView):
     """A View to show series package to upstream package relationships."""
 
-    label = 'All series packages linked to upstream project series'
-    page_title = 'All upstream links'
+    label = "All series packages linked to upstream project series"
+    page_title = "All upstream links"
 
     @cachedproperty
     def cached_packagings(self):
         """The batched upstream packaging links."""
         packagings = self.context.getPrioritizedPackagings()
         navigator = BatchNavigator(packagings, self.request, size=20)
-        navigator.setHeadings('packaging', 'packagings')
+        navigator.setHeadings("packaging", "packagings")
         return navigator
 
 
 # A helper to create package filtering radio button vocabulary.
-NON_IGNORED = 'non-ignored'
-HIGHER_VERSION_THAN_PARENT = 'higher-than-parent'
-RESOLVED = 'resolved'
-ALL = 'all'
+NON_IGNORED = "non-ignored"
+HIGHER_VERSION_THAN_PARENT = "higher-than-parent"
+RESOLVED = "resolved"
+ALL = "all"
 
 DEFAULT_PACKAGE_TYPE = NON_IGNORED
 
 
 def make_package_type_vocabulary(parent_name, higher_version_option=False):
     voc = [
-        SimpleTerm(NON_IGNORED, NON_IGNORED, 'Non ignored packages'),
+        SimpleTerm(NON_IGNORED, NON_IGNORED, "Non ignored packages"),
         SimpleTerm(RESOLVED, RESOLVED, "Resolved package differences"),
-        SimpleTerm(ALL, ALL, 'All packages'),
-        ]
+        SimpleTerm(ALL, ALL, "All packages"),
+    ]
     if higher_version_option:
         higher_term = SimpleTerm(
             HIGHER_VERSION_THAN_PARENT,
             HIGHER_VERSION_THAN_PARENT,
-            "Ignored packages with a higher version than in %s"
-                % parent_name)
+            "Ignored packages with a higher version than in %s" % parent_name,
+        )
         voc.insert(1, higher_term)
     return SimpleVocabulary(tuple(voc))
 
@@ -872,45 +908,50 @@ def make_package_type_vocabulary(parent_name, higher_version_option=False):
 class DistroSeriesNeedsPackagesView(LaunchpadView):
     """A View to show series package to upstream package relationships."""
 
-    label = 'Packages that need upstream packaging links'
-    page_title = 'Needs upstream links'
+    label = "Packages that need upstream packaging links"
+    page_title = "Needs upstream links"
 
     @cachedproperty
     def cached_unlinked_packages(self):
         """The batched `ISourcePackage`s that needs packaging links."""
         packages = self.context.getPrioritizedUnlinkedSourcePackages()
         navigator = BatchNavigator(packages, self.request, size=20)
-        navigator.setHeadings('package', 'packages')
+        navigator.setHeadings("package", "packages")
         return navigator
 
 
 class IDifferencesFormSchema(Interface):
-    name_filter = TextLine(
-        title=_("Package name contains"), required=False)
+    name_filter = TextLine(title=_("Package name contains"), required=False)
 
     selected_differences = List(
-        title=_('Selected differences'),
+        title=_("Selected differences"),
         value_type=Choice(vocabulary="DistroSeriesDifferences"),
         description=_("Select the differences for syncing."),
-        required=True)
+        required=True,
+    )
 
     sponsored_person = Choice(
-        title="Person being sponsored", vocabulary='ValidPerson',
-        required=False)
+        title="Person being sponsored",
+        vocabulary="ValidPerson",
+        required=False,
+    )
 
 
-class DistroSeriesDifferenceBaseView(LaunchpadFormView,
-                                     PackageCopyingMixin,
-                                     DerivedDistroSeriesMixin):
+class DistroSeriesDifferenceBaseView(
+    LaunchpadFormView, PackageCopyingMixin, DerivedDistroSeriesMixin
+):
     """Base class for all pages presenting differences between
     a derived series and its parent."""
+
     schema = IDifferencesFormSchema
-    field_names = ['selected_differences', 'sponsored_person']
+    field_names = ["selected_differences", "sponsored_person"]
     custom_widget_selected_differences = LabeledMultiCheckBoxWidget
     custom_widget_package_type = LaunchpadRadioWidget
     custom_widget_sponsored_person = CustomWidgetFactory(
         PersonPickerWidget,
-        header="Select person being sponsored", show_assign_me_button=False)
+        header="Select person being sponsored",
+        show_assign_me_button=False,
+    )
 
     # Differences type to display. Can be overrided by sublasses.
     differences_type = DistroSeriesDifferenceType.DIFFERENT_VERSIONS
@@ -931,12 +972,16 @@ class DistroSeriesDifferenceBaseView(LaunchpadFormView,
         # action which we can modify.
         actions = self.actions
         sync_action = next(
-            action for action in actions if action.name == "sync")
+            action for action in actions if action.name == "sync"
+        )
         sync_action.label = label
         # Mask the actions descriptor with an instance variable.
         self.actions = actions.__class__(
-            *((sync_action if action.name == "sync" else action)
-              for action in actions))
+            *(
+                (sync_action if action.name == "sync" else action)
+                for action in actions
+            )
+        )
 
     @property
     def label(self):
@@ -946,14 +991,17 @@ class DistroSeriesDifferenceBaseView(LaunchpadFormView,
         if self.has_unique_parent:
             parent_name = "'%s'" % self.unique_parent.displayname
         else:
-            parent_name = 'parent'
-        return form.Fields(Choice(
-            __name__='package_type',
-            vocabulary=make_package_type_vocabulary(
-                parent_name,
-                self.search_higher_parent_option),
-            default=DEFAULT_PACKAGE_TYPE,
-            required=True))
+            parent_name = "parent"
+        return form.Fields(
+            Choice(
+                __name__="package_type",
+                vocabulary=make_package_type_vocabulary(
+                    parent_name, self.search_higher_parent_option
+                ),
+                default=DEFAULT_PACKAGE_TYPE,
+                required=True,
+            )
+        )
 
     def setUpFields(self):
         """Add the selected differences field.
@@ -962,9 +1010,7 @@ class DistroSeriesDifferenceBaseView(LaunchpadFormView,
         for its own vocabulary, we set it up after all the others.
         """
         super().setUpFields()
-        self.form_fields = (
-            self.setupPackageFilterRadio() +
-            self.form_fields)
+        self.form_fields = self.setupPackageFilterRadio() + self.form_fields
 
     def _sync_sources(self, action, data):
         """Synchronise packages from the parent series to this one."""
@@ -973,10 +1019,8 @@ class DistroSeriesDifferenceBaseView(LaunchpadFormView,
         # isn't, we need to implement a way of flagging sources 'to be
         # synced' and write a job runner to do it in the background.
 
-        selected_differences = data['selected_differences']
-        sources = [
-            diff.parent_source_pub
-            for diff in selected_differences]
+        selected_differences = data["selected_differences"]
+        sources = [diff.parent_source_pub for diff in selected_differences]
 
         # PackageCopyingMixin.do_copy() does the work of copying and
         # setting up on-page notifications.
@@ -992,10 +1036,17 @@ class DistroSeriesDifferenceBaseView(LaunchpadFormView,
         sponsored_person = data.get("sponsored_person")
 
         if self.do_copy(
-            'selected_differences', sources, self.context.main_archive,
-            self.context, destination_pocket, include_binaries=False,
-            dest_url=series_url, dest_display_name=series_title,
-            person=self.user, sponsored_person=sponsored_person):
+            "selected_differences",
+            sources,
+            self.context.main_archive,
+            self.context,
+            destination_pocket,
+            include_binaries=False,
+            dest_url=series_url,
+            dest_display_name=series_title,
+            person=self.user,
+            sponsored_person=sponsored_person,
+        ):
             # The copy worked so we redirect back to show the results. Include
             # the query string so that the user ends up on the same batch page
             # with the same filtering parameters as before.
@@ -1017,9 +1068,10 @@ class DistroSeriesDifferenceBaseView(LaunchpadFormView,
         """Validate selected differences."""
         form.getWidgetsData(self.widgets, self.prefix, data)
 
-        if len(data.get('selected_differences', [])) == 0:
+        if len(data.get("selected_differences", [])) == 0:
             self.setFieldError(
-                'selected_differences', 'No differences selected.')
+                "selected_differences", "No differences selected."
+            )
 
     def canPerformSync(self, *args):
         """Return whether a sync can be performed.
@@ -1028,11 +1080,11 @@ class DistroSeriesDifferenceBaseView(LaunchpadFormView,
         well as directly in the template.
         """
         archive = self.context.main_archive
-        has_perm = (self.user is not None and (
-                        archive.hasAnyPermission(self.user) or
-                        check_permission('launchpad.Append', archive)))
-        return (has_perm and
-                self.cached_differences.batch.total() > 0)
+        has_perm = self.user is not None and (
+            archive.hasAnyPermission(self.user)
+            or check_permission("launchpad.Append", archive)
+        )
+        return has_perm and self.cached_differences.batch.total() > 0
 
     @cachedproperty
     def pending_syncs(self):
@@ -1052,7 +1104,8 @@ class DistroSeriesDifferenceBaseView(LaunchpadFormView,
         """
         job_source = getUtility(IDistroSeriesDifferenceJobSource)
         return job_source.getPendingJobsForDifferences(
-            self.context, self.cached_differences.batch)
+            self.context, self.cached_differences.batch
+        )
 
     def hasPendingDSDUpdate(self, dsd):
         """Have there been changes that `dsd` is still being updated for?"""
@@ -1082,7 +1135,8 @@ class DistroSeriesDifferenceBaseView(LaunchpadFormView,
             # parent being newer.
             return False
         comparison = apt_pkg.version_compare(
-            dsd.parent_source_version, dsd.source_version)
+            dsd.parent_source_version, dsd.source_version
+        )
         return comparison < 0
 
     def canRequestSync(self, dsd):
@@ -1092,8 +1146,10 @@ class DistroSeriesDifferenceBaseView(LaunchpadFormView,
         # is newer than the parent's version, or if there is already a sync
         # pending.
         return (
-            dsd.status != DistroSeriesDifferenceStatus.RESOLVED and
-            not self.isNewerThanParent(dsd) and not self.pendingSync(dsd))
+            dsd.status != DistroSeriesDifferenceStatus.RESOLVED
+            and not self.isNewerThanParent(dsd)
+            and not self.pendingSync(dsd)
+        )
 
     def describeJobs(self, dsd):
         """Describe any jobs that may be pending for `dsd`.
@@ -1119,23 +1175,30 @@ class DistroSeriesDifferenceBaseView(LaunchpadFormView,
         if pending_sync is not None:
             # If the pending sync is waiting in the distroseries queues,
             # provide a handy link to there.
-            queue_item = getUtility(IPackageUploadSet).getByPackageCopyJobIDs(
-                (pending_sync.id,)).any()
+            queue_item = (
+                getUtility(IPackageUploadSet)
+                .getByPackageCopyJobIDs((pending_sync.id,))
+                .any()
+            )
             if queue_item is None:
                 description.append("synchronizing")
             else:
                 url = urlappend(
-                    canonical_url(self.context), "+queue?queue_state=%s" %
-                        queue_item.status.value)
-                description.append('waiting in <a href="%s">%s</a>' %
-                    (url, queue_item.status.name))
+                    canonical_url(self.context),
+                    "+queue?queue_state=%s" % queue_item.status.value,
+                )
+                description.append(
+                    'waiting in <a href="%s">%s</a>'
+                    % (url, queue_item.status.name)
+                )
         return " and ".join(description) + "&hellip;"
 
     @property
     def specified_name_filter(self):
         """If specified, return the name filter from the GET form data."""
         requested_name_filter = self.request.query_string_params.get(
-            'field.name_filter')
+            "field.name_filter"
+        )
 
         if requested_name_filter and requested_name_filter[0]:
             return requested_name_filter[0]
@@ -1145,15 +1208,20 @@ class DistroSeriesDifferenceBaseView(LaunchpadFormView,
     @property
     def specified_packagesets_filter(self):
         """If specified, return Packagesets given in the GET form data."""
-        packageset_ids = (
-            self.request.query_string_params.get("field.packageset", []))
+        packageset_ids = self.request.query_string_params.get(
+            "field.packageset", []
+        )
         packageset_ids = {
-            int(packageset_id) for packageset_id in packageset_ids
-            if packageset_id.isdigit()}
+            int(packageset_id)
+            for packageset_id in packageset_ids
+            if packageset_id.isdigit()
+        }
         packagesets = getUtility(IPackagesetSet).getBySeries(self.context)
         packagesets = {
-            packageset for packageset in packagesets
-            if packageset.id in packageset_ids}
+            packageset
+            for packageset in packagesets
+            if packageset.id in packageset_ids
+        }
         return None if len(packagesets) == 0 else packagesets
 
     @property
@@ -1161,11 +1229,10 @@ class DistroSeriesDifferenceBaseView(LaunchpadFormView,
         """If specified, return Persons given in the GET form data."""
         get_person_by_name = getUtility(IPersonSet).getByName
         changed_by_names = set(
-            self.request.query_string_params.get("field.changed_by", ()))
-        changed_by = (
-            get_person_by_name(name) for name in changed_by_names)
-        changed_by = {
-            person for person in changed_by if person is not None}
+            self.request.query_string_params.get("field.changed_by", ())
+        )
+        changed_by = (get_person_by_name(name) for name in changed_by_names)
+        changed_by = {person for person in changed_by if person is not None}
         return None if len(changed_by) == 0 else changed_by
 
     @property
@@ -1174,7 +1241,8 @@ class DistroSeriesDifferenceBaseView(LaunchpadFormView,
         data.
         """
         package_type = self.request.query_string_params.get(
-            'field.package_type')
+            "field.package_type"
+        )
         if package_type and package_type[0]:
             return package_type[0]
         else:
@@ -1186,7 +1254,8 @@ class DistroSeriesDifferenceBaseView(LaunchpadFormView,
         package_type_dsd_status = {
             NON_IGNORED: DistroSeriesDifferenceStatus.NEEDS_ATTENTION,
             HIGHER_VERSION_THAN_PARENT: (
-                DistroSeriesDifferenceStatus.BLACKLISTED_CURRENT),
+                DistroSeriesDifferenceStatus.BLACKLISTED_CURRENT
+            ),
             RESOLVED: DistroSeriesDifferenceStatus.RESOLVED,
             ALL: None,
         }
@@ -1194,35 +1263,42 @@ class DistroSeriesDifferenceBaseView(LaunchpadFormView,
         # If the package_type option is not supported, add an error to
         # the field and return an empty list.
         if self.specified_package_type not in package_type_dsd_status:
-            self.setFieldError('package_type', 'Invalid option')
+            self.setFieldError("package_type", "Invalid option")
             differences = []
         else:
             status = package_type_dsd_status[self.specified_package_type]
             child_version_higher = (
-                self.specified_package_type == HIGHER_VERSION_THAN_PARENT)
+                self.specified_package_type == HIGHER_VERSION_THAN_PARENT
+            )
             differences = get_dsd_source().getForDistroSeries(
-                self.context, difference_type=self.differences_type,
-                name_filter=self.specified_name_filter, status=status,
+                self.context,
+                difference_type=self.differences_type,
+                name_filter=self.specified_name_filter,
+                status=status,
                 child_version_higher=child_version_higher,
                 packagesets=self.specified_packagesets_filter,
-                changed_by=self.specified_changed_by_filter)
+                changed_by=self.specified_changed_by_filter,
+            )
 
         return BatchNavigator(differences, self.request)
 
     def parent_changelog_url(self, distroseriesdifference):
-        """The URL to the /parent/series/+source/package/+changelog """
+        """The URL to the /parent/series/+source/package/+changelog"""
         distro = distroseriesdifference.parent_series.distribution
         dsp = distro.getSourcePackage(
-            distroseriesdifference.source_package_name)
-        return urlappend(canonical_url(dsp), '+changelog')
+            distroseriesdifference.source_package_name
+        )
+        return urlappend(canonical_url(dsp), "+changelog")
 
 
-class DistroSeriesLocalDifferencesView(DistroSeriesDifferenceBaseView,
-                                       LaunchpadFormView):
+class DistroSeriesLocalDifferencesView(
+    DistroSeriesDifferenceBaseView, LaunchpadFormView
+):
     """Present differences of type DIFFERENT_VERSIONS between
     a derived series and its parent.
     """
-    page_title = 'Local package differences'
+
+    page_title = "Local package differences"
     differences_type = DistroSeriesDifferenceType.DIFFERENT_VERSIONS
     show_packagesets = True
     search_higher_parent_option = True
@@ -1232,12 +1308,14 @@ class DistroSeriesLocalDifferencesView(DistroSeriesDifferenceBaseView,
         if self.has_unique_parent:
             parent_name = "'%s'" % self.unique_parent.displayname
         else:
-            parent_name = 'Parent'
+            parent_name = "Parent"
         self.initialize_sync_label(
-            "Sync Selected %s Versions into %s" % (
+            "Sync Selected %s Versions into %s"
+            % (
                 parent_name,
                 self.context.displayname,
-                ))
+            )
+        )
         super().initialize()
 
     @property
@@ -1249,21 +1327,24 @@ class DistroSeriesLocalDifferencesView(DistroSeriesDifferenceBaseView,
             "versions (and the diff if necessary) before syncing the parent "
             'version (<a href="/+help-soyuz/derived-series-syncing.html" '
             'target="help">Read more about syncing from a parent series'
-            '</a>).',
+            "</a>).",
             self.context.displayname,
-            self.getParentName())
+            self.getParentName(),
+        )
 
     @property
     def label(self):
-        return (
-            "Source package differences between '%s' and"
-            " %s" % (
-                self.context.displayname,
-                self.getParentName(multiple_parent_default='parent series'),
-                ))
-
-    @action(_("Sync Sources"), name="sync", validator='validate_sync',
-            condition='canPerformSync')
+        return "Source package differences between '%s' and" " %s" % (
+            self.context.displayname,
+            self.getParentName(multiple_parent_default="parent series"),
+        )
+
+    @action(
+        _("Sync Sources"),
+        name="sync",
+        validator="validate_sync",
+        condition="canPerformSync",
+    )
     def sync_sources(self, action, data):
         self._sync_sources(action, data)
 
@@ -1279,7 +1360,7 @@ class DistroSeriesLocalDifferencesView(DistroSeriesDifferenceBaseView,
         """
         return get_dsd_source().getSimpleUpgrades(self.context)
 
-    @action(_("Upgrade Packages"), name="upgrade", condition='canUpgrade')
+    @action(_("Upgrade Packages"), name="upgrade", condition="canUpgrade")
     def upgrade(self, action, data):
         """Request synchronization of straightforward package upgrades."""
         self.requestUpgrades()
@@ -1296,14 +1377,19 @@ class DistroSeriesLocalDifferencesView(DistroSeriesDifferenceBaseView,
                 target_distroseries,
                 PackagePublishingPocket.RELEASE,
             )
-            for dsd in self.getUpgrades()]
+            for dsd in self.getUpgrades()
+        ]
         getUtility(IPlainPackageCopyJobSource).createMultiple(
-            copies, self.user, copy_policy=PackageCopyPolicy.MASS_SYNC)
+            copies, self.user, copy_policy=PackageCopyPolicy.MASS_SYNC
+        )
 
         self.request.response.addInfoNotification(
-            ("Upgrades of {context.displayname} packages have been "
-             "requested. Please give Launchpad some time to complete "
-             "these.").format(context=self.context))
+            (
+                "Upgrades of {context.displayname} packages have been "
+                "requested. Please give Launchpad some time to complete "
+                "these."
+            ).format(context=self.context)
+        )
 
     def canUpgrade(self, action=None):
         """Should the form offer a packages upgrade?"""
@@ -1320,12 +1406,14 @@ class DistroSeriesLocalDifferencesView(DistroSeriesDifferenceBaseView,
             return check_permission("launchpad.Edit", queue)
 
 
-class DistroSeriesMissingPackagesView(DistroSeriesDifferenceBaseView,
-                                      LaunchpadFormView):
+class DistroSeriesMissingPackagesView(
+    DistroSeriesDifferenceBaseView, LaunchpadFormView
+):
     """Present differences of type MISSING_FROM_DERIVED_SERIES between
     a derived series and its parent.
     """
-    page_title = 'Missing packages'
+
+    page_title = "Missing packages"
     differences_type = DistroSeriesDifferenceType.MISSING_FROM_DERIVED_SERIES
     show_derived_version = False
     show_package_diffs = False
@@ -1334,9 +1422,8 @@ class DistroSeriesMissingPackagesView(DistroSeriesDifferenceBaseView,
     def initialize(self):
         # Update the label for sync action.
         self.initialize_sync_label(
-            "Include Selected packages into %s" % (
-                self.context.displayname,
-                ))
+            "Include Selected packages into %s" % (self.context.displayname,)
+        )
         super().initialize()
 
     @property
@@ -1347,28 +1434,34 @@ class DistroSeriesMissingPackagesView(DistroSeriesDifferenceBaseView,
             "They are listed here so you can consider including them in %s.",
             self.getParentName(),
             self.context.displayname,
-            self.context.displayname)
+            self.context.displayname,
+        )
 
     @property
     def label(self):
-        return (
-            "Packages in %s but not in '%s'" % (
-                self.getParentName(),
-                self.context.displayname,
-                ))
+        return "Packages in %s but not in '%s'" % (
+            self.getParentName(),
+            self.context.displayname,
+        )
 
-    @action(_("Sync Sources"), name="sync", validator='validate_sync',
-            condition='canPerformSync')
+    @action(
+        _("Sync Sources"),
+        name="sync",
+        validator="validate_sync",
+        condition="canPerformSync",
+    )
     def sync_sources(self, action, data):
         self._sync_sources(action, data)
 
 
-class DistroSeriesUniquePackagesView(DistroSeriesDifferenceBaseView,
-                                     LaunchpadFormView):
+class DistroSeriesUniquePackagesView(
+    DistroSeriesDifferenceBaseView, LaunchpadFormView
+):
     """Present differences of type UNIQUE_TO_DERIVED_SERIES between
     a derived series and its parent.
     """
-    page_title = 'Unique packages'
+
+    page_title = "Unique packages"
     differences_type = DistroSeriesDifferenceType.UNIQUE_TO_DERIVED_SERIES
     show_parent = True
     show_parent_version = False  # The DSDs are unique to the derived series.
@@ -1384,15 +1477,15 @@ class DistroSeriesUniquePackagesView(DistroSeriesDifferenceBaseView,
             "Packages that are listed here are those that have been added to "
             "%s but are not yet part of %s.",
             self.context.displayname,
-            self.getParentName())
+            self.getParentName(),
+        )
 
     @property
     def label(self):
-        return (
-            "Packages in '%s' but not in %s" % (
-                self.context.displayname,
-                self.getParentName(),
-                ))
+        return "Packages in '%s' but not in %s" % (
+            self.context.displayname,
+            self.getParentName(),
+        )
 
     def canPerformSync(self, *args):
         return False
diff --git a/lib/lp/registry/browser/distroseriesdifference.py b/lib/lp/registry/browser/distroseriesdifference.py
index ddcd915..a64469b 100644
--- a/lib/lp/registry/browser/distroseriesdifference.py
+++ b/lib/lp/registry/browser/distroseriesdifference.py
@@ -4,62 +4,49 @@
 """Browser views for DistroSeriesDifferences."""
 
 __all__ = [
-    'CommentXHTMLRepresentation',
-    'DistroSeriesDifferenceView',
-    ]
+    "CommentXHTMLRepresentation",
+    "DistroSeriesDifferenceView",
+]
 
 from lazr.delegates import delegate_to
 from lazr.restful.interfaces import IWebServiceClientRequest
 from zope.browserpage import ViewPageTemplateFile
-from zope.component import (
-    adapter,
-    getUtility,
-    )
+from zope.component import adapter, getUtility
 from zope.formlib.itemswidgets import RadioWidget
-from zope.interface import (
-    implementer,
-    Interface,
-    )
+from zope.interface import Interface, implementer
 from zope.schema import Choice
-from zope.schema.vocabulary import (
-    SimpleTerm,
-    SimpleVocabulary,
-    )
+from zope.schema.vocabulary import SimpleTerm, SimpleVocabulary
 
 from lp.app.browser.launchpadform import LaunchpadFormView
 from lp.registry.enums import (
     DistroSeriesDifferenceStatus,
     DistroSeriesDifferenceType,
-    )
+)
 from lp.registry.interfaces.distroseriesdifference import (
     IDistroSeriesDifference,
-    )
+)
 from lp.registry.interfaces.distroseriesdifferencecomment import (
     IDistroSeriesDifferenceComment,
     IDistroSeriesDifferenceCommentSource,
-    )
+)
 from lp.registry.model.distroseriesdifferencecomment import (
     DistroSeriesDifferenceComment,
-    )
+)
 from lp.services.comments.browser.messagecomment import MessageComment
 from lp.services.comments.interfaces.conversation import (
     IComment,
     IConversation,
-    )
+)
 from lp.services.messages.interfaces.message import IMessage
 from lp.services.propertycache import cachedproperty
-from lp.services.webapp import (
-    LaunchpadView,
-    Navigation,
-    stepthrough,
-    )
+from lp.services.webapp import LaunchpadView, Navigation, stepthrough
 from lp.services.webapp.authorization import check_permission
 
 
 class DistroSeriesDifferenceNavigation(Navigation):
     usedfor = IDistroSeriesDifference
 
-    @stepthrough('comments')
+    @stepthrough("comments")
     def traverse_comment(self, id_str):
         try:
             id = int(id_str)
@@ -67,8 +54,8 @@ class DistroSeriesDifferenceNavigation(Navigation):
             return None
 
         return getUtility(
-            IDistroSeriesDifferenceCommentSource).getForDifference(
-                self.context, id)
+            IDistroSeriesDifferenceCommentSource
+        ).getForDifference(self.context, id)
 
     @property
     def parent_packagesets_names(self):
@@ -87,24 +74,31 @@ class DistroSeriesDifferenceNavigation(Navigation):
     def _formatPackageSets(self, packagesets):
         """Format a list of packagesets to display in the UI."""
         if packagesets is not None:
-            return ', '.join([packageset.name for packageset in packagesets])
+            return ", ".join([packageset.name for packageset in packagesets])
         else:
             return None
 
 
 class IDistroSeriesDifferenceForm(Interface):
     """An interface used in the browser only for displaying form elements."""
-    blacklist_options = Choice(vocabulary=SimpleVocabulary((
-        SimpleTerm('NONE', 'NONE', 'No'),
-        SimpleTerm(
-            DistroSeriesDifferenceStatus.BLACKLISTED_ALWAYS,
-            DistroSeriesDifferenceStatus.BLACKLISTED_ALWAYS.name,
-            'All versions'),
-        SimpleTerm(
-            DistroSeriesDifferenceStatus.BLACKLISTED_CURRENT,
-            DistroSeriesDifferenceStatus.BLACKLISTED_CURRENT.name,
-            'These versions'),
-        )))
+
+    blacklist_options = Choice(
+        vocabulary=SimpleVocabulary(
+            (
+                SimpleTerm("NONE", "NONE", "No"),
+                SimpleTerm(
+                    DistroSeriesDifferenceStatus.BLACKLISTED_ALWAYS,
+                    DistroSeriesDifferenceStatus.BLACKLISTED_ALWAYS.name,
+                    "All versions",
+                ),
+                SimpleTerm(
+                    DistroSeriesDifferenceStatus.BLACKLISTED_CURRENT,
+                    DistroSeriesDifferenceStatus.BLACKLISTED_CURRENT.name,
+                    "These versions",
+                ),
+            )
+        )
+    )
 
 
 @implementer(IConversation)
@@ -118,11 +112,11 @@ class DistroSeriesDifferenceView(LaunchpadFormView):
         blacklisted_statuses = (
             DistroSeriesDifferenceStatus.BLACKLISTED_CURRENT,
             DistroSeriesDifferenceStatus.BLACKLISTED_ALWAYS,
-            )
+        )
         if self.context.status in blacklisted_statuses:
             return dict(blacklist_options=self.context.status)
 
-        return dict(blacklist_options='NONE')
+        return dict(blacklist_options="NONE")
 
     @property
     def binary_summaries(self):
@@ -136,7 +130,7 @@ class DistroSeriesDifferenceView(LaunchpadFormView):
         if source_pub is not None:
             summary = source_pub.meta_sourcepackage.summary
             if summary:
-                return summary.split('\n')
+                return summary.split("\n")
 
         return None
 
@@ -144,15 +138,17 @@ class DistroSeriesDifferenceView(LaunchpadFormView):
     def comments(self):
         """See `IConversation`."""
         comments = self.context.getComments().order_by(
-            DistroSeriesDifferenceComment.id)
+            DistroSeriesDifferenceComment.id
+        )
         return [
-            DistroSeriesDifferenceDisplayComment(comment) for
-                comment in comments]
+            DistroSeriesDifferenceDisplayComment(comment)
+            for comment in comments
+        ]
 
     @cachedproperty
     def can_request_diffs(self):
         """Does the user have permission to request diff calculation?"""
-        return check_permission('launchpad.Edit', self.context)
+        return check_permission("launchpad.Edit", self.context)
 
     @cachedproperty
     def show_add_comment(self):
@@ -167,7 +163,8 @@ class DistroSeriesDifferenceView(LaunchpadFormView):
         is an archive admin.
         """
         return self.request.is_ajax and check_permission(
-            'launchpad.Admin', self.context)
+            "launchpad.Admin", self.context
+        )
 
     @cachedproperty
     def blacklist_options_css_class(self):
@@ -176,9 +173,9 @@ class DistroSeriesDifferenceView(LaunchpadFormView):
         'blacklist-options-disabled' if not enabled.
         """
         if self.enable_blacklist_options:
-            return 'blacklist-options'
+            return "blacklist-options"
         else:
-            return 'blacklist-options-disabled'
+            return "blacklist-options-disabled"
 
     @property
     def display_diffs(self):
@@ -212,13 +209,16 @@ class DistroSeriesDifferenceView(LaunchpadFormView):
         request link.
         """
         derived_diff_computable = (
-            not self.context.package_diff and self.display_child_diff)
+            not self.context.package_diff and self.display_child_diff
+        )
         parent_diff_computable = (
-            not self.context.parent_package_diff and self.display_parent_diff)
-        return (self.display_diffs and
-                self.can_request_diffs and
-                (derived_diff_computable or
-                 parent_diff_computable))
+            not self.context.parent_package_diff and self.display_parent_diff
+        )
+        return (
+            self.display_diffs
+            and self.can_request_diffs
+            and (derived_diff_computable or parent_diff_computable)
+        )
 
     @property
     def display_package_diffs_info(self):
@@ -236,9 +236,10 @@ class DistroSeriesDifferenceView(LaunchpadFormView):
 
         """
         return (
-            self.context.package_diff is not None or
-            self.context.parent_package_diff is not None or
-            self.show_package_diffs_request_link)
+            self.context.package_diff is not None
+            or self.context.parent_package_diff is not None
+            or self.show_package_diffs_request_link
+        )
 
 
 class IDistroSeriesDifferenceDisplayComment(IComment, IMessage):
@@ -246,7 +247,7 @@ class IDistroSeriesDifferenceDisplayComment(IComment, IMessage):
 
 
 @implementer(IDistroSeriesDifferenceDisplayComment)
-@delegate_to(IMessage, context='_message')
+@delegate_to(IMessage, context="_message")
 class DistroSeriesDifferenceDisplayComment(MessageComment):
     """Used simply to provide `IComment` for rendering."""
 
@@ -272,8 +273,10 @@ def get_message(comment):
 @implementer(Interface)
 class CommentXHTMLRepresentation(LaunchpadView):
     """Render individual comments when requested via the API."""
+
     template = ViewPageTemplateFile(
-        '../templates/distroseriesdifferencecomment-fragment.pt')
+        "../templates/distroseriesdifferencecomment-fragment.pt"
+    )
 
     @property
     def comment(self):
diff --git a/lib/lp/registry/browser/distroseriesdifferencecomment.py b/lib/lp/registry/browser/distroseriesdifferencecomment.py
index 5bb0c86..f72b0a6 100644
--- a/lib/lp/registry/browser/distroseriesdifferencecomment.py
+++ b/lib/lp/registry/browser/distroseriesdifferencecomment.py
@@ -20,4 +20,4 @@ class DistroSeriesDifferenceCommentView(LaunchpadView):
     def __init__(self, *args, **kwargs):
         super().__init__(*args, **kwargs)
         error_persona = getUtility(ILaunchpadCelebrities).janitor
-        self.is_error = (self.context.comment_author == error_persona)
+        self.is_error = self.context.comment_author == error_persona
diff --git a/lib/lp/registry/browser/driver.py b/lib/lp/registry/browser/driver.py
index 5225d10..dd78c89 100644
--- a/lib/lp/registry/browser/driver.py
+++ b/lib/lp/registry/browser/driver.py
@@ -8,10 +8,7 @@ __all__ = ["AppointDriverView"]
 from zope.interface import providedBy
 from zope.security.proxy import removeSecurityProxy
 
-from lp.app.browser.launchpadform import (
-    action,
-    LaunchpadEditFormView,
-    )
+from lp.app.browser.launchpadform import LaunchpadEditFormView, action
 from lp.registry.interfaces.productseries import IProductSeries
 from lp.registry.interfaces.role import IHasAppointedDriver
 from lp.services.webapp.publisher import canonical_url
@@ -20,15 +17,16 @@ from lp.services.webapp.publisher import canonical_url
 class AppointDriverView(LaunchpadEditFormView):
     """Browser view for appointing a driver to an object."""
 
-    field_names = ['driver']
+    field_names = ["driver"]
 
     @property
     def schema(self):
         """Return the schema that is the most specific extension of
         IHasAppointedDriver
         """
-        assert IHasAppointedDriver.providedBy(self.context), (
-            "context should provide IHasAppointedDriver.")
+        assert IHasAppointedDriver.providedBy(
+            self.context
+        ), "context should provide IHasAppointedDriver."
         for interface in providedBy(self.context):
             if interface.isOrExtends(IHasAppointedDriver):
                 # XXX matsubara 2007-02-13 bug=84940:
@@ -46,25 +44,29 @@ class AppointDriverView(LaunchpadEditFormView):
         if IProductSeries.providedBy(self.context):
             return "release manager"
         else:
-            return 'driver'
+            return "driver"
 
     @property
     def page_title(self):
-        return 'Appoint the %s for %s' % (
-            self.driver_title, self.context.title)
+        return "Appoint the %s for %s" % (
+            self.driver_title,
+            self.context.title,
+        )
 
-    @action('Change', name='change')
+    @action("Change", name="change")
     def change_action(self, action, data):
         """Change the driver."""
-        driver = data['driver']
+        driver = data["driver"]
         self.updateContextFromData(data)
         if driver:
             self.request.response.addNotification(
-                "Successfully changed the %s to %s" % (
-                    self.driver_title, driver.displayname))
+                "Successfully changed the %s to %s"
+                % (self.driver_title, driver.displayname)
+            )
         else:
             self.request.response.addNotification(
-                "Successfully removed the %s" % self.driver_title)
+                "Successfully removed the %s" % self.driver_title
+            )
 
     @property
     def next_url(self):
diff --git a/lib/lp/registry/browser/featuredproject.py b/lib/lp/registry/browser/featuredproject.py
index 214eede..4152919 100644
--- a/lib/lp/registry/browser/featuredproject.py
+++ b/lib/lp/registry/browser/featuredproject.py
@@ -4,21 +4,15 @@
 """Featured Project views."""
 
 __all__ = [
-    'FeaturedProjectsView',
-    ]
+    "FeaturedProjectsView",
+]
 
 from zope.component import getUtility
 from zope.interface import Interface
-from zope.schema import (
-    Choice,
-    Set,
-    )
+from zope.schema import Choice, Set
 
 from lp import _
-from lp.app.browser.launchpadform import (
-    action,
-    LaunchpadFormView,
-    )
+from lp.app.browser.launchpadform import LaunchpadFormView, action
 from lp.app.widgets.itemswidgets import LabeledMultiCheckBoxWidget
 from lp.registry.interfaces.pillar import IPillarNameSet
 from lp.services.webapp import canonical_url
@@ -30,42 +24,47 @@ class FeaturedProjectForm(Interface):
     add = Choice(
         title=_("Add project"),
         description=_(
-            "Choose a project to feature on the Launchpad home page."),
-        required=False, vocabulary='DistributionOrProductOrProjectGroup')
+            "Choose a project to feature on the Launchpad home page."
+        ),
+        required=False,
+        vocabulary="DistributionOrProductOrProjectGroup",
+    )
 
     remove = Set(
-        title='Remove projects',
+        title="Remove projects",
         description=_(
-            'Select projects that you would like to remove from the list.'),
+            "Select projects that you would like to remove from the list."
+        ),
         required=False,
-        value_type=Choice(vocabulary="FeaturedProject"))
+        value_type=Choice(vocabulary="FeaturedProject"),
+    )
 
 
 class FeaturedProjectsView(LaunchpadFormView):
     """A view for adding and removing featured projects."""
 
-    label = 'Manage featured projects in Launchpad'
+    label = "Manage featured projects in Launchpad"
     page_title = label
 
     schema = FeaturedProjectForm
     custom_widget_remove = LabeledMultiCheckBoxWidget
 
-    @action(_('Update featured project list'), name='update')
+    @action(_("Update featured project list"), name="update")
     def update_action(self, action, data):
         """Add and remove featured projects."""
 
-        add = data.get('add')
+        add = data.get("add")
         if add is not None:
             getUtility(IPillarNameSet).add_featured_project(add)
 
-        remove = data.get('remove')
+        remove = data.get("remove")
         if remove is not None:
             for project in remove:
                 getUtility(IPillarNameSet).remove_featured_project(project)
 
         self.next_url = canonical_url(self.context)
 
-    @action(_("Cancel"), name="cancel", validator='validate_cancel')
+    @action(_("Cancel"), name="cancel", validator="validate_cancel")
     def action_cancel(self, action, data):
         self.next_url = canonical_url(self.context)
 
diff --git a/lib/lp/registry/browser/karma.py b/lib/lp/registry/browser/karma.py
index c0be894..19daccc 100644
--- a/lib/lp/registry/browser/karma.py
+++ b/lib/lp/registry/browser/karma.py
@@ -2,35 +2,25 @@
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 __all__ = [
-    'KarmaActionEditView',
-    'KarmaActionSetNavigation',
-    'KarmaContextTopContributorsView',
-    ]
+    "KarmaActionEditView",
+    "KarmaActionSetNavigation",
+    "KarmaContextTopContributorsView",
+]
 
 from operator import attrgetter
 
 from zope.component import getUtility
 
 from lp import _
-from lp.app.browser.launchpadform import (
-    action,
-    LaunchpadEditFormView,
-    )
+from lp.app.browser.launchpadform import LaunchpadEditFormView, action
 from lp.registry.interfaces.distribution import IDistribution
-from lp.registry.interfaces.karma import (
-    IKarmaAction,
-    IKarmaActionSet,
-    )
+from lp.registry.interfaces.karma import IKarmaAction, IKarmaActionSet
 from lp.registry.interfaces.product import IProduct
 from lp.registry.interfaces.projectgroup import IProjectGroup
 from lp.services.propertycache import cachedproperty
-from lp.services.webapp import (
-    canonical_url,
-    Navigation,
-    )
+from lp.services.webapp import Navigation, canonical_url
 from lp.services.webapp.publisher import LaunchpadView
 
-
 TOP_CONTRIBUTORS_LIMIT = 20
 
 
@@ -45,7 +35,7 @@ class KarmaActionSetNavigation(Navigation):
 class KarmaActionView(LaunchpadView):
     """View class for the index of karma actions."""
 
-    page_title = 'Actions that give people karma'
+    page_title = "Actions that give people karma"
 
 
 class KarmaActionEditView(LaunchpadEditFormView):
@@ -56,7 +46,7 @@ class KarmaActionEditView(LaunchpadEditFormView):
     @property
     def label(self):
         """See `LaunchpadFormView`."""
-        return 'Edit %s karma action' % self.context.title
+        return "Edit %s karma action" % self.context.title
 
     @property
     def page_title(self):
@@ -75,7 +65,6 @@ class KarmaActionEditView(LaunchpadEditFormView):
 
 
 class KarmaContextContributor:
-
     def __init__(self, person, karmavalue):
         self.person = person
         self.karmavalue = karmavalue
@@ -91,22 +80,24 @@ class KarmaContextTopContributorsView(LaunchpadView):
     def initialize(self):
         context = self.context
         if IProduct.providedBy(context):
-            self.context_name = 'Project'
+            self.context_name = "Project"
         elif IDistribution.providedBy(context):
-            self.context_name = 'Distribution'
+            self.context_name = "Distribution"
         elif IProjectGroup.providedBy(context):
-            self.context_name = 'Project Group'
+            self.context_name = "Project Group"
         else:
             raise AssertionError(
                 "Context is not a Product, Project group or Distribution: %r"
-                % context)
+                % context
+            )
 
     def _getTopContributorsWithLimit(self, limit=None):
         results = self.context.getTopContributors(limit=limit)
-        contributors = [KarmaContextContributor(person, karmavalue)
-                        for person, karmavalue in results]
-        return sorted(
-            contributors, key=attrgetter('karmavalue'), reverse=True)
+        contributors = [
+            KarmaContextContributor(person, karmavalue)
+            for person, karmavalue in results
+        ]
+        return sorted(contributors, key=attrgetter("karmavalue"), reverse=True)
 
     def getTopContributors(self):
         return self._getTopContributorsWithLimit(limit=TOP_CONTRIBUTORS_LIMIT)
@@ -118,14 +109,14 @@ class KarmaContextTopContributorsView(LaunchpadView):
     def top_contributors_by_category(self):
         contributors_by_category = {}
         limit = TOP_CONTRIBUTORS_LIMIT
-        results = self.context.getTopContributorsGroupedByCategory(
-            limit=limit)
+        results = self.context.getTopContributorsGroupedByCategory(limit=limit)
         for category, people_and_karma in results.items():
             contributors = []
             for person, karmavalue in people_and_karma:
-                contributors.append(KarmaContextContributor(
-                    person, karmavalue))
-            contributors.sort(key=attrgetter('karmavalue'), reverse=True)
+                contributors.append(
+                    KarmaContextContributor(person, karmavalue)
+                )
+            contributors.sort(key=attrgetter("karmavalue"), reverse=True)
             contributors_by_category[category.title] = contributors
         return contributors_by_category
 
diff --git a/lib/lp/registry/browser/mailinglists.py b/lib/lp/registry/browser/mailinglists.py
index be1e872..769e7be 100644
--- a/lib/lp/registry/browser/mailinglists.py
+++ b/lib/lp/registry/browser/mailinglists.py
@@ -4,9 +4,9 @@
 """Browser views for handling mailing lists."""
 
 __all__ = [
-    'HeldMessageView',
-    'enabled_with_active_mailing_list',
-    ]
+    "HeldMessageView",
+    "enabled_with_active_mailing_list",
+]
 
 
 from textwrap import TextWrapper
@@ -18,7 +18,7 @@ from lp.app.browser.tales import PersonFormatterAPI
 from lp.registry.interfaces.mailinglist import (
     IHeldMessageDetails,
     IMailingListSet,
-    )
+)
 from lp.registry.interfaces.person import ITeam
 from lp.services.webapp import LaunchpadView
 from lp.services.webapp.escaping import html_escape
@@ -40,7 +40,7 @@ class HeldMessageView(LaunchpadView):
         self.message_id = self.details.message_id
         self.subject = self.details.subject
         self.date = self.details.date
-        self.widget_name = 'field.' + quote(self.message_id)
+        self.widget_name = "field." + quote(self.message_id)
         self.author = PersonFormatterAPI(self.details.author).link(None)
 
     def initialize(self):
@@ -74,16 +74,16 @@ class HeldMessageView(LaunchpadView):
             else:
                 current_paragraph.append(line)
         self._append_paragraph(paragraphs, current_paragraph)
-        self.body_details = ''.join(paragraphs)
+        self.body_details = "".join(paragraphs)
 
     def _append_paragraph(self, paragraphs, current_paragraph):
         if len(current_paragraph) == 0:
             # There is nothing to append. The message has multiple
             # blank lines.
             return
-        paragraphs.append('\n<p>\n')
-        paragraphs.append('\n'.join(current_paragraph))
-        paragraphs.append('\n</p>\n')
+        paragraphs.append("\n<p>\n")
+        paragraphs.append("\n".join(current_paragraph))
+        paragraphs.append("\n</p>\n")
 
     def _remove_leading_blank_lines(self):
         """Strip off any leading blank lines.
@@ -113,13 +113,13 @@ class HeldMessageView(LaunchpadView):
         """
         # If there are no non-blank lines, then we're done.
         if len(text_lines) == 0:
-            self.body_summary = ''
-            return ''
+            self.body_summary = ""
+            return ""
         # If the first line is of a completely arbitrarily chosen reasonable
         # length, then we'll just use that as the summary.
         elif len(text_lines[0]) < 60:
             self.body_summary = text_lines[0]
-            return '\n'.join(text_lines[1:])
+            return "\n".join(text_lines[1:])
         # It could be the case that the text is actually flowed using RFC
         # 3676 format="flowed" parameters.  In that case, just split the line
         # at the first whitespace after, again, our arbitrarily chosen limit.
@@ -128,8 +128,8 @@ class HeldMessageView(LaunchpadView):
             wrapper = TextWrapper(width=60)
             filled_lines = wrapper.fill(first_line).splitlines()
             self.body_summary = filled_lines[0]
-            text_lines.insert(0, ''.join(filled_lines[1:]))
-            return '\n'.join(text_lines)
+            text_lines.insert(0, "".join(filled_lines[1:]))
+            return "\n".join(text_lines)
 
 
 class enabled_with_active_mailing_list:
@@ -139,8 +139,7 @@ class enabled_with_active_mailing_list:
         self._function = function
 
     def __get__(self, obj, type=None):
-        """Called by the decorator machinery to return a decorated function.
-        """
+        """Called by the decorator machinery to return a decorated function."""
 
         def enable_if_active(*args, **kws):
             link = self._function(obj, *args, **kws)
@@ -150,4 +149,5 @@ class enabled_with_active_mailing_list:
             if mailing_list is None or not mailing_list.is_usable:
                 link.enabled = False
             return link
+
         return enable_if_active
diff --git a/lib/lp/registry/browser/menu.py b/lib/lp/registry/browser/menu.py
index ad03092..8ec0b00 100644
--- a/lib/lp/registry/browser/menu.py
+++ b/lib/lp/registry/browser/menu.py
@@ -4,73 +4,73 @@
 """Shared menus."""
 
 __all__ = [
-    'IRegistryCollectionNavigationMenu',
-    'RegistryCollectionActionMenuBase',
-    'RegistryCollectionNavigationMenu',
-    'TopLevelMenuMixin',
-    ]
+    "IRegistryCollectionNavigationMenu",
+    "RegistryCollectionActionMenuBase",
+    "RegistryCollectionNavigationMenu",
+    "TopLevelMenuMixin",
+]
 
 
 from zope.interface import Interface
 
 from lp.services.webapp.menu import (
-    enabled_with_permission,
     Link,
     NavigationMenu,
-    )
+    enabled_with_permission,
+)
 
 
 class TopLevelMenuMixin:
     """Menu shared by top level collection objects."""
 
     def projects(self):
-        return Link('/projects/', 'View projects', icon='info')
+        return Link("/projects/", "View projects", icon="info")
 
     def distributions(self):
-        return Link('/distros/', 'View distributions', icon='info')
+        return Link("/distros/", "View distributions", icon="info")
 
     def people(self):
-        return Link('/people/', 'View people', icon='info')
+        return Link("/people/", "View people", icon="info")
 
     def meetings(self):
-        return Link('/sprints/', 'View meetings', icon='info')
+        return Link("/sprints/", "View meetings", icon="info")
 
     def project_groups(self):
-        return Link('/projectgroups', 'View project groups', icon='info')
+        return Link("/projectgroups", "View project groups", icon="info")
 
     def register_project(self):
-        text = 'Register a project'
-        return Link('/projects/+new', text, icon='add')
+        text = "Register a project"
+        return Link("/projects/+new", text, icon="add")
 
     def register_team(self):
-        text = 'Register a team'
-        return Link('/people/+newteam', text, icon='add')
+        text = "Register a team"
+        return Link("/people/+newteam", text, icon="add")
 
-    @enabled_with_permission('launchpad.Admin')
+    @enabled_with_permission("launchpad.Admin")
     def register_distribution(self):
-        text = 'Register a distribution'
-        return Link('/distros/+add', text, icon='add')
+        text = "Register a distribution"
+        return Link("/distros/+add", text, icon="add")
 
     def create_account(self):
-        text = 'Create an account'
+        text = "Create an account"
         # Only enable this link for anonymous users.
         enabled = self.user is None
-        return Link('/people/+login', text, icon='add', enabled=enabled)
+        return Link("/people/+login", text, icon="add", enabled=enabled)
 
-    @enabled_with_permission('launchpad.View')
+    @enabled_with_permission("launchpad.View")
     def request_merge(self):
-        text = 'Request a merge'
-        return Link('/people/+requestmerge', text, icon='edit')
+        text = "Request a merge"
+        return Link("/people/+requestmerge", text, icon="edit")
 
-    @enabled_with_permission('launchpad.Moderate')
+    @enabled_with_permission("launchpad.Moderate")
     def admin_merge_people(self):
-        text = 'Merge people'
-        return Link('/people/+adminpeoplemerge', text, icon='edit')
+        text = "Merge people"
+        return Link("/people/+adminpeoplemerge", text, icon="edit")
 
-    @enabled_with_permission('launchpad.Moderate')
+    @enabled_with_permission("launchpad.Moderate")
     def admin_merge_teams(self):
-        text = 'Merge teams'
-        return Link('/people/+adminteammerge', text, icon='edit')
+        text = "Merge teams"
+        return Link("/people/+adminteammerge", text, icon="edit")
 
 
 class IRegistryCollectionNavigationMenu(Interface):
@@ -81,15 +81,15 @@ class RegistryCollectionNavigationMenu(NavigationMenu, TopLevelMenuMixin):
     """Navigation menu for top level registry collections."""
 
     usedfor = IRegistryCollectionNavigationMenu
-    facet = 'overview'
+    facet = "overview"
 
     links = [
-        'projects',
-        'project_groups',
-        'distributions',
-        'people',
-        'meetings',
-        ]
+        "projects",
+        "project_groups",
+        "distributions",
+        "people",
+        "meetings",
+    ]
 
 
 class RegistryCollectionActionMenuBase(NavigationMenu, TopLevelMenuMixin):
@@ -102,4 +102,5 @@ class RegistryCollectionActionMenuBase(NavigationMenu, TopLevelMenuMixin):
     You should also set the `links` attribute to get just the menu items you
     want for the collection's overview page.
     """
-    facet = 'overview'
+
+    facet = "overview"
diff --git a/lib/lp/registry/browser/milestone.py b/lib/lp/registry/browser/milestone.py
index 865b7f9..e6926a3 100644
--- a/lib/lp/registry/browser/milestone.py
+++ b/lib/lp/registry/browser/milestone.py
@@ -4,56 +4,50 @@
 """Milestone views."""
 
 __all__ = [
-    'ISearchMilestoneTagsForm',
-    'MilestoneAddView',
-    'MilestoneBreadcrumb',
-    'MilestoneContextMenu',
-    'MilestoneDeleteView',
-    'MilestoneEditView',
-    'MilestoneInlineNavigationMenu',
-    'MilestoneNavigation',
-    'MilestoneOverviewNavigationMenu',
-    'MilestoneSetNavigation',
-    'MilestoneTagView',
-    'MilestoneWithoutCountsView',
-    'MilestoneView',
-    'MilestoneViewMixin',
-    'ObjectMilestonesView',
-    ]
+    "ISearchMilestoneTagsForm",
+    "MilestoneAddView",
+    "MilestoneBreadcrumb",
+    "MilestoneContextMenu",
+    "MilestoneDeleteView",
+    "MilestoneEditView",
+    "MilestoneInlineNavigationMenu",
+    "MilestoneNavigation",
+    "MilestoneOverviewNavigationMenu",
+    "MilestoneSetNavigation",
+    "MilestoneTagView",
+    "MilestoneWithoutCountsView",
+    "MilestoneView",
+    "MilestoneViewMixin",
+    "ObjectMilestonesView",
+]
 
 
 from zope.component import getUtility
 from zope.formlib import form
-from zope.interface import (
-    implementer,
-    Interface,
-    )
-from zope.schema import (
-    Choice,
-    TextLine,
-    )
+from zope.interface import Interface, implementer
+from zope.schema import Choice, TextLine
 
 from lp import _
 from lp.app.browser.informationtype import InformationTypePortletMixin
 from lp.app.browser.launchpadform import (
-    action,
     LaunchpadEditFormView,
     LaunchpadFormView,
+    action,
     safe_action,
-    )
+)
 from lp.app.widgets.date import DateWidget
 from lp.bugs.browser.buglisting import BugTaskListingItem
 from lp.bugs.browser.structuralsubscription import (
-    expose_structural_subscription_data_to_js,
     StructuralSubscriptionMenuMixin,
     StructuralSubscriptionTargetTraversalMixin,
-    )
+    expose_structural_subscription_data_to_js,
+)
 from lp.bugs.interfaces.bugtask import IBugTaskSet
 from lp.registry.browser import (
+    RegistryDeleteViewMixin,
     add_subscribe_link,
     get_status_counts,
-    RegistryDeleteViewMixin,
-    )
+)
 from lp.registry.browser.product import ProductDownloadFileMixin
 from lp.registry.interfaces.distroseries import IDistroSeries
 from lp.registry.interfaces.milestone import (
@@ -62,22 +56,22 @@ from lp.registry.interfaces.milestone import (
     IMilestoneData,
     IMilestoneSet,
     IProjectGroupMilestone,
-    )
+)
 from lp.registry.interfaces.milestonetag import IProjectGroupMilestoneTag
 from lp.registry.interfaces.person import IPersonSet
 from lp.registry.interfaces.product import IProduct
 from lp.registry.model.milestonetag import (
     ProjectGroupMilestoneTag,
     validate_tags,
-    )
+)
 from lp.services.propertycache import cachedproperty
 from lp.services.webapp import (
-    canonical_url,
-    enabled_with_permission,
     GetitemNavigation,
     LaunchpadView,
     Navigation,
-    )
+    canonical_url,
+    enabled_with_permission,
+)
 from lp.services.webapp.authorization import precache_permission_for_objects
 from lp.services.webapp.breadcrumb import Breadcrumb
 from lp.services.webapp.menu import (
@@ -85,17 +79,20 @@ from lp.services.webapp.menu import (
     ContextMenu,
     Link,
     NavigationMenu,
-    )
+)
 
 
 class MilestoneSetNavigation(GetitemNavigation):
     """The navigation to traverse to milestones."""
+
     usedfor = IMilestoneSet
 
 
-class MilestoneNavigation(Navigation,
-    StructuralSubscriptionTargetTraversalMixin):
+class MilestoneNavigation(
+    Navigation, StructuralSubscriptionTargetTraversalMixin
+):
     """The navigation to traverse to a milestone."""
+
     usedfor = IMilestoneData
 
 
@@ -105,7 +102,7 @@ class MilestoneBreadcrumb(Breadcrumb):
     @property
     def text(self):
         milestone = IMilestoneData(self.context)
-        if hasattr(milestone, 'code_name') and milestone.code_name:
+        if hasattr(milestone, "code_name") and milestone.code_name:
             return '%s "%s"' % (milestone.name, milestone.code_name)
         else:
             return milestone.name
@@ -114,72 +111,81 @@ class MilestoneBreadcrumb(Breadcrumb):
 class MilestoneLinkMixin(StructuralSubscriptionMenuMixin):
     """The menu for this milestone."""
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def edit(self):
         """The link to edit this milestone."""
-        text = 'Change details'
+        text = "Change details"
         # ProjectMilestones are virtual milestones and do not have
         # any properties which can be edited.
         enabled = not IProjectGroupMilestone.providedBy(self.context)
         summary = "Edit this milestone"
         return Link(
-            '+edit', text, icon='edit', summary=summary, enabled=enabled)
+            "+edit", text, icon="edit", summary=summary, enabled=enabled
+        )
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def create_release(self):
         """The link to create a release for this milestone."""
-        text = 'Create release'
-        summary = 'Create a release from this milestone'
+        text = "Create release"
+        summary = "Create a release from this milestone"
         # Releases only exist for products.
         # A milestone can only have a single product release.
-        enabled = (IProduct.providedBy(self.context.target)
-                   and self.context.product_release is None)
-        return Link(
-            '+addrelease', text, summary, icon='add', enabled=enabled)
+        enabled = (
+            IProduct.providedBy(self.context.target)
+            and self.context.product_release is None
+        )
+        return Link("+addrelease", text, summary, icon="add", enabled=enabled)
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def delete(self):
         """The link to delete this milestone."""
-        text = 'Delete milestone'
+        text = "Delete milestone"
         # ProjectMilestones are virtual.
         enabled = not IProjectGroupMilestone.providedBy(self.context)
         summary = "Delete milestone"
         return Link(
-            '+delete', text, icon='trash-icon',
-            summary=summary, enabled=enabled)
+            "+delete",
+            text,
+            icon="trash-icon",
+            summary=summary,
+            enabled=enabled,
+        )
 
 
 class MilestoneContextMenu(ContextMenu, MilestoneLinkMixin):
     """The menu for this milestone."""
+
     usedfor = IMilestoneData
 
     @cachedproperty
     def links(self):
-        links = ['edit']
+        links = ["edit"]
         add_subscribe_link(links)
-        links.append('create_release')
+        links.append("create_release")
         return links
 
 
 class MilestoneOverviewNavigationMenu(NavigationMenu, MilestoneLinkMixin):
     """Overview navigation menu for `IAbstractMilestone` objects."""
+
     usedfor = IAbstractMilestone
-    facet = 'overview'
+    facet = "overview"
 
     @cachedproperty
     def links(self):
-        links = ['edit', 'delete']
+        links = ["edit", "delete"]
         add_subscribe_link(links)
         return links
 
 
 class MilestoneOverviewMenu(ApplicationMenu, MilestoneLinkMixin):
     """Overview  menus for `IMilestone` objects."""
+
     # This menu must not contain 'subscribe' because the link state is too
     # costly to calculate when this menu is used with a list of milestones.
     usedfor = IMilestoneData
-    facet = 'overview'
-    links = ('edit', 'create_release')
+    facet = "overview"
+    links = ("edit", "create_release")
 
 
 class IMilestoneInline(Interface):
@@ -188,9 +194,10 @@ class IMilestoneInline(Interface):
 
 class MilestoneInlineNavigationMenu(NavigationMenu, MilestoneLinkMixin):
     """An inline navigation menus for milestone views."""
+
     usedfor = IMilestoneInline
-    facet = 'overview'
-    links = ('edit', )
+    facet = "overview"
+    links = ("edit",)
 
 
 class MilestoneViewMixin:
@@ -224,22 +231,29 @@ class MilestoneViewMixin:
         # NB: this is in principle unneeded due to injection of permission in
         # the model layer now.
         precache_permission_for_objects(
-            self.request, 'launchpad.View', non_conjoined_replicas)
+            self.request, "launchpad.View", non_conjoined_replicas
+        )
         precache_permission_for_objects(
-            self.request, 'launchpad.View',
-            [task.bug for task in non_conjoined_replicas])
+            self.request,
+            "launchpad.View",
+            [task.bug for task in non_conjoined_replicas],
+        )
         # We want the assignees loaded as we show them in the milestone home
         # page.
-        list(getUtility(IPersonSet).getPrecachedPersonsFromIDs(
-            [bug.assignee_id for bug in non_conjoined_replicas],
-            need_validity=True))
+        list(
+            getUtility(IPersonSet).getPrecachedPersonsFromIDs(
+                [bug.assignee_id for bug in non_conjoined_replicas],
+                need_validity=True,
+            )
+        )
         return non_conjoined_replicas
 
     @cachedproperty
     def _bug_badge_properties(self):
         """The badges for each bug associates with this milestone."""
         return getUtility(IBugTaskSet).getBugTaskBadgeProperties(
-            self._bugtasks)
+            self._bugtasks
+        )
 
     @cachedproperty
     def _bug_task_tags(self):
@@ -256,9 +270,13 @@ class MilestoneViewMixin:
         tags = self._bug_task_tags.get(bugtask.id, ())
         people = self._bug_task_people
         return BugTaskListingItem(
-            bugtask, badge_property['has_branch'],
-            badge_property['has_specification'], badge_property['has_patch'],
-            tags, people)
+            bugtask,
+            badge_property["has_branch"],
+            badge_property["has_specification"],
+            badge_property["has_patch"],
+            tags,
+            people,
+        )
 
     @cachedproperty
     def bugtasks(self):
@@ -270,35 +288,36 @@ class MilestoneViewMixin:
         """The formatted count of bugs for this milestone."""
         count = len(self.bugtasks)
         if count == 1:
-            return '1 bug'
+            return "1 bug"
         else:
-            return '%d bugs' % count
+            return "%d bugs" % count
 
     @property
     def bugtask_status_counts(self):
         """A list StatusCounts summarising the targeted bugtasks."""
-        return get_status_counts(self.bugtasks, 'status')
+        return get_status_counts(self.bugtasks, "status")
 
     @property
     def specification_count_text(self):
         """The formatted count of specifications for this milestone."""
         count = len(self.specifications)
         if count == 1:
-            return '1 blueprint'
+            return "1 blueprint"
         else:
-            return '%d blueprints' % count
+            return "%d blueprints" % count
 
     @property
     def specification_status_counts(self):
         """A list StatusCounts summarising the targeted specification."""
-        return get_status_counts(self.specifications, 'implementation_status')
+        return get_status_counts(self.specifications, "implementation_status")
 
     @cachedproperty
     def assignment_counts(self):
         """The counts of the items assigned to users."""
         all_assignments = self.bugtasks + self.specifications
         return get_status_counts(
-            all_assignments, 'assignee', key='displayname')
+            all_assignments, "assignee", key="displayname"
+        )
 
     @cachedproperty
     def user_counts(self):
@@ -306,20 +325,22 @@ class MilestoneViewMixin:
         all_assignments = []
         if self.user:
             for status_count in get_status_counts(
-                self.specifications, 'assignee', key='displayname'):
+                self.specifications, "assignee", key="displayname"
+            ):
                 if status_count.status == self.user:
                     if status_count.count == 1:
-                        status_count.status = 'blueprint'
+                        status_count.status = "blueprint"
                     else:
-                        status_count.status = 'blueprints'
+                        status_count.status = "blueprints"
                     all_assignments.append(status_count)
             for status_count in get_status_counts(
-                self.bugtasks, 'assignee', key='displayname'):
+                self.bugtasks, "assignee", key="displayname"
+            ):
                 if status_count.status == self.user:
                     if status_count.count == 1:
-                        status_count.status = 'bug'
+                        status_count.status = "bug"
                     else:
-                        status_count.status = 'bugs'
+                        status_count.status = "bugs"
                     all_assignments.append(status_count)
             return all_assignments
         return all_assignments
@@ -339,8 +360,9 @@ class MilestoneViewMixin:
         Return true, if the current milestone is a project milestone or
         a project milestone tag, else return False."""
         return (
-            IProjectGroupMilestone.providedBy(self.context) or
-            self.is_project_milestone_tag)
+            IProjectGroupMilestone.providedBy(self.context)
+            or self.is_project_milestone_tag
+        )
 
     @property
     def has_bugs_or_specs(self):
@@ -350,9 +372,13 @@ class MilestoneViewMixin:
 
 @implementer(IMilestoneInline)
 class MilestoneView(
-    LaunchpadView, MilestoneViewMixin, ProductDownloadFileMixin,
-    InformationTypePortletMixin):
+    LaunchpadView,
+    MilestoneViewMixin,
+    ProductDownloadFileMixin,
+    InformationTypePortletMixin,
+):
     """A View for listing milestones and releases."""
+
     show_series_context = False
 
     def __init__(self, context, request):
@@ -379,7 +405,8 @@ class MilestoneView(
         self.form = self.request.form
         self.processDeleteFiles()
         expose_structural_subscription_data_to_js(
-            self.context, self.request, self.user)
+            self.context, self.request, self.user
+        )
 
     def getReleases(self):
         """See `ProductDownloadFileMixin`."""
@@ -403,7 +430,8 @@ class MilestoneView(
     def total_downloads(self):
         """Total downloads of files associated with this milestone."""
         return sum(
-            file.libraryfile.hits for file in self.product_release_files)
+            file.libraryfile.hits for file in self.product_release_files
+        )
 
     @property
     def is_distroseries_milestone(self):
@@ -422,7 +450,6 @@ class MilestoneWithoutCountsView(MilestoneView):
 
 
 class MilestoneTagBase:
-
     def extendFields(self):
         """See `LaunchpadFormView`.
 
@@ -430,14 +457,18 @@ class MilestoneTagBase:
         on the interface.
         """
         tag_entry = TextLine(
-            __name__='tags', title='Tags', required=False,
-            constraint=lambda value: validate_tags(value.split()))
+            __name__="tags",
+            title="Tags",
+            required=False,
+            constraint=lambda value: validate_tags(value.split()),
+        )
         self.form_fields += form.Fields(
-            tag_entry, render_context=self.render_context)
+            tag_entry, render_context=self.render_context
+        )
         # Make an instance attribute to avoid mutating the class attribute.
-        self.field_names = getattr(self, '_field_names', self.field_names)[:]
+        self.field_names = getattr(self, "_field_names", self.field_names)[:]
         # Insert the tags field before the summary.
-        summary_index = self.field_names.index('summary')
+        summary_index = self.field_names.index("summary")
         self.field_names.insert(summary_index, tag_entry.__name__)
 
 
@@ -445,20 +476,21 @@ class MilestoneAddView(MilestoneTagBase, LaunchpadFormView):
     """A view for creating a new Milestone."""
 
     schema = IMilestone
-    field_names = ['name', 'code_name', 'dateexpected', 'summary']
+    field_names = ["name", "code_name", "dateexpected", "summary"]
     label = "Register a new milestone"
 
     custom_widget_dateexpected = DateWidget
 
-    @action(_('Register Milestone'), name='register')
+    @action(_("Register Milestone"), name="register")
     def register_action(self, action, data):
         """Use the newMilestone method on the context to make a milestone."""
         milestone = self.context.newMilestone(
-            name=data.get('name'),
-            code_name=data.get('code_name'),
-            dateexpected=data.get('dateexpected'),
-            summary=data.get('summary'))
-        tags = data.get('tags')
+            name=data.get("name"),
+            code_name=data.get("code_name"),
+            dateexpected=data.get("dateexpected"),
+            summary=data.get("summary"),
+        )
+        tags = data.get("tags")
         if tags:
             milestone.setTags(tags.lower().split(), self.user)
         self.next_url = canonical_url(self.context)
@@ -501,17 +533,17 @@ class MilestoneEditView(MilestoneTagBase, LaunchpadEditFormView):
         its productseries. The distribution milestone may change its
         distroseries.
         """
-        names = ['name', 'code_name', 'active', 'dateexpected', 'summary']
+        names = ["name", "code_name", "active", "dateexpected", "summary"]
         if self.context.product is None:
             # This is a distribution milestone.
-            names.append('distroseries')
+            names.append("distroseries")
         else:
-            names.append('productseries')
+            names.append("productseries")
         return names
 
     @property
     def initial_values(self):
-        return {'tags': ' '.join(self.context.getTags())}
+        return {"tags": " ".join(self.context.getTags())}
 
     def setUpFields(self):
         """See `LaunchpadFormView`.
@@ -524,20 +556,22 @@ class MilestoneEditView(MilestoneTagBase, LaunchpadEditFormView):
         if self.context.product is None:
             # This is a distribution milestone.
             choice = Choice(
-                __name__='distroseries', vocabulary="FilteredDistroSeries")
+                __name__="distroseries", vocabulary="FilteredDistroSeries"
+            )
         else:
             choice = Choice(
-                __name__='productseries', vocabulary="FilteredProductSeries")
+                __name__="productseries", vocabulary="FilteredProductSeries"
+            )
         choice.title = _("Series")
         choice.description = _("The series for which this is a milestone.")
         field = form.Fields(choice, render_context=self.render_context)
         # Remove the schema's field, then add back the replacement field.
         self.form_fields = self.form_fields.omit(choice.__name__) + field
 
-    @action(_('Update'), name='update')
+    @action(_("Update"), name="update")
     def update_action(self, action, data):
         """Update the milestone."""
-        tags = data.pop('tags') or ''
+        tags = data.pop("tags") or ""
         self.updateContextFromData(data)
         self.context.setTags(tags.lower().split(), self.user)
         self.next_url = canonical_url(self.context)
@@ -545,6 +579,7 @@ class MilestoneEditView(MilestoneTagBase, LaunchpadEditFormView):
 
 class MilestoneDeleteView(LaunchpadFormView, RegistryDeleteViewMixin):
     """A view for deleting an `IMilestone`."""
+
     schema = IMilestone
     field_names = []
 
@@ -555,7 +590,7 @@ class MilestoneDeleteView(LaunchpadFormView, RegistryDeleteViewMixin):
     @property
     def label(self):
         """The form label."""
-        return 'Delete %s' % self.context.title
+        return "Delete %s" % self.context.title
 
     @cachedproperty
     def bugtasks(self):
@@ -577,7 +612,7 @@ class MilestoneDeleteView(LaunchpadFormView, RegistryDeleteViewMixin):
         """The list of `IProductReleaseFile`s related to the milestone."""
         return self._getProductReleaseFiles(self.context)
 
-    @action('Delete Milestone', name='delete')
+    @action("Delete Milestone", name="delete")
     def delete_action(self, action, data):
         """Delete the milestone anddelete or unlink subordinate objects."""
         # Any associated bugtasks and specifications are untargeted.
@@ -585,7 +620,8 @@ class MilestoneDeleteView(LaunchpadFormView, RegistryDeleteViewMixin):
         name = self.context.name
         self._deleteMilestone(self.context)
         self.request.response.addInfoNotification(
-            "Milestone %s deleted." % name)
+            "Milestone %s deleted." % name
+        )
         self.next_url = canonical_url(series)
 
 
@@ -593,15 +629,20 @@ class ISearchMilestoneTagsForm(Interface):
     """Schema for the search milestone tags form."""
 
     tags = TextLine(
-        title=_('Search by tags'),
-        description=_('Insert space separated tag names'),
-        required=True, min_length=2, max_length=64,
-        constraint=lambda value: validate_tags(value.split()))
+        title=_("Search by tags"),
+        description=_("Insert space separated tag names"),
+        required=True,
+        min_length=2,
+        max_length=64,
+        constraint=lambda value: validate_tags(value.split()),
+    )
 
 
 class MilestoneTagView(
-    LaunchpadFormView, MilestoneViewMixin, ProductDownloadFileMixin):
+    LaunchpadFormView, MilestoneViewMixin, ProductDownloadFileMixin
+):
     """A View for listing bugtasks and specification for milestone tags."""
+
     schema = ISearchMilestoneTagsForm
 
     def __init__(self, context, request):
@@ -617,12 +658,12 @@ class MilestoneTagView(
     @property
     def initial_values(self):
         """Set the initial value of the search tags field."""
-        return {'tags': ' '.join(self.context.tags)}
+        return {"tags": " ".join(self.context.tags)}
 
     @safe_action
-    @action('Search Milestone Tags', name='search')
+    @action("Search Milestone Tags", name="search")
     def search_by_tags(self, action, data):
-        tags = data['tags'].split()
+        tags = data["tags"].split()
         milestone_tag = ProjectGroupMilestoneTag(self.context.target, tags)
         self.next_url = canonical_url(milestone_tag, request=self.request)
 
@@ -630,7 +671,7 @@ class MilestoneTagView(
 class ObjectMilestonesView(LaunchpadView):
     """A view for listing the milestones for any `IHasMilestones` object"""
 
-    label = 'Milestones'
+    label = "Milestones"
 
     @cachedproperty
     def milestones(self):
diff --git a/lib/lp/registry/browser/nameblacklist.py b/lib/lp/registry/browser/nameblacklist.py
index a653c12..2c2e175 100644
--- a/lib/lp/registry/browser/nameblacklist.py
+++ b/lib/lp/registry/browser/nameblacklist.py
@@ -2,45 +2,39 @@
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 __all__ = [
-    'NameBlacklistAddView',
-    'NameBlacklistEditView',
-    'NameBlacklistNavigationMenu',
-    'NameBlacklistSetNavigationMenu',
-    'NameBlacklistSetView',
-    ]
+    "NameBlacklistAddView",
+    "NameBlacklistEditView",
+    "NameBlacklistNavigationMenu",
+    "NameBlacklistSetNavigationMenu",
+    "NameBlacklistSetView",
+]
 
 import re
 
-from zope.component import (
-    adapter,
-    getUtility,
-    )
+from zope.component import adapter, getUtility
 from zope.formlib.widget import CustomWidgetFactory
 from zope.formlib.widgets import TextWidget
 from zope.interface import implementer
 
-from lp.app.browser.launchpadform import (
-    action,
-    LaunchpadFormView,
-    )
+from lp.app.browser.launchpadform import LaunchpadFormView, action
 from lp.registry.browser import RegistryEditFormView
 from lp.registry.interfaces.nameblacklist import (
     INameBlacklist,
     INameBlacklistSet,
-    )
+)
 from lp.services.webapp.breadcrumb import Breadcrumb
 from lp.services.webapp.interfaces import IBreadcrumb
 from lp.services.webapp.menu import (
     ApplicationMenu,
-    enabled_with_permission,
     Link,
     NavigationMenu,
-    )
+    enabled_with_permission,
+)
 from lp.services.webapp.publisher import (
-    canonical_url,
     LaunchpadView,
     Navigation,
-    )
+    canonical_url,
+)
 
 
 class NameBlacklistValidationMixin:
@@ -48,31 +42,32 @@ class NameBlacklistValidationMixin:
 
     def validate(self, data):
         """Validate regular expression."""
-        regexp = data['regexp']
+        regexp = data["regexp"]
         try:
             re.compile(regexp)
             name_blacklist_set = getUtility(INameBlacklistSet)
-            if (INameBlacklistSet.providedBy(self.context)
-                or self.context.regexp != regexp):
+            if (
+                INameBlacklistSet.providedBy(self.context)
+                or self.context.regexp != regexp
+            ):
                 # Check if the regular expression already exists if a
                 # new expression is being created or if an existing
                 # regular expression has been modified.
                 if name_blacklist_set.getByRegExp(regexp) is not None:
                     self.setFieldError(
-                        'regexp',
-                        'This regular expression already exists.')
+                        "regexp", "This regular expression already exists."
+                    )
         except re.error as e:
-            self.setFieldError(
-                'regexp',
-                'Invalid regular expression: %s' % e)
+            self.setFieldError("regexp", "Invalid regular expression: %s" % e)
 
 
-class NameBlacklistEditView(NameBlacklistValidationMixin,
-                            RegistryEditFormView):
+class NameBlacklistEditView(
+    NameBlacklistValidationMixin, RegistryEditFormView
+):
     """View for editing a blacklist expression."""
 
     schema = INameBlacklist
-    field_names = ['regexp', 'admin', 'comment']
+    field_names = ["regexp", "admin", "comment"]
     label = "Edit a blacklist expression"
     page_title = label
 
@@ -87,7 +82,7 @@ class NameBlacklistAddView(NameBlacklistValidationMixin, LaunchpadFormView):
     """View for adding a blacklist expression."""
 
     schema = INameBlacklist
-    field_names = ['regexp', 'admin', 'comment']
+    field_names = ["regexp", "admin", "comment"]
     label = "Add a new blacklist expression"
     page_title = label
 
@@ -100,24 +95,24 @@ class NameBlacklistAddView(NameBlacklistValidationMixin, LaunchpadFormView):
 
     next_url = cancel_url
 
-    @action("Add to blacklist", name='add')
+    @action("Add to blacklist", name="add")
     def add_action(self, action, data):
         name_blacklist_set = getUtility(INameBlacklistSet)
         name_blacklist_set.create(
-            regexp=data['regexp'],
-            comment=data['comment'],
-            admin=data['admin'],
-            )
+            regexp=data["regexp"],
+            comment=data["comment"],
+            admin=data["admin"],
+        )
         self.request.response.addInfoNotification(
             'Regular expression "%s" has been added to the name blacklist.'
-            % data['regexp'])
+            % data["regexp"]
+        )
 
 
 class NameBlacklistSetView(LaunchpadView):
     """View for /+nameblacklists top level collection."""
 
-    label = (
-        'Blacklist for names of Launchpad pillars and persons')
+    label = "Blacklist for names of Launchpad pillars and persons"
     page_title = label
 
 
@@ -131,28 +126,30 @@ class NameBlacklistSetNavigation(Navigation):
 
 class NameBlacklistSetNavigationMenu(NavigationMenu):
     """Action menu for NameBlacklistSet."""
+
     usedfor = INameBlacklistSet
-    facet = 'overview'
+    facet = "overview"
     links = [
-        'add_blacklist_expression',
-        ]
+        "add_blacklist_expression",
+    ]
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def add_blacklist_expression(self):
-        return Link('+add', 'Add blacklist expression', icon='add')
+        return Link("+add", "Add blacklist expression", icon="add")
 
 
 class NameBlacklistNavigationMenu(ApplicationMenu):
     """Action menu for NameBlacklist."""
+
     usedfor = INameBlacklist
-    facet = 'overview'
+    facet = "overview"
     links = [
-        'edit_blacklist_expression',
-        ]
+        "edit_blacklist_expression",
+    ]
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def edit_blacklist_expression(self):
-        return Link('+edit', 'Edit blacklist expression', icon='edit')
+        return Link("+edit", "Edit blacklist expression", icon="edit")
 
 
 @adapter(INameBlacklistSet)
diff --git a/lib/lp/registry/browser/objectreassignment.py b/lib/lp/registry/browser/objectreassignment.py
index 014f9a2..e9d079d 100644
--- a/lib/lp/registry/browser/objectreassignment.py
+++ b/lib/lp/registry/browser/objectreassignment.py
@@ -12,27 +12,15 @@ __all__ = ["ObjectReassignmentView"]
 
 from zope.component import getUtility
 from zope.formlib.form import FormFields
-from zope.formlib.interfaces import (
-    ConversionError,
-    WidgetInputError,
-    )
+from zope.formlib.interfaces import ConversionError, WidgetInputError
 from zope.schema import Choice
-from zope.schema.vocabulary import (
-    SimpleTerm,
-    SimpleVocabulary,
-    )
+from zope.schema.vocabulary import SimpleTerm, SimpleVocabulary
 
 from lp import _
-from lp.app.browser.launchpadform import (
-    action,
-    LaunchpadFormView,
-    )
+from lp.app.browser.launchpadform import LaunchpadFormView, action
 from lp.app.validators.name import valid_name
 from lp.app.widgets.itemswidgets import LaunchpadRadioWidget
-from lp.registry.interfaces.person import (
-    IObjectReassignment,
-    IPersonSet,
-    )
+from lp.registry.interfaces.person import IObjectReassignment, IPersonSet
 from lp.services.webapp import canonical_url
 
 
@@ -55,8 +43,8 @@ class ObjectReassignmentView(LaunchpadFormView):
     contextName property in your subclass.
     """
 
-    ownerOrMaintainerAttr = 'owner'
-    ownerOrMaintainerName = 'owner'
+    ownerOrMaintainerAttr = "owner"
+    ownerOrMaintainerName = "owner"
     # Called after changing the owner if it is overridden in a subclass.
     callback = None
 
@@ -66,33 +54,42 @@ class ObjectReassignmentView(LaunchpadFormView):
     @property
     def label(self):
         """The form label."""
-        return 'Change the %s of %s' % (
-            self.ownerOrMaintainerName, self.contextName)
+        return "Change the %s of %s" % (
+            self.ownerOrMaintainerName,
+            self.contextName,
+        )
 
     page_title = label
 
     def setUpFields(self):
         super().setUpFields()
         self.form_fields = FormFields(
-            self.form_fields, self.auto_create_team_field)
+            self.form_fields, self.auto_create_team_field
+        )
 
     @property
     def auto_create_team_field(self):
         terms = [
-            SimpleTerm('existing', token='existing',
-                       title='An existing person or team'),
-            SimpleTerm('new', token='new',
-                       title="A new team I'm creating here"),
-            ]
+            SimpleTerm(
+                "existing",
+                token="existing",
+                title="An existing person or team",
+            ),
+            SimpleTerm(
+                "new", token="new", title="A new team I'm creating here"
+            ),
+        ]
         return Choice(
-            __name__='existing',
-            title=_('This is'),
+            __name__="existing",
+            title=_("This is"),
             source=SimpleVocabulary(terms),
-            default='existing',
+            default="existing",
             description=_(
-              "The new team's name must begin with a lower-case letter "
-              "or number, and contain only letters, numbers, dots, hyphens, "
-              "or plus signs."))
+                "The new team's name must begin with a lower-case letter "
+                "or number, and contain only letters, numbers, dots, hyphens, "
+                "or plus signs."
+            ),
+        )
 
     @property
     def ownerOrMaintainer(self):
@@ -110,12 +107,12 @@ class ObjectReassignmentView(LaunchpadFormView):
 
     @property
     def owner_widget(self):
-        return self.widgets['owner']
+        return self.widgets["owner"]
 
     @action("Change", name="change")
     def changeOwner(self, action, data):
         """Change the owner of self.context to the one choosen by the user."""
-        newOwner = data['owner']
+        newOwner = data["owner"]
         oldOwner = getattr(self.context, self.ownerOrMaintainerAttr)
         setattr(self.context, self.ownerOrMaintainerAttr, newOwner)
         if callable(self.callback):
@@ -145,12 +142,13 @@ class ObjectReassignmentView(LaunchpadFormView):
         owner_name = request.form.get(self.owner_widget.name)
         if not owner_name:
             self.setFieldError(
-                'owner',
+                "owner",
                 "You have to specify the name of the person/team that's "
-                "going to be the new %s." % self.ownerOrMaintainerName)
+                "going to be the new %s." % self.ownerOrMaintainerName,
+            )
             return None
 
-        if request.form.get('field.existing') == 'existing':
+        if request.form.get("field.existing") == "existing":
             try:
                 # By getting the owner using getInputValue() we make sure
                 # it's valid according to the vocabulary of self.schema's
@@ -158,35 +156,40 @@ class ObjectReassignmentView(LaunchpadFormView):
                 owner = self.owner_widget.getInputValue()
             except WidgetInputError:
                 self.setFieldError(
-                    'owner',
+                    "owner",
                     "The person/team named '%s' is not a valid owner for %s."
-                    % (owner_name, self.contextName))
+                    % (owner_name, self.contextName),
+                )
                 return None
             except ConversionError:
                 self.setFieldError(
                     self.ownerOrMaintainerName,
                     "There's no person/team named '%s' in Launchpad."
-                    % owner_name)
+                    % owner_name,
+                )
                 return None
         else:
             if personset.getByName(owner_name):
                 self.setFieldError(
-                    'owner',
+                    "owner",
                     "There's already a person/team with the name '%s' in "
                     "Launchpad. Please choose a different name or select "
                     "the option to make that person/team the new owner, "
-                    "if that's what you want." % owner_name)
+                    "if that's what you want." % owner_name,
+                )
                 return None
 
             if not valid_name(owner_name):
                 self.setFieldError(
-                    'owner',
+                    "owner",
                     "'%s' is not a valid name for a team. Please make sure "
                     "it contains only the allowed characters and no spaces."
-                    % owner_name)
+                    % owner_name,
+                )
                 return None
 
             owner = personset.newTeam(
-                self.user, owner_name, owner_name.capitalize())
+                self.user, owner_name, owner_name.capitalize()
+            )
 
         self.validateOwner(owner)
diff --git a/lib/lp/registry/browser/ociproject.py b/lib/lp/registry/browser/ociproject.py
index 00e8024..e5d00c7 100644
--- a/lib/lp/registry/browser/ociproject.py
+++ b/lib/lp/registry/browser/ociproject.py
@@ -4,28 +4,25 @@
 """Views, menus, and traversal related to `OCIProject`s."""
 
 __all__ = [
-    'OCIProjectBreadcrumb',
-    'OCIProjectContextMenu',
-    'OCIProjectFacets',
-    'OCIProjectNavigation',
-    'OCIProjectNavigationMenu',
-    'OCIProjectURL',
-    ]
+    "OCIProjectBreadcrumb",
+    "OCIProjectContextMenu",
+    "OCIProjectFacets",
+    "OCIProjectNavigation",
+    "OCIProjectNavigationMenu",
+    "OCIProjectURL",
+]
 
-from urllib.parse import (
-    urlsplit,
-    urlunsplit,
-    )
+from urllib.parse import urlsplit, urlunsplit
 
 from breezy import urlutils
 from zope.component import getUtility
 from zope.interface import implementer
 
 from lp.app.browser.launchpadform import (
-    action,
     LaunchpadEditFormView,
     LaunchpadFormView,
-    )
+    action,
+)
 from lp.app.browser.tales import CustomizableFormatter
 from lp.app.errors import NotFoundError
 from lp.app.interfaces.headings import IHeadingBreadcrumb
@@ -36,37 +33,37 @@ from lp.oci.interfaces.ocirecipe import IOCIRecipeSet
 from lp.registry.enums import DistributionDefaultTraversalPolicy
 from lp.registry.interfaces.distribution import IDistribution
 from lp.registry.interfaces.ociproject import (
+    OCI_PROJECT_ALLOW_CREATE,
     IOCIProject,
     IOCIProjectSet,
-    OCI_PROJECT_ALLOW_CREATE,
     OCIProjectCreateFeatureDisabled,
-    )
+)
 from lp.registry.interfaces.ociprojectname import (
     IOCIProjectName,
     IOCIProjectNameSet,
-    )
+)
 from lp.registry.interfaces.product import IProduct
 from lp.services.config import config
 from lp.services.features import getFeatureFlag
 from lp.services.propertycache import cachedproperty
 from lp.services.webapp import (
-    canonical_url,
     ContextMenu,
-    enabled_with_permission,
     LaunchpadView,
     Link,
     Navigation,
     NavigationMenu,
     StandardLaunchpadFacets,
+    canonical_url,
+    enabled_with_permission,
     stepthrough,
-    )
+)
 from lp.services.webapp.authorization import check_permission
 from lp.services.webapp.batching import BatchNavigator
 from lp.services.webapp.breadcrumb import Breadcrumb
 from lp.services.webapp.interfaces import (
     ICanonicalUrlData,
     IMultiFacetedBreadcrumb,
-    )
+)
 
 
 @implementer(ICanonicalUrlData)
@@ -91,79 +88,89 @@ class OCIProjectURL:
     def path(self):
         if self.context.distribution is not None:
             policy = self.context.distribution.default_traversal_policy
-            if (policy == DistributionDefaultTraversalPolicy.OCI_PROJECT and
-                    not self.context.distribution.redirect_default_traversal):
+            if (
+                policy == DistributionDefaultTraversalPolicy.OCI_PROJECT
+                and not self.context.distribution.redirect_default_traversal
+            ):
                 return self.context.name
         return "+oci/%s" % self.context.name
 
 
 def getPillarFieldName(pillar):
     if IDistribution.providedBy(pillar):
-        return 'distribution'
+        return "distribution"
     elif IProduct.providedBy(pillar):
-        return 'project'
-    raise NotImplementedError("This view only supports distribution or "
-                              "project as pillars for OCIProject.")
+        return "project"
+    raise NotImplementedError(
+        "This view only supports distribution or "
+        "project as pillars for OCIProject."
+    )
 
 
 class OCIProjectAddView(LaunchpadFormView):
 
     schema = IOCIProjectName
-    field_names = ['name']
+    field_names = ["name"]
 
     def initialize(self):
-        if (not getFeatureFlag(OCI_PROJECT_ALLOW_CREATE) and not
-                self.context.canAdministerOCIProjects(self.user)):
+        if not getFeatureFlag(
+            OCI_PROJECT_ALLOW_CREATE
+        ) and not self.context.canAdministerOCIProjects(self.user):
             raise OCIProjectCreateFeatureDisabled
         super().initialize()
 
     @action("Create OCI Project", name="create")
     def create_action(self, action, data):
         """Create a new OCI Project."""
-        name = data.get('name')
-        oci_project_name = getUtility(
-            IOCIProjectNameSet).getOrCreateByName(name)
+        name = data.get("name")
+        oci_project_name = getUtility(IOCIProjectNameSet).getOrCreateByName(
+            name
+        )
         oci_project = getUtility(IOCIProjectSet).new(
-            registrant=self.user,
-            pillar=self.context,
-            name=oci_project_name)
+            registrant=self.user, pillar=self.context, name=oci_project_name
+        )
         self.next_url = canonical_url(oci_project)
 
     def validate(self, data):
         super().validate(data)
-        name = data.get('name', None)
-        oci_project_name = getUtility(
-            IOCIProjectNameSet).getOrCreateByName(name)
+        name = data.get("name", None)
+        oci_project_name = getUtility(IOCIProjectNameSet).getOrCreateByName(
+            name
+        )
 
         oci_project = getUtility(IOCIProjectSet).getByPillarAndName(
-            self.context, oci_project_name.name)
+            self.context, oci_project_name.name
+        )
         if oci_project:
             pillar_type = getPillarFieldName(self.context)
-            msg = ('There is already an OCI project in %s %s with this name.'
-                   % (pillar_type, self.context.display_name))
-            self.setFieldError('name', msg)
+            msg = (
+                "There is already an OCI project in %s %s with this name."
+                % (pillar_type, self.context.display_name)
+            )
+            self.setFieldError("name", msg)
 
 
 class OCIProjectFormatterAPI(CustomizableFormatter):
     """Adapt `IOCIProject` objects to a formatted string."""
 
-    _link_summary_template = '%(displayname)s'
+    _link_summary_template = "%(displayname)s"
 
     def _link_summary_values(self):
         displayname = self._context.display_name
-        return {'displayname': displayname}
+        return {"displayname": displayname}
 
 
-class OCIProjectNavigation(TargetDefaultVCSNavigationMixin,
-                           BugTargetTraversalMixin, Navigation):
+class OCIProjectNavigation(
+    TargetDefaultVCSNavigationMixin, BugTargetTraversalMixin, Navigation
+):
 
     usedfor = IOCIProject
 
-    @stepthrough('+series')
+    @stepthrough("+series")
     def traverse_series(self, name):
         series = self.context.getSeriesByName(name)
         if series is None:
-            raise NotFoundError('%s is not a valid series name' % name)
+            raise NotFoundError("%s is not a valid series name" % name)
         return series
 
 
@@ -173,31 +180,31 @@ class OCIProjectBreadcrumb(Breadcrumb):
 
     @property
     def text(self):
-        return '%s OCI project' % self.context.name
+        return "%s OCI project" % self.context.name
 
 
 class OCIProjectFacets(StandardLaunchpadFacets):
 
     usedfor = IOCIProject
     enable_only = [
-        'overview',
-        'branches',
-        'bugs',
-        ]
+        "overview",
+        "branches",
+        "bugs",
+    ]
 
     def makeLink(self, text, context, view_name, site):
-        site = 'mainsite' if self.mainsite_only else site
+        site = "mainsite" if self.mainsite_only else site
         target = canonical_url(context, view_name=view_name, rootsite=site)
         return Link(target, text, site=site)
 
     def branches(self):
-        return self.makeLink('Code', self.context, '+code', 'code')
+        return self.makeLink("Code", self.context, "+code", "code")
 
     def bugs(self):
         """Override bugs link to show the OCIProject's bug page, instead of
         the pillar's bug page.
         """
-        return self.makeLink('Bugs', self.context, '+bugs', 'bugs')
+        return self.makeLink("Bugs", self.context, "+bugs", "bugs")
 
 
 class OCIProjectNavigationMenu(NavigationMenu):
@@ -205,23 +212,27 @@ class OCIProjectNavigationMenu(NavigationMenu):
 
     usedfor = IOCIProject
 
-    facet = 'overview'
+    facet = "overview"
 
-    links = ('edit', 'create_recipe', 'view_recipes')
+    links = ("edit", "create_recipe", "view_recipes")
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def edit(self):
-        return Link('+edit', 'Edit OCI project', icon='edit')
+        return Link("+edit", "Edit OCI project", icon="edit")
 
-    @enabled_with_permission('launchpad.AnyLegitimatePerson')
+    @enabled_with_permission("launchpad.AnyLegitimatePerson")
     def create_recipe(self):
-        return Link('+new-recipe', 'Create OCI recipe', icon='add')
+        return Link("+new-recipe", "Create OCI recipe", icon="add")
 
     def view_recipes(self):
-        enabled = not getUtility(IOCIRecipeSet).findByOCIProject(
-            self.context, visible_by_user=self.user).is_empty()
+        enabled = (
+            not getUtility(IOCIRecipeSet)
+            .findByOCIProject(self.context, visible_by_user=self.user)
+            .is_empty()
+        )
         return Link(
-            '+recipes', 'View all recipes', icon='info', enabled=enabled)
+            "+recipes", "View all recipes", icon="info", enabled=enabled
+        )
 
 
 class OCIProjectContextMenu(ContextMenu):
@@ -229,19 +240,23 @@ class OCIProjectContextMenu(ContextMenu):
 
     usedfor = IOCIProject
 
-    facet = 'overview'
+    facet = "overview"
 
-    links = ('create_recipe', 'view_recipes')
+    links = ("create_recipe", "view_recipes")
 
-    @enabled_with_permission('launchpad.AnyLegitimatePerson')
+    @enabled_with_permission("launchpad.AnyLegitimatePerson")
     def create_recipe(self):
-        return Link('+new-recipe', 'Create OCI recipe', icon='add')
+        return Link("+new-recipe", "Create OCI recipe", icon="add")
 
     def view_recipes(self):
-        enabled = not getUtility(IOCIRecipeSet).findByOCIProject(
-            self.context, visible_by_user=self.user).is_empty()
+        enabled = (
+            not getUtility(IOCIRecipeSet)
+            .findByOCIProject(self.context, visible_by_user=self.user)
+            .is_empty()
+        )
         return Link(
-            '+recipes', 'View all recipes', icon='info', enabled=enabled)
+            "+recipes", "View all recipes", icon="info", enabled=enabled
+        )
 
 
 class OCIProjectIndexView(LaunchpadView):
@@ -254,7 +269,9 @@ class OCIProjectIndexView(LaunchpadView):
         base_url = urlsplit(
             urlutils.join(
                 config.codehosting.git_ssh_root,
-                canonical_url(self.context, force_local_path=True)[1:]))
+                canonical_url(self.context, force_local_path=True)[1:],
+            )
+        )
         url = list(base_url)
         url[1] = "{}@{}".format(self.user.name, base_url.hostname)
         return urlunsplit(url)
@@ -270,12 +287,14 @@ class OCIProjectIndexView(LaunchpadView):
     @cachedproperty
     def official_recipe_count(self):
         return self.context.getOfficialRecipes(
-            visible_by_user=self.user).count()
+            visible_by_user=self.user
+        ).count()
 
     @cachedproperty
     def other_recipe_count(self):
         return self.context.getUnofficialRecipes(
-            visible_by_user=self.user).count()
+            visible_by_user=self.user
+        ).count()
 
 
 class OCIProjectEditView(LaunchpadEditFormView):
@@ -283,8 +302,8 @@ class OCIProjectEditView(LaunchpadEditFormView):
 
     schema = IOCIProject
     field_names = [
-        'name',
-        ]
+        "name",
+    ]
 
     def setUpFields(self):
         pillar_key = getPillarFieldName(self.context.pillar)
@@ -298,25 +317,27 @@ class OCIProjectEditView(LaunchpadEditFormView):
 
     @property
     def label(self):
-        return 'Edit %s OCI project' % self.context.name
+        return "Edit %s OCI project" % self.context.name
 
-    page_title = 'Edit'
+    page_title = "Edit"
 
     def validate(self, data):
         super().validate(data)
         pillar_type_field = getPillarFieldName(self.context.pillar)
         pillar = data.get(pillar_type_field)
-        name = data.get('name')
+        name = data.get("name")
         if pillar and name:
             oci_project = getUtility(IOCIProjectSet).getByPillarAndName(
-                pillar, name)
+                pillar, name
+            )
             if oci_project is not None and oci_project != self.context:
                 self.setFieldError(
-                    'name',
-                    'There is already an OCI project in %s %s with this name.'
-                    % (pillar_type_field, pillar.display_name))
+                    "name",
+                    "There is already an OCI project in %s %s with this name."
+                    % (pillar_type_field, pillar.display_name),
+                )
 
-    @action('Update OCI project', name='update')
+    @action("Update OCI project", name="update")
     def update_action(self, action, data):
         self.updateContextFromData(data)
 
@@ -329,7 +350,8 @@ class OCIProjectEditView(LaunchpadEditFormView):
 
 class OCIProjectSearchView(LaunchpadView):
     """Page to search for OCI projects of a given pillar."""
-    page_title = ''
+
+    page_title = ""
 
     @property
     def label(self):
@@ -371,4 +393,5 @@ class OCIProjectSearchView(LaunchpadView):
     @property
     def search_results(self):
         return getUtility(IOCIProjectSet).findByPillarAndName(
-            self.context, self.text or '')
+            self.context, self.text or ""
+        )
diff --git a/lib/lp/registry/browser/peoplemerge.py b/lib/lp/registry/browser/peoplemerge.py
index f37f6da..63f7bd9 100644
--- a/lib/lp/registry/browser/peoplemerge.py
+++ b/lib/lp/registry/browser/peoplemerge.py
@@ -4,106 +4,123 @@
 """People Merge related wiew classes."""
 
 __all__ = [
-    'AdminPeopleMergeView',
-    'AdminTeamMergeView',
-    'DeleteTeamView',
-    'FinishedPeopleMergeRequestView',
-    'RequestPeopleMergeMultipleEmailsView',
-    'RequestPeopleMergeView',
-    ]
+    "AdminPeopleMergeView",
+    "AdminTeamMergeView",
+    "DeleteTeamView",
+    "FinishedPeopleMergeRequestView",
+    "RequestPeopleMergeMultipleEmailsView",
+    "RequestPeopleMergeView",
+]
 
 
 from zope.component import getUtility
 from zope.security.proxy import removeSecurityProxy
 
 from lp import _
-from lp.app.browser.launchpadform import (
-    action,
-    LaunchpadFormView,
-    )
+from lp.app.browser.launchpadform import LaunchpadFormView, action
 from lp.app.interfaces.launchpad import ILaunchpadCelebrities
 from lp.code.interfaces.branchcollection import IAllBranches
 from lp.code.interfaces.gitcollection import IAllGitRepositories
-from lp.registry.interfaces.mailinglist import (
-    MailingListStatus,
-    PURGE_STATES,
-    )
+from lp.registry.interfaces.mailinglist import PURGE_STATES, MailingListStatus
 from lp.registry.interfaces.person import (
     IAdminPeopleMergeSchema,
     IAdminTeamMergeSchema,
     IPersonSet,
     IRequestPeopleMerge,
-    )
+)
 from lp.services.identity.interfaces.emailaddress import (
     EmailAddressStatus,
     IEmailAddressSet,
-    )
+)
 from lp.services.propertycache import cachedproperty
 from lp.services.verification.interfaces.authtoken import LoginTokenType
 from lp.services.verification.interfaces.logintoken import ILoginTokenSet
-from lp.services.webapp import (
-    canonical_url,
-    LaunchpadView,
-    )
+from lp.services.webapp import LaunchpadView, canonical_url
 from lp.services.webapp.interfaces import ILaunchBag
 from lp.soyuz.enums import ArchiveStatus
 from lp.soyuz.interfaces.archive import IArchiveSet
 
 
 class ValidatingMergeView(LaunchpadFormView):
-
     def validate(self, data):
         """Check that user is not attempting to merge a person into itself."""
-        dupe_person = data.get('dupe_person')
-        target_person = data.get('target_person') or self.user
+        dupe_person = data.get("dupe_person")
+        target_person = data.get("target_person") or self.user
         if dupe_person is None:
             self.setFieldError(
-                'dupe_person', 'The duplicate is not a valid person or team.')
+                "dupe_person", "The duplicate is not a valid person or team."
+            )
         else:
             if dupe_person == target_person:
-                self.addError(_("You can't merge ${name} into itself.",
-                      mapping=dict(name=dupe_person.name)))
+                self.addError(
+                    _(
+                        "You can't merge ${name} into itself.",
+                        mapping=dict(name=dupe_person.name),
+                    )
+                )
             dupe_person_ppas = getUtility(IArchiveSet).getPPAOwnedByPerson(
-                dupe_person, statuses=[ArchiveStatus.ACTIVE,
-                                       ArchiveStatus.DELETING])
+                dupe_person,
+                statuses=[ArchiveStatus.ACTIVE, ArchiveStatus.DELETING],
+            )
             if dupe_person_ppas is not None:
-                self.addError(_(
-                    "${name} has a PPA that must be deleted before it "
-                    "can be merged. It may take ten minutes to remove the "
-                    "deleted PPA's files.",
-                    mapping=dict(name=dupe_person.name)))
+                self.addError(
+                    _(
+                        "${name} has a PPA that must be deleted before it "
+                        "can be merged. It may take ten minutes to remove the "
+                        "deleted PPA's files.",
+                        mapping=dict(name=dupe_person.name),
+                    )
+                )
             all_branches = getUtility(IAllBranches)
             if not all_branches.ownedBy(dupe_person).isPrivate().is_empty():
                 self.addError(
-                    _("${name} owns private branches that must be "
-                      "deleted or transferred to another owner first.",
-                    mapping=dict(name=dupe_person.name)))
+                    _(
+                        "${name} owns private branches that must be "
+                        "deleted or transferred to another owner first.",
+                        mapping=dict(name=dupe_person.name),
+                    )
+                )
             all_repositories = getUtility(IAllGitRepositories)
-            if not all_repositories.ownedBy(
-                    dupe_person).isPrivate().is_empty():
+            if (
+                not all_repositories.ownedBy(dupe_person)
+                .isPrivate()
+                .is_empty()
+            ):
                 self.addError(
-                    _("${name} owns private Git repositories that must be "
-                      "deleted or transferred to another owner first.",
-                    mapping=dict(name=dupe_person.name)))
+                    _(
+                        "${name} owns private Git repositories that must be "
+                        "deleted or transferred to another owner first.",
+                        mapping=dict(name=dupe_person.name),
+                    )
+                )
             if dupe_person.isMergePending():
-                self.addError(_("${name} is already queued for merging.",
-                      mapping=dict(name=dupe_person.name)))
+                self.addError(
+                    _(
+                        "${name} is already queued for merging.",
+                        mapping=dict(name=dupe_person.name),
+                    )
+                )
         if target_person is not None and target_person.isMergePending():
-            self.addError(_("${name} is already queued for merging.",
-                  mapping=dict(name=target_person.name)))
+            self.addError(
+                _(
+                    "${name} is already queued for merging.",
+                    mapping=dict(name=target_person.name),
+                )
+            )
 
 
 class AdminMergeBaseView(ValidatingMergeView):
     """Base view for the pages where admins can merge people/teams."""
 
-    page_title = 'Merge Launchpad accounts'
+    page_title = "Merge Launchpad accounts"
     # Both subclasses share the same template so we need to define these
     # variables (which are used in the template) here rather than on
     # subclasses.
     should_confirm_email_reassignment = False
     should_confirm_member_deactivation = False
     merge_message = _(
-        'A merge is queued and is expected to complete in a few minutes.')
+        "A merge is queued and is expected to complete in a few minutes."
+    )
 
     dupe_person_emails = ()
     dupe_person = None
@@ -132,8 +149,8 @@ class AdminMergeBaseView(ValidatingMergeView):
         instance variable.
         """
         emailset = getUtility(IEmailAddressSet)
-        self.dupe_person = data['dupe_person']
-        self.target_person = data.get('target_person', None)
+        self.dupe_person = data["dupe_person"]
+        self.target_person = data.get("target_person", None)
         self.dupe_person_emails = emailset.getByPerson(self.dupe_person)
 
     def doMerge(self, data):
@@ -151,8 +168,12 @@ class AdminMergeBaseView(ValidatingMergeView):
                 naked_email.personID = self.target_person.id
                 naked_email.status = EmailAddressStatus.NEW
         getUtility(IPersonSet).mergeAsync(
-            self.dupe_person, self.target_person, reviewer=self.user,
-            delete=self.delete, requester=self.user)
+            self.dupe_person,
+            self.target_person,
+            reviewer=self.user,
+            delete=self.delete,
+            requester=self.user,
+        )
         self.request.response.addInfoNotification(self.merge_message)
         self.next_url = self.success_url
 
@@ -170,7 +191,7 @@ class AdminPeopleMergeView(AdminMergeBaseView):
     label = "Merge Launchpad people"
     schema = IAdminPeopleMergeSchema
 
-    @action('Merge', name='merge')
+    @action("Merge", name="merge")
     def merge_action(self, action, data):
         """Merge the two person entries specified in the form.
 
@@ -187,7 +208,7 @@ class AdminPeopleMergeView(AdminMergeBaseView):
             return
         self.doMerge(data)
 
-    @action('Reassign Emails and Merge', name='reassign_emails_and_merge')
+    @action("Reassign Emails and Merge", name="reassign_emails_and_merge")
     def reassign_emails_and_merge_action(self, action, data):
         """Reassign emails of the person to be merged and merge them."""
         self.setUpPeople(data)
@@ -210,7 +231,8 @@ class AdminTeamMergeView(AdminMergeBaseView):
         unused_states.append(MailingListStatus.PURGED)
         return (
             team.mailing_list is not None
-            and team.mailing_list.status not in unused_states)
+            and team.mailing_list.status not in unused_states
+        )
 
     @cachedproperty
     def registry_experts(self):
@@ -225,15 +247,19 @@ class AdminTeamMergeView(AdminMergeBaseView):
             return
 
         super().validate(data)
-        dupe_team = data['dupe_person']
+        dupe_team = data["dupe_person"]
         # We cannot merge the teams if there is a mailing list on the
         # duplicate person, unless that mailing list is purged.
         if self.hasMailingList(dupe_team):
-            self.addError(_(
-                "${name} is associated with a Launchpad mailing list; we "
-                "can't merge it.", mapping=dict(name=dupe_team.name)))
-
-    @action('Merge', name='merge')
+            self.addError(
+                _(
+                    "${name} is associated with a Launchpad mailing list; we "
+                    "can't merge it.",
+                    mapping=dict(name=dupe_team.name),
+                )
+            )
+
+    @action("Merge", name="merge")
     def merge_action(self, action, data):
         """Merge the two team entries specified in the form.
 
@@ -250,8 +276,9 @@ class AdminTeamMergeView(AdminMergeBaseView):
             return
         super().doMerge(data)
 
-    @action('Deactivate Members and Merge',
-            name='deactivate_members_and_merge')
+    @action(
+        "Deactivate Members and Merge", name="deactivate_members_and_merge"
+    )
     def deactivate_members_and_merge_action(self, action, data):
         """Deactivate all members of the team to be merged and merge them."""
         self.setUpPeople(data)
@@ -261,22 +288,22 @@ class AdminTeamMergeView(AdminMergeBaseView):
 class DeleteTeamView(AdminTeamMergeView):
     """A view that deletes a team by merging it with Registry experts."""
 
-    page_title = 'Delete'
-    field_names = ['dupe_person']
-    merge_message = _('The team is queued to be deleted.')
+    page_title = "Delete"
+    field_names = ["dupe_person"]
+    merge_message = _("The team is queued to be deleted.")
 
     @property
     def label(self):
-        return 'Delete %s' % self.context.displayname
+        return "Delete %s" % self.context.displayname
 
     def __init__(self, context, request):
         super().__init__(context, request)
-        if ('field.dupe_person' in self.request.form):
+        if "field.dupe_person" in self.request.form:
             # These fields have fixed values and are managed by this method.
             # The user has crafted a request to gain ownership of the dupe
             # team's assets.
-            self.addError('Unable to process submitted data.')
-        elif 'field.actions.delete' in self.request.form:
+            self.addError("Unable to process submitted data.")
+        elif "field.actions.delete" in self.request.form:
             # In the case of deleting a team, the form values are always
             # the context team, and the registry experts team. These values
             # are injected during __init__ because the base classes assume the
@@ -290,9 +317,9 @@ class DeleteTeamView(AdminTeamMergeView):
     @property
     def default_values(self):
         return {
-            'field.dupe_person': self.context.name,
-            'field.delete': True,
-            }
+            "field.dupe_person": self.context.name,
+            "field.delete": True,
+        }
 
     @property
     def cancel_url(self):
@@ -309,7 +336,7 @@ class DeleteTeamView(AdminTeamMergeView):
     def canDelete(self, data):
         return not self.has_mailing_list
 
-    @action('Delete', name='delete', condition=canDelete)
+    @action("Delete", name="delete", condition=canDelete)
     def merge_action(self, action, data):
         self.delete = True
         super().deactivate_members_and_merge_action.success(data)
@@ -322,12 +349,12 @@ class FinishedPeopleMergeRequestView(LaunchpadView):
     This view is used only when the dupe account has a single email address.
     """
 
-    page_title = 'Merge request sent'
+    page_title = "Merge request sent"
 
     def initialize(self):
         user = getUtility(ILaunchBag).user
         try:
-            dupe_id = int(self.request.get('dupe'))
+            dupe_id = int(self.request.get("dupe"))
         except (ValueError, TypeError):
             self.request.response.redirect(canonical_url(user))
             return
@@ -350,13 +377,13 @@ class FinishedPeopleMergeRequestView(LaunchpadView):
         if self.dupe_email:
             return LaunchpadView.render(self)
         else:
-            return ''
+            return ""
 
 
 class RequestPeopleMergeMultipleEmailsView(LaunchpadView):
     """Merge request view when dupe account has multiple email addresses."""
 
-    label = 'Merge Launchpad accounts'
+    label = "Merge Launchpad accounts"
     page_title = label
 
     def __init__(self, context, request):
@@ -366,11 +393,11 @@ class RequestPeopleMergeMultipleEmailsView(LaunchpadView):
         self.notified_addresses = []
 
     def processForm(self):
-        dupe = self.request.form.get('dupe')
+        dupe = self.request.form.get("dupe")
         if dupe is None:
             # We just got redirected to this page and we don't have the dupe
             # hidden field in request.form.
-            dupe = self.request.get('dupe')
+            dupe = self.request.get("dupe")
             if dupe is None:
                 return
 
@@ -389,8 +416,9 @@ class RequestPeopleMergeMultipleEmailsView(LaunchpadView):
             # If the email addresses are hidden we must send a merge request
             # to each of them.  But first we've got to remove the security
             # proxy so we can get to them.
-            email_addresses = [removeSecurityProxy(email).email
-                               for email in self.dupeemails]
+            email_addresses = [
+                removeSecurityProxy(email).email for email in self.dupeemails
+            ]
         else:
             # Otherwise we send a merge request only to the ones the user
             # selected.
@@ -411,13 +439,15 @@ class RequestPeopleMergeMultipleEmailsView(LaunchpadView):
                         self.request.response.addNotification(
                             "An address was removed from the duplicate "
                             "account while you were making this merge "
-                            "request. Select again.")
+                            "request. Select again."
+                        )
                         return
                     email_addresses.append(emailaddress)
 
         for emailaddress in email_addresses:
             token = logintokenset.new(
-                self.user, login, emailaddress, LoginTokenType.ACCOUNTMERGE)
+                self.user, login, emailaddress, LoginTokenType.ACCOUNTMERGE
+            )
             token.sendMergeRequestEmail()
             self.notified_addresses.append(emailaddress)
         self.form_processed = True
@@ -443,7 +473,7 @@ class RequestPeopleMergeView(ValidatingMergeView):
     of those they want to claim.
     """
 
-    label = 'Merge Launchpad accounts'
+    label = "Merge Launchpad accounts"
     page_title = label
     schema = IRequestPeopleMerge
 
@@ -451,9 +481,9 @@ class RequestPeopleMergeView(ValidatingMergeView):
     def cancel_url(self):
         return canonical_url(getUtility(IPersonSet))
 
-    @action('Continue', name='continue')
+    @action("Continue", name="continue")
     def continue_action(self, action, data):
-        dupeaccount = data['dupe_person']
+        dupeaccount = data["dupe_person"]
         if dupeaccount == self.user:
             # Please, don't try to merge you into yourself.
             return
@@ -464,7 +494,7 @@ class RequestPeopleMergeView(ValidatingMergeView):
             # The dupe account have more than one email address. Must redirect
             # the user to another page to ask which of those emails they
             # want to claim.
-            self.next_url = '+requestmerge-multiple?dupe=%d' % dupeaccount.id
+            self.next_url = "+requestmerge-multiple?dupe=%d" % dupeaccount.id
             return
 
         assert emails_count == 1
@@ -474,7 +504,10 @@ class RequestPeopleMergeView(ValidatingMergeView):
         # Need to remove the security proxy because the dupe account may have
         # hidden email addresses.
         token = logintokenset.new(
-            self.user, login, removeSecurityProxy(email).email,
-            LoginTokenType.ACCOUNTMERGE)
+            self.user,
+            login,
+            removeSecurityProxy(email).email,
+            LoginTokenType.ACCOUNTMERGE,
+        )
         token.sendMergeRequestEmail()
-        self.next_url = './+mergerequest-sent?dupe=%d' % dupeaccount.id
+        self.next_url = "./+mergerequest-sent?dupe=%d" % dupeaccount.id
diff --git a/lib/lp/registry/browser/person.py b/lib/lp/registry/browser/person.py
index e1c31b3..5faea5e 100644
--- a/lib/lp/registry/browser/person.py
+++ b/lib/lp/registry/browser/person.py
@@ -4,121 +4,88 @@
 """Person-related view classes."""
 
 __all__ = [
-    'BeginTeamClaimView',
-    'CommonMenuLinks',
-    'PersonEditOCIRegistryCredentialsView',
-    'EmailToPersonView',
-    'PeopleSearchView',
-    'PersonAccountAdministerView',
-    'PersonAdministerView',
-    'PersonBrandingView',
-    'PersonBreadcrumb',
-    'PersonCloseAccountView',
-    'PersonCodeOfConductEditView',
-    'PersonDeactivateAccountView',
-    'PersonEditEmailsView',
-    'PersonEditIRCNicknamesView',
-    'PersonEditJabberIDsView',
-    'PersonEditTimeZoneView',
-    'PersonEditSSHKeysView',
-    'PersonEditView',
-    'PersonFacets',
-    'PersonGPGView',
-    'PersonIndexMenu',
-    'PersonIndexView',
-    'PersonKarmaView',
-    'PersonLanguagesView',
-    'PersonLiveFSView',
-    'PersonNavigation',
-    'PersonOAuthTokensView',
-    'PersonOCIRegistryCredentialsView',
-    'PersonOverviewMenu',
-    'PersonOwnedTeamsView',
-    'PersonRdfContentsView',
-    'PersonRdfView',
-    'PersonRelatedSoftwareView',
-    'PersonRenameFormMixin',
-    'PersonSetActionNavigationMenu',
-    'PersonSetContextMenu',
-    'PersonSetNavigation',
-    'PersonView',
-    'PPANavigationMenuMixIn',
-    'RedirectToEditLanguagesView',
-    'RestrictedMembershipsPersonView',
-    'archive_to_person',
-    ]
+    "BeginTeamClaimView",
+    "CommonMenuLinks",
+    "PersonEditOCIRegistryCredentialsView",
+    "EmailToPersonView",
+    "PeopleSearchView",
+    "PersonAccountAdministerView",
+    "PersonAdministerView",
+    "PersonBrandingView",
+    "PersonBreadcrumb",
+    "PersonCloseAccountView",
+    "PersonCodeOfConductEditView",
+    "PersonDeactivateAccountView",
+    "PersonEditEmailsView",
+    "PersonEditIRCNicknamesView",
+    "PersonEditJabberIDsView",
+    "PersonEditTimeZoneView",
+    "PersonEditSSHKeysView",
+    "PersonEditView",
+    "PersonFacets",
+    "PersonGPGView",
+    "PersonIndexMenu",
+    "PersonIndexView",
+    "PersonKarmaView",
+    "PersonLanguagesView",
+    "PersonLiveFSView",
+    "PersonNavigation",
+    "PersonOAuthTokensView",
+    "PersonOCIRegistryCredentialsView",
+    "PersonOverviewMenu",
+    "PersonOwnedTeamsView",
+    "PersonRdfContentsView",
+    "PersonRdfView",
+    "PersonRelatedSoftwareView",
+    "PersonRenameFormMixin",
+    "PersonSetActionNavigationMenu",
+    "PersonSetContextMenu",
+    "PersonSetNavigation",
+    "PersonView",
+    "PPANavigationMenuMixIn",
+    "RedirectToEditLanguagesView",
+    "RestrictedMembershipsPersonView",
+    "archive_to_person",
+]
 
 
-from datetime import datetime
 import itertools
+from datetime import datetime
 from itertools import chain
-from operator import (
-    attrgetter,
-    itemgetter,
-    )
+from operator import attrgetter, itemgetter
 from textwrap import dedent
-from urllib.parse import (
-    quote,
-    urlencode,
-    )
+from urllib.parse import quote, urlencode
 
+import pytz
 from lazr.config import as_timedelta
 from lazr.delegates import delegate_to
 from lazr.restful.interface import copy_field
 from lazr.restful.interfaces import IWebServiceClientRequest
 from lazr.restful.utils import smartquote
 from lazr.uri import URI
-import pytz
 from storm.zope.interfaces import IResultSet
 from zope.browserpage import ViewPageTemplateFile
-from zope.component import (
-    adapter,
-    getUtility,
-    queryMultiAdapter,
-    )
+from zope.component import adapter, getUtility, queryMultiAdapter
 from zope.formlib.form import FormFields
 from zope.formlib.widget import CustomWidgetFactory
-from zope.formlib.widgets import (
-    TextAreaWidget,
-    TextWidget,
-    )
-from zope.interface import (
-    classImplements,
-    implementer,
-    Interface,
-    invariant,
-    )
+from zope.formlib.widgets import TextAreaWidget, TextWidget
+from zope.interface import Interface, classImplements, implementer, invariant
 from zope.interface.exceptions import Invalid
 from zope.publisher.interfaces import NotFound
-from zope.schema import (
-    Bool,
-    Choice,
-    Password,
-    Text,
-    TextLine,
-    )
-from zope.schema.vocabulary import (
-    SimpleTerm,
-    SimpleVocabulary,
-    )
+from zope.schema import Bool, Choice, Password, Text, TextLine
+from zope.schema.vocabulary import SimpleTerm, SimpleVocabulary
 from zope.security.interfaces import Unauthorized
 from zope.security.proxy import removeSecurityProxy
 
 from lp import _
 from lp.app.browser.launchpadform import (
-    action,
     LaunchpadEditFormView,
     LaunchpadFormView,
-    )
+    action,
+)
 from lp.app.browser.lazrjs import TextAreaEditorWidget
-from lp.app.browser.tales import (
-    DateTimeFormatterAPI,
-    PersonFormatterAPI,
-    )
-from lp.app.errors import (
-    NotFoundError,
-    UnexpectedFormData,
-    )
+from lp.app.browser.tales import DateTimeFormatterAPI, PersonFormatterAPI
+from lp.app.errors import NotFoundError, UnexpectedFormData
 from lp.app.interfaces.headings import IHeadingBreadcrumb
 from lp.app.interfaces.launchpad import ILaunchpadCelebrities
 from lp.app.validators import LaunchpadValidationError
@@ -128,7 +95,7 @@ from lp.app.widgets.image import ImageChangeWidget
 from lp.app.widgets.itemswidgets import (
     LaunchpadRadioWidget,
     LaunchpadRadioWidgetWithDescription,
-    )
+)
 from lp.bugs.interfaces.bugsupervisor import IHasBugSupervisor
 from lp.bugs.interfaces.bugtask import BugTaskStatus
 from lp.bugs.interfaces.bugtasksearch import BugTaskSearchParams
@@ -145,63 +112,50 @@ from lp.oci.interfaces.ociregistrycredentials import (
     IOCIRegistryCredentialsSet,
     OCIRegistryCredentialsAlreadyExist,
     user_can_edit_credentials_for_owner,
-    )
+)
 from lp.registry.browser import BaseRdfView
 from lp.registry.browser.branding import BrandingChangeView
 from lp.registry.browser.menu import (
     IRegistryCollectionNavigationMenu,
     RegistryCollectionActionMenuBase,
     TopLevelMenuMixin,
-    )
+)
 from lp.registry.browser.teamjoin import TeamJoinMixin
 from lp.registry.enums import PersonVisibility
 from lp.registry.interfaces.codeofconduct import ISignedCodeOfConductSet
 from lp.registry.interfaces.distribution import IDistribution
 from lp.registry.interfaces.distributionsourcepackage import (
     IDistributionSourcePackage,
-    )
+)
 from lp.registry.interfaces.gpg import IGPGKeySet
 from lp.registry.interfaces.irc import IIrcIDSet
-from lp.registry.interfaces.jabber import (
-    IJabberID,
-    IJabberIDSet,
-    )
+from lp.registry.interfaces.jabber import IJabberID, IJabberIDSet
 from lp.registry.interfaces.mailinglist import (
     CannotUnsubscribe,
     IMailingListSet,
-    )
+)
 from lp.registry.interfaces.mailinglistsubscription import (
     MailingListAutoSubscribePolicy,
-    )
+)
 from lp.registry.interfaces.ociproject import IOCIProject
-from lp.registry.interfaces.person import (
-    IPerson,
-    IPersonClaim,
-    IPersonSet,
-    )
+from lp.registry.interfaces.person import IPerson, IPersonClaim, IPersonSet
 from lp.registry.interfaces.persondistributionsourcepackage import (
     IPersonDistributionSourcePackageFactory,
-    )
+)
 from lp.registry.interfaces.personociproject import IPersonOCIProjectFactory
 from lp.registry.interfaces.personproduct import IPersonProductFactory
 from lp.registry.interfaces.persontransferjob import (
     IPersonCloseAccountJobSource,
     IPersonDeactivateJobSource,
-    )
+)
 from lp.registry.interfaces.pillar import IPillarNameSet
 from lp.registry.interfaces.poll import IPollSubset
-from lp.registry.interfaces.product import (
-    InvalidProductName,
-    IProduct,
-    )
-from lp.registry.interfaces.ssh import (
-    ISSHKeySet,
-    SSHKeyAdditionError,
-    )
+from lp.registry.interfaces.product import InvalidProductName, IProduct
+from lp.registry.interfaces.ssh import ISSHKeySet, SSHKeyAdditionError
 from lp.registry.interfaces.teammembership import (
     ITeamMembershipSet,
     TeamMembershipStatus,
-    )
+)
 from lp.registry.interfaces.wikiname import IWikiNameSet
 from lp.registry.mail.notification import send_direct_contact_email
 from lp.registry.model.person import get_recipients
@@ -210,67 +164,55 @@ from lp.services.database.decoratedresultset import DecoratedResultSet
 from lp.services.database.sqlbase import flush_database_updates
 from lp.services.feeds.browser import FeedsMixin
 from lp.services.geoip.interfaces import IRequestPreferredLanguages
-from lp.services.gpg.interfaces import (
-    GPGKeyNotFoundError,
-    IGPGHandler,
-    )
-from lp.services.identity.interfaces.account import (
-    AccountStatus,
-    IAccount,
-    )
+from lp.services.gpg.interfaces import GPGKeyNotFoundError, IGPGHandler
+from lp.services.identity.interfaces.account import AccountStatus, IAccount
 from lp.services.identity.interfaces.emailaddress import (
     EmailAddressStatus,
     IEmailAddress,
     IEmailAddressSet,
-    )
+)
 from lp.services.mail.interfaces import (
     INotificationRecipientSet,
     UnknownRecipientError,
-    )
+)
 from lp.services.messages.interfaces.message import (
     IDirectEmailAuthorization,
     QuotaReachedError,
-    )
+)
 from lp.services.oauth.interfaces import IOAuthConsumerSet
 from lp.services.openid.adapters.openid import CurrentOpenIDEndPoint
 from lp.services.openid.browser.openiddiscovery import (
     XRDSContentNegotiationMixin,
-    )
+)
 from lp.services.openid.interfaces.openid import IOpenIDPersistentIdentity
-from lp.services.propertycache import (
-    cachedproperty,
-    get_property_cache,
-    )
+from lp.services.propertycache import cachedproperty, get_property_cache
 from lp.services.verification.interfaces.authtoken import LoginTokenType
 from lp.services.verification.interfaces.logintoken import ILoginTokenSet
 from lp.services.webapp import (
     ApplicationMenu,
-    canonical_url,
     ContextMenu,
-    enabled_with_permission,
     Link,
     Navigation,
     NavigationMenu,
     StandardLaunchpadFacets,
+    canonical_url,
+    enabled_with_permission,
     stepthrough,
     stepto,
     structured,
-    )
+)
 from lp.services.webapp.authorization import (
     check_permission,
     precache_permission_for_objects,
-    )
+)
 from lp.services.webapp.batching import BatchNavigator
 from lp.services.webapp.breadcrumb import DisplaynameBreadcrumb
 from lp.services.webapp.interfaces import (
     ILaunchBag,
     IMultiFacetedBreadcrumb,
     IOpenLaunchBag,
-    )
-from lp.services.webapp.login import (
-    logoutPerson,
-    require_fresh_login,
-    )
+)
+from lp.services.webapp.login import logoutPerson, require_fresh_login
 from lp.services.webapp.menu import get_current_view
 from lp.services.webapp.publisher import LaunchpadView
 from lp.services.worlddata.interfaces.country import ICountry
@@ -279,7 +221,7 @@ from lp.snappy.browser.hassnaps import HasSnapsMenuMixin
 from lp.snappy.interfaces.snap import ISnapSet
 from lp.soyuz.browser.archivesubscription import (
     traverse_archive_subscription_for_subscriber,
-    )
+)
 from lp.soyuz.interfaces.archivesubscriber import IArchiveSubscriberSet
 from lp.soyuz.interfaces.binarypackagebuild import IBinaryPackageBuildSet
 from lp.soyuz.interfaces.livefs import ILiveFSSet
@@ -308,8 +250,11 @@ class RestrictedMembershipsPersonView(LaunchpadView):
         # getLatestApprovedMembershipsForPerson which returns a sqlobject
         # result set.
         membership_list = self.context.getLatestApprovedMembershipsForPerson()
-        return [membership for membership in membership_list
-                if check_permission('launchpad.View', membership.team)]
+        return [
+            membership
+            for membership in membership_list
+            if check_permission("launchpad.View", membership.team)
+        ]
 
     @property
     def teams_with_icons(self):
@@ -321,8 +266,11 @@ class RestrictedMembershipsPersonView(LaunchpadView):
         # This method returns a list as opposed to the database object's
         # teams_with_icons which returns a sqlobject
         # result set.
-        return [team for team in self.context.teams_with_icons
-                if check_permission('launchpad.View', team)]
+        return [
+            team
+            for team in self.context.teams_with_icons
+            if check_permission("launchpad.View", team)
+        ]
 
     @property
     def administrated_teams(self):
@@ -331,8 +279,11 @@ class RestrictedMembershipsPersonView(LaunchpadView):
         The user must be an administrator of the team, and the team must
         be public.
         """
-        return [team for team in self.context.getAdministratedTeams()
-                if team.visibility == PersonVisibility.PUBLIC]
+        return [
+            team
+            for team in self.context.getAdministratedTeams()
+            if team.visibility == PersonVisibility.PUBLIC
+        ]
 
     def userCanViewMembership(self):
         """Return true if the user can view a team's membership.
@@ -340,7 +291,7 @@ class RestrictedMembershipsPersonView(LaunchpadView):
         Only launchpad admins and team members can view the private
         membership. Anyone can view a public team's membership.
         """
-        return check_permission('launchpad.View', self.context)
+        return check_permission("launchpad.View", self.context)
 
 
 class BranchTraversalMixin:
@@ -360,16 +311,17 @@ class BranchTraversalMixin:
             base.append(pillar_name)
         return itertools.chain(iter(base), iter(self.request.stepstogo))
 
-    @stepto('+branch')
+    @stepto("+branch")
     def redirect_branch(self):
         """Redirect to canonical_url."""
         branch = getUtility(IBranchNamespaceSet).traverse(self._getSegments())
         if branch:
             return self.redirectSubTree(
-                canonical_url(branch, request=self.request))
+                canonical_url(branch, request=self.request)
+            )
         raise NotFoundError
 
-    @stepthrough('+git')
+    @stepthrough("+git")
     def traverse_personal_gitrepo(self, name):
         # XXX wgrant 2015-06-12: traverse() handles traversal for
         # non-personal repos, and works for personal repos except that
@@ -377,7 +329,8 @@ class BranchTraversalMixin:
         # view, but stepthroughs match before views and only for
         # multi-segment paths, so this is a workable hack.
         _, _, repository, _ = getUtility(IGitTraverser).traverse(
-            iter(['+git', name]), owner=self.context)
+            iter(["+git", name]), owner=self.context
+        )
         return repository
 
     def traverse(self, pillar_name):
@@ -385,14 +338,15 @@ class BranchTraversalMixin:
             # Look for a Git repository.  We must be careful not to consume
             # the traversal stack immediately, as if we fail to find a Git
             # repository we will need to look for a Bazaar branch instead.
-            segments = (
-                [pillar_name] +
-                list(reversed(self.request.getTraversalStack())))
+            segments = [pillar_name] + list(
+                reversed(self.request.getTraversalStack())
+            )
             num_segments = len(segments)
             iter_segments = iter(segments)
             traverser = getUtility(IGitTraverser)
             _, target, repository, trailing = traverser.traverse(
-                iter_segments, owner=self.context)
+                iter_segments, owner=self.context
+            )
             if repository is None:
                 raise NotFoundError
             # Subtract one because the pillar has already been traversed.
@@ -417,7 +371,8 @@ class BranchTraversalMixin:
                 # This repository was accessed through one of its project's
                 # aliases, so we must redirect to its canonical URL.
                 return self.redirectSubTree(
-                    canonical_url(repository, request=self.request))
+                    canonical_url(repository, request=self.request)
+                )
 
             return repository
         except (NotFoundError, InvalidNamespace, InvalidProductName):
@@ -431,16 +386,20 @@ class BranchTraversalMixin:
         pillar = getUtility(IPillarNameSet).getByName(pillar_name)
         if IProduct.providedBy(pillar):
             person_product = getUtility(IPersonProductFactory).create(
-                self.context, pillar)
+                self.context, pillar
+            )
             # If accessed through an alias, redirect to the proper name.
             if pillar.name != pillar_name:
                 return self.redirectSubTree(
                     canonical_url(person_product, request=self.request),
-                    status=301)
+                    status=301,
+                )
             getUtility(IOpenLaunchBag).add(pillar)
             return person_product
-        elif (IDistribution.providedBy(pillar) and
-                len(self.request.stepstogo) >= 2):
+        elif (
+            IDistribution.providedBy(pillar)
+            and len(self.request.stepstogo) >= 2
+        ):
             if self.request.stepstogo.peek() == "+source":
                 get_target = IDistribution(pillar).getSourcePackage
                 factory = getUtility(IPersonDistributionSourcePackageFactory)
@@ -460,14 +419,16 @@ class BranchTraversalMixin:
                     if pillar.name != pillar_name:
                         return self.redirectSubTree(
                             canonical_url(person_target, request=self.request),
-                            status=301)
+                            status=301,
+                        )
                     getUtility(IOpenLaunchBag).add(pillar)
                     return person_target
 
         # Otherwise look for a branch.
         try:
             branch = getUtility(IBranchNamespaceSet).traverse(
-                self._getSegments(pillar_name))
+                self._getSegments(pillar_name)
+            )
         except (NotFoundError, InvalidNamespace):
             return super().traverse(pillar_name)
 
@@ -482,14 +443,16 @@ class BranchTraversalMixin:
                 # This branch was accessed through one of its project's
                 # aliases, so we must redirect to its canonical URL.
                 return self.redirectSubTree(
-                    canonical_url(branch, request=self.request))
+                    canonical_url(branch, request=self.request)
+                )
 
         if branch.distribution is not None:
             if branch.distribution.name != pillar_name:
                 # This branch was accessed through one of its distribution's
                 # aliases, so we must redirect to its canonical URL.
                 return self.redirectSubTree(
-                    canonical_url(branch, request=self.request))
+                    canonical_url(branch, request=self.request)
+                )
 
         return branch
 
@@ -498,20 +461,22 @@ class PersonNavigation(BranchTraversalMixin, Navigation):
 
     usedfor = IPerson
 
-    @stepthrough('+expiringmembership')
+    @stepthrough("+expiringmembership")
     def traverse_expiring_membership(self, name):
         # Return the found membership regardless of its status as we know
         # TeamMembershipSelfRenewalView will tell users why the memembership
         # can't be renewed when necessary.
         # Circular imports
         from lp.registry.browser.team import TeamMembershipSelfRenewalView
+
         membership = getUtility(ITeamMembershipSet).getByPersonAndTeam(
-            self.context, getUtility(IPersonSet).getByName(name))
+            self.context, getUtility(IPersonSet).getByName(name)
+        )
         if membership is None:
             return None
         return TeamMembershipSelfRenewalView(membership, self.request)
 
-    @stepto('+archive')
+    @stepto("+archive")
     def traverse_archive(self):
         from lp.soyuz.browser.archive import traverse_named_ppa
 
@@ -521,8 +486,9 @@ class PersonNavigation(BranchTraversalMixin, Navigation):
         # apt-add-repository).
         redirect_allowed = not (
             IWebServiceClientRequest.providedBy(self.request)
-            and self.request.annotations.get(
-                self.request.VERSION_ANNOTATION) == '1.0')
+            and self.request.annotations.get(self.request.VERSION_ANNOTATION)
+            == "1.0"
+        )
 
         # There are three cases, in order of preference:
         #  - 2014 onwards: /~wgrant/+archive/ubuntu/ppa:
@@ -551,12 +517,13 @@ class PersonNavigation(BranchTraversalMixin, Navigation):
                     self.request.stepstogo.consume()
                 if redirect:
                     return self.redirectSubTree(
-                        canonical_url(ppa, request=self.request))
+                        canonical_url(ppa, request=self.request)
+                    )
                 else:
                     return ppa
         return None
 
-    @stepthrough('+email')
+    @stepthrough("+email")
     def traverse_email(self, email):
         """Traverse to this person's emails on the webservice layer."""
         email = getUtility(IEmailAddressSet).getByEmail(email)
@@ -564,7 +531,7 @@ class PersonNavigation(BranchTraversalMixin, Navigation):
             return None
         return email
 
-    @stepthrough('+wikiname')
+    @stepthrough("+wikiname")
     def traverse_wikiname(self, id):
         """Traverse to this person's WikiNames on the webservice layer."""
         wiki = getUtility(IWikiNameSet).get(id)
@@ -572,7 +539,7 @@ class PersonNavigation(BranchTraversalMixin, Navigation):
             return None
         return wiki
 
-    @stepthrough('+jabberid')
+    @stepthrough("+jabberid")
     def traverse_jabberid(self, jabber_id):
         """Traverse to this person's JabberIDs on the webservice layer."""
         jabber = getUtility(IJabberIDSet).getByJabberID(jabber_id)
@@ -580,7 +547,7 @@ class PersonNavigation(BranchTraversalMixin, Navigation):
             return None
         return jabber
 
-    @stepthrough('+ircnick')
+    @stepthrough("+ircnick")
     def traverse_ircnick(self, id):
         """Traverse to this person's IrcIDs on the webservice layer."""
         irc_nick = getUtility(IIrcIDSet).get(id)
@@ -588,7 +555,7 @@ class PersonNavigation(BranchTraversalMixin, Navigation):
             return None
         return irc_nick
 
-    @stepthrough('+oci-registry-credential')
+    @stepthrough("+oci-registry-credential")
     def traverse_oci_registry_credential(self, id):
         """Traverse to this person's OCI registry credentials."""
         oci_credentials = getUtility(IOCIRegistryCredentialsSet).get(id)
@@ -596,7 +563,7 @@ class PersonNavigation(BranchTraversalMixin, Navigation):
             return None
         return oci_credentials
 
-    @stepto('+archivesubscriptions')
+    @stepto("+archivesubscriptions")
     def traverse_archive_subscription(self):
         """Traverse to the archive subscription for this person."""
         if self.context.is_team:
@@ -609,19 +576,21 @@ class PersonNavigation(BranchTraversalMixin, Navigation):
             if not archive_id.isdigit():
                 return None
             return traverse_archive_subscription_for_subscriber(
-                self.context, archive_id)
+                self.context, archive_id
+            )
         else:
             # Otherwise we return the normal view for a person's
             # archive subscriptions.
             return queryMultiAdapter(
-                (self.context, self.request), name="+archivesubscriptions")
+                (self.context, self.request), name="+archivesubscriptions"
+            )
 
-    @stepthrough('+recipe')
+    @stepthrough("+recipe")
     def traverse_recipe(self, name):
         """Traverse to this person's recipes."""
         return self.context.getRecipe(name)
 
-    @stepthrough('+livefs')
+    @stepthrough("+livefs")
     def traverse_livefs(self, distribution_name):
         """Traverse to this person's live filesystem images."""
         if len(self.request.stepstogo) < 2:
@@ -630,8 +599,11 @@ class PersonNavigation(BranchTraversalMixin, Navigation):
         distroseries_name = self.request.stepstogo.consume()
         livefs_name = self.request.stepstogo.consume()
         livefs = getUtility(ILiveFSSet).interpret(
-            self.context.name, distribution_name, distroseries_name,
-            livefs_name)
+            self.context.name,
+            distribution_name,
+            distroseries_name,
+            livefs_name,
+        )
 
         if livefs is None:
             raise NotFoundError
@@ -641,15 +613,17 @@ class PersonNavigation(BranchTraversalMixin, Navigation):
             # distribution's aliases, so we must redirect to its canonical
             # URL.
             return self.redirectSubTree(
-                canonical_url(livefs, request=self.request))
+                canonical_url(livefs, request=self.request)
+            )
 
         return livefs
 
-    @stepthrough('+snap')
+    @stepthrough("+snap")
     def traverse_snap(self, name):
         """Traverse to this person's snap packages."""
         snap = getUtility(ISnapSet).getByPillarAndName(
-            self.context, None, name)
+            self.context, None, name
+        )
         if snap is None:
             raise NotFoundError(name)
         return snap
@@ -666,38 +640,47 @@ class PersonSetNavigation(Navigation):
             raise NotFoundError(name)
         # Redirect to /~name
         return self.redirectSubTree(
-            canonical_url(person, request=self.request))
+            canonical_url(person, request=self.request)
+        )
 
-    @stepto('+me')
+    @stepto("+me")
     def me(self):
         me = getUtility(ILaunchBag).user
         if me is None:
             raise Unauthorized("You need to be logged in to view this URL.")
         return self.redirectSubTree(
-            canonical_url(me, request=self.request), status=303)
+            canonical_url(me, request=self.request), status=303
+        )
 
 
 class PersonSetContextMenu(ContextMenu, TopLevelMenuMixin):
 
     usedfor = IPersonSet
 
-    links = ['projects', 'distributions', 'people', 'meetings',
-             'register_team',
-             'adminpeoplemerge', 'adminteammerge', 'mergeaccounts']
+    links = [
+        "projects",
+        "distributions",
+        "people",
+        "meetings",
+        "register_team",
+        "adminpeoplemerge",
+        "adminteammerge",
+        "mergeaccounts",
+    ]
 
     def mergeaccounts(self):
-        text = 'Merge accounts'
-        return Link('+requestmerge', text, icon='edit')
+        text = "Merge accounts"
+        return Link("+requestmerge", text, icon="edit")
 
-    @enabled_with_permission('launchpad.Moderate')
+    @enabled_with_permission("launchpad.Moderate")
     def adminpeoplemerge(self):
-        text = 'Admin merge people'
-        return Link('+adminpeoplemerge', text, icon='edit')
+        text = "Admin merge people"
+        return Link("+adminpeoplemerge", text, icon="edit")
 
-    @enabled_with_permission('launchpad.Moderate')
+    @enabled_with_permission("launchpad.Moderate")
     def adminteammerge(self):
-        text = 'Admin merge teams'
-        return Link('+adminteammerge', text, icon='edit')
+        text = "Admin merge teams"
+        return Link("+adminteammerge", text, icon="edit")
 
 
 class PersonFacets(StandardLaunchpadFacets):
@@ -707,252 +690,260 @@ class PersonFacets(StandardLaunchpadFacets):
 
 
 class CommonMenuLinks:
-
     @property
     def person(self):
         """Allow subclasses that use the view as the context."""
         return self.context
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def activate_ppa(self):
         target = "+activate-ppa"
-        text = 'Create a new PPA'
-        return Link(target, text, icon='add')
+        text = "Create a new PPA"
+        return Link(target, text, icon="add")
 
     def related_software_summary(self):
-        target = '+related-packages'
-        text = 'Related packages'
-        return Link(target, text, icon='info')
+        target = "+related-packages"
+        text = "Related packages"
+        return Link(target, text, icon="info")
 
     def maintained(self):
-        target = '+maintained-packages'
-        text = 'Maintained packages'
+        target = "+maintained-packages"
+        text = "Maintained packages"
         enabled = self.person.hasMaintainedPackages()
-        return Link(target, text, enabled=enabled, icon='info')
+        return Link(target, text, enabled=enabled, icon="info")
 
     def uploaded(self):
-        target = '+uploaded-packages'
-        text = 'Uploaded packages'
+        target = "+uploaded-packages"
+        text = "Uploaded packages"
         enabled = self.person.hasUploadedButNotMaintainedPackages()
-        return Link(target, text, enabled=enabled, icon='info')
+        return Link(target, text, enabled=enabled, icon="info")
 
     def ppa(self):
-        target = '+ppa-packages'
-        text = 'Related PPA packages'
+        target = "+ppa-packages"
+        text = "Related PPA packages"
         enabled = self.person.hasUploadedPPAPackages()
-        return Link(target, text, enabled=enabled, icon='info')
+        return Link(target, text, enabled=enabled, icon="info")
 
     def synchronised(self):
-        target = '+synchronised-packages'
-        text = 'Synchronised packages'
+        target = "+synchronised-packages"
+        text = "Synchronised packages"
         enabled = self.person.hasSynchronisedPublishings()
-        return Link(target, text, enabled=enabled, icon='info')
+        return Link(target, text, enabled=enabled, icon="info")
 
     def projects(self):
-        target = '+related-projects'
-        text = 'Related projects'
+        target = "+related-projects"
+        text = "Related projects"
         user = getUtility(ILaunchBag).user
         enabled = bool(self.person.getAffiliatedPillars(user))
-        return Link(target, text, enabled=enabled, icon='info')
+        return Link(target, text, enabled=enabled, icon="info")
 
     def owned_teams(self):
-        target = '+owned-teams'
-        text = 'Owned teams'
-        return Link(target, text, icon='info')
+        target = "+owned-teams"
+        text = "Owned teams"
+        return Link(target, text, icon="info")
 
     def subscriptions(self):
-        target = '+subscriptions'
-        text = 'Direct subscriptions'
-        return Link(target, text, icon='info')
+        target = "+subscriptions"
+        text = "Direct subscriptions"
+        return Link(target, text, icon="info")
 
     def structural_subscriptions(self):
-        target = '+structural-subscriptions'
-        text = 'Structural subscriptions'
-        return Link(target, text, icon='info')
+        target = "+structural-subscriptions"
+        text = "Structural subscriptions"
+        return Link(target, text, icon="info")
 
     def oci_registry_credentials(self):
-        target = '+oci-registry-credentials'
-        text = 'OCI registry credentials'
+        target = "+oci-registry-credentials"
+        text = "OCI registry credentials"
         enabled = user_can_edit_credentials_for_owner(self.context, self.user)
-        return Link(target, text, enabled=enabled, icon='info')
+        return Link(target, text, enabled=enabled, icon="info")
 
 
 class PersonMenuMixin(CommonMenuLinks):
-
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def branding(self):
-        target = '+branding'
-        text = 'Change branding'
-        return Link(target, text, icon='edit')
+        target = "+branding"
+        text = "Change branding"
+        return Link(target, text, icon="edit")
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def edit(self):
-        target = '+edit'
-        text = 'Change details'
-        return Link(target, text, icon='edit')
+        target = "+edit"
+        text = "Change details"
+        return Link(target, text, icon="edit")
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def password(self):
         target = config.launchpad.openid_provider_root
-        text = 'Change password'
-        return Link(target, text, icon='edit')
+        text = "Change password"
+        return Link(target, text, icon="edit")
 
-    @enabled_with_permission('launchpad.Moderate')
+    @enabled_with_permission("launchpad.Moderate")
     def administer(self):
-        target = '+review'
-        text = 'Administer'
-        return Link(target, text, icon='edit')
+        target = "+review"
+        text = "Administer"
+        return Link(target, text, icon="edit")
 
-    @enabled_with_permission('launchpad.Moderate')
+    @enabled_with_permission("launchpad.Moderate")
     def administer_account(self):
-        target = '+reviewaccount'
-        text = 'Administer Account'
-        return Link(target, text, icon='edit')
+        target = "+reviewaccount"
+        text = "Administer Account"
+        return Link(target, text, icon="edit")
 
 
-class PersonOverviewMenu(ApplicationMenu, PersonMenuMixin, HasRecipesMenuMixin,
-                         HasSnapsMenuMixin, HasOCIRecipesMenuMixin,
-                         HasCharmRecipesMenuMixin):
+class PersonOverviewMenu(
+    ApplicationMenu,
+    PersonMenuMixin,
+    HasRecipesMenuMixin,
+    HasSnapsMenuMixin,
+    HasOCIRecipesMenuMixin,
+    HasCharmRecipesMenuMixin,
+):
 
     usedfor = IPerson
-    facet = 'overview'
+    facet = "overview"
     links = [
-        'edit',
-        'branding',
-        'editemailaddresses',
-        'editlanguages',
-        'editmailinglists',
-        'editircnicknames',
-        'editjabberids',
-        'editsshkeys',
-        'editpgpkeys',
-        'editlocation',
-        'memberships',
-        'codesofconduct',
-        'karma',
-        'administer',
-        'administer_account',
-        'projects',
-        'activate_ppa',
-        'maintained',
-        'owned_teams',
-        'synchronised',
-        'view_ppa_subscriptions',
-        'ppa',
-        'oauth_tokens',
-        'oci_registry_credentials',
-        'related_software_summary',
-        'view_charm_recipes',
-        'view_recipes',
-        'view_snaps',
-        'view_oci_recipes',
-        'subscriptions',
-        'structural_subscriptions',
-        ]
+        "edit",
+        "branding",
+        "editemailaddresses",
+        "editlanguages",
+        "editmailinglists",
+        "editircnicknames",
+        "editjabberids",
+        "editsshkeys",
+        "editpgpkeys",
+        "editlocation",
+        "memberships",
+        "codesofconduct",
+        "karma",
+        "administer",
+        "administer_account",
+        "projects",
+        "activate_ppa",
+        "maintained",
+        "owned_teams",
+        "synchronised",
+        "view_ppa_subscriptions",
+        "ppa",
+        "oauth_tokens",
+        "oci_registry_credentials",
+        "related_software_summary",
+        "view_charm_recipes",
+        "view_recipes",
+        "view_snaps",
+        "view_oci_recipes",
+        "subscriptions",
+        "structural_subscriptions",
+    ]
 
     def related_software_summary(self):
-        target = '+related-packages'
-        text = 'Related packages'
-        return Link(target, text, icon='info')
+        target = "+related-packages"
+        text = "Related packages"
+        return Link(target, text, icon="info")
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def oauth_tokens(self):
-        target = '+oauth-tokens'
-        text = 'Authorized applications'
+        target = "+oauth-tokens"
+        text = "Authorized applications"
         access_tokens = self.context.oauth_access_tokens
         request_tokens = self.context.oauth_request_tokens
         enabled = bool(access_tokens or request_tokens)
-        return Link(target, text, enabled=enabled, icon='info')
+        return Link(target, text, enabled=enabled, icon="info")
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def editlanguages(self):
-        target = '+editlanguages'
-        text = 'Set preferred languages'
-        return Link(target, text, icon='edit')
+        target = "+editlanguages"
+        text = "Set preferred languages"
+        return Link(target, text, icon="edit")
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def editemailaddresses(self):
-        target = '+editemails'
-        text = 'Change email settings'
-        return Link(target, text, icon='edit')
+        target = "+editemails"
+        text = "Change email settings"
+        return Link(target, text, icon="edit")
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def editmailinglists(self):
-        target = '+editmailinglists'
-        text = 'Manage mailing list subscriptions'
-        return Link(target, text, icon='edit')
+        target = "+editmailinglists"
+        text = "Manage mailing list subscriptions"
+        return Link(target, text, icon="edit")
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def editircnicknames(self):
-        target = '+editircnicknames'
-        text = 'Update IRC nicknames'
-        return Link(target, text, icon='edit')
+        target = "+editircnicknames"
+        text = "Update IRC nicknames"
+        return Link(target, text, icon="edit")
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def editjabberids(self):
-        target = '+editjabberids'
-        text = 'Update Jabber IDs'
-        return Link(target, text, icon='edit')
+        target = "+editjabberids"
+        text = "Update Jabber IDs"
+        return Link(target, text, icon="edit")
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def editlocation(self):
-        target = '+editlocation'
-        text = 'Set location and time zone'
-        return Link(target, text, icon='edit')
+        target = "+editlocation"
+        text = "Set location and time zone"
+        return Link(target, text, icon="edit")
 
     def karma(self):
-        target = '+karma'
-        text = 'Show karma summary'
+        target = "+karma"
+        text = "Show karma summary"
         summary = (
-            '%s\N{right single quotation mark}s activities '
-            'in Launchpad' % self.context.displayname)
-        return Link(target, text, summary, icon='info')
+            "%s\N{right single quotation mark}s activities "
+            "in Launchpad" % self.context.displayname
+        )
+        return Link(target, text, summary, icon="info")
 
     def memberships(self):
-        target = '+participation'
-        text = 'Show team participation'
-        return Link(target, text, icon='info')
+        target = "+participation"
+        text = "Show team participation"
+        return Link(target, text, icon="info")
 
-    @enabled_with_permission('launchpad.Special')
+    @enabled_with_permission("launchpad.Special")
     def editsshkeys(self):
-        target = '+editsshkeys'
+        target = "+editsshkeys"
         if self.context.sshkeys.is_empty():
-            text = 'Add an SSH key'
-            icon = 'add'
+            text = "Add an SSH key"
+            icon = "add"
         else:
-            text = 'Update SSH keys'
-            icon = 'edit'
-        summary = 'Used when storing code on Launchpad'
+            text = "Update SSH keys"
+            icon = "edit"
+        summary = "Used when storing code on Launchpad"
         return Link(target, text, summary, icon=icon)
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def editpgpkeys(self):
-        target = '+editpgpkeys'
-        text = 'Update OpenPGP keys'
-        summary = 'Used when maintaining packages'
-        return Link(target, text, summary, icon='edit')
+        target = "+editpgpkeys"
+        text = "Update OpenPGP keys"
+        summary = "Used when maintaining packages"
+        return Link(target, text, summary, icon="edit")
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def codesofconduct(self):
-        target = '+codesofconduct'
-        text = 'Codes of Conduct'
+        target = "+codesofconduct"
+        text = "Codes of Conduct"
         summary = (
-            'Agreements to abide by the rules of a distribution or project')
-        return Link(target, text, summary, icon='edit')
+            "Agreements to abide by the rules of a distribution or project"
+        )
+        return Link(target, text, summary, icon="edit")
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def view_ppa_subscriptions(self):
         target = "+archivesubscriptions"
         text = "View your private PPA subscriptions"
-        summary = ('View your personal PPA subscriptions and set yourself '
-                   'up to download your software')
+        summary = (
+            "View your personal PPA subscriptions and set yourself "
+            "up to download your software"
+        )
 
         # Only enable the link if the person has some subscriptions.
         subscriptions = getUtility(IArchiveSubscriberSet).getBySubscriber(
-            self.context)
+            self.context
+        )
         enabled = not subscriptions.is_empty()
 
-        return Link(target, text, summary, enabled=enabled, icon='info')
+        return Link(target, text, summary, enabled=enabled, icon="info")
 
 
 class IPersonEditMenu(Interface):
@@ -967,8 +958,8 @@ class PPANavigationMenuMixIn:
     """PPA-related navigation menu links for Person and Team pages."""
 
     def ppas(self):
-        target = '#ppas'
-        text = 'Personal Package Archives'
+        target = "#ppas"
+        text = "Personal Package Archives"
         view = get_current_view()
         if isinstance(view, PersonView):
             enabled = view.should_show_ppa_section
@@ -980,9 +971,16 @@ class PPANavigationMenuMixIn:
 class PersonRelatedSoftwareNavigationMenu(NavigationMenu, CommonMenuLinks):
 
     usedfor = IPersonRelatedSoftwareMenu
-    facet = 'overview'
-    links = ('related_software_summary', 'maintained', 'uploaded', 'ppa',
-             'synchronised', 'projects', 'owned_teams')
+    facet = "overview"
+    links = (
+        "related_software_summary",
+        "maintained",
+        "uploaded",
+        "ppa",
+        "synchronised",
+        "projects",
+        "owned_teams",
+    )
 
     @property
     def person(self):
@@ -994,43 +992,50 @@ class PersonEditNavigationMenu(NavigationMenu):
     """A sub-menu for different aspects of editing a Person's profile."""
 
     usedfor = IPersonEditMenu
-    facet = 'overview'
-    links = ('personal', 'email_settings', 'sshkeys', 'gpgkeys')
+    facet = "overview"
+    links = ("personal", "email_settings", "sshkeys", "gpgkeys")
 
     def personal(self):
-        target = '+edit'
-        text = 'Personal'
+        target = "+edit"
+        text = "Personal"
         return Link(target, text)
 
     def email_settings(self):
-        target = '+editemails'
-        text = 'Email Settings'
+        target = "+editemails"
+        text = "Email Settings"
         return Link(target, text)
 
-    @enabled_with_permission('launchpad.Special')
+    @enabled_with_permission("launchpad.Special")
     def sshkeys(self):
-        target = '+editsshkeys'
-        text = 'SSH keys'
+        target = "+editsshkeys"
+        text = "SSH keys"
         return Link(target, text)
 
     def gpgkeys(self):
-        target = '+editpgpkeys'
-        text = 'OpenPGP Keys'
+        target = "+editpgpkeys"
+        text = "OpenPGP Keys"
         return Link(target, text)
 
 
 class PersonSetActionNavigationMenu(RegistryCollectionActionMenuBase):
     """Action menu for `PeopleSearchView`."""
+
     usedfor = IPersonSet
-    links = ['register_team', 'register_project', 'create_account',
-             'request_merge', 'admin_merge_people', 'admin_merge_teams']
+    links = [
+        "register_team",
+        "register_project",
+        "create_account",
+        "request_merge",
+        "admin_merge_people",
+        "admin_merge_teams",
+    ]
 
 
 @implementer(IRegistryCollectionNavigationMenu)
 class PeopleSearchView(LaunchpadView):
     """Search for people and teams on the /people page."""
 
-    page_title = 'People and teams in Launchpad'
+    page_title = "People and teams in Launchpad"
 
     def __init__(self, context, request):
         super().__init__(context, request)
@@ -1048,13 +1053,13 @@ class PeopleSearchView(LaunchpadView):
     def is_teams_only(self):
         """Is the search restricted to teams."""
         searchfor = self.request.get("searchfor", None)
-        return searchfor == 'teamsonly'
+        return searchfor == "teamsonly"
 
     @property
     def is_people_only(self):
         """Is the search restricted to people."""
         searchfor = self.request.get("searchfor", None)
-        return searchfor == 'peopleonly'
+        return searchfor == "peopleonly"
 
     def searchPeopleBatchNavigator(self):
         name = self.request.get("name")
@@ -1071,7 +1076,8 @@ class PeopleSearchView(LaunchpadView):
 
 class DeactivateAccountSchema(Interface):
     comment = Text(
-        title=_("Why are you deactivating your account?"), required=False)
+        title=_("Why are you deactivating your account?"), required=False
+    )
 
 
 class PersonDeactivateAccountView(LaunchpadFormView):
@@ -1079,7 +1085,8 @@ class PersonDeactivateAccountView(LaunchpadFormView):
     schema = DeactivateAccountSchema
     label = "Deactivate your Launchpad account"
     custom_widget_comment = CustomWidgetFactory(
-        TextAreaWidget, height=5, width=60)
+        TextAreaWidget, height=5, width=60
+    )
 
     def validate(self, data):
         """See `LaunchpadFormView`."""
@@ -1087,11 +1094,12 @@ class PersonDeactivateAccountView(LaunchpadFormView):
 
     @action(_("Deactivate My Account"), name="deactivate")
     def deactivate_action(self, action, data):
-        self.context.preDeactivate(data['comment'])
+        self.context.preDeactivate(data["comment"])
         getUtility(IPersonDeactivateJobSource).create(self.context)
         logoutPerson(self.request)
         self.request.response.addInfoNotification(
-            _('Your account has been deactivated.'))
+            _("Your account has been deactivated.")
+        )
         self.next_url = self.request.getApplicationURL()
 
 
@@ -1101,7 +1109,8 @@ class BeginTeamClaimView(LaunchpadFormView):
     This is actually just the first step, where you enter the email address
     of the team and we email further instructions to that address.
     """
-    label = 'Claim team'
+
+    label = "Claim team"
     schema = IPersonClaim
 
     def initialize(self):
@@ -1109,15 +1118,16 @@ class BeginTeamClaimView(LaunchpadFormView):
             # Valid teams and people aren't claimable. We pull the path
             # out of PATH_INFO to make sure that the exception looks
             # good for subclasses. We're that picky!
-            name = self.request['PATH_INFO'].split("/")[-1]
+            name = self.request["PATH_INFO"].split("/")[-1]
             raise NotFound(self, name, request=self.request)
         LaunchpadFormView.initialize(self)
 
     def validate(self, data):
-        emailaddress = data.get('emailaddress')
+        emailaddress = data.get("emailaddress")
         if emailaddress is None:
             self.setFieldError(
-                'emailaddress', 'Please enter the email address')
+                "emailaddress", "Please enter the email address"
+            )
             return
 
         email = getUtility(IEmailAddressSet).getByEmail(emailaddress)
@@ -1125,29 +1135,31 @@ class BeginTeamClaimView(LaunchpadFormView):
         if email is None:
             # Email not registered in launchpad, ask the user to try another
             # one.
-            error = ("We couldn't find this email address. Please try "
-                     "another one that could possibly be associated with "
-                     "this profile. Note that this profile's name (%s) was "
-                     "generated based on the email address it's "
-                     "associated with."
-                     % self.context.name)
+            error = (
+                "We couldn't find this email address. Please try "
+                "another one that could possibly be associated with "
+                "this profile. Note that this profile's name (%s) was "
+                "generated based on the email address it's "
+                "associated with." % self.context.name
+            )
         elif email.personID != self.context.id:
             error = structured(
-                        "This email address is associated with yet another "
-                        "Launchpad profile, which you seem to have used at "
-                        "some point. If that's the case, you can "
-                        '<a href="/people/+requestmerge'
-                        '?field.dupe_person=%s">combine '
-                        "this profile with the other one</a> (you'll "
-                        "have to log in with the other profile first, "
-                        "though). If that's not the case, please try with a "
-                        "different email address.",
-                        self.context.name)
+                "This email address is associated with yet another "
+                "Launchpad profile, which you seem to have used at "
+                "some point. If that's the case, you can "
+                '<a href="/people/+requestmerge'
+                '?field.dupe_person=%s">combine '
+                "this profile with the other one</a> (you'll "
+                "have to log in with the other profile first, "
+                "though). If that's not the case, please try with a "
+                "different email address.",
+                self.context.name,
+            )
         else:
             # Yay! You got the right email this time.
             pass
         if error:
-            self.setFieldError('emailaddress', error)
+            self.setFieldError("emailaddress", error)
 
     @property
     def next_url(self):
@@ -1155,19 +1167,25 @@ class BeginTeamClaimView(LaunchpadFormView):
 
     @action(_("Continue"), name="confirm")
     def confirm_action(self, action, data):
-        email = data['emailaddress']
+        email = data["emailaddress"]
         token = getUtility(ILoginTokenSet).new(
-            requester=self.user, requesteremail=None, email=email,
-            tokentype=LoginTokenType.TEAMCLAIM)
+            requester=self.user,
+            requesteremail=None,
+            email=email,
+            tokentype=LoginTokenType.TEAMCLAIM,
+        )
         token.sendClaimTeamEmail()
-        self.request.response.addInfoNotification(_(
-            "A confirmation message has been sent to '${email}'. "
-            "Follow the instructions in that message to finish claiming this "
-            "team. "
-            "(If the above address is from a mailing list, it may be "
-            "necessary to talk with one of its admins to accept the message "
-            "from Launchpad so that you can finish the process.)",
-            mapping=dict(email=email)))
+        self.request.response.addInfoNotification(
+            _(
+                "A confirmation message has been sent to '${email}'. "
+                "Follow the instructions in that message to finish claiming "
+                "this team. "
+                "(If the above address is from a mailing list, it may be "
+                "necessary to talk with one of its admins to accept the "
+                "message from Launchpad so that you can finish the process.)",
+                mapping=dict(email=email),
+            )
+        )
 
 
 class RedirectToEditLanguagesView(LaunchpadView):
@@ -1181,18 +1199,18 @@ class RedirectToEditLanguagesView(LaunchpadView):
 
     def initialize(self):
         self.request.response.redirect(
-            '%s/+editlanguages' % canonical_url(self.user))
+            "%s/+editlanguages" % canonical_url(self.user)
+        )
 
 
 class PersonRdfView(BaseRdfView):
     """A view that embeds PersonRdfContentsView in a standalone page."""
 
-    template = ViewPageTemplateFile(
-        '../templates/person-rdf.pt')
+    template = ViewPageTemplateFile("../templates/person-rdf.pt")
 
     @property
     def filename(self):
-        return '%s.rdf' % self.context.name
+        return "%s.rdf" % self.context.name
 
 
 class PersonRdfContentsView:
@@ -1202,8 +1220,9 @@ class PersonRdfContentsView:
     # preserve the case of the elements (which is not preserved in the
     # parsing of the default text/html content-type.)
     template = ViewPageTemplateFile(
-        '../templates/person-rdf-contents.pt',
-        content_type='application/rdf+xml;charset="utf-8"')
+        "../templates/person-rdf-contents.pt",
+        content_type='application/rdf+xml;charset="utf-8"',
+    )
 
     def __init__(self, context, request):
         self.context = context
@@ -1218,12 +1237,11 @@ class PersonRdfContentsView:
         +rdf-contents/template.
         """
         unicodedata = self.template()
-        encodeddata = unicodedata.encode('utf-8')
+        encodeddata = unicodedata.encode("utf-8")
         return encodeddata
 
 
 class PersonRenameFormMixin(LaunchpadEditFormView):
-
     def setUpWidgets(self):
         """See `LaunchpadViewForm`.
 
@@ -1235,23 +1253,27 @@ class PersonRenameFormMixin(LaunchpadEditFormView):
         # No message means renames are permitted.
         if reason:
             # This makes the field's widget display (i.e. read) only.
-            self.form_fields['name'].for_display = True
+            self.form_fields["name"].for_display = True
         super().setUpWidgets()
         if reason:
-            self.widgets['name'].hint = reason
+            self.widgets["name"].hint = reason
 
 
 class PersonAdministerView(PersonRenameFormMixin):
     """Administer an `IPerson`."""
+
     schema = IPerson
     label = "Review person"
     field_names = [
-        'name', 'display_name',
-        'personal_standing', 'personal_standing_reason',
-        'require_strong_email_authentication',
-        ]
+        "name",
+        "display_name",
+        "personal_standing",
+        "personal_standing_reason",
+        "require_strong_email_authentication",
+    ]
     custom_widget_personal_standing_reason = CustomWidgetFactory(
-        TextAreaWidget, height=5, width=60)
+        TextAreaWidget, height=5, width=60
+    )
 
     @property
     def is_viewing_person(self):
@@ -1272,7 +1294,7 @@ class PersonAdministerView(PersonRenameFormMixin):
         """See `LaunchpadEditFormView`."""
         return canonical_url(self.context)
 
-    @action('Change', name='change')
+    @action("Change", name="change")
     def change_action(self, action, data):
         """Update the IPerson."""
         self.updateContextFromData(data)
@@ -1280,6 +1302,7 @@ class PersonAdministerView(PersonRenameFormMixin):
 
 class PersonCloseAccountView(LaunchpadFormView):
     """Close an account."""
+
     schema = Interface
     label = "Close account"
 
@@ -1293,28 +1316,32 @@ class PersonCloseAccountView(LaunchpadFormView):
         """See `LaunchpadEditFormView`."""
         return canonical_url(self.context)
 
-    @action('Close', name='close')
+    @action("Close", name="close")
     def delete_action(self, action, data):
         """Close the account."""
         getUtility(IPersonCloseAccountJobSource).create(self.context)
         self.request.response.addInfoNotification(
-            "This account will now be permanently closed.")
+            "This account will now be permanently closed."
+        )
 
 
 class IAccountAdministerSchema(Interface):
 
-    status = copy_field(IAccount['status'], required=True, readonly=False)
+    status = copy_field(IAccount["status"], required=True, readonly=False)
     comment = Text(
-        title=_('Status change comment'), required=True, readonly=False)
+        title=_("Status change comment"), required=True, readonly=False
+    )
 
 
 class PersonAccountAdministerView(LaunchpadFormView):
     """Administer an `IAccount` belonging to an `IPerson`."""
+
     schema = IAccountAdministerSchema
     label = "Review person's account"
-    field_names = ['status', 'comment']
+    field_names = ["status", "comment"]
     custom_widget_comment = CustomWidgetFactory(
-        TextAreaWidget, height=5, width=60)
+        TextAreaWidget, height=5, width=60
+    )
 
     def __init__(self, context, request):
         """See `LaunchpadEditFormView`."""
@@ -1326,7 +1353,7 @@ class PersonAccountAdministerView(LaunchpadFormView):
 
     @property
     def initial_values(self):
-        return {'status': self.context.status}
+        return {"status": self.context.status}
 
     @property
     def is_viewing_person(self):
@@ -1340,8 +1367,7 @@ class PersonAccountAdministerView(LaunchpadFormView):
     @property
     def email_addresses(self):
         """A list of the user's preferred and validated email addresses."""
-        emails = sorted(
-            email.email for email in self.person.validatedemails)
+        emails = sorted(email.email for email in self.person.validatedemails)
         if self.person.preferredemail is not None:
             emails.insert(0, self.person.preferredemail.email)
         return emails
@@ -1365,33 +1391,37 @@ class PersonAccountAdministerView(LaunchpadFormView):
         """See `LaunchpadEditFormView`."""
         return canonical_url(self.person)
 
-    @action('Change', name='change')
+    @action("Change", name="change")
     def change_action(self, action, data):
         """Update the IAccount."""
-        if data['status'] == self.context.status:
+        if data["status"] == self.context.status:
             return
-        if data['status'] == AccountStatus.SUSPENDED:
+        if data["status"] == AccountStatus.SUSPENDED:
             # The preferred email address is removed to ensure no email
             # is sent to the user.
             self.person.setPreferredEmail(None)
             self.request.response.addInfoNotification(
                 'The account "%s" has been suspended.'
-                % self.context.displayname)
-        elif data['status'] == AccountStatus.DEACTIVATED:
+                % self.context.displayname
+            )
+        elif data["status"] == AccountStatus.DEACTIVATED:
             self.request.response.addInfoNotification(
                 'The account "%s" is now deactivated. The user can log in '
-                'to reactivate it.' % self.context.displayname)
-        elif data['status'] == AccountStatus.DECEASED:
+                "to reactivate it." % self.context.displayname
+            )
+        elif data["status"] == AccountStatus.DECEASED:
             # Deliberately leave the email address in place so that it can't
             # easily be claimed by somebody else.
             self.request.response.addInfoNotification(
                 'The account "%s" has been marked as having belonged to a '
-                'deceased user.' % self.context.displayname)
-        self.context.setStatus(data['status'], self.user, data['comment'])
+                "deceased user." % self.context.displayname
+            )
+        self.context.setStatus(data["status"], self.user, data["comment"])
 
 
 class PersonLanguagesView(LaunchpadFormView):
     """Edit preferred languages for a person or team."""
+
     schema = Interface
 
     @property
@@ -1408,8 +1438,7 @@ class PersonLanguagesView(LaunchpadFormView):
         return ICountry(self.request, None)
 
     def browserLanguages(self):
-        return (
-            IRequestPreferredLanguages(self.request).getPreferredLanguages())
+        return IRequestPreferredLanguages(self.request).getPreferredLanguages()
 
     def visible_checked_languages(self):
         return self.context.languages
@@ -1417,17 +1446,21 @@ class PersonLanguagesView(LaunchpadFormView):
     def visible_unchecked_languages(self):
         common_languages = getUtility(ILanguageSet).common_languages
         person_languages = self.context.languages
-        return sorted(set(common_languages) - set(person_languages),
-                      key=attrgetter('englishname'))
+        return sorted(
+            set(common_languages) - set(person_languages),
+            key=attrgetter("englishname"),
+        )
 
     def getRedirectionURL(self):
         request = self.request
-        referrer = request.getHeader('referer')
-        if referrer and (referrer.startswith(request.getApplicationURL()) or
-                         referrer.find('+languages') != -1):
+        referrer = request.getHeader("referer")
+        if referrer and (
+            referrer.startswith(request.getApplicationURL())
+            or referrer.find("+languages") != -1
+        ):
             return referrer
         else:
-            return ''
+            return ""
 
     @property
     def is_current_user(self):
@@ -1437,7 +1470,7 @@ class PersonLanguagesView(LaunchpadFormView):
     @property
     def next_url(self):
         """Redirect to the +languages page if request originated there."""
-        redirection_url = self.request.get('redirection_url')
+        redirection_url = self.request.get("redirection_url")
         if redirection_url:
             return redirection_url
         return canonical_url(self.context)
@@ -1452,18 +1485,18 @@ class PersonLanguagesView(LaunchpadFormView):
 
     @action(_("Save"), name="save")
     def submitLanguages(self, action, data):
-        '''Process a POST request to the language preference form.
+        """Process a POST request to the language preference form.
 
         This list of languages submitted is compared to the list of
         languages the user has, and the latter is matched to the former.
-        '''
+        """
 
         all_languages = getUtility(ILanguageSet)
         old_languages = self.context.languages
         new_languages = []
 
         for key in all_languages.keys():
-            if self.request.get(key, None) == 'on':
+            if self.request.get(key, None) == "on":
                 new_languages.append(all_languages[key])
 
         if self.is_current_user:
@@ -1476,38 +1509,41 @@ class PersonLanguagesView(LaunchpadFormView):
         for language in set(new_languages) - set(old_languages):
             self.context.addLanguage(language)
             messages.append(
-                "Added %(language)s to %(subject)s preferred languages." %
-                {'language': language.englishname, 'subject': subject})
+                "Added %(language)s to %(subject)s preferred languages."
+                % {"language": language.englishname, "subject": subject}
+            )
 
         # Remove languages from the user's preferences.
         for language in set(old_languages) - set(new_languages):
             self.context.removeLanguage(language)
             messages.append(
-                "Removed %(language)s from %(subject)s preferred languages." %
-                {'language': language.englishname, 'subject': subject})
+                "Removed %(language)s from %(subject)s preferred languages."
+                % {"language": language.englishname, "subject": subject}
+            )
         if len(messages) > 0:
             message = structured(
-                '<br />'.join(['%s'] * len(messages)), *messages)
+                "<br />".join(["%s"] * len(messages)), *messages
+            )
             self.request.response.addInfoNotification(message)
 
     @property
     def answers_url(self):
         return canonical_url(
-            getUtility(ILaunchpadCelebrities).launchpad,
-            rootsite='answers')
+            getUtility(ILaunchpadCelebrities).launchpad, rootsite="answers"
+        )
 
 
 class PersonKarmaView(LaunchpadView):
     """A view class used for ~person/+karma."""
 
-    page_title = 'Karma'
+    page_title = "Karma"
 
     @property
     def label(self):
         if self.user == self.context:
-            return 'Your Launchpad Karma'
+            return "Your Launchpad Karma"
         else:
-            return 'Launchpad Karma'
+            return "Launchpad Karma"
 
     @cachedproperty
     def has_karma(self):
@@ -1521,7 +1557,6 @@ class PersonKarmaView(LaunchpadView):
 
 
 class ContactViaWebLinksMixin:
-
     @cachedproperty
     def group_to_contact(self):
         """Contacting a team may contact different email addresses.
@@ -1533,7 +1568,8 @@ class ContactViaWebLinksMixin:
                 TO_MEMBERS
         """
         return ContactViaWebNotificationRecipientSet(
-            self.user, self.context).primary_reason
+            self.user, self.context
+        ).primary_reason
 
     @property
     def contact_link_title(self):
@@ -1541,15 +1577,15 @@ class ContactViaWebLinksMixin:
         ContactViaWeb = ContactViaWebNotificationRecipientSet
         if self.group_to_contact == ContactViaWeb.TO_USER:
             if self.viewing_own_page:
-                return 'Send an email to yourself through Launchpad'
+                return "Send an email to yourself through Launchpad"
             else:
-                return 'Send an email to this user through Launchpad'
+                return "Send an email to this user through Launchpad"
         elif self.group_to_contact == ContactViaWeb.TO_MEMBERS:
             return "Send an email to your team's members through Launchpad"
         elif self.group_to_contact == ContactViaWeb.TO_ADMINS:
             return "Send an email to this team's admins through Launchpad"
         else:
-            raise AssertionError('Unknown group to contact.')
+            raise AssertionError("Unknown group to contact.")
 
     @property
     def specific_contact_text(self):
@@ -1558,13 +1594,13 @@ class ContactViaWebLinksMixin:
         if self.group_to_contact == ContactViaWeb.TO_USER:
             # Note that we explicitly do not change the text to "Contact
             # yourself" when viewing your own page.
-            return 'Contact this user'
+            return "Contact this user"
         elif self.group_to_contact == ContactViaWeb.TO_MEMBERS:
             return "Contact this team's members"
         elif self.group_to_contact == ContactViaWeb.TO_ADMINS:
             return "Contact this team's admins"
         else:
-            raise AssertionError('Unknown group to contact.')
+            raise AssertionError("Unknown group to contact.")
 
 
 class PersonView(LaunchpadView, FeedsMixin, ContactViaWebLinksMixin):
@@ -1578,7 +1614,8 @@ class PersonView(LaunchpadView, FeedsMixin, ContactViaWebLinksMixin):
         rights to sign it.
         """
         return self.context.is_ubuntu_coc_signer or (
-            check_permission('launchpad.Edit', self.context))
+            check_permission("launchpad.Edit", self.context)
+        )
 
     @property
     def should_show_ircnicknames_section(self):
@@ -1588,7 +1625,8 @@ class PersonView(LaunchpadView, FeedsMixin, ContactViaWebLinksMixin):
         to register new ones.
         """
         return bool(self.context.ircnicknames) or (
-            check_permission('launchpad.Edit', self.context))
+            check_permission("launchpad.Edit", self.context)
+        )
 
     @property
     def should_show_jabberids_section(self):
@@ -1598,7 +1636,8 @@ class PersonView(LaunchpadView, FeedsMixin, ContactViaWebLinksMixin):
         to register new ones.
         """
         return bool(self.context.jabberids) or (
-            check_permission('launchpad.Edit', self.context))
+            check_permission("launchpad.Edit", self.context)
+        )
 
     @property
     def should_show_sshkeys_section(self):
@@ -1608,7 +1647,8 @@ class PersonView(LaunchpadView, FeedsMixin, ContactViaWebLinksMixin):
         to register new ones.
         """
         return bool(self.context.sshkeys) or (
-            check_permission('launchpad.Edit', self.context))
+            check_permission("launchpad.Edit", self.context)
+        )
 
     @property
     def should_show_gpgkeys_section(self):
@@ -1618,7 +1658,8 @@ class PersonView(LaunchpadView, FeedsMixin, ContactViaWebLinksMixin):
         to register new ones.
         """
         return bool(self.gpg_keys) or (
-            check_permission('launchpad.Edit', self.context))
+            check_permission("launchpad.Edit", self.context)
+        )
 
     @cachedproperty
     def gpg_keys(self):
@@ -1640,21 +1681,24 @@ class PersonView(LaunchpadView, FeedsMixin, ContactViaWebLinksMixin):
     def recently_approved_members(self):
         members = self.context.getMembersByStatus(
             TeamMembershipStatus.APPROVED,
-            orderBy='-TeamMembership.date_joined')
+            orderBy="-TeamMembership.date_joined",
+        )
         return members[:5]
 
     @cachedproperty
     def recently_proposed_members(self):
         members = self.context.getMembersByStatus(
             TeamMembershipStatus.PROPOSED,
-            orderBy='-TeamMembership.date_proposed')
+            orderBy="-TeamMembership.date_proposed",
+        )
         return members[:5]
 
     @cachedproperty
     def recently_invited_members(self):
         members = self.context.getMembersByStatus(
             TeamMembershipStatus.INVITED,
-            orderBy='-TeamMembership.date_proposed')
+            orderBy="-TeamMembership.date_proposed",
+        )
         return members[:5]
 
     @property
@@ -1665,9 +1709,9 @@ class PersonView(LaunchpadView, FeedsMixin, ContactViaWebLinksMixin):
         but hidden in case it adds a member to the list.
         """
         if IResultSet(self.recently_approved_members).is_empty():
-            return 'hidden'
+            return "hidden"
         else:
-            return ''
+            return ""
 
     @property
     def recently_proposed_hidden(self):
@@ -1677,9 +1721,9 @@ class PersonView(LaunchpadView, FeedsMixin, ContactViaWebLinksMixin):
         but hidden in case it adds a member to the list.
         """
         if IResultSet(self.recently_proposed_members).is_empty():
-            return 'hidden'
+            return "hidden"
         else:
-            return ''
+            return ""
 
     @property
     def recently_invited_hidden(self):
@@ -1689,9 +1733,9 @@ class PersonView(LaunchpadView, FeedsMixin, ContactViaWebLinksMixin):
         but hidden in case it adds a member to the list.
         """
         if IResultSet(self.recently_invited_members).is_empty():
-            return 'hidden'
+            return "hidden"
         else:
-            return ''
+            return ""
 
     @cachedproperty
     def openpolls(self):
@@ -1712,16 +1756,24 @@ class PersonView(LaunchpadView, FeedsMixin, ContactViaWebLinksMixin):
     def contributions(self):
         """Cache the results of getProjectsAndCategoriesContributedTo()."""
         return self.context.getProjectsAndCategoriesContributedTo(
-            self.user, limit=5)
+            self.user, limit=5
+        )
 
     @cachedproperty
     def contributed_categories(self):
         """Return all karma categories in which this person has some karma."""
         categories = set()
         for contrib in self.contributions:
-            categories.update(category for category in contrib['categories'])
-        sort = {'code': 0, 'bugs': 1, 'blueprints': 2, 'translations': 3,
-                'answers': 4, 'specs': 5, 'soyuz': 6}
+            categories.update(category for category in contrib["categories"])
+        sort = {
+            "code": 0,
+            "bugs": 1,
+            "blueprints": 2,
+            "translations": 3,
+            "answers": 4,
+            "specs": 5,
+            "soyuz": 6,
+        }
         return sorted(categories, key=lambda category: sort[category.name])
 
     @cachedproperty
@@ -1735,11 +1787,12 @@ class PersonView(LaunchpadView, FeedsMixin, ContactViaWebLinksMixin):
         This can only be used when the context is an automatically created
         profile (account_status == NOACCOUNT).
         """
-        assert self.context.account_status == AccountStatus.NOACCOUNT, (
-            "This can only be used when the context has no account.")
+        assert (
+            self.context.account_status == AccountStatus.NOACCOUNT
+        ), "This can only be used when the context has no account."
         emails = getUtility(IEmailAddressSet).getByPerson(self.context)
         for email in emails:
-            if '@lists.' in removeSecurityProxy(email).email:
+            if "@lists." in removeSecurityProxy(email).email:
                 return True
         return False
 
@@ -1749,8 +1802,10 @@ class PersonView(LaunchpadView, FeedsMixin, ContactViaWebLinksMixin):
 
         We only do this if it's enabled for the vhost.
         """
-        return (self.context.is_valid_person
-                and config.vhost.mainsite.openid_delegate_profile)
+        return (
+            self.context.is_valid_person
+            and config.vhost.mainsite.openid_delegate_profile
+        )
 
     @cachedproperty
     def openid_identity_url(self):
@@ -1766,17 +1821,24 @@ class PersonView(LaunchpadView, FeedsMixin, ContactViaWebLinksMixin):
         person that are In Progress.
         """
         query_string = urlencode(
-            [('field.status', BugTaskStatus.INPROGRESS.title)])
+            [("field.status", BugTaskStatus.INPROGRESS.title)]
+        )
         url = "%s/+assignedbugs" % canonical_url(self.context)
-        return ("%(url)s?search=Search&%(query_string)s"
-                % {'url': url, 'query_string': query_string})
+        return "%(url)s?search=Search&%(query_string)s" % {
+            "url": url,
+            "query_string": query_string,
+        }
 
     @cachedproperty
     def assigned_bugs_in_progress(self):
         """Return up to 5 assigned bugs that are In Progress."""
         params = BugTaskSearchParams(
-            user=self.user, assignee=self.context, omit_dupes=True,
-            status=BugTaskStatus.INPROGRESS, orderby='-date_last_updated')
+            user=self.user,
+            assignee=self.context,
+            omit_dupes=True,
+            status=BugTaskStatus.INPROGRESS,
+            orderby="-date_last_updated",
+        )
         return list(self.context.searchTasks(params)[:5])
 
     @cachedproperty
@@ -1804,17 +1866,19 @@ class PersonView(LaunchpadView, FeedsMixin, ContactViaWebLinksMixin):
         cannot contact persons or teams, and no one can contact an invalid
         person (inactive or without a preferred email address).
         """
-        return (
-            self.user is not None and self.context.is_valid_person_or_team)
+        return self.user is not None and self.context.is_valid_person_or_team
 
     @property
     def should_show_polls_portlet(self):
         # Circular imports.
         from lp.registry.browser.team import TeamOverviewMenu
+
         menu = TeamOverviewMenu(self.context)
         return (
-            self.has_current_polls or self.closedpolls
-            or menu.add_poll().enabled)
+            self.has_current_polls
+            or self.closedpolls
+            or menu.add_poll().enabled
+        )
 
     @property
     def has_current_polls(self):
@@ -1868,12 +1932,17 @@ class PersonView(LaunchpadView, FeedsMixin, ContactViaWebLinksMixin):
         :return: A list of email address strings that can be seen.
         """
         visible_states = (
-            EmailAddressVisibleState.PUBLIC, EmailAddressVisibleState.ALLOWED)
+            EmailAddressVisibleState.PUBLIC,
+            EmailAddressVisibleState.ALLOWED,
+        )
         if self.email_address_visibility.state in visible_states:
             emails = [self.context.preferredemail.email]
             if not self.context.is_team:
-                emails.extend(sorted(
-                    email.email for email in self.context.validatedemails))
+                emails.extend(
+                    sorted(
+                        email.email for email in self.context.validatedemails
+                    )
+                )
             return emails
         else:
             return []
@@ -1887,15 +1956,15 @@ class PersonView(LaunchpadView, FeedsMixin, ContactViaWebLinksMixin):
         """
         state = self.email_address_visibility.state
         if state is EmailAddressVisibleState.PUBLIC:
-            return 'This email address is only visible to Launchpad users.'
+            return "This email address is only visible to Launchpad users."
         elif state is EmailAddressVisibleState.ALLOWED:
-            return 'This email address is not disclosed to others.'
+            return "This email address is not disclosed to others."
         else:
             return None
 
     def showSSHKeys(self):
         """Return a data structure used for display of raw SSH keys"""
-        self.request.response.setHeader('Content-Type', 'text/plain')
+        self.request.response.setHeader("Content-Type", "text/plain")
         keys = [key.getFullKeyText() for key in self.context.sshkeys]
         return "".join(key + "\n" for key in keys)
 
@@ -1916,8 +1985,9 @@ class PersonView(LaunchpadView, FeedsMixin, ContactViaWebLinksMixin):
             return mailing_list.archive_url
         elif self.user is None:
             return None
-        elif (self.user.inTeam(self.context) or
-              self.user.inTeam(celebrities.admin)):
+        elif self.user.inTeam(self.context) or self.user.inTeam(
+            celebrities.admin
+        ):
             return mailing_list.archive_url
         else:
             return None
@@ -1928,7 +1998,7 @@ class PersonView(LaunchpadView, FeedsMixin, ContactViaWebLinksMixin):
         languages = list(self.context.languages)
         if len(languages) > 0:
             englishnames = [language.englishname for language in languages]
-            return ', '.join(sorted(englishnames))
+            return ", ".join(sorted(englishnames))
         else:
             return getUtility(ILaunchpadCelebrities).english.englishname
 
@@ -1940,7 +2010,7 @@ class PersonView(LaunchpadView, FeedsMixin, ContactViaWebLinksMixin):
         current_user may view at least one PPA or current_user has lp.edit
         """
         # If the current user has edit permission, show the section.
-        if check_permission('launchpad.Edit', self.context):
+        if check_permission("launchpad.Edit", self.context):
             return True
 
         # If the current user can view any PPA, show the section.
@@ -1949,14 +2019,15 @@ class PersonView(LaunchpadView, FeedsMixin, ContactViaWebLinksMixin):
     @cachedproperty
     def visible_ppas(self):
         ppas = self.context.getVisiblePPAs(self.user)
-        precache_permission_for_objects(self.request, 'launchpad.View', ppas)
+        precache_permission_for_objects(self.request, "launchpad.View", ppas)
         return ppas
 
     @property
     def time_zone_offset(self):
         """Return a string with offset from UTC"""
-        return datetime.now(
-            pytz.timezone(self.context.time_zone)).strftime("%z")
+        return datetime.now(pytz.timezone(self.context.time_zone)).strftime(
+            "%z"
+        )
 
 
 class PersonParticipationView(LaunchpadView):
@@ -1964,20 +2035,28 @@ class PersonParticipationView(LaunchpadView):
 
     @property
     def label(self):
-        return 'Team participation for ' + self.context.displayname
-
-    def _asParticipation(self, team=None, membership=None, via=None,
-                         mailing_list=None, subscription=None):
+        return "Team participation for " + self.context.displayname
+
+    def _asParticipation(
+        self,
+        team=None,
+        membership=None,
+        via=None,
+        mailing_list=None,
+        subscription=None,
+    ):
         """Return a dict of participation information for the membership.
 
         Method requires membership or via, not both.
         :param via: The team through which the membership in the indirect
         team is established.
         """
-        if ((membership is None and via is None) or
-            (membership is not None and via is not None)):
+        if (membership is None and via is None) or (
+            membership is not None and via is not None
+        ):
             raise AssertionError(
-                "The membership or via argument must be provided, not both.")
+                "The membership or via argument must be provided, not both."
+            )
 
         if via is not None:
             # When showing the path, it's unnecessary to show the team in
@@ -1985,16 +2064,16 @@ class PersonParticipationView(LaunchpadView):
             # end of the path.
             via_names = []
             for via_team in via[1:-1]:
-                if check_permission('launchpad.LimitedView', via_team):
+                if check_permission("launchpad.LimitedView", via_team):
                     via_names.append(via_team.displayname)
                 else:
-                    via_names.append('[private team]')
+                    via_names.append("[private team]")
             via = ", ".join(via_names)
 
         if membership is None:
             # Membership is via an indirect team so sane defaults exist.
             # An indirect member cannot be an Owner or Admin of a team.
-            role = 'Member'
+            role = "Member"
             # The Person never joined, and can't have a join date.
             datejoined = None
             dateexpires = None
@@ -2003,23 +2082,29 @@ class PersonParticipationView(LaunchpadView):
             datejoined = membership.datejoined
             dateexpires = membership.dateexpires
             if membership.personID == team.teamownerID:
-                role = 'Owner'
+                role = "Owner"
             elif membership.status == TeamMembershipStatus.ADMIN:
-                role = 'Admin'
+                role = "Admin"
             else:
-                role = 'Member'
+                role = "Member"
 
         if mailing_list is not None:
             if subscription is None:
-                subscribed = 'Not subscribed'
+                subscribed = "Not subscribed"
             else:
-                subscribed = 'Subscribed'
+                subscribed = "Subscribed"
         else:
-            subscribed = '&mdash;'
+            subscribed = "&mdash;"
 
         return dict(
-            displayname=team.displayname, team=team, datejoined=datejoined,
-            role=role, via=via, subscribed=subscribed, dateexpires=dateexpires)
+            displayname=team.displayname,
+            team=team,
+            datejoined=datejoined,
+            role=role,
+            via=via,
+            subscribed=subscribed,
+            dateexpires=dateexpires,
+        )
 
     @cachedproperty
     def active_participations(self):
@@ -2033,20 +2118,24 @@ class PersonParticipationView(LaunchpadView):
             if team not in direct_teams:
                 items.append(dict(team=team, via=via))
         items = [
-            item for item in items
-            if check_permission('launchpad.View', item["team"])]
+            item
+            for item in items
+            if check_permission("launchpad.View", item["team"])
+        ]
         participations = []
 
         # Bulk-load mailing list subscriptions.
         subscriptions = getUtility(IMailingListSet).getSubscriptionsForTeams(
-            self.context, [item["team"] for item in items])
+            self.context, [item["team"] for item in items]
+        )
 
         # Create all the participations.
         for item in items:
             item["mailing_list"], item["subscription"] = subscriptions.get(
-                item["team"].id, (None, None))
+                item["team"].id, (None, None)
+            )
             participations.append(self._asParticipation(**item))
-        return sorted(participations, key=itemgetter('displayname'))
+        return sorted(participations, key=itemgetter("displayname"))
 
     @cachedproperty
     def has_participations(self):
@@ -2072,6 +2161,7 @@ class EmailAddressVisibleState:
       viewing their own page or because the user is a privileged
       administrator.
     """
+
     LOGIN_REQUIRED = object()
     NONE_AVAILABLE = object()
     PUBLIC = object()
@@ -2091,7 +2181,7 @@ class EmailAddressVisibleState:
             self.state = EmailAddressVisibleState.NONE_AVAILABLE
         elif not view.context.hide_email_addresses:
             self.state = EmailAddressVisibleState.PUBLIC
-        elif check_permission('launchpad.View', view.context.preferredemail):
+        elif check_permission("launchpad.View", view.context.preferredemail):
             self.state = EmailAddressVisibleState.ALLOWED
         else:
             self.state = EmailAddressVisibleState.HIDDEN
@@ -2130,23 +2220,24 @@ def is_description_text_linkified(person: IPerson) -> bool:
     return not person.is_probationary
 
 
-class PersonIndexView(XRDSContentNegotiationMixin, PersonView,
-                      TeamJoinMixin):
+class PersonIndexView(XRDSContentNegotiationMixin, PersonView, TeamJoinMixin):
     """View class for person +index and +xrds pages."""
 
     xrds_template = ViewPageTemplateFile(
-        "../../services/openid/templates/person-xrds.pt")
+        "../../services/openid/templates/person-xrds.pt"
+    )
 
     def initialize(self):
         super().initialize()
         if self.context.isMergePending():
             if self.context.is_team:
-                merge_action = 'merged or deleted'
+                merge_action = "merged or deleted"
             else:
-                merge_action = 'merged'
+                merge_action = "merged"
             self.request.response.addInfoNotification(
-                "%s is queued to be %s in a few minutes." % (
-                self.context.displayname, merge_action))
+                "%s is queued to be %s in a few minutes."
+                % (self.context.displayname, merge_action)
+            )
         if self.request.method == "POST":
             self.processForm()
 
@@ -2154,7 +2245,7 @@ class PersonIndexView(XRDSContentNegotiationMixin, PersonView,
     def page_title(self):
         context = self.context
         if context.is_valid_person_or_team:
-            return '%s in Launchpad' % context.displayname
+            return "%s in Launchpad" % context.displayname
         else:
             return "%s does not use Launchpad" % context.displayname
 
@@ -2162,9 +2253,13 @@ class PersonIndexView(XRDSContentNegotiationMixin, PersonView,
     def description_widget(self):
         """The description as a widget."""
         return TextAreaEditorWidget(
-            self.context, IPerson['description'], title="",
-            edit_title='Edit description', hide_empty=False,
-            linkify_text=is_description_text_linkified(self.context))
+            self.context,
+            IPerson["description"],
+            title="",
+            edit_title="Edit description",
+            hide_empty=False,
+            linkify_text=is_description_text_linkified(self.context),
+        )
 
     @cachedproperty
     def page_description(self):
@@ -2188,28 +2283,32 @@ class PersonIndexView(XRDSContentNegotiationMixin, PersonView,
         return IOpenIDPersistentIdentity(self.context).openid_identity_url
 
     def processForm(self):
-        if not self.request.form.get('unsubscribe'):
+        if not self.request.form.get("unsubscribe"):
             raise UnexpectedFormData(
                 "The mailing list form did not receive the expected form "
-                "fields.")
+                "fields."
+            )
 
         mailing_list = self.context.mailing_list
         if mailing_list is None:
             raise UnexpectedFormData(
-                _("This team does not use Launchpad to host a mailing list."))
+                _("This team does not use Launchpad to host a mailing list.")
+            )
         if not self.user:
-            raise Unauthorized(
-                _("You must be logged in to unsubscribe."))
+            raise Unauthorized(_("You must be logged in to unsubscribe."))
         try:
             mailing_list.unsubscribe(self.user)
         except CannotUnsubscribe:
             self.request.response.addErrorNotification(
-                _("You could not be unsubscribed from the team mailing "
-                  "list."))
+                _(
+                    "You could not be unsubscribed from the team mailing "
+                    "list."
+                )
+            )
         else:
             self.request.response.addInfoNotification(
-                _("You have been unsubscribed from the team "
-                  "mailing list."))
+                _("You have been unsubscribed from the team " "mailing list.")
+            )
         self.request.response.redirect(canonical_url(self.context))
 
 
@@ -2219,7 +2318,7 @@ class PersonCodeOfConductEditView(LaunchpadView):
     @property
     def label(self):
         """See `LaunchpadView`."""
-        return 'Codes of Conduct for ' + self.context.displayname
+        return "Codes of Conduct for " + self.context.displayname
 
     def initialize(self):
         """See `LaunchpadView`."""
@@ -2234,7 +2333,7 @@ class PersonCodeOfConductEditView(LaunchpadView):
             for sig_id in sig_ids:
                 sig_id = int(sig_id)
                 # Deactivating signature.
-                comment = 'Deactivated by Owner'
+                comment = "Deactivated by Owner"
                 sCoC_util.modifySignature(sig_id, self.user, comment, False)
 
 
@@ -2264,20 +2363,21 @@ class PersonEditIRCNicknamesView(LaunchpadFormView):
             # unique column we have, so we don't have anything else that we
             # can use to make field names that allow us to uniquely identify
             # them.
-            if form.get('remove_%d' % ircnick.id):
+            if form.get("remove_%d" % ircnick.id):
                 ircnick.destroySelf()
             else:
-                nick = form.get('nick_%d' % ircnick.id)
-                network = form.get('network_%d' % ircnick.id)
+                nick = form.get("nick_%d" % ircnick.id)
+                network = form.get("network_%d" % ircnick.id)
                 if not (nick and network):
                     self.request.response.addErrorNotification(
-                        "Neither Nickname nor Network can be empty.")
+                        "Neither Nickname nor Network can be empty."
+                    )
                     return
                 ircnick.nickname = nick
                 ircnick.network = network
 
-        nick = form.get('newnick')
-        network = form.get('newnetwork')
+        nick = form.get("newnick")
+        network = form.get("newnetwork")
         if nick or network:
             if nick and network:
                 getUtility(IIrcIDSet).new(self.context, network, nick)
@@ -2285,13 +2385,14 @@ class PersonEditIRCNicknamesView(LaunchpadFormView):
                 self.newnick = nick
                 self.newnetwork = network
                 self.request.response.addErrorNotification(
-                    "Neither Nickname nor Network can be empty.")
+                    "Neither Nickname nor Network can be empty."
+                )
 
 
 class PersonEditJabberIDsView(LaunchpadFormView):
 
     schema = IJabberID
-    field_names = ['jabberid']
+    field_names = ["jabberid"]
 
     def setUpFields(self):
         super().setUpFields()
@@ -2299,7 +2400,7 @@ class PersonEditJabberIDsView(LaunchpadFormView):
             # Make the jabberid entry optional on the edit page if one or more
             # ids already exist, which allows the removal of ids without
             # filling out the new jabberid field.
-            jabber_field = self.form_fields['jabberid']
+            jabber_field = self.form_fields["jabberid"]
             # Copy the field so as not to modify the interface.
             jabber_field.field = copy_field(jabber_field.field)
             jabber_field.field.required = False
@@ -2318,32 +2419,36 @@ class PersonEditJabberIDsView(LaunchpadFormView):
 
     def validate(self, data):
         """Ensure the edited data does not already exist."""
-        jabberid = data.get('jabberid')
+        jabberid = data.get("jabberid")
         if jabberid is not None:
             jabberset = getUtility(IJabberIDSet)
             existingjabber = jabberset.getByJabberID(jabberid)
             if existingjabber is not None:
                 if existingjabber.person != self.context:
                     self.setFieldError(
-                        'jabberid',
+                        "jabberid",
                         structured(
-                            'The Jabber ID %s is already registered by '
+                            "The Jabber ID %s is already registered by "
                             '<a href="%s">%s</a>.',
-                            jabberid, canonical_url(existingjabber.person),
-                            existingjabber.person.displayname))
+                            jabberid,
+                            canonical_url(existingjabber.person),
+                            existingjabber.person.displayname,
+                        ),
+                    )
                 else:
                     self.setFieldError(
-                        'jabberid',
-                        'The Jabber ID %s already belongs to you.' % jabberid)
+                        "jabberid",
+                        "The Jabber ID %s already belongs to you." % jabberid,
+                    )
 
     @action(_("Save Changes"), name="save")
     def save(self, action, data):
         """Process the Jabber ID form."""
         form = self.request.form
         for jabber in self.context.jabberids:
-            if form.get('remove_%s' % jabber.jabberid):
+            if form.get("remove_%s" % jabber.jabberid):
                 jabber.destroySelf()
-        jabberid = data.get('jabberid')
+        jabberid = data.get("jabberid")
         if jabberid is not None:
             jabberset = getUtility(IJabberIDSet)
             jabberset.new(self.context, jabberid)
@@ -2356,17 +2461,17 @@ class PersonEditSSHKeysView(LaunchpadView):
     error_message = None
 
     def initialize(self):
-        require_fresh_login(self.request, self.context, '+editsshkeys')
+        require_fresh_login(self.request, self.context, "+editsshkeys")
 
         if self.request.method != "POST":
             # Nothing to do
             return
 
-        action = self.request.form.get('action')
+        action = self.request.form.get("action")
 
-        if action == 'add_ssh':
+        if action == "add_ssh":
             self.add_ssh()
-        elif action == 'remove_ssh':
+        elif action == "remove_ssh":
             self.remove_ssh()
         else:
             raise UnexpectedFormData("Unexpected action: %s" % action)
@@ -2382,23 +2487,24 @@ class PersonEditSSHKeysView(LaunchpadView):
         return canonical_url(self.context, view_name="+edit")
 
     def add_ssh(self):
-        sshkey = self.request.form.get('sshkey')
+        sshkey = self.request.form.get("sshkey")
         try:
             getUtility(ISSHKeySet).new(self.user, sshkey)
         except SSHKeyAdditionError:
-            self.error_message = structured('Invalid public key')
+            self.error_message = structured("Invalid public key")
         else:
-            self.info_message = structured('SSH public key added.')
+            self.info_message = structured("SSH public key added.")
 
     def remove_ssh(self):
-        key_id = self.request.form.get('key')
+        key_id = self.request.form.get("key")
         if not key_id:
-            raise UnexpectedFormData('SSH Key was not defined')
+            raise UnexpectedFormData("SSH Key was not defined")
 
         sshkey = getUtility(ISSHKeySet).getByID(key_id)
         if sshkey is None:
             self.error_message = structured(
-                "Cannot remove a key that doesn't exist")
+                "Cannot remove a key that doesn't exist"
+            )
             return
 
         if sshkey.person != self.user:
@@ -2430,7 +2536,7 @@ class PersonGPGView(LaunchpadView):
     info_message = None
 
     def initialize(self):
-        require_fresh_login(self.request, self.context, '+editpgpkeys')
+        require_fresh_login(self.request, self.context, "+editpgpkeys")
         super().initialize()
 
     @property
@@ -2445,19 +2551,20 @@ class PersonGPGView(LaunchpadView):
 
     def keyserver_url(self):
         assert self.fingerprint
-        return getUtility(
-            IGPGHandler).getURLForKeyInServer(self.fingerprint, public=True)
+        return getUtility(IGPGHandler).getURLForKeyInServer(
+            self.fingerprint, public=True
+        )
 
     def form_action(self):
         if self.request.method != "POST":
-            return ''
+            return ""
         permitted_actions = [
-            'claim_gpg',
-            'deactivate_gpg',
-            'remove_gpgtoken',
-            'reactivate_gpg',
-            ]
-        action = self.request.form.get('action')
+            "claim_gpg",
+            "deactivate_gpg",
+            "remove_gpgtoken",
+            "reactivate_gpg",
+        ]
+        action = self.request.form.get("action")
         if action not in permitted_actions:
             raise UnexpectedFormData("Action not permitted: %s" % action)
         getattr(self, action)()
@@ -2466,7 +2573,7 @@ class PersonGPGView(LaunchpadView):
         # XXX cprov 2005-04-01: As "Claim GPG key" takes a lot of time, we
         # should process it throught the NotificationEngine.
         gpghandler = getUtility(IGPGHandler)
-        fingerprint = self.request.form.get('fingerprint')
+        fingerprint = self.request.form.get("fingerprint")
         self.fingerprint = gpghandler.sanitizeFingerprint(fingerprint)
 
         if not self.fingerprint:
@@ -2492,11 +2599,12 @@ class PersonGPGView(LaunchpadView):
             self.key_ok = True
 
     def deactivate_gpg(self):
-        key_fingerprints = self.request.form.get('DEACTIVATE_GPGKEY')
+        key_fingerprints = self.request.form.get("DEACTIVATE_GPGKEY")
 
         if key_fingerprints is None:
             self.error_message = structured(
-                'No key(s) selected for deactivation.')
+                "No key(s) selected for deactivation."
+            )
             return
 
         # verify if we have multiple entries to deactive
@@ -2511,21 +2619,24 @@ class PersonGPGView(LaunchpadView):
                 continue
             if gpgkey.owner != self.user:
                 self.error_message = structured(
-                    "Cannot deactivate someone else's key")
+                    "Cannot deactivate someone else's key"
+                )
                 return
             gpgkeyset.deactivate(gpgkey)
             deactivated_keys.append(gpgkey.displayname)
 
         flush_database_updates()
         self.info_message = structured(
-           'Deactivated key(s): %s', ", ".join(deactivated_keys))
+            "Deactivated key(s): %s", ", ".join(deactivated_keys)
+        )
 
     def remove_gpgtoken(self):
-        token_fingerprints = self.request.form.get('REMOVE_GPGTOKEN')
+        token_fingerprints = self.request.form.get("REMOVE_GPGTOKEN")
 
         if token_fingerprints is None:
             self.error_message = structured(
-                'No key(s) pending validation selected.')
+                "No key(s) pending validation selected."
+            )
             return
 
         logintokenset = getUtility(ILoginTokenSet)
@@ -2535,21 +2646,25 @@ class PersonGPGView(LaunchpadView):
         cancelled_fingerprints = []
         for fingerprint in token_fingerprints:
             logintokenset.deleteByFingerprintRequesterAndType(
-                fingerprint, self.user, LoginTokenType.VALIDATEGPG)
+                fingerprint, self.user, LoginTokenType.VALIDATEGPG
+            )
             logintokenset.deleteByFingerprintRequesterAndType(
-                fingerprint, self.user, LoginTokenType.VALIDATESIGNONLYGPG)
+                fingerprint, self.user, LoginTokenType.VALIDATESIGNONLYGPG
+            )
             cancelled_fingerprints.append(fingerprint)
 
         self.info_message = structured(
-            'Cancelled validation of key(s): %s',
-            ", ".join(cancelled_fingerprints))
+            "Cancelled validation of key(s): %s",
+            ", ".join(cancelled_fingerprints),
+        )
 
     def reactivate_gpg(self):
-        key_fingerprints = self.request.form.get('REACTIVATE_GPGKEY')
+        key_fingerprints = self.request.form.get("REACTIVATE_GPGKEY")
 
         if key_fingerprints is None:
             self.error_message = structured(
-                'No key(s) selected for reactivation.')
+                "No key(s) selected for reactivation."
+            )
             return
 
         found = []
@@ -2574,27 +2689,30 @@ class PersonGPGView(LaunchpadView):
         comments = []
         if len(found) > 0:
             comments.append(
-                'A message has been sent to %s with instructions to '
-                'reactivate these key(s): %s'
-                % (self.context.preferredemail.email, ', '.join(found)))
+                "A message has been sent to %s with instructions to "
+                "reactivate these key(s): %s"
+                % (self.context.preferredemail.email, ", ".join(found))
+            )
         if len(notfound) > 0:
             if len(notfound) == 1:
                 comments.append(
-                    'Launchpad failed to retrieve this key from '
-                    'the keyserver: %s. Please make sure the key is '
-                    'published in a keyserver (such as '
+                    "Launchpad failed to retrieve this key from "
+                    "the keyserver: %s. Please make sure the key is "
+                    "published in a keyserver (such as "
                     '<a href="http://pgp.mit.edu";>pgp.mit.edu</a>) before '
-                    'trying to reactivate it again.' % (', '.join(notfound)))
+                    "trying to reactivate it again." % (", ".join(notfound))
+                )
             else:
                 comments.append(
-                    'Launchpad failed to retrieve these keys from '
-                    'the keyserver: %s. Please make sure the keys '
-                    'are published in a keyserver (such as '
+                    "Launchpad failed to retrieve these keys from "
+                    "the keyserver: %s. Please make sure the keys "
+                    "are published in a keyserver (such as "
                     '<a href="http://pgp.mit.edu";>pgp.mit.edu</a>) '
-                    'before trying to reactivate them '
-                    'again.' % (', '.join(notfound)))
+                    "before trying to reactivate them "
+                    "again." % (", ".join(notfound))
+                )
 
-        self.info_message = structured('\n<br />\n'.join(comments))
+        self.info_message = structured("\n<br />\n".join(comments))
 
     def _validateGPG(self, key):
         bag = getUtility(ILaunchBag)
@@ -2607,8 +2725,12 @@ class PersonGPGView(LaunchpadView):
             tokentype = LoginTokenType.VALIDATESIGNONLYGPG
 
         token = getUtility(ILoginTokenSet).new(
-            self.context, login, preferredemail, tokentype,
-            fingerprint=key.fingerprint)
+            self.context,
+            login,
+            preferredemail,
+            tokentype,
+            fingerprint=key.fingerprint,
+        )
 
         token.sendGPGValidationRequest(key)
 
@@ -2633,14 +2755,21 @@ class BasePersonEditView(LaunchpadEditFormView):
 class PersonEditView(PersonRenameFormMixin, BasePersonEditView):
     """The Person 'Edit' page."""
 
-    field_names = ['display_name', 'name', 'mugshot', 'description',
-                   'hide_email_addresses', 'verbose_bugnotifications',
-                   'selfgenerated_bugnotifications',
-                   'expanded_notification_footers']
+    field_names = [
+        "display_name",
+        "name",
+        "mugshot",
+        "description",
+        "hide_email_addresses",
+        "verbose_bugnotifications",
+        "selfgenerated_bugnotifications",
+        "expanded_notification_footers",
+    ]
     custom_widget_mugshot = CustomWidgetFactory(
-        ImageChangeWidget, ImageChangeWidget.EDIT_STYLE)
+        ImageChangeWidget, ImageChangeWidget.EDIT_STYLE
+    )
 
-    label = 'Change your personal details'
+    label = "Change your personal details"
     page_title = label
 
     # Will contain an hidden input when the user is renaming their
@@ -2655,11 +2784,12 @@ class PersonEditView(PersonRenameFormMixin, BasePersonEditView):
         if not is_description_text_linkified(self.context):
             self.widgets["description"].hint = _(
                 "Details about interests and goals. Use plain text, "
-                "paragraphs are preserved.")
+                "paragraphs are preserved."
+            )
 
     def validate(self, data):
         """If the name changed, warn the user about the implications."""
-        new_name = data.get('name')
+        new_name = data.get("name")
 
         # Name was not changed, carry on ...
         if not new_name or new_name == self.context.name:
@@ -2669,15 +2799,20 @@ class PersonEditView(PersonRenameFormMixin, BasePersonEditView):
         try:
             username_validator(new_name)
         except LaunchpadValidationError as err:
-            self.setFieldError('name', str(err))
+            self.setFieldError("name", str(err))
             return
 
         # Ensure the user is aware of the implications of changing username.
         bypass_check = self.request.form_ng.getOne(
-            'i_know_this_is_an_openid_security_issue', 0)
+            "i_know_this_is_an_openid_security_issue", 0
+        )
         if not bypass_check:
             # Warn the user that they might shoot themselves in the foot.
-            self.setFieldError('name', structured(dedent('''
+            self.setFieldError(
+                "name",
+                structured(
+                    dedent(
+                        """
             <div class="inline-warning">
               <p>Changing your name will change your
                   public OpenID identifier. This means that you might be
@@ -2691,12 +2826,17 @@ class PersonEditView(PersonRenameFormMixin, BasePersonEditView):
               <p>If you click 'Save' again, we will rename your account
                  anyway.
               </p>
-            </div>'''),))
-            self.i_know_this_is_an_openid_security_issue_input = dedent("""\
+            </div>"""
+                    ),
+                ),
+            )
+            self.i_know_this_is_an_openid_security_issue_input = dedent(
+                """\
                 <input type="hidden"
                        id="i_know_this_is_an_openid_security_issue"
                        name="i_know_this_is_an_openid_security_issue"
-                       value="1">""")
+                       value="1">"""
+            )
 
     @action(_("Save Changes"), name="save")
     def action_save(self, action, data):
@@ -2705,18 +2845,19 @@ class PersonEditView(PersonRenameFormMixin, BasePersonEditView):
         # change their name, but the permission setting for the attribute is
         # launchpad.Moderate, which only allows admins and registry.  A user
         # must have launchpad.Edit to access this page.
-        if 'name' in data:
-            new_name = data['name']
+        if "name" in data:
+            new_name = data["name"]
             removeSecurityProxy(self.context).name = new_name
-            del data['name']
+            del data["name"]
         self.updateContextFromData(data)
         self.request.response.addInfoNotification(
-            'The changes to your personal details have been saved.')
+            "The changes to your personal details have been saved."
+        )
 
 
 class PersonBrandingView(BrandingChangeView):
 
-    field_names = ['logo', 'mugshot']
+    field_names = ["logo", "mugshot"]
     schema = IPerson
 
 
@@ -2732,17 +2873,19 @@ class PersonEditEmailsView(LaunchpadFormView):
     schema = IEmailAddress
 
     custom_widget_VALIDATED_SELECTED = CustomWidgetFactory(
-        LaunchpadRadioWidget, orientation='vertical')
+        LaunchpadRadioWidget, orientation="vertical"
+    )
     custom_widget_UNVALIDATED_SELECTED = CustomWidgetFactory(
-        LaunchpadRadioWidget, orientation='vertical')
+        LaunchpadRadioWidget, orientation="vertical"
+    )
 
-    label = 'Change your email settings'
+    label = "Change your email settings"
 
     def initialize(self):
-        require_fresh_login(self.request, self.context, '+editemails')
+        require_fresh_login(self.request, self.context, "+editemails")
         if self.context.is_team:
             # +editemails is not available on teams.
-            name = self.request['PATH_INFO'].split('/')[-1]
+            name = self.request["PATH_INFO"].split("/")[-1]
             raise NotFound(self, name, request=self.request)
         super().initialize()
 
@@ -2754,10 +2897,13 @@ class PersonEditEmailsView(LaunchpadFormView):
         addresses.
         """
         super().setUpFields()
-        self.form_fields = (self._validated_emails_field() +
-                            self._unvalidated_emails_field() +
-                            FormFields(TextLine(__name__='newemail',
-                                                title='Add a new address')))
+        self.form_fields = (
+            self._validated_emails_field()
+            + self._unvalidated_emails_field()
+            + FormFields(
+                TextLine(__name__="newemail", title="Add a new address")
+            )
+        )
 
     @property
     def initial_values(self):
@@ -2778,25 +2924,30 @@ class PersonEditEmailsView(LaunchpadFormView):
         unvalidated = self.unvalidated_addresses
         if len(unvalidated) > 0:
             unvalidated = unvalidated.pop()
-        return dict(VALIDATED_SELECTED=validated,
-                    UNVALIDATED_SELECTED=unvalidated)
+        return dict(
+            VALIDATED_SELECTED=validated, UNVALIDATED_SELECTED=unvalidated
+        )
 
     def _validated_emails_field(self):
         """Create a field with a vocabulary of validated emails.
 
         :return: A Choice field containing the list of validated emails
         """
-        terms = [SimpleTerm(term, term.email)
-                 for term in self.context.validatedemails]
+        terms = [
+            SimpleTerm(term, term.email)
+            for term in self.context.validatedemails
+        ]
         preferred = self.context.preferredemail
         if preferred:
             terms.insert(0, SimpleTerm(preferred, preferred.email))
 
         return FormFields(
-            Choice(__name__='VALIDATED_SELECTED',
-                   title=_('These addresses are confirmed as being yours'),
-                   source=SimpleVocabulary(terms),
-                   ))
+            Choice(
+                __name__="VALIDATED_SELECTED",
+                title=_("These addresses are confirmed as being yours"),
+                source=SimpleVocabulary(terms),
+            )
+        )
 
     def _unvalidated_emails_field(self):
         """Create a field with a vocabulary of unvalidated and guessed emails.
@@ -2811,15 +2962,19 @@ class PersonEditEmailsView(LaunchpadFormView):
                 term = SimpleTerm(term, term.email)
             terms.append(term)
         if self.validated_addresses:
-            title = _('These addresses may also be yours')
+            title = _("These addresses may also be yours")
         else:
-            title = _('These addresses may be yours')
+            title = _("These addresses may be yours")
 
         return FormFields(
-            Choice(__name__='UNVALIDATED_SELECTED', title=title,
-                   source=SimpleVocabulary(terms)))
-
-    def _validate_selected_address(self, data, field='VALIDATED_SELECTED'):
+            Choice(
+                __name__="UNVALIDATED_SELECTED",
+                title=title,
+                source=SimpleVocabulary(terms),
+            )
+        )
+
+    def _validate_selected_address(self, data, field="VALIDATED_SELECTED"):
         """A generic validator for this view's actions.
 
         Makes sure one (and only one) email address is selected and that
@@ -2844,16 +2999,24 @@ class PersonEditEmailsView(LaunchpadFormView):
             assert person == self.context, (
                 "differing ids in emailaddress.person.id(%s,%d) == "
                 "self.context.id(%s,%d) (%s)"
-                % (person.name, person.id, self.context.name, self.context.id,
-                   email.email))
+                % (
+                    person.name,
+                    person.id,
+                    self.context.name,
+                    self.context.id,
+                    email.email,
+                )
+            )
         elif isinstance(email, str):
             tokenset = getUtility(ILoginTokenSet)
             email = tokenset.searchByEmailRequesterAndType(
-                email, self.context, LoginTokenType.VALIDATEEMAIL)
+                email, self.context, LoginTokenType.VALIDATEEMAIL
+            )
             assert email is not None, "Couldn't find login token!"
         else:
-            raise AssertionError("Selected address was not EmailAddress "
-                                 "or unicode string!")
+            raise AssertionError(
+                "Selected address was not EmailAddress " "or unicode string!"
+            )
 
         # Return the EmailAddress/LoginToken object for use in any
         # further validation.
@@ -2879,110 +3042,136 @@ class PersonEditEmailsView(LaunchpadFormView):
         """
         emailset = set(self.context.unvalidatedemails)
         emailset = emailset.union(
-            [guessed for guessed in self.context.guessedemails
-             if guessed.email not in emailset])
+            [
+                guessed
+                for guessed in self.context.guessedemails
+                if guessed.email not in emailset
+            ]
+        )
         return emailset
 
     def validate_action_remove_validated(self, action, data):
         """Make sure the user selected an email address to remove."""
-        emailaddress = self._validate_selected_address(data,
-                                                       'VALIDATED_SELECTED')
+        emailaddress = self._validate_selected_address(
+            data, "VALIDATED_SELECTED"
+        )
         if emailaddress is None:
             return self.errors
 
         if self.context.preferredemail == emailaddress:
             self.addError(
                 "You can't remove %s because it's your contact email "
-                "address." % self.context.preferredemail.email)
+                "address." % self.context.preferredemail.email
+            )
             return self.errors
         return self.errors
 
-    @action(_("Remove"), name="remove_validated",
-            validator=validate_action_remove_validated)
+    @action(
+        _("Remove"),
+        name="remove_validated",
+        validator=validate_action_remove_validated,
+    )
     def action_remove_validated(self, action, data):
         """Delete the selected (validated) email address."""
-        emailaddress = data['VALIDATED_SELECTED']
+        emailaddress = data["VALIDATED_SELECTED"]
         emailaddress.destroySelf()
         self.request.response.addInfoNotification(
-            "The email address '%s' has been removed." % emailaddress.email)
+            "The email address '%s' has been removed." % emailaddress.email
+        )
         self.next_url = self.action_url
 
     def validate_action_set_preferred(self, action, data):
         """Make sure the user selected an address."""
-        emailaddress = self._validate_selected_address(data,
-                                                       'VALIDATED_SELECTED')
+        emailaddress = self._validate_selected_address(
+            data, "VALIDATED_SELECTED"
+        )
         if emailaddress is None:
             return self.errors
 
         if emailaddress.status == EmailAddressStatus.PREFERRED:
             self.request.response.addInfoNotification(
-                "%s is already set as your contact address." % (
-                    emailaddress.email))
+                "%s is already set as your contact address."
+                % (emailaddress.email)
+            )
         return self.errors
 
-    @action(_("Set as Contact Address"), name="set_preferred",
-            validator=validate_action_set_preferred)
+    @action(
+        _("Set as Contact Address"),
+        name="set_preferred",
+        validator=validate_action_set_preferred,
+    )
     def action_set_preferred(self, action, data):
         """Set the selected email as preferred for the person in context."""
-        emailaddress = data['VALIDATED_SELECTED']
+        emailaddress = data["VALIDATED_SELECTED"]
         if emailaddress.status != EmailAddressStatus.PREFERRED:
             self.context.setPreferredEmail(emailaddress)
             self.request.response.addInfoNotification(
-                "Your contact address has been changed to: %s" % (
-                    emailaddress.email))
+                "Your contact address has been changed to: %s"
+                % (emailaddress.email)
+            )
         self.next_url = self.action_url
 
     def validate_action_confirm(self, action, data):
         """Make sure the user selected an email address to confirm."""
-        self._validate_selected_address(data, 'UNVALIDATED_SELECTED')
+        self._validate_selected_address(data, "UNVALIDATED_SELECTED")
         return self.errors
 
-    @action(_('Confirm'), name='validate', validator=validate_action_confirm)
+    @action(_("Confirm"), name="validate", validator=validate_action_confirm)
     def action_confirm(self, action, data):
         """Mail a validation URL to the selected email address."""
-        email = data['UNVALIDATED_SELECTED']
+        email = data["UNVALIDATED_SELECTED"]
         if IEmailAddress.providedBy(email):
             email = email.email
         token = getUtility(ILoginTokenSet).new(
-            self.context, getUtility(ILaunchBag).login, email,
-            LoginTokenType.VALIDATEEMAIL)
+            self.context,
+            getUtility(ILaunchBag).login,
+            email,
+            LoginTokenType.VALIDATEEMAIL,
+        )
         token.sendEmailValidationRequest()
         self.request.response.addInfoNotification(
             "An email message was sent to '%s' with "
             "instructions on how to confirm that "
-            "it belongs to you." % email)
+            "it belongs to you." % email
+        )
         self.next_url = self.action_url
 
     def validate_action_remove_unvalidated(self, action, data):
         """Make sure the user selected an email address to remove."""
-        email = self._validate_selected_address(data, 'UNVALIDATED_SELECTED')
+        email = self._validate_selected_address(data, "UNVALIDATED_SELECTED")
         if email is not None and IEmailAddress.providedBy(email):
             assert self.context.preferredemail.id != email.id
         return self.errors
 
-    @action(_("Remove"), name="remove_unvalidated",
-            validator=validate_action_remove_unvalidated)
+    @action(
+        _("Remove"),
+        name="remove_unvalidated",
+        validator=validate_action_remove_unvalidated,
+    )
     def action_remove_unvalidated(self, action, data):
         """Delete the selected (un-validated) email address.
 
         This selected address can be either on the EmailAddress table
         marked with status NEW, or in the LoginToken table.
         """
-        emailaddress = data['UNVALIDATED_SELECTED']
+        emailaddress = data["UNVALIDATED_SELECTED"]
         if IEmailAddress.providedBy(emailaddress):
             emailaddress.destroySelf()
             email = emailaddress.email
         elif isinstance(emailaddress, str):
             logintokenset = getUtility(ILoginTokenSet)
             logintokenset.deleteByEmailRequesterAndType(
-                emailaddress, self.context, LoginTokenType.VALIDATEEMAIL)
+                emailaddress, self.context, LoginTokenType.VALIDATEEMAIL
+            )
             email = emailaddress
         else:
-            raise AssertionError("Selected address was not EmailAddress "
-                                 "or Unicode string!")
+            raise AssertionError(
+                "Selected address was not EmailAddress " "or Unicode string!"
+            )
 
         self.request.response.addInfoNotification(
-            "The email address '%s' has been removed." % email)
+            "The email address '%s' has been removed." % email
+        )
         self.next_url = self.action_url
 
     def validate_action_add_email(self, action, data):
@@ -2991,15 +3180,16 @@ class PersonEditEmailsView(LaunchpadFormView):
         The email address must be syntactically valid and must not already
         be in use.
         """
-        has_errors = bool(self.validate_widgets(data, ['newemail']))
+        has_errors = bool(self.validate_widgets(data, ["newemail"]))
         if has_errors:
             # We know that 'newemail' is empty.
             return self.errors
 
-        newemail = data['newemail']
+        newemail = data["newemail"]
         if not valid_email(newemail):
             self.addError(
-                "'%s' doesn't seem to be a valid email address." % newemail)
+                "'%s' doesn't seem to be a valid email address." % newemail
+            )
             return self.errors
 
         # XXX j.c.sackett 2010-09-15 bug=628247, 576757 There is a validation
@@ -3017,38 +3207,49 @@ class PersonEditEmailsView(LaunchpadFormView):
                     "added this email address before or because our system "
                     "detected it as being yours. If it was detected by our "
                     "system, it's probably shown on this page and is waiting "
-                    "to be confirmed as yours." % newemail)
+                    "to be confirmed as yours." % newemail
+                )
             else:
                 owner = email.person
                 owner_name = quote(owner.name)
-                merge_url = (
-                    '%s/+requestmerge?field.dupe_person=%s'
-                    % (canonical_url(getUtility(IPersonSet)), owner_name))
-                self.addError(structured(
-                    "The email address '%s' is already registered to "
-                    '<a href="%s">%s</a>. If you think that is a '
-                    'duplicated account, you can <a href="%s">merge it'
-                    "</a> into your account.",
-                    newemail, canonical_url(owner), owner.displayname,
-                    merge_url))
+                merge_url = "%s/+requestmerge?field.dupe_person=%s" % (
+                    canonical_url(getUtility(IPersonSet)),
+                    owner_name,
+                )
+                self.addError(
+                    structured(
+                        "The email address '%s' is already registered to "
+                        '<a href="%s">%s</a>. If you think that is a '
+                        'duplicated account, you can <a href="%s">merge it'
+                        "</a> into your account.",
+                        newemail,
+                        canonical_url(owner),
+                        owner.displayname,
+                        merge_url,
+                    )
+                )
         return self.errors
 
     @action(_("Add"), name="add_email", validator=validate_action_add_email)
     def action_add_email(self, action, data):
         """Register a new email for the person in context."""
-        newemail = data['newemail']
+        newemail = data["newemail"]
         token = getUtility(ILoginTokenSet).new(
-            self.context, getUtility(ILaunchBag).login, newemail,
-            LoginTokenType.VALIDATEEMAIL)
+            self.context,
+            getUtility(ILaunchBag).login,
+            newemail,
+            LoginTokenType.VALIDATEEMAIL,
+        )
         token.sendEmailValidationRequest()
 
         self.request.response.addInfoNotification(
-                "A confirmation message has been sent to '%s'. "
-                "Follow the instructions in that message to confirm that the "
-                "address is yours. "
-                "(If the message doesn't arrive in a few minutes, your mail "
-                "provider might use 'greylisting', which could delay the "
-                "message for up to an hour or two.)" % newemail)
+            "A confirmation message has been sent to '%s'. "
+            "Follow the instructions in that message to confirm that the "
+            "address is yours. "
+            "(If the message doesn't arrive in a few minutes, your mail "
+            "provider might use 'greylisting', which could delay the "
+            "message for up to an hour or two.)" % newemail
+        )
         self.next_url = self.action_url
 
 
@@ -3059,14 +3260,15 @@ class PersonEditMailingListsView(LaunchpadFormView):
     schema = IEmailAddress
 
     custom_widget_mailing_list_auto_subscribe_policy = (
-        LaunchpadRadioWidgetWithDescription)
+        LaunchpadRadioWidgetWithDescription
+    )
 
-    label = 'Change your mailing list subscriptions'
+    label = "Change your mailing list subscriptions"
 
     def initialize(self):
         if self.context.is_team:
             # +editmailinglists is not available on teams.
-            name = self.request['PATH_INFO'].split('/')[-1]
+            name = self.request["PATH_INFO"].split("/")[-1]
             raise NotFound(self, name, request=self.request)
         super().initialize()
 
@@ -3078,8 +3280,9 @@ class PersonEditMailingListsView(LaunchpadFormView):
         addresses.
         """
         super().setUpFields()
-        self.form_fields = (self._mailing_list_fields()
-                            + self._autosubscribe_policy_fields())
+        self.form_fields = (
+            self._mailing_list_fields() + self._autosubscribe_policy_fields()
+        )
 
     @property
     def initial_values(self):
@@ -3096,12 +3299,14 @@ class PersonEditMailingListsView(LaunchpadFormView):
         # Defaults for the mailing list autosubscribe buttons.
         return dict(
             mailing_list_auto_subscribe_policy=(
-                self.context.mailing_list_auto_subscribe_policy))
+                self.context.mailing_list_auto_subscribe_policy
+            )
+        )
 
     def setUpWidgets(self, context=None):
         """See `LaunchpadFormView`."""
         super().setUpWidgets(context)
-        widget = self.widgets['mailing_list_auto_subscribe_policy']
+        widget = self.widgets["mailing_list_auto_subscribe_policy"]
         widget.display_label = False
 
     def _mailing_list_subscription_type(self, mailing_list):
@@ -3116,7 +3321,7 @@ class PersonEditMailingListsView(LaunchpadFormView):
         if subscription is None:
             return "Don't subscribe"
         elif subscription.email_address is None:
-            return 'Preferred address'
+            return "Preferred address"
         else:
             return subscription.email_address
 
@@ -3131,27 +3336,35 @@ class PersonEditMailingListsView(LaunchpadFormView):
         terms = [
             SimpleTerm("Preferred address"),
             SimpleTerm("Don't subscribe"),
-            ]
+        ]
         for email in self.validated_addresses:
             terms.append(SimpleTerm(email, email.email))
         for team in self.context.teams_participated_in:
             mailing_list = mailing_list_set.get(team.name)
             if mailing_list is not None and mailing_list.is_usable:
-                name = 'subscription.%s' % team.name
+                name = "subscription.%s" % team.name
                 value = self._mailing_list_subscription_type(mailing_list)
-                field = Choice(__name__=name,
-                               title=team.name,
-                               source=SimpleVocabulary(terms), default=value)
+                field = Choice(
+                    __name__=name,
+                    title=team.name,
+                    source=SimpleVocabulary(terms),
+                    default=value,
+                )
                 fields.append(field)
         return FormFields(*fields)
 
     def _autosubscribe_policy_fields(self):
         """Create a field for each mailing list auto-subscription option."""
         return FormFields(
-            Choice(__name__='mailing_list_auto_subscribe_policy',
-                   title=_('When should Launchpad automatically subscribe '
-                           'you to a team&#x2019;s mailing list?'),
-                   source=MailingListAutoSubscribePolicy))
+            Choice(
+                __name__="mailing_list_auto_subscribe_policy",
+                title=_(
+                    "When should Launchpad automatically subscribe "
+                    "you to a team&#x2019;s mailing list?"
+                ),
+                source=MailingListAutoSubscribePolicy,
+            )
+        )
 
     @property
     def mailing_list_widgets(self):
@@ -3159,14 +3372,14 @@ class PersonEditMailingListsView(LaunchpadFormView):
         mailing_list_set = getUtility(IMailingListSet)
         widgets = []
         for widget in self.widgets:
-            if widget.name.startswith('field.subscription.'):
+            if widget.name.startswith("field.subscription."):
                 team_name = widget.label
                 mailing_list = mailing_list_set.get(team_name)
-                assert mailing_list is not None, 'Missing mailing list'
+                assert mailing_list is not None, "Missing mailing list"
                 widget_dict = dict(
                     team=mailing_list.team,
                     widget=widget,
-                    )
+                )
                 widgets.append(widget_dict)
                 # We'll put the label in the first column, so don't include it
                 # in the second column.
@@ -3190,20 +3403,25 @@ class PersonEditMailingListsView(LaunchpadFormView):
         Valid addresses are the ones presented as options for the mailing
         list widgets.
         """
-        names = [widget_dict['widget'].context.getName()
-                 for widget_dict in self.mailing_list_widgets]
+        names = [
+            widget_dict["widget"].context.getName()
+            for widget_dict in self.mailing_list_widgets
+        ]
         self.validate_widgets(data, names)
         return self.errors
 
-    @action(_("Update Subscriptions"), name="update_subscriptions",
-            validator=validate_action_update_subscriptions)
+    @action(
+        _("Update Subscriptions"),
+        name="update_subscriptions",
+        validator=validate_action_update_subscriptions,
+    )
     def action_update_subscriptions(self, action, data):
         """Change the user's mailing list subscriptions."""
         mailing_list_set = getUtility(IMailingListSet)
         dirty = False
-        prefix_length = len('subscription.')
+        prefix_length = len("subscription.")
         for widget_dict in self.mailing_list_widgets:
-            widget = widget_dict['widget']
+            widget = widget_dict["widget"]
             mailing_list_name = widget.context.getName()[prefix_length:]
             mailing_list = mailing_list_set.get(mailing_list_name)
             new_value = data[widget.context.getName()]
@@ -3229,8 +3447,7 @@ class PersonEditMailingListsView(LaunchpadFormView):
                     else:
                         mailing_list.changeAddress(self.context, new_value)
         if dirty:
-            self.request.response.addInfoNotification(
-                "Subscriptions updated.")
+            self.request.response.addInfoNotification("Subscriptions updated.")
         self.next_url = self.action_url
 
     def validate_action_update_autosubscribe_policy(self, action, data):
@@ -3240,19 +3457,21 @@ class PersonEditMailingListsView(LaunchpadFormView):
         # required for LaunchpadFormView to tell apart the three <form>
         # elements on the page.
 
-        widget = self.widgets['mailing_list_auto_subscribe_policy']
+        widget = self.widgets["mailing_list_auto_subscribe_policy"]
         self.validate_widgets(data, widget.name)
         return self.errors
 
     @action(
-        _('Update Policy'),
+        _("Update Policy"),
         name="update_autosubscribe_policy",
-        validator=validate_action_update_autosubscribe_policy)
+        validator=validate_action_update_autosubscribe_policy,
+    )
     def action_update_autosubscribe_policy(self, action, data):
-        newpolicy = data['mailing_list_auto_subscribe_policy']
+        newpolicy = data["mailing_list_auto_subscribe_policy"]
         self.context.mailing_list_auto_subscribe_policy = newpolicy
         self.request.response.addInfoNotification(
-            'Your auto-subscribe policy has been updated.')
+            "Your auto-subscribe policy has been updated."
+        )
         self.next_url = self.action_url
 
 
@@ -3275,6 +3494,7 @@ class BaseWithStats:
 @delegate_to(ISourcePackageRelease)
 class SourcePackageReleaseWithStats(BaseWithStats):
     """An ISourcePackageRelease, with extra stats added."""
+
     pass
 
 
@@ -3287,7 +3507,8 @@ class SourcePackagePublishingHistoryWithStats(BaseWithStats):
 @implementer(IPersonRelatedSoftwareMenu)
 class PersonRelatedSoftwareView(LaunchpadView):
     """View for +related-packages."""
-    _max_results_key = 'summary_list_size'
+
+    _max_results_key = "summary_list_size"
 
     @property
     def max_results_to_display(self):
@@ -3295,7 +3516,7 @@ class PersonRelatedSoftwareView(LaunchpadView):
 
     @property
     def page_title(self):
-        return 'Related packages'
+        return "Related packages"
 
     @cachedproperty
     def related_projects(self):
@@ -3305,24 +3526,28 @@ class PersonRelatedSoftwareView(LaunchpadView):
         A project dict has the following keys: title, url, is_owner,
         is_driver, is_bugsupervisor.
         """
+
         def decorate(pillarnames):
             projects = []
             for pillarname in pillarnames:
                 pillar = pillarname.pillar
                 project = {}
-                project['title'] = pillar.title
-                project['url'] = canonical_url(pillar)
+                project["title"] = pillar.title
+                project["url"] = canonical_url(pillar)
                 person = self.context
-                project['is_owner'] = person.inTeam(pillar.owner)
-                project['is_driver'] = person.inTeam(pillar.driver)
-                project['is_bug_supervisor'] = False
+                project["is_owner"] = person.inTeam(pillar.owner)
+                project["is_driver"] = person.inTeam(pillar.driver)
+                project["is_bug_supervisor"] = False
                 if IHasBugSupervisor.providedBy(pillar):
-                    project['is_bug_supervisor'] = (
-                        person.inTeam(pillar.bug_supervisor))
+                    project["is_bug_supervisor"] = person.inTeam(
+                        pillar.bug_supervisor
+                    )
                 projects.append(project)
             return projects
+
         return DecoratedResultSet(
-            self._related_projects, bulk_decorator=decorate)
+            self._related_projects, bulk_decorator=decorate
+        )
 
     @cachedproperty
     def first_five_related_projects(self):
@@ -3342,7 +3567,8 @@ class PersonRelatedSoftwareView(LaunchpadView):
     @cachedproperty
     def projects_header_message(self):
         return self._tableHeaderMessage(
-            self.related_projects_count, label='project')
+            self.related_projects_count, label="project"
+        )
 
     @cachedproperty
     def _related_projects(self):
@@ -3350,14 +3576,16 @@ class PersonRelatedSoftwareView(LaunchpadView):
         user = getUtility(ILaunchBag).user
         return self.context.getAffiliatedPillars(user)
 
-    def _tableHeaderMessage(self, count, label='package'):
+    def _tableHeaderMessage(self, count, label="package"):
         """Format a header message for the tables on the summary page."""
         if count > 1:
-            label += 's'
+            label += "s"
         if count > self.max_results_to_display:
-            header_message = (
-                "Displaying first %d %s out of %d total" % (
-                    self.max_results_to_display, label, count))
+            header_message = "Displaying first %d %s out of %d total" % (
+                self.max_results_to_display,
+                label,
+                count,
+            )
         else:
             header_message = "%d %s" % (count, label)
 
@@ -3383,7 +3611,7 @@ class PersonRelatedSoftwareView(LaunchpadView):
             # Ensure the SPR.upload_archive is also considered.
             archives.add(package.upload_archive)
             for archive in archives:
-                if check_permission('launchpad.View', archive):
+                if check_permission("launchpad.View", archive):
                     results.append(package)
                     break
 
@@ -3402,7 +3630,8 @@ class PersonRelatedSoftwareView(LaunchpadView):
         """
         # This code causes two SQL queries to be generated.
         results = self._addStatsToPackages(
-            packages[:self.max_results_to_display])
+            packages[: self.max_results_to_display]
+        )
         header_message = self._tableHeaderMessage(packages.count())
         return results, header_message
 
@@ -3419,7 +3648,8 @@ class PersonRelatedSoftwareView(LaunchpadView):
         """
         # This code causes two SQL queries to be generated.
         results = self._addStatsToPublishings(
-            publishings[:self.max_results_to_display])
+            publishings[: self.max_results_to_display]
+        )
         header_message = self._tableHeaderMessage(publishings.count())
         return results, header_message
 
@@ -3456,12 +3686,11 @@ class PersonRelatedSoftwareView(LaunchpadView):
 
     @property
     def latest_synchronised_publishings_with_stats(self):
-        """Return the latest synchronised publishings, including stats.
-
-        """
+        """Return the latest synchronised publishings, including stats."""
         publishings = self.context.getLatestSynchronisedPublishings()
         results, header_message = self._getDecoratedPublishingsSummary(
-            publishings)
+            publishings
+        )
         self.synchronised_packages_header_message = header_message
         return results
 
@@ -3477,9 +3706,11 @@ class PersonRelatedSoftwareView(LaunchpadView):
         # Calculate all the failed builds with one query.
         build_set = getUtility(IBinaryPackageBuildSet)
         package_release_ids = [
-            package_release.id for package_release in package_releases]
+            package_release.id for package_release in package_releases
+        ]
         all_builds = build_set.getBuildsBySourcePackageRelease(
-            package_release_ids)
+            package_release_ids
+        )
         # Make a dictionary of lists of builds keyed by SourcePackageRelease
         # and a dictionary of "needs build" state keyed by the same.
         builds_by_package = {}
@@ -3489,42 +3720,54 @@ class PersonRelatedSoftwareView(LaunchpadView):
             needs_build_by_package[package.id] = False
         for build in all_builds:
             if build.status == BuildStatus.FAILEDTOBUILD:
-                builds_by_package[
-                    build.source_package_release.id].append(build)
+                builds_by_package[build.source_package_release.id].append(
+                    build
+                )
             needs_build = build.status in [
                 BuildStatus.NEEDSBUILD,
                 BuildStatus.MANUALDEPWAIT,
                 BuildStatus.CHROOTWAIT,
-                ]
+            ]
             needs_build_by_package[
-                build.source_package_release.id] = needs_build
+                build.source_package_release.id
+            ] = needs_build
 
         return (builds_by_package, needs_build_by_package)
 
     def _addStatsToPackages(self, package_releases):
         """Add stats to the given package releases, and return them."""
         builds_by_package, needs_build_by_package = self._calculateBuildStats(
-            package_releases)
+            package_releases
+        )
 
         return [
             SourcePackageReleaseWithStats(
-                package, builds_by_package[package.id],
-                needs_build_by_package[package.id])
-            for package in package_releases]
+                package,
+                builds_by_package[package.id],
+                needs_build_by_package[package.id],
+            )
+            for package in package_releases
+        ]
 
     def _addStatsToPublishings(self, publishings):
         """Add stats to the given publishings, and return them."""
         filtered_spphs = [
-            spph for spph in publishings if
-            check_permission('launchpad.View', spph)]
+            spph
+            for spph in publishings
+            if check_permission("launchpad.View", spph)
+        ]
         builds_by_package, needs_build_by_package = self._calculateBuildStats(
-            [spph.sourcepackagerelease for spph in filtered_spphs])
+            [spph.sourcepackagerelease for spph in filtered_spphs]
+        )
 
         return [
             SourcePackagePublishingHistoryWithStats(
-                spph, builds_by_package[spph.sourcepackagerelease.id],
-                needs_build_by_package[spph.sourcepackagerelease.id])
-            for spph in filtered_spphs]
+                spph,
+                builds_by_package[spph.sourcepackagerelease.id],
+                needs_build_by_package[spph.sourcepackagerelease.id],
+            )
+            for spph in filtered_spphs
+        ]
 
     def setUpBatch(self, packages):
         """Set up the batch navigation for the page being viewed.
@@ -3539,7 +3782,8 @@ class PersonRelatedSoftwareView(LaunchpadView):
 
 class PersonMaintainedPackagesView(PersonRelatedSoftwareView):
     """View for +maintained-packages."""
-    _max_results_key = 'default_batch_size'
+
+    _max_results_key = "default_batch_size"
 
     def initialize(self):
         """Set up the batch navigation."""
@@ -3553,7 +3797,8 @@ class PersonMaintainedPackagesView(PersonRelatedSoftwareView):
 
 class PersonUploadedPackagesView(PersonRelatedSoftwareView):
     """View for +uploaded-packages."""
-    _max_results_key = 'default_batch_size'
+
+    _max_results_key = "default_batch_size"
 
     def initialize(self):
         """Set up the batch navigation."""
@@ -3567,7 +3812,8 @@ class PersonUploadedPackagesView(PersonRelatedSoftwareView):
 
 class PersonPPAPackagesView(PersonRelatedSoftwareView):
     """View for +ppa-packages."""
-    _max_results_key = 'default_batch_size'
+
+    _max_results_key = "default_batch_size"
 
     def initialize(self):
         """Set up the batch navigation."""
@@ -3588,7 +3834,8 @@ class PersonPPAPackagesView(PersonRelatedSoftwareView):
 
 class PersonSynchronisedPackagesView(PersonRelatedSoftwareView):
     """View for +synchronised-packages."""
-    _max_results_key = 'default_batch_size'
+
+    _max_results_key = "default_batch_size"
 
     def initialize(self):
         """Set up the batch navigation."""
@@ -3612,12 +3859,12 @@ class PersonSynchronisedPackagesView(PersonRelatedSoftwareView):
 
 class PersonRelatedProjectsView(PersonRelatedSoftwareView):
     """View for +related-projects."""
-    _max_results_key = 'default_batch_size'
+
+    _max_results_key = "default_batch_size"
 
     def initialize(self):
         """Set up the batch navigation."""
-        self.batchnav = BatchNavigator(
-            self.related_projects, self.request)
+        self.batchnav = BatchNavigator(self.related_projects, self.request)
         self.batch = list(self.batchnav.currentBatch())
 
     @property
@@ -3627,62 +3874,71 @@ class PersonRelatedProjectsView(PersonRelatedSoftwareView):
 
 class PersonOwnedTeamsView(PersonRelatedSoftwareView):
     """View for +owned-teams."""
+
     page_title = "Owned teams"
 
     def initialize(self):
         """Set up the batch navigation."""
         self.batchnav = BatchNavigator(
-            self.context.getOwnedTeams(self.user), self.request)
-        self.batchnav.setHeadings('team', 'teams')
+            self.context.getOwnedTeams(self.user), self.request
+        )
+        self.batchnav.setHeadings("team", "teams")
         self.batch = list(self.batchnav.currentBatch())
 
 
 class PersonOAuthTokensView(LaunchpadView):
     """Where users can see/revoke their non-expired access tokens."""
 
-    label = 'Authorized applications'
+    label = "Authorized applications"
 
     def initialize(self):
-        if self.request.method == 'POST':
+        if self.request.method == "POST":
             self.expireToken()
 
     @property
     def access_tokens(self):
         return sorted(
             self.context.oauth_access_tokens,
-            key=lambda token: token.consumer.key)
+            key=lambda token: token.consumer.key,
+        )
 
     @property
     def request_tokens(self):
         return sorted(
             self.context.oauth_request_tokens,
-            key=lambda token: token.consumer.key)
+            key=lambda token: token.consumer.key,
+        )
 
     def expireToken(self):
         """Expire the token with the key contained in the request's form."""
         form = self.request.form
         consumer = getUtility(IOAuthConsumerSet).getByKey(
-            form.get('consumer_key'))
-        token_key = form.get('token_key')
-        token_type = form.get('token_type')
-        if token_type == 'access_token':
+            form.get("consumer_key")
+        )
+        token_key = form.get("token_key")
+        token_type = form.get("token_type")
+        if token_type == "access_token":
             token = consumer.getAccessToken(token_key)
-        elif token_type == 'request_token':
+        elif token_type == "request_token":
             token = consumer.getRequestToken(token_key)
         else:
-            raise UnexpectedFormData("Invalid form value for token_type: %r"
-                                     % token_type)
+            raise UnexpectedFormData(
+                "Invalid form value for token_type: %r" % token_type
+            )
         if token is not None:
-            token.date_expires = datetime.now(pytz.timezone('UTC'))
+            token.date_expires = datetime.now(pytz.timezone("UTC"))
             self.request.response.addInfoNotification(
-                "Authorization revoked successfully.")
+                "Authorization revoked successfully."
+            )
             self.request.response.redirect(canonical_url(self.user))
         else:
             self.request.response.addInfoNotification(
                 "Couldn't find authorization given to %s. Maybe it has been "
-                "revoked already?" % consumer.key)
+                "revoked already?" % consumer.key
+            )
         self.request.response.redirect(
-            canonical_url(self.context, view_name='+oauth-tokens'))
+            canonical_url(self.context, view_name="+oauth-tokens")
+        )
 
 
 class PersonOCIRegistryCredentialsView(LaunchpadView):
@@ -3690,8 +3946,9 @@ class PersonOCIRegistryCredentialsView(LaunchpadView):
 
     @cachedproperty
     def oci_registry_credentials(self):
-        return list(getUtility(
-            IOCIRegistryCredentialsSet).findByOwner(self.context))
+        return list(
+            getUtility(IOCIRegistryCredentialsSet).findByOwner(self.context)
+        )
 
     page_title = "OCI registry credentials"
 
@@ -3723,8 +3980,11 @@ class PersonEditOCIRegistryCredentialsView(LaunchpadFormView):
 
     @cachedproperty
     def oci_registry_credentials(self):
-        return list(getUtility(IOCIRegistryCredentialsSet).findByOwner(
-            self.default_owner))
+        return list(
+            getUtility(IOCIRegistryCredentialsSet).findByOwner(
+                self.default_owner
+            )
+        )
 
     schema = Interface
 
@@ -3742,71 +4002,87 @@ class PersonEditOCIRegistryCredentialsView(LaunchpadFormView):
         return "%s.%d" % (name, credentials_id)
 
     def getEditFieldsRow(self, credentials=None):
-        id = getattr(credentials, 'id', None)
+        id = getattr(credentials, "id", None)
         owner = Choice(
-            vocabulary=(
-                'AllUserTeamsParticipationPlusSelfSimpleDisplay'),
+            vocabulary=("AllUserTeamsParticipationPlusSelfSimpleDisplay"),
             default=credentials.owner,
-            __name__=self._getFieldName('owner', id))
+            __name__=self._getFieldName("owner", id),
+        )
 
         username = TextLine(
-            __name__=self._getFieldName('username', id),
+            __name__=self._getFieldName("username", id),
             default=credentials.username,
-            required=False, readonly=False)
+            required=False,
+            readonly=False,
+        )
 
         password = Password(
-            __name__=self._getFieldName('password', id),
+            __name__=self._getFieldName("password", id),
             default=None,
-            required=False, readonly=False)
+            required=False,
+            readonly=False,
+        )
 
         confirm_password = Password(
-            __name__=self._getFieldName('confirm_password', id),
+            __name__=self._getFieldName("confirm_password", id),
             default=None,
-            required=False, readonly=False)
+            required=False,
+            readonly=False,
+        )
 
         url = TextLine(
-            __name__=self._getFieldName('url', id),
+            __name__=self._getFieldName("url", id),
             default=credentials.url,
-            required=True, readonly=False)
+            required=True,
+            readonly=False,
+        )
 
         region = TextLine(
-            __name__=self._getFieldName('region', id),
+            __name__=self._getFieldName("region", id),
             default=credentials.region,
-            required=False, readonly=False)
+            required=False,
+            readonly=False,
+        )
 
         delete = Bool(
-            __name__=self._getFieldName('delete', id),
+            __name__=self._getFieldName("delete", id),
             default=False,
-            required=True, readonly=False)
+            required=True,
+            readonly=False,
+        )
 
         return owner, username, password, confirm_password, url, region, delete
 
     def getAddFieldsRow(self):
-        add_url = TextLine(
-            __name__='add_url',
-            required=False, readonly=False)
+        add_url = TextLine(__name__="add_url", required=False, readonly=False)
         add_region = TextLine(
-            __name__='add_region',
-            required=False, readonly=False)
+            __name__="add_region", required=False, readonly=False
+        )
         add_owner = Choice(
-            __name__='add_owner',
-            vocabulary=(
-                'AllUserTeamsParticipationPlusSelfSimpleDisplay'),
+            __name__="add_owner",
+            vocabulary=("AllUserTeamsParticipationPlusSelfSimpleDisplay"),
             default=self.default_owner,
-            required=False, readonly=False)
+            required=False,
+            readonly=False,
+        )
         add_username = TextLine(
-            __name__='add_username',
-            required=False, readonly=False)
+            __name__="add_username", required=False, readonly=False
+        )
         add_password = Password(
-            __name__='add_password',
-            required=False, readonly=False)
+            __name__="add_password", required=False, readonly=False
+        )
         add_confirm_password = Password(
-            __name__='add_confirm_password',
-            required=False, readonly=False)
+            __name__="add_confirm_password", required=False, readonly=False
+        )
 
         return (
-            add_url, add_region, add_owner, add_username,
-            add_password, add_confirm_password)
+            add_url,
+            add_region,
+            add_owner,
+            add_username,
+            add_password,
+            add_confirm_password,
+        )
 
     def _parseFieldName(self, field_name):
         """Parse a combined field name as described in `_getFieldName`.
@@ -3817,13 +4093,15 @@ class PersonEditOCIRegistryCredentialsView(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:
             credentials_id = int(field_bits[1])
         except ValueError:
             raise UnexpectedFormData(
-                "Cannot parse field name: %s" % field_name)
+                "Cannot parse field name: %s" % field_name
+            )
         return field_type, credentials_id
 
     def setUpFields(self):
@@ -3845,7 +4123,7 @@ class PersonEditOCIRegistryCredentialsView(LaunchpadFormView):
 
     @property
     def label(self):
-        return 'Edit OCI registry credentials'
+        return "Edit OCI registry credentials"
 
     @property
     def cancel_url(self):
@@ -3853,20 +4131,25 @@ class PersonEditOCIRegistryCredentialsView(LaunchpadFormView):
 
     def getCredentialsWidgets(self, credentials):
         widgets_by_name = {widget.name: widget for widget in self.widgets}
-        owner_field_name = (
-                "field." + self._getFieldName("owner", credentials.id))
-        username_field_name = (
-                "field." + self._getFieldName("username", credentials.id))
-        password_field_name = (
-                "field." + self._getFieldName("password", credentials.id))
-        confirm_password_field_name = (
-                "field." + self._getFieldName("confirm_password",
-                                              credentials.id))
+        owner_field_name = "field." + self._getFieldName(
+            "owner", credentials.id
+        )
+        username_field_name = "field." + self._getFieldName(
+            "username", credentials.id
+        )
+        password_field_name = "field." + self._getFieldName(
+            "password", credentials.id
+        )
+        confirm_password_field_name = "field." + self._getFieldName(
+            "confirm_password", credentials.id
+        )
         url_field_name = "field." + self._getFieldName("url", credentials.id)
         region_field_name = "field." + self._getFieldName(
-            "region", credentials.id)
-        delete_field_name = (
-                "field." + self._getFieldName("delete", credentials.id))
+            "region", credentials.id
+        )
+        delete_field_name = "field." + self._getFieldName(
+            "delete", credentials.id
+        )
         return {
             "owner": widgets_by_name[owner_field_name],
             "username": widgets_by_name[username_field_name],
@@ -3874,7 +4157,7 @@ class PersonEditOCIRegistryCredentialsView(LaunchpadFormView):
             "confirm_password": widgets_by_name[confirm_password_field_name],
             "url": widgets_by_name[url_field_name],
             "region": widgets_by_name[region_field_name],
-            "delete": widgets_by_name[delete_field_name]
+            "delete": widgets_by_name[delete_field_name],
         }
 
     def parseData(self, data):
@@ -3887,26 +4170,32 @@ class PersonEditOCIRegistryCredentialsView(LaunchpadFormView):
         add_password = data["add_password"]
         add_confirm_password = data["add_confirm_password"]
         if add_url or add_username or add_password or add_confirm_password:
-            parsed_data.setdefault(None, {
-                "username": add_username,
-                "password": add_password,
-                "confirm_password": add_confirm_password,
-                "url": add_url,
-                "region": add_region,
-                "owner": add_owner,
-                "action": "add",
-            })
+            parsed_data.setdefault(
+                None,
+                {
+                    "username": add_username,
+                    "password": add_password,
+                    "confirm_password": add_confirm_password,
+                    "url": add_url,
+                    "region": add_region,
+                    "owner": add_owner,
+                    "action": "add",
+                },
+            )
         for field_name in (
-                name for name in data if name.split(".")[0] == "owner"):
+            name for name in data if name.split(".")[0] == "owner"
+        ):
             _, credentials_id = self._parseFieldName(field_name)
-            owner_field_name = self._getFieldName(
-                "owner", credentials_id)
+            owner_field_name = self._getFieldName("owner", credentials_id)
             username_field_name = self._getFieldName(
-                "username", credentials_id)
+                "username", credentials_id
+            )
             password_field_name = self._getFieldName(
-                "password", credentials_id)
+                "password", credentials_id
+            )
             confirm_password_field_name = self._getFieldName(
-                "confirm_password", credentials_id)
+                "confirm_password", credentials_id
+            )
             url_field_name = self._getFieldName("url", credentials_id)
             region_field_name = self._getFieldName("region", credentials_id)
             delete_field_name = self._getFieldName("delete", credentials_id)
@@ -3914,15 +4203,18 @@ class PersonEditOCIRegistryCredentialsView(LaunchpadFormView):
                 action = "delete"
             else:
                 action = "change"
-            parsed_data.setdefault(credentials_id, {
-                "username": data.get(username_field_name),
-                "password": data.get(password_field_name),
-                "confirm_password": data.get(confirm_password_field_name),
-                "url": data.get(url_field_name),
-                "region": data.get(region_field_name),
-                "owner": data.get(owner_field_name),
-                "action": action,
-            })
+            parsed_data.setdefault(
+                credentials_id,
+                {
+                    "username": data.get(username_field_name),
+                    "password": data.get(password_field_name),
+                    "confirm_password": data.get(confirm_password_field_name),
+                    "url": data.get(url_field_name),
+                    "region": data.get(region_field_name),
+                    "owner": data.get(owner_field_name),
+                    "action": action,
+                },
+            )
 
         return parsed_data
 
@@ -3935,14 +4227,14 @@ class PersonEditOCIRegistryCredentialsView(LaunchpadFormView):
         if password or confirm_password:
             if password != confirm_password:
                 self.setFieldError(
-                    self._getFieldName(
-                        "confirm_password", credentials.id),
-                    "Passwords do not match.")
+                    self._getFieldName("confirm_password", credentials.id),
+                    "Passwords do not match.",
+                )
             else:
                 raw_credentials = {
                     "username": username,
                     "password": password,
-                    }
+                }
                 if region:
                     raw_credentials["region"] = region
                 credentials.setCredentials(raw_credentials)
@@ -3957,13 +4249,12 @@ class PersonEditOCIRegistryCredentialsView(LaunchpadFormView):
 
     def deleteCredentials(self, credentials):
         push_rule_set = getUtility(IOCIPushRuleSet)
-        if not push_rule_set.findByRegistryCredentials(
-                credentials).is_empty():
+        if not push_rule_set.findByRegistryCredentials(credentials).is_empty():
             self.setFieldError(
-                self._getFieldName(
-                    "delete", credentials.id),
+                self._getFieldName("delete", credentials.id),
                 "These credentials cannot be deleted as there are "
-                "push rules defined that still use them.")
+                "push rules defined that still use them.",
+            )
         else:
             credentials.destroySelf()
 
@@ -3981,46 +4272,52 @@ class PersonEditOCIRegistryCredentialsView(LaunchpadFormView):
                         "add_password",
                         "Please make sure the new "
                         "password matches the "
-                        "confirm password field.")
+                        "confirm password field.",
+                    )
                     return
 
-                credentials = {
-                    'username': username,
-                    'password': password}
+                credentials = {"username": username, "password": password}
                 if region:
                     credentials["region"] = region
                 try:
                     getUtility(IOCIRegistryCredentialsSet).new(
-                        registrant=self.user, owner=owner, url=url,
-                        credentials=credentials)
+                        registrant=self.user,
+                        owner=owner,
+                        url=url,
+                        credentials=credentials,
+                    )
                 except OCIRegistryCredentialsAlreadyExist:
                     self.setFieldError(
                         "add_url",
                         "Credentials already exist "
                         "with the same URL and "
-                        "username.")
+                        "username.",
+                    )
             else:
-                credentials = {'username': username}
+                credentials = {"username": username}
                 if region:
                     credentials["region"] = region
                 try:
                     getUtility(IOCIRegistryCredentialsSet).new(
-                        registrant=self.user, owner=owner, url=url,
-                        credentials=credentials)
+                        registrant=self.user,
+                        owner=owner,
+                        url=url,
+                        credentials=credentials,
+                    )
                 except OCIRegistryCredentialsAlreadyExist:
                     self.setFieldError(
                         "add_url",
                         "Credentials already exist "
-                        "with the same URL and username.")
+                        "with the same URL and username.",
+                    )
         else:
-            self.setFieldError(
-                "add_url",
-                "Registry URL cannot be empty.")
+            self.setFieldError("add_url", "Registry URL cannot be empty.")
 
     def updateCredentialsFromData(self, parsed_data):
         credentials_map = {
             credentials.id: credentials
-            for credentials in self.oci_registry_credentials}
+            for credentials in self.oci_registry_credentials
+        }
 
         for credentials_id, parsed_credentials in parsed_data.items():
             credentials = credentials_map.get(credentials_id)
@@ -4048,16 +4345,17 @@ class PersonEditOCIRegistryCredentialsView(LaunchpadFormView):
 
 class PersonLiveFSView(LaunchpadView):
     """Default view for the list of live filesystems owned by a person."""
-    page_title = 'LiveFS'
+
+    page_title = "LiveFS"
 
     @property
     def label(self):
-        return 'Live filesystems for %s' % self.context.display_name
+        return "Live filesystems for %s" % self.context.display_name
 
     @property
     def livefses(self):
         livefses = getUtility(ILiveFSSet).getByPerson(self.context)
-        return livefses.order_by('name')
+        return livefses.order_by("name")
 
     @property
     def livefses_navigator(self):
@@ -4071,21 +4369,25 @@ class PersonLiveFSView(LaunchpadView):
 class PersonTimeZoneForm(Interface):
 
     time_zone = Choice(
-        vocabulary='TimezoneName', title=_('Time zone'), required=True,
+        vocabulary="TimezoneName",
+        title=_("Time zone"),
+        required=True,
         description=_(
-            'Once the time zone is correctly set, events '
-            'in Launchpad will be displayed in local time.'))
+            "Once the time zone is correctly set, events "
+            "in Launchpad will be displayed in local time."
+        ),
+    )
 
 
 class PersonEditTimeZoneView(LaunchpadFormView):
     """Edit a person's time zone."""
 
     schema = PersonTimeZoneForm
-    page_title = label = 'Set timezone'
+    page_title = label = "Set timezone"
 
     @property
     def initial_values(self):
-        return {'time_zone': self.context.time_zone}
+        return {"time_zone": self.context.time_zone}
 
     @property
     def next_url(self):
@@ -4096,9 +4398,9 @@ class PersonEditTimeZoneView(LaunchpadFormView):
     @action(_("Update"), name="update")
     def action_update(self, action, data):
         """Set the time zone for the person."""
-        timezone = data.get('time_zone')
+        timezone = data.get("time_zone")
         if timezone is None:
-            raise UnexpectedFormData('No location received.')
+            raise UnexpectedFormData("No location received.")
         # XXX salgado, 2012-02-16, bug=933699: Use setLocation() because it's
         # the cheaper way to set the timezone of a person. Once the bug is
         # fixed we'll be able to get rid of this hack.
@@ -4113,20 +4415,17 @@ def archive_to_person(archive):
 class IEmailToPerson(Interface):
     """Schema for contacting a user via email through Launchpad."""
 
-    from_ = TextLine(
-        title=_('From'), required=True, readonly=False)
+    from_ = TextLine(title=_("From"), required=True, readonly=False)
 
-    subject = TextLine(
-        title=_('Subject'), required=True, readonly=False)
+    subject = TextLine(title=_("Subject"), required=True, readonly=False)
 
-    message = Text(
-        title=_('Message'), required=True, readonly=False)
+    message = Text(title=_("Message"), required=True, readonly=False)
 
     @invariant
     def subject_and_message_are_not_empty(data):
         """Raise an Invalid error if the message or subject is empty."""
-        if '' in (data.message.strip(), data.subject.strip()):
-            raise Invalid('You must provide a subject and a message.')
+        if "" in (data.message.strip(), data.subject.strip()):
+            raise Invalid("You must provide a subject and a message.")
 
 
 @implementer(INotificationRecipientSet)
@@ -4191,24 +4490,28 @@ class ContactViaWebNotificationRecipientSet:
         if self.primary_reason is self.TO_USER:
             reason = (
                 'using the "Contact this user" link on your profile page '
-                '(%s)' % canonical_url(person_or_team))
-            header = 'ContactViaWeb user'
+                "(%s)" % canonical_url(person_or_team)
+            )
+            header = "ContactViaWeb user"
         elif self.primary_reason is self.TO_ADMINS:
             reason = (
                 'using the "Contact this team\'s admins" link on the '
-                '%s team page (%s)' % (
-                    person_or_team.displayname,
-                    canonical_url(person_or_team)))
-            header = 'ContactViaWeb owner (%s team)' % person_or_team.name
+                "%s team page (%s)"
+                % (person_or_team.displayname, canonical_url(person_or_team))
+            )
+            header = "ContactViaWeb owner (%s team)" % person_or_team.name
         else:
             # self.primary_reason is self.TO_MEMBERS.
             reason = (
-                'to each member of the %s team using the '
-                '"Contact this team" link on the %s team page (%s)' % (
+                "to each member of the %s team using the "
+                '"Contact this team" link on the %s team page (%s)'
+                % (
                     person_or_team.displayname,
                     person_or_team.displayname,
-                    canonical_url(person_or_team)))
-            header = 'ContactViaWeb member (%s team)' % person_or_team.name
+                    canonical_url(person_or_team),
+                )
+            )
+            header = "ContactViaWeb member (%s team)" % person_or_team.name
         return (reason, header)
 
     def _getDescription(self, person_or_team):
@@ -4218,24 +4521,28 @@ class ContactViaWebNotificationRecipientSet:
         :type person_or_team: `IPerson`.
         """
         if self.primary_reason is self.TO_USER:
-            return (
-                'You are contacting %s (%s).' %
-                (person_or_team.displayname, person_or_team.name))
+            return "You are contacting %s (%s)." % (
+                person_or_team.displayname,
+                person_or_team.name,
+            )
         elif self.primary_reason is self.TO_ADMINS:
-            return (
-                'You are contacting the %s (%s) team admins.' %
-                (person_or_team.displayname, person_or_team.name))
+            return "You are contacting the %s (%s) team admins." % (
+                person_or_team.displayname,
+                person_or_team.name,
+            )
         else:
             # This is a team without a contact address (self.TO_MEMBERS).
             recipients_count = len(self)
             if recipients_count == 1:
-                plural_suffix = ''
+                plural_suffix = ""
             else:
-                plural_suffix = 's'
-            text = '%d member%s' % (recipients_count, plural_suffix)
-            return (
-                'You are contacting %s of the %s (%s) team directly.'
-                % (text, person_or_team.displayname, person_or_team.name))
+                plural_suffix = "s"
+            text = "%d member%s" % (recipients_count, plural_suffix)
+            return "You are contacting %s of the %s (%s) team directly." % (
+                text,
+                person_or_team.displayname,
+                person_or_team.name,
+            )
 
     @cachedproperty
     def _all_recipients(self):
@@ -4258,7 +4565,8 @@ class ContactViaWebNotificationRecipientSet:
                     all_recipients[email] = recipient
         elif self._primary_recipient.is_valid_person_or_team:
             email = removeSecurityProxy(
-                self._primary_recipient).preferredemail.email
+                self._primary_recipient
+            ).preferredemail.email
             all_recipients[email] = self._primary_recipient
         else:
             # The user or team owner is not active.
@@ -4272,7 +4580,8 @@ class ContactViaWebNotificationRecipientSet:
     def getRecipients(self):
         """See `INotificationRecipientSet`."""
         yield from sorted(
-            self._all_recipients.values(), key=attrgetter('displayname'))
+            self._all_recipients.values(), key=attrgetter("displayname")
+        )
 
     def getRecipientPersons(self):
         """See `INotificationRecipientSet`."""
@@ -4296,7 +4605,8 @@ class ContactViaWebNotificationRecipientSet:
             if self.primary_reason is self.TO_MEMBERS:
                 # Get the count without loading all the members.
                 self._count_recipients = (
-                    recipient.getMembersWithPreferredEmailsCount())
+                    recipient.getMembersWithPreferredEmailsCount()
+                )
             elif self.primary_reason is self.TO_ADMINS:
                 self._count_recipients = len(self._all_recipients)
             elif recipient.is_valid_person_or_team:
@@ -4314,7 +4624,8 @@ class ContactViaWebNotificationRecipientSet:
         """See `INotificationRecipientSet`."""
         if person_or_email not in self:
             raise UnknownRecipientError(
-                '%s in not in the recipients' % person_or_email)
+                "%s in not in the recipients" % person_or_email
+            )
         # All users have the same reason based on the primary recipient.
         return (self._reason, self._header)
 
@@ -4349,7 +4660,7 @@ class EmailToPersonView(LaunchpadFormView):
     """The 'Contact this user' page."""
 
     schema = IEmailToPerson
-    field_names = ['subject', 'message']
+    field_names = ["subject", "message"]
     custom_widget_subject = CustomWidgetFactory(TextWidget, displayWidth=60)
 
     def initialize(self):
@@ -4370,15 +4681,17 @@ class EmailToPersonView(LaunchpadFormView):
         usable_addresses = [self.user.preferredemail]
         usable_addresses.extend(self.user.validatedemails)
         terms = [SimpleTerm(email, email.email) for email in usable_addresses]
-        field = Choice(__name__='field.from_',
-                       title=_('From'),
-                       source=SimpleVocabulary(terms),
-                       default=terms[0].value)
+        field = Choice(
+            __name__="field.from_",
+            title=_("From"),
+            source=SimpleVocabulary(terms),
+            default=terms[0].value,
+        )
         # Get the order right; the From field should be first, followed by the
         # Subject and then Message fields.
-        self.form_fields = FormFields(*chain((field, ), self.form_fields))
+        self.form_fields = FormFields(*chain((field,), self.form_fields))
 
-    label = 'Contact user'
+    label = "Contact user"
 
     @cachedproperty
     def recipients(self):
@@ -4389,35 +4702,46 @@ class EmailToPersonView(LaunchpadFormView):
         """
         return ContactViaWebNotificationRecipientSet(self.user, self.context)
 
-    @action(_('Send'), name='send')
+    @action(_("Send"), name="send")
     def action_send(self, action, data):
         """Send an email to the user."""
-        sender_email = data['field.from_'].email
-        subject = data['subject']
-        message = data['message']
+        sender_email = data["field.from_"].email
+        subject = data["subject"]
+        message = data["message"]
 
         if not self.recipients:
             self.request.response.addErrorNotification(
-                _('Your message was not sent because the recipient '
-                  'does not have a preferred email address.'))
+                _(
+                    "Your message was not sent because the recipient "
+                    "does not have a preferred email address."
+                )
+            )
             self.next_url = canonical_url(self.context)
             return
         try:
             send_direct_contact_email(
-                sender_email, self.recipients, self.context, subject, message)
+                sender_email, self.recipients, self.context, subject, message
+            )
         except QuotaReachedError as error:
             fmt_date = DateTimeFormatterAPI(self.next_try)
             self.request.response.addErrorNotification(
-                _('Your message was not sent because you have exceeded your '
-                  'daily quota of $quota messages to contact users. '
-                  'Try again $when.', mapping=dict(
-                      quota=error.authorization.message_quota,
-                      when=fmt_date.approximatedate(),
-                      )))
+                _(
+                    "Your message was not sent because you have exceeded your "
+                    "daily quota of $quota messages to contact users. "
+                    "Try again $when.",
+                    mapping=dict(
+                        quota=error.authorization.message_quota,
+                        when=fmt_date.approximatedate(),
+                    ),
+                )
+            )
         else:
             self.request.response.addInfoNotification(
-                _('Message sent to $name',
-                  mapping=dict(name=self.context.displayname)))
+                _(
+                    "Message sent to $name",
+                    mapping=dict(name=self.context.displayname),
+                )
+            )
         self.next_url = canonical_url(self.context)
 
     @property
@@ -4445,7 +4769,8 @@ class EmailToPersonView(LaunchpadFormView):
         """When can the user try again?"""
         throttle_date = IDirectEmailAuthorization(self.user).throttle_date
         interval = as_timedelta(
-            config.launchpad.user_to_user_throttle_interval)
+            config.launchpad.user_to_user_throttle_interval
+        )
         return throttle_date + interval
 
     @property
@@ -4465,13 +4790,13 @@ class EmailToPersonView(LaunchpadFormView):
         """Return the appropriate pagetitle."""
         if self.context.is_team:
             if self.user.inTeam(self.context):
-                return 'Contact your team'
+                return "Contact your team"
             else:
-                return 'Contact this team'
+                return "Contact this team"
         elif self.context == self.user:
-            return 'Contact yourself'
+            return "Contact yourself"
         else:
-            return 'Contact this user'
+            return "Contact this user"
 
 
 class IPersonIndexMenu(Interface):
@@ -4480,10 +4805,15 @@ class IPersonIndexMenu(Interface):
 
 class PersonIndexMenu(NavigationMenu, PersonMenuMixin):
     usedfor = IPersonIndexMenu
-    facet = 'overview'
-    title = 'Change person'
-    links = ('edit', 'administer', 'administer_account', 'branding',
-             'password')
+    facet = "overview"
+    title = "Change person"
+    links = (
+        "edit",
+        "administer",
+        "administer_account",
+        "branding",
+        "password",
+    )
 
 
 classImplements(PersonIndexView, IPersonIndexMenu)
diff --git a/lib/lp/registry/browser/persondistributionsourcepackage.py b/lib/lp/registry/browser/persondistributionsourcepackage.py
index 71e2456..73cbf50 100644
--- a/lib/lp/registry/browser/persondistributionsourcepackage.py
+++ b/lib/lp/registry/browser/persondistributionsourcepackage.py
@@ -4,10 +4,10 @@
 """Views, menus and traversal related to PersonDistributionSourcePackages."""
 
 __all__ = [
-    'PersonDistributionSourcePackageBreadcrumb',
-    'PersonDistributionSourcePackageFacets',
-    'PersonDistributionSourcePackageNavigation',
-    ]
+    "PersonDistributionSourcePackageBreadcrumb",
+    "PersonDistributionSourcePackageFacets",
+    "PersonDistributionSourcePackageNavigation",
+]
 
 
 from zope.component import queryAdapter
@@ -18,18 +18,19 @@ from lp.app.errors import NotFoundError
 from lp.code.browser.vcslisting import PersonTargetDefaultVCSNavigationMixin
 from lp.registry.interfaces.persondistributionsourcepackage import (
     IPersonDistributionSourcePackage,
-    )
+)
 from lp.services.webapp import (
-    canonical_url,
     Navigation,
     StandardLaunchpadFacets,
-    )
+    canonical_url,
+)
 from lp.services.webapp.breadcrumb import Breadcrumb
 from lp.services.webapp.interfaces import IMultiFacetedBreadcrumb
 
 
 class PersonDistributionSourcePackageNavigation(
-        PersonTargetDefaultVCSNavigationMixin, Navigation):
+    PersonTargetDefaultVCSNavigationMixin, Navigation
+):
     usedfor = IPersonDistributionSourcePackage
 
     def traverse(self, branch_name):
@@ -52,19 +53,20 @@ class PersonDistributionSourcePackageBreadcrumb(Breadcrumb):
     def url(self):
         if self._url is None:
             return canonical_url(
-                self.context.distro_source_package, rootsite=self.rootsite)
+                self.context.distro_source_package, rootsite=self.rootsite
+            )
         else:
             return self._url
 
     @property
     def icon(self):
         return queryAdapter(
-            self.context.distro_source_package, IPathAdapter,
-            name='image').icon()
+            self.context.distro_source_package, IPathAdapter, name="image"
+        ).icon()
 
 
 class PersonDistributionSourcePackageFacets(StandardLaunchpadFacets):
     """The links that will appear in the facet menu for an IPersonDSP."""
 
     usedfor = IPersonDistributionSourcePackage
-    enable_only = ['branches']
+    enable_only = ["branches"]
diff --git a/lib/lp/registry/browser/personociproject.py b/lib/lp/registry/browser/personociproject.py
index 251f5db..47344c8 100644
--- a/lib/lp/registry/browser/personociproject.py
+++ b/lib/lp/registry/browser/personociproject.py
@@ -4,13 +4,10 @@
 """Views, menus, and traversal related to `PersonOCIProject`s."""
 
 __all__ = [
-    'PersonOCIProjectNavigation',
-    ]
+    "PersonOCIProjectNavigation",
+]
 
-from zope.component import (
-    getUtility,
-    queryAdapter,
-    )
+from zope.component import getUtility, queryAdapter
 from zope.interface import implementer
 from zope.traversing.interfaces import IPathAdapter
 
@@ -18,24 +15,26 @@ from lp.code.browser.vcslisting import PersonTargetDefaultVCSNavigationMixin
 from lp.oci.interfaces.ocirecipe import IOCIRecipeSet
 from lp.registry.interfaces.personociproject import IPersonOCIProject
 from lp.services.webapp import (
-    canonical_url,
     Navigation,
     StandardLaunchpadFacets,
+    canonical_url,
     stepthrough,
-    )
+)
 from lp.services.webapp.breadcrumb import Breadcrumb
 from lp.services.webapp.interfaces import IMultiFacetedBreadcrumb
 
 
 class PersonOCIProjectNavigation(
-        PersonTargetDefaultVCSNavigationMixin, Navigation):
+    PersonTargetDefaultVCSNavigationMixin, Navigation
+):
 
     usedfor = IPersonOCIProject
 
-    @stepthrough('+recipe')
+    @stepthrough("+recipe")
     def traverse_recipe(self, name):
         return getUtility(IOCIRecipeSet).getByName(
-            self.context.person, self.context.oci_project, name)
+            self.context.person, self.context.oci_project, name
+        )
 
 
 # XXX cjwatson 2019-11-26: Do we need two breadcrumbs, one for the
@@ -52,19 +51,20 @@ class PersonOCIProjectBreadcrumb(Breadcrumb):
     def url(self):
         if self._url is None:
             return canonical_url(
-                self.context.oci_project, rootsite=self.rootsite)
+                self.context.oci_project, rootsite=self.rootsite
+            )
         else:
             return self._url
 
     @property
     def icon(self):
         return queryAdapter(
-            self.context.oci_project, IPathAdapter, name='image').icon()
+            self.context.oci_project, IPathAdapter, name="image"
+        ).icon()
 
 
 class PersonOCIProjectFacets(StandardLaunchpadFacets):
-    """The links that will appear in the facet menu for an `IPersonOCIProject`.
-    """
+    """The facet menu links for an `IPersonOCIProject`."""
 
     usedfor = IPersonOCIProject
-    enable_only = ['branches']
+    enable_only = ["branches"]
diff --git a/lib/lp/registry/browser/personproduct.py b/lib/lp/registry/browser/personproduct.py
index 0af1156..86c8f7d 100644
--- a/lib/lp/registry/browser/personproduct.py
+++ b/lib/lp/registry/browser/personproduct.py
@@ -4,15 +4,12 @@
 """Views, menus and traversal related to PersonProducts."""
 
 __all__ = [
-    'PersonProductBreadcrumb',
-    'PersonProductFacets',
-    'PersonProductNavigation',
-    ]
-
-from zope.component import (
-    getUtility,
-    queryAdapter,
-    )
+    "PersonProductBreadcrumb",
+    "PersonProductFacets",
+    "PersonProductNavigation",
+]
+
+from zope.component import getUtility, queryAdapter
 from zope.interface import implementer
 from zope.traversing.interfaces import IPathAdapter
 
@@ -23,50 +20,52 @@ from lp.code.interfaces.branchnamespace import get_branch_namespace
 from lp.registry.interfaces.personociproject import IPersonOCIProjectFactory
 from lp.registry.interfaces.personproduct import IPersonProduct
 from lp.services.webapp import (
-    canonical_url,
     Navigation,
     StandardLaunchpadFacets,
+    canonical_url,
     stepthrough,
-    )
+)
 from lp.services.webapp.breadcrumb import Breadcrumb
 from lp.services.webapp.interfaces import IMultiFacetedBreadcrumb
 from lp.snappy.interfaces.snap import ISnapSet
 
 
-class PersonProductNavigation(PersonTargetDefaultVCSNavigationMixin,
-                              Navigation):
+class PersonProductNavigation(
+    PersonTargetDefaultVCSNavigationMixin, Navigation
+):
     """Navigation to branches for this person/product."""
+
     usedfor = IPersonProduct
 
-    @stepthrough('+oci')
+    @stepthrough("+oci")
     def traverse_oci(self, name):
         oci_project = self.context.product.getOCIProject(name)
         return getUtility(IPersonOCIProjectFactory).create(
-            self.context.person, oci_project)
+            self.context.person, oci_project
+        )
 
     def traverse(self, branch_name):
         """Look for a branch in the person/product namespace."""
         namespace = get_branch_namespace(
-            person=self.context.person, product=self.context.product)
+            person=self.context.person, product=self.context.product
+        )
         branch = namespace.getByName(branch_name)
         if branch is None:
             raise NotFoundError
         else:
             return branch
 
-    @stepthrough('+snap')
+    @stepthrough("+snap")
     def traverse_snap(self, name):
         return getUtility(ISnapSet).getByPillarAndName(
-            owner=self.context.person,
-            pillar=self.context.product,
-            name=name)
+            owner=self.context.person, pillar=self.context.product, name=name
+        )
 
-    @stepthrough('+charm')
+    @stepthrough("+charm")
     def traverse_charm(self, name):
         return getUtility(ICharmRecipeSet).getByName(
-            owner=self.context.person,
-            project=self.context.product,
-            name=name)
+            owner=self.context.person, project=self.context.product, name=name
+        )
 
 
 @implementer(IMultiFacetedBreadcrumb)
@@ -87,11 +86,12 @@ class PersonProductBreadcrumb(Breadcrumb):
     @property
     def icon(self):
         return queryAdapter(
-            self.context.product, IPathAdapter, name='image').icon()
+            self.context.product, IPathAdapter, name="image"
+        ).icon()
 
 
 class PersonProductFacets(StandardLaunchpadFacets):
     """The links that will appear in the facet menu for an IPerson."""
 
     usedfor = IPersonProduct
-    enable_only = ['branches']
+    enable_only = ["branches"]
diff --git a/lib/lp/registry/browser/pillar.py b/lib/lp/registry/browser/pillar.py
index 7817a7c..4f776e9 100644
--- a/lib/lp/registry/browser/pillar.py
+++ b/lib/lp/registry/browser/pillar.py
@@ -4,14 +4,14 @@
 """Common views for objects that implement `IPillar`."""
 
 __all__ = [
-    'InvolvedMenu',
-    'PillarBugsMenu',
-    'PillarInvolvementView',
-    'PillarViewMixin',
-    'PillarNavigationMixin',
-    'PillarPersonSharingView',
-    'PillarSharingView',
-    ]
+    "InvolvedMenu",
+    "PillarBugsMenu",
+    "PillarInvolvementView",
+    "PillarViewMixin",
+    "PillarNavigationMixin",
+    "PillarPersonSharingView",
+    "PillarSharingView",
+]
 
 import json
 from operator import attrgetter
@@ -20,33 +20,24 @@ from lazr.restful import ResourceJSONEncoder
 from lazr.restful.interfaces import IJSONRequestCache
 from lazr.restful.utils import get_current_web_service_request
 from zope.component import getUtility
-from zope.interface import (
-    implementer,
-    Interface,
-    )
-from zope.schema.vocabulary import (
-    getVocabularyRegistry,
-    SimpleVocabulary,
-    )
+from zope.interface import Interface, implementer
+from zope.schema.vocabulary import SimpleVocabulary, getVocabularyRegistry
 from zope.traversing.browser.absoluteurl import absoluteURL
 
 from lp.app.browser.lazrjs import vocabulary_to_choice_edit_items
 from lp.app.browser.tales import MenuAPI
 from lp.app.browser.vocabulary import vocabulary_filters
-from lp.app.enums import (
-    service_uses_launchpad,
-    ServiceUsage,
-    )
+from lp.app.enums import ServiceUsage, service_uses_launchpad
 from lp.app.interfaces.headings import IHeadingBreadcrumb
 from lp.app.interfaces.launchpad import IServiceUsage
 from lp.app.interfaces.services import IService
 from lp.bugs.browser.structuralsubscription import (
     StructuralSubscriptionMenuMixin,
-    )
+)
 from lp.registry.enums import EXCLUSIVE_TEAM_POLICY
 from lp.registry.interfaces.distributionsourcepackage import (
     IDistributionSourcePackage,
-    )
+)
 from lp.registry.interfaces.distroseries import IDistroSeries
 from lp.registry.interfaces.person import IPersonSet
 from lp.registry.interfaces.pillar import IPillar
@@ -57,29 +48,26 @@ from lp.services.propertycache import cachedproperty
 from lp.services.webapp.authorization import (
     check_permission,
     precache_permission_for_objects,
-    )
+)
 from lp.services.webapp.batching import (
     BatchNavigator,
-    get_batch_properties_for_json_cache,
     StormRangeFactory,
-    )
-from lp.services.webapp.breadcrumb import (
-    Breadcrumb,
-    DisplaynameBreadcrumb,
-    )
+    get_batch_properties_for_json_cache,
+)
+from lp.services.webapp.breadcrumb import Breadcrumb, DisplaynameBreadcrumb
 from lp.services.webapp.interfaces import IMultiFacetedBreadcrumb
 from lp.services.webapp.menu import (
     ApplicationMenu,
-    enabled_with_permission,
     Link,
     NavigationMenu,
-    )
+    enabled_with_permission,
+)
 from lp.services.webapp.publisher import (
-    canonical_url,
     LaunchpadView,
+    canonical_url,
     nearest,
     stepthrough,
-    )
+)
 
 
 @implementer(IHeadingBreadcrumb, IMultiFacetedBreadcrumb)
@@ -103,12 +91,13 @@ class PillarPersonBreadcrumb(Breadcrumb):
         return Breadcrumb(
             self.context.pillar,
             url=canonical_url(self.context.pillar, view_name="+sharing"),
-            text="Sharing", inside=self.context.pillar)
+            text="Sharing",
+            inside=self.context.pillar,
+        )
 
 
 class PillarNavigationMixin:
-
-    @stepthrough('+sharing')
+    @stepthrough("+sharing")
     def traverse_details(self, name):
         """Traverse to the sharing details for a given person."""
         person = getUtility(IPersonSet).getByName(name)
@@ -123,9 +112,14 @@ class IInvolved(Interface):
 
 class InvolvedMenu(NavigationMenu):
     """The get involved menu."""
+
     usedfor = IInvolved
     links = [
-        'report_bug', 'ask_question', 'help_translate', 'register_blueprint']
+        "report_bug",
+        "ask_question",
+        "help_translate",
+        "register_blueprint",
+    ]
 
     @property
     def pillar(self):
@@ -133,26 +127,39 @@ class InvolvedMenu(NavigationMenu):
 
     def report_bug(self):
         return Link(
-            '+filebug', 'Report a bug', site='bugs', icon='bugs',
-            enabled=self.pillar.official_malone)
+            "+filebug",
+            "Report a bug",
+            site="bugs",
+            icon="bugs",
+            enabled=self.pillar.official_malone,
+        )
 
     def ask_question(self):
         return Link(
-            '+addquestion', 'Ask a question', site='answers', icon='answers',
-            enabled=service_uses_launchpad(self.pillar.answers_usage))
+            "+addquestion",
+            "Ask a question",
+            site="answers",
+            icon="answers",
+            enabled=service_uses_launchpad(self.pillar.answers_usage),
+        )
 
     def help_translate(self):
         return Link(
-            '', 'Help translate', site='translations', icon='translations',
-            enabled=service_uses_launchpad(self.pillar.translations_usage))
+            "",
+            "Help translate",
+            site="translations",
+            icon="translations",
+            enabled=service_uses_launchpad(self.pillar.translations_usage),
+        )
 
     def register_blueprint(self):
         return Link(
-            '+addspec',
-            'Register a blueprint',
-            site='blueprints',
-            icon='blueprints',
-            enabled=service_uses_launchpad(self.pillar.blueprints_usage))
+            "+addspec",
+            "Register a blueprint",
+            site="blueprints",
+            icon="blueprints",
+            enabled=service_uses_launchpad(self.pillar.blueprints_usage),
+        )
 
 
 @implementer(IInvolved)
@@ -210,19 +217,22 @@ class PillarInvolvementView(LaunchpadView):
     @property
     def has_involvement(self):
         """This `IPillar` uses Launchpad."""
-        return (self.official_malone
-                or service_uses_launchpad(self.answers_usage)
-                or service_uses_launchpad(self.blueprints_usage)
-                or service_uses_launchpad(self.translations_usage)
-                or service_uses_launchpad(self.codehosting_usage))
+        return (
+            self.official_malone
+            or service_uses_launchpad(self.answers_usage)
+            or service_uses_launchpad(self.blueprints_usage)
+            or service_uses_launchpad(self.translations_usage)
+            or service_uses_launchpad(self.codehosting_usage)
+        )
 
     @property
     def enabled_links(self):
         """The enabled involvement links."""
         menuapi = MenuAPI(self)
-        return sorted((
-            link for link in menuapi.navigation.values() if link.enabled),
-            key=attrgetter('sort_key'))
+        return sorted(
+            (link for link in menuapi.navigation.values() if link.enabled),
+            key=attrgetter("sort_key"),
+        )
 
     @cachedproperty
     def visible_disabled_links(self):
@@ -236,11 +246,12 @@ class PillarInvolvementView(LaunchpadView):
         """
         involved_menu = MenuAPI(self).navigation
         important_links = [
-            involved_menu[name]
-            for name in self.visible_disabled_link_names]
-        return sorted((
-            link for link in important_links if not link.enabled),
-            key=attrgetter('sort_key'))
+            involved_menu[name] for name in self.visible_disabled_link_names
+        ]
+        return sorted(
+            (link for link in important_links if not link.enabled),
+            key=attrgetter("sort_key"),
+        )
 
     @property
     def registration_completeness(self):
@@ -254,24 +265,24 @@ class PillarInvolvementView(LaunchpadView):
 class PillarBugsMenu(ApplicationMenu, StructuralSubscriptionMenuMixin):
     """Base class for pillar bugs menus."""
 
-    facet = 'bugs'
+    facet = "bugs"
     configurable_bugtracker = False
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def bugsupervisor(self):
-        text = 'Change bug supervisor'
-        return Link('+bugsupervisor', text, icon='edit')
+        text = "Change bug supervisor"
+        return Link("+bugsupervisor", text, icon="edit")
 
     def cve(self):
-        text = 'CVE reports'
-        return Link('+cve', text, icon='cve')
+        text = "CVE reports"
+        return Link("+cve", text, icon="cve")
 
     def filebug(self):
-        text = 'Report a bug'
-        return Link('+filebug', text, icon='bug')
+        text = "Report a bug"
+        return Link("+filebug", text, icon="bug")
 
 
-class PillarViewMixin():
+class PillarViewMixin:
     """A mixin for pillar views to populate the json request cache."""
 
     def initialize(self):
@@ -282,9 +293,11 @@ class PillarViewMixin():
         policy_items = [(item.name, item) for item in EXCLUSIVE_TEAM_POLICY]
         team_membership_policy_data = vocabulary_to_choice_edit_items(
             SimpleVocabulary.fromItems(policy_items),
-            value_fn=lambda item: item.name)
-        cache.objects['team_membership_policy_data'] = (
-            team_membership_policy_data)
+            value_fn=lambda item: item.name,
+        )
+        cache.objects[
+            "team_membership_policy_data"
+        ] = team_membership_policy_data
 
 
 class PillarSharingView(LaunchpadView):
@@ -292,17 +305,18 @@ class PillarSharingView(LaunchpadView):
     page_title = "Sharing"
     label = "Sharing information"
 
-    sharing_vocabulary_name = 'NewPillarGrantee'
+    sharing_vocabulary_name = "NewPillarGrantee"
 
     _batch_navigator = None
 
     def _getSharingService(self):
-        return getUtility(IService, 'sharing')
+        return getUtility(IService, "sharing")
 
     @property
     def information_types(self):
         return self._getSharingService().getAllowedInformationTypes(
-            self.context)
+            self.context
+        )
 
     @property
     def bug_sharing_policies(self):
@@ -315,7 +329,8 @@ class PillarSharingView(LaunchpadView):
     @property
     def specification_sharing_policies(self):
         return self._getSharingService().getSpecificationSharingPolicies(
-            self.context)
+            self.context
+        )
 
     @property
     def sharing_permissions(self):
@@ -324,8 +339,7 @@ class PillarSharingView(LaunchpadView):
     @cachedproperty
     def sharing_vocabulary(self):
         registry = getVocabularyRegistry()
-        return registry.get(
-            self.context, self.sharing_vocabulary_name)
+        return registry.get(self.context, self.sharing_vocabulary_name)
 
     @cachedproperty
     def sharing_vocabulary_filters(self):
@@ -337,7 +351,8 @@ class PillarSharingView(LaunchpadView):
             vocabulary=self.sharing_vocabulary_name,
             vocabulary_filters=self.sharing_vocabulary_filters,
             header=self.sharing_vocabulary.displayname,
-            steptitle=self.sharing_vocabulary.step_title)
+            steptitle=self.sharing_vocabulary.step_title,
+        )
 
     @property
     def json_sharing_picker_config(self):
@@ -346,10 +361,12 @@ class PillarSharingView(LaunchpadView):
     def _getBatchNavigator(self, grantees):
         """Return the batch navigator to be used to batch the grantees."""
         return BatchNavigator(
-            grantees, self.request,
+            grantees,
+            self.request,
             hide_counts=True,
             size=config.launchpad.default_batch_size,
-            range_factory=StormRangeFactory(grantees))
+            range_factory=StormRangeFactory(grantees),
+        )
 
     def grantees(self):
         """An `IBatchNavigator` for grantees."""
@@ -365,15 +382,16 @@ class PillarSharingView(LaunchpadView):
     def initialize(self):
         super().initialize()
         cache = IJSONRequestCache(self.request)
-        cache.objects['information_types'] = self.information_types
-        cache.objects['sharing_permissions'] = self.sharing_permissions
-        cache.objects['bug_sharing_policies'] = self.bug_sharing_policies
-        cache.objects['branch_sharing_policies'] = (
-            self.branch_sharing_policies)
-        cache.objects['specification_sharing_policies'] = (
-            self.specification_sharing_policies)
-        cache.objects['has_edit_permission'] = check_permission(
-            "launchpad.Edit", self.context)
+        cache.objects["information_types"] = self.information_types
+        cache.objects["sharing_permissions"] = self.sharing_permissions
+        cache.objects["bug_sharing_policies"] = self.bug_sharing_policies
+        cache.objects["branch_sharing_policies"] = self.branch_sharing_policies
+        cache.objects[
+            "specification_sharing_policies"
+        ] = self.specification_sharing_policies
+        cache.objects["has_edit_permission"] = check_permission(
+            "launchpad.Edit", self.context
+        )
         batch_navigator = self.grantees()
         # Precache LimitedView for all the grantees, partly for performance
         # but mainly because it's possible that the user won't strictly have
@@ -381,18 +399,25 @@ class PillarSharingView(LaunchpadView):
         # see who has access to pillars they drive.  Fixing this in
         # PublicOrPrivateTeamsExistence would very likely be too expensive.
         precache_permission_for_objects(
-            None, 'launchpad.LimitedView',
-            [grantee for grantee, _, _ in batch_navigator.batch])
-        cache.objects['grantee_data'] = (
-            self._getSharingService().jsonGranteeData(batch_navigator.batch))
+            None,
+            "launchpad.LimitedView",
+            [grantee for grantee, _, _ in batch_navigator.batch],
+        )
+        cache.objects[
+            "grantee_data"
+        ] = self._getSharingService().jsonGranteeData(batch_navigator.batch)
         cache.objects.update(
-            get_batch_properties_for_json_cache(self, batch_navigator))
+            get_batch_properties_for_json_cache(self, batch_navigator)
+        )
 
-        grant_counts = (
-            self._getSharingService().getAccessPolicyGrantCounts(self.context))
-        cache.objects['invisible_information_types'] = [
-            count_info[0].title for count_info in grant_counts
-            if count_info[1] == 0]
+        grant_counts = self._getSharingService().getAccessPolicyGrantCounts(
+            self.context
+        )
+        cache.objects["invisible_information_types"] = [
+            count_info[0].title
+            for count_info in grant_counts
+            if count_info[1] == 0
+        ]
 
 
 class PillarPersonSharingView(LaunchpadView):
@@ -406,7 +431,7 @@ class PillarPersonSharingView(LaunchpadView):
 
         self.label = "Information shared with %s" % self.person.displayname
         self.page_title = "%s" % self.person.displayname
-        self.sharing_service = getUtility(IService, 'sharing')
+        self.sharing_service = getUtility(IService, "sharing")
 
         self._loadSharedArtifacts()
 
@@ -414,34 +439,36 @@ class PillarPersonSharingView(LaunchpadView):
         request = get_current_web_service_request()
         branch_data = self._build_branch_template_data(self.branches, request)
         gitrepository_data = self._build_gitrepository_template_data(
-            self.gitrepositories, request)
+            self.gitrepositories, request
+        )
         bug_data = self._build_bug_template_data(self.bugtasks, request)
         spec_data = self._build_specification_template_data(
-            self.specifications, request)
+            self.specifications, request
+        )
         snap_data = self._build_ocirecipe_template_data(self.snaps, request)
         ocirecipe_data = self._build_ocirecipe_template_data(
-            self.ocirecipes, request)
+            self.ocirecipes, request
+        )
         grantee_data = {
-            'displayname': self.person.displayname,
-            'self_link': absoluteURL(self.person, request)
-        }
-        pillar_data = {
-            'self_link': absoluteURL(self.pillar, request)
+            "displayname": self.person.displayname,
+            "self_link": absoluteURL(self.person, request),
         }
-        cache.objects['grantee'] = grantee_data
-        cache.objects['pillar'] = pillar_data
-        cache.objects['bugs'] = bug_data
-        cache.objects['branches'] = branch_data
-        cache.objects['gitrepositories'] = gitrepository_data
-        cache.objects['specifications'] = spec_data
-        cache.objects['snaps'] = snap_data
-        cache.objects['ocirecipes'] = ocirecipe_data
+        pillar_data = {"self_link": absoluteURL(self.pillar, request)}
+        cache.objects["grantee"] = grantee_data
+        cache.objects["pillar"] = pillar_data
+        cache.objects["bugs"] = bug_data
+        cache.objects["branches"] = branch_data
+        cache.objects["gitrepositories"] = gitrepository_data
+        cache.objects["specifications"] = spec_data
+        cache.objects["snaps"] = snap_data
+        cache.objects["ocirecipes"] = ocirecipe_data
 
     def _loadSharedArtifacts(self):
         # As a concrete can by linked via more than one policy, we use sets to
         # filter out dupes.
         artifacts = self.sharing_service.getSharedArtifacts(
-                self.pillar, self.person, self.user)
+            self.pillar, self.person, self.user
+        )
         self.bugtasks = artifacts["bugtasks"]
         self.branches = artifacts["branches"]
         self.gitrepositories = artifacts["gitrepositories"]
@@ -460,34 +487,45 @@ class PillarPersonSharingView(LaunchpadView):
     def _build_specification_template_data(self, specs, request):
         spec_data = []
         for spec in specs:
-            spec_data.append(dict(
-                self_link=absoluteURL(spec, request),
-                web_link=canonical_url(spec, path_only_if_possible=True),
-                name=spec.name,
-                id=spec.id,
-                information_type=spec.information_type.title))
+            spec_data.append(
+                dict(
+                    self_link=absoluteURL(spec, request),
+                    web_link=canonical_url(spec, path_only_if_possible=True),
+                    name=spec.name,
+                    id=spec.id,
+                    information_type=spec.information_type.title,
+                )
+            )
         return spec_data
 
     def _build_branch_template_data(self, branches, request):
         branch_data = []
         for branch in branches:
-            branch_data.append(dict(
-                self_link=absoluteURL(branch, request),
-                web_link=canonical_url(branch, path_only_if_possible=True),
-                branch_name=branch.unique_name,
-                branch_id=branch.id,
-                information_type=branch.information_type.title))
+            branch_data.append(
+                dict(
+                    self_link=absoluteURL(branch, request),
+                    web_link=canonical_url(branch, path_only_if_possible=True),
+                    branch_name=branch.unique_name,
+                    branch_id=branch.id,
+                    information_type=branch.information_type.title,
+                )
+            )
         return branch_data
 
     def _build_gitrepository_template_data(self, repositories, request):
         repository_data = []
         for repository in repositories:
-            repository_data.append(dict(
-                self_link=absoluteURL(repository, request),
-                web_link=canonical_url(repository, path_only_if_possible=True),
-                repository_name=repository.unique_name,
-                repository_id=repository.id,
-                information_type=repository.information_type.title))
+            repository_data.append(
+                dict(
+                    self_link=absoluteURL(repository, request),
+                    web_link=canonical_url(
+                        repository, path_only_if_possible=True
+                    ),
+                    repository_name=repository.unique_name,
+                    repository_id=repository.id,
+                    information_type=repository.information_type.title,
+                )
+            )
         return repository_data
 
     def _build_bug_template_data(self, bugtasks, request):
@@ -497,33 +535,42 @@ class PillarPersonSharingView(LaunchpadView):
             self_link = absoluteURL(bugtask.bug, request)
             importance = bugtask.importance.title.lower()
             information_type = bugtask.bug.information_type.title
-            bug_data.append(dict(
-                self_link=self_link,
-                web_link=web_link,
-                bug_summary=bugtask.bug.title,
-                bug_id=bugtask.bug.id,
-                bug_importance=importance,
-                information_type=information_type))
+            bug_data.append(
+                dict(
+                    self_link=self_link,
+                    web_link=web_link,
+                    bug_summary=bugtask.bug.title,
+                    bug_id=bugtask.bug.id,
+                    bug_importance=importance,
+                    information_type=information_type,
+                )
+            )
         return bug_data
 
     def _build_ocirecipe_template_data(self, oci_recipes, request):
         recipe_data = []
         for recipe in oci_recipes:
-            recipe_data.append(dict(
-                self_link=absoluteURL(recipe, request),
-                web_link=canonical_url(recipe, path_only_if_possible=True),
-                name=recipe.name,
-                id=recipe.id,
-                information_type=recipe.information_type.title))
+            recipe_data.append(
+                dict(
+                    self_link=absoluteURL(recipe, request),
+                    web_link=canonical_url(recipe, path_only_if_possible=True),
+                    name=recipe.name,
+                    id=recipe.id,
+                    information_type=recipe.information_type.title,
+                )
+            )
         return recipe_data
 
     def _build_snap_template_data(self, snaps, request):
         snap_data = []
         for snap in snaps:
-            snap_data.append(dict(
-                self_link=absoluteURL(snap, request),
-                web_link=canonical_url(snap, path_only_if_possible=True),
-                name=snap.name,
-                id=snap.id,
-                information_type=snap.information_type.title))
+            snap_data.append(
+                dict(
+                    self_link=absoluteURL(snap, request),
+                    web_link=canonical_url(snap, path_only_if_possible=True),
+                    name=snap.name,
+                    id=snap.id,
+                    information_type=snap.information_type.title,
+                )
+            )
         return snap_data
diff --git a/lib/lp/registry/browser/poll.py b/lib/lp/registry/browser/poll.py
index 66049fe..b182a91 100644
--- a/lib/lp/registry/browser/poll.py
+++ b/lib/lp/registry/browser/poll.py
@@ -2,36 +2,33 @@
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 __all__ = [
-    'BasePollView',
-    'PollAddView',
-    'PollEditNavigationMenu',
-    'PollEditView',
-    'PollNavigation',
-    'PollOptionAddView',
-    'PollOptionEditView',
-    'PollOverviewMenu',
-    'PollView',
-    'PollVoteView',
-    'PollBreadcrumb',
-    'TeamPollsView',
-    ]
+    "BasePollView",
+    "PollAddView",
+    "PollEditNavigationMenu",
+    "PollEditView",
+    "PollNavigation",
+    "PollOptionAddView",
+    "PollOptionEditView",
+    "PollOverviewMenu",
+    "PollView",
+    "PollVoteView",
+    "PollBreadcrumb",
+    "TeamPollsView",
+]
 
 from zope.browserpage import ViewPageTemplateFile
 from zope.component import getUtility
 from zope.event import notify
 from zope.formlib.widget import CustomWidgetFactory
 from zope.formlib.widgets import TextWidget
-from zope.interface import (
-    implementer,
-    Interface,
-    )
+from zope.interface import Interface, implementer
 from zope.lifecycleevent import ObjectCreatedEvent
 
 from lp.app.browser.launchpadform import (
-    action,
     LaunchpadEditFormView,
     LaunchpadFormView,
-    )
+    action,
+)
 from lp.registry.browser.person import PersonView
 from lp.registry.interfaces.poll import (
     IPoll,
@@ -41,38 +38,37 @@ from lp.registry.interfaces.poll import (
     IVoteSet,
     PollAlgorithm,
     PollSecrecy,
-    )
+)
 from lp.services.helpers import shortlist
 from lp.services.webapp import (
     ApplicationMenu,
-    canonical_url,
-    enabled_with_permission,
     LaunchpadView,
     Link,
     Navigation,
     NavigationMenu,
+    canonical_url,
+    enabled_with_permission,
     stepthrough,
-    )
+)
 from lp.services.webapp.breadcrumb import TitleBreadcrumb
 
 
 class PollEditLinksMixin:
-
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def addnew(self):
-        text = 'Add new option'
-        return Link('+newoption', text, icon='add')
+        text = "Add new option"
+        return Link("+newoption", text, icon="add")
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def edit(self):
-        text = 'Change details'
-        return Link('+edit', text, icon='edit')
+        text = "Change details"
+        return Link("+edit", text, icon="edit")
 
 
 class PollOverviewMenu(ApplicationMenu, PollEditLinksMixin):
     usedfor = IPoll
-    facet = 'overview'
-    links = ['addnew']
+    facet = "overview"
+    links = ["addnew"]
 
 
 class IPollEditMenu(Interface):
@@ -81,8 +77,8 @@ class IPollEditMenu(Interface):
 
 class PollEditNavigationMenu(NavigationMenu, PollEditLinksMixin):
     usedfor = IPollEditMenu
-    facet = 'overview'
-    links = ['addnew', 'edit']
+    facet = "overview"
+    links = ["addnew", "edit"]
 
 
 class IPollActionMenu(Interface):
@@ -91,17 +87,18 @@ class IPollActionMenu(Interface):
 
 class PollActionNavigationMenu(PollEditNavigationMenu):
     usedfor = IPollActionMenu
-    links = ['edit']
+    links = ["edit"]
 
 
 class PollNavigation(Navigation):
 
     usedfor = IPoll
 
-    @stepthrough('+option')
+    @stepthrough("+option")
     def traverse_option(self, name):
         return getUtility(IPollOptionSet).getByPollAndId(
-            self.context, int(name))
+            self.context, int(name)
+        )
 
 
 def vote_sort_key(vote):
@@ -127,7 +124,7 @@ class BasePollView(LaunchpadView):
 
         # For secret polls we can only display the votes after the token
         # is submitted.
-        if self.request.method == 'POST' and self.isSecret():
+        if self.request.method == "POST" and self.isSecret():
             self.setUpTokenAndVotesForSecretPolls()
         elif not self.isSecret():
             self.setUpTokenAndVotesForNonSecretPolls()
@@ -143,8 +140,10 @@ class BasePollView(LaunchpadView):
         """
         assert not self.isSecret() and self.userVoted()
         votes = self.context.getVotesByPerson(self.user)
-        assert votes, (
-            "User %r hasn't voted on poll %r" % (self.user, self.context))
+        assert votes, "User %r hasn't voted on poll %r" % (
+            self.user,
+            self.context,
+        )
         if self.isSimple():
             # Here we have only one vote.
             self.currentVote = votes[0]
@@ -170,22 +169,25 @@ class BasePollView(LaunchpadView):
         in user has voted on this poll.
         """
         assert self.isSecret() and self.userVoted()
-        token = self.request.form.get('token')
+        token = self.request.form.get("token")
         # Only overwrite self.token if the request contains a 'token'
         # variable.
         if token is not None:
             self.token = token
         votes = getUtility(IVoteSet).getByToken(self.token)
         if not votes:
-            self.feedback = ("There's no vote associated with the token %s"
-                             % self.token)
+            self.feedback = (
+                "There's no vote associated with the token %s" % self.token
+            )
             return False
 
         # All votes with a given token must be on the same poll. That means
         # checking the poll of the first vote is enough.
         if votes[0].poll != self.context:
-            self.feedback = ("The vote associated with the token %s is not "
-                             "a vote on this poll." % self.token)
+            self.feedback = (
+                "The vote associated with the token %s is not "
+                "a vote on this poll." % self.token
+            )
             return False
 
         if self.isSimple():
@@ -199,11 +201,11 @@ class BasePollView(LaunchpadView):
 
     def userCanVote(self):
         """Return True if the user is/was eligible to vote on this poll."""
-        return (self.user and self.user.inTeam(self.context.team))
+        return self.user and self.user.inTeam(self.context.team)
 
     def userVoted(self):
         """Return True if the user voted on this poll."""
-        return (self.user and self.context.personVoted(self.user))
+        return self.user and self.context.personVoted(self.user)
 
     def isCondorcet(self):
         """Return True if this poll's type is Condorcet."""
@@ -229,9 +231,12 @@ class PollView(BasePollView):
     def initialize(self):
         super().initialize()
         request = self.request
-        if (self.userCanVote() and self.context.isOpen() and
-            self.context.getActiveOptions()):
-            vote_url = canonical_url(self.context, view_name='+vote')
+        if (
+            self.userCanVote()
+            and self.context.isOpen()
+            and self.context.getActiveOptions()
+        ):
+            vote_url = canonical_url(self.context, view_name="+vote")
             request.response.redirect(vote_url)
 
     def getVotesByOption(self, option):
@@ -265,12 +270,12 @@ class PollVoteView(BasePollView):
     change it. Otherwise they can register their vote.
     """
 
-    default_template = ViewPageTemplateFile(
-        '../templates/poll-vote-simple.pt')
+    default_template = ViewPageTemplateFile("../templates/poll-vote-simple.pt")
     condorcet_template = ViewPageTemplateFile(
-        '../templates/poll-vote-condorcet.pt')
+        "../templates/poll-vote-condorcet.pt"
+    )
 
-    page_title = 'Vote'
+    page_title = "Vote"
 
     @property
     def template(self):
@@ -286,7 +291,7 @@ class PollVoteView(BasePollView):
             # For non-secret polls, the user's vote is always displayed
             self.setUpTokenAndVotesForNonSecretPolls()
 
-        if self.request.method != 'POST':
+        if self.request.method != "POST":
             return
 
         if self.isSecret() and self.userVoted():
@@ -294,7 +299,7 @@ class PollVoteView(BasePollView):
                 # Not possible to get the votes. Probably the token was wrong.
                 return
 
-        if 'showvote' in self.request.form:
+        if "showvote" in self.request.form:
             # The user only wants to see the vote.
             return
 
@@ -318,18 +323,19 @@ class PollVoteView(BasePollView):
         """
         assert self.context.isOpen()