← Back to team overview

launchpad-reviewers team mailing list archive

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

 

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

Commit message:
lp.testing: Apply black

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/+git/launchpad/+merge/427281
-- 
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-testing into launchpad:master.
diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs
index 4961658..2298cc3 100644
--- a/.git-blame-ignore-revs
+++ b/.git-blame-ignore-revs
@@ -88,3 +88,5 @@ ed7d7b97b8fb4ebe92799f922b0fa9c4bd1714e8
 cf7c6a08bd010dd260bff4690d64479fadf37e67
 # apply black to lp.soyuz
 5a98ef6df022b52adc06787b56f2482bc4a28a3e
+# apply black to lp.testing
+15e1c80d57296e830e0dd85f1e03245281a88f02
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index b0c79ac..9593ef2 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -58,6 +58,7 @@ repos:
             |services
             |snappy
             |soyuz
+            |testing
           )/
 -   repo: https://github.com/PyCQA/isort
     rev: 5.9.2
@@ -92,6 +93,7 @@ repos:
             |services
             |snappy
             |soyuz
+            |testing
           )/
     -   id: isort
         alias: isort-black
@@ -116,6 +118,7 @@ repos:
             |services
             |snappy
             |soyuz
+            |testing
           )/
 -   repo: https://github.com/PyCQA/flake8
     rev: 3.9.2
diff --git a/lib/lp/testing/__init__.py b/lib/lp/testing/__init__.py
index 63a45fc..604d2ff 100644
--- a/lib/lp/testing/__init__.py
+++ b/lib/lp/testing/__init__.py
@@ -2,115 +2,99 @@
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 __all__ = [
-    'AbstractYUITestCase',
-    'ANONYMOUS',
-    'admin_logged_in',
-    'anonymous_logged_in',
-    'api_url',
-    'BrowserTestCase',
-    'build_yui_unittest_suite',
-    'celebrity_logged_in',
-    'clean_up_reactor',
-    'ExpectedException',
-    'extract_lp_cache',
-    'FakeAdapterMixin',
-    'FakeLaunchpadRequest',
-    'FakeTime',
-    'launchpadlib_credentials_for',
-    'launchpadlib_for',
-    'login',
-    'login_admin',
-    'login_as',
-    'login_celebrity',
-    'login_person',
-    'login_team',
-    'logout',
-    'map_branch_contents',
-    'normalize_whitespace',
-    'nonblocking_readline',
-    'oauth_access_token_for',
-    'person_logged_in',
-    'record_statements',
-    'reset_logging',
-    'run_process',
-    'run_script',
-    'run_with_login',
-    'run_with_storm_debug',
-    'RunIsolatedTest',
-    'StormStatementRecorder',
-    'test_tales',
-    'TestCase',
-    'TestCaseWithFactory',
-    'time_counter',
-    'unlink_source_packages',
-    'verifyObject',
-    'with_anonymous_login',
-    'with_celebrity_logged_in',
-    'with_person_logged_in',
-    'ws_object',
-    'YUIUnitTestCase',
-    ]
+    "AbstractYUITestCase",
+    "ANONYMOUS",
+    "admin_logged_in",
+    "anonymous_logged_in",
+    "api_url",
+    "BrowserTestCase",
+    "build_yui_unittest_suite",
+    "celebrity_logged_in",
+    "clean_up_reactor",
+    "ExpectedException",
+    "extract_lp_cache",
+    "FakeAdapterMixin",
+    "FakeLaunchpadRequest",
+    "FakeTime",
+    "launchpadlib_credentials_for",
+    "launchpadlib_for",
+    "login",
+    "login_admin",
+    "login_as",
+    "login_celebrity",
+    "login_person",
+    "login_team",
+    "logout",
+    "map_branch_contents",
+    "normalize_whitespace",
+    "nonblocking_readline",
+    "oauth_access_token_for",
+    "person_logged_in",
+    "record_statements",
+    "reset_logging",
+    "run_process",
+    "run_script",
+    "run_with_login",
+    "run_with_storm_debug",
+    "RunIsolatedTest",
+    "StormStatementRecorder",
+    "test_tales",
+    "TestCase",
+    "TestCaseWithFactory",
+    "time_counter",
+    "unlink_source_packages",
+    "verifyObject",
+    "with_anonymous_login",
+    "with_celebrity_logged_in",
+    "with_person_logged_in",
+    "ws_object",
+    "YUIUnitTestCase",
+]
 
-from contextlib import contextmanager
-from datetime import (
-    datetime,
-    timedelta,
-    )
-from fnmatch import fnmatchcase
-from functools import partial
 import io
 import logging
 import os
 import re
-from select import select
 import shutil
 import subprocess
 import sys
 import tempfile
 import time
 import unittest
+from contextlib import contextmanager
+from datetime import datetime, timedelta
+from fnmatch import fnmatchcase
+from functools import partial
+from select import select
 
-from breezy import trace
-from breezy.controldir import (
-    ControlDir,
-    format_registry,
-    )
 import fixtures
-from lazr.restful.testing.tales import test_tales
-from lazr.restful.testing.webservice import FakeRequest
 import lp_sitecustomize
 import oops_datedir_repo.serializer_rfc822
 import pytz
 import simplejson
 import six
-from storm.store import Store
 import subunit
 import testtools
+import transaction
+import zope.event
+from breezy import trace
+from breezy.controldir import ControlDir, format_registry
+from lazr.restful.testing.tales import test_tales
+from lazr.restful.testing.webservice import FakeRequest
+from storm.store import Store
 from testtools.content import Content
 from testtools.content_type import UTF8_TEXT
-from testtools.matchers import (
-    Equals,
-    MatchesRegex,
-    MatchesSetwise,
-    )
+from testtools.matchers import Equals, MatchesRegex, MatchesSetwise
 from testtools.testcase import ExpectedException as TTExpectedException
-import transaction
-from zope.component import (
-    getMultiAdapter,
-    getSiteManager,
-    getUtility,
-    )
-import zope.event
+from zope.component import getMultiAdapter, getSiteManager, getUtility
 from zope.interface import Interface
 from zope.interface.interfaces import ComponentLookupError
 from zope.interface.verify import verifyObject as zope_verifyObject
 from zope.publisher.interfaces import IEndRequestEvent
 from zope.publisher.interfaces.browser import IBrowserRequest
 from zope.security.management import queryInteraction
-from zope.security.proxy import (
-    isinstance as zope_isinstance,
-    removeSecurityProxy,
-    )
+from zope.security.proxy import isinstance as zope_isinstance
+from zope.security.proxy import removeSecurityProxy
 
 from lp.app.interfaces.launchpad import ILaunchpadCelebrities
 from lp.app.interfaces.security import IAuthorization
@@ -120,10 +104,7 @@ from lp.services import features
 from lp.services.config import config
 from lp.services.database.sqlbase import flush_database_caches
 from lp.services.features.flags import FeatureController
-from lp.services.features.model import (
-    FeatureFlag,
-    getFeatureStore,
-    )
+from lp.services.features.model import FeatureFlag, getFeatureStore
 from lp.services.features.webapp import ScopesFromRequest
 from lp.services.osutils import override_environ
 from lp.services.webapp import canonical_url
@@ -132,16 +113,17 @@ from lp.services.webapp.adapter import (
     print_queries,
     start_sql_logging,
     stop_sql_logging,
-    )
+)
 from lp.services.webapp.authorization import (
     clear_cache as clear_permission_cache,
-    )
+)
 from lp.services.webapp.interaction import ANONYMOUS
 from lp.services.webapp.servers import (
     LaunchpadTestRequest,
     StepsToGo,
     WebServiceTestRequest,
-    )
+)
+
 # Import the login helper functions here as it is a much better
 # place to import them from in tests.
 from lp.testing._login import (
@@ -160,22 +142,18 @@ from lp.testing._login import (
     with_anonymous_login,
     with_celebrity_logged_in,
     with_person_logged_in,
-    )
+)
 from lp.testing._webservice import (
     api_url,
     launchpadlib_credentials_for,
     launchpadlib_for,
     oauth_access_token_for,
-    )
+)
 from lp.testing.dbuser import switch_dbuser
-from lp.testing.fixture import (
-    CaptureOops,
-    ZopeEventHandlerFixture,
-    )
+from lp.testing.fixture import CaptureOops, ZopeEventHandlerFixture
 from lp.testing.karma import KarmaRecorder
 from lp.testing.mail_helpers import pop_notifications
 
-
 # The following names have been imported for the purpose of being
 # exported. They are referred to here to silence lint warnings.
 admin_logged_in
@@ -207,7 +185,7 @@ def reset_logging():
     # Remove all handlers from non-root loggers, and remove the loggers too.
     loggerDict = logging.Logger.manager.loggerDict
     for name, logger in list(loggerDict.items()):
-        if name == 'pagetests-access':
+        if name == "pagetests-access":
             # Don't reset the hit logger used by the test infrastructure.
             continue
         if not isinstance(logger, logging.PlaceHolder):
@@ -216,7 +194,7 @@ def reset_logging():
         del loggerDict[name]
 
     # Remove all handlers from the root logger
-    root = logging.getLogger('')
+    root = logging.getLogger("")
     for handler in root.handlers:
         root.removeHandler(handler)
 
@@ -232,6 +210,7 @@ def reset_logging():
     # Reset the setup
     from zope.testrunner.logsupport import Logging
     from zope.testrunner.runner import Runner
+
     Logging(Runner()).global_setup()
     lp_sitecustomize.customize_logger()
 
@@ -324,6 +303,7 @@ class StormStatementRecorder:
     of every SQL query, or a callable that takes the SQL query string and
     returns a boolean decision as to whether a traceback is desired.
     """
+
     # Note that tests for this are in lp.services.webapp.tests.
     # test_statementtracer, because this is really just a small wrapper of
     # the functionality found there.
@@ -334,7 +314,7 @@ class StormStatementRecorder:
 
     @property
     def queries(self):
-        return [record['sql'] for record in self.query_data]
+        return [record["sql"] for record in self.query_data]
 
     @property
     def count(self):
@@ -342,7 +322,7 @@ class StormStatementRecorder:
 
     @property
     def statements(self):
-        return [record['sql'][3] for record in self.query_data]
+        return [record["sql"][3] for record in self.query_data]
 
     def __enter__(self):
         self.query_data = start_sql_logging(self.tracebacks_if)
@@ -387,7 +367,8 @@ class RequestTimelineCollector:
         After each web request the count and queries attributes are updated.
         """
         self._event_fixture = ZopeEventHandlerFixture(
-            self, (IEndRequestEvent, ))
+            self, (IEndRequestEvent,)
+        )
         self._event_fixture.setUp()
         self._active = True
 
@@ -421,9 +402,14 @@ def record_statements(function, *args, **kwargs):
     return (ret, recorder.statements)
 
 
-def record_two_runs(tested_method, item_creator, first_round_number,
-                    second_round_number=None, login_method=None,
-                    record_request=False):
+def record_two_runs(
+    tested_method,
+    item_creator,
+    first_round_number,
+    second_round_number=None,
+    login_method=None,
+    record_request=False,
+):
     """A helper that returns the two storm statement recorders
     obtained when running tested_method after having run the
     method {item_creator} {first_round_number} times and then
@@ -477,6 +463,7 @@ def record_two_runs(tested_method, item_creator, first_round_number,
 def run_with_storm_debug(function, *args, **kwargs):
     """A helper function to run a function with storm debug tracing on."""
     from storm.tracer import debug
+
     debug(True)
     try:
         return function(*args, **kwargs)
@@ -534,10 +521,12 @@ class TestCase(testtools.TestCase, fixtures.TestWithFixtures):
     def assertProvides(self, obj, interface):
         """Assert 'obj' correctly provides 'interface'."""
         from lp.testing.matchers import Provides
+
         self.assertThat(obj, Provides(interface))
 
-    def assertNotifies(self, event_types, propagate, callable_obj,
-                       *args, **kwargs):
+    def assertNotifies(
+        self, event_types, propagate, callable_obj, *args, **kwargs
+    ):
         """Assert that a callable performs a given notification.
 
         :param event_type: One or more event types that notification is
@@ -555,7 +544,7 @@ class TestCase(testtools.TestCase, fixtures.TestWithFixtures):
         with EventRecorder(propagate=propagate) as recorder:
             result = callable_obj(*args, **kwargs)
         if len(recorder.events) == 0:
-            raise AssertionError('No notification was performed.')
+            raise AssertionError("No notification was performed.")
         self.assertEqual(len(event_types), len(recorder.events))
         for event, expected_type in zip(recorder.events, event_types):
             self.assertIsInstance(event, expected_type)
@@ -572,12 +561,11 @@ class TestCase(testtools.TestCase, fixtures.TestWithFixtures):
             result = callable_obj(*args, **kwargs)
         if len(recorder.events) == 1:
             raise AssertionError(
-                'An event was generated: %r.' % recorder.events[0])
+                "An event was generated: %r." % recorder.events[0]
+            )
         elif len(recorder.events) > 1:
-            event_list = ', '.join(
-                [repr(event) for event in recorder.events])
-            raise AssertionError(
-                'Events were generated: %s.' % event_list)
+            event_list = ", ".join([repr(event) for event in recorder.events])
+            raise AssertionError("Events were generated: %s." % event_list)
         return result
 
     def assertSqlAttributeEqualsDate(self, sql_object, attribute_name, date):
@@ -599,22 +587,26 @@ class TestCase(testtools.TestCase, fixtures.TestWithFixtures):
         sql_class = type(sql_object)
         store = Store.of(sql_object)
         found_object = store.find(
-            sql_class, **({'id': sql_object.id, attribute_name: date})).one()
+            sql_class, **({"id": sql_object.id, attribute_name: date})
+        ).one()
         if found_object is None:
             self.fail(
                 "Expected %s to be %s, but it was %s."
-                % (attribute_name, date, getattr(sql_object, attribute_name)))
-
-    def assertTextMatchesExpressionIgnoreWhitespace(self,
-                                                    regular_expression_txt,
-                                                    text):
+                % (attribute_name, date, getattr(sql_object, attribute_name))
+            )
 
+    def assertTextMatchesExpressionIgnoreWhitespace(
+        self, regular_expression_txt, text
+    ):
         def normalise_whitespace(text):
-            return ' '.join(text.split())
+            return " ".join(text.split())
+
         pattern = re.compile(
-            normalise_whitespace(regular_expression_txt), re.S)
+            normalise_whitespace(regular_expression_txt), re.S
+        )
         self.assertIsNot(
-            None, pattern.search(normalise_whitespace(text)), text)
+            None, pattern.search(normalise_whitespace(text)), text
+        )
 
     def assertIsInstance(self, instance, assert_class, msg=None):
         """Assert that an instance is an instance of assert_class.
@@ -623,7 +615,7 @@ class TestCase(testtools.TestCase, fixtures.TestWithFixtures):
         to isinstance.
         """
         if msg is None:
-            msg = '%r is not an instance of %r' % (instance, assert_class)
+            msg = "%r is not an instance of %r" % (instance, assert_class)
         self.assertTrue(zope_isinstance(instance, assert_class), msg)
 
     def assertIsNot(self, expected, observed, msg=None):
@@ -636,8 +628,9 @@ class TestCase(testtools.TestCase, fixtures.TestWithFixtures):
         """Assert that 'iter1' has the same content as 'iter2'."""
         self.assertThat(iter1, MatchesSetwise(*(map(Equals, iter2))))
 
-    def assertRaisesWithContent(self, exception, exception_content,
-                                func, *args, **kwargs):
+    def assertRaisesWithContent(
+        self, exception, exception_content, func, *args, **kwargs
+    ):
         """Check if the given exception is raised with given content.
 
         If the exception isn't raised or the exception_content doesn't
@@ -650,7 +643,8 @@ class TestCase(testtools.TestCase, fixtures.TestWithFixtures):
         """Assert that 'variable' is strictly between two boundaries."""
         self.assertTrue(
             lower_bound < variable < upper_bound,
-            "%r < %r < %r" % (lower_bound, variable, upper_bound))
+            "%r < %r < %r" % (lower_bound, variable, upper_bound),
+        )
 
     def assertVectorEqual(self, *args):
         """Apply assertEqual to all given pairs in one go.
@@ -681,32 +675,36 @@ class TestCase(testtools.TestCase, fixtures.TestWithFixtures):
         The config values will be restored during test tearDown.
         """
         name = self.factory.getUniqueString()
-        body = '\n'.join("%s: %s" % (k, v) for k, v in kwargs.items())
+        body = "\n".join("%s: %s" % (k, v) for k, v in kwargs.items())
         config.push(name, "\n[%s]\n%s\n" % (section, body))
         self.addCleanup(config.pop, name)
 
     def attachOopses(self):
         if len(self.oopses) > 0:
             for (i, report) in enumerate(self.oopses):
-                content = Content(UTF8_TEXT,
-                    partial(oops_datedir_repo.serializer_rfc822.to_chunks,
-                    report))
+                content = Content(
+                    UTF8_TEXT,
+                    partial(
+                        oops_datedir_repo.serializer_rfc822.to_chunks, report
+                    ),
+                )
                 self.addDetail("oops-%d" % i, content)
 
     def attachLibrarianLog(self, fixture):
         """Include the logChunks from fixture in the test details."""
         # Evaluate the log when called, not later, to permit the librarian to
         # be shutdown before the detail is rendered.
-        if 'librarian-log' not in self.getDetails():
+        if "librarian-log" not in self.getDetails():
             chunks = fixture.getLogChunks()
             content = Content(UTF8_TEXT, lambda: chunks)
-            self.addDetail('librarian-log', content)
+            self.addDetail("librarian-log", content)
 
     def setUp(self):
         super().setUp()
         # Circular imports.
         from lp.testing.factory import ObjectFactory
         from lp.testing.layers import LibrarianLayer
+
         self.factory = ObjectFactory()
         # Record the oopses generated during the test run.
         # You can call self.oops_capture.sync() to collect oopses from
@@ -716,8 +714,8 @@ class TestCase(testtools.TestCase, fixtures.TestWithFixtures):
         self.addCleanup(self.attachOopses)
         if LibrarianLayer.librarian_fixture is not None:
             self.addCleanup(
-                self.attachLibrarianLog,
-                LibrarianLayer.librarian_fixture)
+                self.attachLibrarianLog, LibrarianLayer.librarian_fixture
+            )
         # Remove all log handlers, tests should not depend on global logging
         # config but should make their own config instead.
         logger = logging.getLogger()
@@ -733,7 +731,8 @@ class TestCase(testtools.TestCase, fixtures.TestWithFixtures):
         if len(statements) != expected_count:
             self.fail(
                 "Expected %d statements, got %d:\n%s"
-                % (expected_count, len(statements), "\n".join(statements)))
+                % (expected_count, len(statements), "\n".join(statements))
+            )
         return ret
 
     def useTempDir(self):
@@ -746,8 +745,8 @@ class TestCase(testtools.TestCase, fixtures.TestWithFixtures):
 
     def _unfoldEmailHeader(self, header):
         """Unfold a multiline email header."""
-        header = ''.join(header.splitlines())
-        return header.replace('\t', ' ')
+        header = "".join(header.splitlines())
+        return header.replace("\t", " ")
 
     def assertEmailHeadersEqual(self, expected, observed):
         """Assert that two email headers are equal.
@@ -756,21 +755,23 @@ class TestCase(testtools.TestCase, fixtures.TestWithFixtures):
         """
         return self.assertEqual(
             self._unfoldEmailHeader(expected),
-            self._unfoldEmailHeader(observed))
+            self._unfoldEmailHeader(observed),
+        )
 
     def assertStartsWith(self, s, prefix):
         if not s.startswith(prefix):
             raise AssertionError(
-                'string %r does not start with %r' % (s, prefix))
+                "string %r does not start with %r" % (s, prefix)
+            )
 
     def assertEndsWith(self, s, suffix):
         """Asserts that s ends with suffix."""
         if not s.endswith(suffix):
             raise AssertionError(
-                'string %r does not end with %r' % (s, suffix))
+                "string %r does not end with %r" % (s, suffix)
+            )
 
-    def checkPermissions(self, expected_permissions, used_permissions,
-                          type_):
+    def checkPermissions(self, expected_permissions, used_permissions, type_):
         """Check if the used_permissions match expected_permissions.
 
         :param expected_permissions: A dictionary mapping a permission
@@ -781,23 +782,29 @@ class TestCase(testtools.TestCase, fixtures.TestWithFixtures):
         """
         expected = set(expected_permissions.keys())
         self.assertEqual(
-            expected, set(used_permissions.values()),
-            'Unexpected %s permissions' % type_)
+            expected,
+            set(used_permissions.values()),
+            "Unexpected %s permissions" % type_,
+        )
         for permission in expected_permissions:
             attribute_names = {
-                name for name, value in used_permissions.items()
-                if value == permission}
+                name
+                for name, value in used_permissions.items()
+                if value == permission
+            }
             self.assertEqual(
-                expected_permissions[permission], attribute_names,
-                'Unexpected set of attributes with %s permission %s:\n'
-                'Defined but not expected: %s\n'
-                'Expected but not defined: %s'
+                expected_permissions[permission],
+                attribute_names,
+                "Unexpected set of attributes with %s permission %s:\n"
+                "Defined but not expected: %s\n"
+                "Expected but not defined: %s"
                 % (
-                    type_, permission,
-                    sorted(
-                        attribute_names - expected_permissions[permission]),
-                    sorted(
-                        expected_permissions[permission] - attribute_names)))
+                    type_,
+                    permission,
+                    sorted(attribute_names - expected_permissions[permission]),
+                    sorted(expected_permissions[permission] - attribute_names),
+                ),
+            )
 
     def assertEmailQueueLength(self, length, sort_key=None):
         """Pop the email queue, assert its length, and return it.
@@ -806,10 +813,15 @@ class TestCase(testtools.TestCase, fixtures.TestWithFixtures):
         """
         notifications = pop_notifications(sort_key=sort_key)
         self.assertEqual(
-            length, len(notifications),
-            "Expected %d emails, got %d:\n\n%s" % (
-                length, len(notifications),
-                "\n\n".join(str(n) for n in notifications)))
+            length,
+            len(notifications),
+            "Expected %d emails, got %d:\n\n%s"
+            % (
+                length,
+                len(notifications),
+                "\n\n".join(str(n) for n in notifications),
+            ),
+        )
         return notifications
 
     def getWebserviceJSON(self, webservice, url):
@@ -820,12 +832,12 @@ class TestCase(testtools.TestCase, fixtures.TestWithFixtures):
 
 
 class TestCaseWithFactory(TestCase):
-
     def setUp(self, user=ANONYMOUS):
         super().setUp()
         login(user)
         self.addCleanup(logout)
         from lp.testing.factory import LaunchpadObjectFactory
+
         self.factory = LaunchpadObjectFactory()
         self.direct_database_server = False
         self._use_bzr_branch_called = False
@@ -835,7 +847,7 @@ class TestCaseWithFactory(TestCase):
         # necessarily equal logging.getLogger('brz'), so we have to explicitly
         # make it so in order to avoid "No handlers for "brz" logger'
         # messages.
-        trace._brz_logger = logging.getLogger('brz')
+        trace._brz_logger = logging.getLogger("brz")
 
     def getUserBrowser(self, url=None, user=None):
         """Return a Browser logged in as a fresh user, maybe opened at `url`.
@@ -844,6 +856,7 @@ class TestCaseWithFactory(TestCase):
         """
         # Do the import here to avoid issues with import cycles.
         from lp.testing.pages import setupBrowserForUser
+
         login(ANONYMOUS)
         if user is None:
             user = self.factory.makePerson()
@@ -854,6 +867,7 @@ class TestCaseWithFactory(TestCase):
 
     def getNonRedirectingBrowser(self, url=None, user=None):
         from lp.testing.pages import setupBrowser
+
         if user == ANONYMOUS:
             browser = setupBrowser()
         else:
@@ -874,9 +888,14 @@ class TestCaseWithFactory(TestCase):
             format = format_registry.get(format)()
         return ControlDir.create_branch_convenience(branch_url, format=format)
 
-    def create_branch_and_tree(self, tree_location=None, product=None,
-                               db_branch=None, format=None,
-                               **kwargs):
+    def create_branch_and_tree(
+        self,
+        tree_location=None,
+        product=None,
+        db_branch=None,
+        format=None,
+        **kwargs
+    ):
         """Create a database branch, bzr branch and bzr checkout.
 
         :param tree_location: The path on disk to create the tree at.
@@ -891,7 +910,8 @@ class TestCaseWithFactory(TestCase):
             else:
                 db_branch = self.factory.makeProductBranch(product, **kwargs)
         branch_url = (
-            'lp-internal:///' + removeSecurityProxy(db_branch).unique_name)
+            "lp-internal:///" + removeSecurityProxy(db_branch).unique_name
+        )
         if not self.direct_database_server:
             transaction.commit()
         bzr_branch = self.createBranchAtURL(branch_url, format=format)
@@ -899,7 +919,8 @@ class TestCaseWithFactory(TestCase):
             tree_location = tempfile.mkdtemp()
             self.addCleanup(lambda: shutil.rmtree(tree_location))
         return db_branch, bzr_branch.create_checkout(
-            tree_location, lightweight=True)
+            tree_location, lightweight=True
+        )
 
     def createBzrBranch(self, db_branch, parent=None):
         """Create a bzr branch for a database branch.
@@ -912,16 +933,22 @@ class TestCaseWithFactory(TestCase):
             bzr_branch.pull(parent)
             naked_branch = removeSecurityProxy(db_branch)
             naked_branch.last_scanned_id = six.ensure_text(
-                bzr_branch.last_revision())
+                bzr_branch.last_revision()
+            )
         return bzr_branch
 
     def useTempBzrHome(self):
         self.useTempDir()
         # Avoid leaking local user configuration into tests.
-        self.useContext(override_environ(
-            BRZ_HOME=os.getcwd(), BRZ_EMAIL=None,
-            BZR_HOME=os.getcwd(), BZR_EMAIL=None, EMAIL=None,
-            ))
+        self.useContext(
+            override_environ(
+                BRZ_HOME=os.getcwd(),
+                BRZ_EMAIL=None,
+                BZR_HOME=os.getcwd(),
+                BZR_EMAIL=None,
+                EMAIL=None,
+            )
+        )
 
     def useBzrBranches(self, direct_database=False):
         """Prepare for using bzr branches.
@@ -939,7 +966,8 @@ class TestCaseWithFactory(TestCase):
             if direct_database != self.direct_database_server:
                 raise AssertionError(
                     "useBzrBranches called with inconsistent values for "
-                    "direct_database")
+                    "direct_database"
+                )
             return
         self._use_bzr_branch_called = True
         self.useTempBzrHome()
@@ -961,8 +989,9 @@ class BrowserTestCase(TestCaseWithFactory):
         super().setUp()
         self.user = self.factory.makePerson()
 
-    def getViewBrowser(self, context, view_name=None, no_login=False,
-                       rootsite=None, user=None):
+    def getViewBrowser(
+        self, context, view_name=None, no_login=False, rootsite=None, user=None
+    ):
         # Make sure that there is a user interaction in order to generate the
         # canonical url for the context object.
         if no_login:
@@ -975,27 +1004,33 @@ class BrowserTestCase(TestCaseWithFactory):
         logout()
         if no_login:
             from lp.testing.pages import setupBrowser
+
             browser = setupBrowser()
             browser.open(url)
             return browser
         else:
             return self.getUserBrowser(url, user)
 
-    def getMainContent(self, context, view_name=None, rootsite=None,
-                       no_login=False, user=None):
+    def getMainContent(
+        self, context, view_name=None, rootsite=None, no_login=False, user=None
+    ):
         """Beautiful soup of the main content area of context's page."""
         from lp.testing.pages import find_main_content
+
         browser = self.getViewBrowser(
-            context, view_name, rootsite=rootsite, no_login=no_login,
-            user=user)
+            context, view_name, rootsite=rootsite, no_login=no_login, user=user
+        )
         return find_main_content(browser.contents)
 
-    def getMainText(self, context, view_name=None, rootsite=None,
-                    no_login=False, user=None):
+    def getMainText(
+        self, context, view_name=None, rootsite=None, no_login=False, user=None
+    ):
         """Return the main text of a context's page."""
         from lp.testing.pages import extract_text
+
         return extract_text(
-            self.getMainContent(context, view_name, rootsite, no_login, user))
+            self.getMainContent(context, view_name, rootsite, no_login, user)
+        )
 
 
 class WebServiceTestCase(TestCaseWithFactory):
@@ -1007,13 +1042,15 @@ class WebServiceTestCase(TestCaseWithFactory):
         # TestTwistedJobRunner.test_timeout fails if this is at the
         # module level. There is probably some hidden circular import.
         from lp.testing.layers import AppServerLayer
+
         return AppServerLayer
 
     def setUp(self):
         super().setUp()
-        self.ws_version = 'devel'
+        self.ws_version = "devel"
         self.service = self.factory.makeLaunchpadService(
-            version=self.ws_version)
+            version=self.ws_version
+        )
 
     def wsObject(self, obj, user=None):
         """Return the launchpadlib version of the supplied object.
@@ -1024,7 +1061,8 @@ class WebServiceTestCase(TestCaseWithFactory):
         """
         if user is not None:
             service = self.factory.makeLaunchpadService(
-                user, version=self.ws_version)
+                user, version=self.ws_version
+            )
         else:
             service = self.service
         return ws_object(service, obj)
@@ -1033,7 +1071,7 @@ class WebServiceTestCase(TestCaseWithFactory):
 class AbstractYUITestCase(TestCase):
 
     layer = None
-    suite_name = ''
+    suite_name = ""
     # 30 seconds for the suite.
     suite_timeout = 30000
     # By default we do not restrict per-test or times.  yuixhr tests do.
@@ -1068,11 +1106,14 @@ class AbstractYUITestCase(TestCase):
         # html5browser imports from the gir/pygtk stack which causes
         # twisted tests to break because of gtk's initialize.
         from lp.testing import html5browser
+
         client = html5browser.Browser()
-        page = client.load_page(self.html_uri,
-                                timeout=self.suite_timeout,
-                                initial_timeout=self.initial_timeout,
-                                incremental_timeout=self.incremental_timeout)
+        page = client.load_page(
+            self.html_uri,
+            timeout=self.suite_timeout,
+            initial_timeout=self.initial_timeout,
+            incremental_timeout=self.incremental_timeout,
+        )
         report = None
         if page.content:
             report = simplejson.loads(page.content)
@@ -1084,21 +1125,22 @@ class AbstractYUITestCase(TestCase):
         # Data['results'] is a dict (type=report)
         # with 1 or more dicts (type=testcase)
         # with 1 for more dicts (type=test).
-        if report.get('type', None) != 'complete':
+        if report.get("type", None) != "complete":
             # Did not get a report back.
             self._yui_results = self.MISSING_REPORT
             return
         self._yui_results = {}
-        for key, value in report['results'].items():
-            if isinstance(value, dict) and value['type'] == 'testcase':
+        for key, value in report["results"].items():
+            if isinstance(value, dict) and value["type"] == "testcase":
                 testcase_name = key
                 test_case = value
                 for key, value in test_case.items():
-                    if isinstance(value, dict) and value['type'] == 'test':
-                        test_name = '%s.%s' % (testcase_name, key)
+                    if isinstance(value, dict) and value["type"] == "test":
+                        test_name = "%s.%s" % (testcase_name, key)
                         test = value
                         self._yui_results[test_name] = dict(
-                            result=test['result'], message=test['message'])
+                            result=test["result"], message=test["message"]
+                        )
 
     def checkResults(self):
         """Check the results.
@@ -1107,21 +1149,27 @@ class AbstractYUITestCase(TestCase):
         from here.
         """
         if self._yui_results == self.TIMEOUT:
-            msg = 'JS timed out.'
+            msg = "JS timed out."
             if self._last_test_info is not None:
                 try:
-                    msg += ('  The last test that ran to '
-                            'completion before timing out was '
-                            '%(testCase)s:%(testName)s.  The test %(type)sed.'
-                            % self._last_test_info)
+                    msg += (
+                        "  The last test that ran to "
+                        "completion before timing out was "
+                        "%(testCase)s:%(testName)s.  The test %(type)sed."
+                        % self._last_test_info
+                    )
                 except (KeyError, TypeError):
-                    msg += ('  The test runner received an unexpected error '
-                            'when trying to show information about the last '
-                            'test to run.  The data it received was %r.'
-                            % (self._last_test_info,))
-            elif (self.incremental_timeout is not None or
-                  self.initial_timeout is not None):
-                msg += '  The test may never have started.'
+                    msg += (
+                        "  The test runner received an unexpected error "
+                        "when trying to show information about the last "
+                        "test to run.  The data it received was %r."
+                        % (self._last_test_info,)
+                    )
+            elif (
+                self.incremental_timeout is not None
+                or self.initial_timeout is not None
+            ):
+                msg += "  The test may never have started."
             self.fail(msg)
         elif self._yui_results == self.MISSING_REPORT:
             self.fail("The data returned by js is not a test report.")
@@ -1130,27 +1178,29 @@ class AbstractYUITestCase(TestCase):
         failures = []
         for test_name in self._yui_results:
             result = self._yui_results[test_name]
-            if result['result'] not in ('pass', 'ignore'):
+            if result["result"] not in ("pass", "ignore"):
                 failures.append(
-                    'Failure in %s.%s: %s' % (
-                    self.test_path, test_name, result['message']))
-        self.assertEqual([], failures, '\n'.join(failures))
+                    "Failure in %s.%s: %s"
+                    % (self.test_path, test_name, result["message"])
+                )
+        self.assertEqual([], failures, "\n".join(failures))
 
 
 class YUIUnitTestCase(AbstractYUITestCase):
 
-    _testMethodName = 'checkResults'
+    _testMethodName = "checkResults"
 
     def initialize(self, test_path):
         # The path is a .html file.
         self.test_path = test_path
-        self.html_uri = 'file://%s' % os.path.join(
-            config.root, 'lib', self.test_path)
+        self.html_uri = "file://%s" % os.path.join(
+            config.root, "lib", self.test_path
+        )
 
 
 def build_yui_unittest_suite(app_testing_path, yui_test_class):
     suite = unittest.TestSuite()
-    testing_path = os.path.join(config.root, 'lib', app_testing_path)
+    testing_path = os.path.join(config.root, "lib", app_testing_path)
     unit_test_names = _harvest_yui_test_files(testing_path)
     for unit_test_path in unit_test_names:
         test_case = yui_test_class()
@@ -1200,7 +1250,9 @@ class RunIsolatedTest(testtools.RunTest):
         try:
             return self._run_started(
                 _StartedTestResult(
-                    testtools.ExtendedToOriginalDecorator(result)))
+                    testtools.ExtendedToOriginalDecorator(result)
+                )
+            )
         finally:
             result.stopTest(self.case)
 
@@ -1224,7 +1276,7 @@ class RunIsolatedTest(testtools.RunTest):
         if pid == 0:
             # Child.
             os.close(pread)
-            fdwrite = os.fdopen(pwrite, 'wb', 1)
+            fdwrite = os.fdopen(pwrite, "wb", 1)
             # Send results to the subunit stream client so that the parent
             # process can obtain the result.
             super().run(subunit.TestProtocolClient(fdwrite))
@@ -1239,15 +1291,16 @@ class RunIsolatedTest(testtools.RunTest):
         else:
             # Parent.
             os.close(pwrite)
-            fdread = os.fdopen(pread, 'rb')
+            fdread = os.fdopen(pread, "rb")
             # Accept the result from the child process, but don't write a
             # duplicate copy to stdout.
             protocol = subunit.TestProtocolServer(
-                result, stream=subunit.DiscardStream())
+                result, stream=subunit.DiscardStream()
+            )
             protocol.readFrom(fdread)
             fdread.close()
             os.waitpid(pid, 0)
-            layer = getattr(self.case, 'layer', None)
+            layer = getattr(self.case, "layer", None)
             if layer is not None and issubclass(layer, DatabaseLayer):
                 layer.force_dirty_database()
 
@@ -1275,8 +1328,9 @@ class EventRecorder:
         return self
 
     def __exit__(self, exc_type, exc_value, traceback):
-        assert zope.event.subscribers == self.new_subscribers, (
-            'Subscriber list has been changed while running!')
+        assert (
+            zope.event.subscribers == self.new_subscribers
+        ), "Subscriber list has been changed while running!"
         zope.event.subscribers[:] = self.old_subscribers
 
 
@@ -1285,8 +1339,9 @@ def feature_flags():
     """Provide a context in which feature flags work."""
     empty_request = LaunchpadTestRequest()
     old_features = features.get_relevant_feature_controller()
-    features.install_feature_controller(FeatureController(
-        ScopesFromRequest(empty_request).lookup))
+    features.install_feature_controller(
+        FeatureController(ScopesFromRequest(empty_request).lookup)
+    )
     try:
         yield
     finally:
@@ -1330,11 +1385,17 @@ def run_script(cmd_line, env=None, cwd=None, universal_newlines=True):
     """
     if env is None:
         env = os.environ.copy()
-    env.pop('PYTHONPATH', None)
+    env.pop("PYTHONPATH", None)
     process = subprocess.Popen(
-        cmd_line, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
-        stderr=subprocess.PIPE, env=env, cwd=cwd,
-        universal_newlines=universal_newlines)
+        cmd_line,
+        shell=True,
+        stdin=subprocess.PIPE,
+        stdout=subprocess.PIPE,
+        stderr=subprocess.PIPE,
+        env=env,
+        cwd=cwd,
+        universal_newlines=universal_newlines,
+    )
     (out, err) = process.communicate()
     return out, err, process.returncode
 
@@ -1356,12 +1417,16 @@ def run_process(cmd, env=None, universal_newlines=True):
     """
     if env is None:
         env = os.environ.copy()
-    env.pop('PYTHONPATH', None)
+    env.pop("PYTHONPATH", None)
     with open(os.devnull, "rb") as devnull:
         process = subprocess.Popen(
-            cmd, stdin=devnull, stdout=subprocess.PIPE,
-            stderr=subprocess.PIPE, env=env,
-            universal_newlines=universal_newlines)
+            cmd,
+            stdin=devnull,
+            stdout=subprocess.PIPE,
+            stderr=subprocess.PIPE,
+            env=env,
+            universal_newlines=universal_newlines,
+        )
         stdout, stderr = process.communicate()
         return stdout, stderr, process.returncode
 
@@ -1392,7 +1457,7 @@ def map_branch_contents(branch):
         for _, entries in tree.walkdirs():
             for entry in entries:
                 file_path, file_name, file_type = entry[:3]
-                if file_type == 'file':
+                if file_type == "file":
                     stored_file = tree.get_file(file_path)
                     contents[file_path] = stored_file.read()
     finally:
@@ -1401,7 +1466,7 @@ def map_branch_contents(branch):
     return contents
 
 
-def set_feature_flag(name, value, scope='default', priority=1):
+def set_feature_flag(name, value, scope="default", priority=1):
     """Set a feature flag to the specified value.
 
     In order to access the flag, use the feature_flags context manager or
@@ -1411,8 +1476,7 @@ def set_feature_flag(name, value, scope='default', priority=1):
     :param scope: The scope in which the specified value applies.
     """
     assert features.get_relevant_feature_controller() is not None
-    flag = FeatureFlag(
-        scope=scope, flag=name, value=value, priority=priority)
+    flag = FeatureFlag(scope=scope, flag=name, value=value, priority=priority)
     store = getFeatureStore()
     store.add(flag)
     # Make sure that the feature is saved into the db right now.
@@ -1463,7 +1527,8 @@ def unlink_source_packages(product):
         packaging_util.deletePackaging(
             source_package.productseries,
             source_package.sourcepackagename,
-            source_package.distroseries)
+            source_package.distroseries,
+        )
 
 
 class ExpectedException(TTExpectedException):
@@ -1479,9 +1544,9 @@ class ExpectedException(TTExpectedException):
 
 
 def extract_lp_cache(text):
-    match = re.search(r'<script[^>]*>LP.cache = (\{.*\});</script>', text)
+    match = re.search(r"<script[^>]*>LP.cache = (\{.*\});</script>", text)
     if match is None:
-        raise ValueError('No JSON cache found.')
+        raise ValueError("No JSON cache found.")
     return simplejson.loads(match.group(1))
 
 
@@ -1494,7 +1559,7 @@ def nonblocking_readline(instream, timeout):
     result = io.BytesIO()
     start = now = time.time()
     deadline = start + timeout
-    while (now < deadline and not result.getvalue().endswith(b'\n')):
+    while now < deadline and not result.getvalue().endswith(b"\n"):
         rlist = select([instream], [], [], deadline - now)
         if rlist:
             # Reading 1 character at a time is inefficient, but means
@@ -1508,7 +1573,6 @@ def nonblocking_readline(instream, timeout):
 
 
 class FakeLaunchpadRequest(FakeRequest):
-
     @property
     def stepstogo(self):
         """See `IBasicLaunchpadRequest`."""
@@ -1522,29 +1586,40 @@ class FakeAdapterMixin:
     during the setup of a test and they will be unregistered when the
     test completes.
     """
-    def registerAdapter(self, adapter_class, for_interfaces,
-                        provided_interface, name=None):
+
+    def registerAdapter(
+        self, adapter_class, for_interfaces, provided_interface, name=None
+    ):
         """Register an adapter from the required interfacs to the provided.
 
         eg. registerAdapter(
                 TestOtherThing, (IThing, ILayer), IOther, name='fnord')
         """
         getSiteManager().registerAdapter(
-            adapter_class, for_interfaces, provided_interface, name=name)
+            adapter_class, for_interfaces, provided_interface, name=name
+        )
         self.addCleanup(
-            getSiteManager().unregisterAdapter, adapter_class,
-            for_interfaces, provided_interface, name=name)
-
-    def registerAuthorizationAdapter(self, authorization_class,
-                                     for_interface, permission_name):
+            getSiteManager().unregisterAdapter,
+            adapter_class,
+            for_interfaces,
+            provided_interface,
+            name=name,
+        )
+
+    def registerAuthorizationAdapter(
+        self, authorization_class, for_interface, permission_name
+    ):
         """Register a security checker to test authorisation.
 
         eg. registerAuthorizationAdapter(
                 TestChecker, IPerson, 'launchpad.View')
         """
         self.registerAdapter(
-            authorization_class, (for_interface, ), IAuthorization,
-            name=permission_name)
+            authorization_class,
+            (for_interface,),
+            IAuthorization,
+            name=permission_name,
+        )
 
     def registerBrowserViewAdapter(self, view_class, for_interface, name):
         """Register a security checker to test authorization.
@@ -1552,13 +1627,13 @@ class FakeAdapterMixin:
         eg registerBrowserViewAdapter(TestView, IPerson, '+test-view')
         """
         self.registerAdapter(
-            view_class, (for_interface, IBrowserRequest), Interface,
-            name=name)
+            view_class, (for_interface, IBrowserRequest), Interface, name=name
+        )
 
     def getAdapter(self, for_interfaces, provided_interface, name=None):
         return getMultiAdapter(for_interfaces, provided_interface, name=name)
 
-    def registerUtility(self, component, for_interface, name=''):
+    def registerUtility(self, component, for_interface, name=""):
         try:
             current_commponent = getUtility(for_interface, name=name)
         except ComponentLookupError:
@@ -1566,12 +1641,16 @@ class FakeAdapterMixin:
         site_manager = getSiteManager()
         site_manager.registerUtility(component, for_interface, name)
         self.addCleanup(
-            site_manager.unregisterUtility, component, for_interface, name)
+            site_manager.unregisterUtility, component, for_interface, name
+        )
         if current_commponent is not None:
             # Restore the default utility.
             self.addCleanup(
-                site_manager.registerUtility, current_commponent,
-                for_interface, name)
+                site_manager.registerUtility,
+                current_commponent,
+                for_interface,
+                name,
+            )
 
 
 def clean_up_reactor():
@@ -1579,6 +1658,7 @@ def clean_up_reactor():
     # calls around.  They need to be updated to use Twisted correctly.
     # For the meantime, just blat the reactor.
     from twisted.internet import reactor
+
     for delayed_call in reactor.getDelayedCalls():
         delayed_call.cancel()
 
diff --git a/lib/lp/testing/_login.py b/lib/lp/testing/_login.py
index a90b3ec..e206eb8 100644
--- a/lib/lp/testing/_login.py
+++ b/lib/lp/testing/_login.py
@@ -2,31 +2,28 @@
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 __all__ = [
-    'admin_logged_in',
-    'anonymous_logged_in',
-    'celebrity_logged_in',
-    'login',
-    'login_admin',
-    'login_as',
-    'login_celebrity',
-    'login_person',
-    'login_team',
-    'logout',
-    'person_logged_in',
-    'run_with_login',
-    'with_anonymous_login',
-    'with_celebrity_logged_in',
-    'with_person_logged_in',
-    ]
+    "admin_logged_in",
+    "anonymous_logged_in",
+    "celebrity_logged_in",
+    "login",
+    "login_admin",
+    "login_as",
+    "login_celebrity",
+    "login_person",
+    "login_team",
+    "logout",
+    "person_logged_in",
+    "run_with_login",
+    "with_anonymous_login",
+    "with_celebrity_logged_in",
+    "with_person_logged_in",
+]
 
 from contextlib import contextmanager
 
 from zope.component import getUtility
-from zope.security.management import (
-    endInteraction,
-    queryInteraction,
-    thread_local as zope_security_thread_local,
-    )
+from zope.security.management import endInteraction, queryInteraction
+from zope.security.management import thread_local as zope_security_thread_local
 
 from lp.app.interfaces.launchpad import ILaunchpadCelebrities
 from lp.services.utils import decorate_with
@@ -34,7 +31,7 @@ from lp.services.webapp.interaction import (
     ANONYMOUS,
     setupInteractionByEmail,
     setupInteractionForPerson,
-    )
+)
 from lp.services.webapp.interfaces import ILaunchBag
 from lp.services.webapp.servers import LaunchpadTestRequest
 from lp.services.webapp.vhosts import allvhosts
@@ -50,8 +47,11 @@ def _test_login_impl(participation):
         # canonical_url produce a real-looking host name rather than
         # 127.0.0.1.
         participation = LaunchpadTestRequest(
-            environ={'HTTP_HOST': allvhosts.configs['mainsite'].hostname,
-                     'SERVER_URL': allvhosts.configs['mainsite'].rooturl})
+            environ={
+                "HTTP_HOST": allvhosts.configs["mainsite"].hostname,
+                "SERVER_URL": allvhosts.configs["mainsite"].rooturl,
+            }
+        )
     return participation
 
 
@@ -79,7 +79,7 @@ def login_person(person, participation=None):
     if person is not None:
         # The login will fail even without this check, but this gives us a
         # nice error message, which can save time when debugging.
-        if getattr(person, 'is_team', None):
+        if getattr(person, "is_team", None):
             raise ValueError("Got team, expected person: %r" % (person,))
     participation = _test_login_impl(participation)
     setupInteractionForPerson(person, participation)
@@ -90,6 +90,7 @@ def login_team(team, participation=None):
     """Login as a member of 'team'."""
     # Prevent import loop.
     from lp.testing.factory import LaunchpadObjectFactory
+
     if not team.is_team:
         raise ValueError("Got person, expected team: %r" % (team,))
     login(ADMIN_EMAIL)
diff --git a/lib/lp/testing/_webservice.py b/lib/lp/testing/_webservice.py
index c705f68..1ca16cc 100644
--- a/lib/lp/testing/_webservice.py
+++ b/lib/lp/testing/_webservice.py
@@ -2,35 +2,32 @@
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 __all__ = [
-    'launchpadlib_credentials_for',
-    'launchpadlib_for',
-    'oauth_access_token_for',
-    ]
+    "launchpadlib_credentials_for",
+    "launchpadlib_for",
+    "oauth_access_token_for",
+]
 
 
 import shutil
 import tempfile
 
+import six
+import transaction
+import zope.testing.cleanup
 from launchpadlib.credentials import (
     AccessToken,
     AnonymousAccessToken,
     Credentials,
-    )
+)
 from launchpadlib.launchpad import Launchpad
-import six
-import transaction
 from zope.component import getUtility
-import zope.testing.cleanup
 
 from lp.registry.interfaces.person import IPersonSet
 from lp.services.oauth.interfaces import IOAuthConsumerSet
 from lp.services.webapp.interaction import ANONYMOUS
 from lp.services.webapp.interfaces import OAuthPermission
 from lp.services.webapp.publisher import canonical_url
-from lp.testing._login import (
-    login,
-    logout,
-    )
+from lp.testing._login import login, logout
 
 
 def api_url(obj):
@@ -65,6 +62,7 @@ def oauth_access_token_for(consumer_name, person, permission, context=None):
         # Turn an OAuth context string into the corresponding object.
         # Avoid an import loop by importing from launchpad.browser here.
         from lp.services.oauth.browser import lookup_oauth_context
+
         context = lookup_oauth_context(context)
     if isinstance(permission, str):
         # Look up a permission by its token string.
@@ -83,8 +81,11 @@ def oauth_access_token_for(consumer_name, person, permission, context=None):
 
 
 def launchpadlib_credentials_for(
-    consumer_name, person, permission=OAuthPermission.WRITE_PRIVATE,
-    context=None):
+    consumer_name,
+    person,
+    permission=OAuthPermission.WRITE_PRIVATE,
+    context=None,
+):
     """Create launchpadlib credentials for the given person.
 
     :param consumer_name: An OAuth consumer name.
@@ -102,11 +103,13 @@ def launchpadlib_credentials_for(
     # launchpadlib to use.
     login(ANONYMOUS)
     access_token, access_secret = oauth_access_token_for(
-        consumer_name, person, permission, context)
+        consumer_name, person, permission, context
+    )
     logout()
     launchpadlib_token = AccessToken(access_token.key, access_secret)
-    return Credentials(consumer_name=consumer_name,
-                       access_token=launchpadlib_token)
+    return Credentials(
+        consumer_name=consumer_name, access_token=launchpadlib_token
+    )
 
 
 def _clean_up_cache(cache):
@@ -115,8 +118,13 @@ def _clean_up_cache(cache):
 
 
 def launchpadlib_for(
-    consumer_name, person=None, permission=OAuthPermission.WRITE_PRIVATE,
-    context=None, version="devel", service_root="http://api.launchpad.test/";):
+    consumer_name,
+    person=None,
+    permission=OAuthPermission.WRITE_PRIVATE,
+    context=None,
+    version="devel",
+    service_root="http://api.launchpad.test/";,
+):
     """Create a Launchpad object for the given person.
 
     :param consumer_name: An OAuth consumer name.
@@ -138,9 +146,16 @@ def launchpadlib_for(
         credentials = Credentials(consumer_name, access_token=token)
     else:
         credentials = launchpadlib_credentials_for(
-            consumer_name, person, permission, context)
+            consumer_name, person, permission, context
+        )
     transaction.commit()
-    cache = tempfile.mkdtemp(prefix='launchpadlib-cache-')
+    cache = tempfile.mkdtemp(prefix="launchpadlib-cache-")
     zope.testing.cleanup.addCleanUp(_clean_up_cache, (cache,))
-    return Launchpad(credentials, None, None, service_root=service_root,
-                     version=version, cache=cache)
+    return Launchpad(
+        credentials,
+        None,
+        None,
+        service_root=service_root,
+        version=version,
+        cache=cache,
+    )
diff --git a/lib/lp/testing/branding.py b/lib/lp/testing/branding.py
index 05bd3a7..f7210b6 100644
--- a/lib/lp/testing/branding.py
+++ b/lib/lp/testing/branding.py
@@ -1,7 +1,7 @@
 # Copyright 2009-2010 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
-__all__ = ['set_branding']
+__all__ = ["set_branding"]
 
 
 import os.path
@@ -22,24 +22,28 @@ def set_branding(browser, icon=True, logo=True, mugshot=True):
     """
     # make sure we have relevant-sized files handy
     icon_file = os.path.join(
-      os.path.dirname(canonical.launchpad.__file__),
-      'images/team.png')
+        os.path.dirname(canonical.launchpad.__file__), "images/team.png"
+    )
     logo_file = os.path.join(
-      os.path.dirname(canonical.launchpad.__file__),
-      'images/team-logo.png')
+        os.path.dirname(canonical.launchpad.__file__), "images/team-logo.png"
+    )
     mugshot_file = os.path.join(
-      os.path.dirname(canonical.launchpad.__file__),
-      'images/team-mugshot.png')
+        os.path.dirname(canonical.launchpad.__file__),
+        "images/team-mugshot.png",
+    )
     # set each of the branding elements in turn, if requested
     if icon:
-        browser.getControl(name='field.icon.action').value = ['change']
-        browser.getControl(name='field.icon.image').add_file(
-          open(icon_file, 'rb'), 'image/png', 'icon.png')
+        browser.getControl(name="field.icon.action").value = ["change"]
+        browser.getControl(name="field.icon.image").add_file(
+            open(icon_file, "rb"), "image/png", "icon.png"
+        )
     if logo:
-        browser.getControl(name='field.logo.action').value = ['change']
-        browser.getControl(name='field.logo.image').add_file(
-          open(logo_file, 'rb'), 'image/png', 'logo.png')
+        browser.getControl(name="field.logo.action").value = ["change"]
+        browser.getControl(name="field.logo.image").add_file(
+            open(logo_file, "rb"), "image/png", "logo.png"
+        )
     if mugshot:
-        browser.getControl(name='field.mugshot.action').value = ['change']
-        browser.getControl(name='field.mugshot.image').add_file(
-          open(mugshot_file, 'rb'), 'image/png', 'mugshot.png')
+        browser.getControl(name="field.mugshot.action").value = ["change"]
+        browser.getControl(name="field.mugshot.image").add_file(
+            open(mugshot_file, "rb"), "image/png", "mugshot.png"
+        )
diff --git a/lib/lp/testing/breadcrumbs.py b/lib/lp/testing/breadcrumbs.py
index f47121e..c6c5ad5 100644
--- a/lib/lp/testing/breadcrumbs.py
+++ b/lib/lp/testing/breadcrumbs.py
@@ -21,17 +21,19 @@ class BaseBreadcrumbTestCase(TestCaseWithFactory):
         """
         crumbs = self.getBreadcrumbsForObject(obj, view_name, rootsite)
         self.assertEqual(
-            expected,
-            [(crumb.text, crumb.url) for crumb in crumbs])
+            expected, [(crumb.text, crumb.url) for crumb in crumbs]
+        )
 
-    def assertBreadcrumbTexts(self, expected, obj, view_name=None,
-                              rootsite=None):
+    def assertBreadcrumbTexts(
+        self, expected, obj, view_name=None, rootsite=None
+    ):
         """The text of the breadcrumbs for obj match the expected values."""
         crumbs = self.getBreadcrumbsForObject(obj, view_name, rootsite)
         self.assertEqual(expected, [crumb.text for crumb in crumbs])
 
-    def assertBreadcrumbUrls(self, expected, obj, view_name=None,
-                             rootsite=None):
+    def assertBreadcrumbUrls(
+        self, expected, obj, view_name=None, rootsite=None
+    ):
         """The urls of the breadcrumbs for obj match the expected values."""
         crumbs = self.getBreadcrumbsForObject(obj, view_name, rootsite)
         self.assertEqual(expected, [crumb.url for crumb in crumbs])
@@ -50,6 +52,6 @@ class BaseBreadcrumbTestCase(TestCaseWithFactory):
         obj, view, request = test_traverse(url)
         # Sometimes test_traverse returns the __call__, while the template
         # always has access to the instance.
-        view = getattr(removeSecurityProxy(view), '__self__', view)
-        hier = create_initialized_view(view, '+hierarchy', request=request)
+        view = getattr(removeSecurityProxy(view), "__self__", view)
+        hier = create_initialized_view(view, "+hierarchy", request=request)
         return hier.items
diff --git a/lib/lp/testing/browser.py b/lib/lp/testing/browser.py
index ed2f222..6a4a665 100644
--- a/lib/lp/testing/browser.py
+++ b/lib/lp/testing/browser.py
@@ -10,20 +10,18 @@ that's not good enough.
 """
 
 __all__ = [
-    'setUp',
-    ]
+    "setUp",
+]
 
 import ssl
 
-from lazr.uri import URI
 import six
+from lazr.uri import URI
 from urllib3 import PoolManager
 from wsgiproxy.proxies import TransparentProxy
 from wsgiproxy.urllib3_client import HttpClient
-from zope.testbrowser.wsgi import (
-    AuthorizationMiddleware,
-    Browser as _Browser,
-    )
+from zope.testbrowser.wsgi import AuthorizationMiddleware
+from zope.testbrowser.wsgi import Browser as _Browser
 
 from lp.testing.layers import TransactionMiddleware
 from lp.testing.pages import (
@@ -31,12 +29,11 @@ from lp.testing.pages import (
     find_main_content,
     find_tag_by_id,
     print_feedback_messages,
-    )
+)
 from lp.testing.systemdocs import PrettyPrinter
 
 
 class Browser(_Browser):
-
     def __init__(self, url=None, wsgi_app=None):
         if wsgi_app is None:
             # urllib3 is carefully-chosen: both the httplib and requests
@@ -47,19 +44,19 @@ class Browser(_Browser):
             # +logout redirecting to https://bazaar.launchpad.test/+logout.
             client = HttpClient(pool=PoolManager(10, cert_reqs=ssl.CERT_NONE))
             wsgi_app = AuthorizationMiddleware(
-                TransactionMiddleware(
-                    TransparentProxy(client=client)))
+                TransactionMiddleware(TransparentProxy(client=client))
+            )
         super().__init__(url=url, wsgi_app=wsgi_app)
 
     @property
     def vhost(self):
         uri = URI(self.url)
-        return '%s://%s' % (uri.scheme, uri.host)
+        return "%s://%s" % (uri.scheme, uri.host)
 
     @property
     def rooturl(self):
         uri = URI(self.url)
-        return '%s://%s:%s' % (uri.scheme, uri.host, uri.port)
+        return "%s://%s:%s" % (uri.scheme, uri.host, uri.port)
 
     @property
     def urlpath(self):
@@ -69,11 +66,11 @@ class Browser(_Browser):
 
 def setUp(test):
     """Set up appserver tests."""
-    test.globs['Browser'] = Browser
-    test.globs['browser'] = Browser()
-    test.globs['find_tag_by_id'] = find_tag_by_id
-    test.globs['find_main_content'] = find_main_content
-    test.globs['print_feedback_messages'] = print_feedback_messages
-    test.globs['extract_text'] = extract_text
-    test.globs['pretty'] = PrettyPrinter(width=1).pformat
-    test.globs['six'] = six
+    test.globs["Browser"] = Browser
+    test.globs["browser"] = Browser()
+    test.globs["find_tag_by_id"] = find_tag_by_id
+    test.globs["find_main_content"] = find_main_content
+    test.globs["print_feedback_messages"] = print_feedback_messages
+    test.globs["extract_text"] = extract_text
+    test.globs["pretty"] = PrettyPrinter(width=1).pformat
+    test.globs["six"] = six
diff --git a/lib/lp/testing/dbuser.py b/lib/lp/testing/dbuser.py
index deb9b14..3441593 100644
--- a/lib/lp/testing/dbuser.py
+++ b/lib/lp/testing/dbuser.py
@@ -4,19 +4,16 @@
 """Provides a context manager to run parts of a test as a different dbuser."""
 
 __all__ = [
-    'dbuser',
-    'lp_dbuser',
-    'switch_dbuser',
-    ]
+    "dbuser",
+    "lp_dbuser",
+    "switch_dbuser",
+]
 
 from contextlib import contextmanager
 
-from storm.database import (
-    STATE_CONNECTED,
-    STATE_DISCONNECTED,
-    )
-from storm.zope.interfaces import IZStorm
 import transaction
+from storm.database import STATE_CONNECTED, STATE_DISCONNECTED
+from storm.zope.interfaces import IZStorm
 from zope.component import getUtility
 
 from lp.services.config import dbconfig
@@ -47,7 +44,7 @@ def update_store_connections():
             # only called by the test suite to kill the existing
             # connections so the Store's reconnect with updated
             # connection settings.
-            store._event.emit('register-transaction')
+            store._event.emit("register-transaction")
 
             connection._raw_connection = None
             connection._state = STATE_DISCONNECTED
@@ -73,7 +70,7 @@ def dbuser(temporary_name):
     temporary_name is the name of the dbuser that should be in place for the
     code in the "with" block.
     """
-    old_name = getattr(dbconfig.overrides, 'dbuser', None)
+    old_name = getattr(dbconfig.overrides, "dbuser", None)
     switch_dbuser(temporary_name)
     yield
     switch_dbuser(old_name)
@@ -84,4 +81,4 @@ def lp_dbuser():
 
     Use with the LaunchpadZopelessLayer layer and subclasses.
     """
-    return dbuser('launchpad')
+    return dbuser("launchpad")
diff --git a/lib/lp/testing/deprecated.py b/lib/lp/testing/deprecated.py
index 6849332..16b7753 100644
--- a/lib/lp/testing/deprecated.py
+++ b/lib/lp/testing/deprecated.py
@@ -11,26 +11,34 @@ from zope.security.management import (
     newInteraction,
     queryInteraction,
     restoreInteraction,
-    )
+)
 
 from lp.services.webapp.interaction import get_current_principal
 from lp.services.webapp.servers import LaunchpadTestRequest
 
 
 class LaunchpadFormHarness:
-
-    def __init__(self, context, view_class, form_values=None,
-                 request_class=LaunchpadTestRequest, request_environ=None):
+    def __init__(
+        self,
+        context,
+        view_class,
+        form_values=None,
+        request_class=LaunchpadTestRequest,
+        request_environ=None,
+    ):
         self.context = context
         self.view_class = view_class
         self.request_class = request_class
         self.request_environ = request_environ
         self._render(form_values)
 
-    def _render(self, form_values=None, method='GET'):
+    def _render(self, form_values=None, method="GET"):
         self.request = self.request_class(
-            method=method, form=form_values, PATH_INFO='/',
-            environ=self.request_environ)
+            method=method,
+            form=form_values,
+            PATH_INFO="/",
+            environ=self.request_environ,
+        )
         if queryInteraction() is not None:
             self.request.setPrincipal(get_current_principal())
         # Setup a new interaction using self.request, create the view,
@@ -41,10 +49,10 @@ class LaunchpadFormHarness:
         self.view.initialize()
         restoreInteraction()
 
-    def submit(self, action_name, form_values, method='POST'):
-        action_name = '%s.actions.%s' % (self.view.prefix, action_name)
+    def submit(self, action_name, form_values, method="POST"):
+        action_name = "%s.actions.%s" % (self.view.prefix, action_name)
         form_values = dict(form_values)
-        form_values[action_name] = ''
+        form_values[action_name] = ""
         self._render(form_values, method)
 
     def hasErrors(self):
@@ -60,4 +68,4 @@ class LaunchpadFormHarness:
         return self.request.response.getStatus() in [302, 303]
 
     def redirectionTarget(self):
-        return self.request.response.getHeader('location')
+        return self.request.response.getHeader("location")
diff --git a/lib/lp/testing/factory.py b/lib/lp/testing/factory.py
index a589045..4cdfa06 100644
--- a/lib/lp/testing/factory.py
+++ b/lib/lp/testing/factory.py
@@ -7,67 +7,51 @@ This module should not contain tests (but it should be tested).
 """
 
 __all__ = [
-    'GPGSigningContext',
-    'is_security_proxied_or_harmless',
-    'LaunchpadObjectFactory',
-    'ObjectFactory',
-    'remove_security_proxy_and_shout_at_engineer',
-    ]
+    "GPGSigningContext",
+    "is_security_proxied_or_harmless",
+    "LaunchpadObjectFactory",
+    "ObjectFactory",
+    "remove_security_proxy_and_shout_at_engineer",
+]
 
 import base64
-from collections.abc import (
-    Mapping,
-    Sequence,
-    )
-from datetime import (
-    datetime,
-    timedelta,
-    )
+import hashlib
+import os
+import sys
+import uuid
+import warnings
+from collections.abc import Mapping, Sequence
+from datetime import datetime, timedelta
 from email.encoders import encode_base64
 from email.message import Message as EmailMessage
 from email.mime.multipart import MIMEMultipart
 from email.mime.text import MIMEText
-from email.utils import (
-    formatdate,
-    make_msgid,
-    )
+from email.utils import formatdate, make_msgid
 from functools import wraps
-import hashlib
 from io import BytesIO
 from itertools import count
-import os
-import sys
 from textwrap import dedent
-import uuid
-import warnings
 
+import pytz
+import six
 from breezy.plugins.builder.recipe import BaseRecipeBranch
 from breezy.revision import Revision as BzrRevision
 from cryptography.utils import int_to_bytes
 from lazr.jobrunner.jobrunner import SuspendJobException
-import pytz
 from pytz import UTC
-import six
-from twisted.conch.ssh.common import (
-    MP,
-    NS,
-    )
+from twisted.conch.ssh.common import MP, NS
 from twisted.conch.test import keydata
 from twisted.python.util import mergeFunctionMetadata
 from zope.component import getUtility
 from zope.interface.interfaces import ComponentLookupError
-from zope.security.proxy import (
-    Proxy,
-    ProxyFactory,
-    removeSecurityProxy,
-    )
+from zope.security.proxy import Proxy, ProxyFactory, removeSecurityProxy
 
 from lp.app.enums import (
-    InformationType,
     PROPRIETARY_INFORMATION_TYPES,
     PUBLIC_INFORMATION_TYPES,
+    InformationType,
     ServiceUsage,
-    )
+)
 from lp.app.interfaces.launchpad import ILaunchpadCelebrities
 from lp.archivepublisher.interfaces.publisherconfig import IPublisherConfigSet
 from lp.archiveuploader.dscfile import DSCFile
@@ -75,45 +59,36 @@ from lp.blueprints.enums import (
     NewSpecificationDefinitionStatus,
     SpecificationDefinitionStatus,
     SpecificationWorkItemStatus,
-    )
+)
 from lp.blueprints.interfaces.specification import ISpecificationSet
 from lp.blueprints.interfaces.sprint import ISprintSet
 from lp.bugs.interfaces.apportjob import IProcessApportBlobJobSource
-from lp.bugs.interfaces.bug import (
-    CreateBugParams,
-    IBugSet,
-    )
+from lp.bugs.interfaces.bug import CreateBugParams, IBugSet
 from lp.bugs.interfaces.bugtask import (
     BugTaskImportance,
     BugTaskStatus,
     IBugTaskSet,
-    )
-from lp.bugs.interfaces.bugtracker import (
-    BugTrackerType,
-    IBugTrackerSet,
-    )
+)
+from lp.bugs.interfaces.bugtracker import BugTrackerType, IBugTrackerSet
 from lp.bugs.interfaces.bugwatch import IBugWatchSet
-from lp.bugs.interfaces.cve import (
-    CveStatus,
-    ICveSet,
-    )
+from lp.bugs.interfaces.cve import CveStatus, ICveSet
 from lp.bugs.interfaces.vulnerability import (
     IVulnerabilityActivitySet,
     IVulnerabilitySet,
     VulnerabilityChange,
     VulnerabilityStatus,
-    )
+)
 from lp.bugs.model.bug import FileBugData
 from lp.buildmaster.enums import (
     BuildBaseImageType,
     BuilderResetProtocol,
     BuildStatus,
-    )
+)
 from lp.buildmaster.interfaces.builder import IBuilderSet
 from lp.buildmaster.interfaces.processor import (
     IProcessorSet,
     ProcessorNotFound,
-    )
+)
 from lp.charms.interfaces.charmbase import ICharmBaseSet
 from lp.charms.interfaces.charmrecipe import ICharmRecipeSet
 from lp.charms.interfaces.charmrecipebuild import ICharmRecipeBuildSet
@@ -131,7 +106,7 @@ from lp.code.enums import (
     RevisionControlSystems,
     RevisionStatusArtifactType,
     TargetRevisionControlSystems,
-    )
+)
 from lp.code.errors import UnknownBranchTypeError
 from lp.code.interfaces.branch import IBranch
 from lp.code.interfaces.branchnamespace import get_branch_namespace
@@ -141,42 +116,34 @@ from lp.code.interfaces.codeimportevent import ICodeImportEventSet
 from lp.code.interfaces.codeimportmachine import ICodeImportMachineSet
 from lp.code.interfaces.codeimportresult import ICodeImportResultSet
 from lp.code.interfaces.gitnamespace import get_git_namespace
-from lp.code.interfaces.gitref import (
-    IGitRef,
-    IGitRefRemoteSet,
-    )
+from lp.code.interfaces.gitref import IGitRef, IGitRefRemoteSet
 from lp.code.interfaces.gitrepository import IGitRepository
 from lp.code.interfaces.linkedbranch import ICanHasLinkedBranch
 from lp.code.interfaces.revision import IRevisionSet
 from lp.code.interfaces.revisionstatus import (
     IRevisionStatusArtifactSet,
     IRevisionStatusReportSet,
-    )
+)
 from lp.code.interfaces.sourcepackagerecipe import (
-    ISourcePackageRecipeSource,
     MINIMAL_RECIPE_TEXT_BZR,
     MINIMAL_RECIPE_TEXT_GIT,
-    )
+    ISourcePackageRecipeSource,
+)
 from lp.code.interfaces.sourcepackagerecipebuild import (
     ISourcePackageRecipeBuildSource,
-    )
-from lp.code.model.diff import (
-    Diff,
-    PreviewDiff,
-    )
+)
+from lp.code.model.diff import Diff, PreviewDiff
 from lp.code.tests.helpers import GitHostingFixture
 from lp.oci.interfaces.ocipushrule import IOCIPushRuleSet
 from lp.oci.interfaces.ocirecipe import IOCIRecipeSet
 from lp.oci.interfaces.ocirecipebuild import IOCIRecipeBuildSet
-from lp.oci.interfaces.ociregistrycredentials import (
-    IOCIRegistryCredentialsSet,
-    )
+from lp.oci.interfaces.ociregistrycredentials import IOCIRegistryCredentialsSet
 from lp.oci.model.ocirecipe import OCIRecipeArch
 from lp.oci.model.ocirecipebuild import OCIFile
 from lp.oci.model.ocirecipebuildjob import (
     OCIRecipeBuildJob,
     OCIRecipeBuildJobType,
-    )
+)
 from lp.registry.enums import (
     BranchSharingPolicy,
     BugSharingPolicy,
@@ -184,63 +151,49 @@ from lp.registry.enums import (
     DistroSeriesDifferenceType,
     SpecificationSharingPolicy,
     TeamMembershipPolicy,
-    )
+)
 from lp.registry.interfaces.accesspolicy import (
     IAccessArtifactGrantSource,
     IAccessArtifactSource,
     IAccessPolicyArtifactSource,
     IAccessPolicyGrantSource,
     IAccessPolicySource,
-    )
-from lp.registry.interfaces.distribution import (
-    IDistribution,
-    IDistributionSet,
-    )
+)
+from lp.registry.interfaces.distribution import IDistribution, IDistributionSet
 from lp.registry.interfaces.distributionmirror import (
     MirrorContent,
     MirrorSpeed,
-    )
+)
 from lp.registry.interfaces.distributionsourcepackage import (
     IDistributionSourcePackage,
-    )
+)
 from lp.registry.interfaces.distroseries import IDistroSeries
 from lp.registry.interfaces.distroseriesdifference import (
     IDistroSeriesDifferenceSource,
-    )
+)
 from lp.registry.interfaces.distroseriesdifferencecomment import (
     IDistroSeriesDifferenceCommentSource,
-    )
+)
 from lp.registry.interfaces.distroseriesparent import IDistroSeriesParentSet
 from lp.registry.interfaces.gpg import IGPGKeySet
 from lp.registry.interfaces.mailinglist import (
     IMailingListSet,
     MailingListStatus,
-    )
+)
 from lp.registry.interfaces.mailinglistsubscription import (
     MailingListAutoSubscribePolicy,
-    )
+)
 from lp.registry.interfaces.ociproject import IOCIProjectSet
 from lp.registry.interfaces.ociprojectname import IOCIProjectNameSet
-from lp.registry.interfaces.packaging import (
-    IPackagingUtil,
-    PackagingType,
-    )
+from lp.registry.interfaces.packaging import IPackagingUtil, PackagingType
 from lp.registry.interfaces.person import (
     IPerson,
     IPersonSet,
     PersonCreationRationale,
-    )
+)
 from lp.registry.interfaces.pocket import PackagePublishingPocket
-from lp.registry.interfaces.poll import (
-    IPollSet,
-    PollAlgorithm,
-    PollSecrecy,
-    )
-from lp.registry.interfaces.product import (
-    IProduct,
-    IProductSet,
-    License,
-    )
+from lp.registry.interfaces.poll import IPollSet, PollAlgorithm, PollSecrecy
+from lp.registry.interfaces.product import IProduct, IProductSet, License
 from lp.registry.interfaces.productseries import IProductSeries
 from lp.registry.interfaces.projectgroup import IProjectGroupSet
 from lp.registry.interfaces.series import SeriesStatus
@@ -249,7 +202,7 @@ from lp.registry.interfaces.sourcepackage import (
     SourcePackageFileType,
     SourcePackageType,
     SourcePackageUrgency,
-    )
+)
 from lp.registry.interfaces.sourcepackagename import ISourcePackageNameSet
 from lp.registry.interfaces.ssh import ISSHKeySet
 from lp.registry.model.commercialsubscription import CommercialSubscription
@@ -260,53 +213,38 @@ from lp.services.auth.interfaces import IAccessTokenSet
 from lp.services.auth.utils import create_access_token_secret
 from lp.services.compat import message_as_bytes
 from lp.services.config import config
-from lp.services.database.constants import (
-    DEFAULT,
-    UTC_NOW,
-    )
+from lp.services.database.constants import DEFAULT, UTC_NOW
 from lp.services.database.interfaces import (
     IMasterStore,
     IStore,
     IStoreSelector,
-    )
+)
 from lp.services.database.policy import PrimaryDatabasePolicy
 from lp.services.database.sqlbase import flush_database_updates
-from lp.services.gpg.interfaces import (
-    GPGKeyAlgorithm,
-    IGPGHandler,
-    )
+from lp.services.gpg.interfaces import GPGKeyAlgorithm, IGPGHandler
 from lp.services.identity.interfaces.account import (
     AccountCreationRationale,
     AccountStatus,
     IAccountSet,
-    )
+)
 from lp.services.identity.interfaces.emailaddress import (
     EmailAddressStatus,
     IEmailAddressSet,
-    )
+)
 from lp.services.identity.model.account import Account
 from lp.services.librarian.interfaces import ILibraryFileAliasSet
-from lp.services.librarian.model import (
-    LibraryFileAlias,
-    LibraryFileContent,
-    )
+from lp.services.librarian.model import LibraryFileAlias, LibraryFileContent
 from lp.services.mail.signedmessage import SignedMessage
-from lp.services.messages.model.message import (
-    Message,
-    MessageChunk,
-    )
+from lp.services.messages.model.message import Message, MessageChunk
 from lp.services.oauth.interfaces import IOAuthConsumerSet
 from lp.services.openid.model.openididentifier import OpenIdIdentifier
-from lp.services.propertycache import (
-    clear_property_cache,
-    get_property_cache,
-    )
+from lp.services.propertycache import clear_property_cache, get_property_cache
 from lp.services.signing.enums import SigningKeyType
 from lp.services.signing.interfaces.signingkey import IArchiveSigningKeySet
 from lp.services.signing.model.signingkey import SigningKey
 from lp.services.temporaryblobstorage.interfaces import (
     ITemporaryStorageManager,
-    )
+)
 from lp.services.temporaryblobstorage.model import TemporaryBlobStorage
 from lp.services.utils import AutoDecorateMetaClass
 from lp.services.webapp.interfaces import OAuthPermission
@@ -333,19 +271,13 @@ from lp.soyuz.enums import (
     PackagePublishingStatus,
     PackageUploadCustomFormat,
     PackageUploadStatus,
-    )
-from lp.soyuz.interfaces.archive import (
-    default_name_by_purpose,
-    IArchiveSet,
-    )
+)
+from lp.soyuz.interfaces.archive import IArchiveSet, default_name_by_purpose
 from lp.soyuz.interfaces.archivefile import IArchiveFileSet
 from lp.soyuz.interfaces.archivepermission import IArchivePermissionSet
 from lp.soyuz.interfaces.binarypackagebuild import IBinaryPackageBuildSet
 from lp.soyuz.interfaces.binarypackagename import IBinaryPackageNameSet
-from lp.soyuz.interfaces.component import (
-    IComponent,
-    IComponentSet,
-    )
+from lp.soyuz.interfaces.component import IComponent, IComponentSet
 from lp.soyuz.interfaces.livefs import ILiveFSSet
 from lp.soyuz.interfaces.livefsbuild import ILiveFSBuildSet
 from lp.soyuz.interfaces.packagecopyjob import IPlainPackageCopyJobSource
@@ -356,14 +288,14 @@ from lp.soyuz.interfaces.section import ISectionSet
 from lp.soyuz.model.component import ComponentSelection
 from lp.soyuz.model.distributionsourcepackagecache import (
     DistributionSourcePackageCache,
-    )
+)
 from lp.soyuz.model.distroarchseriesfilter import DistroArchSeriesFilter
 from lp.soyuz.model.files import BinaryPackageFile
 from lp.soyuz.model.livefsbuild import LiveFSFile
 from lp.soyuz.model.packagediff import PackageDiff
 from lp.testing import (
-    admin_logged_in,
     ANONYMOUS,
+    admin_logged_in,
     celebrity_logged_in,
     launchpadlib_for,
     login,
@@ -373,39 +305,33 @@ from lp.testing import (
     run_with_login,
     time_counter,
     with_celebrity_logged_in,
-    )
+)
 from lp.testing.dbuser import dbuser
-from lp.translations.enums import (
-    LanguagePackType,
-    RosettaImportStatus,
-    )
+from lp.translations.enums import LanguagePackType, RosettaImportStatus
 from lp.translations.interfaces.languagepack import ILanguagePackSet
 from lp.translations.interfaces.potemplate import IPOTemplateSet
 from lp.translations.interfaces.side import TranslationSide
 from lp.translations.interfaces.translationfileformat import (
     TranslationFileFormat,
-    )
+)
 from lp.translations.interfaces.translationgroup import ITranslationGroupSet
 from lp.translations.interfaces.translationimportqueue import (
     ITranslationImportQueue,
-    )
+)
 from lp.translations.interfaces.translationmessage import (
     RosettaTranslationOrigin,
-    )
+)
 from lp.translations.interfaces.translationsperson import ITranslationsPerson
 from lp.translations.interfaces.translationtemplatesbuild import (
     ITranslationTemplatesBuildSource,
-    )
+)
 from lp.translations.interfaces.translator import ITranslatorSet
 from lp.translations.model.translationtemplateitem import (
     TranslationTemplateItem,
-    )
-from lp.translations.utilities.sanitize import (
-    sanitize_translations_from_webui,
-    )
-
+)
+from lp.translations.utilities.sanitize import sanitize_translations_from_webui
 
-SPACE = ' '
+SPACE = " "
 
 
 def default_master_store(func):
@@ -430,6 +356,7 @@ def default_master_store(func):
             return func(*args, **kw)
         finally:
             store_selector.pop()
+
     return mergeFunctionMetadata(func, with_default_master_store)
 
 
@@ -442,7 +369,7 @@ _DEFAULT = object()
 class GPGSigningContext:
     """A helper object to hold the key, password and mode."""
 
-    def __init__(self, key, password='', mode=None):
+    def __init__(self, key, password="", mode=None):
         self.key = key
         self.password = password
         self.mode = mode
@@ -454,12 +381,12 @@ class ObjectFactory(metaclass=AutoDecorateMetaClass):
     # This allocates process-wide unique integers.  We count on Python doing
     # only cooperative threading to make this safe across threads.
 
-    __decorators = (default_master_store, )
+    __decorators = (default_master_store,)
 
     _unique_int_counter = count(100000)
 
     def getUniqueEmailAddress(self):
-        return "%s@xxxxxxxxxxx" % self.getUniqueUnicode('email')
+        return "%s@xxxxxxxxxxx" % self.getUniqueUnicode("email")
 
     def getUniqueInteger(self):
         """Return an integer unique to this factory instance.
@@ -476,7 +403,7 @@ class ObjectFactory(metaclass=AutoDecorateMetaClass):
             don't care.
         :return: A hexadecimal string, with 'a'-'f' in lower case.
         """
-        hex_number = '%x' % self.getUniqueInteger()
+        hex_number = "%x" % self.getUniqueInteger()
         if digits is not None:
             hex_number = hex_number.zfill(digits)
         return hex_number
@@ -502,17 +429,17 @@ class ObjectFactory(metaclass=AutoDecorateMetaClass):
             # names.
             source = (
                 os.path.basename(source_filename)
-                .replace('_', '-')
-                .replace('.', '-'))
-            if source.startswith(
-                    '<doctest '):
+                .replace("_", "-")
+                .replace(".", "-")
+            )
+            if source.startswith("<doctest "):
                 # Like '-<doctest xx-build-summary-rst[10]>'.
-                source = (source
-                    .replace('<doctest ', '')
-                    .replace('[', '')
-                    .replace(']>', ''))
-            prefix = 'unique-from-%s-line%d' % (
-                source, frame.f_lineno)
+                source = (
+                    source.replace("<doctest ", "")
+                    .replace("[", "")
+                    .replace("]>", "")
+                )
+            prefix = "unique-from-%s-line%d" % (source, frame.f_lineno)
         string = "%s-%s" % (prefix, self.getUniqueInteger())
         return string
 
@@ -525,10 +452,10 @@ class ObjectFactory(metaclass=AutoDecorateMetaClass):
     def getUniqueURL(self, scheme=None, host=None):
         """Return a URL unique to this run of the test case."""
         if scheme is None:
-            scheme = 'http'
+            scheme = "http"
         if host is None:
-            host = "%s.example.com" % self.getUniqueUnicode('domain')
-        return '%s://%s/%s' % (scheme, host, self.getUniqueUnicode('path'))
+            host = "%s.example.com" % self.getUniqueUnicode("domain")
+        return "%s://%s/%s" % (scheme, host, self.getUniqueUnicode("path"))
 
     def getUniqueDate(self):
         """Return a unique date since January 1 2009.
@@ -566,7 +493,7 @@ class LaunchpadObjectFactory(ObjectFactory):
     for any other required objects.
     """
 
-    __decorators = (check_security_proxy, )
+    __decorators = (check_security_proxy,)
 
     def loginAsAnyone(self, participation=None):
         """Log in as an arbitrary person.
@@ -579,43 +506,56 @@ class LaunchpadObjectFactory(ObjectFactory):
         login_as(person, participation)
         return person
 
-    @with_celebrity_logged_in('admin')
+    @with_celebrity_logged_in("admin")
     def makeAdministrator(self, name=None, email=None):
         return self.makePerson(
-            name=name, email=email,
-            member_of=[getUtility(ILaunchpadCelebrities).admin])
+            name=name,
+            email=email,
+            member_of=[getUtility(ILaunchpadCelebrities).admin],
+        )
 
-    @with_celebrity_logged_in('admin')
-    def makeRegistryExpert(self, name=None, email='expert@xxxxxxxxxxx'):
+    @with_celebrity_logged_in("admin")
+    def makeRegistryExpert(self, name=None, email="expert@xxxxxxxxxxx"):
         return self.makePerson(
-            name=name, email=email,
-            member_of=[getUtility(ILaunchpadCelebrities).registry_experts])
+            name=name,
+            email=email,
+            member_of=[getUtility(ILaunchpadCelebrities).registry_experts],
+        )
 
-    @with_celebrity_logged_in('admin')
+    @with_celebrity_logged_in("admin")
     def makeCommercialAdmin(self, name=None, email=None):
         return self.makePerson(
-            name=name, email=email,
-            member_of=[getUtility(ILaunchpadCelebrities).commercial_admin])
+            name=name,
+            email=email,
+            member_of=[getUtility(ILaunchpadCelebrities).commercial_admin],
+        )
 
-    def makeCopyArchiveLocation(self, distribution=None, owner=None,
-        name=None, enabled=True):
+    def makeCopyArchiveLocation(
+        self, distribution=None, owner=None, name=None, enabled=True
+    ):
         """Create and return a new arbitrary location for copy packages."""
-        copy_archive = self.makeArchive(distribution, owner, name,
-                                        ArchivePurpose.COPY, enabled)
+        copy_archive = self.makeArchive(
+            distribution, owner, name, ArchivePurpose.COPY, enabled
+        )
 
         distribution = copy_archive.distribution
         distroseries = distribution.currentseries
         pocket = PackagePublishingPocket.RELEASE
 
-        location = PackageLocation(copy_archive, distribution, distroseries,
-            pocket)
+        location = PackageLocation(
+            copy_archive, distribution, distroseries, pocket
+        )
         return ProxyFactory(location)
 
-    def makeAccount(self, displayname=None, status=AccountStatus.ACTIVE,
-                    rationale=AccountCreationRationale.UNKNOWN):
+    def makeAccount(
+        self,
+        displayname=None,
+        status=AccountStatus.ACTIVE,
+        rationale=AccountCreationRationale.UNKNOWN,
+    ):
         """Create and return a new Account."""
         if displayname is None:
-            displayname = self.getUniqueString('displayname')
+            displayname = self.getUniqueString("displayname")
         account = getUtility(IAccountSet).new(rationale, displayname)
         removeSecurityProxy(account).status = status
         self.makeOpenIdIdentifier(account)
@@ -635,12 +575,13 @@ class LaunchpadObjectFactory(ObjectFactory):
         # production environments so access to execute this stored
         # procedure cannot be used to compromise accounts.
         IMasterStore(OpenIdIdentifier).execute(
-            "SELECT add_test_openid_identifier(%s)", (account.id, ))
+            "SELECT add_test_openid_identifier(%s)", (account.id,)
+        )
 
     def makeGPGKey(self, owner):
         """Give 'owner' a crappy GPG key for the purposes of testing."""
         key_id = self.getUniqueHexString(digits=8).upper()
-        fingerprint = key_id + 'A' * 32
+        fingerprint = key_id + "A" * 32
         keyset = getUtility(IGPGKeySet)
         key = keyset.new(
             owner.id,
@@ -649,14 +590,26 @@ class LaunchpadObjectFactory(ObjectFactory):
             keysize=self.getUniqueInteger(),
             algorithm=GPGKeyAlgorithm.R,
             active=True,
-            can_encrypt=False)
+            can_encrypt=False,
+        )
         return key
 
     def makePerson(
-        self, email=None, name=None, displayname=None, account_status=None,
-        email_address_status=None, hide_email_addresses=False,
-        time_zone=None, latitude=None, longitude=None, description=None,
-        selfgenerated_bugnotifications=False, member_of=(), karma=None):
+        self,
+        email=None,
+        name=None,
+        displayname=None,
+        account_status=None,
+        email_address_status=None,
+        hide_email_addresses=False,
+        time_zone=None,
+        latitude=None,
+        longitude=None,
+        description=None,
+        selfgenerated_bugnotifications=False,
+        member_of=(),
+        karma=None,
+    ):
         """Create and return a new, arbitrary Person.
 
         :param email: The email address for the new person.
@@ -672,32 +625,41 @@ class LaunchpadObjectFactory(ObjectFactory):
         :param selfgenerated_bugnotifications: Receive own bugmail.
         """
         if name is None:
-            name = self.getUniqueString('person-name')
+            name = self.getUniqueString("person-name")
         if account_status == AccountStatus.PLACEHOLDER:
             # Placeholder people are pretty special, so just create and
             # bail out.
-            openid = self.getUniqueUnicode('%s-openid' % name)
+            openid = self.getUniqueUnicode("%s-openid" % name)
             person = getUtility(IPersonSet).createPlaceholderPerson(
-                openid, name)
+                openid, name
+            )
             return person
         # By default, make the email address preferred.
         if email is None:
             email = self.getUniqueEmailAddress()
-        if (email_address_status is None
-                or email_address_status == EmailAddressStatus.VALIDATED):
+        if (
+            email_address_status is None
+            or email_address_status == EmailAddressStatus.VALIDATED
+        ):
             email_address_status = EmailAddressStatus.PREFERRED
         if account_status == AccountStatus.NOACCOUNT:
             email_address_status = EmailAddressStatus.NEW
         person, email = getUtility(IPersonSet).createPersonAndEmail(
-            email, rationale=PersonCreationRationale.UNKNOWN, name=name,
+            email,
+            rationale=PersonCreationRationale.UNKNOWN,
+            name=name,
             displayname=displayname,
-            hide_email_addresses=hide_email_addresses)
+            hide_email_addresses=hide_email_addresses,
+        )
         naked_person = removeSecurityProxy(person)
         if description is not None:
             naked_person.description = description
 
-        if (time_zone is not None or latitude is not None or
-            longitude is not None):
+        if (
+            time_zone is not None
+            or latitude is not None
+            or longitude is not None
+        ):
             naked_person.setLocation(latitude, longitude, time_zone, person)
 
         # Make sure the non-security-proxied object is not returned.
@@ -711,20 +673,22 @@ class LaunchpadObjectFactory(ObjectFactory):
         # To make the person someone valid in Launchpad, validate the
         # email.
         if email_address_status == EmailAddressStatus.PREFERRED:
-            account = IMasterStore(Account).get(
-                Account, person.accountID)
+            account = IMasterStore(Account).get(Account, person.accountID)
             account.status = AccountStatus.ACTIVE
             person.setPreferredEmail(email)
 
         removeSecurityProxy(email).status = email_address_status
 
         once_active = (
-            AccountStatus.DEACTIVATED, AccountStatus.SUSPENDED,
-            AccountStatus.DECEASED)
+            AccountStatus.DEACTIVATED,
+            AccountStatus.SUSPENDED,
+            AccountStatus.DECEASED,
+        )
         if account_status:
             if account_status in once_active:
-                removeSecurityProxy(person.account).status = (
-                    AccountStatus.ACTIVE)
+                removeSecurityProxy(
+                    person.account
+                ).status = AccountStatus.ACTIVE
             removeSecurityProxy(person.account).status = account_status
         self.makeOpenIdIdentifier(person.account)
 
@@ -733,15 +697,19 @@ class LaunchpadObjectFactory(ObjectFactory):
                 team.addMember(person, team.teamowner)
 
         if karma is not None:
-            with dbuser('karma'):
+            with dbuser("karma"):
                 # Give the user karma to make the user non-probationary.
                 KarmaTotalCache(person=person, karma_total=karma)
         # Ensure updated ValidPersonCache
         flush_database_updates()
         return person
 
-    def makePersonByName(self, first_name, set_preferred_email=True,
-                         use_default_autosubscribe_policy=False):
+    def makePersonByName(
+        self,
+        first_name,
+        set_preferred_email=True,
+        use_default_autosubscribe_policy=False,
+    ):
         """Create a new person with the given first name.
 
         The person will be given two email addresses, with the 'long form'
@@ -767,15 +735,17 @@ class LaunchpadObjectFactory(ObjectFactory):
         :rtype: `IPerson`
         """
         variable_name = first_name.lower()
-        full_name = first_name + ' Person'
+        full_name = first_name + " Person"
         # E.g. firstname.person@xxxxxxxxxxx will be an alternative address.
-        preferred_address = variable_name + '.person@xxxxxxxxxxx'
+        preferred_address = variable_name + ".person@xxxxxxxxxxx"
         # E.g. aperson@xxxxxxxxxxx will be the preferred address.
-        alternative_address = variable_name[0] + 'person@xxxxxxxxxxx'
+        alternative_address = variable_name[0] + "person@xxxxxxxxxxx"
         person, email = getUtility(IPersonSet).createPersonAndEmail(
             preferred_address,
             PersonCreationRationale.OWNER_CREATED_LAUNCHPAD,
-            name=variable_name, displayname=full_name)
+            name=variable_name,
+            displayname=full_name,
+        )
         if set_preferred_email:
             # setPreferredEmail no longer activates the account
             # automatically.
@@ -788,10 +758,12 @@ class LaunchpadObjectFactory(ObjectFactory):
             # over subscriptions in the doctests.
             with person_logged_in(person):
                 person.mailing_list_auto_subscribe_policy = (
-                    MailingListAutoSubscribePolicy.NEVER)
+                    MailingListAutoSubscribePolicy.NEVER
+                )
         account = IMasterStore(Account).get(Account, person.accountID)
         getUtility(IEmailAddressSet).new(
-            alternative_address, person, EmailAddressStatus.VALIDATED)
+            alternative_address, person, EmailAddressStatus.VALIDATED
+        )
         return person
 
     def makeEmail(self, address, person, email_status=None):
@@ -810,13 +782,21 @@ class LaunchpadObjectFactory(ObjectFactory):
         """
         if email_status is None:
             email_status = EmailAddressStatus.VALIDATED
-        return getUtility(IEmailAddressSet).new(
-            address, person, email_status)
-
-    def makeTeam(self, owner=None, displayname=None, email=None, name=None,
-                 description=None, icon=None, logo=None,
-                 membership_policy=TeamMembershipPolicy.OPEN,
-                 visibility=None, members=None):
+        return getUtility(IEmailAddressSet).new(address, person, email_status)
+
+    def makeTeam(
+        self,
+        owner=None,
+        displayname=None,
+        email=None,
+        name=None,
+        description=None,
+        icon=None,
+        logo=None,
+        membership_policy=TeamMembershipPolicy.OPEN,
+        visibility=None,
+        members=None,
+    ):
         """Create and return a new, arbitrary Team.
 
         :param owner: The person or person name to use as the team's owner.
@@ -847,13 +827,18 @@ class LaunchpadObjectFactory(ObjectFactory):
         else:
             pass
         if name is None:
-            name = self.getUniqueString('team-name')
+            name = self.getUniqueString("team-name")
         if displayname is None:
             displayname = SPACE.join(
-                word.capitalize() for word in name.split('-'))
+                word.capitalize() for word in name.split("-")
+            )
         team = getUtility(IPersonSet).newTeam(
-            owner, name, displayname, description,
-            membership_policy=membership_policy)
+            owner,
+            name,
+            displayname,
+            description,
+            membership_policy=membership_policy,
+        )
         naked_team = removeSecurityProxy(team)
         if visibility is not None:
             # Visibility is normally restricted to launchpad.Commercial, so
@@ -862,7 +847,8 @@ class LaunchpadObjectFactory(ObjectFactory):
             naked_team._ensurePolicies()
         if email is not None:
             removeSecurityProxy(team).setContactAddress(
-                getUtility(IEmailAddressSet).new(email, team))
+                getUtility(IEmailAddressSet).new(email, team)
+            )
         if icon is not None:
             naked_team.icon = icon
         if logo is not None:
@@ -872,18 +858,28 @@ class LaunchpadObjectFactory(ObjectFactory):
                 naked_team.addMember(member, owner)
         return team
 
-    def makePoll(self, team, name, title, proposition,
-                 poll_type=PollAlgorithm.SIMPLE):
+    def makePoll(
+        self, team, name, title, proposition, poll_type=PollAlgorithm.SIMPLE
+    ):
         """Create a new poll which starts tomorrow and lasts for a week."""
         dateopens = datetime.now(pytz.UTC) + timedelta(days=1)
         datecloses = dateopens + timedelta(days=7)
         return getUtility(IPollSet).new(
-            team, name, title, proposition, dateopens, datecloses,
-            PollSecrecy.SECRET, allowspoilt=True,
-            poll_type=poll_type, check_permissions=False)
+            team,
+            name,
+            title,
+            proposition,
+            dateopens,
+            datecloses,
+            PollSecrecy.SECRET,
+            allowspoilt=True,
+            poll_type=poll_type,
+            check_permissions=False,
+        )
 
-    def makeTranslationGroup(self, owner=None, name=None, title=None,
-                             summary=None, url=None):
+    def makeTranslationGroup(
+        self, owner=None, name=None, title=None, summary=None, url=None
+    ):
         """Create a new, arbitrary `TranslationGroup`."""
         if owner is None:
             owner = self.makePerson()
@@ -894,20 +890,29 @@ class LaunchpadObjectFactory(ObjectFactory):
         if summary is None:
             summary = self.getUniqueString("summary")
         return getUtility(ITranslationGroupSet).new(
-            name, title, summary, url, owner)
+            name, title, summary, url, owner
+        )
 
-    def makeTranslator(self, language_code=None, group=None, person=None,
-                       license=True, language=None):
+    def makeTranslator(
+        self,
+        language_code=None,
+        group=None,
+        person=None,
+        license=True,
+        language=None,
+    ):
         """Create a new, arbitrary `Translator`."""
-        assert language_code is None or language is None, (
-            "Please specify only one of language_code and language.")
+        assert (
+            language_code is None or language is None
+        ), "Please specify only one of language_code and language."
         if language_code is None:
             if language is None:
                 language = self.makeLanguage()
             language_code = language.code
         else:
             language = getUtility(ILanguageSet).getLanguageByCode(
-                language_code)
+                language_code
+            )
             if language is None:
                 language = self.makeLanguage(language_code=language_code)
 
@@ -920,11 +925,22 @@ class LaunchpadObjectFactory(ObjectFactory):
         insecure_tx_person.translations_relicensing_agreement = license
         return getUtility(ITranslatorSet).new(group, language, person)
 
-    def makeMilestone(self, product=None, distribution=None,
-                      productseries=None, name=None, active=True,
-                      dateexpected=None, distroseries=None):
-        if (product is None and distribution is None and productseries is None
-            and distroseries is None):
+    def makeMilestone(
+        self,
+        product=None,
+        distribution=None,
+        productseries=None,
+        name=None,
+        active=True,
+        dateexpected=None,
+        distroseries=None,
+    ):
+        if (
+            product is None
+            and distribution is None
+            and productseries is None
+            and distroseries is None
+        ):
             product = self.makeProduct()
         if distribution is None and distroseries is None:
             if productseries is not None:
@@ -938,14 +954,27 @@ class LaunchpadObjectFactory(ObjectFactory):
         if name is None:
             name = self.getUniqueString()
         return ProxyFactory(
-            Milestone(product=product, distribution=distribution,
-                      productseries=productseries, distroseries=distroseries,
-                      name=name, active=active, dateexpected=dateexpected))
-
-    def makeProcessor(self, name=None, title=None, description=None,
-                      restricted=False, build_by_default=True,
-                      supports_virtualized=False,
-                      supports_nonvirtualized=True):
+            Milestone(
+                product=product,
+                distribution=distribution,
+                productseries=productseries,
+                distroseries=distroseries,
+                name=name,
+                active=active,
+                dateexpected=dateexpected,
+            )
+        )
+
+    def makeProcessor(
+        self,
+        name=None,
+        title=None,
+        description=None,
+        restricted=False,
+        build_by_default=True,
+        supports_virtualized=False,
+        supports_nonvirtualized=True,
+    ):
         """Create a new processor.
 
         :param name: Name of the processor
@@ -961,81 +990,118 @@ class LaunchpadObjectFactory(ObjectFactory):
         if description is None:
             description = "The %s processor and compatible processors" % name
         return getUtility(IProcessorSet).new(
-            name, title, description, restricted=restricted,
+            name,
+            title,
+            description,
+            restricted=restricted,
             build_by_default=build_by_default,
             supports_virtualized=supports_virtualized,
-            supports_nonvirtualized=supports_nonvirtualized)
+            supports_nonvirtualized=supports_nonvirtualized,
+        )
 
-    def makeProductRelease(self, milestone=None, product=None,
-                           productseries=None):
+    def makeProductRelease(
+        self, milestone=None, product=None, productseries=None
+    ):
         if milestone is None:
-            milestone = self.makeMilestone(product=product,
-                                           productseries=productseries)
+            milestone = self.makeMilestone(
+                product=product, productseries=productseries
+            )
         with person_logged_in(milestone.productseries.product.owner):
             release = milestone.createProductRelease(
-                milestone.product.owner, datetime.now(pytz.UTC))
+                milestone.product.owner, datetime.now(pytz.UTC)
+            )
         return release
 
-    def makeProductReleaseFile(self, signed=True,
-                               product=None, productseries=None,
-                               milestone=None,
-                               release=None,
-                               description="test file",
-                               filename='test.txt'):
+    def makeProductReleaseFile(
+        self,
+        signed=True,
+        product=None,
+        productseries=None,
+        milestone=None,
+        release=None,
+        description="test file",
+        filename="test.txt",
+    ):
         signature_filename = None
         signature_content = None
         if signed:
-            signature_filename = '%s.asc' % filename
-            signature_content = b'123'
+            signature_filename = "%s.asc" % filename
+            signature_content = b"123"
         if release is None:
-            release = self.makeProductRelease(product=product,
-                                              productseries=productseries,
-                                              milestone=milestone)
+            release = self.makeProductRelease(
+                product=product,
+                productseries=productseries,
+                milestone=milestone,
+            )
         with person_logged_in(release.milestone.product.owner):
             release_file = release.addReleaseFile(
-                filename, b'test', 'text/plain',
+                filename,
+                b"test",
+                "text/plain",
                 uploader=release.milestone.product.owner,
                 signature_filename=signature_filename,
                 signature_content=signature_content,
-                description=description)
+                description=description,
+            )
         IStore(release).flush()
         return release_file
 
     def makeProduct(
-        self, name=None, projectgroup=None, displayname=None,
-        licenses=None, owner=None, registrant=None,
-        title=None, summary=None, official_malone=None,
-        translations_usage=None, bug_supervisor=None, driver=None, icon=None,
-        bug_sharing_policy=None, branch_sharing_policy=None,
-        specification_sharing_policy=None, information_type=None,
-        answers_usage=None, vcs=None):
+        self,
+        name=None,
+        projectgroup=None,
+        displayname=None,
+        licenses=None,
+        owner=None,
+        registrant=None,
+        title=None,
+        summary=None,
+        official_malone=None,
+        translations_usage=None,
+        bug_supervisor=None,
+        driver=None,
+        icon=None,
+        bug_sharing_policy=None,
+        branch_sharing_policy=None,
+        specification_sharing_policy=None,
+        information_type=None,
+        answers_usage=None,
+        vcs=None,
+    ):
         """Create and return a new, arbitrary Product."""
         if owner is None:
             owner = self.makePerson()
         if name is None:
-            name = self.getUniqueString('product-name')
+            name = self.getUniqueString("product-name")
         if displayname is None:
             if name is None:
-                displayname = self.getUniqueString('displayname')
+                displayname = self.getUniqueString("displayname")
             else:
                 displayname = name.capitalize()
         if licenses is None:
-            if (information_type in PROPRIETARY_INFORMATION_TYPES or
-                (bug_sharing_policy is not None and
-                 bug_sharing_policy != BugSharingPolicy.PUBLIC) or
-                (branch_sharing_policy is not None and
-                 branch_sharing_policy != BranchSharingPolicy.PUBLIC) or
-                (specification_sharing_policy is not None and
-                 specification_sharing_policy !=
-                 SpecificationSharingPolicy.PUBLIC)
-                ):
+            if (
+                information_type in PROPRIETARY_INFORMATION_TYPES
+                or (
+                    bug_sharing_policy is not None
+                    and bug_sharing_policy != BugSharingPolicy.PUBLIC
+                )
+                or (
+                    branch_sharing_policy is not None
+                    and branch_sharing_policy != BranchSharingPolicy.PUBLIC
+                )
+                or (
+                    specification_sharing_policy is not None
+                    and specification_sharing_policy
+                    != SpecificationSharingPolicy.PUBLIC
+                )
+            ):
                 licenses = [License.OTHER_PROPRIETARY]
             else:
                 licenses = [License.GNU_GPL_V2]
         if title is None:
-            title = self.getUniqueString('title')
+            title = self.getUniqueString("title")
         if summary is None:
-            summary = self.getUniqueString('summary')
+            summary = self.getUniqueString("summary")
         admins = getUtility(ILaunchpadCelebrities).admin
         with person_logged_in(admins.teamowner):
             product = getUtility(IProductSet).createProduct(
@@ -1044,13 +1110,14 @@ class LaunchpadObjectFactory(ObjectFactory):
                 displayname,
                 title,
                 summary,
-                self.getUniqueString('description'),
+                self.getUniqueString("description"),
                 licenses=licenses,
                 projectgroup=projectgroup,
                 registrant=registrant,
                 icon=icon,
                 information_type=information_type,
-                vcs=vcs)
+                vcs=vcs,
+            )
         naked_product = removeSecurityProxy(product)
         if official_malone is not None:
             naked_product.official_malone = official_malone
@@ -1068,11 +1135,19 @@ class LaunchpadObjectFactory(ObjectFactory):
             naked_product.setBugSharingPolicy(bug_sharing_policy)
         if specification_sharing_policy:
             naked_product.setSpecificationSharingPolicy(
-                specification_sharing_policy)
+                specification_sharing_policy
+            )
         return product
 
-    def makeProductSeries(self, product=None, name=None, owner=None,
-                          summary=None, date_created=None, branch=None):
+    def makeProductSeries(
+        self,
+        product=None,
+        name=None,
+        owner=None,
+        summary=None,
+        date_created=None,
+        branch=None,
+    ):
         """Create a new, arbitrary ProductSeries.
 
         :param branch: If supplied, the branch to set as
@@ -1096,27 +1171,36 @@ class LaunchpadObjectFactory(ObjectFactory):
         # so we remove the security proxy before creating the series.
         naked_product = removeSecurityProxy(product)
         series = naked_product.newSeries(
-            owner=owner, name=name, summary=summary, branch=branch)
+            owner=owner, name=name, summary=summary, branch=branch
+        )
         if date_created is not None:
             series.datecreated = date_created
         return ProxyFactory(series)
 
-    def makeProject(self, name=None, displayname=None, title=None,
-                    homepageurl=None, summary=None, owner=None, driver=None,
-                    description=None):
+    def makeProject(
+        self,
+        name=None,
+        displayname=None,
+        title=None,
+        homepageurl=None,
+        summary=None,
+        owner=None,
+        driver=None,
+        description=None,
+    ):
         """Create and return a new, arbitrary ProjectGroup."""
         if owner is None:
             owner = self.makePerson()
         if name is None:
-            name = self.getUniqueString('project-name')
+            name = self.getUniqueString("project-name")
         if displayname is None:
-            displayname = self.getUniqueString('displayname')
+            displayname = self.getUniqueString("displayname")
         if summary is None:
-            summary = self.getUniqueString('summary')
+            summary = self.getUniqueString("summary")
         if description is None:
-            description = self.getUniqueString('description')
+            description = self.getUniqueString("description")
         if title is None:
-            title = self.getUniqueString('title')
+            title = self.getUniqueString("title")
         project = getUtility(IProjectGroupSet).new(
             name=name,
             display_name=displayname,
@@ -1124,7 +1208,8 @@ class LaunchpadObjectFactory(ObjectFactory):
             homepageurl=homepageurl,
             summary=summary,
             description=description,
-            owner=owner)
+            owner=owner,
+        )
         if driver is not None:
             removeSecurityProxy(project).driver = driver
         return project
@@ -1132,18 +1217,24 @@ class LaunchpadObjectFactory(ObjectFactory):
     def makeSprint(self, title=None, name=None, time_starts=None):
         """Make a sprint."""
         if title is None:
-            title = self.getUniqueUnicode('title')
+            title = self.getUniqueUnicode("title")
         owner = self.makePerson()
         if name is None:
-            name = self.getUniqueUnicode('name')
+            name = self.getUniqueUnicode("name")
         if time_starts is None:
             time_starts = datetime(2009, 1, 1, tzinfo=pytz.UTC)
         time_ends = time_starts + timedelta(days=1)
-        time_zone = 'UTC'
-        summary = self.getUniqueUnicode('summary')
+        time_zone = "UTC"
+        summary = self.getUniqueUnicode("summary")
         return getUtility(ISprintSet).new(
-            owner=owner, name=name, title=title, time_zone=time_zone,
-            time_starts=time_starts, time_ends=time_ends, summary=summary)
+            owner=owner,
+            name=name,
+            title=title,
+            time_zone=time_zone,
+            time_starts=time_starts,
+            time_ends=time_ends,
+            summary=summary,
+        )
 
     def makeStackedOnBranchChain(self, depth=5, **kwargs):
         branch = None
@@ -1151,11 +1242,21 @@ class LaunchpadObjectFactory(ObjectFactory):
             branch = self.makeAnyBranch(stacked_on=branch, **kwargs)
         return branch
 
-    def makeBranch(self, branch_type=None, owner=None,
-                   name=None, product=_DEFAULT, url=_DEFAULT, registrant=None,
-                   information_type=None, stacked_on=None,
-                   sourcepackage=None, reviewer=None, target=None,
-                   **optional_branch_args):
+    def makeBranch(
+        self,
+        branch_type=None,
+        owner=None,
+        name=None,
+        product=_DEFAULT,
+        url=_DEFAULT,
+        registrant=None,
+        information_type=None,
+        stacked_on=None,
+        sourcepackage=None,
+        reviewer=None,
+        target=None,
+        **optional_branch_args
+    ):
         """Create and return a new, arbitrary Branch of the given type.
 
         Any parameters for `IBranchNamespace.createBranch` can be specified to
@@ -1166,7 +1267,7 @@ class LaunchpadObjectFactory(ObjectFactory):
         if owner is None:
             owner = self.makePerson()
         if name is None:
-            name = self.getUniqueString('branch')
+            name = self.getUniqueString("branch")
         if target is not None:
             assert product is _DEFAULT
             assert sourcepackage is None
@@ -1183,8 +1284,9 @@ class LaunchpadObjectFactory(ObjectFactory):
             sourcepackagename = None
             distroseries = None
         else:
-            assert product is _DEFAULT, (
-                "Passed source package AND product details")
+            assert (
+                product is _DEFAULT
+            ), "Passed source package AND product details"
             product = None
             sourcepackagename = sourcepackage.sourcepackagename
             distroseries = sourcepackage.distroseries
@@ -1202,45 +1304,68 @@ class LaunchpadObjectFactory(ObjectFactory):
                 url = self.getUniqueURL()
         else:
             raise UnknownBranchTypeError(
-                'Unrecognized branch type: %r' % (branch_type, ))
+                "Unrecognized branch type: %r" % (branch_type,)
+            )
 
         namespace = get_branch_namespace(
-            owner, product=product, distroseries=distroseries,
-            sourcepackagename=sourcepackagename)
+            owner,
+            product=product,
+            distroseries=distroseries,
+            sourcepackagename=sourcepackagename,
+        )
         branch = namespace.createBranch(
-            branch_type=branch_type, name=name, registrant=registrant,
-            url=url, **optional_branch_args)
+            branch_type=branch_type,
+            name=name,
+            registrant=registrant,
+            url=url,
+            **optional_branch_args,
+        )
         naked_branch = removeSecurityProxy(branch)
         if information_type is not None:
             naked_branch.transitionToInformationType(
-                information_type, registrant, verify_policy=False)
+                information_type, registrant, verify_policy=False
+            )
         if stacked_on is not None:
             naked_branch.branchChanged(
-                removeSecurityProxy(stacked_on).unique_name, 'rev1', None,
-                None, None)
+                removeSecurityProxy(stacked_on).unique_name,
+                "rev1",
+                None,
+                None,
+                None,
+            )
         if reviewer is not None:
             naked_branch.reviewer = reviewer
         return branch
 
-    def makePackagingLink(self, productseries=None, sourcepackagename=None,
-                          distroseries=None, packaging_type=None, owner=None,
-                          sourcepackage=None, in_ubuntu=False):
+    def makePackagingLink(
+        self,
+        productseries=None,
+        sourcepackagename=None,
+        distroseries=None,
+        packaging_type=None,
+        owner=None,
+        sourcepackage=None,
+        in_ubuntu=False,
+    ):
         assert sourcepackage is None or (
-            distroseries is None and sourcepackagename is None), (
+            distroseries is None and sourcepackagename is None
+        ), (
             "Specify either a sourcepackage or a "
-            "distroseries/sourcepackagename pair")
+            "distroseries/sourcepackagename pair"
+        )
         if productseries is None:
             productseries = self.makeProduct().development_focus
         if sourcepackage is not None:
             distroseries = sourcepackage.distroseries
             sourcepackagename = sourcepackage.sourcepackagename
         else:
-            make_sourcepackagename = (
-                sourcepackagename is None or
-                isinstance(sourcepackagename, str))
+            make_sourcepackagename = sourcepackagename is None or isinstance(
+                sourcepackagename, str
+            )
             if make_sourcepackagename:
                 sourcepackagename = self.makeSourcePackageName(
-                    sourcepackagename)
+                    sourcepackagename
+                )
             if distroseries is None:
                 if in_ubuntu:
                     distroseries = self.makeUbuntuDistroSeries()
@@ -1255,10 +1380,16 @@ class LaunchpadObjectFactory(ObjectFactory):
             sourcepackagename=sourcepackagename,
             distroseries=distroseries,
             packaging=packaging_type,
-            owner=owner)
+            owner=owner,
+        )
 
-    def makePackageBranch(self, sourcepackage=None, distroseries=None,
-                          sourcepackagename=None, **kwargs):
+    def makePackageBranch(
+        self,
+        sourcepackage=None,
+        distroseries=None,
+        sourcepackagename=None,
+        **kwargs
+    ):
         """Make a package branch on an arbitrary package.
 
         See `makeBranch` for more information on arguments.
@@ -1267,15 +1398,16 @@ class LaunchpadObjectFactory(ObjectFactory):
         `distroseries` and `sourcepackagename`, but not combinations or all of
         them.
         """
-        assert not(sourcepackage is not None and distroseries is not None), (
-            "Don't pass in both sourcepackage and distroseries")
-        assert not(sourcepackage is not None
-                   and sourcepackagename is not None), (
-            "Don't pass in both sourcepackage and sourcepackagename")
+        assert not (
+            sourcepackage is not None and distroseries is not None
+        ), "Don't pass in both sourcepackage and distroseries"
+        assert not (
+            sourcepackage is not None and sourcepackagename is not None
+        ), "Don't pass in both sourcepackage and sourcepackagename"
         if sourcepackage is None:
             sourcepackage = self.makeSourcePackage(
-                sourcepackagename=sourcepackagename,
-                distroseries=distroseries)
+                sourcepackagename=sourcepackagename, distroseries=distroseries
+            )
         return self.makeBranch(sourcepackage=sourcepackage, **kwargs)
 
     def makePersonalBranch(self, owner=None, **kwargs):
@@ -1286,7 +1418,8 @@ class LaunchpadObjectFactory(ObjectFactory):
         if owner is None:
             owner = self.makePerson()
         return self.makeBranch(
-            owner=owner, product=None, sourcepackage=None, **kwargs)
+            owner=owner, product=None, sourcepackage=None, **kwargs
+        )
 
     def makeProductBranch(self, product=None, **kwargs):
         """Make a product branch on an arbitrary product.
@@ -1304,11 +1437,17 @@ class LaunchpadObjectFactory(ObjectFactory):
         """
         return self.makeProductBranch(**kwargs)
 
-    def makeBranchTargetBranch(self, target, branch_type=BranchType.HOSTED,
-                               name=None, owner=None, creator=None):
+    def makeBranchTargetBranch(
+        self,
+        target,
+        branch_type=BranchType.HOSTED,
+        name=None,
+        owner=None,
+        creator=None,
+    ):
         """Create a branch in a BranchTarget."""
         if name is None:
-            name = self.getUniqueString('branch')
+            name = self.getUniqueString("branch")
         if owner is None:
             owner = self.makePerson()
         if creator is None:
@@ -1316,25 +1455,31 @@ class LaunchpadObjectFactory(ObjectFactory):
         namespace = target.getNamespace(owner)
         return namespace.createBranch(branch_type, name, creator)
 
-    def makeRelatedBranchesForSourcePackage(self, sourcepackage=None,
-                                            **kwargs):
+    def makeRelatedBranchesForSourcePackage(
+        self, sourcepackage=None, **kwargs
+    ):
         """Create some branches associated with a sourcepackage."""
 
         reference_branch = self.makePackageBranch(sourcepackage=sourcepackage)
         return self.makeRelatedBranches(
-                reference_branch=reference_branch, **kwargs)
+            reference_branch=reference_branch, **kwargs
+        )
 
     def makeRelatedBranchesForProduct(self, product=None, **kwargs):
         """Create some branches associated with a product."""
 
         reference_branch = self.makeProductBranch(product=product)
         return self.makeRelatedBranches(
-                reference_branch=reference_branch, **kwargs)
+            reference_branch=reference_branch, **kwargs
+        )
 
-    def makeRelatedBranches(self, reference_branch=None,
-                            with_series_branches=True,
-                            with_package_branches=True,
-                            with_private_branches=False):
+    def makeRelatedBranches(
+        self,
+        reference_branch=None,
+        with_series_branches=True,
+        with_package_branches=True,
+        with_private_branches=False,
+    ):
         """Create some branches associated with a reference branch.
         The other branches are:
           - series branches: a set of branches associated with product
@@ -1357,8 +1502,8 @@ class LaunchpadObjectFactory(ObjectFactory):
             naked_product = removeSecurityProxy(self.makeProduct())
             # Create the 'source' branch ie the base branch of a recipe.
             reference_branch = self.makeProductBranch(
-                                            name="reference_branch",
-                                            product=naked_product)
+                name="reference_branch", product=naked_product
+            )
         elif reference_branch.product is not None:
             naked_product = removeSecurityProxy(reference_branch.product)
 
@@ -1371,34 +1516,43 @@ class LaunchpadObjectFactory(ObjectFactory):
             def makeSeriesBranch(name, information_type):
                 branch = self.makeBranch(
                     name=name,
-                    product=naked_product, owner=related_branch_owner,
-                    information_type=information_type)
+                    product=naked_product,
+                    owner=related_branch_owner,
+                    information_type=information_type,
+                )
                 series = self.makeProductSeries(
-                    product=naked_product, branch=branch)
+                    product=naked_product, branch=branch
+                )
                 return branch, series
+
             for x in range(4):
                 information_type = InformationType.PUBLIC
                 if x == 0 and with_private_branches:
                     information_type = InformationType.USERDATA
                 (branch, series) = makeSeriesBranch(
-                        ("series_branch_%s" % x), information_type)
+                    ("series_branch_%s" % x), information_type
+                )
                 if information_type == InformationType.PUBLIC:
                     series_branch_info.append((branch, series))
 
             # Sort them
             related_series_branch_info = sorted_version_numbers(
-                    series_branch_info, key=lambda branch_info: (
-                        getattr(branch_info[1], 'name')))
+                series_branch_info,
+                key=lambda branch_info: (getattr(branch_info[1], "name")),
+            )
 
             # Add a development branch at the start of the list.
-            naked_product.development_focus.name = 'trunk'
+            naked_product.development_focus.name = "trunk"
             devel_branch = self.makeProductBranch(
-                product=naked_product, name='trunk_branch',
-                owner=related_branch_owner)
+                product=naked_product,
+                name="trunk_branch",
+                owner=related_branch_owner,
+            )
             linked_branch = ICanHasLinkedBranch(naked_product)
             linked_branch.setBranch(devel_branch)
-            related_series_branch_info.insert(0,
-                    (devel_branch, naked_product.development_focus))
+            related_series_branch_info.insert(
+                0, (devel_branch, naked_product.development_focus)
+            )
 
         if with_package_branches:
             # Create related package branches if the base_branch is
@@ -1407,30 +1561,35 @@ class LaunchpadObjectFactory(ObjectFactory):
 
                 def makePackageBranch(name, information_type):
                     distro = self.makeDistribution()
-                    distroseries = self.makeDistroSeries(
-                        distribution=distro)
+                    distroseries = self.makeDistroSeries(distribution=distro)
                     sourcepackagename = self.makeSourcePackageName()
 
                     suitesourcepackage = self.makeSuiteSourcePackage(
                         sourcepackagename=sourcepackagename,
                         distroseries=distroseries,
-                        pocket=PackagePublishingPocket.RELEASE)
+                        pocket=PackagePublishingPocket.RELEASE,
+                    )
                     naked_sourcepackage = removeSecurityProxy(
-                        suitesourcepackage)
+                        suitesourcepackage
+                    )
 
                     branch = self.makePackageBranch(
-                        name=name, owner=related_branch_owner,
+                        name=name,
+                        owner=related_branch_owner,
                         sourcepackagename=sourcepackagename,
                         distroseries=distroseries,
-                        information_type=information_type)
+                        information_type=information_type,
+                    )
                     linked_branch = ICanHasLinkedBranch(naked_sourcepackage)
-                    with celebrity_logged_in('admin'):
+                    with celebrity_logged_in("admin"):
                         linked_branch.setBranch(branch, related_branch_owner)
 
                     series = self.makeProductSeries(product=naked_product)
                     self.makePackagingLink(
-                        distroseries=distroseries, productseries=series,
-                        sourcepackagename=sourcepackagename)
+                        distroseries=distroseries,
+                        productseries=series,
+                        sourcepackagename=sourcepackagename,
+                    )
                     return branch, distroseries
 
                 for x in range(5):
@@ -1438,39 +1597,42 @@ class LaunchpadObjectFactory(ObjectFactory):
                     if x == 0 and with_private_branches:
                         information_type = InformationType.USERDATA
                     branch, distroseries = makePackageBranch(
-                            ("product_package_branch_%s" % x),
-                            information_type)
+                        ("product_package_branch_%s" % x), information_type
+                    )
                     if information_type == InformationType.PUBLIC:
                         related_package_branch_info.append(
-                                (branch, distroseries))
+                            (branch, distroseries)
+                        )
 
             # Create related package branches if the base_branch is
             # associated with a sourcepackage.
             if reference_branch.sourcepackage is not None:
                 distroseries = reference_branch.sourcepackage.distroseries
                 for pocket in [
-                        PackagePublishingPocket.RELEASE,
-                        PackagePublishingPocket.UPDATES,
-                        ]:
+                    PackagePublishingPocket.RELEASE,
+                    PackagePublishingPocket.UPDATES,
+                ]:
                     branch = self.makePackageBranch(
-                            name="package_branch_%s" % pocket.name,
-                            distroseries=distroseries)
-                    with celebrity_logged_in('admin'):
+                        name="package_branch_%s" % pocket.name,
+                        distroseries=distroseries,
+                    )
+                    with celebrity_logged_in("admin"):
                         reference_branch.sourcepackage.setBranch(
-                            pocket, branch,
-                            related_branch_owner)
+                            pocket, branch, related_branch_owner
+                        )
 
-                    related_package_branch_info.append(
-                            (branch, distroseries))
+                    related_package_branch_info.append((branch, distroseries))
 
             related_package_branch_info = sorted_version_numbers(
-                    related_package_branch_info, key=lambda branch_info: (
-                        getattr(branch_info[1], 'name')))
+                related_package_branch_info,
+                key=lambda branch_info: (getattr(branch_info[1], "name")),
+            )
 
         return (
             reference_branch,
             related_series_branch_info,
-            related_package_branch_info)
+            related_package_branch_info,
+        )
 
     def enableDefaultStackingForProduct(self, product, branch=None):
         """Give 'product' a default stacked-on branch.
@@ -1483,8 +1645,7 @@ class LaunchpadObjectFactory(ObjectFactory):
             branch = self.makeBranch(product=product)
         # We just remove the security proxies to be able to change the objects
         # here.
-        removeSecurityProxy(branch).branchChanged(
-            '', 'rev1', None, None, None)
+        removeSecurityProxy(branch).branchChanged("", "rev1", None, None, None)
         naked_series = removeSecurityProxy(product.development_focus)
         naked_series.branch = branch
         return branch
@@ -1498,20 +1659,30 @@ class LaunchpadObjectFactory(ObjectFactory):
         """
         # We just remove the security proxies to be able to change the branch
         # here.
-        removeSecurityProxy(branch).branchChanged(
-            '', 'rev1', None, None, None)
+        removeSecurityProxy(branch).branchChanged("", "rev1", None, None, None)
         with person_logged_in(package.distribution.owner):
             package.development_version.setBranch(
-                PackagePublishingPocket.RELEASE, branch,
-                package.distribution.owner)
+                PackagePublishingPocket.RELEASE,
+                branch,
+                package.distribution.owner,
+            )
         return branch
 
-    def makeBranchMergeProposal(self, target_branch=None, registrant=None,
-                                set_state=None, prerequisite_branch=None,
-                                product=None, initial_comment=None,
-                                source_branch=None, date_created=None,
-                                commit_message=None, description=None,
-                                reviewer=None, merged_revno=None):
+    def makeBranchMergeProposal(
+        self,
+        target_branch=None,
+        registrant=None,
+        set_state=None,
+        prerequisite_branch=None,
+        product=None,
+        initial_comment=None,
+        source_branch=None,
+        date_created=None,
+        commit_message=None,
+        description=None,
+        reviewer=None,
+        merged_revno=None,
+    ):
         """Create a proposal to merge based on anonymous branches."""
         if target_branch is not None:
             target_branch = removeSecurityProxy(target_branch)
@@ -1543,39 +1714,57 @@ class LaunchpadObjectFactory(ObjectFactory):
         if reviewer is not None:
             review_requests.append((reviewer, None))
         proposal = source_branch.addLandingTarget(
-            registrant, target_branch, review_requests=review_requests,
-            merge_prerequisite=prerequisite_branch, description=description,
-            commit_message=commit_message, date_created=date_created)
+            registrant,
+            target_branch,
+            review_requests=review_requests,
+            merge_prerequisite=prerequisite_branch,
+            description=description,
+            commit_message=commit_message,
+            date_created=date_created,
+        )
 
         unsafe_proposal = removeSecurityProxy(proposal)
         unsafe_proposal.merged_revno = merged_revno
-        if (set_state is None or
-            set_state == BranchMergeProposalStatus.WORK_IN_PROGRESS):
+        if (
+            set_state is None
+            or set_state == BranchMergeProposalStatus.WORK_IN_PROGRESS
+        ):
             # The initial state is work in progress, so do nothing.
             pass
         elif set_state == BranchMergeProposalStatus.NEEDS_REVIEW:
             unsafe_proposal.requestReview()
         elif set_state == BranchMergeProposalStatus.CODE_APPROVED:
             unsafe_proposal.approveBranch(
-                proposal.merge_target.owner, 'some_revision')
+                proposal.merge_target.owner, "some_revision"
+            )
         elif set_state == BranchMergeProposalStatus.REJECTED:
             unsafe_proposal.rejectBranch(
-                proposal.merge_target.owner, 'some_revision')
+                proposal.merge_target.owner, "some_revision"
+            )
         elif set_state == BranchMergeProposalStatus.MERGED:
             unsafe_proposal.markAsMerged()
         elif set_state == BranchMergeProposalStatus.SUPERSEDED:
             unsafe_proposal.resubmit(proposal.registrant)
         else:
-            raise AssertionError('Unknown status: %s' % set_state)
+            raise AssertionError("Unknown status: %s" % set_state)
 
         return proposal
 
-    def makeBranchMergeProposalForGit(self, target_ref=None, registrant=None,
-                                      set_state=None, prerequisite_ref=None,
-                                      target=_DEFAULT, initial_comment=None,
-                                      source_ref=None, date_created=None,
-                                      commit_message=None, description=None,
-                                      reviewer=None, merged_revision_id=None):
+    def makeBranchMergeProposalForGit(
+        self,
+        target_ref=None,
+        registrant=None,
+        set_state=None,
+        prerequisite_ref=None,
+        target=_DEFAULT,
+        initial_comment=None,
+        source_ref=None,
+        date_created=None,
+        commit_message=None,
+        description=None,
+        reviewer=None,
+        merged_revision_id=None,
+    ):
         """Create a proposal to merge based on anonymous branches."""
         if target is not _DEFAULT:
             pass
@@ -1607,35 +1796,45 @@ class LaunchpadObjectFactory(ObjectFactory):
         if reviewer is not None:
             review_requests.append((reviewer, None))
         proposal = source_ref.addLandingTarget(
-            registrant, target_ref, review_requests=review_requests,
-            merge_prerequisite=prerequisite_ref, description=description,
-            commit_message=commit_message, date_created=date_created)
+            registrant,
+            target_ref,
+            review_requests=review_requests,
+            merge_prerequisite=prerequisite_ref,
+            description=description,
+            commit_message=commit_message,
+            date_created=date_created,
+        )
 
         unsafe_proposal = removeSecurityProxy(proposal)
         unsafe_proposal.merged_revision_id = merged_revision_id
-        if (set_state is None or
-            set_state == BranchMergeProposalStatus.WORK_IN_PROGRESS):
+        if (
+            set_state is None
+            or set_state == BranchMergeProposalStatus.WORK_IN_PROGRESS
+        ):
             # The initial state is work in progress, so do nothing.
             pass
         elif set_state == BranchMergeProposalStatus.NEEDS_REVIEW:
             unsafe_proposal.requestReview()
         elif set_state == BranchMergeProposalStatus.CODE_APPROVED:
             unsafe_proposal.approveBranch(
-                proposal.merge_target.owner, 'some_revision')
+                proposal.merge_target.owner, "some_revision"
+            )
         elif set_state == BranchMergeProposalStatus.REJECTED:
             unsafe_proposal.rejectBranch(
-                proposal.merge_target.owner, 'some_revision')
+                proposal.merge_target.owner, "some_revision"
+            )
         elif set_state == BranchMergeProposalStatus.MERGED:
             unsafe_proposal.markAsMerged()
         elif set_state == BranchMergeProposalStatus.SUPERSEDED:
             unsafe_proposal.resubmit(proposal.registrant)
         else:
-            raise AssertionError('Unknown status: %s' % set_state)
+            raise AssertionError("Unknown status: %s" % set_state)
 
         return proposal
 
-    def makeBranchSubscription(self, branch=None, person=None,
-                               subscribed_by=None):
+    def makeBranchSubscription(
+        self, branch=None, person=None, subscribed_by=None
+    ):
         """Create a BranchSubscription."""
         if branch is None:
             branch = self.makeBranch()
@@ -1643,20 +1842,32 @@ class LaunchpadObjectFactory(ObjectFactory):
             person = self.makePerson()
         if subscribed_by is None:
             subscribed_by = person
-        return branch.subscribe(removeSecurityProxy(person),
-            BranchSubscriptionNotificationLevel.NOEMAIL, None,
-            CodeReviewNotificationLevel.NOEMAIL, subscribed_by)
-
-    def makeDiff(self, size='small'):
-        diff_path = os.path.join(os.path.dirname(__file__),
-                                 'data/{}.diff'.format(size))
-        with open(os.path.join(diff_path), 'rb') as diff:
+        return branch.subscribe(
+            removeSecurityProxy(person),
+            BranchSubscriptionNotificationLevel.NOEMAIL,
+            None,
+            CodeReviewNotificationLevel.NOEMAIL,
+            subscribed_by,
+        )
+
+    def makeDiff(self, size="small"):
+        diff_path = os.path.join(
+            os.path.dirname(__file__), "data/{}.diff".format(size)
+        )
+        with open(os.path.join(diff_path), "rb") as diff:
             diff_text = diff.read()
             return ProxyFactory(
-                Diff.fromFile(BytesIO(diff_text), len(diff_text)))
+                Diff.fromFile(BytesIO(diff_text), len(diff_text))
+            )
 
-    def makePreviewDiff(self, conflicts='', merge_proposal=None,
-                        date_created=None, size='small', git=False):
+    def makePreviewDiff(
+        self,
+        conflicts="",
+        merge_proposal=None,
+        date_created=None,
+        size="small",
+        git=False,
+    ):
         diff = self.makeDiff(size)
         if merge_proposal is None:
             if git:
@@ -1673,8 +1884,9 @@ class LaunchpadObjectFactory(ObjectFactory):
             preview_diff.date_created = date_created
         return preview_diff
 
-    def makeIncrementalDiff(self, merge_proposal=None, old_revision=None,
-                            new_revision=None):
+    def makeIncrementalDiff(
+        self, merge_proposal=None, old_revision=None, new_revision=None
+    ):
         diff = self.makeDiff()
         if merge_proposal is None:
             source_branch = self.makeBranch()
@@ -1688,37 +1900,52 @@ class LaunchpadObjectFactory(ObjectFactory):
             else:
                 parent_ids = [parent.revision_id]
             branch_revision = self.makeBranchRevision(
-                source_branch, sequence=sequence,
-                revision_date=self.getUniqueDate(), parent_ids=parent_ids)
+                source_branch,
+                sequence=sequence,
+                revision_date=self.getUniqueDate(),
+                parent_ids=parent_ids,
+            )
             return branch_revision.revision
+
         if old_revision is None:
             old_revision = make_revision()
         if merge_proposal is None:
             merge_proposal = self.makeBranchMergeProposal(
-                date_created=self.getUniqueDate(),
-                source_branch=source_branch)
+                date_created=self.getUniqueDate(), source_branch=source_branch
+            )
         if new_revision is None:
             new_revision = make_revision(old_revision)
         return merge_proposal.generateIncrementalDiff(
-            old_revision, new_revision, diff)
+            old_revision, new_revision, diff
+        )
 
     def makeBzrRevision(self, revision_id=None, parent_ids=None, props=None):
         if revision_id is None:
-            revision_id = self.getUniqueString('revision-id')
+            revision_id = self.getUniqueString("revision-id")
         if parent_ids is None:
             parent_ids = []
         return BzrRevision(
-            message=self.getUniqueString('message'),
+            message=self.getUniqueString("message"),
             revision_id=revision_id,
-            committer=self.getUniqueString('committer'),
+            committer=self.getUniqueString("committer"),
             parent_ids=parent_ids,
-            timestamp=0, timezone=0, properties=props)
+            timestamp=0,
+            timezone=0,
+            properties=props,
+        )
 
-    def makeRevision(self, author=None, revision_date=None, parent_ids=None,
-                     rev_id=None, log_body=None, date_created=None):
+    def makeRevision(
+        self,
+        author=None,
+        revision_date=None,
+        parent_ids=None,
+        rev_id=None,
+        log_body=None,
+        date_created=None,
+    ):
         """Create a single `Revision`."""
         if author is None:
-            author = self.getUniqueString('author')
+            author = self.getUniqueString("author")
         elif IPerson.providedBy(author):
             author = removeSecurityProxy(author).preferredemail.email
         if revision_date is None:
@@ -1726,19 +1953,24 @@ class LaunchpadObjectFactory(ObjectFactory):
         if parent_ids is None:
             parent_ids = []
         if rev_id is None:
-            rev_id = self.getUniqueUnicode('revision-id')
+            rev_id = self.getUniqueUnicode("revision-id")
         else:
             rev_id = six.ensure_text(rev_id)
         if log_body is None:
-            log_body = self.getUniqueString('log-body')
+            log_body = self.getUniqueString("log-body")
         return getUtility(IRevisionSet).new(
-            revision_id=rev_id, log_body=log_body,
-            revision_date=revision_date, revision_author=author,
-            parent_ids=parent_ids, properties={},
-            _date_created=date_created)
+            revision_id=rev_id,
+            log_body=log_body,
+            revision_date=revision_date,
+            revision_author=author,
+            parent_ids=parent_ids,
+            properties={},
+            _date_created=date_created,
+        )
 
-    def makeRevisionsForBranch(self, branch, count=5, author=None,
-                               date_generator=None):
+    def makeRevisionsForBranch(
+        self, branch, count=5, author=None, date_generator=None
+    ):
         """Add `count` revisions to the revision history of `branch`.
 
         :param branch: The branch to add the revisions to.
@@ -1749,8 +1981,8 @@ class LaunchpadObjectFactory(ObjectFactory):
         """
         if date_generator is None:
             date_generator = time_counter(
-                datetime(2007, 1, 1, tzinfo=pytz.UTC),
-                delta=timedelta(days=1))
+                datetime(2007, 1, 1, tzinfo=pytz.UTC), delta=timedelta(days=1)
+            )
         sequence = branch.revision_count
         parent = branch.getTipRevision()
         if parent is None:
@@ -1760,15 +1992,16 @@ class LaunchpadObjectFactory(ObjectFactory):
 
         revision_set = getUtility(IRevisionSet)
         if author is None:
-            author = self.getUniqueString('author')
+            author = self.getUniqueString("author")
         for index in range(count):
             revision = revision_set.new(
-                revision_id=self.getUniqueString('revision-id'),
-                log_body=self.getUniqueString('log-body'),
+                revision_id=self.getUniqueString("revision-id"),
+                log_body=self.getUniqueString("log-body"),
                 revision_date=next(date_generator),
                 revision_author=author,
                 parent_ids=parent_ids,
-                properties={})
+                properties={},
+            )
             sequence += 1
             branch.createBranchRevision(sequence, revision)
             parent = revision
@@ -1776,24 +2009,40 @@ class LaunchpadObjectFactory(ObjectFactory):
         if branch.branch_type not in (BranchType.REMOTE, BranchType.HOSTED):
             branch.startMirroring()
         removeSecurityProxy(branch).branchChanged(
-            '', parent.revision_id, None, None, None)
+            "", parent.revision_id, None, None, None
+        )
         branch.updateScannedDetails(parent, sequence)
 
-    def makeBranchRevision(self, branch=None, revision_id=None, sequence=None,
-                           parent_ids=None, revision_date=None):
+    def makeBranchRevision(
+        self,
+        branch=None,
+        revision_id=None,
+        sequence=None,
+        parent_ids=None,
+        revision_date=None,
+    ):
         if branch is None:
             branch = self.makeBranch()
         else:
             branch = removeSecurityProxy(branch)
         revision = self.makeRevision(
-            rev_id=revision_id, parent_ids=parent_ids,
-            revision_date=revision_date)
+            rev_id=revision_id,
+            parent_ids=parent_ids,
+            revision_date=revision_date,
+        )
         return branch.createBranchRevision(sequence, revision)
 
-    def makeGitRepository(self, repository_type=None, owner=None,
-                          reviewer=None, target=_DEFAULT, registrant=None,
-                          name=None, information_type=None,
-                          **optional_repository_args):
+    def makeGitRepository(
+        self,
+        repository_type=None,
+        owner=None,
+        reviewer=None,
+        target=_DEFAULT,
+        registrant=None,
+        name=None,
+        information_type=None,
+        **optional_repository_args
+    ):
         """Create and return a new, arbitrary GitRepository.
 
         Any parameters for `IGitNamespace.createRepository` can be specified
@@ -1804,7 +2053,7 @@ class LaunchpadObjectFactory(ObjectFactory):
         if owner is None:
             owner = self.makePerson()
         if name is None:
-            name = self.getUniqueUnicode('gitrepository')
+            name = self.getUniqueUnicode("gitrepository")
 
         if target is _DEFAULT:
             target = self.makeProduct()
@@ -1817,16 +2066,22 @@ class LaunchpadObjectFactory(ObjectFactory):
 
         namespace = get_git_namespace(target, owner)
         repository = namespace.createRepository(
-            repository_type=repository_type, registrant=registrant, name=name,
-            reviewer=reviewer, **optional_repository_args)
+            repository_type=repository_type,
+            registrant=registrant,
+            name=name,
+            reviewer=reviewer,
+            **optional_repository_args,
+        )
         naked_repository = removeSecurityProxy(repository)
         if information_type is not None:
             naked_repository.transitionToInformationType(
-                information_type, registrant, verify_policy=False)
+                information_type, registrant, verify_policy=False
+            )
         return repository
 
-    def makeGitSubscription(self, repository=None, person=None,
-                            subscribed_by=None):
+    def makeGitSubscription(
+        self, repository=None, person=None, subscribed_by=None
+    ):
         """Create a GitSubscription."""
         if repository is None:
             repository = self.makeGitRepository()
@@ -1834,27 +2089,34 @@ class LaunchpadObjectFactory(ObjectFactory):
             person = self.makePerson()
         if subscribed_by is None:
             subscribed_by = person
-        return repository.subscribe(removeSecurityProxy(person),
-            BranchSubscriptionNotificationLevel.NOEMAIL, None,
-            CodeReviewNotificationLevel.NOEMAIL, subscribed_by)
+        return repository.subscribe(
+            removeSecurityProxy(person),
+            BranchSubscriptionNotificationLevel.NOEMAIL,
+            None,
+            CodeReviewNotificationLevel.NOEMAIL,
+            subscribed_by,
+        )
 
     def makeGitRefs(self, repository=None, paths=None, **repository_kwargs):
         """Create and return a list of new, arbitrary GitRefs."""
         if repository is None:
             repository = self.makeGitRepository(**repository_kwargs)
         if paths is None:
-            paths = [self.getUniqueUnicode('refs/heads/path')]
+            paths = [self.getUniqueUnicode("refs/heads/path")]
         refs_info = {
             path: {
                 "sha1": hashlib.sha1(path.encode()).hexdigest(),
                 "type": GitObjectType.COMMIT,
-                }
-            for path in paths}
+            }
+            for path in paths
+        }
         with GitHostingFixture():
             refs_by_path = {
                 ref.path: ref
                 for ref in removeSecurityProxy(repository).createOrUpdateRefs(
-                    refs_info, get_objects=True)}
+                    refs_info, get_objects=True
+                )
+            }
         return [refs_by_path[path] for path in paths]
 
     def makeGitRefRemote(self, repository_url=None, path=None):
@@ -1862,11 +2124,17 @@ class LaunchpadObjectFactory(ObjectFactory):
         if repository_url is None:
             repository_url = self.getUniqueURL()
         if path is None:
-            path = self.getUniqueUnicode('refs/heads/path')
+            path = self.getUniqueUnicode("refs/heads/path")
         return getUtility(IGitRefRemoteSet).new(repository_url, path)
 
-    def makeGitRule(self, repository=None, ref_pattern="refs/heads/*",
-                    creator=None, position=None, **repository_kwargs):
+    def makeGitRule(
+        self,
+        repository=None,
+        ref_pattern="refs/heads/*",
+        creator=None,
+        position=None,
+        **repository_kwargs
+    ):
         """Create a Git repository access rule."""
         if repository is None:
             repository = self.makeGitRepository(**repository_kwargs)
@@ -1875,9 +2143,16 @@ class LaunchpadObjectFactory(ObjectFactory):
         with person_logged_in(creator):
             return repository.addRule(ref_pattern, creator, position=position)
 
-    def makeGitRuleGrant(self, rule=None, grantee=None, grantor=None,
-                         can_create=False, can_push=False,
-                         can_force_push=False, **rule_kwargs):
+    def makeGitRuleGrant(
+        self,
+        rule=None,
+        grantee=None,
+        grantor=None,
+        can_create=False,
+        can_push=False,
+        can_force_push=False,
+        **rule_kwargs
+    ):
         """Create a Git repository access grant."""
         if rule is None:
             rule = self.makeGitRule(**rule_kwargs)
@@ -1887,13 +2162,24 @@ class LaunchpadObjectFactory(ObjectFactory):
             grantor = rule.repository.owner
         with person_logged_in(grantor):
             return rule.addGrant(
-                grantee, grantor, can_create=can_create, can_push=can_push,
-                can_force_push=can_force_push)
+                grantee,
+                grantor,
+                can_create=can_create,
+                can_push=can_push,
+                can_force_push=can_force_push,
+            )
 
-    def makeRevisionStatusReport(self, user=None, title=None,
-                                 git_repository=None, commit_sha1=None,
-                                 result_summary=None, url=None, result=None,
-                                 ci_build=None):
+    def makeRevisionStatusReport(
+        self,
+        user=None,
+        title=None,
+        git_repository=None,
+        commit_sha1=None,
+        result_summary=None,
+        url=None,
+        result=None,
+        ci_build=None,
+    ):
         """Create a new RevisionStatusReport."""
         if title is None:
             title = self.getUniqueUnicode()
@@ -1912,27 +2198,53 @@ class LaunchpadObjectFactory(ObjectFactory):
         if result_summary is None:
             result_summary = self.getUniqueUnicode()
         return getUtility(IRevisionStatusReportSet).new(
-            user, title, git_repository, commit_sha1, url,
-            result_summary, result, ci_build=ci_build)
+            user,
+            title,
+            git_repository,
+            commit_sha1,
+            url,
+            result_summary,
+            result,
+            ci_build=ci_build,
+        )
 
     def makeRevisionStatusArtifact(
-            self, lfa=None, content=None, report=None,
-            artifact_type=None, restricted=False):
+        self,
+        lfa=None,
+        content=None,
+        report=None,
+        artifact_type=None,
+        restricted=False,
+    ):
         """Create a new RevisionStatusArtifact."""
         if lfa is None:
             lfa = self.makeLibraryFileAlias(
-                content=content, restricted=restricted)
+                content=content, restricted=restricted
+            )
         if report is None:
             report = self.makeRevisionStatusReport()
         if artifact_type is None:
             artifact_type = RevisionStatusArtifactType.LOG
         return getUtility(IRevisionStatusArtifactSet).new(
-            lfa, report, artifact_type)
+            lfa, report, artifact_type
+        )
 
-    def makeBug(self, target=None, owner=None, bug_watch_url=None,
-                information_type=None, date_closed=None, title=None,
-                date_created=None, description=None, comment=None,
-                status=None, milestone=None, series=None, tags=None):
+    def makeBug(
+        self,
+        target=None,
+        owner=None,
+        bug_watch_url=None,
+        information_type=None,
+        date_closed=None,
+        title=None,
+        date_created=None,
+        description=None,
+        comment=None,
+        status=None,
+        milestone=None,
+        series=None,
+        tags=None,
+    ):
         """Create and return a new, arbitrary Bug.
 
         The bug returned uses default values where possible. See
@@ -1962,17 +2274,25 @@ class LaunchpadObjectFactory(ObjectFactory):
         if IDistributionSourcePackage.providedBy(target):
             self.makeSourcePackagePublishingHistory(
                 distroseries=target.distribution.currentseries,
-                sourcepackagename=target.sourcepackagename)
+                sourcepackagename=target.sourcepackagename,
+            )
         if owner is None:
             owner = self.makePerson()
         if title is None:
-            title = self.getUniqueString('bug-title')
+            title = self.getUniqueString("bug-title")
         if comment is None:
             comment = self.getUniqueString()
         create_bug_params = CreateBugParams(
-            owner, title, comment=comment, information_type=information_type,
-            datecreated=date_created, description=description,
-            status=status, tags=tags, target=target)
+            owner,
+            title,
+            comment=comment,
+            information_type=information_type,
+            datecreated=date_created,
+            description=description,
+            status=status,
+            tags=tags,
+            target=target,
+        )
         bug = getUtility(IBugSet).createBug(create_bug_params)
         if bug_watch_url is not None:
             # fromText() creates a bug watch associated with the bug.
@@ -1982,11 +2302,13 @@ class LaunchpadObjectFactory(ObjectFactory):
         if date_closed is not None:
             with person_logged_in(owner):
                 bugtask.transitionToStatus(
-                    BugTaskStatus.FIXRELEASED, owner, when=date_closed)
+                    BugTaskStatus.FIXRELEASED, owner, when=date_closed
+                )
         if milestone is not None:
             with person_logged_in(owner):
                 bugtask.transitionToMilestone(
-                    milestone, milestone.target.owner)
+                    milestone, milestone.target.owner
+                )
         if series is not None:
             with person_logged_in(owner):
                 task = bug.addTask(owner, series)
@@ -1994,8 +2316,9 @@ class LaunchpadObjectFactory(ObjectFactory):
         removeSecurityProxy(bug).clearBugNotificationRecipientsCache()
         return bug
 
-    def makeBugTask(self, bug=None, target=None, owner=None, publish=True,
-                    status=None):
+    def makeBugTask(
+        self, bug=None, target=None, owner=None, publish=True, status=None
+    ):
         """Create and return a bug task.
 
         If the bug is already targeted to the given target, the existing
@@ -2028,7 +2351,8 @@ class LaunchpadObjectFactory(ObjectFactory):
                     target = self.makeProductSeries(product=existing_pillar)
                 elif IDistribution.providedBy(existing_pillar):
                     target = self.makeDistroSeries(
-                        distribution=existing_pillar)
+                        distribution=existing_pillar
+                    )
             if target is None:
                 target = self.makeProduct()
 
@@ -2045,18 +2369,22 @@ class LaunchpadObjectFactory(ObjectFactory):
             if publish:
                 self.makeSourcePackagePublishingHistory(
                     distroseries=target.distroseries,
-                    sourcepackagename=target.sourcepackagename)
+                    sourcepackagename=target.sourcepackagename,
+                )
         if IDistributionSourcePackage.providedBy(target):
             if publish:
                 self.makeSourcePackagePublishingHistory(
                     distroseries=target.distribution.currentseries,
-                    sourcepackagename=target.sourcepackagename)
+                    sourcepackagename=target.sourcepackagename,
+                )
         if prerequisite_target is not None:
             prerequisite = bug and removeSecurityProxy(bug).getBugTask(
-                prerequisite_target)
+                prerequisite_target
+            )
             if prerequisite is None:
                 prerequisite = self.makeBugTask(
-                    bug, prerequisite_target, publish=publish)
+                    bug, prerequisite_target, publish=publish
+                )
                 bug = prerequisite.bug
 
         if bug is None:
@@ -2065,7 +2393,8 @@ class LaunchpadObjectFactory(ObjectFactory):
         if owner is None:
             owner = self.makePerson()
         task = getUtility(IBugTaskSet).createTask(
-            removeSecurityProxy(bug), owner, target, status=status)
+            removeSecurityProxy(bug), owner, target, status=status
+        )
         removeSecurityProxy(bug).clearBugNotificationRecipientsCache()
         return task
 
@@ -2085,31 +2414,34 @@ class LaunchpadObjectFactory(ObjectFactory):
         else:
             non_series = target.parent
             series = target
-        with celebrity_logged_in('admin'):
+        with celebrity_logged_in("admin"):
             bug = self.makeBugTask(bug=bug, target=non_series).bug
             nomination = bug.addNomination(
-                getUtility(ILaunchpadCelebrities).admin, series)
+                getUtility(ILaunchpadCelebrities).admin, series
+            )
         return nomination
 
-    def makeBugTracker(self, base_url=None, bugtrackertype=None, title=None,
-                       name=None):
+    def makeBugTracker(
+        self, base_url=None, bugtrackertype=None, title=None, name=None
+    ):
         """Make a new bug tracker."""
         owner = self.makePerson()
 
         if base_url is None:
-            base_url = 'http://%s.example.com/' % self.getUniqueString()
+            base_url = "http://%s.example.com/"; % self.getUniqueString()
         if bugtrackertype is None:
             bugtrackertype = BugTrackerType.BUGZILLA
 
         return getUtility(IBugTrackerSet).ensureBugTracker(
-            base_url, owner, bugtrackertype, title=title, name=name)
+            base_url, owner, bugtrackertype, title=title, name=name
+        )
 
     def makeBugTrackerWithWatches(self, base_url=None, count=2):
         """Make a new bug tracker with some watches."""
         bug_tracker = self.makeBugTracker(base_url=base_url)
         bug_watches = [
-            self.makeBugWatch(bugtracker=bug_tracker)
-            for i in range(count)]
+            self.makeBugWatch(bugtracker=bug_tracker) for i in range(count)
+        ]
         return (bug_tracker, bug_watches)
 
     def makeBugTrackerComponentGroup(self, name=None, bug_tracker=None):
@@ -2122,8 +2454,9 @@ class LaunchpadObjectFactory(ObjectFactory):
         component_group = bug_tracker.addRemoteComponentGroup(name)
         return component_group
 
-    def makeBugTrackerComponent(self, name=None, component_group=None,
-                                custom=None):
+    def makeBugTrackerComponent(
+        self, name=None, component_group=None, custom=None
+    ):
         """Make a new bug tracker component."""
         if name is None:
             name = self.getUniqueUnicode()
@@ -2137,8 +2470,14 @@ class LaunchpadObjectFactory(ObjectFactory):
             component = component_group.addComponent(name)
         return component
 
-    def makeBugWatch(self, remote_bug=None, bugtracker=None, bug=None,
-                     owner=None, bug_task=None):
+    def makeBugWatch(
+        self,
+        remote_bug=None,
+        bugtracker=None,
+        bug=None,
+        owner=None,
+        bug_task=None,
+    ):
         """Make a new bug watch."""
         if remote_bug is None:
             remote_bug = self.getUniqueInteger()
@@ -2160,15 +2499,18 @@ class LaunchpadObjectFactory(ObjectFactory):
             owner = self.makePerson()
 
         bug_watch = getUtility(IBugWatchSet).createBugWatch(
-            bug, owner, bugtracker, str(remote_bug))
+            bug, owner, bugtracker, str(remote_bug)
+        )
         if bug_task is not None:
             bug_task.bugwatch = bug_watch
-        removeSecurityProxy(bug_watch).next_check = (
-            datetime.now(pytz.timezone('UTC')))
+        removeSecurityProxy(bug_watch).next_check = datetime.now(
+            pytz.timezone("UTC")
+        )
         return bug_watch
 
-    def makeBugComment(self, bug=None, owner=None, subject=None, body=None,
-                       bug_watch=None):
+    def makeBugComment(
+        self, bug=None, owner=None, subject=None, body=None, bug_watch=None
+    ):
         """Create and return a new bug comment.
 
         :param bug: An `IBug` or a bug ID or name, or None, in which
@@ -2194,13 +2536,26 @@ class LaunchpadObjectFactory(ObjectFactory):
         if body is None:
             body = self.getUniqueString()
         with person_logged_in(owner):
-            return bug.newMessage(owner=owner, subject=subject, content=body,
-                                  parent=None, bugwatch=bug_watch,
-                                  remote_comment_id=None)
+            return bug.newMessage(
+                owner=owner,
+                subject=subject,
+                content=body,
+                parent=None,
+                bugwatch=bug_watch,
+                remote_comment_id=None,
+            )
 
-    def makeBugAttachment(self, bug=None, owner=None, data=None,
-                          comment=None, filename=None, content_type=None,
-                          description=None, is_patch=_DEFAULT):
+    def makeBugAttachment(
+        self,
+        bug=None,
+        owner=None,
+        data=None,
+        comment=None,
+        filename=None,
+        content_type=None,
+        description=None,
+        is_patch=_DEFAULT,
+    ):
         """Create and return a new bug attachment.
 
         :param bug: An `IBug` or a bug ID or name, or None, in which
@@ -2238,13 +2593,20 @@ class LaunchpadObjectFactory(ObjectFactory):
         # passed it.
         other_params = {}
         if is_patch is not _DEFAULT:
-            other_params['is_patch'] = is_patch
+            other_params["is_patch"] = is_patch
         return bug.addAttachment(
-            owner, data, comment, filename, content_type=content_type,
-            description=description, **other_params)
+            owner,
+            data,
+            comment,
+            filename,
+            content_type=content_type,
+            description=description,
+            **other_params,
+        )
 
-    def makeBugSubscriptionFilter(self, target=None, subscriber=None,
-                                  subscribed_by=None):
+    def makeBugSubscriptionFilter(
+        self, target=None, subscriber=None, subscribed_by=None
+    ):
         """Create and return a new bug subscription filter.
 
         :param target: An `IStructuralSubscriptionTarget`.  Defaults to a
@@ -2260,11 +2622,20 @@ class LaunchpadObjectFactory(ObjectFactory):
         if subscribed_by is None:
             subscribed_by = subscriber
         return removeSecurityProxy(target).addBugSubscriptionFilter(
-            subscriber, subscribed_by)
+            subscriber, subscribed_by
+        )
 
-    def makeSignedMessage(self, msgid=None, body=None, subject=None,
-            attachment_contents=None, force_transfer_encoding=False,
-            email_address=None, signing_context=None, to_address=None):
+    def makeSignedMessage(
+        self,
+        msgid=None,
+        body=None,
+        subject=None,
+        attachment_contents=None,
+        force_transfer_encoding=False,
+        email_address=None,
+        signing_context=None,
+        to_address=None,
+    ):
         """Return an ISignedMessage.
 
         :param msgid: An rfc2822 message-id.
@@ -2281,31 +2652,35 @@ class LaunchpadObjectFactory(ObjectFactory):
         if email_address is None:
             person = self.makePerson()
             email_address = removeSecurityProxy(person).preferredemail.email
-        mail['From'] = email_address
+        mail["From"] = email_address
         if to_address is None:
             to_address = removeSecurityProxy(
-                self.makePerson()).preferredemail.email
-        mail['To'] = to_address
+                self.makePerson()
+            ).preferredemail.email
+        mail["To"] = to_address
         if subject is None:
-            subject = self.getUniqueString('subject')
-        mail['Subject'] = subject
+            subject = self.getUniqueString("subject")
+        mail["Subject"] = subject
         if msgid is None:
             msgid = self.makeUniqueRFC822MsgId()
         if body is None:
-            body = self.getUniqueString('body')
-        charset = 'ascii'
+            body = self.getUniqueString("body")
+        charset = "ascii"
         try:
             body = body.encode(charset)
         except UnicodeEncodeError:
-            charset = 'utf-8'
+            charset = "utf-8"
             body = body.encode(charset)
-        mail['Message-Id'] = msgid
-        mail['Date'] = formatdate()
+        mail["Message-Id"] = msgid
+        mail["Date"] = formatdate()
         if signing_context is not None:
             gpghandler = getUtility(IGPGHandler)
             body = gpghandler.signContent(
-                body, signing_context.key, signing_context.password,
-                signing_context.mode)
+                body,
+                signing_context.key,
+                signing_context.password,
+                signing_context.mode,
+            )
             assert body is not None
         if attachment_contents is None:
             mail.set_payload(body)
@@ -2316,25 +2691,38 @@ class LaunchpadObjectFactory(ObjectFactory):
             mail.attach(body_part)
             attach_part = EmailMessage()
             attach_part.set_payload(attachment_contents)
-            attach_part['Content-type'] = 'application/octet-stream'
+            attach_part["Content-type"] = "application/octet-stream"
             if force_transfer_encoding:
                 encode_base64(attach_part)
             mail.attach(attach_part)
-            mail['Content-type'] = 'multipart/mixed'
-        body_part['Content-type'] = 'text/plain'
+            mail["Content-type"] = "multipart/mixed"
+        body_part["Content-type"] = "text/plain"
         if force_transfer_encoding:
             encode_base64(body_part)
         body_part.set_charset(charset)
         mail.parsed_bytes = message_as_bytes(mail)
         return mail
 
-    def makeSpecification(self, product=None, title=None, distribution=None,
-                          name=None, summary=None, owner=None,
-                          status=NewSpecificationDefinitionStatus.NEW,
-                          implementation_status=None, goal=None, specurl=None,
-                          assignee=None, drafter=None, approver=None,
-                          whiteboard=None, milestone=None,
-                          information_type=None, priority=None):
+    def makeSpecification(
+        self,
+        product=None,
+        title=None,
+        distribution=None,
+        name=None,
+        summary=None,
+        owner=None,
+        status=NewSpecificationDefinitionStatus.NEW,
+        implementation_status=None,
+        goal=None,
+        specurl=None,
+        assignee=None,
+        drafter=None,
+        approver=None,
+        whiteboard=None,
+        milestone=None,
+        information_type=None,
+        priority=None,
+    ):
         """Create and return a new, arbitrary Blueprint.
 
         :param product: The product to make the blueprint on.  If one is
@@ -2342,31 +2730,40 @@ class LaunchpadObjectFactory(ObjectFactory):
         """
         if distribution and product:
             raise AssertionError(
-                'Cannot target a Specification to a distribution and '
-                'a product simultaneously.')
-        proprietary = (information_type not in PUBLIC_INFORMATION_TYPES and
-            information_type is not None)
-        if (product is None and milestone is not None and
-            milestone.productseries is not None):
+                "Cannot target a Specification to a distribution and "
+                "a product simultaneously."
+            )
+        proprietary = (
+            information_type not in PUBLIC_INFORMATION_TYPES
+            and information_type is not None
+        )
+        if (
+            product is None
+            and milestone is not None
+            and milestone.productseries is not None
+        ):
             product = milestone.productseries.product
         if distribution is None and product is None:
             if proprietary:
                 if information_type == InformationType.EMBARGOED:
                     specification_sharing_policy = (
-                        SpecificationSharingPolicy.EMBARGOED_OR_PROPRIETARY)
+                        SpecificationSharingPolicy.EMBARGOED_OR_PROPRIETARY
+                    )
                 else:
                     specification_sharing_policy = (
-                        SpecificationSharingPolicy.PUBLIC_OR_PROPRIETARY)
+                        SpecificationSharingPolicy.PUBLIC_OR_PROPRIETARY
+                    )
             else:
                 specification_sharing_policy = None
             product = self.makeProduct(
-                specification_sharing_policy=specification_sharing_policy)
+                specification_sharing_policy=specification_sharing_policy
+            )
         if name is None:
-            name = self.getUniqueString('name')
+            name = self.getUniqueString("name")
         if summary is None:
-            summary = self.getUniqueString('summary')
+            summary = self.getUniqueString("summary")
         if title is None:
-            title = self.getUniqueString('title')
+            title = self.getUniqueString("title")
         if owner is None:
             owner = self.makePerson()
         status_names = NewSpecificationDefinitionStatus.items.mapping.keys()
@@ -2386,15 +2783,18 @@ class LaunchpadObjectFactory(ObjectFactory):
             assignee=assignee,
             drafter=drafter,
             approver=approver,
-            target=product or distribution)
+            target=product or distribution,
+        )
         naked_spec = removeSecurityProxy(spec)
         if priority is not None:
             naked_spec.priority = priority
         if status.name not in status_names:
             # Set the closed status after the status has a sane initial state.
             naked_spec.definition_status = status
-        if status in (SpecificationDefinitionStatus.OBSOLETE,
-                      SpecificationDefinitionStatus.SUPERSEDED):
+        if status in (
+            SpecificationDefinitionStatus.OBSOLETE,
+            SpecificationDefinitionStatus.SUPERSEDED,
+        ):
             # This is to satisfy a DB constraint of obsolete specs.
             naked_spec.completer = owner
             naked_spec.date_completed = datetime.now(pytz.UTC)
@@ -2409,17 +2809,24 @@ class LaunchpadObjectFactory(ObjectFactory):
             if proprietary:
                 naked_spec.target._ensurePolicies([information_type])
             naked_spec.transitionToInformationType(
-                information_type, naked_spec.target.owner)
+                information_type, naked_spec.target.owner
+            )
         return spec
 
     makeBlueprint = makeSpecification
 
-    def makeSpecificationWorkItem(self, title=None, specification=None,
-                                  assignee=None, milestone=None, deleted=False,
-                                  status=SpecificationWorkItemStatus.TODO,
-                                  sequence=None):
+    def makeSpecificationWorkItem(
+        self,
+        title=None,
+        specification=None,
+        assignee=None,
+        milestone=None,
+        deleted=False,
+        status=SpecificationWorkItemStatus.TODO,
+        sequence=None,
+    ):
         if title is None:
-            title = self.getUniqueString('title')
+            title = self.getUniqueString("title")
         if specification is None:
             product = None
             distribution = None
@@ -2427,17 +2834,23 @@ class LaunchpadObjectFactory(ObjectFactory):
                 product = milestone.product
                 distribution = milestone.distribution
             specification = self.makeSpecification(
-                product=product, distribution=distribution)
+                product=product, distribution=distribution
+            )
         if sequence is None:
             sequence = self.getUniqueInteger()
         work_item = removeSecurityProxy(specification).newWorkItem(
-            title=title, sequence=sequence, status=status, assignee=assignee,
-            milestone=milestone)
+            title=title,
+            sequence=sequence,
+            status=status,
+            assignee=assignee,
+            milestone=milestone,
+        )
         work_item.deleted = deleted
         return work_item
 
-    def makeQuestion(self, target=None, title=None,
-                     owner=None, description=None, **kwargs):
+    def makeQuestion(
+        self, target=None, title=None, owner=None, description=None, **kwargs
+    ):
         """Create and return a new, arbitrary Question.
 
         :param target: The IQuestionTarget to make the question on. If one is
@@ -2451,14 +2864,15 @@ class LaunchpadObjectFactory(ObjectFactory):
         if target is None:
             target = self.makeProduct()
         if title is None:
-            title = self.getUniqueUnicode('title')
+            title = self.getUniqueUnicode("title")
         if owner is None:
             owner = target.owner
         if description is None:
-            description = self.getUniqueUnicode('description')
+            description = self.getUniqueUnicode("description")
         with person_logged_in(owner):
             question = target.newQuestion(
-                owner=owner, title=title, description=description, **kwargs)
+                owner=owner, title=title, description=description, **kwargs
+            )
         return question
 
     def makeQuestionSubscription(self, question=None, person=None):
@@ -2481,9 +2895,10 @@ class LaunchpadObjectFactory(ObjectFactory):
         if target is None:
             target = self.makeProduct()
         if title is None:
-            title = self.getUniqueString('title')
+            title = self.getUniqueString("title")
         return target.newFAQ(
-            owner=target.owner, title=title, content='content')
+            owner=target.owner, title=title, content="content"
+        )
 
     def makePackageCodeImport(self, sourcepackage=None, **kwargs):
         """Make a code import targetting a sourcepackage."""
@@ -2497,12 +2912,21 @@ class LaunchpadObjectFactory(ObjectFactory):
             product = self.makeProduct()
         return self.makeCodeImport(context=product, **kwargs)
 
-    def makeCodeImport(self, svn_branch_url=None, cvs_root=None,
-                       cvs_module=None, context=None, branch_name=None,
-                       git_repo_url=None,
-                       bzr_branch_url=None, registrant=None,
-                       rcs_type=None, target_rcs_type=None,
-                       review_status=None, owner=None):
+    def makeCodeImport(
+        self,
+        svn_branch_url=None,
+        cvs_root=None,
+        cvs_module=None,
+        context=None,
+        branch_name=None,
+        git_repo_url=None,
+        bzr_branch_url=None,
+        registrant=None,
+        rcs_type=None,
+        target_rcs_type=None,
+        review_status=None,
+        owner=None,
+    ):
         """Create and return a new, arbitrary code import.
 
         The type of code import will be inferred from the source details
@@ -2512,8 +2936,14 @@ class LaunchpadObjectFactory(ObjectFactory):
         """
         if target_rcs_type is None:
             target_rcs_type = TargetRevisionControlSystems.BZR
-        if (svn_branch_url is cvs_root is cvs_module is git_repo_url is
-            bzr_branch_url is None):
+        if (
+            svn_branch_url
+            is cvs_root
+            is cvs_module
+            is git_repo_url
+            is bzr_branch_url
+            is None
+        ):
             if target_rcs_type == TargetRevisionControlSystems.BZR:
                 svn_branch_url = self.getUniqueURL()
             else:
@@ -2522,7 +2952,7 @@ class LaunchpadObjectFactory(ObjectFactory):
         if context is None:
             context = self.makeProduct()
         if branch_name is None:
-            branch_name = self.getUniqueUnicode('name')
+            branch_name = self.getUniqueUnicode("name")
         if registrant is None:
             registrant = self.makePerson()
 
@@ -2530,31 +2960,51 @@ class LaunchpadObjectFactory(ObjectFactory):
         if svn_branch_url is not None:
             assert rcs_type in (None, RevisionControlSystems.BZR_SVN)
             return code_import_set.new(
-                registrant, context, branch_name,
+                registrant,
+                context,
+                branch_name,
                 rcs_type=RevisionControlSystems.BZR_SVN,
                 target_rcs_type=target_rcs_type,
-                url=svn_branch_url, review_status=review_status, owner=owner)
+                url=svn_branch_url,
+                review_status=review_status,
+                owner=owner,
+            )
         elif git_repo_url is not None:
             assert rcs_type in (None, RevisionControlSystems.GIT)
             return code_import_set.new(
-                registrant, context, branch_name,
+                registrant,
+                context,
+                branch_name,
                 rcs_type=RevisionControlSystems.GIT,
                 target_rcs_type=target_rcs_type,
-                url=git_repo_url, review_status=review_status, owner=owner)
+                url=git_repo_url,
+                review_status=review_status,
+                owner=owner,
+            )
         elif bzr_branch_url is not None:
             return code_import_set.new(
-                registrant, context, branch_name,
+                registrant,
+                context,
+                branch_name,
                 rcs_type=RevisionControlSystems.BZR,
                 target_rcs_type=target_rcs_type,
-                url=bzr_branch_url, review_status=review_status, owner=owner)
+                url=bzr_branch_url,
+                review_status=review_status,
+                owner=owner,
+            )
         else:
             assert rcs_type in (None, RevisionControlSystems.CVS)
             return code_import_set.new(
-                registrant, context, branch_name,
+                registrant,
+                context,
+                branch_name,
                 rcs_type=RevisionControlSystems.CVS,
                 target_rcs_type=target_rcs_type,
-                cvs_root=cvs_root, cvs_module=cvs_module,
-                review_status=review_status, owner=owner)
+                cvs_root=cvs_root,
+                cvs_module=cvs_module,
+                review_status=review_status,
+                owner=owner,
+            )
 
     def makeChangelog(self, spn=None, versions=[]):
         """Create and return a LFA of a valid Debian-style changelog.
@@ -2565,16 +3015,19 @@ class LaunchpadObjectFactory(ObjectFactory):
         """
         if spn is None:
             spn = self.getUniqueString()
-        changelog = ''
+        changelog = ""
         for version in versions:
-            entry = dedent('''\
+            entry = dedent(
+                """\
             %s (%s) unstable; urgency=low
 
               * %s.
 
              -- Føo Bær <foo@xxxxxxxxxxx>  Tue, 01 Jan 1970 01:50:41 +0000
 
-            ''' % (spn, version, version))
+            """
+                % (spn, version, version)
+            )
             changelog += entry
         return self.makeLibraryFileAlias(content=changelog.encode("utf-8"))
 
@@ -2594,8 +3047,9 @@ class LaunchpadObjectFactory(ObjectFactory):
         if code_import is None:
             code_import = self.makeCodeImport()
         code_import.updateFromData(
-            {'review_status': CodeImportReviewStatus.REVIEWED},
-            code_import.registrant)
+            {"review_status": CodeImportReviewStatus.REVIEWED},
+            code_import.registrant,
+        )
         return code_import.import_job
 
     def makeCodeImportMachine(self, set_online=False, hostname=None):
@@ -2603,7 +3057,7 @@ class LaunchpadObjectFactory(ObjectFactory):
 
         The machine will be in the OFFLINE state."""
         if hostname is None:
-            hostname = self.getUniqueUnicode('machine-')
+            hostname = self.getUniqueUnicode("machine-")
         if set_online:
             state = CodeImportMachineState.ONLINE
         else:
@@ -2611,10 +3065,17 @@ class LaunchpadObjectFactory(ObjectFactory):
         machine = getUtility(ICodeImportMachineSet).new(hostname, state)
         return machine
 
-    def makeCodeImportResult(self, code_import=None, result_status=None,
-                             date_started=None, date_finished=None,
-                             log_excerpt=None, log_alias=None, machine=None,
-                             requesting_user=None):
+    def makeCodeImportResult(
+        self,
+        code_import=None,
+        result_status=None,
+        date_started=None,
+        date_finished=None,
+        log_excerpt=None,
+        log_alias=None,
+        machine=None,
+        requesting_user=None,
+    ):
         """Create and return a new CodeImportResult."""
         if code_import is None:
             code_import = self.makeCodeImport()
@@ -2636,32 +3097,55 @@ class LaunchpadObjectFactory(ObjectFactory):
         if log_alias is None:
             log_alias = self.makeLibraryFileAlias()
         return getUtility(ICodeImportResultSet).new(
-            code_import, machine, requesting_user, log_excerpt, log_alias,
-            result_status, date_started, date_finished)
+            code_import,
+            machine,
+            requesting_user,
+            log_excerpt,
+            log_alias,
+            result_status,
+            date_started,
+            date_finished,
+        )
 
-    def makeCodeReviewComment(self, sender=None, subject=None, body=None,
-                              vote=None, vote_tag=None, parent=None,
-                              merge_proposal=None, date_created=DEFAULT,
-                              git=False):
+    def makeCodeReviewComment(
+        self,
+        sender=None,
+        subject=None,
+        body=None,
+        vote=None,
+        vote_tag=None,
+        parent=None,
+        merge_proposal=None,
+        date_created=DEFAULT,
+        git=False,
+    ):
         if sender is None:
             sender = self.makePerson()
         if subject is None:
-            subject = self.getUniqueString('subject')
+            subject = self.getUniqueString("subject")
         if body is None:
-            body = self.getUniqueString('content')
+            body = self.getUniqueString("content")
         if merge_proposal is None:
             if parent:
                 merge_proposal = parent.branch_merge_proposal
             elif git:
                 merge_proposal = self.makeBranchMergeProposalForGit(
-                    registrant=sender)
+                    registrant=sender
+                )
             else:
                 merge_proposal = self.makeBranchMergeProposal(
-                    registrant=sender)
+                    registrant=sender
+                )
         with person_logged_in(sender):
             return merge_proposal.createComment(
-                sender, subject, body, vote, vote_tag, parent,
-                _date_created=date_created)
+                sender,
+                subject,
+                body,
+                vote,
+                vote_tag,
+                parent,
+                _date_created=date_created,
+            )
 
     def makeCodeReviewVoteReference(self, git=False):
         if git:
@@ -2671,8 +3155,14 @@ class LaunchpadObjectFactory(ObjectFactory):
         candidate = self.makePerson()
         return bmp.nominateReviewer(candidate, bmp.registrant)
 
-    def makeMessage(self, subject=None, content=None, parent=None,
-                    owner=None, datecreated=None):
+    def makeMessage(
+        self,
+        subject=None,
+        content=None,
+        parent=None,
+        owner=None,
+        datecreated=None,
+    ):
         if subject is None:
             subject = self.getUniqueString()
         if content is None:
@@ -2682,27 +3172,40 @@ class LaunchpadObjectFactory(ObjectFactory):
         if datecreated is None:
             datecreated = datetime.now(UTC)
         rfc822msgid = self.makeUniqueRFC822MsgId()
-        message = Message(rfc822msgid=rfc822msgid, subject=subject,
-            owner=owner, parent=parent, datecreated=datecreated)
+        message = Message(
+            rfc822msgid=rfc822msgid,
+            subject=subject,
+            owner=owner,
+            parent=parent,
+            datecreated=datecreated,
+        )
         MessageChunk(message=message, sequence=1, content=content)
         return message
 
-    def makeLanguage(self, language_code=None, name=None, pluralforms=None,
-                     plural_expression=None):
+    def makeLanguage(
+        self,
+        language_code=None,
+        name=None,
+        pluralforms=None,
+        plural_expression=None,
+    ):
         """Makes a language given the language_code and name."""
         if language_code is None:
-            language_code = self.getUniqueString('lang')
+            language_code = self.getUniqueString("lang")
         if name is None:
             name = "Language %s" % language_code
         if plural_expression is None and pluralforms is not None:
             # If the number of plural forms is known, the language
             # should also have a plural expression and vice versa.
-            plural_expression = 'n %% %d' % pluralforms
+            plural_expression = "n %% %d" % pluralforms
 
         language_set = getUtility(ILanguageSet)
         return language_set.createLanguage(
-            language_code, name, pluralforms=pluralforms,
-            pluralexpression=plural_expression)
+            language_code,
+            name,
+            pluralforms=pluralforms,
+            pluralexpression=plural_expression,
+        )
 
     def makeLanguagePack(self, distroseries=None, languagepack_type=None):
         """Create a language pack."""
@@ -2711,14 +3214,21 @@ class LaunchpadObjectFactory(ObjectFactory):
         if languagepack_type is None:
             languagepack_type = LanguagePackType.FULL
         return getUtility(ILanguagePackSet).addLanguagePack(
-            distroseries, self.makeLibraryFileAlias(), languagepack_type)
+            distroseries, self.makeLibraryFileAlias(), languagepack_type
+        )
 
-    def makeLibraryFileAlias(self, filename=None, content=None,
-                             content_type='text/plain', restricted=False,
-                             expires=None, db_only=False):
+    def makeLibraryFileAlias(
+        self,
+        filename=None,
+        content=None,
+        content_type="text/plain",
+        restricted=False,
+        expires=None,
+        db_only=False,
+    ):
         """Make a library file, and return the alias."""
         if filename is None:
-            filename = self.getUniqueString('filename')
+            filename = self.getUniqueString("filename")
         if content is None:
             content = self.getUniqueBytes()
         else:
@@ -2731,25 +3241,46 @@ class LaunchpadObjectFactory(ObjectFactory):
                 filesize=len(content),
                 sha256=hashlib.sha256(content).hexdigest(),
                 sha1=hashlib.sha1(content).hexdigest(),
-                md5=hashlib.md5(content).hexdigest())
+                md5=hashlib.md5(content).hexdigest(),
+            )
             lfa = LibraryFileAlias(
-                content=lfc, filename=filename, mimetype=content_type)
+                content=lfc, filename=filename, mimetype=content_type
+            )
         else:
             lfa = getUtility(ILibraryFileAliasSet).create(
-                filename, len(content), BytesIO(content), content_type,
-                expires=expires, restricted=restricted)
+                filename,
+                len(content),
+                BytesIO(content),
+                content_type,
+                expires=expires,
+                restricted=restricted,
+            )
         return lfa
 
-    def makeDistribution(self, name=None, displayname=None, owner=None,
-                         registrant=None, members=None, title=None,
-                         aliases=None, bug_supervisor=None, driver=None,
-                         publish_root_dir=None, publish_base_url=None,
-                         publish_copy_base_url=None, no_pubconf=False,
-                         icon=None, summary=None, vcs=None,
-                         oci_project_admin=None, bug_sharing_policy=None,
-                         branch_sharing_policy=None,
-                         specification_sharing_policy=None,
-                         information_type=None):
+    def makeDistribution(
+        self,
+        name=None,
+        displayname=None,
+        owner=None,
+        registrant=None,
+        members=None,
+        title=None,
+        aliases=None,
+        bug_supervisor=None,
+        driver=None,
+        publish_root_dir=None,
+        publish_base_url=None,
+        publish_copy_base_url=None,
+        no_pubconf=False,
+        icon=None,
+        summary=None,
+        vcs=None,
+        oci_project_admin=None,
+        bug_sharing_policy=None,
+        branch_sharing_policy=None,
+        specification_sharing_policy=None,
+        information_type=None,
+    ):
         """Make a new distribution."""
         if name is None:
             name = self.getUniqueString(prefix="distribution")
@@ -2768,9 +3299,19 @@ class LaunchpadObjectFactory(ObjectFactory):
         if members is None:
             members = self.makeTeam(owner)
         distro = getUtility(IDistributionSet).new(
-            name, displayname, title, description, summary, domainname,
-            members, owner, registrant, icon=icon, vcs=vcs,
-            information_type=information_type)
+            name,
+            displayname,
+            title,
+            description,
+            summary,
+            domainname,
+            members,
+            owner,
+            registrant,
+            icon=icon,
+            vcs=vcs,
+            information_type=information_type,
+        )
         naked_distro = removeSecurityProxy(distro)
         if aliases is not None:
             naked_distro.setAliases(aliases)
@@ -2785,13 +3326,21 @@ class LaunchpadObjectFactory(ObjectFactory):
         # complimentary commercial subscription.  However, Distribution
         # doesn't have a licenses field, so deal with the commercial
         # subscription directly here instead.
-        if ((bug_sharing_policy is not None and
-             bug_sharing_policy != BugSharingPolicy.PUBLIC) or
-            (branch_sharing_policy is not None and
-             branch_sharing_policy != BranchSharingPolicy.PUBLIC) or
-            (specification_sharing_policy is not None and
-             specification_sharing_policy !=
-             SpecificationSharingPolicy.PUBLIC)):
+        if (
+            (
+                bug_sharing_policy is not None
+                and bug_sharing_policy != BugSharingPolicy.PUBLIC
+            )
+            or (
+                branch_sharing_policy is not None
+                and branch_sharing_policy != BranchSharingPolicy.PUBLIC
+            )
+            or (
+                specification_sharing_policy is not None
+                and specification_sharing_policy
+                != SpecificationSharingPolicy.PUBLIC
+            )
+        ):
             naked_distro._ensure_complimentary_subscription()
         if branch_sharing_policy:
             naked_distro.setBranchSharingPolicy(branch_sharing_policy)
@@ -2799,17 +3348,27 @@ class LaunchpadObjectFactory(ObjectFactory):
             naked_distro.setBugSharingPolicy(bug_sharing_policy)
         if specification_sharing_policy:
             naked_distro.setSpecificationSharingPolicy(
-                specification_sharing_policy)
+                specification_sharing_policy
+            )
         if not no_pubconf:
             self.makePublisherConfig(
-                distro, publish_root_dir, publish_base_url,
-                publish_copy_base_url)
+                distro,
+                publish_root_dir,
+                publish_base_url,
+                publish_copy_base_url,
+            )
         return distro
 
-    def makeDistroSeries(self, distribution=None, version=None,
-                         status=SeriesStatus.DEVELOPMENT,
-                         previous_series=None, name=None, displayname=None,
-                         registrant=None):
+    def makeDistroSeries(
+        self,
+        distribution=None,
+        version=None,
+        status=SeriesStatus.DEVELOPMENT,
+        previous_series=None,
+        name=None,
+        displayname=None,
+        registrant=None,
+    ):
         """Make a new `DistroSeries`."""
         if distribution is None:
             distribution = self.makeDistribution()
@@ -2829,95 +3388,120 @@ class LaunchpadObjectFactory(ObjectFactory):
             version=version,
             name=name,
             display_name=displayname,
-            title=self.getUniqueString(), summary=self.getUniqueString(),
+            title=self.getUniqueString(),
+            summary=self.getUniqueString(),
             description=self.getUniqueString(),
-            previous_series=previous_series, registrant=registrant)
+            previous_series=previous_series,
+            registrant=registrant,
+        )
         series.status = status
 
         return ProxyFactory(series)
 
-    def makeUbuntuDistroSeries(self, version=None,
-                               status=SeriesStatus.DEVELOPMENT,
-                               previous_series=None, name=None,
-                               displayname=None):
+    def makeUbuntuDistroSeries(
+        self,
+        version=None,
+        status=SeriesStatus.DEVELOPMENT,
+        previous_series=None,
+        name=None,
+        displayname=None,
+    ):
         """Short cut to use the celebrity 'ubuntu' as the distribution."""
         ubuntu = getUtility(ILaunchpadCelebrities).ubuntu
         return self.makeDistroSeries(
-            ubuntu, version, status, previous_series, name, displayname)
+            ubuntu, version, status, previous_series, name, displayname
+        )
 
     def makeDistroSeriesDifference(
-        self, derived_series=None, source_package_name_str=None,
+        self,
+        derived_series=None,
+        source_package_name_str=None,
         versions=None,
         difference_type=DistroSeriesDifferenceType.DIFFERENT_VERSIONS,
         status=DistroSeriesDifferenceStatus.NEEDS_ATTENTION,
-        changelogs=None, set_base_version=False, parent_series=None):
+        changelogs=None,
+        set_base_version=False,
+        parent_series=None,
+    ):
         """Create a new distro series source package difference."""
         if derived_series is None:
-            dsp = self.makeDistroSeriesParent(
-                parent_series=parent_series)
+            dsp = self.makeDistroSeriesParent(parent_series=parent_series)
             derived_series = dsp.derived_series
             parent_series = dsp.parent_series
         else:
             if parent_series is None:
                 dsp = getUtility(IDistroSeriesParentSet).getByDerivedSeries(
-                    derived_series)
+                    derived_series
+                )
                 if dsp.is_empty():
                     new_dsp = self.makeDistroSeriesParent(
                         derived_series=derived_series,
-                        parent_series=parent_series)
+                        parent_series=parent_series,
+                    )
                     parent_series = new_dsp.parent_series
                 else:
                     parent_series = dsp[0].parent_series
 
         if source_package_name_str is None:
-            source_package_name_str = self.getUniqueString('src-name')
+            source_package_name_str = self.getUniqueString("src-name")
 
         source_package_name = self.getOrMakeSourcePackageName(
-            source_package_name_str)
+            source_package_name_str
+        )
 
         if versions is None:
             versions = {}
         if changelogs is None:
             changelogs = {}
 
-        base_version = versions.get('base')
+        base_version = versions.get("base")
         if base_version is not None:
             for series in [derived_series, parent_series]:
                 spr = self.makeSourcePackageRelease(
-                    sourcepackagename=source_package_name,
-                    version=base_version)
+                    sourcepackagename=source_package_name, version=base_version
+                )
                 self.makeSourcePackagePublishingHistory(
-                    distroseries=series, sourcepackagerelease=spr,
-                    status=PackagePublishingStatus.SUPERSEDED)
+                    distroseries=series,
+                    sourcepackagerelease=spr,
+                    status=PackagePublishingStatus.SUPERSEDED,
+                )
 
         if difference_type is not (
-            DistroSeriesDifferenceType.MISSING_FROM_DERIVED_SERIES):
+            DistroSeriesDifferenceType.MISSING_FROM_DERIVED_SERIES
+        ):
             spr = self.makeSourcePackageRelease(
                 sourcepackagename=source_package_name,
-                version=versions.get('derived'),
-                changelog=changelogs.get('derived'))
+                version=versions.get("derived"),
+                changelog=changelogs.get("derived"),
+            )
             self.makeSourcePackagePublishingHistory(
-                distroseries=derived_series, sourcepackagerelease=spr,
-                status=PackagePublishingStatus.PUBLISHED)
+                distroseries=derived_series,
+                sourcepackagerelease=spr,
+                status=PackagePublishingStatus.PUBLISHED,
+            )
 
         if difference_type is not (
-            DistroSeriesDifferenceType.UNIQUE_TO_DERIVED_SERIES):
+            DistroSeriesDifferenceType.UNIQUE_TO_DERIVED_SERIES
+        ):
             spr = self.makeSourcePackageRelease(
                 sourcepackagename=source_package_name,
-                version=versions.get('parent'),
-                changelog=changelogs.get('parent'))
+                version=versions.get("parent"),
+                changelog=changelogs.get("parent"),
+            )
             self.makeSourcePackagePublishingHistory(
                 distroseries=parent_series,
                 sourcepackagerelease=spr,
-                status=PackagePublishingStatus.PUBLISHED)
+                status=PackagePublishingStatus.PUBLISHED,
+            )
 
         diff = getUtility(IDistroSeriesDifferenceSource).new(
-            derived_series, source_package_name, parent_series)
+            derived_series, source_package_name, parent_series
+        )
 
         removeSecurityProxy(diff).status = status
 
         if set_base_version:
-            version = versions.get('base', "%s.0" % self.getUniqueInteger())
+            version = versions.get("base", "%s.0" % self.getUniqueInteger())
             removeSecurityProxy(diff).base_version = version
 
         # We clear the cache on the diff, returning the object as if it
@@ -2926,33 +3510,53 @@ class LaunchpadObjectFactory(ObjectFactory):
         return diff
 
     def makeDistroSeriesDifferenceComment(
-        self, distro_series_difference=None, owner=None, comment=None):
+        self, distro_series_difference=None, owner=None, comment=None
+    ):
         """Create a new distro series difference comment."""
         if distro_series_difference is None:
             distro_series_difference = self.makeDistroSeriesDifference()
         if owner is None:
             owner = self.makePerson()
         if comment is None:
-            comment = self.getUniqueString('dsdcomment')
+            comment = self.getUniqueString("dsdcomment")
 
         return getUtility(IDistroSeriesDifferenceCommentSource).new(
-            distro_series_difference, owner, comment)
+            distro_series_difference, owner, comment
+        )
 
-    def makeDistroSeriesParent(self, derived_series=None, parent_series=None,
-                               initialized=False, is_overlay=False,
-                               inherit_overrides=False, pocket=None,
-                               component=None):
+    def makeDistroSeriesParent(
+        self,
+        derived_series=None,
+        parent_series=None,
+        initialized=False,
+        is_overlay=False,
+        inherit_overrides=False,
+        pocket=None,
+        component=None,
+    ):
         if parent_series is None:
             parent_series = self.makeDistroSeries()
         if derived_series is None:
             derived_series = self.makeDistroSeries()
         return getUtility(IDistroSeriesParentSet).new(
-            derived_series, parent_series, initialized, is_overlay,
-            inherit_overrides, pocket, component)
+            derived_series,
+            parent_series,
+            initialized,
+            is_overlay,
+            inherit_overrides,
+            pocket,
+            component,
+        )
 
-    def makeDistroArchSeries(self, distroseries=None,
-                             architecturetag=None, processor=None,
-                             official=True, owner=None, enabled=True):
+    def makeDistroArchSeries(
+        self,
+        distroseries=None,
+        architecturetag=None,
+        processor=None,
+        official=True,
+        owner=None,
+        enabled=True,
+    ):
         """Create a new distroarchseries"""
 
         if distroseries is None:
@@ -2965,35 +3569,45 @@ class LaunchpadObjectFactory(ObjectFactory):
         # wrong to just make a fresh architecture tag without also making a
         # processor to go with it.
         if architecturetag is None:
-            architecturetag = self.getUniqueString('arch')
+            architecturetag = self.getUniqueString("arch")
         return distroseries.newArch(
-            architecturetag, processor, official, owner, enabled)
+            architecturetag, processor, official, owner, enabled
+        )
 
-    def makeBuildableDistroArchSeries(self, architecturetag=None,
-                                      processor=None,
-                                      supports_virtualized=True,
-                                      supports_nonvirtualized=True, **kwargs):
+    def makeBuildableDistroArchSeries(
+        self,
+        architecturetag=None,
+        processor=None,
+        supports_virtualized=True,
+        supports_nonvirtualized=True,
+        **kwargs
+    ):
         if architecturetag is None:
             architecturetag = self.getUniqueUnicode("arch")
         if processor is None:
             try:
                 processor = getUtility(IProcessorSet).getByName(
-                    architecturetag)
+                    architecturetag
+                )
             except ProcessorNotFound:
                 processor = self.makeProcessor(
                     name=architecturetag,
                     supports_virtualized=supports_virtualized,
-                    supports_nonvirtualized=supports_nonvirtualized)
+                    supports_nonvirtualized=supports_nonvirtualized,
+                )
         das = self.makeDistroArchSeries(
-            architecturetag=architecturetag, processor=processor, **kwargs)
+            architecturetag=architecturetag, processor=processor, **kwargs
+        )
         # Add both a chroot and a LXD image to test that
         # getAllowedArchitectures doesn't get confused by multiple
         # PocketChroot rows for a single DistroArchSeries.
         fake_chroot = self.makeLibraryFileAlias(
-            filename="fake_chroot.tar.gz", db_only=True)
+            filename="fake_chroot.tar.gz", db_only=True
+        )
         das.addOrUpdateChroot(fake_chroot)
         fake_lxd = self.makeLibraryFileAlias(
-            filename="fake_lxd.tar.gz", db_only=True)
+            filename="fake_lxd.tar.gz", db_only=True
+        )
         das.addOrUpdateChroot(fake_lxd, image_type=BuildBaseImageType.LXD)
         return das
 
@@ -3018,17 +3632,27 @@ class LaunchpadObjectFactory(ObjectFactory):
             component = self.makeComponent(component)
 
         selection = ComponentSelection(
-            distroseries=distroseries, component=component)
+            distroseries=distroseries, component=component
+        )
         del get_property_cache(distroseries).components
         return selection
 
-    def makeArchive(self, distribution=None, owner=None, name=None,
-                    purpose=None, enabled=True, private=False,
-                    virtualized=True, description=None, displayname=None,
-                    suppress_subscription_notifications=False,
-                    processors=None,
-                    publishing_method=ArchivePublishingMethod.LOCAL,
-                    repository_format=ArchiveRepositoryFormat.DEBIAN):
+    def makeArchive(
+        self,
+        distribution=None,
+        owner=None,
+        name=None,
+        purpose=None,
+        enabled=True,
+        private=False,
+        virtualized=True,
+        description=None,
+        displayname=None,
+        suppress_subscription_notifications=False,
+        processors=None,
+        publishing_method=ArchivePublishingMethod.LOCAL,
+        repository_format=ArchiveRepositoryFormat.DEBIAN,
+    ):
         """Create and return a new arbitrary archive.
 
         :param distribution: Supply IDistribution, defaults to a new one
@@ -3076,12 +3700,18 @@ class LaunchpadObjectFactory(ObjectFactory):
         admins = getUtility(ILaunchpadCelebrities).admin
         with person_logged_in(admins.teamowner):
             archive = getUtility(IArchiveSet).new(
-                owner=owner, purpose=purpose,
-                distribution=distribution, name=name, displayname=displayname,
-                enabled=enabled, require_virtualized=virtualized,
-                description=description, processors=processors,
+                owner=owner,
+                purpose=purpose,
+                distribution=distribution,
+                name=name,
+                displayname=displayname,
+                enabled=enabled,
+                require_virtualized=virtualized,
+                description=description,
+                processors=processors,
                 publishing_method=publishing_method,
-                repository_format=repository_format)
+                repository_format=repository_format,
+            )
 
         if private:
             naked_archive = removeSecurityProxy(archive)
@@ -3106,11 +3736,17 @@ class LaunchpadObjectFactory(ObjectFactory):
 
         person = self.makePerson()
         permission_set = getUtility(IArchivePermissionSet)
-        permission_set.newQueueAdmin(archive, person, 'main')
+        permission_set.newQueueAdmin(archive, person, "main")
         return person
 
-    def makeArchiveFile(self, archive=None, container=None, path=None,
-                        library_file=None, scheduled_deletion_date=None):
+    def makeArchiveFile(
+        self,
+        archive=None,
+        container=None,
+        path=None,
+        library_file=None,
+        scheduled_deletion_date=None,
+    ):
         if archive is None:
             archive = self.makeArchive()
         if container is None:
@@ -3120,16 +3756,30 @@ class LaunchpadObjectFactory(ObjectFactory):
         if library_file is None:
             library_file = self.makeLibraryFileAlias()
         archive_file = getUtility(IArchiveFileSet).new(
-            archive=archive, container=container, path=path,
-            library_file=library_file)
+            archive=archive,
+            container=container,
+            path=path,
+            library_file=library_file,
+        )
         if scheduled_deletion_date is not None:
-            removeSecurityProxy(archive_file).scheduled_deletion_date = (
-                scheduled_deletion_date)
+            removeSecurityProxy(
+                archive_file
+            ).scheduled_deletion_date = scheduled_deletion_date
         return archive_file
 
-    def makeBuilder(self, processors=None, url=None, name=None, title=None,
-                    owner=None, active=True, virtualized=True, vm_host=None,
-                    vm_reset_protocol=None, manual=False):
+    def makeBuilder(
+        self,
+        processors=None,
+        url=None,
+        name=None,
+        title=None,
+        owner=None,
+        active=True,
+        virtualized=True,
+        vm_host=None,
+        vm_reset_protocol=None,
+        manual=False,
+    ):
         """Make a new builder for i386 virtualized builds by default.
 
         Note: the builder returned will not be able to actually build -
@@ -3137,13 +3787,13 @@ class LaunchpadObjectFactory(ObjectFactory):
         test environment.
         """
         if processors is None:
-            processors = [getUtility(IProcessorSet).getByName('386')]
+            processors = [getUtility(IProcessorSet).getByName("386")]
         if url is None:
-            url = 'http://%s:8221/' % self.getUniqueUnicode()
+            url = "http://%s:8221/"; % self.getUniqueUnicode()
         if name is None:
-            name = self.getUniqueUnicode('builder-name')
+            name = self.getUniqueUnicode("builder-name")
         if title is None:
-            title = self.getUniqueUnicode('builder-title')
+            title = self.getUniqueUnicode("builder-title")
         if owner is None:
             owner = self.makePerson()
         if virtualized and vm_reset_protocol is None:
@@ -3151,13 +3801,21 @@ class LaunchpadObjectFactory(ObjectFactory):
 
         with admin_logged_in():
             return getUtility(IBuilderSet).new(
-                processors, url, name, title, owner, active,
-                virtualized, vm_host, manual=manual,
-                vm_reset_protocol=vm_reset_protocol)
+                processors,
+                url,
+                name,
+                title,
+                owner,
+                active,
+                virtualized,
+                vm_host,
+                manual=manual,
+                vm_reset_protocol=vm_reset_protocol,
+            )
 
     def makeRecipeText(self, *branches):
         if len(branches) == 0:
-            branches = (self.makeAnyBranch(), )
+            branches = (self.makeAnyBranch(),)
         base_branch = branches[0]
         other_branches = branches[1:]
         if IBranch.providedBy(base_branch):
@@ -3168,19 +3826,27 @@ class LaunchpadObjectFactory(ObjectFactory):
             # which is equivalent to the repository's default branch.  This
             # makes that mode easier to test.
             text = "%s\n%s\n" % (
-                MINIMAL_RECIPE_TEXT_GIT.splitlines()[0], base_branch.identity)
+                MINIMAL_RECIPE_TEXT_GIT.splitlines()[0],
+                base_branch.identity,
+            )
         elif IGitRef.providedBy(base_branch):
             text = MINIMAL_RECIPE_TEXT_GIT % (
-                base_branch.repository.identity, base_branch.name)
+                base_branch.repository.identity,
+                base_branch.name,
+            )
         else:
             raise AssertionError(
-                "Unsupported base_branch: %r" % (base_branch,))
+                "Unsupported base_branch: %r" % (base_branch,)
+            )
         for i, branch in enumerate(other_branches):
             if IBranch.providedBy(branch) or IGitRepository.providedBy(branch):
-                text += 'merge dummy-%s %s\n' % (i, branch.identity)
+                text += "merge dummy-%s %s\n" % (i, branch.identity)
             elif IGitRef.providedBy(branch):
-                text += 'merge dummy-%s %s %s\n' % (
-                    i, branch.repository.identity, branch.name)
+                text += "merge dummy-%s %s %s\n" % (
+                    i,
+                    branch.repository.identity,
+                    branch.name,
+                )
             else:
                 raise AssertionError("Unsupported branch: %r" % (branch,))
         return text
@@ -3192,6 +3858,7 @@ class LaunchpadObjectFactory(ObjectFactory):
         arbitrary branch.
         """
         from breezy.plugins.builder.recipe import RecipeParser
+
         parser = RecipeParser(self.makeRecipeText(*branches))
         return parser.parse()
 
@@ -3201,15 +3868,23 @@ class LaunchpadObjectFactory(ObjectFactory):
         Ew.  This uses sampledata currently, which is the ONLY reason this
         method exists: it gives us a migration path away from sampledata.
         """
-        ubuntu = getUtility(IDistributionSet).getByName('ubuntu')
+        ubuntu = getUtility(IDistributionSet).getByName("ubuntu")
         return ubuntu.getSeries(name)
 
-    def makeSourcePackageRecipe(self, registrant=None, owner=None,
-                                distroseries=None, name=None,
-                                description=None, branches=(),
-                                build_daily=False, daily_build_archive=None,
-                                is_stale=None, recipe=None,
-                                date_created=DEFAULT):
+    def makeSourcePackageRecipe(
+        self,
+        registrant=None,
+        owner=None,
+        distroseries=None,
+        name=None,
+        description=None,
+        branches=(),
+        build_daily=False,
+        daily_build_archive=None,
+        is_stale=None,
+        recipe=None,
+        date_created=DEFAULT,
+    ):
         """Make a `SourcePackageRecipe`."""
         if registrant is None:
             registrant = self.makePerson()
@@ -3219,30 +3894,46 @@ class LaunchpadObjectFactory(ObjectFactory):
             distroseries = self.makeSourcePackageRecipeDistroseries()
 
         if name is None:
-            name = self.getUniqueUnicode('spr-name')
+            name = self.getUniqueUnicode("spr-name")
         if description is None:
-            description = self.getUniqueUnicode('spr-description')
+            description = self.getUniqueUnicode("spr-description")
         if daily_build_archive is None:
             daily_build_archive = self.makeArchive(
-                distribution=distroseries.distribution, owner=owner)
+                distribution=distroseries.distribution, owner=owner
+            )
         if recipe is None:
             recipe = self.makeRecipeText(*branches)
         else:
             assert branches == ()
         source_package_recipe = getUtility(ISourcePackageRecipeSource).new(
-            registrant, owner, name, recipe, description, [distroseries],
-            daily_build_archive, build_daily, date_created)
+            registrant,
+            owner,
+            name,
+            recipe,
+            description,
+            [distroseries],
+            daily_build_archive,
+            build_daily,
+            date_created,
+        )
         if is_stale is not None:
             removeSecurityProxy(source_package_recipe).is_stale = is_stale
         IStore(source_package_recipe).flush()
         return source_package_recipe
 
-    def makeSourcePackageRecipeBuild(self, sourcepackage=None, recipe=None,
-                                     requester=None, archive=None,
-                                     sourcename=None, distroseries=None,
-                                     pocket=None, date_created=None,
-                                     status=BuildStatus.NEEDSBUILD,
-                                     duration=None):
+    def makeSourcePackageRecipeBuild(
+        self,
+        sourcepackage=None,
+        recipe=None,
+        requester=None,
+        archive=None,
+        sourcename=None,
+        distroseries=None,
+        pocket=None,
+        date_created=None,
+        status=BuildStatus.NEEDSBUILD,
+        duration=None,
+    ):
         """Make a new SourcePackageRecipeBuild."""
         if recipe is None:
             recipe = self.makeSourcePackageRecipe(name=sourcename)
@@ -3250,7 +3941,8 @@ class LaunchpadObjectFactory(ObjectFactory):
             archive = self.makeArchive()
         if distroseries is None:
             distroseries = self.makeDistroSeries(
-                distribution=archive.distribution)
+                distribution=archive.distribution
+            )
             arch = self.makeDistroArchSeries(distroseries=distroseries)
             removeSecurityProxy(distroseries).nominatedarchindep = arch
         if requester is None:
@@ -3261,12 +3953,15 @@ class LaunchpadObjectFactory(ObjectFactory):
             archive=archive,
             requester=requester,
             pocket=pocket,
-            date_created=date_created)
+            date_created=date_created,
+        )
         if duration is not None:
             removeSecurityProxy(spr_build).updateStatus(
-                BuildStatus.BUILDING, date_started=spr_build.date_created)
+                BuildStatus.BUILDING, date_started=spr_build.date_created
+            )
             removeSecurityProxy(spr_build).updateStatus(
-                status, date_finished=spr_build.date_started + duration)
+                status, date_finished=spr_build.date_started + duration
+            )
         else:
             removeSecurityProxy(spr_build).updateStatus(status)
         IStore(spr_build).flush()
@@ -3284,18 +3979,29 @@ class LaunchpadObjectFactory(ObjectFactory):
         jobset = getUtility(ITranslationTemplatesBuildSource)
         return jobset.create(branch)
 
-    def makePOTemplate(self, productseries=None, distroseries=None,
-                       sourcepackagename=None, owner=None, name=None,
-                       translation_domain=None, path=None,
-                       copy_pofiles=True, side=None, sourcepackage=None,
-                       iscurrent=True):
+    def makePOTemplate(
+        self,
+        productseries=None,
+        distroseries=None,
+        sourcepackagename=None,
+        owner=None,
+        name=None,
+        translation_domain=None,
+        path=None,
+        copy_pofiles=True,
+        side=None,
+        sourcepackage=None,
+        iscurrent=True,
+    ):
         """Make a new translation template."""
         if sourcepackage is not None:
-            assert distroseries is None, (
-                'Cannot specify sourcepackage and distroseries')
+            assert (
+                distroseries is None
+            ), "Cannot specify sourcepackage and distroseries"
             distroseries = sourcepackage.distroseries
-            assert sourcepackagename is None, (
-                'Cannot specify sourcepackage and sourcepackagename')
+            assert (
+                sourcepackagename is None
+            ), "Cannot specify sourcepackage and sourcepackagename"
             sourcepackagename = sourcepackage.sourcepackagename
         if productseries is None and distroseries is None:
             if side != TranslationSide.UBUNTU:
@@ -3305,7 +4011,8 @@ class LaunchpadObjectFactory(ObjectFactory):
                 # to us creating a template for it.
                 naked_series = removeSecurityProxy(productseries)
                 naked_series.product.translations_usage = (
-                    ServiceUsage.LAUNCHPAD)
+                    ServiceUsage.LAUNCHPAD
+                )
             else:
                 distroseries = self.makeUbuntuDistroSeries()
         if distroseries is not None and sourcepackagename is None:
@@ -3313,7 +4020,8 @@ class LaunchpadObjectFactory(ObjectFactory):
 
         templateset = getUtility(IPOTemplateSet)
         subset = templateset.getSubset(
-            distroseries, sourcepackagename, productseries)
+            distroseries, sourcepackagename, productseries
+        )
 
         if name is None:
             name = self.getUniqueString()
@@ -3327,7 +4035,7 @@ class LaunchpadObjectFactory(ObjectFactory):
                 owner = productseries.owner
 
         if path is None:
-            path = 'messages.pot'
+            path = "messages.pot"
 
         pot = subset.new(name, translation_domain, path, owner, copy_pofiles)
         removeSecurityProxy(pot).iscurrent = iscurrent
@@ -3345,11 +4053,19 @@ class LaunchpadObjectFactory(ObjectFactory):
             self.makePOFile(language_code, template, template.owner)
         return template
 
-    def makePOFile(self, language_code=None, potemplate=None, owner=None,
-                   create_sharing=False, language=None, side=None):
+    def makePOFile(
+        self,
+        language_code=None,
+        potemplate=None,
+        owner=None,
+        create_sharing=False,
+        language=None,
+        side=None,
+    ):
         """Make a new translation file."""
-        assert language_code is None or language is None, (
-            "Please specify only one of language_code and language.")
+        assert (
+            language_code is None or language is None
+        ), "Please specify only one of language_code and language."
         if language_code is None:
             if language is None:
                 language = self.makeLanguage()
@@ -3357,14 +4073,23 @@ class LaunchpadObjectFactory(ObjectFactory):
         if potemplate is None:
             potemplate = self.makePOTemplate(owner=owner, side=side)
         else:
-            assert side is None, 'Cannot specify both side and potemplate.'
-        return potemplate.newPOFile(language_code,
-                                    create_sharing=create_sharing)
-
-    def makePOTMsgSet(self, potemplate=None, singular=None, plural=None,
-                      context=None, sequence=None, commenttext=None,
-                      filereferences=None, sourcecomment=None,
-                      flagscomment=None):
+            assert side is None, "Cannot specify both side and potemplate."
+        return potemplate.newPOFile(
+            language_code, create_sharing=create_sharing
+        )
+
+    def makePOTMsgSet(
+        self,
+        potemplate=None,
+        singular=None,
+        plural=None,
+        context=None,
+        sequence=None,
+        commenttext=None,
+        filereferences=None,
+        sourcecomment=None,
+        flagscomment=None,
+    ):
         """Make a new `POTMsgSet` in the given template."""
         if potemplate is None:
             potemplate = self.makePOTemplate()
@@ -3373,7 +4098,8 @@ class LaunchpadObjectFactory(ObjectFactory):
         if sequence is None:
             sequence = self.getUniqueInteger()
         potmsgset = potemplate.createMessageSetFromText(
-            singular, plural, context, sequence)
+            singular, plural, context, sequence
+        )
         if commenttext is not None:
             potmsgset.commenttext = commenttext
         if filereferences is not None:
@@ -3385,8 +4111,9 @@ class LaunchpadObjectFactory(ObjectFactory):
         removeSecurityProxy(potmsgset).sync()
         return potmsgset
 
-    def makePOFileAndPOTMsgSet(self, language_code=None, msgid=None,
-                               with_plural=False, side=None):
+    def makePOFileAndPOTMsgSet(
+        self, language_code=None, msgid=None, with_plural=False, side=None
+    ):
         """Make a `POFile` with a `POTMsgSet`."""
         pofile = self.makePOFile(language_code, side=side)
 
@@ -3398,7 +4125,8 @@ class LaunchpadObjectFactory(ObjectFactory):
             plural = None
 
         potmsgset = self.makePOTMsgSet(
-            pofile.potemplate, singular=msgid, plural=plural)
+            pofile.potemplate, singular=msgid, plural=plural
+        )
 
         return pofile, potmsgset
 
@@ -3414,38 +4142,56 @@ class LaunchpadObjectFactory(ObjectFactory):
             return {0: self.getUniqueUnicode()}
         if isinstance(translations, dict):
             return translations
-        assert isinstance(translations, (list, tuple)), (
-                "Expecting either a dict or a sequence.")
+        assert isinstance(
+            translations, (list, tuple)
+        ), "Expecting either a dict or a sequence."
         return dict(enumerate(translations))
 
-    def makeSuggestion(self, pofile=None, potmsgset=None, translator=None,
-                       translations=None, date_created=None):
+    def makeSuggestion(
+        self,
+        pofile=None,
+        potmsgset=None,
+        translator=None,
+        translations=None,
+        date_created=None,
+    ):
         """Make a new suggested `TranslationMessage` in the given PO file."""
         if pofile is None:
-            pofile = self.makePOFile('sr')
+            pofile = self.makePOFile("sr")
         if potmsgset is None:
             potmsgset = self.makePOTMsgSet(pofile.potemplate)
         if translator is None:
             translator = self.makePerson()
         translations = self.makeTranslationsDict(translations)
         translation_message = potmsgset.submitSuggestion(
-            pofile, translator, translations)
-        assert translation_message is not None, (
-            "Cannot make suggestion on translation credits POTMsgSet.")
+            pofile, translator, translations
+        )
+        assert (
+            translation_message is not None
+        ), "Cannot make suggestion on translation credits POTMsgSet."
         if date_created is not None:
             naked_translation_message = removeSecurityProxy(
-                translation_message)
+                translation_message
+            )
             naked_translation_message.date_created = date_created
             naked_translation_message.sync()
         return translation_message
 
-    def makeCurrentTranslationMessage(self, pofile=None, potmsgset=None,
-                                      translator=None, reviewer=None,
-                                      translations=None, diverged=False,
-                                      current_other=False,
-                                      date_created=None, date_reviewed=None,
-                                      language=None, side=None,
-                                      potemplate=None):
+    def makeCurrentTranslationMessage(
+        self,
+        pofile=None,
+        potmsgset=None,
+        translator=None,
+        reviewer=None,
+        translations=None,
+        diverged=False,
+        current_other=False,
+        date_created=None,
+        date_reviewed=None,
+        language=None,
+        side=None,
+        potemplate=None,
+    ):
         """Create a `TranslationMessage` and make it current.
 
         By default the message will only be current on the side (Ubuntu
@@ -3476,35 +4222,40 @@ class LaunchpadObjectFactory(ObjectFactory):
         :param potemplate: If provided, the POTemplate to use when creating
             the POFile.
         """
-        assert not (diverged and current_other), (
-            "A diverged message can't be current on the other side.")
-        assert None in (language, pofile), (
-            'Cannot specify both language and pofile.')
-        assert None in (side, pofile), (
-            'Cannot specify both side and pofile.')
+        assert not (
+            diverged and current_other
+        ), "A diverged message can't be current on the other side."
+        assert None in (
+            language,
+            pofile,
+        ), "Cannot specify both language and pofile."
+        assert None in (side, pofile), "Cannot specify both side and pofile."
         link_potmsgset_with_potemplate = (
-            (pofile is None and potemplate is None) or potmsgset is None)
+            pofile is None and potemplate is None
+        ) or potmsgset is None
         if pofile is None:
             pofile = self.makePOFile(
-                language=language, side=side, potemplate=potemplate)
+                language=language, side=side, potemplate=potemplate
+            )
         else:
-            assert potemplate is None, (
-                'Cannot specify both pofile and potemplate')
+            assert (
+                potemplate is None
+            ), "Cannot specify both pofile and potemplate"
         potemplate = pofile.potemplate
         if potmsgset is None:
             potmsgset = self.makePOTMsgSet(potemplate)
         if link_potmsgset_with_potemplate:
             # If we have a new pofile or a new potmsgset, we must link
             # the potmsgset to the pofile's potemplate.
-            potmsgset.setSequence(
-                pofile.potemplate, self.getUniqueInteger())
+            potmsgset.setSequence(pofile.potemplate, self.getUniqueInteger())
         else:
             # Otherwise it is the duty of the callsite to ensure
             # consistency.
             store = IStore(TranslationTemplateItem)
             tti_for_message_in_template = store.find(
                 TranslationTemplateItem.potmsgset == potmsgset,
-                TranslationTemplateItem.potemplate == pofile.potemplate).any()
+                TranslationTemplateItem.potemplate == pofile.potemplate,
+            ).any()
             assert tti_for_message_in_template is not None
         if translator is None:
             translator = self.makePerson()
@@ -3513,17 +4264,26 @@ class LaunchpadObjectFactory(ObjectFactory):
         translations = sanitize_translations_from_webui(
             potmsgset.singular_text,
             self.makeTranslationsDict(translations),
-            pofile.language.pluralforms)
+            pofile.language.pluralforms,
+        )
 
         if diverged:
             message = self.makeDivergedTranslationMessage(
-                pofile, potmsgset, translator, reviewer,
-                translations, date_created)
+                pofile,
+                potmsgset,
+                translator,
+                reviewer,
+                translations,
+                date_created,
+            )
         else:
             message = potmsgset.setCurrentTranslation(
-                pofile, translator, translations,
+                pofile,
+                translator,
+                translations,
                 RosettaTranslationOrigin.ROSETTAWEB,
-                share_with_other_side=current_other)
+                share_with_other_side=current_other,
+            )
             if date_created is not None:
                 removeSecurityProxy(message).date_created = date_created
 
@@ -3531,34 +4291,51 @@ class LaunchpadObjectFactory(ObjectFactory):
 
         return message
 
-    def makeDivergedTranslationMessage(self, pofile=None, potmsgset=None,
-                                       translator=None, reviewer=None,
-                                       translations=None, date_created=None):
+    def makeDivergedTranslationMessage(
+        self,
+        pofile=None,
+        potmsgset=None,
+        translator=None,
+        reviewer=None,
+        translations=None,
+        date_created=None,
+    ):
         """Create a diverged, current `TranslationMessage`."""
         if pofile is None:
-            pofile = self.makePOFile('lt')
+            pofile = self.makePOFile("lt")
         if reviewer is None:
             reviewer = self.makePerson()
 
         message = self.makeSuggestion(
-            pofile=pofile, potmsgset=potmsgset, translator=translator,
-            translations=translations, date_created=date_created)
+            pofile=pofile,
+            potmsgset=potmsgset,
+            translator=translator,
+            translations=translations,
+            date_created=date_created,
+        )
         return message.approveAsDiverged(pofile, reviewer)
 
-    def makeTranslationImportQueueEntry(self, path=None, productseries=None,
-                                        distroseries=None,
-                                        sourcepackagename=None,
-                                        potemplate=None, content=None,
-                                        uploader=None, pofile=None,
-                                        format=None, status=None,
-                                        by_maintainer=False):
+    def makeTranslationImportQueueEntry(
+        self,
+        path=None,
+        productseries=None,
+        distroseries=None,
+        sourcepackagename=None,
+        potemplate=None,
+        content=None,
+        uploader=None,
+        pofile=None,
+        format=None,
+        status=None,
+        by_maintainer=False,
+    ):
         """Create a `TranslationImportQueueEntry`."""
         if path is None:
-            path = self.getUniqueUnicode() + '.pot'
+            path = self.getUniqueUnicode() + ".pot"
 
         for_distro = not (distroseries is None and sourcepackagename is None)
         for_project = productseries is not None
-        has_template = (potemplate is not None)
+        has_template = potemplate is not None
         if has_template and not for_distro and not for_project:
             # Copy target from template.
             distroseries = potemplate.distroseries
@@ -3589,12 +4366,20 @@ class LaunchpadObjectFactory(ObjectFactory):
         content = six.ensure_binary(content)
 
         entry = getUtility(ITranslationImportQueue).addOrUpdateEntry(
-            path=path, content=content, by_maintainer=by_maintainer,
-            importer=uploader, productseries=productseries,
-            distroseries=distroseries, sourcepackagename=sourcepackagename,
-            potemplate=potemplate, pofile=pofile, format=format)
+            path=path,
+            content=content,
+            by_maintainer=by_maintainer,
+            importer=uploader,
+            productseries=productseries,
+            distroseries=distroseries,
+            sourcepackagename=sourcepackagename,
+            potemplate=potemplate,
+            pofile=pofile,
+            format=format,
+        )
         entry.setStatus(
-            status, getUtility(ILaunchpadCelebrities).rosetta_experts)
+            status, getUtility(ILaunchpadCelebrities).rosetta_experts
+        )
         return entry
 
     def makeMailingList(self, team, owner):
@@ -3605,9 +4390,12 @@ class LaunchpadObjectFactory(ObjectFactory):
         return team_list
 
     def makeTeamAndMailingList(
-        self, team_name, owner_name,
+        self,
+        team_name,
+        owner_name,
         visibility=None,
-        membership_policy=TeamMembershipPolicy.OPEN):
+        membership_policy=TeamMembershipPolicy.OPEN,
+    ):
         """Make a new active mailing list for the named team.
 
         :param team_name: The new team's name.
@@ -3624,18 +4412,23 @@ class LaunchpadObjectFactory(ObjectFactory):
         """
         owner = getUtility(IPersonSet).getByName(owner_name)
         display_name = SPACE.join(
-            word.capitalize() for word in team_name.split('-'))
+            word.capitalize() for word in team_name.split("-")
+        )
         team = getUtility(IPersonSet).getByName(team_name)
         if team is None:
             team = self.makeTeam(
-                owner, displayname=display_name, name=team_name,
+                owner,
+                displayname=display_name,
+                name=team_name,
                 visibility=visibility,
-                membership_policy=membership_policy)
+                membership_policy=membership_policy,
+            )
         team_list = self.makeMailingList(team, owner)
         return team, team_list
 
-    def makeTeamWithMailingListSubscribers(self, team_name, super_team=None,
-                                           auto_subscribe=True):
+    def makeTeamWithMailingListSubscribers(
+        self, team_name, super_team=None, auto_subscribe=True
+    ):
         """Create a team, mailing list, and subscribers.
 
         :param team_name: The name of the team to create.
@@ -3646,12 +4439,13 @@ class LaunchpadObjectFactory(ObjectFactory):
         """
         team = self.makeTeam(name=team_name)
         member = self.makePerson()
-        with celebrity_logged_in('admin'):
+        with celebrity_logged_in("admin"):
             if super_team is None:
                 mailing_list = self.makeMailingList(team, team.teamowner)
             else:
                 super_team.addMember(
-                    team, reviewer=team.teamowner, force_team_add=True)
+                    team, reviewer=team.teamowner, force_team_add=True
+                )
                 mailing_list = super_team.mailing_list
             team.addMember(member, reviewer=team.teamowner)
             if auto_subscribe:
@@ -3665,15 +4459,26 @@ class LaunchpadObjectFactory(ObjectFactory):
         log_file.seek(0)
 
         library_alias = getUtility(ILibraryFileAliasSet).create(
-            name='foo', size=len(log_file.getvalue()),
-            file=log_file, contentType='text/plain')
+            name="foo",
+            size=len(log_file.getvalue()),
+            file=log_file,
+            contentType="text/plain",
+        )
 
         proberecord = mirror.newProbeRecord(library_alias)
         return proberecord
 
-    def makeMirror(self, distribution, displayname=None, country=None,
-                   http_url=None, https_url=None, ftp_url=None, rsync_url=None,
-                   official_candidate=False):
+    def makeMirror(
+        self,
+        distribution,
+        displayname=None,
+        country=None,
+        http_url=None,
+        https_url=None,
+        ftp_url=None,
+        rsync_url=None,
+        official_candidate=False,
+    ):
         """Create a mirror for the distribution."""
         if displayname is None:
             displayname = self.getUniqueString("mirror")
@@ -3682,7 +4487,7 @@ class LaunchpadObjectFactory(ObjectFactory):
             http_url = self.getUniqueURL()
         # If no country is given use Argentina.
         if country is None:
-            country = getUtility(ICountrySet)['AR']
+            country = getUtility(ICountrySet)["AR"]
 
         mirror = distribution.newMirror(
             owner=distribution.owner,
@@ -3695,7 +4500,8 @@ class LaunchpadObjectFactory(ObjectFactory):
             https_base_url=https_url,
             ftp_base_url=ftp_url,
             rsync_base_url=rsync_url,
-            official_candidate=official_candidate)
+            official_candidate=official_candidate,
+        )
         return mirror
 
     def makeUniqueRFC822MsgId(self):
@@ -3704,9 +4510,9 @@ class LaunchpadObjectFactory(ObjectFactory):
         The created message id is guaranteed not to exist in the
         `Message` table already.
         """
-        msg_id = make_msgid('launchpad')
+        msg_id = make_msgid("launchpad")
         while not Message.selectBy(rfc822msgid=msg_id).is_empty():
-            msg_id = make_msgid('launchpad')
+            msg_id = make_msgid("launchpad")
         return msg_id
 
     def makeSourcePackageName(self, name=None):
@@ -3725,8 +4531,9 @@ class LaunchpadObjectFactory(ObjectFactory):
             return self.makeSourcePackageName()
         return getUtility(ISourcePackageNameSet).getOrCreateByName(name)
 
-    def makeSourcePackage(self, sourcepackagename=None, distroseries=None,
-                          publish=False):
+    def makeSourcePackage(
+        self, sourcepackagename=None, distroseries=None, publish=False
+    ):
         """Make an `ISourcePackage`.
 
         :param publish: if true, create a corresponding
@@ -3735,34 +4542,43 @@ class LaunchpadObjectFactory(ObjectFactory):
         # Make sure we have a real sourcepackagename object.
         if sourcepackagename is None or isinstance(sourcepackagename, str):
             sourcepackagename = self.getOrMakeSourcePackageName(
-                sourcepackagename)
+                sourcepackagename
+            )
         if distroseries is None:
             distroseries = self.makeDistroSeries()
         if publish:
             self.makeSourcePackagePublishingHistory(
-                distroseries=distroseries,
-                sourcepackagename=sourcepackagename)
-            with dbuser('statistician'):
+                distroseries=distroseries, sourcepackagename=sourcepackagename
+            )
+            with dbuser("statistician"):
                 DistributionSourcePackageCache(
                     distribution=distroseries.distribution,
                     sourcepackagename=sourcepackagename,
                     archive=distroseries.main_archive,
-                    name=sourcepackagename.name)
+                    name=sourcepackagename.name,
+                )
         return distroseries.getSourcePackage(sourcepackagename)
 
     def getAnySourcePackageUrgency(self):
         return SourcePackageUrgency.MEDIUM
 
-    def makePackageUpload(self, distroseries=None, archive=None,
-                          pocket=None, changes_filename=None,
-                          changes_file_content=None,
-                          signing_key=None, status=None,
-                          package_copy_job=None):
+    def makePackageUpload(
+        self,
+        distroseries=None,
+        archive=None,
+        pocket=None,
+        changes_filename=None,
+        changes_file_content=None,
+        signing_key=None,
+        status=None,
+        package_copy_job=None,
+    ):
         if archive is None:
             archive = self.makeArchive()
         if distroseries is None:
             distroseries = self.makeDistroSeries(
-                distribution=archive.distribution)
+                distribution=archive.distribution
+            )
         if changes_filename is None:
             changes_filename = self.getUniqueString("changesfilename")
         if changes_file_content is None:
@@ -3770,53 +4586,82 @@ class LaunchpadObjectFactory(ObjectFactory):
         if pocket is None:
             pocket = PackagePublishingPocket.RELEASE
         package_upload = distroseries.createQueueEntry(
-            pocket, archive, changes_filename, changes_file_content,
-            signing_key=signing_key, package_copy_job=package_copy_job)
+            pocket,
+            archive,
+            changes_filename,
+            changes_file_content,
+            signing_key=signing_key,
+            package_copy_job=package_copy_job,
+        )
         if status is not None:
             if status is not PackageUploadStatus.NEW:
                 naked_package_upload = removeSecurityProxy(package_upload)
                 status_changers = {
-                    PackageUploadStatus.UNAPPROVED:
-                        naked_package_upload.setUnapproved,
-                    PackageUploadStatus.REJECTED:
-                        naked_package_upload.setRejected,
+                    PackageUploadStatus.UNAPPROVED: (
+                        naked_package_upload.setUnapproved
+                    ),
+                    PackageUploadStatus.REJECTED: (
+                        naked_package_upload.setRejected
+                    ),
                     PackageUploadStatus.DONE: naked_package_upload.setDone,
-                    PackageUploadStatus.ACCEPTED:
-                        naked_package_upload.setAccepted,
-                    }
+                    PackageUploadStatus.ACCEPTED: (
+                        naked_package_upload.setAccepted
+                    ),
+                }
                 status_changers[status]()
         return package_upload
 
-    def makeSourcePackageUpload(self, distroseries=None,
-                                sourcepackagename=None, component=None):
+    def makeSourcePackageUpload(
+        self, distroseries=None, sourcepackagename=None, component=None
+    ):
         """Make a `PackageUpload` with a `PackageUploadSource` attached."""
         if distroseries is None:
             distroseries = self.makeDistroSeries()
         upload = self.makePackageUpload(
-            distroseries=distroseries, archive=distroseries.main_archive)
-        upload.addSource(self.makeSourcePackageRelease(
-            sourcepackagename=sourcepackagename, component=component))
+            distroseries=distroseries, archive=distroseries.main_archive
+        )
+        upload.addSource(
+            self.makeSourcePackageRelease(
+                sourcepackagename=sourcepackagename, component=component
+            )
+        )
         return upload
 
-    def makeBuildPackageUpload(self, distroseries=None, pocket=None,
-                               binarypackagename=None,
-                               source_package_release=None, component=None):
+    def makeBuildPackageUpload(
+        self,
+        distroseries=None,
+        pocket=None,
+        binarypackagename=None,
+        source_package_release=None,
+        component=None,
+    ):
         """Make a `PackageUpload` with a `PackageUploadBuild` attached."""
         if distroseries is None:
             distroseries = self.makeDistroSeries()
         upload = self.makePackageUpload(
-            distroseries=distroseries, archive=distroseries.main_archive,
-            pocket=pocket)
+            distroseries=distroseries,
+            archive=distroseries.main_archive,
+            pocket=pocket,
+        )
         build = self.makeBinaryPackageBuild(
-            source_package_release=source_package_release, pocket=pocket)
+            source_package_release=source_package_release, pocket=pocket
+        )
         self.makeBinaryPackageRelease(
-            binarypackagename=binarypackagename, build=build,
-            component=component)
+            binarypackagename=binarypackagename,
+            build=build,
+            component=component,
+        )
         upload.addBuild(build)
         return upload
 
-    def makeCustomPackageUpload(self, distroseries=None, archive=None,
-                                pocket=None, custom_type=None, filename=None):
+    def makeCustomPackageUpload(
+        self,
+        distroseries=None,
+        archive=None,
+        pocket=None,
+        custom_type=None,
+        filename=None,
+    ):
         """Make a `PackageUpload` with a `PackageUploadCustom` attached."""
         if distroseries is None:
             distroseries = self.makeDistroSeries()
@@ -3825,20 +4670,27 @@ class LaunchpadObjectFactory(ObjectFactory):
         if custom_type is None:
             custom_type = PackageUploadCustomFormat.DEBIAN_INSTALLER
         upload = self.makePackageUpload(
-            distroseries=distroseries, archive=archive, pocket=pocket)
+            distroseries=distroseries, archive=archive, pocket=pocket
+        )
         file_alias = self.makeLibraryFileAlias(filename=filename)
         upload.addCustom(file_alias, custom_type)
         return upload
 
-    def makeCopyJobPackageUpload(self, distroseries=None,
-                                 sourcepackagename=None, source_archive=None,
-                                 target_pocket=None, requester=None,
-                                 include_binaries=False):
+    def makeCopyJobPackageUpload(
+        self,
+        distroseries=None,
+        sourcepackagename=None,
+        source_archive=None,
+        target_pocket=None,
+        requester=None,
+        include_binaries=False,
+    ):
         """Make a `PackageUpload` with a `PackageCopyJob` attached."""
         if distroseries is None:
             distroseries = self.makeDistroSeries()
         spph = self.makeSourcePackagePublishingHistory(
-            archive=source_archive, sourcepackagename=sourcepackagename)
+            archive=source_archive, sourcepackagename=sourcepackagename
+        )
         spr = spph.sourcepackagerelease
         job = self.makePlainPackageCopyJob(
             package_name=spr.sourcepackagename.name,
@@ -3846,8 +4698,10 @@ class LaunchpadObjectFactory(ObjectFactory):
             source_archive=spph.archive,
             target_pocket=target_pocket,
             target_archive=distroseries.main_archive,
-            target_distroseries=distroseries, requester=requester,
-            include_binaries=include_binaries)
+            target_distroseries=distroseries,
+            requester=requester,
+            include_binaries=include_binaries,
+        )
         job.addSourceOverride(SourceOverride(spr.component, spr.section))
         try:
             job.run()
@@ -3857,28 +4711,37 @@ class LaunchpadObjectFactory(ObjectFactory):
         upload_set = getUtility(IPackageUploadSet)
         return upload_set.getByPackageCopyJobIDs([job.id]).one()
 
-    def makeSourcePackageRelease(self, archive=None, sourcepackagename=None,
-                                 distroseries=None, maintainer=None,
-                                 creator=None, component=None,
-                                 section_name=None, urgency=None,
-                                 version=None, builddepends=None,
-                                 builddependsindep=None,
-                                 build_conflicts=None,
-                                 build_conflicts_indep=None,
-                                 architecturehintlist='all',
-                                 dsc_maintainer_rfc822=None,
-                                 dsc_standards_version='3.6.2',
-                                 dsc_format='1.0', dsc_binaries='foo-bin',
-                                 date_uploaded=UTC_NOW,
-                                 source_package_recipe_build=None,
-                                 ci_build=None,
-                                 dscsigningkey=None,
-                                 user_defined_fields=None,
-                                 changelog_entry=None,
-                                 homepage=None,
-                                 changelog=None,
-                                 copyright=None,
-                                 format=None):
+    def makeSourcePackageRelease(
+        self,
+        archive=None,
+        sourcepackagename=None,
+        distroseries=None,
+        maintainer=None,
+        creator=None,
+        component=None,
+        section_name=None,
+        urgency=None,
+        version=None,
+        builddepends=None,
+        builddependsindep=None,
+        build_conflicts=None,
+        build_conflicts_indep=None,
+        architecturehintlist="all",
+        dsc_maintainer_rfc822=None,
+        dsc_standards_version="3.6.2",
+        dsc_format="1.0",
+        dsc_binaries="foo-bin",
+        date_uploaded=UTC_NOW,
+        source_package_recipe_build=None,
+        ci_build=None,
+        dscsigningkey=None,
+        user_defined_fields=None,
+        changelog_entry=None,
+        homepage=None,
+        changelog=None,
+        copyright=None,
+        format=None,
+    ):
         """Make a `SourcePackageRelease`."""
         if distroseries is None:
             if source_package_recipe_build is not None:
@@ -3890,17 +4753,17 @@ class LaunchpadObjectFactory(ObjectFactory):
                     distribution = None
                 else:
                     distribution = archive.distribution
-                distroseries = self.makeDistroSeries(
-                    distribution=distribution)
+                distroseries = self.makeDistroSeries(distribution=distribution)
 
         if archive is None:
             archive = distroseries.main_archive
 
         if sourcepackagename is None or isinstance(sourcepackagename, str):
             sourcepackagename = self.getOrMakeSourcePackageName(
-                sourcepackagename)
+                sourcepackagename
+            )
 
-        if (component is None or isinstance(component, str)):
+        if component is None or isinstance(component, str):
             component = self.makeComponent(component)
 
         if urgency is None:
@@ -3914,15 +4777,16 @@ class LaunchpadObjectFactory(ObjectFactory):
             maintainer = self.makePerson()
 
         if dsc_maintainer_rfc822 is None:
-            dsc_maintainer_rfc822 = '%s <%s>' % (
+            dsc_maintainer_rfc822 = "%s <%s>" % (
                 maintainer.displayname,
-                removeSecurityProxy(maintainer).preferredemail.email)
+                removeSecurityProxy(maintainer).preferredemail.email,
+            )
 
         if creator is None:
             creator = self.makePerson()
 
         if version is None:
-            version = str(self.getUniqueInteger()) + 'version'
+            version = str(self.getUniqueInteger()) + "version"
 
         if format is None:
             format = SourcePackageType.DPKG
@@ -3958,10 +4822,12 @@ class LaunchpadObjectFactory(ObjectFactory):
             source_package_recipe_build=source_package_recipe_build,
             ci_build=ci_build,
             user_defined_fields=user_defined_fields,
-            homepage=homepage)
+            homepage=homepage,
+        )
 
-    def makeSourcePackageReleaseFile(self, sourcepackagerelease=None,
-                                     library_file=None, filetype=None):
+    def makeSourcePackageReleaseFile(
+        self, sourcepackagerelease=None, library_file=None, filetype=None
+    ):
         if sourcepackagerelease is None:
             sourcepackagerelease = self.makeSourcePackageRelease()
         if library_file is None:
@@ -3969,12 +4835,22 @@ class LaunchpadObjectFactory(ObjectFactory):
         if filetype is None:
             filetype = SourcePackageFileType.DSC
         return ProxyFactory(
-            sourcepackagerelease.addFile(library_file, filetype=filetype))
+            sourcepackagerelease.addFile(library_file, filetype=filetype)
+        )
 
-    def makeBinaryPackageBuild(self, source_package_release=None,
-            distroarchseries=None, archive=None, builder=None,
-            status=None, pocket=None, date_created=None, processor=None,
-            sourcepackagename=None, arch_indep=None):
+    def makeBinaryPackageBuild(
+        self,
+        source_package_release=None,
+        distroarchseries=None,
+        archive=None,
+        builder=None,
+        status=None,
+        pocket=None,
+        date_created=None,
+        processor=None,
+        sourcepackagename=None,
+        arch_indep=None,
+    ):
         """Create a BinaryPackageBuild.
 
         If archive is not supplied, the source_package_release is used
@@ -3998,16 +4874,21 @@ class LaunchpadObjectFactory(ObjectFactory):
                 distroseries = source_package_release.upload_distroseries
             elif archive is not None:
                 distroseries = self.makeDistroSeries(
-                    distribution=archive.distribution)
+                    distribution=archive.distribution
+                )
             else:
                 distroseries = self.makeDistroSeries()
             distroarchseries = self.makeDistroArchSeries(
-                distroseries=distroseries, processor=processor)
+                distroseries=distroseries, processor=processor
+            )
         else:
-            if (processor is not None
-                    and processor != distroarchseries.processor):
+            if (
+                processor is not None
+                and processor != distroarchseries.processor
+            ):
                 raise AssertionError(
-                    "DistroArchSeries and Processor must match.")
+                    "DistroArchSeries and Processor must match."
+                )
         if arch_indep is None:
             arch_indep = distroarchseries.isNominatedArchIndep
         if archive is None:
@@ -4021,15 +4902,19 @@ class LaunchpadObjectFactory(ObjectFactory):
             pocket = PackagePublishingPocket.items[pocket.upper()]
 
         if source_package_release is None:
-            multiverse = self.makeComponent(name='multiverse')
+            multiverse = self.makeComponent(name="multiverse")
             source_package_release = self.makeSourcePackageRelease(
-                archive, component=multiverse,
+                archive,
+                component=multiverse,
                 distroseries=distroarchseries.distroseries,
-                sourcepackagename=sourcepackagename)
+                sourcepackagename=sourcepackagename,
+            )
             self.makeSourcePackagePublishingHistory(
                 distroseries=distroarchseries.distroseries,
-                archive=archive, sourcepackagerelease=source_package_release,
-                pocket=pocket)
+                archive=archive,
+                sourcepackagerelease=source_package_release,
+                pocket=pocket,
+            )
         if status is None:
             status = BuildStatus.NEEDSBUILD
         admins = getUtility(ILaunchpadCelebrities).admin
@@ -4041,25 +4926,28 @@ class LaunchpadObjectFactory(ObjectFactory):
                 archive=archive,
                 pocket=pocket,
                 builder=builder,
-                arch_indep=arch_indep)
+                arch_indep=arch_indep,
+            )
         IStore(binary_package_build).flush()
         return binary_package_build
 
-    def makeSourcePackagePublishingHistory(self,
-                                           distroseries=None,
-                                           archive=None,
-                                           sourcepackagerelease=None,
-                                           pocket=None,
-                                           status=None,
-                                           dateremoved=None,
-                                           date_uploaded=UTC_NOW,
-                                           scheduleddeletiondate=None,
-                                           ancestor=None,
-                                           creator=None,
-                                           packageupload=None,
-                                           spr_creator=None,
-                                           channel=None,
-                                           **kwargs):
+    def makeSourcePackagePublishingHistory(
+        self,
+        distroseries=None,
+        archive=None,
+        sourcepackagerelease=None,
+        pocket=None,
+        status=None,
+        dateremoved=None,
+        date_uploaded=UTC_NOW,
+        scheduleddeletiondate=None,
+        ancestor=None,
+        creator=None,
+        packageupload=None,
+        spr_creator=None,
+        channel=None,
+        **kwargs
+    ):
         """Make a `SourcePackagePublishingHistory`.
 
         :param sourcepackagerelease: The source package release to publish
@@ -4092,8 +4980,7 @@ class LaunchpadObjectFactory(ObjectFactory):
                     distribution = None
                 else:
                     distribution = archive.distribution
-                distroseries = self.makeDistroSeries(
-                    distribution=distribution)
+                distroseries = self.makeDistroSeries(distribution=distribution)
         if archive is None:
             archive = distroseries.main_archive
 
@@ -4109,17 +4996,27 @@ class LaunchpadObjectFactory(ObjectFactory):
 
         if sourcepackagerelease is None:
             sourcepackagerelease = self.makeSourcePackageRelease(
-                archive=archive, distroseries=distroseries,
-                date_uploaded=date_uploaded, creator=spr_creator, **kwargs)
+                archive=archive,
+                distroseries=distroseries,
+                date_uploaded=date_uploaded,
+                creator=spr_creator,
+                **kwargs,
+            )
 
         admins = getUtility(ILaunchpadCelebrities).admin
         with person_logged_in(admins.teamowner):
             spph = getUtility(IPublishingSet).newSourcePublication(
-                archive, sourcepackagerelease, distroseries, pocket,
+                archive,
+                sourcepackagerelease,
+                distroseries,
+                pocket,
                 component=sourcepackagerelease.component,
                 section=sourcepackagerelease.section,
-                ancestor=ancestor, creator=creator,
-                packageupload=packageupload, channel=channel)
+                ancestor=ancestor,
+                creator=creator,
+                packageupload=packageupload,
+                channel=channel,
+            )
 
         naked_spph = removeSecurityProxy(spph)
         naked_spph.status = status
@@ -4130,22 +5027,30 @@ class LaunchpadObjectFactory(ObjectFactory):
             naked_spph.datepublished = UTC_NOW
         return spph
 
-    def makeBinaryPackagePublishingHistory(self, binarypackagerelease=None,
-                                           binarypackagename=None,
-                                           distroarchseries=None,
-                                           component=None, section_name=None,
-                                           priority=None, status=None,
-                                           scheduleddeletiondate=None,
-                                           dateremoved=None,
-                                           datecreated=None,
-                                           pocket=None, archive=None,
-                                           source_package_release=None,
-                                           binpackageformat=None,
-                                           sourcepackagename=None,
-                                           version=None,
-                                           architecturespecific=False,
-                                           with_debug=False, with_file=False,
-                                           creator=None, channel=None):
+    def makeBinaryPackagePublishingHistory(
+        self,
+        binarypackagerelease=None,
+        binarypackagename=None,
+        distroarchseries=None,
+        component=None,
+        section_name=None,
+        priority=None,
+        status=None,
+        scheduleddeletiondate=None,
+        dateremoved=None,
+        datecreated=None,
+        pocket=None,
+        archive=None,
+        source_package_release=None,
+        binpackageformat=None,
+        sourcepackagename=None,
+        version=None,
+        architecturespecific=False,
+        with_debug=False,
+        with_file=False,
+        creator=None,
+        channel=None,
+    ):
         """Make a `BinaryPackagePublishingHistory`."""
         if distroarchseries is None:
             if archive is None:
@@ -4154,16 +5059,19 @@ class LaunchpadObjectFactory(ObjectFactory):
                 distribution = archive.distribution
             distroseries = self.makeDistroSeries(distribution=distribution)
             distroarchseries = self.makeDistroArchSeries(
-                distroseries=distroseries)
+                distroseries=distroseries
+            )
 
         if archive is None:
             archive = self.makeArchive(
                 distribution=distroarchseries.distroseries.distribution,
-                purpose=ArchivePurpose.PRIMARY)
+                purpose=ArchivePurpose.PRIMARY,
+            )
             # XXX wgrant 2013-05-23: We need to set build_debug_symbols
             # until the guard in publishBinaries is gone.
             need_debug = (
-                with_debug or binpackageformat == BinaryPackageFormat.DDEB)
+                with_debug or binpackageformat == BinaryPackageFormat.DDEB
+            )
             if archive.purpose == ArchivePurpose.PRIMARY and need_debug:
                 with admin_logged_in():
                     archive.build_debug_symbols = True
@@ -4185,40 +5093,58 @@ class LaunchpadObjectFactory(ObjectFactory):
             # Create a new BinaryPackageBuild and BinaryPackageRelease
             # in the same archive and suite.
             binarypackagebuild = self.makeBinaryPackageBuild(
-                archive=archive, distroarchseries=distroarchseries,
-                pocket=pocket, source_package_release=source_package_release,
-                sourcepackagename=sourcepackagename)
+                archive=archive,
+                distroarchseries=distroarchseries,
+                pocket=pocket,
+                source_package_release=source_package_release,
+                sourcepackagename=sourcepackagename,
+            )
             binarypackagerelease = self.makeBinaryPackageRelease(
-                binarypackagename=binarypackagename, version=version,
+                binarypackagename=binarypackagename,
+                version=version,
                 build=binarypackagebuild,
-                component=component, binpackageformat=binpackageformat,
-                section_name=section_name, priority=priority,
-                architecturespecific=architecturespecific)
+                component=component,
+                binpackageformat=binpackageformat,
+                section_name=section_name,
+                priority=priority,
+                architecturespecific=architecturespecific,
+            )
             if with_file:
                 ext = {
-                    BinaryPackageFormat.DEB: 'deb',
-                    BinaryPackageFormat.UDEB: 'udeb',
-                    BinaryPackageFormat.DDEB: 'ddeb',
-                    }[binarypackagerelease.binpackageformat]
+                    BinaryPackageFormat.DEB: "deb",
+                    BinaryPackageFormat.UDEB: "udeb",
+                    BinaryPackageFormat.DDEB: "ddeb",
+                }[binarypackagerelease.binpackageformat]
                 lfa = self.makeLibraryFileAlias(
-                    filename='%s_%s_%s.%s' % (
+                    filename="%s_%s_%s.%s"
+                    % (
                         binarypackagerelease.binarypackagename.name,
                         binarypackagerelease.version,
                         binarypackagebuild.distro_arch_series.architecturetag,
-                        ext))
+                        ext,
+                    )
+                )
                 self.makeBinaryPackageFile(
-                    binarypackagerelease=binarypackagerelease,
-                    library_file=lfa)
+                    binarypackagerelease=binarypackagerelease, library_file=lfa
+                )
 
         if datecreated is None:
             datecreated = self.getUniqueDate()
 
         bpphs = getUtility(IPublishingSet).publishBinaries(
-            archive, distroarchseries.distroseries, pocket,
-            {binarypackagerelease: (
-                binarypackagerelease.component, binarypackagerelease.section,
-                priority, None)},
-            channel=channel)
+            archive,
+            distroarchseries.distroseries,
+            pocket,
+            {
+                binarypackagerelease: (
+                    binarypackagerelease.component,
+                    binarypackagerelease.section,
+                    priority,
+                    None,
+                )
+            },
+            channel=channel,
+        )
         for bpph in bpphs:
             naked_bpph = removeSecurityProxy(bpph)
             naked_bpph.status = status
@@ -4231,21 +5157,29 @@ class LaunchpadObjectFactory(ObjectFactory):
         if with_debug:
             debug_bpph = self.makeBinaryPackagePublishingHistory(
                 binarypackagename=(
-                    binarypackagerelease.binarypackagename.name + '-dbgsym'),
-                version=version, distroarchseries=distroarchseries,
-                component=component, section_name=binarypackagerelease.section,
-                priority=priority, status=status,
+                    binarypackagerelease.binarypackagename.name + "-dbgsym"
+                ),
+                version=version,
+                distroarchseries=distroarchseries,
+                component=component,
+                section_name=binarypackagerelease.section,
+                priority=priority,
+                status=status,
                 scheduleddeletiondate=scheduleddeletiondate,
-                dateremoved=dateremoved, datecreated=datecreated,
-                pocket=pocket, archive=archive,
+                dateremoved=dateremoved,
+                datecreated=datecreated,
+                pocket=pocket,
+                archive=archive,
                 source_package_release=source_package_release,
                 binpackageformat=BinaryPackageFormat.DDEB,
                 sourcepackagename=sourcepackagename,
                 architecturespecific=architecturespecific,
                 with_file=with_file,
-                creator=creator)
-            removeSecurityProxy(bpph.binarypackagerelease).debug_package = (
-                debug_bpph.binarypackagerelease)
+                creator=creator,
+            )
+            removeSecurityProxy(
+                bpph.binarypackagerelease
+            ).debug_package = debug_bpph.binarypackagerelease
             return bpphs[0], debug_bpph
         return bpphs[0]
 
@@ -4261,7 +5195,9 @@ class LaunchpadObjectFactory(ObjectFactory):
         return self.makeSourcePackagePublishingHistory(
             distroseries=bpph.distroarchseries.distroseries,
             sourcepackagerelease=bpr.build.source_package_release,
-            pocket=bpph.pocket, archive=bpph.archive)
+            pocket=bpph.pocket,
+            archive=bpph.archive,
+        )
 
     def makeBinaryPackageName(self, name=None):
         """Make an `IBinaryPackageName`."""
@@ -4279,38 +5215,60 @@ class LaunchpadObjectFactory(ObjectFactory):
             return self.makeBinaryPackageName()
         return getUtility(IBinaryPackageNameSet).getOrCreateByName(name)
 
-    def makeBinaryPackageFile(self, binarypackagerelease=None,
-                              library_file=None, filetype=None):
+    def makeBinaryPackageFile(
+        self, binarypackagerelease=None, library_file=None, filetype=None
+    ):
         if binarypackagerelease is None:
             binarypackagerelease = self.makeBinaryPackageRelease()
         if library_file is None:
             library_file = self.makeLibraryFileAlias()
         if filetype is None:
             filetype = BinaryPackageFileType.DEB
-        return ProxyFactory(BinaryPackageFile(
-            binarypackagerelease=binarypackagerelease,
-            libraryfile=library_file, filetype=filetype))
-
-    def makeBinaryPackageRelease(self, binarypackagename=None,
-                                 version=None, build=None, ci_build=None,
-                                 binpackageformat=None, component=None,
-                                 section_name=None, priority=None,
-                                 architecturespecific=False,
-                                 summary=None, description=None,
-                                 shlibdeps=None, depends=None,
-                                 recommends=None, suggests=None,
-                                 conflicts=None, replaces=None,
-                                 provides=None, pre_depends=None,
-                                 enhances=None, breaks=None,
-                                 essential=False, installed_size=None,
-                                 date_created=None, debug_package=None,
-                                 homepage=None, user_defined_fields=None):
+        return ProxyFactory(
+            BinaryPackageFile(
+                binarypackagerelease=binarypackagerelease,
+                libraryfile=library_file,
+                filetype=filetype,
+            )
+        )
+
+    def makeBinaryPackageRelease(
+        self,
+        binarypackagename=None,
+        version=None,
+        build=None,
+        ci_build=None,
+        binpackageformat=None,
+        component=None,
+        section_name=None,
+        priority=None,
+        architecturespecific=False,
+        summary=None,
+        description=None,
+        shlibdeps=None,
+        depends=None,
+        recommends=None,
+        suggests=None,
+        conflicts=None,
+        replaces=None,
+        provides=None,
+        pre_depends=None,
+        enhances=None,
+        breaks=None,
+        essential=False,
+        installed_size=None,
+        date_created=None,
+        debug_package=None,
+        homepage=None,
+        user_defined_fields=None,
+    ):
         """Make a `BinaryPackageRelease`."""
         if build is None and ci_build is None:
             build = self.makeBinaryPackageBuild()
         if binarypackagename is None or isinstance(binarypackagename, str):
             binarypackagename = self.getOrMakeBinaryPackageName(
-                binarypackagename)
+                binarypackagename
+            )
         if version is None and build is not None:
             version = build.source_package_release.version
         if binpackageformat is None:
@@ -4345,88 +5303,111 @@ class LaunchpadObjectFactory(ObjectFactory):
             "installedsize": installed_size,
             "homepage": homepage,
             "user_defined_fields": user_defined_fields,
-            }
+        }
         if build is not None:
-            kwargs.update({
-                "component": component,
-                "section": section,
-                "priority": priority,
-                "shlibdeps": shlibdeps,
-                "depends": depends,
-                "recommends": recommends,
-                "suggests": suggests,
-                "conflicts": conflicts,
-                "replaces": replaces,
-                "provides": provides,
-                "pre_depends": pre_depends,
-                "enhances": enhances,
-                "breaks": breaks,
-                "essential": essential,
-                "debug_package": debug_package,
-                })
+            kwargs.update(
+                {
+                    "component": component,
+                    "section": section,
+                    "priority": priority,
+                    "shlibdeps": shlibdeps,
+                    "depends": depends,
+                    "recommends": recommends,
+                    "suggests": suggests,
+                    "conflicts": conflicts,
+                    "replaces": replaces,
+                    "provides": provides,
+                    "pre_depends": pre_depends,
+                    "enhances": enhances,
+                    "breaks": breaks,
+                    "essential": essential,
+                    "debug_package": debug_package,
+                }
+            )
         bpr = (build or ci_build).createBinaryPackageRelease(**kwargs)
         if date_created is not None:
             removeSecurityProxy(bpr).datecreated = date_created
         return bpr
 
-    def makeSigningKey(self, key_type=None, fingerprint=None,
-                       public_key=None, description=None):
-        """Makes a SigningKey (integration with lp-signing)
-        """
+    def makeSigningKey(
+        self,
+        key_type=None,
+        fingerprint=None,
+        public_key=None,
+        description=None,
+    ):
+        """Makes a SigningKey (integration with lp-signing)"""
         if key_type is None:
             key_type = SigningKeyType.UEFI
         if fingerprint is None:
-            fingerprint = self.getUniqueUnicode('fingerprint')
+            fingerprint = self.getUniqueUnicode("fingerprint")
         if public_key is None:
-            public_key = self.getUniqueHexString(64).encode('ASCII')
+            public_key = self.getUniqueHexString(64).encode("ASCII")
         store = IMasterStore(SigningKey)
         signing_key = SigningKey(
-            key_type=key_type, fingerprint=fingerprint, public_key=public_key,
-            description=description)
+            key_type=key_type,
+            fingerprint=fingerprint,
+            public_key=public_key,
+            description=description,
+        )
         store.add(signing_key)
         return signing_key
 
-    def makeArchiveSigningKey(self, archive=None, distro_series=None,
-                              signing_key=None):
+    def makeArchiveSigningKey(
+        self, archive=None, distro_series=None, signing_key=None
+    ):
         if archive is None:
             archive = self.makeArchive()
         if signing_key is None:
             signing_key = self.makeSigningKey()
         return getUtility(IArchiveSigningKeySet).create(
-            archive, distro_series, signing_key)
+            archive, distro_series, signing_key
+        )
 
     def makeSection(self, name=None):
         """Make a `Section`."""
         if name is None:
-            name = self.getUniqueString('section')
+            name = self.getUniqueString("section")
         return getUtility(ISectionSet).ensure(name)
 
-    def makePackageset(self, name=None, description=None, owner=None,
-                       packages=(), distroseries=None, related_set=None):
+    def makePackageset(
+        self,
+        name=None,
+        description=None,
+        owner=None,
+        packages=(),
+        distroseries=None,
+        related_set=None,
+    ):
         """Make an `IPackageset`."""
         if name is None:
-            name = self.getUniqueString('package-set-name')
+            name = self.getUniqueString("package-set-name")
         if description is None:
-            description = self.getUniqueString('package-set-description')
+            description = self.getUniqueString("package-set-description")
         if owner is None:
-            person = self.getUniqueString('package-set-owner')
+            person = self.getUniqueString("package-set-owner")
             owner = self.makePerson(name=person)
         if distroseries is None:
-            distroseries = getUtility(IDistributionSet)[
-                'ubuntu'].currentseries
+            distroseries = getUtility(IDistributionSet)["ubuntu"].currentseries
         techboard = getUtility(ILaunchpadCelebrities).ubuntu_techboard
         ps_set = getUtility(IPackagesetSet)
         package_set = run_with_login(
             techboard.teamowner,
             lambda: ps_set.new(
-                name, description, owner, distroseries, related_set))
+                name, description, owner, distroseries, related_set
+            ),
+        )
         run_with_login(owner, lambda: package_set.add(packages))
         return package_set
 
-    def makeDistroArchSeriesFilter(self, distroarchseries=None,
-                                   packageset=None,
-                                   sense=DistroArchSeriesFilterSense.INCLUDE,
-                                   creator=None, date_created=DEFAULT):
+    def makeDistroArchSeriesFilter(
+        self,
+        distroarchseries=None,
+        packageset=None,
+        sense=DistroArchSeriesFilterSense.INCLUDE,
+        creator=None,
+        date_created=DEFAULT,
+    ):
         """Make a new `DistroArchSeriesFilter`."""
         if distroarchseries is None:
             if packageset is not None:
@@ -4434,21 +5415,28 @@ class LaunchpadObjectFactory(ObjectFactory):
             else:
                 distroseries = None
             distroarchseries = self.makeDistroArchSeries(
-                distroseries=distroseries)
+                distroseries=distroseries
+            )
         if packageset is None:
             packageset = self.makePackageset(
-                distroseries=distroarchseries.distroseries)
+                distroseries=distroarchseries.distroseries
+            )
         if creator is None:
             creator = self.makePerson()
         return DistroArchSeriesFilter(
-            distroarchseries=distroarchseries, packageset=packageset,
-            sense=sense, creator=creator, date_created=date_created)
+            distroarchseries=distroarchseries,
+            packageset=packageset,
+            sense=sense,
+            creator=creator,
+            date_created=date_created,
+        )
 
     def getAnyPocket(self):
         return PackagePublishingPocket.BACKPORTS
 
-    def makeSuiteSourcePackage(self, distroseries=None,
-                               sourcepackagename=None, pocket=None):
+    def makeSuiteSourcePackage(
+        self, distroseries=None, sourcepackagename=None, pocket=None
+    ):
         if distroseries is None:
             distroseries = self.makeDistroSeries()
         if pocket is None:
@@ -4456,16 +5444,20 @@ class LaunchpadObjectFactory(ObjectFactory):
         # Make sure we have a real sourcepackagename object.
         if sourcepackagename is None or isinstance(sourcepackagename, str):
             sourcepackagename = self.getOrMakeSourcePackageName(
-                sourcepackagename)
+                sourcepackagename
+            )
         return ProxyFactory(
-            SuiteSourcePackage(distroseries, pocket, sourcepackagename))
+            SuiteSourcePackage(distroseries, pocket, sourcepackagename)
+        )
 
-    def makeDistributionSourcePackage(self, sourcepackagename=None,
-                                      distribution=None, with_db=False):
+    def makeDistributionSourcePackage(
+        self, sourcepackagename=None, distribution=None, with_db=False
+    ):
         # Make sure we have a real sourcepackagename object.
         if sourcepackagename is None or isinstance(sourcepackagename, str):
             sourcepackagename = self.getOrMakeSourcePackageName(
-                sourcepackagename)
+                sourcepackagename
+            )
         if distribution is None:
             distribution = self.makeDistribution()
         package = distribution.getSourcePackage(sourcepackagename)
@@ -4477,31 +5469,47 @@ class LaunchpadObjectFactory(ObjectFactory):
                 naked_package._new(distribution, sourcepackagename, False)
         return package
 
-    def makeDSPCache(self, distroseries=None, sourcepackagename=None,
-                     official=True, binary_names=None, archive=None):
+    def makeDSPCache(
+        self,
+        distroseries=None,
+        sourcepackagename=None,
+        official=True,
+        binary_names=None,
+        archive=None,
+    ):
         if distroseries is None:
             distroseries = self.makeDistroSeries()
         dsp = self.makeDistributionSourcePackage(
             distribution=distroseries.distribution,
-            sourcepackagename=sourcepackagename, with_db=official)
+            sourcepackagename=sourcepackagename,
+            with_db=official,
+        )
         if archive is None:
             archive = dsp.distribution.main_archive
         if official:
             self.makeSourcePackagePublishingHistory(
                 distroseries=distroseries,
                 sourcepackagename=dsp.sourcepackagename,
-                archive=archive)
-        with dbuser('statistician'):
+                archive=archive,
+            )
+        with dbuser("statistician"):
             DistributionSourcePackageCache(
                 distribution=dsp.distribution,
                 sourcepackagename=dsp.sourcepackagename,
                 archive=archive,
                 name=dsp.sourcepackagename.name,
-                binpkgnames=binary_names)
+                binpkgnames=binary_names,
+            )
         return dsp
 
-    def makeEmailMessage(self, body=None, sender=None, to=None,
-                         attachments=None, encode_attachments=False):
+    def makeEmailMessage(
+        self,
+        body=None,
+        sender=None,
+        to=None,
+        attachments=None,
+        encode_attachments=False,
+    ):
         """Make an email message with possible attachments.
 
         :param attachments: Should be an interable of tuples containing
@@ -4510,16 +5518,16 @@ class LaunchpadObjectFactory(ObjectFactory):
         if sender is None:
             sender = self.makePerson()
         if body is None:
-            body = self.getUniqueString('body')
+            body = self.getUniqueString("body")
         if to is None:
             to = self.getUniqueEmailAddress()
 
         msg = MIMEMultipart()
-        msg['Message-Id'] = make_msgid('launchpad')
-        msg['Date'] = formatdate()
-        msg['To'] = to
-        msg['From'] = removeSecurityProxy(sender).preferredemail.email
-        msg['Subject'] = 'Sample'
+        msg["Message-Id"] = make_msgid("launchpad")
+        msg["Date"] = formatdate()
+        msg["To"] = to
+        msg["From"] = removeSecurityProxy(sender).preferredemail.email
+        msg["Subject"] = "Sample"
 
         if attachments is None:
             msg.set_payload(body)
@@ -4528,9 +5536,10 @@ class LaunchpadObjectFactory(ObjectFactory):
             for filename, content_type, payload in attachments:
                 attachment = EmailMessage()
                 attachment.set_payload(payload)
-                attachment['Content-Type'] = content_type
-                attachment['Content-Disposition'] = (
-                    'attachment; filename="%s"' % filename)
+                attachment["Content-Type"] = content_type
+                attachment["Content-Disposition"] = (
+                    'attachment; filename="%s"' % filename
+                )
                 if encode_attachments:
                     encode_base64(attachment)
                 msg.attach(attachment)
@@ -4550,36 +5559,46 @@ class LaunchpadObjectFactory(ObjectFactory):
             parameters = [MP(keydata.RSAData[param]) for param in ("e", "n")]
         elif key_type == "ssh-dss":
             parameters = [
-                MP(keydata.DSAData[param]) for param in ("p", "q", "g", "y")]
+                MP(keydata.DSAData[param]) for param in ("p", "q", "g", "y")
+            ]
         elif key_type.startswith("ecdsa-sha2-"):
-            curve = key_type[len("ecdsa-sha2-"):]
+            curve = key_type[len("ecdsa-sha2-") :]
             key_size, curve_data = {
                 "nistp256": (256, keydata.ECDatanistp256),
                 "nistp384": (384, keydata.ECDatanistp384),
                 "nistp521": (521, keydata.ECDatanistp521),
-                }.get(curve, (None, None))
+            }.get(curve, (None, None))
             if curve_data is not None:
                 key_byte_length = (key_size + 7) // 8
                 parameters = [
                     NS(curve_data["curve"][-8:]),
-                    NS(b"\x04" +
-                       int_to_bytes(curve_data["x"], key_byte_length) +
-                       int_to_bytes(curve_data["y"], key_byte_length)),
-                    ]
+                    NS(
+                        b"\x04"
+                        + int_to_bytes(curve_data["x"], key_byte_length)
+                        + int_to_bytes(curve_data["y"], key_byte_length)
+                    ),
+                ]
         elif key_type == "ssh-ed25519":
             parameters = [NS(keydata.Ed25519Data["a"])]
         if parameters is None:
             raise AssertionError(
-                "key_type must be a member of SSH_TEXT_TO_KEY_TYPE, not %r" %
-                key_type)
+                "key_type must be a member of SSH_TEXT_TO_KEY_TYPE, not %r"
+                % key_type
+            )
         key_text = base64.b64encode(
-            NS(key_type) + b"".join(parameters)).decode("ASCII")
+            NS(key_type) + b"".join(parameters)
+        ).decode("ASCII")
         if comment is None:
             comment = self.getUniqueString()
         return "%s %s %s" % (key_type, key_text, comment)
 
-    def makeSSHKey(self, person=None, key_type="ssh-rsa",
-                   send_notification=True, comment=None):
+    def makeSSHKey(
+        self,
+        person=None,
+        key_type="ssh-rsa",
+        send_notification=True,
+        comment=None,
+    ):
         """Create a new SSHKey.
 
         :param person: If specified, the person to attach the key to. If
@@ -4594,14 +5613,16 @@ class LaunchpadObjectFactory(ObjectFactory):
             person = self.makePerson()
         public_key = self.makeSSHKeyText(key_type=key_type, comment=comment)
         return getUtility(ISSHKeySet).new(
-            person, public_key, send_notification=send_notification)
+            person, public_key, send_notification=send_notification
+        )
 
     def makeBlob(self, blob=None, expires=None, blob_file=None):
         """Create a new TemporaryFileStorage BLOB."""
         if blob_file is not None:
             blob_path = os.path.join(
-                config.root, 'lib/lp/bugs/tests/testfiles', blob_file)
-            with open(blob_path, 'rb') as blob_file:
+                config.root, "lib/lp/bugs/tests/testfiles", blob_file
+            )
+            with open(blob_path, "rb") as blob_file:
                 blob = blob_file.read()
         if blob is None:
             blob = self.getUniqueBytes()
@@ -4619,7 +5640,8 @@ class LaunchpadObjectFactory(ObjectFactory):
         job = getUtility(IProcessApportBlobJobSource).create(blob)
         job.job.start()
         removeSecurityProxy(job).metadata = {
-            'processed_data': FileBugData(**metadata).asDict()}
+            "processed_data": FileBugData(**metadata).asDict()
+        }
         job.job.complete()
         return blob
 
@@ -4627,15 +5649,26 @@ class LaunchpadObjectFactory(ObjectFactory):
         if person is None:
             person = self.makePerson()
         from lp.testing.layers import BaseLayer
+
         launchpad = launchpadlib_for(
-            "test", person, service_root=BaseLayer.appserver_root_url("api"),
-            version=version)
+            "test",
+            person,
+            service_root=BaseLayer.appserver_root_url("api"),
+            version=version,
+        )
         login_person(person)
         return launchpad
 
-    def makePackageDiff(self, from_source=None, to_source=None,
-                        requester=None, status=None, date_fulfilled=None,
-                        diff_content=None, diff_filename=None):
+    def makePackageDiff(
+        self,
+        from_source=None,
+        to_source=None,
+        requester=None,
+        status=None,
+        date_fulfilled=None,
+        diff_content=None,
+        diff_filename=None,
+    ):
         """Create a new `PackageDiff`."""
         if from_source is None:
             from_source = self.makeSourcePackageRelease()
@@ -4650,12 +5683,18 @@ class LaunchpadObjectFactory(ObjectFactory):
         if diff_content is None:
             diff_content = self.getUniqueBytes("packagediff")
         lfa = self.makeLibraryFileAlias(
-            filename=diff_filename, content=diff_content)
+            filename=diff_filename, content=diff_content
+        )
         package_diff = ProxyFactory(
             PackageDiff(
-                requester=requester, from_source=from_source,
-                to_source=to_source, date_fulfilled=date_fulfilled,
-                status=status, diff_content=lfa))
+                requester=requester,
+                from_source=from_source,
+                to_source=to_source,
+                date_fulfilled=date_fulfilled,
+                status=status,
+                diff_content=lfa,
+            )
+        )
         IStore(package_diff).flush()
         return package_diff
 
@@ -4664,12 +5703,16 @@ class LaunchpadObjectFactory(ObjectFactory):
         if key is None:
             key = self.getUniqueUnicode("oauthconsumerkey")
         if secret is None:
-            secret = ''
+            secret = ""
         return getUtility(IOAuthConsumerSet).new(key, secret)
 
-    def makeOAuthRequestToken(self, consumer=None, date_created=None,
-                              reviewed_by=None,
-                              access_level=OAuthPermission.READ_PUBLIC):
+    def makeOAuthRequestToken(
+        self,
+        consumer=None,
+        date_created=None,
+        reviewed_by=None,
+        access_level=OAuthPermission.READ_PUBLIC,
+    ):
         """Create a (possibly reviewed) OAuth request token."""
         if consumer is None:
             consumer = self.makeOAuthConsumer()
@@ -4686,17 +5729,29 @@ class LaunchpadObjectFactory(ObjectFactory):
             unwrapped_token.date_created = date_created
         return token
 
-    def makeOAuthAccessToken(self, consumer=None, owner=None,
-                             access_level=OAuthPermission.READ_PUBLIC):
+    def makeOAuthAccessToken(
+        self,
+        consumer=None,
+        owner=None,
+        access_level=OAuthPermission.READ_PUBLIC,
+    ):
         """Create an OAuth access token."""
         if owner is None:
             owner = self.makePerson()
         request_token = self.makeOAuthRequestToken(
-            consumer, reviewed_by=owner, access_level=access_level)
+            consumer, reviewed_by=owner, access_level=access_level
+        )
         return request_token.createAccessToken()
 
-    def makeAccessToken(self, secret=None, owner=None, description=None,
-                        target=None, scopes=None, date_expires=None):
+    def makeAccessToken(
+        self,
+        secret=None,
+        owner=None,
+        description=None,
+        target=None,
+        scopes=None,
+        date_expires=None,
+    ):
         """Create a personal access token.
 
         :return: A tuple of the secret for the new token and the token
@@ -4713,30 +5768,40 @@ class LaunchpadObjectFactory(ObjectFactory):
         if scopes is None:
             scopes = []
         token = getUtility(IAccessTokenSet).new(
-            secret, owner, description, target, scopes,
-            date_expires=date_expires)
+            secret,
+            owner,
+            description,
+            target,
+            scopes,
+            date_expires=date_expires,
+        )
         IStore(token).flush()
         return secret, token
 
-    def makeCVE(self, sequence, description=None,
-                cvestate=CveStatus.CANDIDATE,
-                date_made_public=None, discoverer=None,
-                cvss=None):
+    def makeCVE(
+        self,
+        sequence,
+        description=None,
+        cvestate=CveStatus.CANDIDATE,
+        date_made_public=None,
+        discoverer=None,
+        cvss=None,
+    ):
         """Create a new CVE record."""
         if description is None:
             description = self.getUniqueUnicode()
 
         return getUtility(ICveSet).new(
-            sequence,
-            description,
-            cvestate,
-            date_made_public,
-            discoverer,
-            cvss
+            sequence, description, cvestate, date_made_public, discoverer, cvss
         )
 
-    def makePublisherConfig(self, distribution=None, root_dir=None,
-                            base_url=None, copy_base_url=None):
+    def makePublisherConfig(
+        self,
+        distribution=None,
+        root_dir=None,
+        base_url=None,
+        copy_base_url=None,
+    ):
         """Create a new `PublisherConfig` record."""
         if distribution is None:
             distribution = self.makeDistribution()
@@ -4747,16 +5812,24 @@ class LaunchpadObjectFactory(ObjectFactory):
         if copy_base_url is None:
             copy_base_url = self.getUniqueUnicode()
         return getUtility(IPublisherConfigSet).new(
-            distribution, root_dir, base_url, copy_base_url)
+            distribution, root_dir, base_url, copy_base_url
+        )
 
     def makePlainPackageCopyJob(
-        self, package_name=None, package_version=None, source_archive=None,
-        target_archive=None, target_distroseries=None, target_pocket=None,
-        requester=None, include_binaries=False):
+        self,
+        package_name=None,
+        package_version=None,
+        source_archive=None,
+        target_archive=None,
+        target_distroseries=None,
+        target_pocket=None,
+        requester=None,
+        include_binaries=False,
+    ):
         """Create a new `PlainPackageCopyJob`."""
         if package_name is None and package_version is None:
             package_name = self.makeSourcePackageName().name
-            package_version = str(self.getUniqueInteger()) + 'version'
+            package_version = str(self.getUniqueInteger()) + "version"
         if source_archive is None:
             source_archive = self.makeArchive()
         if target_archive is None:
@@ -4768,14 +5841,22 @@ class LaunchpadObjectFactory(ObjectFactory):
         if requester is None:
             requester = self.makePerson()
         return getUtility(IPlainPackageCopyJobSource).create(
-            package_name, source_archive, target_archive,
-            target_distroseries, target_pocket,
-            package_version=package_version, requester=requester,
-            include_binaries=include_binaries)
-
-    def makeAccessPolicy(self, pillar=None,
-                         type=InformationType.PROPRIETARY,
-                         check_existing=False):
+            package_name,
+            source_archive,
+            target_archive,
+            target_distroseries,
+            target_pocket,
+            package_version=package_version,
+            requester=requester,
+            include_binaries=include_binaries,
+        )
+
+    def makeAccessPolicy(
+        self,
+        pillar=None,
+        type=InformationType.PROPRIETARY,
+        check_existing=False,
+    ):
         if pillar is None:
             pillar = self.makeProduct()
         policy_source = getUtility(IAccessPolicySource)
@@ -4798,11 +5879,13 @@ class LaunchpadObjectFactory(ObjectFactory):
         if policy is None:
             policy = self.makeAccessPolicy()
         [link] = getUtility(IAccessPolicyArtifactSource).create(
-            [(artifact, policy)])
+            [(artifact, policy)]
+        )
         return link
 
-    def makeAccessArtifactGrant(self, artifact=None, grantee=None,
-                                grantor=None, concrete_artifact=None):
+    def makeAccessArtifactGrant(
+        self, artifact=None, grantee=None, grantor=None, concrete_artifact=None
+    ):
         if artifact is None:
             artifact = self.makeAccessArtifact(concrete_artifact)
         if grantee is None:
@@ -4810,7 +5893,8 @@ class LaunchpadObjectFactory(ObjectFactory):
         if grantor is None:
             grantor = self.makePerson()
         [grant] = getUtility(IAccessArtifactGrantSource).grant(
-            [(artifact, grantee, grantor)])
+            [(artifact, grantee, grantor)]
+        )
         return grant
 
     def makeAccessPolicyGrant(self, policy=None, grantee=None, grantor=None):
@@ -4821,7 +5905,8 @@ class LaunchpadObjectFactory(ObjectFactory):
         if grantor is None:
             grantor = self.makePerson()
         [grant] = getUtility(IAccessPolicyGrantSource).grant(
-            [(policy, grantee, grantor)])
+            [(policy, grantee, grantor)]
+        )
         return grant
 
     def makeFakeFileUpload(self, filename=None, content=None):
@@ -4836,13 +5921,14 @@ class LaunchpadObjectFactory(ObjectFactory):
         fileupload = BytesIO(content)
         fileupload.filename = filename
         fileupload.headers = {
-            'Content-Type': 'text/plain; charset=utf-8',
-            'Content-Disposition': 'attachment; filename="%s"' % filename
-            }
+            "Content-Type": "text/plain; charset=utf-8",
+            "Content-Disposition": 'attachment; filename="%s"' % filename,
+        }
         return fileupload
 
-    def makeCommercialSubscription(self, pillar, expired=False,
-                                   voucher_id='new'):
+    def makeCommercialSubscription(
+        self, pillar, expired=False, voucher_id="new"
+    ):
         """Create a commercial subscription for the given pillar."""
         if IProduct.providedBy(pillar):
             find_kwargs = {"product": pillar}
@@ -4850,10 +5936,15 @@ class LaunchpadObjectFactory(ObjectFactory):
             find_kwargs = {"distribution": pillar}
         else:
             raise AssertionError("Unknown pillar: %r" % pillar)
-        if IStore(CommercialSubscription).find(
-                CommercialSubscription, **find_kwargs).one() is not None:
+        if (
+            IStore(CommercialSubscription)
+            .find(CommercialSubscription, **find_kwargs)
+            .one()
+            is not None
+        ):
             raise AssertionError(
-                "The pillar under test already has a CommercialSubscription.")
+                "The pillar under test already has a CommercialSubscription."
+            )
         if expired:
             expiry = datetime.now(pytz.UTC) - timedelta(days=1)
         else:
@@ -4865,7 +5956,8 @@ class LaunchpadObjectFactory(ObjectFactory):
             registrant=pillar.owner,
             purchaser=pillar.owner,
             sales_system_id=voucher_id,
-            whiteboard='')
+            whiteboard="",
+        )
         del get_property_cache(pillar).commercial_subscription
         return commercial_subscription
 
@@ -4873,11 +5965,20 @@ class LaunchpadObjectFactory(ObjectFactory):
         """Give 'person' a commercial subscription."""
         product = self.makeProduct(owner=person)
         self.makeCommercialSubscription(
-            product, voucher_id=self.getUniqueUnicode())
+            product, voucher_id=self.getUniqueUnicode()
+        )
 
-    def makeLiveFS(self, registrant=None, owner=None, distroseries=None,
-                   name=None, metadata=None, require_virtualized=True,
-                   keep_binary_files_days=1, date_created=DEFAULT):
+    def makeLiveFS(
+        self,
+        registrant=None,
+        owner=None,
+        distroseries=None,
+        name=None,
+        metadata=None,
+        require_virtualized=True,
+        keep_binary_files_days=1,
+        date_created=DEFAULT,
+    ):
         """Make a new LiveFS."""
         if registrant is None:
             registrant = self.makePerson()
@@ -4890,18 +5991,35 @@ class LaunchpadObjectFactory(ObjectFactory):
         if metadata is None:
             metadata = {}
         livefs = getUtility(ILiveFSSet).new(
-            registrant, owner, distroseries, name, metadata,
+            registrant,
+            owner,
+            distroseries,
+            name,
+            metadata,
             require_virtualized=require_virtualized,
             keep_binary_files_days=keep_binary_files_days,
-            date_created=date_created)
+            date_created=date_created,
+        )
         IStore(livefs).flush()
         return livefs
 
-    def makeLiveFSBuild(self, requester=None, registrant=None, livefs=None,
-                        archive=None, distroarchseries=None, pocket=None,
-                        unique_key=None, metadata_override=None, version=None,
-                        date_created=DEFAULT, status=BuildStatus.NEEDSBUILD,
-                        builder=None, duration=None, **kwargs):
+    def makeLiveFSBuild(
+        self,
+        requester=None,
+        registrant=None,
+        livefs=None,
+        archive=None,
+        distroarchseries=None,
+        pocket=None,
+        unique_key=None,
+        metadata_override=None,
+        version=None,
+        date_created=DEFAULT,
+        status=BuildStatus.NEEDSBUILD,
+        builder=None,
+        duration=None,
+        **kwargs
+    ):
         """Make a new LiveFSBuild."""
         if requester is None:
             requester = self.makePerson()
@@ -4913,34 +6031,49 @@ class LaunchpadObjectFactory(ObjectFactory):
                 distroseries = distroarchseries.distroseries
             elif archive is not None:
                 distroseries = self.makeDistroSeries(
-                    distribution=archive.distribution)
+                    distribution=archive.distribution
+                )
             else:
                 distroseries = None
             if registrant is None:
                 registrant = requester
             livefs = self.makeLiveFS(
-                registrant=registrant, distroseries=distroseries, **kwargs)
+                registrant=registrant, distroseries=distroseries, **kwargs
+            )
         if archive is None:
             archive = livefs.distro_series.main_archive
         if distroarchseries is None:
             distroarchseries = self.makeDistroArchSeries(
-                distroseries=livefs.distro_series)
+                distroseries=livefs.distro_series
+            )
         if pocket is None:
             pocket = PackagePublishingPocket.RELEASE
         livefsbuild = getUtility(ILiveFSBuildSet).new(
-            requester, livefs, archive, distroarchseries, pocket,
-            unique_key=unique_key, metadata_override=metadata_override,
-            version=version, date_created=date_created)
+            requester,
+            livefs,
+            archive,
+            distroarchseries,
+            pocket,
+            unique_key=unique_key,
+            metadata_override=metadata_override,
+            version=version,
+            date_created=date_created,
+        )
         if duration is not None:
             removeSecurityProxy(livefsbuild).updateStatus(
-                BuildStatus.BUILDING, builder=builder,
-                date_started=livefsbuild.date_created)
+                BuildStatus.BUILDING,
+                builder=builder,
+                date_started=livefsbuild.date_created,
+            )
             removeSecurityProxy(livefsbuild).updateStatus(
-                status, builder=builder,
-                date_finished=livefsbuild.date_started + duration)
+                status,
+                builder=builder,
+                date_finished=livefsbuild.date_started + duration,
+            )
         else:
             removeSecurityProxy(livefsbuild).updateStatus(
-                status, builder=builder)
+                status, builder=builder
+            )
         IStore(livefsbuild).flush()
         return livefsbuild
 
@@ -4950,47 +6083,84 @@ class LaunchpadObjectFactory(ObjectFactory):
         if libraryfile is None:
             libraryfile = self.makeLibraryFileAlias()
         return ProxyFactory(
-            LiveFSFile(livefsbuild=livefsbuild, libraryfile=libraryfile))
+            LiveFSFile(livefsbuild=livefsbuild, libraryfile=libraryfile)
+        )
 
-    def makeWebhook(self, target=None, delivery_url=None, secret=None,
-                    active=True, event_types=None):
+    def makeWebhook(
+        self,
+        target=None,
+        delivery_url=None,
+        secret=None,
+        active=True,
+        event_types=None,
+    ):
         if target is None:
             target = self.makeGitRepository()
         if delivery_url is None:
             delivery_url = self.getUniqueURL()
         return getUtility(IWebhookSet).new(
-            target, self.makePerson(), delivery_url, event_types or [],
-            active, secret)
-
-    def makeSnap(self, registrant=None, owner=None, distroseries=_DEFAULT,
-                 name=None, branch=None, git_ref=None, auto_build=False,
-                 auto_build_archive=None, auto_build_pocket=None,
-                 auto_build_channels=None, is_stale=None,
-                 require_virtualized=True, processors=None,
-                 date_created=DEFAULT, private=None, information_type=None,
-                 allow_internet=True, build_source_tarball=False,
-                 store_upload=False, store_series=None, store_name=None,
-                 store_secrets=None, store_channels=None, project=_DEFAULT):
+            target,
+            self.makePerson(),
+            delivery_url,
+            event_types or [],
+            active,
+            secret,
+        )
+
+    def makeSnap(
+        self,
+        registrant=None,
+        owner=None,
+        distroseries=_DEFAULT,
+        name=None,
+        branch=None,
+        git_ref=None,
+        auto_build=False,
+        auto_build_archive=None,
+        auto_build_pocket=None,
+        auto_build_channels=None,
+        is_stale=None,
+        require_virtualized=True,
+        processors=None,
+        date_created=DEFAULT,
+        private=None,
+        information_type=None,
+        allow_internet=True,
+        build_source_tarball=False,
+        store_upload=False,
+        store_series=None,
+        store_name=None,
+        store_secrets=None,
+        store_channels=None,
+        project=_DEFAULT,
+    ):
         """Make a new Snap."""
         assert information_type is None or private is None
         if information_type is None:
             # Defaults to public information type, unless "private" flag was
             # passed.
-            information_type = (InformationType.PUBLIC if not private
-                                else InformationType.PROPRIETARY)
+            information_type = (
+                InformationType.PUBLIC
+                if not private
+                else InformationType.PROPRIETARY
+            )
         if private is None:
             private = information_type not in PUBLIC_INFORMATION_TYPES
         if registrant is None:
             registrant = self.makePerson()
         if owner is None:
             is_private_snap = (
-                private or information_type not in PUBLIC_INFORMATION_TYPES)
+                private or information_type not in PUBLIC_INFORMATION_TYPES
+            )
             # Private snaps cannot be owned by non-moderated teams.
             membership_policy = (
-                TeamMembershipPolicy.OPEN if not is_private_snap
-                else TeamMembershipPolicy.MODERATED)
+                TeamMembershipPolicy.OPEN
+                if not is_private_snap
+                else TeamMembershipPolicy.MODERATED
+            )
             owner = self.makeTeam(
-                registrant, membership_policy=membership_policy)
+                registrant, membership_policy=membership_policy
+            )
         if distroseries is _DEFAULT:
             distroseries = self.makeDistroSeries()
         if name is None:
@@ -5000,42 +6170,63 @@ class LaunchpadObjectFactory(ObjectFactory):
         if auto_build:
             if auto_build_archive is None:
                 auto_build_archive = self.makeArchive(
-                    distribution=distroseries.distribution, owner=owner)
+                    distribution=distroseries.distribution, owner=owner
+                )
             if auto_build_pocket is None:
                 auto_build_pocket = PackagePublishingPocket.UPDATES
         if private and project is _DEFAULT:
             # If we are creating a private snap and didn't explicitly set a
             # pillar for it, we must create a pillar.
             branch_sharing = (
-                BranchSharingPolicy.PUBLIC_OR_PROPRIETARY if not private
-                else BranchSharingPolicy.PROPRIETARY)
+                BranchSharingPolicy.PUBLIC_OR_PROPRIETARY
+                if not private
+                else BranchSharingPolicy.PROPRIETARY
+            )
             project = self.makeProduct(
-                owner=registrant, registrant=registrant,
+                owner=registrant,
+                registrant=registrant,
                 information_type=information_type,
-                branch_sharing_policy=branch_sharing)
+                branch_sharing_policy=branch_sharing,
+            )
         if project is _DEFAULT:
             project = None
         snap = getUtility(ISnapSet).new(
-            registrant, owner, distroseries, name,
-            require_virtualized=require_virtualized, processors=processors,
-            date_created=date_created, branch=branch, git_ref=git_ref,
-            auto_build=auto_build, auto_build_archive=auto_build_archive,
+            registrant,
+            owner,
+            distroseries,
+            name,
+            require_virtualized=require_virtualized,
+            processors=processors,
+            date_created=date_created,
+            branch=branch,
+            git_ref=git_ref,
+            auto_build=auto_build,
+            auto_build_archive=auto_build_archive,
             auto_build_pocket=auto_build_pocket,
             auto_build_channels=auto_build_channels,
             information_type=information_type,
             allow_internet=allow_internet,
             build_source_tarball=build_source_tarball,
-            store_upload=store_upload, store_series=store_series,
-            store_name=store_name, store_secrets=store_secrets,
-            store_channels=store_channels, project=project)
+            store_upload=store_upload,
+            store_series=store_series,
+            store_name=store_name,
+            store_secrets=store_secrets,
+            store_channels=store_channels,
+            project=project,
+        )
         if is_stale is not None:
             removeSecurityProxy(snap).is_stale = is_stale
         IStore(snap).flush()
         return snap
 
-    def makeSnapBuildRequest(self, snap=None, requester=None, archive=None,
-                             pocket=PackagePublishingPocket.UPDATES,
-                             channels=None):
+    def makeSnapBuildRequest(
+        self,
+        snap=None,
+        requester=None,
+        archive=None,
+        pocket=PackagePublishingPocket.UPDATES,
+        channels=None,
+    ):
         """Make a new SnapBuildRequest."""
         if snap is None:
             snap = self.makeSnap()
@@ -5044,14 +6235,27 @@ class LaunchpadObjectFactory(ObjectFactory):
         if archive is None:
             archive = snap.distro_series.main_archive
         return snap.requestBuilds(
-            requester, archive, pocket, channels=channels)
-
-    def makeSnapBuild(self, requester=None, registrant=None, snap=None,
-                      archive=None, distroarchseries=None, pocket=None,
-                      snap_base=None, channels=None, date_created=DEFAULT,
-                      build_request=None, status=BuildStatus.NEEDSBUILD,
-                      builder=None, duration=None, target_architectures=None,
-                      **kwargs):
+            requester, archive, pocket, channels=channels
+        )
+
+    def makeSnapBuild(
+        self,
+        requester=None,
+        registrant=None,
+        snap=None,
+        archive=None,
+        distroarchseries=None,
+        pocket=None,
+        snap_base=None,
+        channels=None,
+        date_created=DEFAULT,
+        build_request=None,
+        status=BuildStatus.NEEDSBUILD,
+        builder=None,
+        duration=None,
+        target_architectures=None,
+        **kwargs
+    ):
         """Make a new SnapBuild."""
         if requester is None:
             requester = self.makePerson()
@@ -5063,35 +6267,50 @@ class LaunchpadObjectFactory(ObjectFactory):
                 distroseries = distroarchseries.distroseries
             elif archive is not None:
                 distroseries = self.makeDistroSeries(
-                    distribution=archive.distribution)
+                    distribution=archive.distribution
+                )
             else:
                 distroseries = _DEFAULT
             if registrant is None:
                 registrant = requester
             snap = self.makeSnap(
-                registrant=registrant, distroseries=distroseries, **kwargs)
+                registrant=registrant, distroseries=distroseries, **kwargs
+            )
         if archive is None:
             archive = snap.distro_series.main_archive
         if distroarchseries is None:
             distroarchseries = self.makeDistroArchSeries(
-                distroseries=snap.distro_series)
+                distroseries=snap.distro_series
+            )
         if pocket is None:
             pocket = PackagePublishingPocket.UPDATES
         snapbuild = getUtility(ISnapBuildSet).new(
-            requester, snap, archive, distroarchseries, pocket,
-            snap_base=snap_base, channels=channels, date_created=date_created,
+            requester,
+            snap,
+            archive,
+            distroarchseries,
+            pocket,
+            snap_base=snap_base,
+            channels=channels,
+            date_created=date_created,
             build_request=build_request,
-            target_architectures=target_architectures)
+            target_architectures=target_architectures,
+        )
         if duration is not None:
             removeSecurityProxy(snapbuild).updateStatus(
-                BuildStatus.BUILDING, builder=builder,
-                date_started=snapbuild.date_created)
+                BuildStatus.BUILDING,
+                builder=builder,
+                date_started=snapbuild.date_created,
+            )
             removeSecurityProxy(snapbuild).updateStatus(
-                status, builder=builder,
-                date_finished=snapbuild.date_started + duration)
+                status,
+                builder=builder,
+                date_finished=snapbuild.date_started + duration,
+            )
         else:
             removeSecurityProxy(snapbuild).updateStatus(
-                status, builder=builder)
+                status, builder=builder
+            )
         IStore(snapbuild).flush()
         return snapbuild
 
@@ -5101,13 +6320,20 @@ class LaunchpadObjectFactory(ObjectFactory):
         if libraryfile is None:
             libraryfile = self.makeLibraryFileAlias()
         return ProxyFactory(
-            SnapFile(snapbuild=snapbuild, libraryfile=libraryfile))
+            SnapFile(snapbuild=snapbuild, libraryfile=libraryfile)
+        )
 
-    def makeSnappySeries(self, registrant=None, name=None, display_name=None,
-                         status=SeriesStatus.DEVELOPMENT,
-                         preferred_distro_series=None, date_created=DEFAULT,
-                         usable_distro_series=None,
-                         can_infer_distro_series=False):
+    def makeSnappySeries(
+        self,
+        registrant=None,
+        name=None,
+        display_name=None,
+        status=SeriesStatus.DEVELOPMENT,
+        preferred_distro_series=None,
+        date_created=DEFAULT,
+        usable_distro_series=None,
+        can_infer_distro_series=False,
+    ):
         """Make a new SnappySeries."""
         if registrant is None:
             registrant = self.makePerson()
@@ -5115,11 +6341,16 @@ class LaunchpadObjectFactory(ObjectFactory):
             name = self.getUniqueString("snappy-series-name")
         if display_name is None:
             display_name = SPACE.join(
-                word.capitalize() for word in name.split('-'))
+                word.capitalize() for word in name.split("-")
+            )
         snappy_series = getUtility(ISnappySeriesSet).new(
-            registrant, name, display_name, status,
+            registrant,
+            name,
+            display_name,
+            status,
             preferred_distro_series=preferred_distro_series,
-            date_created=date_created)
+            date_created=date_created,
+        )
         if usable_distro_series is not None:
             snappy_series.usable_distro_series = usable_distro_series
         elif preferred_distro_series is not None:
@@ -5129,9 +6360,16 @@ class LaunchpadObjectFactory(ObjectFactory):
         IStore(snappy_series).flush()
         return snappy_series
 
-    def makeSnapBase(self, registrant=None, name=None, display_name=None,
-                     distro_series=None, build_channels=None, processors=None,
-                     date_created=DEFAULT):
+    def makeSnapBase(
+        self,
+        registrant=None,
+        name=None,
+        display_name=None,
+        distro_series=None,
+        build_channels=None,
+        processors=None,
+        date_created=DEFAULT,
+    ):
         """Make a new SnapBase."""
         if registrant is None:
             registrant = self.makePerson()
@@ -5139,25 +6377,38 @@ class LaunchpadObjectFactory(ObjectFactory):
             name = self.getUniqueString("snap-base-name")
         if display_name is None:
             display_name = SPACE.join(
-                word.capitalize() for word in name.split('-'))
+                word.capitalize() for word in name.split("-")
+            )
         if distro_series is None:
             distro_series = self.makeDistroSeries()
         if build_channels is None:
             build_channels = {"snapcraft": "stable"}
         return getUtility(ISnapBaseSet).new(
-            registrant, name, display_name, distro_series, build_channels,
-            processors=processors, date_created=date_created)
+            registrant,
+            name,
+            display_name,
+            distro_series,
+            build_channels,
+            processors=processors,
+            date_created=date_created,
+        )
 
     def makeOCIProjectName(self, name=None):
         if name is None:
             name = self.getUniqueString("oci-project-name")
         return getUtility(IOCIProjectNameSet).getOrCreateByName(name)
 
-    def makeOCIProject(self, registrant=None, pillar=None,
-                       ociprojectname=None, date_created=DEFAULT,
-                       description=None, bug_reporting_guidelines=None,
-                       bug_reported_acknowledgement=None,
-                       bugfiling_duplicate_search=False):
+    def makeOCIProject(
+        self,
+        registrant=None,
+        pillar=None,
+        ociprojectname=None,
+        date_created=DEFAULT,
+        description=None,
+        bug_reporting_guidelines=None,
+        bug_reported_acknowledgement=None,
+        bugfiling_duplicate_search=False,
+    ):
         """Make a new OCIProject."""
         if registrant is None:
             registrant = self.makePerson()
@@ -5166,14 +6417,24 @@ class LaunchpadObjectFactory(ObjectFactory):
         if ociprojectname is None or isinstance(ociprojectname, str):
             ociprojectname = self.makeOCIProjectName(ociprojectname)
         return getUtility(IOCIProjectSet).new(
-            registrant, pillar, ociprojectname, date_created=date_created,
+            registrant,
+            pillar,
+            ociprojectname,
+            date_created=date_created,
             description=description,
             bug_reporting_guidelines=bug_reporting_guidelines,
             bug_reported_acknowledgement=bug_reported_acknowledgement,
-            bugfiling_duplicate_search=bugfiling_duplicate_search)
+            bugfiling_duplicate_search=bugfiling_duplicate_search,
+        )
 
-    def makeOCIProjectSeries(self, name=None, summary=None, registrant=None,
-                             oci_project=None, **kwargs):
+    def makeOCIProjectSeries(
+        self,
+        name=None,
+        summary=None,
+        registrant=None,
+        oci_project=None,
+        **kwargs
+    ):
         """Make a new OCIProjectSeries attached to an OCIProject."""
         if name is None:
             name = self.getUniqueString("oci-project-series-name")
@@ -5185,12 +6446,23 @@ class LaunchpadObjectFactory(ObjectFactory):
             oci_project = self.makeOCIProject(**kwargs)
         return oci_project.newSeries(name, summary, registrant)
 
-    def makeOCIRecipe(self, name=None, registrant=None, owner=None,
-                      oci_project=None, git_ref=None, description=None,
-                      official=False, require_virtualized=True,
-                      build_file=None, date_created=DEFAULT,
-                      allow_internet=True, build_args=None, build_path=None,
-                      information_type=InformationType.PUBLIC):
+    def makeOCIRecipe(
+        self,
+        name=None,
+        registrant=None,
+        owner=None,
+        oci_project=None,
+        git_ref=None,
+        description=None,
+        official=False,
+        require_virtualized=True,
+        build_file=None,
+        date_created=DEFAULT,
+        allow_internet=True,
+        build_args=None,
+        build_path=None,
+        information_type=InformationType.PUBLIC,
+    ):
         """Make a new OCIRecipe."""
         if name is None:
             name = self.getUniqueString("oci-recipe-name")
@@ -5204,7 +6476,7 @@ class LaunchpadObjectFactory(ObjectFactory):
             oci_project = self.makeOCIProject()
         if git_ref is None:
             component = self.getUniqueUnicode()
-            paths = ['refs/heads/{}-20.04'.format(component)]
+            paths = ["refs/heads/{}-20.04".format(component)]
             [git_ref] = self.makeGitRefs(paths=paths)
         if build_file is None:
             build_file = self.getUniqueUnicode("build_file_for")
@@ -5224,7 +6496,8 @@ class LaunchpadObjectFactory(ObjectFactory):
             date_created=date_created,
             allow_internet=allow_internet,
             build_args=build_args,
-            information_type=information_type)
+            information_type=information_type,
+        )
 
     def makeOCIRecipeArch(self, recipe=None, processor=None):
         """Make a new OCIRecipeArch."""
@@ -5234,10 +6507,19 @@ class LaunchpadObjectFactory(ObjectFactory):
             processor = self.makeProcessor()
         return OCIRecipeArch(recipe, processor)
 
-    def makeOCIRecipeBuild(self, requester=None, registrant=None, recipe=None,
-                           distro_arch_series=None, date_created=DEFAULT,
-                           status=BuildStatus.NEEDSBUILD, builder=None,
-                           duration=None, build_request=None, **kwargs):
+    def makeOCIRecipeBuild(
+        self,
+        requester=None,
+        registrant=None,
+        recipe=None,
+        distro_arch_series=None,
+        date_created=DEFAULT,
+        status=BuildStatus.NEEDSBUILD,
+        builder=None,
+        duration=None,
+        build_request=None,
+        **kwargs
+    ):
         """Make a new OCIRecipeBuild."""
         if requester is None:
             requester = self.makePerson()
@@ -5247,55 +6529,78 @@ class LaunchpadObjectFactory(ObjectFactory):
             else:
                 distribution = None
             distroseries = self.makeDistroSeries(
-                distribution=distribution, status=SeriesStatus.CURRENT)
+                distribution=distribution, status=SeriesStatus.CURRENT
+            )
             processor = getUtility(IProcessorSet).getByName("386")
             distro_arch_series = self.makeDistroArchSeries(
-                distroseries=distroseries, architecturetag="i386",
-                processor=processor)
+                distroseries=distroseries,
+                architecturetag="i386",
+                processor=processor,
+            )
         if recipe is None:
             oci_project = self.makeOCIProject(
-                pillar=distro_arch_series.distroseries.distribution)
+                pillar=distro_arch_series.distroseries.distribution
+            )
             if registrant is None:
                 registrant = requester
             recipe = self.makeOCIRecipe(
-                registrant=registrant, oci_project=oci_project, **kwargs)
+                registrant=registrant, oci_project=oci_project, **kwargs
+            )
         oci_build = getUtility(IOCIRecipeBuildSet).new(
-            requester, recipe, distro_arch_series, date_created, build_request)
+            requester, recipe, distro_arch_series, date_created, build_request
+        )
         if duration is not None:
             removeSecurityProxy(oci_build).updateStatus(
-                BuildStatus.BUILDING, builder=builder,
-                date_started=oci_build.date_created)
+                BuildStatus.BUILDING,
+                builder=builder,
+                date_started=oci_build.date_created,
+            )
             removeSecurityProxy(oci_build).updateStatus(
-                status, builder=builder,
-                date_finished=oci_build.date_started + duration)
+                status,
+                builder=builder,
+                date_finished=oci_build.date_started + duration,
+            )
         else:
             removeSecurityProxy(oci_build).updateStatus(
-                status, builder=builder)
+                status, builder=builder
+            )
         IStore(oci_build).flush()
         return oci_build
 
-    def makeOCIFile(self, build=None, library_file=None,
-                    layer_file_digest=None, content=None, filename=None):
+    def makeOCIFile(
+        self,
+        build=None,
+        library_file=None,
+        layer_file_digest=None,
+        content=None,
+        filename=None,
+    ):
         """Make a new OCIFile."""
         if build is None:
             build = self.makeOCIRecipeBuild()
         if library_file is None:
             library_file = self.makeLibraryFileAlias(
-                content=content, filename=filename)
-        return OCIFile(build=build, library_file=library_file,
-                       layer_file_digest=layer_file_digest)
+                content=content, filename=filename
+            )
+        return OCIFile(
+            build=build,
+            library_file=library_file,
+            layer_file_digest=layer_file_digest,
+        )
 
     def makeOCIRecipeBuildJob(self, build=None):
         store = IStore(OCIRecipeBuildJob)
         if build is None:
             build = self.makeOCIRecipeBuild()
         job = OCIRecipeBuildJob(
-            build, OCIRecipeBuildJobType.REGISTRY_UPLOAD, {})
+            build, OCIRecipeBuildJobType.REGISTRY_UPLOAD, {}
+        )
         store.add(job)
         return job
 
-    def makeOCIRegistryCredentials(self, registrant=None, owner=None, url=None,
-                                   credentials=None):
+    def makeOCIRegistryCredentials(
+        self, registrant=None, owner=None, url=None, credentials=None
+    ):
         """Make a new OCIRegistryCredentials."""
         if registrant is None:
             registrant = self.makePerson()
@@ -5305,14 +6610,19 @@ class LaunchpadObjectFactory(ObjectFactory):
             url = self.getUniqueURL()
         if credentials is None:
             credentials = {
-                'username': self.getUniqueUnicode(),
-                'password': self.getUniqueUnicode()}
+                "username": self.getUniqueUnicode(),
+                "password": self.getUniqueUnicode(),
+            }
         return getUtility(IOCIRegistryCredentialsSet).new(
-            registrant=registrant, owner=owner, url=url,
-            credentials=credentials)
+            registrant=registrant,
+            owner=owner,
+            url=url,
+            credentials=credentials,
+        )
 
-    def makeOCIPushRule(self, recipe=None, registry_credentials=None,
-                        image_name=None):
+    def makeOCIPushRule(
+        self, recipe=None, registry_credentials=None, image_name=None
+    ):
         """Make a new OCIPushRule."""
         if recipe is None:
             recipe = self.makeOCIRecipe()
@@ -5323,16 +6633,29 @@ class LaunchpadObjectFactory(ObjectFactory):
         return getUtility(IOCIPushRuleSet).new(
             recipe=recipe,
             registry_credentials=registry_credentials,
-            image_name=image_name)
-
-    def makeCharmRecipe(self, registrant=None, owner=None, project=None,
-                        name=None, description=None, git_ref=None,
-                        build_path=None, require_virtualized=True,
-                        information_type=InformationType.PUBLIC,
-                        auto_build=False, auto_build_channels=None,
-                        is_stale=None, store_upload=False, store_name=None,
-                        store_secrets=None, store_channels=None,
-                        date_created=DEFAULT):
+            image_name=image_name,
+        )
+
+    def makeCharmRecipe(
+        self,
+        registrant=None,
+        owner=None,
+        project=None,
+        name=None,
+        description=None,
+        git_ref=None,
+        build_path=None,
+        require_virtualized=True,
+        information_type=InformationType.PUBLIC,
+        auto_build=False,
+        auto_build_channels=None,
+        is_stale=None,
+        store_upload=False,
+        store_name=None,
+        store_secrets=None,
+        store_channels=None,
+        date_created=DEFAULT,
+    ):
         """Make a new charm recipe."""
         if registrant is None:
             registrant = self.makePerson()
@@ -5340,37 +6663,55 @@ class LaunchpadObjectFactory(ObjectFactory):
         if owner is None:
             # Private charm recipes cannot be owned by non-moderated teams.
             membership_policy = (
-                TeamMembershipPolicy.OPEN if private
-                else TeamMembershipPolicy.MODERATED)
+                TeamMembershipPolicy.OPEN
+                if private
+                else TeamMembershipPolicy.MODERATED
+            )
             owner = self.makeTeam(
-                registrant, membership_policy=membership_policy)
+                registrant, membership_policy=membership_policy
+            )
         if project is None:
             branch_sharing_policy = (
-                BranchSharingPolicy.PUBLIC if not private
-                else BranchSharingPolicy.PROPRIETARY)
+                BranchSharingPolicy.PUBLIC
+                if not private
+                else BranchSharingPolicy.PROPRIETARY
+            )
             project = self.makeProduct(
-                owner=registrant, registrant=registrant,
+                owner=registrant,
+                registrant=registrant,
                 information_type=information_type,
-                branch_sharing_policy=branch_sharing_policy)
+                branch_sharing_policy=branch_sharing_policy,
+            )
         if name is None:
             name = self.getUniqueUnicode("charm-name")
         if git_ref is None:
             git_ref = self.makeGitRefs()[0]
         recipe = getUtility(ICharmRecipeSet).new(
-            registrant=registrant, owner=owner, project=project, name=name,
-            description=description, git_ref=git_ref, build_path=build_path,
+            registrant=registrant,
+            owner=owner,
+            project=project,
+            name=name,
+            description=description,
+            git_ref=git_ref,
+            build_path=build_path,
             require_virtualized=require_virtualized,
-            information_type=information_type, auto_build=auto_build,
-            auto_build_channels=auto_build_channels, store_upload=store_upload,
-            store_name=store_name, store_secrets=store_secrets,
-            store_channels=store_channels, date_created=date_created)
+            information_type=information_type,
+            auto_build=auto_build,
+            auto_build_channels=auto_build_channels,
+            store_upload=store_upload,
+            store_name=store_name,
+            store_secrets=store_secrets,
+            store_channels=store_channels,
+            date_created=date_created,
+        )
         if is_stale is not None:
             removeSecurityProxy(recipe).is_stale = is_stale
         IStore(recipe).flush()
         return recipe
 
-    def makeCharmRecipeBuildRequest(self, recipe=None, requester=None,
-                                    channels=None, architectures=None):
+    def makeCharmRecipeBuildRequest(
+        self, recipe=None, requester=None, channels=None, architectures=None
+    ):
         """Make a new CharmRecipeBuildRequest."""
         if recipe is None:
             recipe = self.makeCharmRecipe()
@@ -5380,14 +6721,24 @@ class LaunchpadObjectFactory(ObjectFactory):
             else:
                 requester = recipe.owner
         return recipe.requestBuilds(
-            requester, channels=channels, architectures=architectures)
-
-    def makeCharmRecipeBuild(self, registrant=None, recipe=None,
-                             build_request=None, requester=None,
-                             distro_arch_series=None, channels=None,
-                             store_upload_metadata=None, date_created=DEFAULT,
-                             status=BuildStatus.NEEDSBUILD, builder=None,
-                             duration=None, **kwargs):
+            requester, channels=channels, architectures=architectures
+        )
+
+    def makeCharmRecipeBuild(
+        self,
+        registrant=None,
+        recipe=None,
+        build_request=None,
+        requester=None,
+        distro_arch_series=None,
+        channels=None,
+        store_upload_metadata=None,
+        date_created=DEFAULT,
+        status=BuildStatus.NEEDSBUILD,
+        builder=None,
+        duration=None,
+        **kwargs
+    ):
         if recipe is None:
             if registrant is None:
                 if build_request is not None:
@@ -5399,18 +6750,27 @@ class LaunchpadObjectFactory(ObjectFactory):
             distro_arch_series = self.makeDistroArchSeries()
         if build_request is None:
             build_request = self.makeCharmRecipeBuildRequest(
-                recipe=recipe, requester=requester, channels=channels)
+                recipe=recipe, requester=requester, channels=channels
+            )
         build = getUtility(ICharmRecipeBuildSet).new(
-            build_request, recipe, distro_arch_series, channels=channels,
+            build_request,
+            recipe,
+            distro_arch_series,
+            channels=channels,
             store_upload_metadata=store_upload_metadata,
-            date_created=date_created)
+            date_created=date_created,
+        )
         if duration is not None:
             removeSecurityProxy(build).updateStatus(
-                BuildStatus.BUILDING, builder=builder,
-                date_started=build.date_created)
+                BuildStatus.BUILDING,
+                builder=builder,
+                date_started=build.date_created,
+            )
             removeSecurityProxy(build).updateStatus(
-                status, builder=builder,
-                date_finished=build.date_started + duration)
+                status,
+                builder=builder,
+                date_finished=build.date_started + duration,
+            )
         else:
             removeSecurityProxy(build).updateStatus(status, builder=builder)
         IStore(build).flush()
@@ -5423,9 +6783,14 @@ class LaunchpadObjectFactory(ObjectFactory):
             library_file = self.makeLibraryFileAlias()
         return ProxyFactory(CharmFile(build=build, library_file=library_file))
 
-    def makeCharmBase(self, registrant=None, distro_series=None,
-                      build_snap_channels=None, processors=None,
-                      date_created=DEFAULT):
+    def makeCharmBase(
+        self,
+        registrant=None,
+        distro_series=None,
+        build_snap_channels=None,
+        processors=None,
+        date_created=DEFAULT,
+    ):
         """Make a new CharmBase."""
         if registrant is None:
             registrant = self.makePerson()
@@ -5434,13 +6799,24 @@ class LaunchpadObjectFactory(ObjectFactory):
         if build_snap_channels is None:
             build_snap_channels = {"charmcraft": "stable"}
         return getUtility(ICharmBaseSet).new(
-            registrant, distro_series, build_snap_channels,
-            processors=processors, date_created=date_created)
+            registrant,
+            distro_series,
+            build_snap_channels,
+            processors=processors,
+            date_created=date_created,
+        )
 
-    def makeCIBuild(self, git_repository=None, commit_sha1=None,
-                    distro_arch_series=None, stages=None, date_created=DEFAULT,
-                    status=BuildStatus.NEEDSBUILD, builder=None,
-                    duration=None):
+    def makeCIBuild(
+        self,
+        git_repository=None,
+        commit_sha1=None,
+        distro_arch_series=None,
+        stages=None,
+        date_created=DEFAULT,
+        status=BuildStatus.NEEDSBUILD,
+        builder=None,
+        duration=None,
+    ):
         """Make a new `CIBuild`."""
         if git_repository is None:
             git_repository = self.makeGitRepository()
@@ -5451,25 +6827,42 @@ class LaunchpadObjectFactory(ObjectFactory):
         if stages is None:
             stages = [[("test", 0)]]
         build = getUtility(ICIBuildSet).new(
-            git_repository, commit_sha1, distro_arch_series, stages,
-            date_created=date_created)
+            git_repository,
+            commit_sha1,
+            distro_arch_series,
+            stages,
+            date_created=date_created,
+        )
         if duration is not None:
             removeSecurityProxy(build).updateStatus(
-                BuildStatus.BUILDING, builder=builder,
-                date_started=build.date_created)
+                BuildStatus.BUILDING,
+                builder=builder,
+                date_started=build.date_created,
+            )
             removeSecurityProxy(build).updateStatus(
-                status, builder=builder,
-                date_finished=build.date_started + duration)
+                status,
+                builder=builder,
+                date_finished=build.date_started + duration,
+            )
         else:
             removeSecurityProxy(build).updateStatus(status, builder=builder)
         IStore(build).flush()
         return build
 
-    def makeVulnerability(self, distribution=None, status=None,
-                          importance=None, creator=None,
-                          information_type=InformationType.PUBLIC, cve=None,
-                          description=None, notes=None, mitigation=None,
-                          importance_explanation=None, date_made_public=None):
+    def makeVulnerability(
+        self,
+        distribution=None,
+        status=None,
+        importance=None,
+        creator=None,
+        information_type=InformationType.PUBLIC,
+        cve=None,
+        description=None,
+        notes=None,
+        mitigation=None,
+        importance_explanation=None,
+        date_made_public=None,
+    ):
         """Make a new `Vulnerability`."""
         if distribution is None:
             distribution = self.makeDistribution()
@@ -5481,19 +6874,30 @@ class LaunchpadObjectFactory(ObjectFactory):
             creator = self.makePerson()
         if importance_explanation is None:
             importance_explanation = self.getUniqueString(
-                "vulnerability-importance-explanation")
-        return getUtility(
-            IVulnerabilitySet).new(
-            distribution=distribution, cve=cve, status=status,
-            importance=importance, creator=creator,
-            information_type=information_type, description=description,
-            notes=notes, mitigation=mitigation,
+                "vulnerability-importance-explanation"
+            )
+        return getUtility(IVulnerabilitySet).new(
+            distribution=distribution,
+            cve=cve,
+            status=status,
+            importance=importance,
+            creator=creator,
+            information_type=information_type,
+            description=description,
+            notes=notes,
+            mitigation=mitigation,
             importance_explanation=importance_explanation,
-            date_made_public=date_made_public)
+            date_made_public=date_made_public,
+        )
 
-    def makeVulnerabilityActivity(self, vulnerability=None, changer=None,
-                                  what_changed=None, old_value=None,
-                                  new_value=None):
+    def makeVulnerabilityActivity(
+        self,
+        vulnerability=None,
+        changer=None,
+        what_changed=None,
+        old_value=None,
+        new_value=None,
+    ):
         """Make a new `VulnerabilityActivity`."""
         if vulnerability is None:
             vulnerability = self.makeVulnerability()
@@ -5505,25 +6909,28 @@ class LaunchpadObjectFactory(ObjectFactory):
             old_value = self.getUniqueString("old-value")
         if new_value is None:
             new_value = self.getUniqueString("new-value")
-        return getUtility(
-            IVulnerabilityActivitySet).new(vulnerability=vulnerability,
-                                           changer=changer,
-                                           what_changed=what_changed,
-                                           old_value=old_value,
-                                           new_value=new_value)
+        return getUtility(IVulnerabilityActivitySet).new(
+            vulnerability=vulnerability,
+            changer=changer,
+            what_changed=what_changed,
+            old_value=old_value,
+            new_value=new_value,
+        )
 
 
 # Some factory methods return simple Python types. We don't add
 # security wrappers for them, as well as for objects created by
 # other Python libraries.
-unwrapped_types = frozenset({
-    BaseRecipeBranch,
-    DSCFile,
-    Message,
-    datetime,
-    int,
-    str,
-    })
+unwrapped_types = frozenset(
+    {
+        BaseRecipeBranch,
+        DSCFile,
+        Message,
+        datetime,
+        int,
+        str,
+    }
+)
 
 
 def is_security_proxied_or_harmless(obj):
@@ -5535,14 +6942,15 @@ def is_security_proxied_or_harmless(obj):
     if type(obj) in unwrapped_types:
         return True
     if isinstance(obj, (Sequence, set, frozenset)):
-        return all(
-            is_security_proxied_or_harmless(element)
-            for element in obj)
+        return all(is_security_proxied_or_harmless(element) for element in obj)
     if isinstance(obj, Mapping):
         return all(
-            (is_security_proxied_or_harmless(key) and
-             is_security_proxied_or_harmless(obj[key]))
-            for key in obj)
+            (
+                is_security_proxied_or_harmless(key)
+                and is_security_proxied_or_harmless(obj[key])
+            )
+            for key in obj
+        )
     return False
 
 
@@ -5552,7 +6960,8 @@ class UnproxiedFactoryMethodWarning(UserWarning):
     def __init__(self, method_name):
         super().__init__(
             "PLEASE FIX: LaunchpadObjectFactory.%s returns an "
-            "unproxied object." % (method_name, ))
+            "unproxied object." % (method_name,)
+        )
 
 
 class ShouldThisBeUsingRemoveSecurityProxy(UserWarning):
@@ -5561,7 +6970,8 @@ class ShouldThisBeUsingRemoveSecurityProxy(UserWarning):
     def __init__(self, obj):
         message = (
             "removeSecurityProxy(%r) called. Is this correct? "
-            "Either call it directly or fix the test." % obj)
+            "Either call it directly or fix the test." % obj
+        )
         super().__init__(message)
 
 
@@ -5575,6 +6985,6 @@ def remove_security_proxy_and_shout_at_engineer(obj):
     This function should only be used in legacy tests which fail because
     they expect unproxied objects.
     """
-    if os.environ.get('LP_PROXY_WARNINGS') == '1':
+    if os.environ.get("LP_PROXY_WARNINGS") == "1":
         warnings.warn(ShouldThisBeUsingRemoveSecurityProxy(obj), stacklevel=2)
     return removeSecurityProxy(obj)
diff --git a/lib/lp/testing/fakemethod.py b/lib/lp/testing/fakemethod.py
index d3a7df7..7fcd87b 100644
--- a/lib/lp/testing/fakemethod.py
+++ b/lib/lp/testing/fakemethod.py
@@ -2,8 +2,8 @@
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 __all__ = [
-    'FakeMethod',
-    ]
+    "FakeMethod",
+]
 
 
 class FakeMethod:
diff --git a/lib/lp/testing/faketransaction.py b/lib/lp/testing/faketransaction.py
index 3c3d786..b5b7623 100644
--- a/lib/lp/testing/faketransaction.py
+++ b/lib/lp/testing/faketransaction.py
@@ -3,7 +3,7 @@
 
 """Fake transaction manager."""
 
-__all__ = ['FakeTransaction']
+__all__ = ["FakeTransaction"]
 
 
 class FakeTransaction:
@@ -14,6 +14,7 @@ class FakeTransaction:
 
     Set `log_calls` to True to enable printing of commits and aborts.
     """
+
     commit_count = 0
 
     def __init__(self, log_calls=False):
diff --git a/lib/lp/testing/fixture.py b/lib/lp/testing/fixture.py
index 9effd7f..f112eb1 100644
--- a/lib/lp/testing/fixture.py
+++ b/lib/lp/testing/fixture.py
@@ -4,46 +4,39 @@
 """Launchpad test fixtures that have no better home."""
 
 __all__ = [
-    'CapturedOutput',
-    'CaptureOops',
-    'DemoMode',
-    'DisableTriggerFixture',
-    'PGBouncerFixture',
-    'PGNotReadyError',
-    'run_capturing_output',
-    'ZopeAdapterFixture',
-    'ZopeEventHandlerFixture',
-    'ZopeUtilityFixture',
-    'ZopeViewReplacementFixture',
-    ]
+    "CapturedOutput",
+    "CaptureOops",
+    "DemoMode",
+    "DisableTriggerFixture",
+    "PGBouncerFixture",
+    "PGNotReadyError",
+    "run_capturing_output",
+    "ZopeAdapterFixture",
+    "ZopeEventHandlerFixture",
+    "ZopeUtilityFixture",
+    "ZopeViewReplacementFixture",
+]
 
-from configparser import ConfigParser
 import io
 import os.path
 import socket
 import time
+from configparser import ConfigParser
 
 import amqp
-from fixtures import (
-    EnvironmentVariableFixture,
-    Fixture,
-    MonkeyPatch,
-    )
-from lazr.restful.utils import get_current_browser_request
 import oops
 import oops_amqp
 import pgbouncer.fixture
-from zope.component import (
-    adapter,
-    getGlobalSiteManager,
-    )
+from fixtures import EnvironmentVariableFixture, Fixture, MonkeyPatch
+from lazr.restful.utils import get_current_browser_request
+from zope.component import adapter, getGlobalSiteManager
 from zope.interface import Interface
 from zope.publisher.interfaces.browser import IDefaultBrowserLayer
 from zope.security.checker import (
     defineChecker,
     getCheckerForInstancesOf,
     undefineChecker,
-    )
+)
 
 from lp.services import webapp
 from lp.services.config import config
@@ -72,29 +65,32 @@ class PGBouncerFixture(pgbouncer.fixture.PGBouncerFixture):
 
         # Known databases
         from lp.testing.layers import DatabaseLayer
+
         dbnames = [
             DatabaseLayer._db_fixture.dbname,
             DatabaseLayer._db_template_fixture.dbname,
-            'session_ftest',
-            'launchpad_empty',
-            ]
+            "session_ftest",
+            "launchpad_empty",
+        ]
         for dbname in dbnames:
-            self.databases[dbname] = 'dbname=%s port=5432 host=localhost' % (
-                dbname,)
+            self.databases[dbname] = "dbname=%s port=5432 host=localhost" % (
+                dbname,
+            )
 
         # Known users, pulled from security.cfg
         security_cfg_path = os.path.join(
-            config.root, 'database', 'schema', 'security.cfg')
+            config.root, "database", "schema", "security.cfg"
+        )
         security_cfg_config = ConfigParser({})
         security_cfg_config.read([security_cfg_path])
         for section_name in security_cfg_config.sections():
-            self.users[section_name] = 'trusted'
-            self.users[section_name + '_ro'] = 'trusted'
-        self.users[os.environ['USER']] = 'trusted'
-        self.users['pgbouncer'] = 'trusted'
+            self.users[section_name] = "trusted"
+            self.users[section_name + "_ro"] = "trusted"
+        self.users[os.environ["USER"]] = "trusted"
+        self.users["pgbouncer"] = "trusted"
 
         # Administrative access is useful for debugging.
-        self.admin_users = ['launchpad', 'pgbouncer', os.environ['USER']]
+        self.admin_users = ["launchpad", "pgbouncer", os.environ["USER"]]
 
     def setUp(self):
         super().setUp()
@@ -106,9 +102,9 @@ class PGBouncerFixture(pgbouncer.fixture.PGBouncerFixture):
         # Abuse the PGPORT environment variable to get things connecting
         # via pgbouncer. Otherwise, we would need to temporarily
         # overwrite the database connection strings in the config.
-        self.useFixture(EnvironmentVariableFixture('PGPORT', str(self.port)))
+        self.useFixture(EnvironmentVariableFixture("PGPORT", str(self.port)))
         # Force TCP, as pgbouncer doesn't listen on a UNIX socket.
-        self.useFixture(EnvironmentVariableFixture('PGHOST', 'localhost'))
+        self.useFixture(EnvironmentVariableFixture("PGHOST", "localhost"))
 
         # Reset database connections so they go through pgbouncer.
         self._maybe_reconnect_stores()
@@ -120,16 +116,13 @@ class PGBouncerFixture(pgbouncer.fixture.PGBouncerFixture):
         as we are using a test layer that doesn't provide database
         connections.
         """
-        from lp.testing.layers import (
-            is_ca_available,
-            reconnect_stores,
-            )
+        from lp.testing.layers import is_ca_available, reconnect_stores
+
         if is_ca_available():
             reconnect_stores()
 
     def start(self, retries=20, sleep=0.5):
-        """Start PGBouncer, waiting for it to accept connections if neccesary.
-        """
+        """Start PGBouncer, waiting for it to accept connections."""
         super().start()
         for i in range(retries):
             try:
@@ -147,8 +140,15 @@ class PGBouncerFixture(pgbouncer.fixture.PGBouncerFixture):
 class ZopeAdapterFixture(Fixture):
     """A fixture to register and unregister an adapter."""
 
-    def __init__(self, factory, required=None, provided=None, name="",
-                 info="", event=True):
+    def __init__(
+        self,
+        factory,
+        required=None,
+        provided=None,
+        name="",
+        info="",
+        event=True,
+    ):
         # We use some private functions from here since we need them to work
         # out how to query for existing adapters.  We could copy and paste
         # the code instead, but it doesn't seem worth it.
@@ -166,15 +166,26 @@ class ZopeAdapterFixture(Fixture):
     def _setUp(self):
         site_manager = getGlobalSiteManager()
         original = site_manager.adapters.lookup(
-            self._required, self._provided, self._name)
+            self._required, self._provided, self._name
+        )
         site_manager.registerAdapter(
-            self._factory, required=self._required, provided=self._provided,
-            name=self._name, info=self._info, event=self._event)
+            self._factory,
+            required=self._required,
+            provided=self._provided,
+            name=self._name,
+            info=self._info,
+            event=self._event,
+        )
         # Equivalent to unregisterAdapter if original is None.
         self.addCleanup(
             site_manager.registerAdapter,
-            original, required=self._required, provided=self._provided,
-            name=self._name, info=self._info, event=self._event)
+            original,
+            required=self._required,
+            provided=self._provided,
+            name=self._name,
+            info=self._info,
+            event=self._event,
+        )
 
 
 class ZopeEventHandlerFixture(Fixture):
@@ -189,7 +200,8 @@ class ZopeEventHandlerFixture(Fixture):
         gsm = getGlobalSiteManager()
         gsm.registerHandler(self._handler, required=self._required)
         self.addCleanup(
-            gsm.unregisterHandler, self._handler, required=self._required)
+            gsm.unregisterHandler, self._handler, required=self._required
+        )
 
 
 class ZopeViewReplacementFixture(Fixture):
@@ -198,9 +210,13 @@ class ZopeViewReplacementFixture(Fixture):
     This will not work with the AppServerLayer.
     """
 
-    def __init__(self, name, context_interface,
-                 request_interface=IDefaultBrowserLayer,
-                 replacement=None):
+    def __init__(
+        self,
+        name,
+        context_interface,
+        request_interface=IDefaultBrowserLayer,
+        replacement=None,
+    ):
         super().__init__()
         self.name = name
         self.context_interface = context_interface
@@ -209,33 +225,39 @@ class ZopeViewReplacementFixture(Fixture):
         # It can be convenient--bordering on necessary--to use this original
         # class as a base for the replacement.
         self.original = self.gsm.adapters.registered(
-            (context_interface, request_interface), Interface, name)
+            (context_interface, request_interface), Interface, name
+        )
         self.checker = getCheckerForInstancesOf(self.original)
         if self.original is None:
             # The adapter registry does not provide good methods to introspect
             # it. If it did, we might try harder here.
             raise ValueError(
-                'No existing view to replace.  Wrong request interface?  '
-                'Try a layer.')
+                "No existing view to replace.  Wrong request interface?  "
+                "Try a layer."
+            )
         self.replacement = replacement
 
     def _setUp(self):
         if self.replacement is None:
-            raise ValueError('replacement is not set')
+            raise ValueError("replacement is not set")
         self.gsm.adapters.register(
-            (self.context_interface, self.request_interface), Interface,
-             self.name, self.replacement)
+            (self.context_interface, self.request_interface),
+            Interface,
+            self.name,
+            self.replacement,
+        )
         # The same checker should be sufficient.  If it ever isn't, we
         # can add more flexibility then.
         defineChecker(self.replacement, self.checker)
 
-        self.addCleanup(
-            undefineChecker, self.replacement)
+        self.addCleanup(undefineChecker, self.replacement)
         self.addCleanup(
             self.gsm.adapters.register,
             (self.context_interface, self.request_interface),
             Interface,
-            self.name, self.original)
+            self.name,
+            self.original,
+        )
 
 
 class ZopeUtilityFixture(Fixture):
@@ -260,10 +282,11 @@ class ZopeUtilityFixture(Fixture):
         gsm.registerUtility(self.component, self.intf, self.name)
         if original is not None:
             self.addCleanup(
-                gsm.registerUtility, original, self.intf, self.name)
+                gsm.registerUtility, original, self.intf, self.name
+            )
         self.addCleanup(
-            gsm.unregisterUtility,
-            self.component, self.intf, self.name)
+            gsm.unregisterUtility, self.component, self.intf, self.name
+        )
 
 
 class CapturedOutput(Fixture):
@@ -275,8 +298,8 @@ class CapturedOutput(Fixture):
         self.stderr = io.StringIO()
 
     def _setUp(self):
-        self.useFixture(MonkeyPatch('sys.stdout', self.stdout))
-        self.useFixture(MonkeyPatch('sys.stderr', self.stderr))
+        self.useFixture(MonkeyPatch("sys.stdout", self.stdout))
+        self.useFixture(MonkeyPatch("sys.stderr", self.stderr))
 
 
 def run_capturing_output(function, *args, **kwargs):
@@ -328,27 +351,34 @@ class CaptureOops(Fixture):
         that it will be automatically nuked and must be recreated.
         """
         self.queue_name, _, _ = self.channel.queue_declare(
-            durable=True, auto_delete=False)
+            durable=True, auto_delete=False
+        )
         self.addCleanup(self.channel.queue_delete, self.queue_name)
         # In production the exchange already exists and is durable, but
         # here we make it just-in-time, and tell it to go when the test
         # fixture goes.
-        self.channel.exchange_declare(config.error_reports.error_exchange,
-            "fanout", durable=True, auto_delete=False)
+        self.channel.exchange_declare(
+            config.error_reports.error_exchange,
+            "fanout",
+            durable=True,
+            auto_delete=False,
+        )
         self.addCleanup(
-            self.channel.exchange_delete, config.error_reports.error_exchange)
+            self.channel.exchange_delete, config.error_reports.error_exchange
+        )
         self.channel.queue_bind(
-            self.queue_name, config.error_reports.error_exchange)
+            self.queue_name, config.error_reports.error_exchange
+        )
 
     def _add_oops(self, report):
         """Add an oops if it isn't already recorded.
 
         This is called from both amqp and in-appserver situations.
         """
-        if report['id'] not in self.oops_ids:
+        if report["id"] not in self.oops_ids:
             self.oopses.append(report)
-            self.oops_ids.add(report['id'])
-        return [report['id']]
+            self.oops_ids.add(report["id"])
+        return [report["id"]]
 
     @adapter(ErrorReportEvent)
     def _recordOops(self, event):
@@ -371,14 +401,17 @@ class CaptureOops(Fixture):
             channel = connection.channel()
             try:
                 channel.basic_publish(
-                    message, config.error_reports.error_exchange,
-                    config.error_reports.error_queue_key)
+                    message,
+                    config.error_reports.error_exchange,
+                    config.error_reports.error_queue_key,
+                )
             finally:
                 channel.close()
         finally:
             connection.close()
         receiver = oops_amqp.Receiver(
-            self.oops_config, connect, self.queue_name)
+            self.oops_config, connect, self.queue_name
+        )
         receiver.sentinel = self.AMQP_SENTINEL
         try:
             receiver.run_forever()
@@ -397,8 +430,7 @@ class CaptureTimeline(Fixture):
 
     def _setUp(self):
         webapp.adapter.set_request_started(time.time())
-        self.timeline = get_request_timeline(
-            get_current_browser_request())
+        self.timeline = get_request_timeline(get_current_browser_request())
         self.addCleanup(webapp.adapter.clear_request_started)
 
 
@@ -410,13 +442,16 @@ class DemoMode(Fixture):
     """
 
     def _setUp(self):
-        config.push('demo-fixture', '''
+        config.push(
+            "demo-fixture",
+            """
 [launchpad]
 is_demo: true
 site_message = This is a demo site mmk. \
 <a href="http://example.com";>File a bug</a>.
-            ''')
-        self.addCleanup(lambda: config.pop('demo-fixture'))
+            """,
+        )
+        self.addCleanup(lambda: config.pop("demo-fixture"))
 
 
 class DisableTriggerFixture(Fixture):
@@ -430,17 +465,15 @@ class DisableTriggerFixture(Fixture):
         self.addCleanup(self._enable_triggers)
 
     def _process_triggers(self, mode):
-        with dbuser('postgres'):
+        with dbuser("postgres"):
             for table, trigger in self.table_triggers.items():
-                sql = ("ALTER TABLE %(table)s %(mode)s trigger "
-                       "%(trigger)s") % {
-                    'table': table,
-                    'mode': mode,
-                    'trigger': trigger}
+                sql = (
+                    "ALTER TABLE %(table)s %(mode)s trigger " "%(trigger)s"
+                ) % {"table": table, "mode": mode, "trigger": trigger}
                 IStore(LibraryFileAlias).execute(sql)
 
     def _disable_triggers(self):
-        self._process_triggers(mode='DISABLE')
+        self._process_triggers(mode="DISABLE")
 
     def _enable_triggers(self):
-        self._process_triggers(mode='ENABLE')
+        self._process_triggers(mode="ENABLE")
diff --git a/lib/lp/testing/gpgkeys/__init__.py b/lib/lp/testing/gpgkeys/__init__.py
index 482e049..46b9581 100644
--- a/lib/lp/testing/gpgkeys/__init__.py
+++ b/lib/lp/testing/gpgkeys/__init__.py
@@ -16,8 +16,8 @@ Secret keys are also imported into the local key ring, they are used for
 decrypt data in pagetests.
 """
 
-from io import BytesIO
 import os
+from io import BytesIO
 
 import gpgme
 import six
@@ -25,13 +25,9 @@ from zope.component import getUtility
 
 from lp.registry.interfaces.gpg import IGPGKeySet
 from lp.registry.interfaces.person import IPersonSet
-from lp.services.gpg.interfaces import (
-    get_gpgme_context,
-    IGPGHandler,
-    )
+from lp.services.gpg.interfaces import IGPGHandler, get_gpgme_context
 
-
-gpgkeysdir = os.path.join(os.path.dirname(__file__), 'data')
+gpgkeysdir = os.path.join(os.path.dirname(__file__), "data")
 
 
 def import_public_key(email_addr):
@@ -43,10 +39,10 @@ def import_public_key(email_addr):
     key = gpghandler.importPublicKey(pubkey)
 
     # Strip out any '-passwordless' annotation from the email addresses.
-    email_addr = email_addr.replace('-passwordless', '')
+    email_addr = email_addr.replace("-passwordless", "")
 
     # Some of the keys shouldn't be inserted into the db.
-    if email_addr.endswith('do-not-insert-into-db'):
+    if email_addr.endswith("do-not-insert-into-db"):
         return
 
     person = personset.getByEmail(email_addr)
@@ -69,13 +65,14 @@ def import_public_key(email_addr):
         fingerprint=key.fingerprint,
         keysize=key.keysize,
         algorithm=key.algorithm,
-        active=(not key.revoked))
+        active=(not key.revoked),
+    )
 
 
 def iter_test_key_emails():
     """Iterates over the email addresses for the keys in the gpgkeysdir."""
     for name in sorted(os.listdir(gpgkeysdir), reverse=True):
-        if name.endswith('.pub'):
+        if name.endswith(".pub"):
             yield name[:-4]
 
 
@@ -85,32 +82,32 @@ def import_public_test_keys():
         import_public_key(email)
 
 
-def import_secret_test_key(keyfile='test@xxxxxxxxxxxxxxxxx'):
+def import_secret_test_key(keyfile="test@xxxxxxxxxxxxxxxxx"):
     """Imports the secret key located in gpgkeysdir into local keyring.
 
     :param keyfile: The name of the file to be imported.
     """
     gpghandler = getUtility(IGPGHandler)
-    with open(os.path.join(gpgkeysdir, keyfile), 'rb') as f:
+    with open(os.path.join(gpgkeysdir, keyfile), "rb") as f:
         seckey = f.read()
     return gpghandler.importSecretKey(seckey)
 
 
 def test_pubkey_file_from_email(email_addr):
     """Get the file name for a test pubkey by email address."""
-    return os.path.join(gpgkeysdir, email_addr + '.pub')
+    return os.path.join(gpgkeysdir, email_addr + ".pub")
 
 
 def test_pubkey_from_email(email_addr):
     """Get the on disk content for a test pubkey by email address."""
-    with open(test_pubkey_file_from_email(email_addr), 'rb') as f:
+    with open(test_pubkey_file_from_email(email_addr), "rb") as f:
         return f.read()
 
 
 def test_keyrings():
     """Iterate over the filenames for test keyrings."""
     for entry in os.scandir(gpgkeysdir):
-        if entry.name.endswith('.gpg'):
+        if entry.name.endswith(".gpg"):
             yield entry.path
 
 
@@ -124,10 +121,10 @@ def decrypt_content(content, password):
     :password: password to unlock the secret key in question
     """
     if not isinstance(password, str):
-        raise TypeError('Password must be a str.')
+        raise TypeError("Password must be a str.")
 
     if not isinstance(content, bytes):
-        raise TypeError('Content must be bytes.')
+        raise TypeError("Content must be bytes.")
 
     ctx = get_gpgme_context()
 
@@ -136,7 +133,7 @@ def decrypt_content(content, password):
     plain = BytesIO()
 
     def passphrase_cb(uid_hint, passphrase_info, prev_was_bad, fd):
-        os.write(fd, six.ensure_binary('%s\n' % password))
+        os.write(fd, six.ensure_binary("%s\n" % password))
 
     ctx.passphrase_cb = passphrase_cb
 
diff --git a/lib/lp/testing/html5browser.py b/lib/lp/testing/html5browser.py
index 7230ab2..ef19904 100644
--- a/lib/lp/testing/html5browser.py
+++ b/lib/lp/testing/html5browser.py
@@ -24,25 +24,21 @@
 """A Web browser that can be driven by an application."""
 
 __all__ = [
-    'Browser',
-    'Command',
-    ]
+    "Browser",
+    "Command",
+]
 
 import gi
 
+gi.require_version("Gtk", "3.0")
+gi.require_version("WebKit", "3.0")
 
-gi.require_version('Gtk', '3.0')
-gi.require_version('WebKit', '3.0')
-
-from gi.repository import (  # noqa: E402
-    GLib,
-    Gtk,
-    WebKit,
-    )
+from gi.repository import GLib, Gtk, WebKit  # noqa: E402
 
 
 class Command:
     """A representation of the status and result of a command."""
+
     STATUS_RUNNING = object()
     STATUS_COMPLETE = object()
 
@@ -50,8 +46,9 @@ class Command:
     CODE_SUCCESS = 0
     CODE_FAIL = 1
 
-    def __init__(self, status=STATUS_RUNNING, return_code=CODE_UNKNOWN,
-                 content=None):
+    def __init__(
+        self, status=STATUS_RUNNING, return_code=CODE_UNKNOWN, content=None
+    ):
         self.status = status
         self.return_code = return_code
         self.content = content
@@ -60,9 +57,9 @@ class Command:
 class Browser(WebKit.WebView):
     """A browser that can be driven by an application."""
 
-    STATUS_PREFIX = '::::'
+    STATUS_PREFIX = "::::"
     TIMEOUT = 5000
-    INCREMENTAL_PREFIX = '>>>>'
+    INCREMENTAL_PREFIX = ">>>>"
     INITIAL_TIMEOUT = None
     INCREMENTAL_TIMEOUT = None
 
@@ -74,51 +71,65 @@ class Browser(WebKit.WebView):
         self.script = None
         self.command = None
         self.listeners = {}
-        self._connect('console-message', self._on_console_message, False)
-
-    def load_page(self, uri,
-                  timeout=TIMEOUT,
-                  initial_timeout=INITIAL_TIMEOUT,
-                  incremental_timeout=INCREMENTAL_TIMEOUT):
+        self._connect("console-message", self._on_console_message, False)
+
+    def load_page(
+        self,
+        uri,
+        timeout=TIMEOUT,
+        initial_timeout=INITIAL_TIMEOUT,
+        incremental_timeout=INCREMENTAL_TIMEOUT,
+    ):
         """Load a page and return the content."""
         self._setup_listening_operation(
-            timeout, initial_timeout, incremental_timeout)
-        if uri.startswith('/'):
-            uri = 'file://' + uri
+            timeout, initial_timeout, incremental_timeout
+        )
+        if uri.startswith("/"):
+            uri = "file://" + uri
         self.load_uri(uri)
         Gtk.main()
         return self.command
 
-    def run_script(self, script,
-                   timeout=TIMEOUT,
-                   initial_timeout=INITIAL_TIMEOUT,
-                   incremental_timeout=INCREMENTAL_TIMEOUT):
+    def run_script(
+        self,
+        script,
+        timeout=TIMEOUT,
+        initial_timeout=INITIAL_TIMEOUT,
+        incremental_timeout=INCREMENTAL_TIMEOUT,
+    ):
         """Run a script and return the result."""
         self._setup_listening_operation(
-            timeout, initial_timeout, incremental_timeout)
+            timeout, initial_timeout, incremental_timeout
+        )
         self.script = script
-        self._connect('notify::load-status', self._on_script_load_finished)
+        self._connect("notify::load-status", self._on_script_load_finished)
         self.load_string(
-            '<html><head></head><body></body></html>',
-            'text/html', 'UTF-8', 'file:///')
+            "<html><head></head><body></body></html>",
+            "text/html",
+            "UTF-8",
+            "file:///",
+        )
         Gtk.main()
         return self.command
 
-    def _setup_listening_operation(self, timeout, initial_timeout,
-                                   incremental_timeout):
+    def _setup_listening_operation(
+        self, timeout, initial_timeout, incremental_timeout
+    ):
         """Setup a one-time listening operation for command's completion."""
         self._create_window()
         self.command = Command()
         self._last_status = None
         self._incremental_timeout = incremental_timeout
         self._connect(
-            'status-bar-text-changed', self._on_status_bar_text_changed)
+            "status-bar-text-changed", self._on_status_bar_text_changed
+        )
         self._timeout_source = GLib.timeout_add(timeout, self._on_timeout)
         if initial_timeout is None:
             initial_timeout = incremental_timeout
         if initial_timeout is not None:
             self._incremental_timeout_source = GLib.timeout_add(
-                initial_timeout, self._on_timeout)
+                initial_timeout, self._on_timeout
+            )
         else:
             self._incremental_timeout_source = None
 
@@ -148,11 +159,12 @@ class Browser(WebKit.WebView):
             self._last_status = text[4:]
             if self._incremental_timeout:
                 self._incremental_timeout_source = GLib.timeout_add(
-                    self._incremental_timeout, self._on_timeout)
+                    self._incremental_timeout, self._on_timeout
+                )
         elif text.startswith(self.STATUS_PREFIX):
             self._clear_timeout()
             self._clear_incremental_timeout()
-            self._disconnect('status-bar-text-changed')
+            self._disconnect("status-bar-text-changed")
             self._clear_status()
             self.command.status = Command.STATUS_COMPLETE
             self.command.return_code = Command.CODE_SUCCESS
@@ -161,10 +173,10 @@ class Browser(WebKit.WebView):
 
     def _on_script_load_finished(self, view, load_status):
         # pywebkit does not have WebKit.LoadStatus.FINISHED.
-        statuses = ('WEBKIT_LOAD_FINISHED', 'WEBKIT_LOAD_FAILED')
+        statuses = ("WEBKIT_LOAD_FINISHED", "WEBKIT_LOAD_FAILED")
         if self.props.load_status.value_name not in statuses:
             return
-        self._disconnect('notify::load-status')
+        self._disconnect("notify::load-status")
         self.execute_script(self.script)
         self.script = None
 
diff --git a/lib/lp/testing/karma.py b/lib/lp/testing/karma.py
index 0393a4b..d59764e 100644
--- a/lib/lp/testing/karma.py
+++ b/lib/lp/testing/karma.py
@@ -4,9 +4,9 @@
 """Helper functions/classes to be used when testing the karma framework."""
 
 __all__ = [
-    'KarmaAssignedEventListener',
-    'KarmaRecorder',
-    ]
+    "KarmaAssignedEventListener",
+    "KarmaRecorder",
+]
 
 from lp.registry.interfaces.karma import IKarmaAssignedEvent
 from lp.registry.interfaces.person import IPerson
@@ -24,8 +24,14 @@ class KarmaRecorder:
     property.
     """
 
-    def __init__(self, person=None, action_name=None, product=None,
-                 distribution=None, sourcepackagename=None):
+    def __init__(
+        self,
+        person=None,
+        action_name=None,
+        product=None,
+        distribution=None,
+        sourcepackagename=None,
+    ):
         """Create a `KarmaRecorder`, but do not activate it yet.
 
         :param person: If given, record only karma for this `Person`.
@@ -53,11 +59,14 @@ class KarmaRecorder:
     def filter(self, karma):
         """Does `karma` match our filters?"""
         return (
-            self._filterFor(self.person, karma.person) and
-            self._filterFor(self.action_name, karma.action.name) and
-            self._filterFor(self.product, karma.product) and
-            self._filterFor(self.distribution, karma.distribution) and
-            self._filterFor(self.sourcepackagename, karma.sourcepackagename))
+            self._filterFor(self.person, karma.person)
+            and self._filterFor(self.action_name, karma.action.name)
+            and self._filterFor(self.product, karma.product)
+            and self._filterFor(self.distribution, karma.distribution)
+            and self._filterFor(
+                self.sourcepackagename, karma.sourcepackagename
+            )
+        )
 
     def record(self, karma):
         """Overridable: record the assignment of karma.
@@ -79,7 +88,8 @@ class KarmaRecorder:
     def register_listener(self):
         """Register listener.  Must be `unregister`ed later."""
         self.listener = ZopeEventHandlerFixture(
-            self.receive, (IPerson, IKarmaAssignedEvent))
+            self.receive, (IPerson, IKarmaAssignedEvent)
+        )
         self.listener.setUp()
 
     def unregister_listener(self):
diff --git a/lib/lp/testing/keyserver/__init__.py b/lib/lp/testing/keyserver/__init__.py
index 581e05b..8c52527 100644
--- a/lib/lp/testing/keyserver/__init__.py
+++ b/lib/lp/testing/keyserver/__init__.py
@@ -2,9 +2,9 @@
 # Affero General Public License version 3 (see the file LICENSE).
 
 __all__ = [
-    'InProcessKeyServerFixture',
-    'KeyServerTac',
-    ]
+    "InProcessKeyServerFixture",
+    "KeyServerTac",
+]
 
 from lp.testing.keyserver.harness import KeyServerTac
 from lp.testing.keyserver.inprocess import InProcessKeyServerFixture
diff --git a/lib/lp/testing/keyserver/harness.py b/lib/lp/testing/keyserver/harness.py
index eda5316..1235e1d 100644
--- a/lib/lp/testing/keyserver/harness.py
+++ b/lib/lp/testing/keyserver/harness.py
@@ -7,8 +7,7 @@ import shutil
 from lp.services.config import config
 from lp.services.daemons.tachandler import TacTestSetup
 
-
-KEYS_DIR = os.path.join(os.path.dirname(__file__), 'tests/keys')
+KEYS_DIR = os.path.join(os.path.dirname(__file__), "tests/keys")
 
 
 class KeyServerTac(TacTestSetup):
@@ -27,18 +26,21 @@ class KeyServerTac(TacTestSetup):
     @property
     def tacfile(self):
         return os.path.abspath(
-            os.path.join(os.path.dirname(__file__), 'testkeyserver.tac'))
+            os.path.join(os.path.dirname(__file__), "testkeyserver.tac")
+        )
 
     @property
     def pidfile(self):
-        return os.path.join(self.root, 'testkeyserver.pid')
+        return os.path.join(self.root, "testkeyserver.pid")
 
     @property
     def logfile(self):
-        return os.path.join(self.root, 'testkeyserver.log')
+        return os.path.join(self.root, "testkeyserver.log")
 
     @property
     def url(self):
         """The URL that the web server will be running on."""
-        return 'http://%s:%d' % (
-            config.gpghandler.host, config.gpghandler.port)
+        return "http://%s:%d"; % (
+            config.gpghandler.host,
+            config.gpghandler.port,
+        )
diff --git a/lib/lp/testing/keyserver/inprocess.py b/lib/lp/testing/keyserver/inprocess.py
index b994bea..2a5e528 100644
--- a/lib/lp/testing/keyserver/inprocess.py
+++ b/lib/lp/testing/keyserver/inprocess.py
@@ -4,20 +4,13 @@
 """In-process keyserver fixture."""
 
 __all__ = [
-    'InProcessKeyServerFixture',
-    ]
+    "InProcessKeyServerFixture",
+]
 
 from textwrap import dedent
 
-from fixtures import (
-    Fixture,
-    TempDir,
-    )
-from twisted.internet import (
-    defer,
-    endpoints,
-    reactor,
-    )
+from fixtures import Fixture, TempDir
+from twisted.internet import defer, endpoints, reactor
 from twisted.python.compat import nativeString
 from twisted.web import server
 
@@ -56,10 +49,16 @@ class InProcessKeyServerFixture(Fixture):
         resource = KeyServerResource(self.useFixture(TempDir()).path)
         endpoint = endpoints.serverFromString(reactor, nativeString("tcp:0"))
         self._port = yield endpoint.listen(server.Site(resource))
-        config.push("in-process-key-server-fixture", dedent("""
+        config.push(
+            "in-process-key-server-fixture",
+            dedent(
+                """
             [gpghandler]
             port: %s
-            """) % self._port.getHost().port)
+            """
+            )
+            % self._port.getHost().port,
+        )
         self.addCleanup(config.pop, "in-process-key-server-fixture")
 
     @defer.inlineCallbacks
@@ -72,5 +71,6 @@ class InProcessKeyServerFixture(Fixture):
     @property
     def url(self):
         """The URL that the web server will be running on."""
-        return ("http://%s:%d"; % (
-            config.gpghandler.host, config.gpghandler.port)).encode("UTF-8")
+        return (
+            "http://%s:%d"; % (config.gpghandler.host, config.gpghandler.port)
+        ).encode("UTF-8")
diff --git a/lib/lp/testing/keyserver/testkeyserver.tac b/lib/lp/testing/keyserver/testkeyserver.tac
index e308e8e..b6efaa7 100644
--- a/lib/lp/testing/keyserver/testkeyserver.tac
+++ b/lib/lp/testing/keyserver/testkeyserver.tac
@@ -4,10 +4,7 @@
 # Twisted Application Configuration file.
 # Use with "twistd -y <file.tac>", e.g. "twistd -noy server.tac"
 
-from twisted.application import (
-    service,
-    strports,
-    )
+from twisted.application import service, strports
 from twisted.web import server
 
 from lp.services.config import config
@@ -15,11 +12,10 @@ from lp.services.daemons import readyservice
 from lp.services.scripts import execute_zcml_for_scripts
 from lp.testing.keyserver.web import KeyServerResource
 
-
 # Needed for using IGPGHandler for processing key submit.
 execute_zcml_for_scripts()
 
-application = service.Application('testkeyserver')
+application = service.Application("testkeyserver")
 svc = service.IServiceCollection(application)
 
 # Service that announces when the daemon is ready
@@ -29,5 +25,5 @@ site = server.Site(KeyServerResource(config.testkeyserver.root))
 site.displayTracebacks = False
 
 # Run on the port that gpghandler is configured to hit.
-port = 'tcp:%s' % (config.gpghandler.port,)
+port = "tcp:%s" % (config.gpghandler.port,)
 strports.service(port, site).setServiceParent(svc)
diff --git a/lib/lp/testing/keyserver/tests/test_harness.py b/lib/lp/testing/keyserver/tests/test_harness.py
index e0727f4..78c4754 100644
--- a/lib/lp/testing/keyserver/tests/test_harness.py
+++ b/lib/lp/testing/keyserver/tests/test_harness.py
@@ -10,14 +10,13 @@ from lp.testing.keyserver.web import GREETING
 
 
 class TestKeyServerTac(TestCase):
-
     def test_url(self):
         # The url is the one that gpghandler is configured to hit.
         fixture = KeyServerTac()
         self.assertEqual(
-            'http://%s:%d' % (
-                config.gpghandler.host, config.gpghandler.port),
-            fixture.url)
+            "http://%s:%d"; % (config.gpghandler.host, config.gpghandler.port),
+            fixture.url,
+        )
 
     def test_starts_properly(self):
         # Make sure the tac starts properly and that we can load the page.
diff --git a/lib/lp/testing/keyserver/tests/test_inprocess.py b/lib/lp/testing/keyserver/tests/test_inprocess.py
index 350844b..9cb4698 100644
--- a/lib/lp/testing/keyserver/tests/test_inprocess.py
+++ b/lib/lp/testing/keyserver/tests/test_inprocess.py
@@ -3,10 +3,10 @@
 
 """In-process keyserver fixture tests."""
 
+import treq
 from testtools.twistedsupport import (
     AsynchronousDeferredRunTestForBrokenTwisted,
-    )
-import treq
+)
 from twisted.internet import defer
 
 from lp.services.config import config
@@ -20,7 +20,8 @@ from lp.testing.keyserver.web import GREETING
 class TestInProcessKeyServerFixture(TestCase):
 
     run_tests_with = AsynchronousDeferredRunTestForBrokenTwisted.make_factory(
-        timeout=30)
+        timeout=30
+    )
 
     @defer.inlineCallbacks
     def test_url(self):
@@ -28,15 +29,18 @@ class TestInProcessKeyServerFixture(TestCase):
         fixture = self.useFixture(InProcessKeyServerFixture())
         yield fixture.start()
         self.assertEqual(
-            ("http://%s:%d"; % (
-                config.gpghandler.host,
-                config.gpghandler.port)).encode("UTF-8"),
-            fixture.url)
+            (
+                "http://%s:%d";
+                % (config.gpghandler.host, config.gpghandler.port)
+            ).encode("UTF-8"),
+            fixture.url,
+        )
 
     @defer.inlineCallbacks
     def test_starts_properly(self):
         # The fixture starts properly and we can load the page.
         from twisted.internet import reactor
+
         fixture = self.useFixture(InProcessKeyServerFixture())
         yield fixture.start()
         client = self.useFixture(TReqFixture(reactor)).client
diff --git a/lib/lp/testing/keyserver/tests/test_locate_key.py b/lib/lp/testing/keyserver/tests/test_locate_key.py
index 37eed49..24242fc 100644
--- a/lib/lp/testing/keyserver/tests/test_locate_key.py
+++ b/lib/lp/testing/keyserver/tests/test_locate_key.py
@@ -8,7 +8,7 @@ from lp.testing.keyserver.web import locate_key
 
 
 class LocateKeyTestCase(TestCase):
-    root = os.path.join(os.path.dirname(__file__), 'keys')
+    root = os.path.join(os.path.dirname(__file__), "keys")
 
     def assertKeyFile(self, suffix, filename):
         """Verify that a suffix maps to the given filename."""
@@ -19,18 +19,19 @@ class LocateKeyTestCase(TestCase):
 
     def test_locate_key_exact_fingerprint_match(self):
         self.assertKeyFile(
-            '0xA419AE861E88BC9E04B9C26FBA2B9389DFD20543.get',
-            '0xA419AE861E88BC9E04B9C26FBA2B9389DFD20543.get')
+            "0xA419AE861E88BC9E04B9C26FBA2B9389DFD20543.get",
+            "0xA419AE861E88BC9E04B9C26FBA2B9389DFD20543.get",
+        )
 
     def test_locate_key_keyid_glob_match(self):
         self.assertKeyFile(
-            '0xDFD20543.get',
-            '0xA419AE861E88BC9E04B9C26FBA2B9389DFD20543.get')
+            "0xDFD20543.get", "0xA419AE861E88BC9E04B9C26FBA2B9389DFD20543.get"
+        )
 
     def test_locate_key_keyid_without_prefix_glob_match(self):
         self.assertKeyFile(
-            'DFD20543.get',
-            '0xA419AE861E88BC9E04B9C26FBA2B9389DFD20543.get')
+            "DFD20543.get", "0xA419AE861E88BC9E04B9C26FBA2B9389DFD20543.get"
+        )
 
     def test_locate_key_no_match(self):
-        self.assertKeyFile('0xDEADBEEF.get', None)
+        self.assertKeyFile("0xDEADBEEF.get", None)
diff --git a/lib/lp/testing/keyserver/tests/test_web.py b/lib/lp/testing/keyserver/tests/test_web.py
index 5798db8..851ea73 100644
--- a/lib/lp/testing/keyserver/tests/test_web.py
+++ b/lib/lp/testing/keyserver/tests/test_web.py
@@ -6,8 +6,8 @@
 import os
 import shutil
 
-from testtools.twistedsupport import AsynchronousDeferredRunTest
 import treq
+from testtools.twistedsupport import AsynchronousDeferredRunTest
 from twisted.internet.endpoints import serverFromString
 from twisted.python.failure import Failure
 from twisted.web.server import Site
@@ -31,28 +31,33 @@ class TestWebResources(TestCase):
 
     def setUpKeysDirectory(self):
         path = self.makeTemporaryDirectory()
-        path = os.path.join(path, 'keys')
+        path = os.path.join(path, "keys")
         shutil.copytree(KEYS_DIR, path)
         return path
 
     def makeService(self):
         """Run a test key server on whatever port we have available."""
         from twisted.internet import reactor
+
         resource = KeyServerResource(self.setUpKeysDirectory())
         site = Site(resource)
-        endpoint = serverFromString(reactor, 'tcp:0')
+        endpoint = serverFromString(reactor, "tcp:0")
         return endpoint.listen(site)
 
     def fetchResource(self, listening_port, path):
-        """GET the content at 'path' from the web server at 'listening_port'.
-        """
+        """GET the content at 'path' from the server at 'listening_port'."""
         from twisted.internet import reactor
-        url = 'http://localhost:%s/%s' % (
+
+        url = "http://localhost:%s/%s"; % (
             listening_port.getHost().port,
-            path.lstrip('/'))
+            path.lstrip("/"),
+        )
         client = self.useFixture(TReqFixture(reactor)).client
-        return client.get(url).addCallback(check_status).addCallback(
-            treq.text_content)
+        return (
+            client.get(url)
+            .addCallback(check_status)
+            .addCallback(treq.text_content)
+        )
 
     def getURL(self, path):
         """Start a test key server and get the content at 'path'."""
@@ -61,6 +66,7 @@ class TestWebResources(TestCase):
         def service_started(port):
             self.addCleanup(port.stopListening)
             return self.fetchResource(port, path)
+
         return d.addCallback(service_started)
 
     def assertContentMatches(self, path, content):
@@ -94,15 +100,16 @@ class TestWebResources(TestCase):
 
         def check_error_details(failure):
             if isinstance(failure.value, RegularCallbackExecuted):
-                self.fail('Response was not an HTTP error response.')
+                self.fail("Response was not an HTTP error response.")
             if not isinstance(failure, Failure):
                 raise failure
-            self.assertEqual(b'404', failure.value.status)
+            self.assertEqual(b"404", failure.value.status)
             self.assertEqual(
-                b'<html><head><title>Error handling request</title></head>\n'
-                b'<body><h1>Error handling request</h1>'
-                b'No results found: No keys found</body></html>',
-                failure.value.response)
+                b"<html><head><title>Error handling request</title></head>\n"
+                b"<body><h1>Error handling request</h1>"
+                b"No results found: No keys found</body></html>",
+                failure.value.response,
+            )
 
         d.addCallback(regular_execution_callback)
         return d.addErrback(check_error_details)
@@ -110,22 +117,23 @@ class TestWebResources(TestCase):
     def test_index_lookup(self):
         # A key index lookup form via GET.
         return self.assertContentMatches(
-            '/pks/lookup?fingerprint=on&op=index&search=0xDFD20543',
-            '''\
+            "/pks/lookup?fingerprint=on&op=index&search=0xDFD20543",
+            """\
 <html>
 ...
 <title>Results for Key 0xDFD20543</title>
 ...
 pub  1024D/DFD20543 2005-04-13 Sample Person (revoked) &lt;sample.revoked@xxxxxxxxxxxxx&gt;
 ...
-''')  # noqa: E501
+""",  # noqa: E501
+        )
 
     def test_content_lookup(self):
         # A key content lookup form via GET.
         return self.assertContentMatches(
-            '/pks/lookup?fingerprint=on&op=get&'
-            'search=0xA419AE861E88BC9E04B9C26FBA2B9389DFD20543',
-            '''\
+            "/pks/lookup?fingerprint=on&op=get&"
+            "search=0xA419AE861E88BC9E04B9C26FBA2B9389DFD20543",
+            """\
 <html>
 ...
 <title>Results for Key 0xA419AE861E88BC9E04B9C26FBA2B9389DFD20543</title>
@@ -135,14 +143,15 @@ Version: GnuPG v1.4.9 (GNU/Linux)
 <BLANKLINE>
 mQGiBEJdmOcRBADkNJPTBuCIefBdRAhvWyD9SSVHh8GHQWS7l9sRLEsirQkKz1yB
 ...
-''')
+""",
+        )
 
     def test_lookup_key_id(self):
         # We can also request a key ID instead of a fingerprint, and it will
         # glob for the fingerprint.
         return self.assertContentMatches(
-            '/pks/lookup?fingerprint=on&op=get&search=0xDFD20543',
-            '''\
+            "/pks/lookup?fingerprint=on&op=get&search=0xDFD20543",
+            """\
 <html>
 ...
 <title>Results for Key 0xDFD20543</title>
@@ -152,21 +161,24 @@ Version: GnuPG v1.4.9 (GNU/Linux)
 <BLANKLINE>
 mQGiBEJdmOcRBADkNJPTBuCIefBdRAhvWyD9SSVHh8GHQWS7l9sRLEsirQkKz1yB
 ...
-''')
+""",
+        )
 
     def test_nonexistent_key(self):
         # If we request a nonexistent key, we get a nice error.
         return self.assertRaises404ErrorForKeyNotFound(
-            '/pks/lookup?fingerprint=on&op=get&search=0xDFD20544')
+            "/pks/lookup?fingerprint=on&op=get&search=0xDFD20544"
+        )
 
     def test_add_key(self):
         # A key submit form via POST (see doc/gpghandler.rst for more
         # information).
         return self.assertContentMatches(
-            '/pks/add',
-            '''\
+            "/pks/add",
+            """\
 <html>
 ...
 <title>Submit a key</title>
 ...
-''')
+""",
+        )
diff --git a/lib/lp/testing/keyserver/web.py b/lib/lp/testing/keyserver/web.py
index c2eb26d..03d76b5 100644
--- a/lib/lp/testing/keyserver/web.py
+++ b/lib/lp/testing/keyserver/web.py
@@ -27,8 +27,8 @@ $ gpg --export -a cprov > 0x681B6469.get
 """
 
 __all__ = [
-    'KeyServerResource',
-    ]
+    "KeyServerResource",
+]
 
 import glob
 import html
@@ -43,10 +43,9 @@ from lp.services.gpg.interfaces import (
     IGPGHandler,
     MoreThanOneGPGKeyFound,
     SecretGPGKeyImportDetected,
-    )
+)
 
-
-GREETING = b'Copyright 2004-2009 Canonical Ltd.\n'
+GREETING = b"Copyright 2004-2009 Canonical Ltd.\n"
 
 
 def locate_key(root, suffix):
@@ -65,9 +64,9 @@ def locate_key(root, suffix):
     if not os.path.exists(path):
         # GPG might request a key ID from us, but we name the keys by
         # fingerprint. Let's glob.
-        if suffix.startswith('0x'):
+        if suffix.startswith("0x"):
             suffix = suffix[2:]
-        keys = glob.glob(os.path.join(root, '*' + suffix))
+        keys = glob.glob(os.path.join(root, "*" + suffix))
         if len(keys) == 1:
             path = keys[0]
         else:
@@ -77,13 +76,11 @@ def locate_key(root, suffix):
 
 
 class _BaseResource(Resource):
-
     def getChild(self, name, request):
         """Redirect trailing slash correctly."""
-        if name == b'':
+        if name == b"":
             return self
-        return Resource.getChild(
-            self, name, request)
+        return Resource.getChild(self, name, request)
 
 
 class KeyServerResource(_BaseResource):
@@ -91,33 +88,33 @@ class KeyServerResource(_BaseResource):
 
     def __init__(self, root):
         _BaseResource.__init__(self)
-        self.putChild(b'pks', PksResource(root))
+        self.putChild(b"pks", PksResource(root))
 
     def render_GET(self, request):
         return GREETING
 
 
 class PksResource(_BaseResource):
-
     def __init__(self, root):
         _BaseResource.__init__(self)
-        self.putChild(b'lookup', LookUp(root))
-        self.putChild(b'add', SubmitKey(root))
+        self.putChild(b"lookup", LookUp(root))
+        self.putChild(b"add", SubmitKey(root))
 
     def render_GET(self, request):
-        return b'Welcome To Fake SKS service.\n'
+        return b"Welcome To Fake SKS service.\n"
 
 
 KEY_NOT_FOUND_BODY = (
     b"<html><head><title>Error handling request</title></head>\n"
     b"<body><h1>Error handling request</h1>No results found: "
-    b"No keys found</body></html>")
+    b"No keys found</body></html>"
+)
 
 
 class LookUp(Resource):
 
     isLeaf = True
-    permitted_actions = ['index', 'get']
+    permitted_actions = ["index", "get"]
 
     def __init__(self, root):
         Resource.__init__(self)
@@ -125,10 +122,10 @@ class LookUp(Resource):
 
     def render_GET(self, request):
         try:
-            action = request.args[b'op'][0].decode('ISO-8859-1')
-            keyid = request.args[b'search'][0].decode('ISO-8859-1')
+            action = request.args[b"op"][0].decode("ISO-8859-1")
+            keyid = request.args[b"search"][0].decode("ISO-8859-1")
         except KeyError:
-            return ('Invalid Arguments %s' % request.args).encode('UTF-8')
+            return ("Invalid Arguments %s" % request.args).encode("UTF-8")
 
         return self.processRequest(action, keyid, request)
 
@@ -138,20 +135,22 @@ class LookUp(Resource):
         sleep(0.02)
         if (action not in self.permitted_actions) or not keyid:
             message = 'Forbidden: "%s" on ID "%s"' % (action, keyid)
-            return message.encode('UTF-8')
+            return message.encode("UTF-8")
 
-        filename = '%s.%s' % (keyid, action)
+        filename = "%s.%s" % (keyid, action)
 
         path = locate_key(self.root, filename)
         if path is not None:
             with open(path) as f:
                 content = html.escape(f.read(), quote=False)
-            page = ('<html>\n<head>\n'
-                    '<title>Results for Key %s</title>\n'
-                    '</head>\n<body>'
-                    '<h1>Results for Key %s</h1>\n'
-                    '<pre>\n%s\n</pre>\n</html>') % (keyid, keyid, content)
-            return page.encode('UTF-8')
+            page = (
+                "<html>\n<head>\n"
+                "<title>Results for Key %s</title>\n"
+                "</head>\n<body>"
+                "<h1>Results for Key %s</h1>\n"
+                "<pre>\n%s\n</pre>\n</html>"
+            ) % (keyid, keyid, content)
+            return page.encode("UTF-8")
         else:
             request.setResponseCode(404)
             return KEY_NOT_FOUND_BODY
@@ -182,27 +181,30 @@ class SubmitKey(Resource):
         self.root = root
 
     def render_GET(self, request):
-        return (SUBMIT_KEY_PAGE % {'banner': ''}).encode('UTF-8')
+        return (SUBMIT_KEY_PAGE % {"banner": ""}).encode("UTF-8")
 
     def render_POST(self, request):
         try:
-            keytext = request.args[b'keytext'][0]
+            keytext = request.args[b"keytext"][0]
         except KeyError:
-            return ('Invalid Arguments %s' % request.args).encode('UTF-8')
+            return ("Invalid Arguments %s" % request.args).encode("UTF-8")
         return self.storeKey(keytext)
 
     def storeKey(self, keytext):
         gpghandler = getUtility(IGPGHandler)
         try:
             key = gpghandler.importPublicKey(keytext)
-        except (GPGKeyNotFoundError, SecretGPGKeyImportDetected,
-                MoreThanOneGPGKeyFound) as err:
-            return (SUBMIT_KEY_PAGE % {'banner': str(err)}).encode('UTF-8')
-
-        filename = '0x%s.get' % key.fingerprint
+        except (
+            GPGKeyNotFoundError,
+            SecretGPGKeyImportDetected,
+            MoreThanOneGPGKeyFound,
+        ) as err:
+            return (SUBMIT_KEY_PAGE % {"banner": str(err)}).encode("UTF-8")
+
+        filename = "0x%s.get" % key.fingerprint
         path = os.path.join(self.root, filename)
 
-        with open(path, 'wb') as fp:
+        with open(path, "wb") as fp:
             fp.write(keytext)
 
-        return (SUBMIT_KEY_PAGE % {'banner': 'Key added'}).encode('UTF-8')
+        return (SUBMIT_KEY_PAGE % {"banner": "Key added"}).encode("UTF-8")
diff --git a/lib/lp/testing/layers.py b/lib/lp/testing/layers.py
index abdc224..4539ae2 100644
--- a/lib/lp/testing/layers.py
+++ b/lib/lp/testing/layers.py
@@ -19,37 +19,35 @@ of one, forcing us to attempt to make some sort of layer tree.
 """
 
 __all__ = [
-    'AppServerLayer',
-    'BaseLayer',
-    'BingLaunchpadFunctionalLayer',
-    'BingServiceLayer',
-    'DatabaseFunctionalLayer',
-    'DatabaseLayer',
-    'FunctionalLayer',
-    'LaunchpadFunctionalLayer',
-    'LaunchpadLayer',
-    'LaunchpadScriptLayer',
-    'LaunchpadTestSetup',
-    'LaunchpadZopelessLayer',
-    'LayerInvariantError',
-    'LayerIsolationError',
-    'LibrarianLayer',
-    'PageTestLayer',
-    'RabbitMQLayer',
-    'TwistedLayer',
-    'YUITestLayer',
-    'YUIAppServerLayer',
-    'ZopelessAppServerLayer',
-    'ZopelessDatabaseLayer',
-    'ZopelessLayer',
-    'reconnect_stores',
-    ]
+    "AppServerLayer",
+    "BaseLayer",
+    "BingLaunchpadFunctionalLayer",
+    "BingServiceLayer",
+    "DatabaseFunctionalLayer",
+    "DatabaseLayer",
+    "FunctionalLayer",
+    "LaunchpadFunctionalLayer",
+    "LaunchpadLayer",
+    "LaunchpadScriptLayer",
+    "LaunchpadTestSetup",
+    "LaunchpadZopelessLayer",
+    "LayerInvariantError",
+    "LayerIsolationError",
+    "LibrarianLayer",
+    "PageTestLayer",
+    "RabbitMQLayer",
+    "TwistedLayer",
+    "YUITestLayer",
+    "YUIAppServerLayer",
+    "ZopelessAppServerLayer",
+    "ZopelessDatabaseLayer",
+    "ZopelessLayer",
+    "reconnect_stores",
+]
 
 import base64
-from cProfile import Profile
 import datetime
 import errno
-from functools import partial
 import gc
 import os
 import select
@@ -58,103 +56,70 @@ import socket
 import subprocess
 import sys
 import tempfile
-from textwrap import dedent
 import threading
 import time
-from unittest import (
-    TestCase,
-    TestResult,
-    )
-from urllib.error import (
-    HTTPError,
-    URLError,
-    )
+from cProfile import Profile
+from functools import partial
+from textwrap import dedent
+from unittest import TestCase, TestResult
+from urllib.error import HTTPError, URLError
 from urllib.parse import urlparse
 from urllib.request import urlopen
 
-from fixtures import (
-    Fixture,
-    MonkeyPatch,
-    )
 import psycopg2
+import transaction
+import wsgi_intercept
+import zope.testbrowser.wsgi
+from fixtures import Fixture, MonkeyPatch
 from requests import Session
 from requests.adapters import HTTPAdapter
 from storm.uri import URI
 from talisker.context import Context
-import transaction
 from webob.request import environ_from_url as orig_environ_from_url
-import wsgi_intercept
 from wsgi_intercept import httplib2_intercept
 from zope.app.wsgi import WSGIPublisherApplication
-from zope.component import (
-    getUtility,
-    globalregistry,
-    provideUtility,
-    )
+from zope.component import getUtility, globalregistry, provideUtility
 from zope.component.testlayer import ZCMLFileLayer
 from zope.event import notify
 from zope.interface.interfaces import ComponentLookupError
 from zope.processlifetime import DatabaseOpened
-from zope.security.management import (
-    endInteraction,
-    getSecurityPolicy,
-    )
+from zope.security.management import endInteraction, getSecurityPolicy
 from zope.testbrowser.browser import HostNotAllowed
-import zope.testbrowser.wsgi
 from zope.testbrowser.wsgi import AuthorizationMiddleware
 
+import lp.services.mail.stub
+import lp.services.webapp.session
+import zcml
 from lp.services import pidfile
-from lp.services.config import (
-    config,
-    dbconfig,
-    LaunchpadConfig,
-    )
-from lp.services.config.fixture import (
-    ConfigFixture,
-    ConfigUseFixture,
-    )
+from lp.services.config import LaunchpadConfig, config, dbconfig
+from lp.services.config.fixture import ConfigFixture, ConfigUseFixture
 from lp.services.database.interfaces import IStore
-from lp.services.database.sqlbase import (
-    disconnect_stores,
-    session_store,
-    )
+from lp.services.database.sqlbase import disconnect_stores, session_store
 from lp.services.encoding import wsgi_native_string
 from lp.services.job.tests import celery_worker
 from lp.services.librarian.model import LibraryFileAlias
 from lp.services.librarianserver.testing.server import LibrarianServerFixture
-from lp.services.mail.mailbox import (
-    IMailBox,
-    TestMailBox,
-    )
+from lp.services.mail.mailbox import IMailBox, TestMailBox
 from lp.services.mail.sendmail import set_immediate_mail_delivery
-import lp.services.mail.stub
 from lp.services.memcache.client import memcache_client_factory
 from lp.services.osutils import kill_by_pidfile
 from lp.services.rabbit.server import RabbitServer
 from lp.services.scripts import execute_zcml_for_scripts
 from lp.services.sitesearch.tests.bingserviceharness import (
     BingServiceTestSetup,
-    )
+)
 from lp.services.testing.profiled import profiled
 from lp.services.timeout import (
     get_default_timeout_function,
     set_default_timeout_function,
-    )
+)
 from lp.services.webapp.authorization import LaunchpadPermissiveSecurityPolicy
 from lp.services.webapp.interfaces import IOpenLaunchBag
 from lp.services.webapp.servers import (
     register_launchpad_request_publication_factories,
-    )
-import lp.services.webapp.session
-from lp.testing import (
-    ANONYMOUS,
-    login,
-    logout,
-    reset_logging,
-    )
+)
+from lp.testing import ANONYMOUS, login, logout, reset_logging
 from lp.testing.pgsql import PgTestSetup
-import zcml
-
 
 WAIT_INTERVAL = datetime.timedelta(seconds=180)
 
@@ -169,6 +134,7 @@ class LayerInvariantError(LayerError):
     This indicates the Layer infrastructure has messed up. The test run
     should be aborted.
     """
+
     pass
 
 
@@ -204,13 +170,13 @@ def reconnect_stores(reset=False):
         dbconfig.reset()
 
     main_store = IStore(LibraryFileAlias)
-    assert main_store is not None, 'Failed to reconnect'
+    assert main_store is not None, "Failed to reconnect"
 
     # Confirm that SQLOS is again talking to the database (it connects
     # as soon as SQLBase._connection is accessed
-    r = main_store.execute('SELECT count(*) FROM LaunchpadDatabaseRevision')
-    assert r.get_one()[0] > 0, 'Storm is not talking to the database'
-    assert session_store() is not None, 'Failed to reconnect'
+    r = main_store.execute("SELECT count(*) FROM LaunchpadDatabaseRevision")
+    assert r.get_one()[0] > 0, "Storm is not talking to the database"
+    assert session_store() is not None, "Failed to reconnect"
 
 
 class BaseLayer:
@@ -224,6 +190,7 @@ class BaseLayer:
     get these checks. The Z3 test runner should be updated so that a layer
     can be specified to use for unit tests.
     """
+
     # Set to True when we are running tests in this layer.
     isSetUp = False
 
@@ -268,13 +235,14 @@ class BaseLayer:
     def setUp(cls):
         # Set the default appserver config instance name.
         # May be changed as required eg when running parallel tests.
-        cls.appserver_config_name = 'testrunner-appserver'
+        cls.appserver_config_name = "testrunner-appserver"
         BaseLayer.isSetUp = True
         cls.fixture = Fixture()
         cls.fixture.setUp()
-        cls.fixture.addCleanup(setattr, cls, 'fixture', None)
+        cls.fixture.addCleanup(setattr, cls, "fixture", None)
         BaseLayer.persist_test_services = (
-            os.environ.get('LP_PERSISTENT_TEST_SERVICES') is not None)
+            os.environ.get("LP_PERSISTENT_TEST_SERVICES") is not None
+        )
         # We can only do unique test allocation and parallelisation if
         # LP_PERSISTENT_TEST_SERVICES is off.
         if not BaseLayer.persist_test_services:
@@ -283,29 +251,33 @@ class BaseLayer:
             # PostgreSQL's 63-character limit for identifiers.  Linux
             # currently allows up to 2^22 PIDs, so PIDs may be up to seven
             # digits long.
-            test_instance = '%d_%s' % (
-                os.getpid(), base64.b16encode(os.urandom(12)).decode().lower())
-            os.environ['LP_TEST_INSTANCE'] = test_instance
-            cls.fixture.addCleanup(os.environ.pop, 'LP_TEST_INSTANCE', '')
+            test_instance = "%d_%s" % (
+                os.getpid(),
+                base64.b16encode(os.urandom(12)).decode().lower(),
+            )
+            os.environ["LP_TEST_INSTANCE"] = test_instance
+            cls.fixture.addCleanup(os.environ.pop, "LP_TEST_INSTANCE", "")
             # Kill any Memcached or Librarian left running from a previous
             # test run, or from the parent test process if the current
             # layer is being run in a subprocess. No need to be polite
             # about killing memcached - just do it quickly.
             kill_by_pidfile(MemcachedLayer.getPidFile(), num_polls=0)
-            config_name = 'testrunner_%s' % test_instance
-            cls.make_config(config_name, 'testrunner', 'config_fixture')
-            app_config_name = 'testrunner-appserver_%s' % test_instance
+            config_name = "testrunner_%s" % test_instance
+            cls.make_config(config_name, "testrunner", "config_fixture")
+            app_config_name = "testrunner-appserver_%s" % test_instance
             cls.make_config(
-                app_config_name, 'testrunner-appserver',
-                'appserver_config_fixture')
+                app_config_name,
+                "testrunner-appserver",
+                "appserver_config_fixture",
+            )
             cls.appserver_config_name = app_config_name
         else:
-            config_name = 'testrunner'
-            app_config_name = 'testrunner-appserver'
+            config_name = "testrunner"
+            app_config_name = "testrunner-appserver"
         cls.config_name = config_name
-        cls.fixture.addCleanup(setattr, cls, 'config_name', None)
+        cls.fixture.addCleanup(setattr, cls, "config_name", None)
         cls.appserver_config_name = app_config_name
-        cls.fixture.addCleanup(setattr, cls, 'appserver_config_name', None)
+        cls.fixture.addCleanup(setattr, cls, "appserver_config_name", None)
         use_fixture = ConfigUseFixture(config_name)
         cls.fixture.addCleanup(use_fixture.cleanUp)
         use_fixture.setUp()
@@ -338,11 +310,12 @@ class BaseLayer:
         # name.  The testrunner doesn't provide this, so we have to do
         # some snooping.
         import inspect
+
         frame = inspect.currentframe()
         try:
-            while frame.f_code.co_name != 'startTest':
+            while frame.f_code.co_name != "startTest":
                 frame = frame.f_back
-            BaseLayer.test_name = str(frame.f_locals['test'])
+            BaseLayer.test_name = str(frame.f_locals["test"])
         finally:
             del frame  # As per no-leak stack inspection in Python reference.
 
@@ -361,7 +334,8 @@ class BaseLayer:
         # run can continue.
         if cwd != BaseLayer.original_working_directory:
             BaseLayer.flagTestIsolationFailure(
-                    "Test failed to restore working directory.")
+                "Test failed to restore working directory."
+            )
             os.chdir(BaseLayer.original_working_directory)
 
         BaseLayer.original_working_directory = None
@@ -372,8 +346,10 @@ class BaseLayer:
 
         def new_live_threads():
             return [
-                thread for thread in threading.enumerate()
-                    if thread not in BaseLayer._threads and thread.is_alive()]
+                thread
+                for thread in threading.enumerate()
+                if thread not in BaseLayer._threads and thread.is_alive()
+            ]
 
         if BaseLayer.disable_thread_check:
             new_threads = None
@@ -401,19 +377,20 @@ class BaseLayer:
             # tests that leave threads behind from failing. Its use
             # should only ever be temporary.
             if BaseLayer.disable_thread_check:
-                print((
-                    "ERROR DISABLED: "
-                    "Test left new live threads: %s") % repr(new_threads))
+                print(
+                    ("ERROR DISABLED: " "Test left new live threads: %s")
+                    % repr(new_threads)
+                )
             else:
                 BaseLayer.flagTestIsolationFailure(
-                    "Test left new live threads: %s" % repr(new_threads))
+                    "Test left new live threads: %s" % repr(new_threads)
+                )
 
         BaseLayer.disable_thread_check = False
         del BaseLayer._threads
 
         if signal.getsignal(signal.SIGCHLD) != signal.SIG_DFL:
-            BaseLayer.flagTestIsolationFailure(
-                "Test left SIGCHLD handler.")
+            BaseLayer.flagTestIsolationFailure("Test left SIGCHLD handler.")
 
         # Objects with __del__ methods cannot participate in reference cycles.
         # Fail tests with memory leaks now rather than when Launchpad crashes
@@ -423,10 +400,14 @@ class BaseLayer:
             gc.collect()  # Expensive, so only do if there might be garbage.
             if gc.garbage:
                 BaseLayer.flagTestIsolationFailure(
-                        "Test left uncollectable garbage\n"
-                        "%s (referenced from %s; referencing %s)"
-                        % (gc.garbage, gc.get_referrers(*gc.garbage),
-                           gc.get_referents(*gc.garbage)))
+                    "Test left uncollectable garbage\n"
+                    "%s (referenced from %s; referencing %s)"
+                    % (
+                        gc.garbage,
+                        gc.get_referrers(*gc.garbage),
+                        gc.get_referents(*gc.garbage),
+                    )
+                )
 
     @classmethod
     @profiled
@@ -439,23 +420,28 @@ class BaseLayer:
         """
         if FunctionalLayer.isSetUp and ZopelessLayer.isSetUp:
             raise LayerInvariantError(
-                "Both Zopefull and Zopeless CA environments setup")
+                "Both Zopefull and Zopeless CA environments setup"
+            )
 
         # Detect a test that causes the component architecture to be loaded.
         # This breaks test isolation, as it cannot be torn down.
-        if (is_ca_available()
+        if (
+            is_ca_available()
             and not FunctionalLayer.isSetUp
-            and not ZopelessLayer.isSetUp):
+            and not ZopelessLayer.isSetUp
+        ):
             raise LayerIsolationError(
                 "Component architecture should not be loaded by tests. "
-                "This should only be loaded by the Layer.")
+                "This should only be loaded by the Layer."
+            )
 
         # Detect a test that forgot to reset the default socket timeout.
         # This safety belt is cheap and protects us from very nasty
         # intermittent test failures: see bug #140068 for an example.
         if socket.getdefaulttimeout() is not None:
             raise LayerIsolationError(
-                "Test didn't reset the socket default timeout.")
+                "Test didn't reset the socket default timeout."
+            )
 
     @classmethod
     def flagTestIsolationFailure(cls, message):
@@ -480,12 +466,13 @@ class BaseLayer:
     def getCurrentTestResult(cls):
         """Return the TestResult currently in play."""
         import inspect
+
         frame = inspect.currentframe()
         try:
             while True:
-                f_self = frame.f_locals.get('self', None)
+                f_self = frame.f_locals.get("self", None)
                 if isinstance(f_self, TestResult):
-                    return frame.f_locals['self']
+                    return frame.f_locals["self"]
                 frame = frame.f_back
         finally:
             del frame  # As per no-leak stack inspection in Python reference.
@@ -494,17 +481,18 @@ class BaseLayer:
     def getCurrentTestCase(cls):
         """Return the test currently in play."""
         import inspect
+
         frame = inspect.currentframe()
         try:
             while True:
-                f_self = frame.f_locals.get('self', None)
+                f_self = frame.f_locals.get("self", None)
                 if isinstance(f_self, TestCase):
                     return f_self
-                f_test = frame.f_locals.get('test', None)
+                f_test = frame.f_locals.get("test", None)
                 if isinstance(f_test, TestCase):
                     return f_test
                 frame = frame.f_back
-            return frame.f_locals['test']
+            return frame.f_locals["test"]
         finally:
             del frame  # As per no-leak stack inspection in Python reference.
 
@@ -514,10 +502,9 @@ class BaseLayer:
         return LaunchpadConfig(cls.appserver_config_name)
 
     @classmethod
-    def appserver_root_url(cls, facet='mainsite', ensureSlash=False):
+    def appserver_root_url(cls, facet="mainsite", ensureSlash=False):
         """Return the correct app server root url for the given facet."""
-        return cls.appserver_config().appserver_root_url(
-                facet, ensureSlash)
+        return cls.appserver_config().appserver_root_url(facet, ensureSlash)
 
 
 class MemcachedLayer(BaseLayer):
@@ -543,8 +530,9 @@ class MemcachedLayer(BaseLayer):
         cls._is_setup = True
         # Create a client
         MemcachedLayer.client = memcache_client_factory()
-        if (BaseLayer.persist_test_services and
-            os.path.exists(MemcachedLayer.getPidFile())):
+        if BaseLayer.persist_test_services and os.path.exists(
+            MemcachedLayer.getPidFile()
+        ):
             return
 
         # First, check to see if there is a memcached already running.
@@ -562,25 +550,31 @@ class MemcachedLayer(BaseLayer):
         # the item size at a megabyte.  Note that the argument to -m is in
         # megabytes.
         item_size = min(
-            config.memcached.memory_size * 1024 * 1024 / 4,
-            1024 * 1024)
+            config.memcached.memory_size * 1024 * 1024 / 4, 1024 * 1024
+        )
         cmd = [
-            'memcached',
-            '-m', str(config.memcached.memory_size),
-            '-I', str(item_size),
-            '-l', str(config.memcached.address),
-            '-p', str(config.memcached.port),
-            '-U', str(config.memcached.port),
-            ]
+            "memcached",
+            "-m",
+            str(config.memcached.memory_size),
+            "-I",
+            str(item_size),
+            "-l",
+            str(config.memcached.address),
+            "-p",
+            str(config.memcached.port),
+            "-U",
+            str(config.memcached.port),
+        ]
         if config.memcached.verbose:
-            cmd.append('-vv')
+            cmd.append("-vv")
             stdout = sys.stdout
             stderr = sys.stderr
         else:
             stdout = tempfile.NamedTemporaryFile()
             stderr = tempfile.NamedTemporaryFile()
         MemcachedLayer._memcached_process = subprocess.Popen(
-            cmd, stdin=subprocess.PIPE, stdout=stdout, stderr=stderr)
+            cmd, stdin=subprocess.PIPE, stdout=stdout, stderr=stderr
+        )
         MemcachedLayer._memcached_process.stdin.close()
 
         # Wait for the memcached to become operational.
@@ -592,12 +586,13 @@ class MemcachedLayer(BaseLayer):
                 if MemcachedLayer._memcached_process.returncode is not None:
                     raise LayerInvariantError(
                         "memcached never started or has died.",
-                        MemcachedLayer._memcached_process.stdout.read())
+                        MemcachedLayer._memcached_process.stdout.read(),
+                    )
                 time.sleep(0.1)
 
         # Store the pidfile for other processes to kill.
         pid_file = MemcachedLayer.getPidFile()
-        with open(pid_file, 'w') as f:
+        with open(pid_file, "w") as f:
             f.write(str(MemcachedLayer._memcached_process.pid))
 
     @classmethod
@@ -634,7 +629,7 @@ class MemcachedLayer(BaseLayer):
 
     @classmethod
     def getPidFile(cls):
-        return os.path.join(config.root, '.memcache.pid')
+        return os.path.join(config.root, ".memcache.pid")
 
     @classmethod
     def purge(cls):
@@ -655,10 +650,10 @@ class RabbitMQLayer(BaseLayer):
     @profiled
     def setUp(cls):
         cls.rabbit.setUp()
-        cls.config_fixture.add_section(
-            cls.rabbit.config.service_config)
+        cls.config_fixture.add_section(cls.rabbit.config.service_config)
         cls.appserver_config_fixture.add_section(
-            cls.rabbit.config.service_config)
+            cls.rabbit.config.service_config
+        )
         cls._is_setup = True
 
     @classmethod
@@ -667,9 +662,9 @@ class RabbitMQLayer(BaseLayer):
         if not cls._is_setup:
             return
         cls.appserver_config_fixture.remove_section(
-            cls.rabbit.config.service_config)
-        cls.config_fixture.remove_section(
-            cls.rabbit.config.service_config)
+            cls.rabbit.config.service_config
+        )
+        cls.config_fixture.remove_section(cls.rabbit.config.service_config)
         cls.rabbit.cleanUp()
         cls._is_setup = False
 
@@ -703,9 +698,13 @@ class DatabaseLayer(BaseLayer):
     def setUp(cls):
         cls._is_setup = True
         # Allocate a template for this test instance
-        if os.environ.get('LP_TEST_INSTANCE'):
-            template_name = '_'.join([LaunchpadTestSetup.template,
-                os.environ.get('LP_TEST_INSTANCE')])
+        if os.environ.get("LP_TEST_INSTANCE"):
+            template_name = "_".join(
+                [
+                    LaunchpadTestSetup.template,
+                    os.environ.get("LP_TEST_INSTANCE"),
+                ]
+            )
             cls._db_template_fixture = LaunchpadTestSetup(dbname=template_name)
             cls._db_template_fixture.setUp()
         else:
@@ -739,7 +738,7 @@ class DatabaseLayer(BaseLayer):
         cls.force_dirty_database()
         cls._db_fixture.tearDown()
         cls._db_fixture = None
-        if os.environ.get('LP_TEST_INSTANCE'):
+        if os.environ.get("LP_TEST_INSTANCE"):
             cls._db_template_fixture.tearDown()
             cls._db_template_fixture = None
 
@@ -755,10 +754,12 @@ class DatabaseLayer(BaseLayer):
 
         # Fail tests that forget to uninstall their database policies.
         from lp.services.webapp.adapter import StoreSelector
+
         while StoreSelector.get_current() is not None:
             BaseLayer.flagTestIsolationFailure(
                 "Database policy %s still installed"
-                % repr(StoreSelector.pop()))
+                % repr(StoreSelector.pop())
+            )
         # Reset/bring up the db - makes it available for either the next test,
         # or a subordinate layer which builds on the db. This wastes one setup
         # per db layer teardown per run, but thats tolerable.
@@ -788,7 +789,8 @@ class LibrarianLayer(DatabaseLayer):
     @profiled
     def setUp(cls):
         cls.librarian_fixture = LibrarianServerFixture(
-            BaseLayer.config_fixture)
+            BaseLayer.config_fixture
+        )
         cls.librarian_fixture.setUp()
         cls._check_and_reset()
 
@@ -805,7 +807,8 @@ class LibrarianLayer(DatabaseLayer):
         if cls.librarian_fixture is None:
             return
         cls.appserver_config_fixture.remove_section(
-            cls.appserver_service_config)
+            cls.appserver_service_config
+        )
         try:
             cls._check_and_reset()
         finally:
@@ -820,16 +823,17 @@ class LibrarianLayer(DatabaseLayer):
         try:
             session = Session()
             session.mount(
-                config.librarian.download_url,
-                HTTPAdapter(max_retries=3))
+                config.librarian.download_url, HTTPAdapter(max_retries=3)
+            )
             session.get(config.librarian.download_url).content
         except Exception as e:
             raise LayerIsolationError(
-                    "Librarian has been killed or has hung."
-                    "Tests should use LibrarianLayer.hide() and "
-                    "LibrarianLayer.reveal() where possible, and ensure "
-                    "the Librarian is restarted if it absolutely must be "
-                    "shutdown: " + str(e))
+                "Librarian has been killed or has hung."
+                "Tests should use LibrarianLayer.hide() and "
+                "LibrarianLayer.reveal() where possible, and ensure "
+                "the Librarian is restarted if it absolutely must be "
+                "shutdown: " + str(e)
+            )
         else:
             cls.librarian_fixture.reset()
 
@@ -865,17 +869,22 @@ class LibrarianLayer(DatabaseLayer):
             # Bind to a socket, but don't listen to it.  This way we
             # guarantee that connections to the given port will fail.
             cls._fake_upload_socket = socket.socket(
-                socket.AF_INET, socket.SOCK_STREAM)
-            assert config.librarian.upload_host == 'localhost', (
-                'Can only hide librarian if it is running locally')
-            cls._fake_upload_socket.bind(('127.0.0.1', 0))
+                socket.AF_INET, socket.SOCK_STREAM
+            )
+            assert (
+                config.librarian.upload_host == "localhost"
+            ), "Can only hide librarian if it is running locally"
+            cls._fake_upload_socket.bind(("127.0.0.1", 0))
 
         host, port = cls._fake_upload_socket.getsockname()
-        librarian_data = dedent("""
+        librarian_data = dedent(
+            """
             [librarian]
             upload_port: %s
-            """ % port)
-        config.push('hide_librarian', librarian_data)
+            """
+            % port
+        )
+        config.push("hide_librarian", librarian_data)
 
     @classmethod
     @profiled
@@ -885,7 +894,7 @@ class LibrarianLayer(DatabaseLayer):
         This just involves restoring the config to the original value.
         """
         cls._hidden = False
-        config.pop('hide_librarian')
+        config.pop("hide_librarian")
 
 
 def test_default_timeout():
@@ -921,7 +930,8 @@ class LaunchpadLayer(LibrarianLayer, MemcachedLayer, RabbitMQLayer):
         # By default, don't make external service tests timeout.
         if get_default_timeout_function() is not None:
             raise LayerIsolationError(
-                "Global default timeout function should be None.")
+                "Global default timeout function should be None."
+            )
         set_default_timeout_function(test_default_timeout)
 
     @classmethod
@@ -929,7 +939,8 @@ class LaunchpadLayer(LibrarianLayer, MemcachedLayer, RabbitMQLayer):
     def testTearDown(cls):
         if get_default_timeout_function() is not test_default_timeout:
             raise LayerIsolationError(
-                "Test didn't reset default timeout function.")
+                "Test didn't reset default timeout function."
+            )
         set_default_timeout_function(None)
 
     # A database connection to the session database, created by the first
@@ -946,12 +957,16 @@ class LaunchpadLayer(LibrarianLayer, MemcachedLayer, RabbitMQLayer):
         """
         if LaunchpadLayer._raw_sessiondb_connection is None:
             from lp.services.webapp.adapter import LaunchpadSessionDatabase
+
             launchpad_session_database = LaunchpadSessionDatabase(
-                URI('launchpad-session:'))
+                URI("launchpad-session:")
+            )
             LaunchpadLayer._raw_sessiondb_connection = (
-                launchpad_session_database.raw_connect())
+                launchpad_session_database.raw_connect()
+            )
         LaunchpadLayer._raw_sessiondb_connection.cursor().execute(
-            "DELETE FROM SessionData")
+            "DELETE FROM SessionData"
+        )
 
 
 class BasicTaliskerMiddleware:
@@ -996,7 +1011,7 @@ class RemoteAddrMiddleware:
         self.app = app
 
     def __call__(self, environ, start_response):
-        environ.setdefault('REMOTE_ADDR', wsgi_native_string('127.0.0.1'))
+        environ.setdefault("REMOTE_ADDR", wsgi_native_string("127.0.0.1"))
         return self.app(environ, start_response)
 
 
@@ -1037,7 +1052,7 @@ class _FunctionalBrowserLayer(zope.testbrowser.wsgi.Layer, ZCMLFileLayer):
             SortHeadersMiddleware,
             TransactionMiddleware,
             BasicTaliskerMiddleware,
-            ]
+        ]
 
     def setUp(self):
         super().setUp()
@@ -1093,7 +1108,8 @@ class FunctionalLayer(BaseLayer):
         # around this by creating a BrowserLayer instance here rather than
         # having this layer subclass it.
         FunctionalLayer.browser_layer = _FunctionalBrowserLayer(
-            zcml, zcml_file='ftesting.zcml')
+            zcml, zcml_file="ftesting.zcml"
+        )
         FunctionalLayer.browser_layer.setUp()
 
         # Assert that ZCMLFileLayer did what it says it does
@@ -1113,9 +1129,11 @@ class FunctionalLayer(BaseLayer):
         # that use lazr.restfulclient or launchpadlib) still talk to the app
         # server over HTTP and need to be intercepted.
         wsgi_intercept.add_wsgi_intercept(
-            'localhost', 80, _FunctionalBrowserLayer.get_app)
+            "localhost", 80, _FunctionalBrowserLayer.get_app
+        )
         wsgi_intercept.add_wsgi_intercept(
-            'api.launchpad.test', 80, _FunctionalBrowserLayer.get_app)
+            "api.launchpad.test", 80, _FunctionalBrowserLayer.get_app
+        )
         httplib2_intercept.install()
 
         # webob.request.environ_from_url defaults to HTTP/1.0, which is
@@ -1124,11 +1142,12 @@ class FunctionalLayer(BaseLayer):
         # HTTP/1.1 instead.
         def environ_from_url_http11(path):
             env = orig_environ_from_url(path)
-            env['SERVER_PROTOCOL'] = 'HTTP/1.1'
+            env["SERVER_PROTOCOL"] = "HTTP/1.1"
             return env
 
         FunctionalLayer._environ_from_url_http11 = MonkeyPatch(
-            'webob.request.environ_from_url', environ_from_url_http11)
+            "webob.request.environ_from_url", environ_from_url_http11
+        )
         FunctionalLayer._environ_from_url_http11.setUp()
 
     @classmethod
@@ -1136,8 +1155,8 @@ class FunctionalLayer(BaseLayer):
     def tearDown(cls):
         FunctionalLayer.isSetUp = False
         FunctionalLayer._environ_from_url_http11.cleanUp()
-        wsgi_intercept.remove_wsgi_intercept('localhost', 80)
-        wsgi_intercept.remove_wsgi_intercept('api.launchpad.test', 80)
+        wsgi_intercept.remove_wsgi_intercept("localhost", 80)
+        wsgi_intercept.remove_wsgi_intercept("api.launchpad.test", 80)
         httplib2_intercept.uninstall()
         FunctionalLayer.browser_layer.tearDown()
         # Signal Layer cannot be torn down fully
@@ -1152,14 +1171,15 @@ class FunctionalLayer(BaseLayer):
         # Allow the WSGI test browser to talk to our various test hosts.
         def _assertAllowed(self, url):
             parsed = urlparse(url)
-            host = parsed.netloc.partition(':')[0]
-            if host == 'localhost' or host.endswith('.test'):
+            host = parsed.netloc.partition(":")[0]
+            if host == "localhost" or host.endswith(".test"):
                 return
             raise HostNotAllowed(url)
 
         FunctionalLayer._testbrowser_allowed = MonkeyPatch(
-            'zope.testbrowser.browser.TestbrowserApp._assertAllowed',
-            _assertAllowed)
+            "zope.testbrowser.browser.TestbrowserApp._assertAllowed",
+            _assertAllowed,
+        )
         FunctionalLayer._testbrowser_allowed.setUp()
         FunctionalLayer.browser_layer.testSetUp()
 
@@ -1167,7 +1187,8 @@ class FunctionalLayer(BaseLayer):
         # mighty nasty has happened if this is triggered.
         if not is_ca_available():
             raise LayerInvariantError(
-                "Component architecture not loaded or totally screwed")
+                "Component architecture not loaded or totally screwed"
+            )
 
     @classmethod
     @profiled
@@ -1179,7 +1200,8 @@ class FunctionalLayer(BaseLayer):
         # mighty nasty has happened if this is triggered.
         if not is_ca_available():
             raise LayerInvariantError(
-                "Component architecture not loaded or totally screwed")
+                "Component architecture not loaded or totally screwed"
+            )
 
         transaction.abort()
 
@@ -1202,7 +1224,8 @@ class ZopelessLayer(BaseLayer):
         if not is_ca_available():
             raise LayerInvariantError(
                 "Component architecture not loaded by "
-                "execute_zcml_for_scripts")
+                "execute_zcml_for_scripts"
+            )
 
         # If our request publication factories were defined using
         # ZCML, they'd be set up by execute_zcml_for_scripts(). Since
@@ -1224,14 +1247,15 @@ class ZopelessLayer(BaseLayer):
         # mighty nasty has happened if this is triggered.
         if not is_ca_available():
             raise LayerInvariantError(
-                "Component architecture not loaded or totally screwed")
+                "Component architecture not loaded or totally screwed"
+            )
         # This should not happen here, it should be caught by the
         # testTearDown() method. If it does, something very nasty
         # happened.
         if getSecurityPolicy() != LaunchpadPermissiveSecurityPolicy:
             raise LayerInvariantError(
                 "Previous test removed the LaunchpadPermissiveSecurityPolicy."
-                )
+            )
 
         # execute_zcml_for_scripts() sets up an interaction for the
         # anonymous user. A previous script may have changed or removed
@@ -1245,13 +1269,15 @@ class ZopelessLayer(BaseLayer):
         # mighty nasty has happened if this is triggered.
         if not is_ca_available():
             raise LayerInvariantError(
-                "Component architecture not loaded or totally screwed")
+                "Component architecture not loaded or totally screwed"
+            )
         # Make sure that a test that changed the security policy, reset it
         # back to its default value.
         if getSecurityPolicy() != LaunchpadPermissiveSecurityPolicy:
             raise LayerInvariantError(
                 "This test removed the LaunchpadPermissiveSecurityPolicy and "
-                "didn't restore it.")
+                "didn't restore it."
+            )
         logout()
 
 
@@ -1293,10 +1319,7 @@ class TwistedLayer(BaseLayer):
     @profiled
     def testSetUp(cls):
         TwistedLayer._save_signals()
-        from twisted.internet import (
-            interfaces,
-            reactor,
-            )
+        from twisted.internet import interfaces, reactor
         from twisted.python import threadpool
 
         # zope.exception demands more of frame objects than
@@ -1304,11 +1327,11 @@ class TwistedLayer(BaseLayer):
         # to make it work with them as of 2009-09-16.  See
         # https://bugs.launchpad.net/bugs/425113.
         cls._patch = MonkeyPatch(
-            'twisted.python.failure._Frame.f_locals',
-            property(lambda self: {}))
+            "twisted.python.failure._Frame.f_locals", property(lambda self: {})
+        )
         cls._patch.setUp()
         if interfaces.IReactorThreads.providedBy(reactor):
-            pool = getattr(reactor, 'threadpool', None)
+            pool = getattr(reactor, "threadpool", None)
             # If the Twisted threadpool has been obliterated (probably by
             # testTearDown), then re-build it using the values that Twisted
             # uses.
@@ -1321,13 +1344,11 @@ class TwistedLayer(BaseLayer):
     def testTearDown(cls):
         # Shutdown and obliterate the Twisted threadpool, to plug up leaking
         # threads.
-        from twisted.internet import (
-            interfaces,
-            reactor,
-            )
+        from twisted.internet import interfaces, reactor
+
         if interfaces.IReactorThreads.providedBy(reactor):
             reactor.suggestThreadPoolSize(0)
-            pool = getattr(reactor, 'threadpool', None)
+            pool = getattr(reactor, "threadpool", None)
             if pool is not None:
                 reactor.threadpool.stop()
                 reactor.threadpool = None
@@ -1403,6 +1424,7 @@ class LaunchpadFunctionalLayer(LaunchpadLayer, FunctionalLayer):
     def testSetUp(cls):
         # Reset any statistics
         from lp.services.webapp.opstats import OpStats
+
         OpStats.resetStats()
 
         # Connect Storm
@@ -1417,14 +1439,14 @@ class LaunchpadFunctionalLayer(LaunchpadLayer, FunctionalLayer):
 
         # Reset any statistics
         from lp.services.webapp.opstats import OpStats
+
         OpStats.resetStats()
 
         # Disconnect Storm so it doesn't get in the way of database resets
         disconnect_stores()
 
 
-class BingLaunchpadFunctionalLayer(LaunchpadFunctionalLayer,
-                                   BingServiceLayer):
+class BingLaunchpadFunctionalLayer(LaunchpadFunctionalLayer, BingServiceLayer):
     """Provides Bing service in addition to LaunchpadFunctionalLayer."""
 
     @classmethod
@@ -1497,7 +1519,7 @@ class LaunchpadScriptLayer(ZopelessLayer, LaunchpadLayer):
     @profiled
     def tearDown(cls):
         if not globalregistry.base.unregisterUtility(cls._mailbox):
-            raise NotImplementedError('failed to unregister mailbox')
+            raise NotImplementedError("failed to unregister mailbox")
 
     @classmethod
     @profiled
@@ -1513,9 +1535,9 @@ class LaunchpadScriptLayer(ZopelessLayer, LaunchpadLayer):
 
 
 class LaunchpadTestSetup(PgTestSetup):
-    template = 'launchpad_ftest_template'
-    dbuser = 'launchpad'
-    host = 'localhost'
+    template = "launchpad_ftest_template"
+    dbuser = "launchpad"
+    host = "localhost"
 
 
 class LaunchpadZopelessLayer(LaunchpadScriptLayer):
@@ -1539,7 +1561,7 @@ class LaunchpadZopelessLayer(LaunchpadScriptLayer):
     @classmethod
     @profiled
     def testSetUp(cls):
-        dbconfig.override(isolation_level='read_committed')
+        dbconfig.override(isolation_level="read_committed")
         # XXX wgrant 2011-09-24 bug=29744: initZopeless used to do this.
         # Tests that still need it should eventually set this directly,
         # so the whole layer is not polluted.
@@ -1584,30 +1606,33 @@ class ProfilingMiddleware:
 
 
 class PageTestLayer(LaunchpadFunctionalLayer, BingServiceLayer):
-    """Environment for page tests.
-    """
+    """Environment for page tests."""
 
     @classmethod
     @profiled
     def setUp(cls):
-        if os.environ.get('PROFILE_PAGETESTS_REQUESTS'):
+        if os.environ.get("PROFILE_PAGETESTS_REQUESTS"):
             PageTestLayer.profiler = Profile()
         else:
             PageTestLayer.profiler = None
 
         PageTestLayer._profiling_middleware = partial(
-            ProfilingMiddleware, profiler=PageTestLayer.profiler)
+            ProfilingMiddleware, profiler=PageTestLayer.profiler
+        )
         FunctionalLayer.browser_layer.addMiddlewares(
-            PageTestLayer._profiling_middleware)
+            PageTestLayer._profiling_middleware
+        )
 
     @classmethod
     @profiled
     def tearDown(cls):
         FunctionalLayer.browser_layer.removeMiddlewares(
-            PageTestLayer._profiling_middleware)
+            PageTestLayer._profiling_middleware
+        )
         if PageTestLayer.profiler:
             PageTestLayer.profiler.dump_stats(
-                os.environ.get('PROFILE_PAGETESTS_REQUESTS'))
+                os.environ.get("PROFILE_PAGETESTS_REQUESTS")
+            )
 
     @classmethod
     @profiled
@@ -1637,7 +1662,8 @@ class LayerProcessController:
     def setConfig(cls):
         """Stash a config for use."""
         cls.appserver_config = LaunchpadConfig(
-            BaseLayer.appserver_config_name, 'runlaunchpad')
+            BaseLayer.appserver_config_name, "runlaunchpad"
+        )
 
     @classmethod
     def setUp(cls):
@@ -1646,10 +1672,10 @@ class LayerProcessController:
 
     @classmethod
     @profiled
-    def startAppServer(cls, run_name='run'):
+    def startAppServer(cls, run_name="run"):
         """Start the app server if it hasn't already been started."""
         if cls.appserver is not None:
-            raise LayerInvariantError('App server already running')
+            raise LayerInvariantError("App server already running")
         cls._cleanUpStaleAppServer()
         cls._runAppServer(run_name)
         cls._waitUntilAppServerIsReady()
@@ -1716,8 +1742,9 @@ class LayerProcessController:
         """
         if cls.appserver.poll() is not None:
             raise LayerIsolationError(
-                "App server died in this test (status=%s):\n%s" % (
-                    cls.appserver.returncode, cls.appserver.stdout.read()))
+                "App server died in this test (status=%s):\n%s"
+                % (cls.appserver.returncode, cls.appserver.stdout.read())
+            )
         # Cleanup the app server's output buffer between tests.
         while True:
             # Read while we have something available at the stdout.
@@ -1731,7 +1758,7 @@ class LayerProcessController:
     @classmethod
     def _cleanUpStaleAppServer(cls):
         """Kill any stale app server or pid file."""
-        pid = pidfile.get_pid('launchpad', cls.appserver_config)
+        pid = pidfile.get_pid("launchpad", cls.appserver_config)
         if pid is not None:
             # Don't worry if the process no longer exists.
             try:
@@ -1739,18 +1766,22 @@ class LayerProcessController:
             except OSError as error:
                 if error.errno != errno.ESRCH:
                     raise
-            pidfile.remove_pidfile('launchpad', cls.appserver_config)
+            pidfile.remove_pidfile("launchpad", cls.appserver_config)
 
     @classmethod
     def _runAppServer(cls, run_name):
         """Start the app server using runlaunchpad.py"""
         _config = cls.appserver_config
-        cmd = [os.path.join(_config.root, 'bin', run_name)]
+        cmd = [os.path.join(_config.root, "bin", run_name)]
         environ = dict(os.environ)
-        environ['LPCONFIG'] = _config.instance_name
+        environ["LPCONFIG"] = _config.instance_name
         cls.appserver = subprocess.Popen(
-            cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
-            env=environ, cwd=_config.root)
+            cmd,
+            stdout=subprocess.PIPE,
+            stderr=subprocess.STDOUT,
+            env=environ,
+            cwd=_config.root,
+        )
 
     @classmethod
     def appserver_root_url(cls):
@@ -1770,8 +1801,9 @@ class LayerProcessController:
                 if error.code == 503:
                     raise RuntimeError(
                         "App server is returning unknown error code %s. Is "
-                        "there another instance running in the same port?" %
-                        error.code)
+                        "there another instance running in the same port?"
+                        % error.code
+                    )
             except URLError as error:
                 # We are interested in a wrapped socket.error.
                 if not isinstance(error.reason, socket.error):
@@ -1781,8 +1813,9 @@ class LayerProcessController:
                 returncode = cls.appserver.poll()
                 if returncode is not None:
                     raise RuntimeError(
-                        'App server failed to start (status=%d):\n%s' % (
-                            returncode, cls.appserver.stdout.read()))
+                        "App server failed to start (status=%d):\n%s"
+                        % (returncode, cls.appserver.stdout.read())
+                    )
                 time.sleep(0.5)
             else:
                 connection.close()
@@ -1791,12 +1824,11 @@ class LayerProcessController:
             os.kill(cls.appserver.pid, signal.SIGTERM)
             cls.appserver = None
             # Go no further.
-            raise AssertionError('App server startup timed out.')
+            raise AssertionError("App server startup timed out.")
 
 
 class AppServerLayer(LaunchpadFunctionalLayer):
-    """Layer for tests that run in the webapp environment with an app server.
-    """
+    """Layer for tests that run in a webapp environment with an app server."""
 
     @classmethod
     @profiled
@@ -1827,7 +1859,7 @@ class CeleryJobLayer(AppServerLayer):
     @classmethod
     @profiled
     def setUp(cls):
-        cls.celery_worker = celery_worker('launchpad_job')
+        cls.celery_worker = celery_worker("launchpad_job")
         cls.celery_worker.__enter__()
 
     @classmethod
@@ -1845,7 +1877,7 @@ class CelerySlowJobLayer(AppServerLayer):
     @classmethod
     @profiled
     def setUp(cls):
-        cls.celery_worker = celery_worker('launchpad_job_slow')
+        cls.celery_worker = celery_worker("launchpad_job_slow")
         cls.celery_worker.__enter__()
 
     @classmethod
@@ -1863,7 +1895,7 @@ class CeleryBzrsyncdJobLayer(AppServerLayer):
     @classmethod
     @profiled
     def setUp(cls):
-        cls.celery_worker = celery_worker('bzrsyncd_job')
+        cls.celery_worker = celery_worker("bzrsyncd_job")
         cls.celery_worker.__enter__()
 
     @classmethod
@@ -1881,7 +1913,7 @@ class CeleryBranchWriteJobLayer(AppServerLayer):
     @classmethod
     @profiled
     def setUp(cls):
-        cls.celery_worker = celery_worker('branch_write_job')
+        cls.celery_worker = celery_worker("branch_write_job")
         cls.celery_worker.__enter__()
 
     @classmethod
@@ -1892,8 +1924,7 @@ class CeleryBranchWriteJobLayer(AppServerLayer):
 
 
 class ZopelessAppServerLayer(LaunchpadZopelessLayer):
-    """Layer for tests that run in the zopeless environment with an appserver.
-    """
+    """Layer for Zopeless tests with an appserver."""
 
     @classmethod
     @profiled
@@ -1947,7 +1978,7 @@ class YUIAppServerLayer(MemcachedLayer):
     @profiled
     def setUp(cls):
         LayerProcessController.setConfig()
-        LayerProcessController.startAppServer('run-testapp')
+        LayerProcessController.startAppServer("run-testapp")
 
     @classmethod
     @profiled
diff --git a/lib/lp/testing/librarianhelpers.py b/lib/lp/testing/librarianhelpers.py
index a416473..7151d20 100644
--- a/lib/lp/testing/librarianhelpers.py
+++ b/lib/lp/testing/librarianhelpers.py
@@ -4,7 +4,7 @@
 """Various helper functions for using the librarian in testing.."""
 
 __all__ = [
-    'get_newest_librarian_file',
+    "get_newest_librarian_file",
 ]
 
 from storm.expr import Desc
@@ -23,6 +23,10 @@ def get_newest_librarian_file():
 
     :return: A file-like object of the file content.
     """
-    alias = IStore(LibraryFileAlias).find(LibraryFileAlias).order_by(
-        Desc(LibraryFileAlias.date_created)).first()
+    alias = (
+        IStore(LibraryFileAlias)
+        .find(LibraryFileAlias)
+        .order_by(Desc(LibraryFileAlias.date_created))
+        .first()
+    )
     return getUtility(ILibrarianClient).getFileByAlias(alias.id)
diff --git a/lib/lp/testing/mail.py b/lib/lp/testing/mail.py
index 7198142..016b5b5 100644
--- a/lib/lp/testing/mail.py
+++ b/lib/lp/testing/mail.py
@@ -3,31 +3,28 @@
 
 """Useful helper functions used for testing."""
 
-from email.utils import formatdate
 import os
+from email.utils import formatdate
 
 from zope.component import getUtility
 
 from lp.services.mail.mailbox import IMailBox
-from lp.services.mail.sendmail import (
-    get_msgid,
-    MailController,
-    )
+from lp.services.mail.sendmail import MailController, get_msgid
 
 
-def create_mail_for_directoryMailBox(from_addr, to_addrs, subject, body,
-                                     headers=None):
+def create_mail_for_directoryMailBox(
+    from_addr, to_addrs, subject, body, headers=None
+):
     """Create a email in the DirectoryMailBox."""
     mc = MailController(from_addr, to_addrs, subject, body, headers)
     message = mc.makeMessage()
-    if 'message-id' not in message:
-        message['Message-Id'] = get_msgid()
-    if 'date' not in message:
-        message['Date'] = formatdate()
+    if "message-id" not in message:
+        message["Message-Id"] = get_msgid()
+    if "date" not in message:
+        message["Date"] = formatdate()
     # Since this is faking incoming email, set the X-Original-To.
-    message['X-Original-To'] = to_addrs
+    message["X-Original-To"] = to_addrs
     mailbox = getUtility(IMailBox)
-    msg_file = open(
-        os.path.join(mailbox.mail_dir, message['Message-Id']), 'w')
+    msg_file = open(os.path.join(mailbox.mail_dir, message["Message-Id"]), "w")
     msg_file.write(message.as_string())
     msg_file.close()
diff --git a/lib/lp/testing/mail_helpers.py b/lib/lp/testing/mail_helpers.py
index 5792861..d344936 100644
--- a/lib/lp/testing/mail_helpers.py
+++ b/lib/lp/testing/mail_helpers.py
@@ -16,7 +16,7 @@ from lp.registry.interfaces.persontransferjob import (
     ISelfRenewalNotificationJobSource,
     ITeamInvitationNotificationJobSource,
     ITeamJoinNotificationJobSource,
-    )
+)
 from lp.services.config import config
 from lp.services.job.runner import JobRunner
 from lp.services.log.logger import DevNullLogger
@@ -38,13 +38,13 @@ def pop_notifications(sort_key=None, commit=True):
     if commit:
         transaction.commit()
     if sort_key is None:
-        sort_key = operator.itemgetter('To')
+        sort_key = operator.itemgetter("To")
 
     notifications = []
     for fromaddr, toaddrs, raw_message in stub.test_emails:
         notification = email.message_from_bytes(raw_message)
-        notification['X-Envelope-To'] = ', '.join(toaddrs)
-        notification['X-Envelope-From'] = fromaddr
+        notification["X-Envelope-To"] = ", ".join(toaddrs)
+        notification["X-Envelope-From"] = fromaddr
         notifications.append(notification)
     stub.test_emails = []
 
@@ -53,14 +53,19 @@ def pop_notifications(sort_key=None, commit=True):
 
 def sort_addresses(header):
     """Sort an address-list in an email header field body."""
-    addresses = {address.strip() for address in header.split(',')}
+    addresses = {address.strip() for address in header.split(",")}
     return ", ".join(sorted(addresses))
 
 
-def print_emails(include_reply_to=False, group_similar=False,
-                 include_rationale=False, include_for=False,
-                 notifications=None, include_notification_type=False,
-                 decode=False):
+def print_emails(
+    include_reply_to=False,
+    group_similar=False,
+    include_rationale=False,
+    include_for=False,
+    notifications=None,
+    include_notification_type=False,
+    decode=False,
+):
     """Pop all messages from stub.test_emails and print them with
      their recipients.
 
@@ -87,49 +92,59 @@ def print_emails(include_reply_to=False, group_similar=False,
         notifications = pop_notifications()
     for message in notifications:
         recipients = {
-            recipient.strip()
-            for recipient in message['To'].split(',')}
+            recipient.strip() for recipient in message["To"].split(",")
+        }
         body = message.get_payload(decode=decode)
         if group_similar:
             # Strip the first line as it's different for each recipient.
-            body = body[body.find(b'\n' if decode else '\n') + 1:]
+            body = body[body.find(b"\n" if decode else "\n") + 1 :]
         if body in distinct_bodies and group_similar:
             message, existing_recipients = distinct_bodies[body]
             distinct_bodies[body] = (
-                message, existing_recipients.union(recipients))
+                message,
+                existing_recipients.union(recipients),
+            )
         else:
             distinct_bodies[body] = (message, recipients)
     for body in sorted(distinct_bodies):
         message, recipients = distinct_bodies[body]
-        print('From:', message['From'])
-        print('To:', ", ".join(sorted(recipients)))
+        print("From:", message["From"])
+        print("To:", ", ".join(sorted(recipients)))
         if include_reply_to:
-            print('Reply-To:', message['Reply-To'])
-        rationale_header = 'X-Launchpad-Message-Rationale'
+            print("Reply-To:", message["Reply-To"])
+        rationale_header = "X-Launchpad-Message-Rationale"
         if include_rationale and rationale_header in message:
-            print('%s: %s' % (rationale_header, message[rationale_header]))
-        for_header = 'X-Launchpad-Message-For'
+            print("%s: %s" % (rationale_header, message[rationale_header]))
+        for_header = "X-Launchpad-Message-For"
         if include_for and for_header in message:
-            print('%s: %s' % (for_header, message[for_header]))
-        notification_type_header = 'X-Launchpad-Notification-Type'
+            print("%s: %s" % (for_header, message[for_header]))
+        notification_type_header = "X-Launchpad-Notification-Type"
         if include_notification_type and notification_type_header in message:
-            print('%s: %s' % (
-                notification_type_header, message[notification_type_header]))
-        print('Subject:', message['Subject'])
+            print(
+                "%s: %s"
+                % (notification_type_header, message[notification_type_header])
+            )
+        print("Subject:", message["Subject"])
         print(six.ensure_text(body))
         print("-" * 40)
 
 
-def print_distinct_emails(include_reply_to=False, include_rationale=True,
-                          include_for=False, include_notification_type=True,
-                          decode=False):
+def print_distinct_emails(
+    include_reply_to=False,
+    include_rationale=True,
+    include_for=False,
+    include_notification_type=True,
+    decode=False,
+):
     """A convenient shortcut for `print_emails`(group_similar=True)."""
-    return print_emails(group_similar=True,
-                        include_reply_to=include_reply_to,
-                        include_rationale=include_rationale,
-                        include_for=include_for,
-                        include_notification_type=include_notification_type,
-                        decode=decode)
+    return print_emails(
+        group_similar=True,
+        include_reply_to=include_reply_to,
+        include_rationale=include_rationale,
+        include_for=include_for,
+        include_notification_type=include_notification_type,
+        decode=decode,
+    )
 
 
 def run_mail_jobs():
@@ -146,12 +161,12 @@ def run_mail_jobs():
     # the queued jobs.
     transaction.commit()
     for interface in (
-            IExpiringMembershipNotificationJobSource,
-            IMembershipNotificationJobSource,
-            ISelfRenewalNotificationJobSource,
-            ITeamInvitationNotificationJobSource,
-            ITeamJoinNotificationJobSource,
-            ):
+        IExpiringMembershipNotificationJobSource,
+        IMembershipNotificationJobSource,
+        ISelfRenewalNotificationJobSource,
+        ITeamInvitationNotificationJobSource,
+        ITeamJoinNotificationJobSource,
+    ):
         job_source = getUtility(interface)
         logger = DevNullLogger()
         dbuser_name = getattr(config, interface.__name__).dbuser
diff --git a/lib/lp/testing/matchers.py b/lib/lp/testing/matchers.py
index 2f5ffad..47e7c92 100644
--- a/lib/lp/testing/matchers.py
+++ b/lib/lp/testing/matchers.py
@@ -2,52 +2,49 @@
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 __all__ = [
-    'BrowsesWithQueryLimit',
-    'Contains',
-    'DocTestMatches',
-    'DoesNotCorrectlyProvide',
-    'DoesNotProvide',
-    'EqualsIgnoringWhitespace',
-    'FileContainsBytes',
-    'HasQueryCount',
-    'IsNotProxied',
-    'IsProxied',
-    'MatchesPickerText',
-    'MatchesTagText',
-    'MissingElement',
-    'MultipleElements',
-    'Provides',
-    'ProvidesAndIsProxied',
-    ]
+    "BrowsesWithQueryLimit",
+    "Contains",
+    "DocTestMatches",
+    "DoesNotCorrectlyProvide",
+    "DoesNotProvide",
+    "EqualsIgnoringWhitespace",
+    "FileContainsBytes",
+    "HasQueryCount",
+    "IsNotProxied",
+    "IsProxied",
+    "MatchesPickerText",
+    "MatchesTagText",
+    "MissingElement",
+    "MultipleElements",
+    "Provides",
+    "ProvidesAndIsProxied",
+]
 
 from lazr.lifecycle.snapshot import Snapshot
 from testtools import matchers
 from testtools.content import text_content
+from testtools.matchers import DocTestMatches as OriginalDocTestMatches
 from testtools.matchers import (
-    DocTestMatches as OriginalDocTestMatches,
     Equals,
     FileContains,
     LessThan,
     Matcher,
     Mismatch,
     PathExists,
-    )
+)
 from testtools.matchers._higherorder import MismatchesAll
 from zope.interface.exceptions import (
     BrokenImplementation,
     BrokenMethodImplementation,
     DoesNotImplement,
-    )
+)
 from zope.interface.verify import verifyObject
 from zope.security.proxy import Proxy
 
 from lp.services.database.sqlbase import flush_database_caches
 from lp.services.webapp import canonical_url
 from lp.services.webapp.batching import BatchNavigator
-from lp.testing import (
-    normalize_whitespace,
-    RequestTimelineCollector,
-    )
+from lp.testing import RequestTimelineCollector, normalize_whitespace
 from lp.testing._login import person_logged_in
 
 
@@ -75,9 +72,11 @@ class BrowsesWithQueryLimit(Matcher):
     def match(self, context):
         # circular dependencies.
         from lp.testing.pages import setupBrowserForUser
+
         with person_logged_in(self.user):
             context_url = canonical_url(
-                context, view_name=self.view_name, **self.options)
+                context, view_name=self.view_name, **self.options
+            )
         browser = setupBrowserForUser(self.user)
         flush_database_caches()
         with RequestTimelineCollector() as collector:
@@ -126,8 +125,11 @@ class DoesNotCorrectlyProvide(DoesNotProvide):
             extra = ": %s" % self.extra
         else:
             extra = "."
-        return ("%r claims to provide %r, but does not do so correctly%s"
-                % (self.obj, self.interface, extra))
+        return "%r claims to provide %r, but does not do so correctly%s" % (
+            self.obj,
+            self.interface,
+            extra,
+        )
 
 
 class Provides(Matcher):
@@ -151,13 +153,17 @@ class Provides(Matcher):
         try:
             if not verifyObject(self.interface, matchee):
                 passed = False
-        except (BrokenImplementation, BrokenMethodImplementation,
-                DoesNotImplement) as e:
+        except (
+            BrokenImplementation,
+            BrokenMethodImplementation,
+            DoesNotImplement,
+        ) as e:
             passed = False
             extra = str(e)
         if not passed:
             return DoesNotCorrectlyProvide(
-                matchee, self.interface, extra=extra)
+                matchee, self.interface, extra=extra
+            )
         return None
 
 
@@ -188,8 +194,10 @@ class HasQueryCount(Matcher):
         if mismatch is None:
             return None
         return _MismatchedQueryCount(
-            mismatch, something,
-            other_query_collector=self.other_query_collector)
+            mismatch,
+            something,
+            other_query_collector=self.other_query_collector,
+        )
 
 
 class _MismatchedQueryCount(Mismatch):
@@ -208,20 +216,22 @@ class _MismatchedQueryCount(Mismatch):
         result = []
         for query in collector.queries:
             start, stop, dbname, statement, backtrace = query
-            result.append('%d-%d@%s %s' % (
-                start, stop, dbname, statement.rstrip()))
-            result.append('-' * 70)
+            result.append(
+                "%d-%d@%s %s" % (start, stop, dbname, statement.rstrip())
+            )
+            result.append("-" * 70)
             if backtrace is not None:
                 result.append(backtrace.rstrip())
-                result.append('.' * 70)
-        return text_content('\n'.join(result))
+                result.append("." * 70)
+        return text_content("\n".join(result))
 
     def get_details(self):
         details = {}
-        details['queries'] = self._getQueryDetails(self.query_collector)
+        details["queries"] = self._getQueryDetails(self.query_collector)
         if self.other_query_collector is not None:
-            details['other_queries'] = self._getQueryDetails(
-                self.other_query_collector)
+            details["other_queries"] = self._getQueryDetails(
+                self.other_query_collector
+            )
         return details
 
 
@@ -272,7 +282,6 @@ class ProvidesAndIsProxied(Matcher):
 
 
 class DoesNotContain(Mismatch):
-
     def __init__(self, matchee, expected):
         """Create a DoesNotContain Mismatch.
 
@@ -283,8 +292,7 @@ class DoesNotContain(Mismatch):
         self.expected = expected
 
     def describe(self):
-        return "'%s' does not contain '%s'." % (
-            self.matchee, self.expected)
+        return "'%s' does not contain '%s'." % (self.matchee, self.expected)
 
 
 class Contains(Matcher):
@@ -323,23 +331,28 @@ class IsConfiguredBatchNavigator(Matcher):
         if batch_size:
             self._batch = Equals(batch_size)
         self.matchers = dict(
-            _singular_heading=self._single, _plural_heading=self._plural)
+            _singular_heading=self._single, _plural_heading=self._plural
+        )
         if self._batch:
-            self.matchers['default_size'] = self._batch
+            self.matchers["default_size"] = self._batch
 
     def __str__(self):
         if self._batch:
             batch = ", %r" % self._batch.expected
         else:
-            batch = ''
+            batch = ""
         return "ConfiguredBatchNavigator(%r, %r%s)" % (
-            self._single.expected, self._plural.expected, batch)
+            self._single.expected,
+            self._plural.expected,
+            batch,
+        )
 
     def match(self, matchee):
         if not isinstance(matchee, BatchNavigator):
             # Testtools doesn't have an IsInstanceMismatch yet.
             return matchers._BinaryMismatch(
-                BatchNavigator, 'isinstance', matchee)
+                BatchNavigator, "isinstance", matchee
+            )
         mismatches = []
         for attrname, matcher in self.matchers.items():
             mismatch = matcher.match(getattr(matchee, attrname))
@@ -350,14 +363,15 @@ class IsConfiguredBatchNavigator(Matcher):
 
 
 class WasSnapshotted(Mismatch):
-
     def __init__(self, matchee, attribute):
         self.matchee = matchee
         self.attribute = attribute
 
     def describe(self):
         return "Snapshot of %s should not include %s" % (
-            self.matchee, self.attribute)
+            self.matchee,
+            self.attribute,
+        )
 
 
 class DoesNotSnapshot(Matcher):
@@ -370,7 +384,9 @@ class DoesNotSnapshot(Matcher):
 
     def __str__(self):
         return "Does not include %s when Snapshot is provided %s." % (
-            ', '.join(self.attr_list), self.interface)
+            ", ".join(self.attr_list),
+            self.interface,
+        )
 
     def match(self, matchee):
         snapshot = Snapshot(matchee, providing=self.interface)
@@ -391,29 +407,27 @@ def DocTestMatches(example):
     Uses the default doctest flags used across Launchpad.
     """
     from lp.testing.systemdocs import default_optionflags
+
     return OriginalDocTestMatches(example, default_optionflags)
 
 
 class SoupMismatch(Mismatch):
-
     def __init__(self, widget_id, soup_content):
         self.widget_id = widget_id
         self.soup_content = soup_content
 
     def get_details(self):
-        return {'content': text_content(str(self.soup_content))}
+        return {"content": text_content(str(self.soup_content))}
 
 
 class MissingElement(SoupMismatch):
-
     def describe(self):
-        return 'No HTML element found with id %r' % self.widget_id
+        return "No HTML element found with id %r" % self.widget_id
 
 
 class MultipleElements(SoupMismatch):
-
     def describe(self):
-        return 'HTML id %r found multiple times in document' % self.widget_id
+        return "HTML id %r found multiple times in document" % self.widget_id
 
 
 class MatchesTagText(Matcher):
@@ -430,6 +444,7 @@ class MatchesTagText(Matcher):
     def match(self, matchee):
         # Here to avoid circular dependancies.
         from lp.testing.pages import extract_text
+
         widgets = self.soup_content.find_all(id=self.tag_id)
         if len(widgets) == 0:
             return MissingElement(self.tag_id, self.soup_content)
@@ -454,13 +469,14 @@ class MatchesPickerText(Matcher):
     def match(self, matchee):
         # Here to avoid circular dependancies.
         from lp.testing.pages import extract_text
+
         widgets = self.soup_content.find_all(id=self.widget_id)
         if len(widgets) == 0:
             return MissingElement(self.widget_id, self.soup_content)
         elif len(widgets) > 1:
             return MultipleElements(self.widget_id, self.soup_content)
         widget = widgets[0]
-        text = widget.find_all(attrs={'class': 'yui3-activator-data-box'})[0]
+        text = widget.find_all(attrs={"class": "yui3-activator-data-box"})[0]
         text_matcher = DocTestMatches(extract_text(text))
         return text_matcher.match(matchee)
 
diff --git a/lib/lp/testing/menu.py b/lib/lp/testing/menu.py
index bce4988..61f2c83 100644
--- a/lib/lp/testing/menu.py
+++ b/lib/lp/testing/menu.py
@@ -4,14 +4,11 @@
 """Helpers for testing menus."""
 
 __all__ = [
-    'summarise_tal_links',
-    'make_fake_request',
-    ]
+    "summarise_tal_links",
+    "make_fake_request",
+]
 
-from zope.security.management import (
-    endInteraction,
-    newInteraction,
-    )
+from zope.security.management import endInteraction, newInteraction
 from zope.security.proxy import isinstance as zope_isinstance
 
 from lp.services.webapp import urlsplit
@@ -23,14 +20,14 @@ from lp.services.webapp.servers import LaunchpadTestRequest
 def check_menu_links(menu):
     context = menu.context
     for link in menu.iterlinks():
-        if link.target.startswith(('/', 'http://')):
+        if link.target.startswith(("/", "http://";)):
             # The context is not the context of this target.
             continue
-        if '?' in link.target:
-            view_name, _args = link.target.split('?')
+        if "?" in link.target:
+            view_name, _args = link.target.split("?")
         else:
             view_name = link.target
-        if view_name == '':
+        if view_name == "":
             view_name = None
         try:
             canonical_url(context, view_name=view_name, rootsite=link.site)
@@ -39,7 +36,7 @@ def check_menu_links(menu):
                 url = canonical_url(context)
             except Exception:
                 url = repr(context)
-            return 'Bad link %s: %s' % (link.name, url)
+            return "Bad link %s: %s" % (link.name, url)
     return True
 
 
@@ -60,14 +57,14 @@ def summarise_tal_links(links):
         else:
             link = key
         if ILink.providedBy(link):
-            print('link %s' % link.name)
-            attributes = ('url', 'enabled', 'menu', 'selected', 'linked')
+            print("link %s" % link.name)
+            attributes = ("url", "enabled", "menu", "selected", "linked")
             for attrname in attributes:
                 if not hasattr(link, attrname):
                     continue
-                print('    %s:' % attrname, getattr(link, attrname))
+                print("    %s:" % attrname, getattr(link, attrname))
         else:
-            print('attribute %s: %s' % (key, link))
+            print("attribute %s: %s" % (key, link))
 
 
 def make_fake_request(url, traversed_objects=None):
@@ -77,12 +74,10 @@ def make_fake_request(url, traversed_objects=None):
         traversed_objects attribute.
     """
     url_parts = urlsplit(url)
-    server_url = '://'.join(url_parts[0:2])
+    server_url = "://".join(url_parts[0:2])
     path_info = url_parts[2]
-    request = LaunchpadTestRequest(
-        SERVER_URL=server_url,
-        PATH_INFO=path_info)
-    request._traversed_names = path_info.split('/')[1:]
+    request = LaunchpadTestRequest(SERVER_URL=server_url, PATH_INFO=path_info)
+    request._traversed_names = path_info.split("/")[1:]
     if traversed_objects is not None:
         request.traversed_objects = traversed_objects[:]
     # After making the request, setup a new interaction.
diff --git a/lib/lp/testing/pages.py b/lib/lp/testing/pages.py
index 6e2d360..f0eb960 100644
--- a/lib/lp/testing/pages.py
+++ b/lib/lp/testing/pages.py
@@ -3,16 +3,18 @@
 
 """Testing infrastructure for page tests."""
 
-from contextlib import contextmanager
-from datetime import datetime
 import doctest
-from io import BytesIO
-from itertools import chain
 import os
 import re
 import unittest
+from contextlib import contextmanager
+from datetime import datetime
+from io import BytesIO
+from itertools import chain
 from urllib.parse import urljoin
 
+import six
+import transaction
 from bs4.element import (
     CData,
     Comment,
@@ -22,52 +24,36 @@ from bs4.element import (
     PageElement,
     ProcessingInstruction,
     Tag,
-    )
+)
 from lazr.restful.testing.webservice import WebServiceCaller
 from oauthlib import oauth1
-import six
 from soupsieve import escape as css_escape
-import transaction
 from webtest import TestRequest
-from zope.app.wsgi.testlayer import (
-    FakeResponse,
-    NotInBrowserLayer,
-    )
+from zope.app.wsgi.testlayer import FakeResponse, NotInBrowserLayer
 from zope.component import getUtility
 from zope.security.management import setSecurityPolicy
 from zope.security.proxy import removeSecurityProxy
+from zope.testbrowser.browser import BrowserStateError
+from zope.testbrowser.browser import Link as _Link
 from zope.testbrowser.browser import (
-    BrowserStateError,
-    isMatching,
-    Link as _Link,
     LinkNotFoundError,
+    isMatching,
     normalizeWhitespace,
-    )
-from zope.testbrowser.wsgi import (
-    Browser as _Browser,
-    Layer as TestBrowserWSGILayer,
-    )
+)
+from zope.testbrowser.wsgi import Browser as _Browser
+from zope.testbrowser.wsgi import Layer as TestBrowserWSGILayer
 
 from lp.app.interfaces.launchpad import ILaunchpadCelebrities
 from lp.registry.errors import NameAlreadyTaken
 from lp.registry.interfaces.teammembership import TeamMembershipStatus
-from lp.services.beautifulsoup import (
-    BeautifulSoup,
-    SoupStrainer,
-    )
+from lp.services.beautifulsoup import BeautifulSoup, SoupStrainer
 from lp.services.config import config
 from lp.services.encoding import wsgi_native_string
 from lp.services.helpers import backslashreplace
-from lp.services.oauth.interfaces import (
-    IOAuthConsumerSet,
-    OAUTH_REALM,
-    )
+from lp.services.oauth.interfaces import OAUTH_REALM, IOAuthConsumerSet
 from lp.services.webapp import canonical_url
 from lp.services.webapp.authorization import LaunchpadPermissiveSecurityPolicy
-from lp.services.webapp.interfaces import (
-    ISession,
-    OAuthPermission,
-    )
+from lp.services.webapp.interfaces import ISession, OAuthPermission
 from lp.services.webapp.servers import LaunchpadTestRequest
 from lp.services.webapp.url import urlsplit
 from lp.testing import (
@@ -77,22 +63,17 @@ from lp.testing import (
     login_person,
     logout,
     person_logged_in,
-    )
+)
 from lp.testing.dbuser import dbuser
 from lp.testing.factory import LaunchpadObjectFactory
 from lp.testing.layers import PageTestLayer
-from lp.testing.systemdocs import (
-    LayeredDocFileSuite,
-    PrettyPrinter,
-    stop,
-    )
-
+from lp.testing.systemdocs import LayeredDocFileSuite, PrettyPrinter, stop
 
 SAMPLEDATA_ACCESS_SECRETS = {
-    'salgado-read-nonprivate': 'secret',
-    'salgado-change-anything': 'test',
-    'nopriv-read-nonprivate': 'mystery',
-    }
+    "salgado-read-nonprivate": "secret",
+    "salgado-change-anything": "test",
+    "nopriv-read-nonprivate": "mystery",
+}
 
 
 def http(string, handle_errors=True):
@@ -109,17 +90,17 @@ def http(string, handle_errors=True):
         raise NotInBrowserLayer(NotInBrowserLayer.__doc__)
 
     if not isinstance(string, bytes):
-        string = string.encode('UTF-8')
+        string = string.encode("UTF-8")
     request = TestRequest.from_file(BytesIO(string.lstrip()))
-    request.environ['wsgi.handleErrors'] = handle_errors
-    if 'HTTP_HOST' in request.environ:
-        if ':' in request.environ['HTTP_HOST']:
-            host, port = request.environ['HTTP_HOST'].split(':', 1)
+    request.environ["wsgi.handleErrors"] = handle_errors
+    if "HTTP_HOST" in request.environ:
+        if ":" in request.environ["HTTP_HOST"]:
+            host, port = request.environ["HTTP_HOST"].split(":", 1)
         else:
-            host = request.environ['HTTP_HOST']
+            host = request.environ["HTTP_HOST"]
             port = 80
-        request.environ['SERVER_NAME'] = host
-        request.environ['SERVER_PORT'] = int(port)
+        request.environ["SERVER_NAME"] = host
+        request.environ["SERVER_PORT"] = int(port)
     response = request.get_response(app)
     return FakeResponse(response, request)
 
@@ -127,11 +108,17 @@ def http(string, handle_errors=True):
 class LaunchpadWebServiceCaller(WebServiceCaller):
     """A class for making calls to Launchpad web services."""
 
-    def __init__(self, oauth_consumer_key=None, oauth_access_key=None,
-                 oauth_access_secret=None, access_token_secret=None,
-                 handle_errors=True,
-                 domain='api.launchpad.test', protocol='http',
-                 default_api_version=None):
+    def __init__(
+        self,
+        oauth_consumer_key=None,
+        oauth_access_key=None,
+        oauth_access_secret=None,
+        access_token_secret=None,
+        handle_errors=True,
+        domain="api.launchpad.test",
+        protocol="http",
+        default_api_version=None,
+    ):
         """Create a LaunchpadWebServiceCaller.
         :param oauth_consumer_key: The OAuth consumer key to use.
         :param oauth_access_key: The OAuth access key to use for the request.
@@ -148,12 +135,14 @@ class LaunchpadWebServiceCaller(WebServiceCaller):
         if oauth_consumer_key is not None and oauth_access_key is not None:
             if oauth_access_secret is None:
                 oauth_access_secret = SAMPLEDATA_ACCESS_SECRETS.get(
-                    oauth_access_key, '')
+                    oauth_access_key, ""
+                )
             self.oauth_client = oauth1.Client(
                 oauth_consumer_key,
                 resource_owner_key=oauth_access_key,
                 resource_owner_secret=oauth_access_secret,
-                signature_method=oauth1.SIGNATURE_PLAINTEXT)
+                signature_method=oauth1.SIGNATURE_PLAINTEXT,
+            )
             logout()
         elif access_token_secret is not None:
             self.access_token_secret = access_token_secret
@@ -167,15 +156,20 @@ class LaunchpadWebServiceCaller(WebServiceCaller):
     def addHeadersTo(self, full_url, full_headers):
         if self.oauth_client is not None:
             _, oauth_headers, _ = self.oauth_client.sign(
-                full_url, realm=OAUTH_REALM)
-            full_headers.update({
-                wsgi_native_string(key): wsgi_native_string(value)
-                for key, value in oauth_headers.items()})
+                full_url, realm=OAUTH_REALM
+            )
+            full_headers.update(
+                {
+                    wsgi_native_string(key): wsgi_native_string(value)
+                    for key, value in oauth_headers.items()
+                }
+            )
         elif self.access_token_secret is not None:
-            full_headers['Authorization'] = (
-                'Token %s' % self.access_token_secret)
+            full_headers["Authorization"] = (
+                "Token %s" % self.access_token_secret
+            )
         if not self.handle_errors:
-            full_headers['X_Zope_handle_errors'] = 'False'
+            full_headers["X_Zope_handle_errors"] = "False"
 
 
 def extract_url_parameter(url, parameter):
@@ -186,9 +180,9 @@ def extract_url_parameter(url, parameter):
     or how the parameters are ordered.
     """
     scheme, host, path, query, fragment = urlsplit(url)
-    args = query.split('&')
+    args = query.split("&")
     for arg in args:
-        key, value = arg.split('=')
+        key, value = arg.split("=")
         if key == parameter:
             return arg
     return None
@@ -201,18 +195,20 @@ class DuplicateIdError(Exception):
 def find_tag_by_id(content, id):
     """Find and return the tag with the given ID"""
     if isinstance(content, PageElement):
-        elements_with_id = content.find_all(True, {'id': id})
+        elements_with_id = content.find_all(True, {"id": id})
     else:
         elements_with_id = [
-            tag for tag in BeautifulSoup(
-                content, parse_only=SoupStrainer(id=id))]
+            tag
+            for tag in BeautifulSoup(content, parse_only=SoupStrainer(id=id))
+        ]
     if len(elements_with_id) == 0:
         return None
     elif len(elements_with_id) == 1:
         return elements_with_id[0]
     else:
         raise DuplicateIdError(
-            "Found %d elements with id '%s'" % (len(elements_with_id), id))
+            "Found %d elements with id '%s'" % (len(elements_with_id), id)
+        )
 
 
 def first_tag_by_class(content, class_):
@@ -230,13 +226,15 @@ def find_tags_by_class(content, class_, only_first=False):
             return False
         classes = set(value.split())
         return match_classes.issubset(classes)
+
     soup = BeautifulSoup(
-        content, parse_only=SoupStrainer(attrs={'class': class_matcher}))
+        content, parse_only=SoupStrainer(attrs={"class": class_matcher})
+    )
     if only_first:
         find = BeautifulSoup.find
     else:
         find = BeautifulSoup.find_all
-    return find(soup, attrs={'class': class_matcher})
+    return find(soup, attrs={"class": class_matcher})
 
 
 def find_portlet(content, name):
@@ -245,23 +243,23 @@ def find_portlet(content, name):
     ending whitespace is also ignored, as are non-text elements such as
     images.
     """
-    whitespace_re = re.compile(r'\s+')
-    name = whitespace_re.sub(' ', name.strip())
-    for portlet in find_tags_by_class(content, 'portlet'):
-        if portlet.find('h2'):
-            portlet_title = extract_text(portlet.find('h2'))
-            if name == whitespace_re.sub(' ', portlet_title.strip()):
+    whitespace_re = re.compile(r"\s+")
+    name = whitespace_re.sub(" ", name.strip())
+    for portlet in find_tags_by_class(content, "portlet"):
+        if portlet.find("h2"):
+            portlet_title = extract_text(portlet.find("h2"))
+            if name == whitespace_re.sub(" ", portlet_title.strip()):
                 return portlet
     return None
 
 
 def find_main_content(content):
     """Return the main content of the page, excluding any portlets."""
-    main_content = find_tag_by_id(content, 'maincontent')
+    main_content = find_tag_by_id(content, "maincontent")
     if main_content is None:
         # One-column pages don't use a <div id="maincontent">, so we
         # use the next best thing: <div id="container">.
-        main_content = find_tag_by_id(content, 'container')
+        main_content = find_tag_by_id(content, "container")
     if main_content is None:
         # Simple pages have neither of these, so as a last resort, we get
         # the page <body>.
@@ -271,15 +269,20 @@ def find_main_content(content):
 
 def get_feedback_messages(content):
     """Find and return the feedback messages of the page."""
-    message_classes = ['message', 'informational message', 'error message',
-                       'warning message']
+    message_classes = [
+        "message",
+        "informational message",
+        "error message",
+        "warning message",
+    ]
     soup = BeautifulSoup(
         content,
-        parse_only=SoupStrainer(['div', 'p'], {'class': message_classes}))
+        parse_only=SoupStrainer(["div", "p"], {"class": message_classes}),
+    )
     return [extract_text(tag) for tag in soup]
 
 
-def print_feedback_messages(content, formatter='minimal'):
+def print_feedback_messages(content, formatter="minimal"):
     """Print out the feedback messages."""
     for message in get_feedback_messages(content):
         print(extract_text(message, formatter=formatter))
@@ -295,11 +298,11 @@ def print_table(content, columns=None, skip_rows=None, sep="\t"):
                      None no rows are skipped.
     :param sep       the separator to be used between output items.
     """
-    for row_num, row in enumerate(content.find_all('tr')):
+    for row_num, row in enumerate(content.find_all("tr")):
         if skip_rows is not None and row_num in skip_rows:
             continue
         row_content = []
-        for col_num, item in enumerate(row.find_all('td')):
+        for col_num, item in enumerate(row.find_all("td")):
             if columns is None or col_num in columns:
                 row_content.append(extract_text(item))
         if len(row_content) > 0:
@@ -312,18 +315,18 @@ def get_radio_button_text_for_field(soup, name):
     The resulting output will look something like:
     ['(*) A checked option', '( ) An unchecked option']
     """
-    buttons = soup.find_all(
-        'input', {'name': 'field.%s' % name})
+    buttons = soup.find_all("input", {"name": "field.%s" % name})
     for button in buttons:
-        if button.parent.name == 'label':
+        if button.parent.name == "label":
             label = extract_text(button.parent)
         else:
             label = extract_text(
-                soup.find('label', attrs={'for': button['id']}))
-        if button.get('checked', None):
-            radio = '(*)'
+                soup.find("label", attrs={"for": button["id"]})
+            )
+        if button.get("checked", None):
+            radio = "(*)"
         else:
-            radio = '( )'
+            radio = "( )"
         yield "%s %s" % (radio, label)
 
 
@@ -341,23 +344,49 @@ def print_radio_button_field(content, name):
 
 def strip_label(label):
     """Strip surrounding whitespace and non-breaking spaces."""
-    return label.replace('\xC2', '').replace('\xA0', '').strip()
+    return label.replace("\xC2", "").replace("\xA0", "").strip()
 
 
 IGNORED_ELEMENTS = [
-    Comment, Declaration, Doctype, ProcessingInstruction,
-    ]
+    Comment,
+    Declaration,
+    Doctype,
+    ProcessingInstruction,
+]
 ELEMENTS_INTRODUCING_NEWLINE = [
-    'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'ul', 'ol', 'pre', 'dl',
-    'div', 'noscript', 'blockquote', 'form', 'hr', 'table', 'fieldset',
-    'address', 'li', 'dt', 'dd', 'th', 'td', 'caption', 'br']
-
-
-NEWLINES_RE = re.compile('\n+')
-LEADING_AND_TRAILING_SPACES_RE = re.compile(
-    '(^[ \t]+)|([ \t]$)', re.MULTILINE)
-TABS_AND_SPACES_RE = re.compile('[ \t]+')
-NBSP_RE = re.compile('&nbsp;|&#160;|\xa0')
+    "p",
+    "h1",
+    "h2",
+    "h3",
+    "h4",
+    "h5",
+    "h6",
+    "ul",
+    "ol",
+    "pre",
+    "dl",
+    "div",
+    "noscript",
+    "blockquote",
+    "form",
+    "hr",
+    "table",
+    "fieldset",
+    "address",
+    "li",
+    "dt",
+    "dd",
+    "th",
+    "td",
+    "caption",
+    "br",
+]
+
+
+NEWLINES_RE = re.compile("\n+")
+LEADING_AND_TRAILING_SPACES_RE = re.compile("(^[ \t]+)|([ \t]$)", re.MULTILINE)
+TABS_AND_SPACES_RE = re.compile("[ \t]+")
+NBSP_RE = re.compile("&nbsp;|&#160;|\xa0")
 
 
 def extract_link_from_tag(tag, base=None):
@@ -371,15 +400,16 @@ def extract_link_from_tag(tag, base=None):
     else:
         link = tag
 
-    href = dict(link.attrs).get('href')
+    href = dict(link.attrs).get("href")
     if base is None:
         return href
     else:
         return urljoin(base, href)
 
 
-def extract_text(content, extract_image_text=False, skip_tags=None,
-                 formatter='minimal'):
+def extract_text(
+    content, extract_image_text=False, skip_tags=None, formatter="minimal"
+):
     """Return the text stripped of all tags.
 
     All runs of tabs and spaces are replaced by a single space and runs of
@@ -387,7 +417,7 @@ def extract_text(content, extract_image_text=False, skip_tags=None,
     spaces are stripped.
     """
     if skip_tags is None:
-        skip_tags = ['script']
+        skip_tags = ["script"]
     if not isinstance(content, PageElement):
         soup = BeautifulSoup(content)
     else:
@@ -406,31 +436,31 @@ def extract_text(content, extract_image_text=False, skip_tags=None,
         else:
             if isinstance(node, Tag):
                 # If the node has the class "sortkey" then it is invisible.
-                if node.get('class') == ['sortkey']:
+                if node.get("class") == ["sortkey"]:
                     continue
-                elif getattr(node, 'name', '') in skip_tags:
+                elif getattr(node, "name", "") in skip_tags:
                     continue
                 if node.name.lower() in ELEMENTS_INTRODUCING_NEWLINE:
-                    result.append('\n')
+                    result.append("\n")
 
                 # If extract_image_text is True and the node is an
                 # image, try to find its title or alt attributes.
-                if extract_image_text and node.name.lower() == 'img':
+                if extract_image_text and node.name.lower() == "img":
                     # Title outweighs alt text for the purposes of
                     # pagetest output.
-                    if node.get('title') is not None:
-                        result.append(node['title'])
-                    elif node.get('alt') is not None:
-                        result.append(node['alt'])
+                    if node.get("title") is not None:
+                        result.append(node["title"])
+                    elif node.get("alt") is not None:
+                        result.append(node["alt"])
 
             # Process this node's children next.
             nodes[0:0] = list(node)
 
-    text = ''.join(result)
-    text = NBSP_RE.sub(' ', text)
-    text = TABS_AND_SPACES_RE.sub(' ', text)
-    text = LEADING_AND_TRAILING_SPACES_RE.sub('', text)
-    text = NEWLINES_RE.sub('\n', text)
+    text = "".join(result)
+    text = NBSP_RE.sub(" ", text)
+    text = TABS_AND_SPACES_RE.sub(" ", text)
+    text = LEADING_AND_TRAILING_SPACES_RE.sub("", text)
+    text = NEWLINES_RE.sub("\n", text)
 
     # Remove possible newlines at beginning and end.
     return text.strip()
@@ -445,49 +475,49 @@ def parse_relationship_section(content):
     See package-relationship-pages.rst and related.
     """
     soup = BeautifulSoup(content)
-    section = soup.find('ul')
-    whitespace_re = re.compile(r'\s+')
+    section = soup.find("ul")
+    whitespace_re = re.compile(r"\s+")
     if section is None:
-        print('EMPTY SECTION')
+        print("EMPTY SECTION")
         return
-    for li in section.find_all('li'):
+    for li in section.find_all("li"):
         if li.a:
             link = li.a
-            content = whitespace_re.sub(' ', link.string.strip())
-            url = link['href']
+            content = whitespace_re.sub(" ", link.string.strip())
+            url = link["href"]
             print('LINK: "%s" -> %s' % (content, url))
         else:
-            content = whitespace_re.sub(' ', li.string.strip())
+            content = whitespace_re.sub(" ", li.string.strip())
             print('TEXT: "%s"' % content)
 
 
 def print_action_links(content):
     """Print action menu urls."""
-    actions = find_tag_by_id(content, 'actions')
+    actions = find_tag_by_id(content, "actions")
     if actions is None:
         print("No actions portlet")
         return
-    entries = actions.find_all('li')
+    entries = actions.find_all("li")
     for entry in entries:
         if entry.a:
-            print('%s: %s' % (entry.a.string, entry.a['href']))
+            print("%s: %s" % (entry.a.string, entry.a["href"]))
         elif entry.strong:
             print(entry.strong.string)
 
 
 def print_navigation_links(content):
     """Print navigation menu urls."""
-    navigation_links = find_tag_by_id(content, 'navigation-tabs')
+    navigation_links = find_tag_by_id(content, "navigation-tabs")
     if navigation_links is None:
         print("No navigation links")
         return
-    title = navigation_links.find('label')
+    title = navigation_links.find("label")
     if title is not None:
-        print('= %s =' % title.string)
-    entries = navigation_links.find_all(['strong', 'a'])
+        print("= %s =" % title.string)
+    entries = navigation_links.find_all(["strong", "a"])
     for entry in entries:
         try:
-            print('%s: %s' % (entry.span.string, entry['href']))
+            print("%s: %s" % (entry.span.string, entry["href"]))
         except KeyError:
             print(entry.span.string)
 
@@ -512,13 +542,15 @@ def print_portlet_links(content, name, base=None):
     if portlet_contents is None:
         print("No portlet found with name:", name)
         return
-    portlet_links = portlet_contents.find_all('a')
+    portlet_links = portlet_contents.find_all("a")
     if len(portlet_links) == 0:
         print("No links were found in the portlet.")
         return
     for portlet_link in portlet_links:
-        print('%s: %s' % (portlet_link.string,
-            extract_link_from_tag(portlet_link, base)))
+        print(
+            "%s: %s"
+            % (portlet_link.string, extract_link_from_tag(portlet_link, base))
+        )
 
 
 def print_submit_buttons(content):
@@ -527,19 +559,20 @@ def print_submit_buttons(content):
     Use this to check that the buttons on a page match your expectations.
     """
     buttons = find_main_content(content).find_all(
-        'input', attrs={'class': 'button', 'type': 'submit'})
+        "input", attrs={"class": "button", "type": "submit"}
+    )
     if buttons is None:
         print("No buttons found")
     else:
         for button in buttons:
-            print(button['value'])
+            print(button["value"])
 
 
 def print_comments(page):
     """Print the comments on a BugTask index page."""
     main_content = find_main_content(page)
-    for comment in main_content('div', 'boardCommentBody'):
-        for li_tag in comment('li'):
+    for comment in main_content("div", "boardCommentBody"):
+        for li_tag in comment("li"):
             print("Attachment: %s" % li_tag.a.decode_contents())
         print(comment.div.decode_contents())
         print("-" * 40)
@@ -547,22 +580,22 @@ def print_comments(page):
 
 def print_batch_header(soup):
     """Print the batch navigator header."""
-    navigation = soup.find('td', {'class': 'batch-navigation-index'})
+    navigation = soup.find("td", {"class": "batch-navigation-index"})
     print(backslashreplace(extract_text(navigation)))
 
 
 def print_self_link_of_entries(json_body):
     """Print the self_link attribute of each entry in the given JSON body."""
-    links = sorted(entry['self_link'] for entry in json_body['entries'])
+    links = sorted(entry["self_link"] for entry in json_body["entries"])
     for link in links:
         print(link)
 
 
 def print_ppa_packages(contents):
-    packages = find_tags_by_class(contents, 'archive_package_row')
+    packages = find_tags_by_class(contents, "archive_package_row")
     for pkg in packages:
         print(extract_text(pkg))
-    empty_section = find_tag_by_id(contents, 'empty-result')
+    empty_section = find_tag_by_id(contents, "empty-result")
     if empty_section is not None:
         print(extract_text(empty_section))
 
@@ -576,37 +609,37 @@ def print_location(contents):
     for example, Overview, Bugs, and Translations.
     The main heading is the first <h1> element in the page.
     """
-    doc = find_tag_by_id(contents, 'document')
-    heading = doc.find(attrs={'id': 'watermark-heading'}).find_all('a')
-    container = doc.find(attrs={'class': 'breadcrumbs'})
+    doc = find_tag_by_id(contents, "document")
+    heading = doc.find(attrs={"id": "watermark-heading"}).find_all("a")
+    container = doc.find(attrs={"class": "breadcrumbs"})
     hierarchy = container.find_all(recursive=False) if container else []
     segments = [extract_text(step) for step in chain(heading, hierarchy)]
 
     if len(segments) == 0:
-        breadcrumbs = 'None displayed'
+        breadcrumbs = "None displayed"
     else:
-        breadcrumbs = ' > '.join(segments)
+        breadcrumbs = " > ".join(segments)
 
-    print('Hierarchy:', breadcrumbs)
-    print('Tabs:')
+    print("Hierarchy:", breadcrumbs)
+    print("Tabs:")
     print_location_apps(contents)
     main_heading = doc.h1
     if main_heading:
         main_heading = extract_text(main_heading)
     else:
-        main_heading = '(No main heading)'
+        main_heading = "(No main heading)"
     print("Main heading: %s" % main_heading)
 
 
 def print_location_apps(contents):
     """Print the application tabs' text and URL."""
-    location_apps = find_tag_by_id(contents, 'lp-apps')
+    location_apps = find_tag_by_id(contents, "lp-apps")
     if location_apps is None:
-        location_apps = first_tag_by_class(contents, 'watermark-apps-portlet')
+        location_apps = first_tag_by_class(contents, "watermark-apps-portlet")
         if location_apps is not None:
-            location_apps = location_apps.ul.find_all('li')
+            location_apps = location_apps.ul.find_all("li")
     else:
-        location_apps = location_apps.find_all('span')
+        location_apps = location_apps.find_all("span")
     if location_apps is None:
         print("(Application tabs omitted)")
     elif len(location_apps) == 0:
@@ -614,12 +647,12 @@ def print_location_apps(contents):
     else:
         for tab in location_apps:
             tab_text = extract_text(tab)
-            if 'active' in tab['class']:
-                tab_text += ' (selected)'
+            if "active" in tab["class"]:
+                tab_text += " (selected)"
             if tab.a:
-                link = tab.a['href']
+                link = tab.a["href"]
             else:
-                link = 'not linked'
+                link = "not linked"
             print("* %s - %s" % (tab_text, link))
 
 
@@ -631,7 +664,7 @@ def print_tag_with_id(contents, id):
 
 def print_errors(contents):
     """Print all the errors on the page."""
-    errors = find_tags_by_class(contents, 'error')
+    errors = find_tags_by_class(contents, "error")
     error_texts = [extract_text(error) for error in errors]
     for error in error_texts:
         print(error)
@@ -664,26 +697,29 @@ class Browser(_Browser):
             for descendant in elem.descendants:
                 if isinstance(descendant, (NavigableString, CData)):
                     yield descendant
-                elif isinstance(descendant, Tag) and descendant.name == 'img':
-                    yield '%s[%s]' % (
-                        descendant.get('alt', ''), descendant.name.upper())
+                elif isinstance(descendant, Tag) and descendant.name == "img":
+                    yield "%s[%s]" % (
+                        descendant.get("alt", ""),
+                        descendant.name.upper(),
+                    )
 
-        return ''.join(list(get_strings(element)))
+        return "".join(list(get_strings(element)))
 
     def getLink(self, text=None, url=None, id=None, index=0):
         """Search for both text nodes and image alt attributes."""
         # XXX cjwatson 2019-11-09: This should be merged back into
         # `zope.testbrowser.browser.Browser.getLink`.
-        qa = 'a' if id is None else 'a#%s' % css_escape(id)
-        qarea = 'area' if id is None else 'area#%s' % css_escape(id)
+        qa = "a" if id is None else "a#%s" % css_escape(id)
+        qarea = "area" if id is None else "area#%s" % css_escape(id)
         html = self._html
         links = html.select(qa)
         links.extend(html.select(qarea))
 
         matching = []
         for elem in links:
-            matches = (isMatching(self._getText(elem), text) and
-                       isMatching(elem.get('href', ''), url))
+            matches = isMatching(self._getText(elem), text) and isMatching(
+                elem.get("href", ""), url
+            )
 
             if matches:
                 matching.append(elem)
@@ -733,12 +769,12 @@ def setupBrowserFreshLogin(user):
     """
     request = LaunchpadTestRequest()
     session = ISession(request)
-    authdata = session['launchpad.authenticateduser']
-    authdata['logintime'] = datetime.utcnow()
+    authdata = session["launchpad.authenticateduser"]
+    authdata["logintime"] = datetime.utcnow()
     namespace = config.launchpad_session.cookie
-    cookie = '%s=%s' % (namespace, session.client_id)
+    cookie = "%s=%s" % (namespace, session.client_id)
     browser = setupBrowserForUser(user)
-    browser.addHeader('Cookie', cookie)
+    browser.addHeader("Cookie", cookie)
     return browser
 
 
@@ -747,10 +783,14 @@ def safe_canonical_url(*args, **kwargs):
     return str(canonical_url(*args, **kwargs))
 
 
-def webservice_for_person(person, consumer_key='launchpad-library',
-                          permission=OAuthPermission.READ_PUBLIC,
-                          context=None, default_api_version=None,
-                          access_token_secret=None):
+def webservice_for_person(
+    person,
+    consumer_key="launchpad-library",
+    permission=OAuthPermission.READ_PUBLIC,
+    context=None,
+    default_api_version=None,
+    access_token_secret=None,
+):
     """Return a valid LaunchpadWebServiceCaller for the person.
 
     Use this method to create a way to test the webservice that doesn't depend
@@ -759,7 +799,7 @@ def webservice_for_person(person, consumer_key='launchpad-library',
     kwargs = {}
     if person is not None:
         if person.is_team:
-            raise AssertionError('This cannot be used with teams.')
+            raise AssertionError("This cannot be used with teams.")
         login(ANONYMOUS)
         if access_token_secret is None:
             oacs = getUtility(IOAuthConsumerSet)
@@ -769,12 +809,12 @@ def webservice_for_person(person, consumer_key='launchpad-library',
             request_token, _ = consumer.newRequestToken()
             request_token.review(person, permission, context)
             access_token, access_secret = request_token.createAccessToken()
-            kwargs['oauth_consumer_key'] = consumer_key
-            kwargs['oauth_access_key'] = access_token.key
-            kwargs['oauth_access_secret'] = access_secret
+            kwargs["oauth_consumer_key"] = consumer_key
+            kwargs["oauth_access_key"] = access_token.key
+            kwargs["oauth_access_secret"] = access_secret
         else:
-            kwargs['access_token_secret'] = access_token_secret
-    kwargs['default_api_version'] = default_api_version
+            kwargs["access_token_secret"] = access_token_secret
+    kwargs["default_api_version"] = default_api_version
     logout()
     service = LaunchpadWebServiceCaller(**kwargs)
     service.user = person
@@ -786,10 +826,11 @@ def setupDTCBrowser():
 
     Ubuntu is the configured distribution.
     """
-    login('foo.bar@xxxxxxxxxxxxx')
+    login("foo.bar@xxxxxxxxxxxxx")
     try:
         dtg_member = LaunchpadObjectFactory().makePerson(
-            name='ubuntu-translations-coordinator', email="dtg-member@xxxxxx")
+            name="ubuntu-translations-coordinator", email="dtg-member@xxxxxx"
+        )
     except NameAlreadyTaken:
         # We have already created the translations coordinator
         pass
@@ -797,31 +838,36 @@ def setupDTCBrowser():
         dtg = LaunchpadObjectFactory().makeTranslationGroup(
             name="ubuntu-translators",
             title="Ubuntu Translators",
-            owner=dtg_member)
+            owner=dtg_member,
+        )
         ubuntu = getUtility(ILaunchpadCelebrities).ubuntu
         ubuntu.translationgroup = dtg
     logout()
-    return setupBrowser(auth='Basic dtg-member@xxxxxx:test')
+    return setupBrowser(auth="Basic dtg-member@xxxxxx:test")
 
 
 def setupRosettaExpertBrowser():
     """Testbrowser configured for Rosetta Experts."""
 
-    login('admin@xxxxxxxxxxxxx')
+    login("admin@xxxxxxxxxxxxx")
     try:
         rosetta_expert = LaunchpadObjectFactory().makePerson(
-            name='rosetta-experts-member', email='re@xxxxxx')
+            name="rosetta-experts-member", email="re@xxxxxx"
+        )
     except NameAlreadyTaken:
         # We have already created an Rosetta expert
         pass
     else:
-        rosetta_experts_team = removeSecurityProxy(getUtility(
-            ILaunchpadCelebrities).rosetta_experts)
+        rosetta_experts_team = removeSecurityProxy(
+            getUtility(ILaunchpadCelebrities).rosetta_experts
+        )
         rosetta_experts_team.addMember(
-            rosetta_expert, reviewer=rosetta_experts_team,
-            status=TeamMembershipStatus.ADMIN)
+            rosetta_expert,
+            reviewer=rosetta_experts_team,
+            status=TeamMembershipStatus.ADMIN,
+        )
     logout()
-    return setupBrowser(auth='Basic re@xxxxxx:test')
+    return setupBrowser(auth="Basic re@xxxxxx:test")
 
 
 @contextmanager
@@ -845,70 +891,77 @@ def permissive_security_policy(dbuser_name=None):
 
 
 def setUpGlobs(test):
-    test.globs['transaction'] = transaction
-    test.globs['http'] = http
-    test.globs['webservice'] = LaunchpadWebServiceCaller(
-        'launchpad-library', 'salgado-change-anything')
-    test.globs['public_webservice'] = LaunchpadWebServiceCaller(
-        'foobar123451432', 'salgado-read-nonprivate')
-    test.globs['user_webservice'] = LaunchpadWebServiceCaller(
-        'launchpad-library', 'nopriv-read-nonprivate')
-    test.globs['anon_webservice'] = LaunchpadWebServiceCaller(
-        'launchpad-library', '')
-    test.globs['setupBrowser'] = setupBrowser
-    test.globs['setupDTCBrowser'] = setupDTCBrowser
-    test.globs['setupRosettaExpertBrowser'] = setupRosettaExpertBrowser
-    test.globs['browser'] = setupBrowser()
-    test.globs['anon_browser'] = setupBrowser()
-    test.globs['user_browser'] = setupBrowser(
-        auth="Basic no-priv@xxxxxxxxxxxxx:test")
-    test.globs['admin_browser'] = setupBrowser(
-        auth="Basic foo.bar@xxxxxxxxxxxxx:test")
-
-    test.globs['ANONYMOUS'] = ANONYMOUS
+    test.globs["transaction"] = transaction
+    test.globs["http"] = http
+    test.globs["webservice"] = LaunchpadWebServiceCaller(
+        "launchpad-library", "salgado-change-anything"
+    )
+    test.globs["public_webservice"] = LaunchpadWebServiceCaller(
+        "foobar123451432", "salgado-read-nonprivate"
+    )
+    test.globs["user_webservice"] = LaunchpadWebServiceCaller(
+        "launchpad-library", "nopriv-read-nonprivate"
+    )
+    test.globs["anon_webservice"] = LaunchpadWebServiceCaller(
+        "launchpad-library", ""
+    )
+    test.globs["setupBrowser"] = setupBrowser
+    test.globs["setupDTCBrowser"] = setupDTCBrowser
+    test.globs["setupRosettaExpertBrowser"] = setupRosettaExpertBrowser
+    test.globs["browser"] = setupBrowser()
+    test.globs["anon_browser"] = setupBrowser()
+    test.globs["user_browser"] = setupBrowser(
+        auth="Basic no-priv@xxxxxxxxxxxxx:test"
+    )
+    test.globs["admin_browser"] = setupBrowser(
+        auth="Basic foo.bar@xxxxxxxxxxxxx:test"
+    )
+
+    test.globs["ANONYMOUS"] = ANONYMOUS
     # If a unicode URL is opened by the test browswer, later navigation
     # raises ValueError exceptions in /usr/lib/python2.4/Cookie.py
-    test.globs['canonical_url'] = safe_canonical_url
-    test.globs['factory'] = LaunchpadObjectFactory()
-    test.globs['find_tag_by_id'] = find_tag_by_id
-    test.globs['first_tag_by_class'] = first_tag_by_class
-    test.globs['find_tags_by_class'] = find_tags_by_class
-    test.globs['find_portlet'] = find_portlet
-    test.globs['find_main_content'] = find_main_content
-    test.globs['print_feedback_messages'] = print_feedback_messages
-    test.globs['print_table'] = print_table
-    test.globs['extract_link_from_tag'] = extract_link_from_tag
-    test.globs['extract_text'] = extract_text
-    test.globs['launchpadlib_for'] = launchpadlib_for
-    test.globs['login'] = login
-    test.globs['login_person'] = login_person
-    test.globs['logout'] = logout
-    test.globs['parse_relationship_section'] = parse_relationship_section
-    test.globs['permissive_security_policy'] = permissive_security_policy
-    test.globs['pretty'] = PrettyPrinter(width=1).pformat
-    test.globs['print_action_links'] = print_action_links
-    test.globs['print_errors'] = print_errors
-    test.globs['print_location'] = print_location
-    test.globs['print_location_apps'] = print_location_apps
-    test.globs['print_navigation_links'] = print_navigation_links
-    test.globs['print_portlet_links'] = print_portlet_links
-    test.globs['print_comments'] = print_comments
-    test.globs['print_submit_buttons'] = print_submit_buttons
-    test.globs['print_radio_button_field'] = print_radio_button_field
-    test.globs['print_batch_header'] = print_batch_header
-    test.globs['print_ppa_packages'] = print_ppa_packages
-    test.globs['print_self_link_of_entries'] = print_self_link_of_entries
-    test.globs['print_tag_with_id'] = print_tag_with_id
-    test.globs['PageTestLayer'] = PageTestLayer
-    test.globs['stop'] = stop
-    test.globs['six'] = six
-    test.globs['backslashreplace'] = backslashreplace
+    test.globs["canonical_url"] = safe_canonical_url
+    test.globs["factory"] = LaunchpadObjectFactory()
+    test.globs["find_tag_by_id"] = find_tag_by_id
+    test.globs["first_tag_by_class"] = first_tag_by_class
+    test.globs["find_tags_by_class"] = find_tags_by_class
+    test.globs["find_portlet"] = find_portlet
+    test.globs["find_main_content"] = find_main_content
+    test.globs["print_feedback_messages"] = print_feedback_messages
+    test.globs["print_table"] = print_table
+    test.globs["extract_link_from_tag"] = extract_link_from_tag
+    test.globs["extract_text"] = extract_text
+    test.globs["launchpadlib_for"] = launchpadlib_for
+    test.globs["login"] = login
+    test.globs["login_person"] = login_person
+    test.globs["logout"] = logout
+    test.globs["parse_relationship_section"] = parse_relationship_section
+    test.globs["permissive_security_policy"] = permissive_security_policy
+    test.globs["pretty"] = PrettyPrinter(width=1).pformat
+    test.globs["print_action_links"] = print_action_links
+    test.globs["print_errors"] = print_errors
+    test.globs["print_location"] = print_location
+    test.globs["print_location_apps"] = print_location_apps
+    test.globs["print_navigation_links"] = print_navigation_links
+    test.globs["print_portlet_links"] = print_portlet_links
+    test.globs["print_comments"] = print_comments
+    test.globs["print_submit_buttons"] = print_submit_buttons
+    test.globs["print_radio_button_field"] = print_radio_button_field
+    test.globs["print_batch_header"] = print_batch_header
+    test.globs["print_ppa_packages"] = print_ppa_packages
+    test.globs["print_self_link_of_entries"] = print_self_link_of_entries
+    test.globs["print_tag_with_id"] = print_tag_with_id
+    test.globs["PageTestLayer"] = PageTestLayer
+    test.globs["stop"] = stop
+    test.globs["six"] = six
+    test.globs["backslashreplace"] = backslashreplace
 
 
 # This function name doesn't follow our standard naming conventions,
 # but does follow the convention of the other doctest related *Suite()
 # functions.
 
+
 def PageTestSuite(storydir, package=None, setUp=setUpGlobs, **kw):
     """Create a suite of page tests for files found in storydir.
 
@@ -927,13 +980,21 @@ def PageTestSuite(storydir, package=None, setUp=setUpGlobs, **kw):
     filenames = {
         filename
         for filename in os.listdir(abs_storydir)
-        if filename.lower().endswith('.rst')}
+        if filename.lower().endswith(".rst")
+    }
 
     suite = unittest.TestSuite()
     # Add tests to the suite individually.
     if filenames:
         paths = [os.path.join(storydir, filename) for filename in filenames]
-        suite.addTest(LayeredDocFileSuite(
-            paths=paths, package=package, stdout_logging=False,
-            layer=PageTestLayer, setUp=setUp, **kw))
+        suite.addTest(
+            LayeredDocFileSuite(
+                paths=paths,
+                package=package,
+                stdout_logging=False,
+                layer=PageTestLayer,
+                setUp=setUp,
+                **kw,
+            )
+        )
     return suite
diff --git a/lib/lp/testing/pgsql.py b/lib/lp/testing/pgsql.py
index a13f43b..9db1fd0 100644
--- a/lib/lp/testing/pgsql.py
+++ b/lib/lp/testing/pgsql.py
@@ -1,9 +1,9 @@
 # Copyright 2009-2021 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
-'''
+"""
 Test harness for tests needing a PostgreSQL backend.
-'''
+"""
 
 import atexit
 import os
@@ -11,13 +11,10 @@ import random
 import sys
 import time
 
+import psycopg2
 from breezy.errors import LockContention
 from breezy.lock import WriteLock
-import psycopg2
-from psycopg2.errors import (
-    InvalidCatalogName,
-    ObjectInUse,
-    )
+from psycopg2.errors import InvalidCatalogName, ObjectInUse
 
 from lp.services.config import config
 from lp.services.database import activity_cols
@@ -31,8 +28,9 @@ class ConnectionWrapper:
     auto_close = True
 
     def __init__(self, real_connection):
-        assert not isinstance(real_connection, ConnectionWrapper), \
-                "Wrapped the wrapper!"
+        assert not isinstance(
+            real_connection, ConnectionWrapper
+        ), "Wrapped the wrapper!"
         self.real_connection = real_connection
         # Set to True to stop test cleanup forcing the connection closed.
         PgTestSetup.connections.append(self)
@@ -92,13 +90,15 @@ class CursorWrapper:
     to CursorWrapper.last_executed_sql.  This is useful for tests that want to
     ensure that certain SQL is generated.
     """
+
     real_cursor = None
     last_executed_sql = []
     record_sql = False
 
     def __init__(self, real_cursor):
-        assert not isinstance(real_cursor, CursorWrapper), \
-                "Wrapped the wrapper!"
+        assert not isinstance(
+            real_cursor, CursorWrapper
+        ), "Wrapped the wrapper!"
         self.real_cursor = real_cursor
 
     def execute(self, *args, **kwargs):
@@ -106,9 +106,15 @@ class CursorWrapper:
         # but should be good enough. In particular, it won't notice
         # data modification made by stored procedures.
         mutating_commands = [
-                'INSERT', 'UPDATE', 'DELETE', 'CREATE', 'DROP', 'INTO',
-                'TRUNCATE', 'REPLACE',
-                ]
+            "INSERT",
+            "UPDATE",
+            "DELETE",
+            "CREATE",
+            "DROP",
+            "INTO",
+            "TRUNCATE",
+            "REPLACE",
+        ]
         for command in mutating_commands:
             if command in args[0].upper():
                 ConnectionWrapper.dirty = True
@@ -160,9 +166,9 @@ class PgTestSetup:
     # Use a dynamically generated dbname:
     dynamic = object()
 
-    template = 'template1'
+    template = "template1"
     # Needs to match configs/testrunner*/*:
-    dbname = 'launchpad_ftest'
+    dbname = "launchpad_ftest"
     dbuser = None
     host = None
     port = None
@@ -179,20 +185,24 @@ class PgTestSetup:
     # Class attribute. True if we should destroy the DB because changes made.
     _reset_db = True
 
-    def __init__(self, template=None, dbname=dynamic, dbuser=None,
-                 host=None, port=None):
-        '''Construct the PgTestSetup
+    def __init__(
+        self, template=None, dbname=dynamic, dbuser=None, host=None, port=None
+    ):
+        """Construct the PgTestSetup
 
         Note that dbuser is not used for setting up or tearing down
         the database - it is only used by the connect() method
-        '''
+        """
         if template is not None:
             self.template = template
         if dbname is PgTestSetup.dynamic:
             from lp.testing.layers import BaseLayer
-            if os.environ.get('LP_TEST_INSTANCE'):
+
+            if os.environ.get("LP_TEST_INSTANCE"):
                 self.dbname = "%s_%s" % (
-                    self.__class__.dbname, os.environ.get('LP_TEST_INSTANCE'))
+                    self.__class__.dbname,
+                    os.environ.get("LP_TEST_INSTANCE"),
+                )
                 # Stash the name we use in the config if a writable config is
                 # available.
                 # Avoid circular imports
@@ -200,13 +210,18 @@ class PgTestSetup:
 rw_main_primary: dbname=%s
 rw_main_standby: dbname=%s
 
-""" % (self.dbname, self.dbname)
+""" % (
+                    self.dbname,
+                    self.dbname,
+                )
                 if BaseLayer.config_fixture is not None:
                     BaseLayer.config_fixture.add_section(section)
                 if BaseLayer.appserver_config_fixture is not None:
                     BaseLayer.appserver_config_fixture.add_section(section)
             if config.instance_name in (
-                BaseLayer.config_name, BaseLayer.appserver_config_name):
+                BaseLayer.config_name,
+                BaseLayer.appserver_config_name,
+            ):
                 config.reloadConfig()
             else:
                 # Fallback to the class name.
@@ -224,14 +239,14 @@ rw_main_standby: dbname=%s
             self.port = port
 
     def _connectionString(self, dbname, dbuser=None):
-        connection_parameters = ['dbname=%s' % dbname]
+        connection_parameters = ["dbname=%s" % dbname]
         if dbuser is not None:
-            connection_parameters.append('user=%s' % dbuser)
+            connection_parameters.append("user=%s" % dbuser)
         if self.host is not None:
-            connection_parameters.append('host=%s' % self.host)
+            connection_parameters.append("host=%s" % self.host)
         if self.port is not None:
-            connection_parameters.append('port=%s' % self.host)
-        return ' '.join(connection_parameters)
+            connection_parameters.append("port=%s" % self.host)
+        return " ".join(connection_parameters)
 
     def superuser_connection(self, dbname=None):
         if dbname is None:
@@ -239,12 +254,12 @@ rw_main_standby: dbname=%s
         return psycopg2.connect(self._connectionString(dbname))
 
     def setUp(self):
-        '''Create a fresh database (dropping the old if necessary)
+        """Create a fresh database (dropping the old if necessary)
 
         Skips db creation if reset_db is False
-        '''
+        """
         # This is now done globally in test.py
-        #installFakeConnect()
+        # installFakeConnect()
         if (self.template, self.dbname) != PgTestSetup._last_db:
             PgTestSetup._reset_db = True
         if not PgTestSetup._reset_db:
@@ -261,9 +276,15 @@ rw_main_standby: dbname=%s
         # try for up to 10 seconds:
         debug = False
         if debug:
-            sys.stderr.write('%0.2f starting %s\n' % (start, pid,))
+            sys.stderr.write(
+                "%0.2f starting %s\n"
+                % (
+                    start,
+                    pid,
+                )
+            )
         lock = None
-        lockname = '/tmp/lp.createdb.%s' % (self.template,)
+        lockname = "/tmp/lp.createdb.%s" % (self.template,)
         # Wait for the external lock.  Most LP tests use the
         # DatabaseLayer which does a double-indirect: it clones the
         # launchpad_ftest_template into a per-test runner template, so
@@ -277,14 +298,20 @@ rw_main_standby: dbname=%s
         while time.time() - start < 30.0:
             try:
                 if debug:
-                    sys.stderr.write('taking %s\n' % (pid,))
+                    sys.stderr.write("taking %s\n" % (pid,))
                 lock = WriteLock(lockname)
                 if debug:
-                    sys.stderr.write('%0.2f taken %s\n' % (time.time(), pid,))
+                    sys.stderr.write(
+                        "%0.2f taken %s\n"
+                        % (
+                            time.time(),
+                            pid,
+                        )
+                    )
                 break
             except LockContention:
                 if debug:
-                    sys.stderr.write('blocked %s\n' % (pid,))
+                    sys.stderr.write("blocked %s\n" % (pid,))
             time.sleep(random.random())
         if lock is None:
             raise LockContention(lockname)
@@ -297,7 +324,8 @@ rw_main_standby: dbname=%s
                 if debug:
                     sys.stderr.write(
                         "%0.2f connecting %s %s\n"
-                        % (time.time(), pid, self.template))
+                        % (time.time(), pid, self.template)
+                    )
                 con = self.superuser_connection(self.template)
                 try:
                     con.set_isolation_level(0)
@@ -307,44 +335,48 @@ rw_main_standby: dbname=%s
                         try:
                             cur.execute(
                                 "CREATE DATABASE %s TEMPLATE=%s "
-                                "ENCODING='UNICODE'" % (
-                                    self.dbname, self.template))
+                                "ENCODING='UNICODE'"
+                                % (self.dbname, self.template)
+                            )
                             # Try to ensure our cleanup gets invoked, even in