← Back to team overview

launchpad-reviewers team mailing list archive

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

 

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

Commit message:
lp.snappy: Apply black

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/+git/launchpad/+merge/427276
-- 
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-snappy into launchpad:master.
diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs
index 19c4d14..a4e636a 100644
--- a/.git-blame-ignore-revs
+++ b/.git-blame-ignore-revs
@@ -84,3 +84,5 @@ ed7d7b97b8fb4ebe92799f922b0fa9c4bd1714e8
 66301b8ad409deeb9092dfe62c8e4ef6f3093302
 # apply black to lp.services
 4719b7aa672a2674c7fdbbde58772871b77c3301
+# apply black to lp.snappy
+cf7c6a08bd010dd260bff4690d64479fadf37e67
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index d56d052..a665559 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -56,6 +56,7 @@ repos:
             |registry
             |scripts
             |services
+            |snappy
           )/
 -   repo: https://github.com/PyCQA/isort
     rev: 5.9.2
@@ -88,6 +89,7 @@ repos:
             |registry
             |scripts
             |services
+            |snappy
           )/
     -   id: isort
         alias: isort-black
@@ -110,6 +112,7 @@ repos:
             |registry
             |scripts
             |services
+            |snappy
           )/
 -   repo: https://github.com/PyCQA/flake8
     rev: 3.9.2
diff --git a/lib/lp/snappy/adapters/buildarch.py b/lib/lp/snappy/adapters/buildarch.py
index 3e1ea75..e4e92f7 100644
--- a/lib/lp/snappy/adapters/buildarch.py
+++ b/lib/lp/snappy/adapters/buildarch.py
@@ -2,17 +2,11 @@
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 __all__ = [
-    'determine_architectures_to_build',
-    ]
+    "determine_architectures_to_build",
+]
 
 from collections import Counter
-from typing import (
-    Any,
-    Dict,
-    List,
-    Optional,
-    Union,
-    )
+from typing import Any, Dict, List, Optional, Union
 
 from lp.services.helpers import english_list
 
@@ -27,7 +21,9 @@ class MissingPropertyError(SnapArchitecturesParserError):
     def __init__(self, prop):
         super().__init__(
             "Architecture specification is missing the {!r} property".format(
-                prop))
+                prop
+            )
+        )
         self.property = prop
 
 
@@ -37,7 +33,8 @@ class IncompatibleArchitecturesStyleError(SnapArchitecturesParserError):
     def __init__(self):
         super().__init__(
             "'architectures' must either be a list of strings or dicts, not "
-            "both")
+            "both"
+        )
 
 
 class DuplicateBuildOnError(SnapArchitecturesParserError):
@@ -47,7 +44,9 @@ class DuplicateBuildOnError(SnapArchitecturesParserError):
         super().__init__(
             "{} {} present in the 'build-on' of multiple items".format(
                 english_list(duplicates),
-                "is" if len(duplicates) == 1 else "are"))
+                "is" if len(duplicates) == 1 else "are",
+            )
+        )
 
 
 class UnsupportedBuildOnError(SnapArchitecturesParserError):
@@ -56,7 +55,9 @@ class UnsupportedBuildOnError(SnapArchitecturesParserError):
     def __init__(self, build_on):
         super().__init__(
             "build-on specifies no supported architectures: {!r}".format(
-                build_on))
+                build_on
+            )
+        )
         self.build_on = build_on
 
 
@@ -67,7 +68,7 @@ class SnapArchitecture:
         self,
         build_on: Union[str, List[str]],
         build_for: Optional[Union[str, List[str]]] = None,
-        build_error: Optional[str] = None
+        build_error: Optional[str] = None,
     ):
         """Create a new architecture entry.
 
@@ -133,8 +134,10 @@ class SnapBuildInstance:
         """
         try:
             self.architecture = next(
-                arch for arch in supported_architectures
-                if arch in architecture.build_on)
+                arch
+                for arch in supported_architectures
+                if arch in architecture.build_on
+            )
         except StopIteration:
             raise UnsupportedBuildOnError(architecture.build_on)
 
@@ -153,8 +156,8 @@ def determine_architectures_to_build(
         we can create builds for.
     :return: a list of `SnapBuildInstance`s.
     """
-    architectures_list = (
-        snapcraft_data.get("architectures")
+    architectures_list = snapcraft_data.get(
+        "architectures"
     )  # type: Optional[List]
 
     if architectures_list:
@@ -167,7 +170,8 @@ def determine_architectures_to_build(
         elif all(isinstance(arch, dict) for arch in architectures_list):
             # If a list of dicts (new style), then that's multiple items.
             architectures = [
-                SnapArchitecture.from_dict(a) for a in architectures_list]
+                SnapArchitecture.from_dict(a) for a in architectures_list
+            ]
         else:
             # If a mix of both, bail.  We can't reasonably handle it.
             raise IncompatibleArchitecturesStyleError()
@@ -175,7 +179,8 @@ def determine_architectures_to_build(
         # If no architectures are specified, build one for each supported
         # architecture.
         architectures = [
-            SnapArchitecture(build_on=a) for a in supported_arches]
+            SnapArchitecture(build_on=a) for a in supported_arches
+        ]
 
     # Ensure that multiple `build-on` items don't include the same
     # architecture; this is ambiguous and forbidden by snapcraft.  Checking
@@ -192,7 +197,8 @@ def determine_architectures_to_build(
     for arch in architectures:
         try:
             architectures_to_build.append(
-                SnapBuildInstance(arch, supported_arches))
+                SnapBuildInstance(arch, supported_arches)
+            )
         except UnsupportedBuildOnError:
             # Snaps are allowed to declare that they build on architectures
             # that Launchpad doesn't currently support (perhaps they're
diff --git a/lib/lp/snappy/adapters/tests/test_buildarch.py b/lib/lp/snappy/adapters/tests/test_buildarch.py
index 2f1610f..81f3c87 100644
--- a/lib/lp/snappy/adapters/tests/test_buildarch.py
+++ b/lib/lp/snappy/adapters/tests/test_buildarch.py
@@ -1,64 +1,83 @@
 # Copyright 2018 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
-from testscenarios import (
-    load_tests_apply_scenarios,
-    WithScenarios,
-    )
+from testscenarios import WithScenarios, load_tests_apply_scenarios
 from testtools.matchers import HasLength
 
 from lp.snappy.adapters.buildarch import (
-    determine_architectures_to_build,
     SnapArchitecture,
     SnapBuildInstance,
     UnsupportedBuildOnError,
-    )
+    determine_architectures_to_build,
+)
 from lp.testing import TestCase
 
 
 class TestSnapArchitecture(WithScenarios, TestCase):
 
     scenarios = [
-        ("lists", {
-            "architectures": {"build-on": ["amd64"], "build-for": ["amd64"]},
-            "expected_build_on": ["amd64"],
-            "expected_build_for": ["amd64"],
-            "expected_build_error": None,
-            }),
-        ("strings", {
-            "architectures": {"build-on": "amd64", "build-for": "amd64"},
-            "expected_build_on": ["amd64"],
-            "expected_build_for": ["amd64"],
-            "expected_build_error": None,
-            }),
-        ("no build-for", {
-            "architectures": {"build-on": ["amd64"]},
-            "expected_build_on": ["amd64"],
-            "expected_build_for": ["amd64"],
-            "expected_build_error": None,
-            }),
-        ("not required", {
-            "architectures": {
-                "build-on": ["amd64"],
-                "build-for": "amd64",
-                "build-error": "ignore"},
-            "expected_build_on": ["amd64"],
-            "expected_build_for": ["amd64"],
-            "expected_build_error": "ignore",
-            }),
-        ("build-for", {
-            "architectures": {"build-on": ["amd64"], "build-for": "all"},
-            "expected_build_on": ["amd64"],
-            "expected_build_for": ["all"],
-            "expected_build_error": None,
-            }),
-        ("run-on", {
-            "architectures": {"build-on": ["amd64"], "run-on": "all"},
-            "expected_build_on": ["amd64"],
-            "expected_build_for": ["all"],
-            "expected_build_error": None,
-            }),
-        ]
+        (
+            "lists",
+            {
+                "architectures": {
+                    "build-on": ["amd64"],
+                    "build-for": ["amd64"],
+                },
+                "expected_build_on": ["amd64"],
+                "expected_build_for": ["amd64"],
+                "expected_build_error": None,
+            },
+        ),
+        (
+            "strings",
+            {
+                "architectures": {"build-on": "amd64", "build-for": "amd64"},
+                "expected_build_on": ["amd64"],
+                "expected_build_for": ["amd64"],
+                "expected_build_error": None,
+            },
+        ),
+        (
+            "no build-for",
+            {
+                "architectures": {"build-on": ["amd64"]},
+                "expected_build_on": ["amd64"],
+                "expected_build_for": ["amd64"],
+                "expected_build_error": None,
+            },
+        ),
+        (
+            "not required",
+            {
+                "architectures": {
+                    "build-on": ["amd64"],
+                    "build-for": "amd64",
+                    "build-error": "ignore",
+                },
+                "expected_build_on": ["amd64"],
+                "expected_build_for": ["amd64"],
+                "expected_build_error": "ignore",
+            },
+        ),
+        (
+            "build-for",
+            {
+                "architectures": {"build-on": ["amd64"], "build-for": "all"},
+                "expected_build_on": ["amd64"],
+                "expected_build_for": ["all"],
+                "expected_build_error": None,
+            },
+        ),
+        (
+            "run-on",
+            {
+                "architectures": {"build-on": ["amd64"], "run-on": "all"},
+                "expected_build_on": ["amd64"],
+                "expected_build_for": ["all"],
+                "expected_build_error": None,
+            },
+        ),
+    ]
 
     def test_architecture(self):
         architecture = SnapArchitecture.from_dict(self.architectures)
@@ -72,64 +91,85 @@ class TestSnapBuildInstance(WithScenarios, TestCase):
     # Single-item scenarios taken from the architectures document:
     # https://forum.snapcraft.io/t/architectures/4972
     scenarios = [
-        ("i386", {
-            "architecture": SnapArchitecture(
-                build_on="i386", build_for=["amd64", "i386"]),
-            "supported_architectures": ["amd64", "i386", "armhf"],
-            "expected_architecture": "i386",
-            "expected_target_architectures": ["amd64", "i386"],
-            "expected_required": True,
-            }),
-        ("amd64", {
-            "architecture": SnapArchitecture(
-                build_on="amd64", build_for="all"
-            ),
-            "supported_architectures": ["amd64", "i386", "armhf"],
-            "expected_architecture": "amd64",
-            "expected_target_architectures": ["all"],
-            "expected_required": True,
-            }),
-        ("amd64 priority", {
-            "architecture": SnapArchitecture(
-                build_on=["amd64", "i386"], build_for="all"),
-            "supported_architectures": ["amd64", "i386", "armhf"],
-            "expected_architecture": "amd64",
-            "expected_target_architectures": ["all"],
-            "expected_required": True,
-            }),
-        ("i386 priority", {
-            "architecture": SnapArchitecture(
-                build_on=["amd64", "i386"], build_for="all"),
-            "supported_architectures": ["i386", "amd64", "armhf"],
-            "expected_architecture": "i386",
-            "expected_target_architectures": ["all"],
-            "expected_required": True,
-            }),
-        ("optional", {
-            "architecture": SnapArchitecture(
-                build_on="amd64", build_error="ignore"),
-            "supported_architectures": ["amd64", "i386", "armhf"],
-            "expected_architecture": "amd64",
-            "expected_target_architectures": ["amd64"],
-            "expected_required": False,
-            }),
-        ]
+        (
+            "i386",
+            {
+                "architecture": SnapArchitecture(
+                    build_on="i386", build_for=["amd64", "i386"]
+                ),
+                "supported_architectures": ["amd64", "i386", "armhf"],
+                "expected_architecture": "i386",
+                "expected_target_architectures": ["amd64", "i386"],
+                "expected_required": True,
+            },
+        ),
+        (
+            "amd64",
+            {
+                "architecture": SnapArchitecture(
+                    build_on="amd64", build_for="all"
+                ),
+                "supported_architectures": ["amd64", "i386", "armhf"],
+                "expected_architecture": "amd64",
+                "expected_target_architectures": ["all"],
+                "expected_required": True,
+            },
+        ),
+        (
+            "amd64 priority",
+            {
+                "architecture": SnapArchitecture(
+                    build_on=["amd64", "i386"], build_for="all"
+                ),
+                "supported_architectures": ["amd64", "i386", "armhf"],
+                "expected_architecture": "amd64",
+                "expected_target_architectures": ["all"],
+                "expected_required": True,
+            },
+        ),
+        (
+            "i386 priority",
+            {
+                "architecture": SnapArchitecture(
+                    build_on=["amd64", "i386"], build_for="all"
+                ),
+                "supported_architectures": ["i386", "amd64", "armhf"],
+                "expected_architecture": "i386",
+                "expected_target_architectures": ["all"],
+                "expected_required": True,
+            },
+        ),
+        (
+            "optional",
+            {
+                "architecture": SnapArchitecture(
+                    build_on="amd64", build_error="ignore"
+                ),
+                "supported_architectures": ["amd64", "i386", "armhf"],
+                "expected_architecture": "amd64",
+                "expected_target_architectures": ["amd64"],
+                "expected_required": False,
+            },
+        ),
+    ]
 
     def test_build_instance(self):
         instance = SnapBuildInstance(
-            self.architecture, self.supported_architectures)
+            self.architecture, self.supported_architectures
+        )
         self.assertEqual(self.expected_architecture, instance.architecture)
-        self.assertEqual(self.expected_target_architectures,
-                         instance.target_architectures)
+        self.assertEqual(
+            self.expected_target_architectures, instance.target_architectures
+        )
         self.assertEqual(self.expected_required, instance.required)
 
 
 class TestSnapBuildInstanceError(TestCase):
-
     def test_no_matching_arch_raises(self):
         architecture = SnapArchitecture(build_on="amd64", build_for="amd64")
         raised = self.assertRaises(
-            UnsupportedBuildOnError, SnapBuildInstance, architecture, ["i386"])
+            UnsupportedBuildOnError, SnapBuildInstance, architecture, ["i386"]
+        )
         self.assertEqual(["amd64"], raised.build_on)
 
 
@@ -139,192 +179,226 @@ class TestDetermineArchitecturesToBuild(WithScenarios, TestCase):
     # https://forum.snapcraft.io/t/architectures/4972
 
     scenarios = [
-        ("none", {
-            "architectures": None,
-            "supported_architectures": ["amd64", "i386", "armhf"],
-            "expected": [
-                {
-                    "architecture": "amd64",
-                    "target_architectures": ["amd64"],
-                    "required": True,
-                },
-                {
-                    "architecture": "i386",
-                    "target_architectures": ["i386"],
-                    "required": True
-                },
-                {
-                    "architecture": "armhf",
-                    "target_architectures": ["armhf"],
-                    "required": True
-                },
-            ],
-        }),
-        ("i386", {
-            "architectures": [
-                {"build-on": "i386", "build-for": ["amd64", "i386"]},
-            ],
-            "supported_architectures": ["amd64", "i386", "armhf"],
-            "expected": [
-                {
-                    "architecture": "i386",
-                    "target_architectures": ["amd64", "i386"],
-                    "required": True,
-                }
-            ],
-        }),
-        ("amd64", {
-            "architectures": [{"build-on": "amd64", "build-for": "all"}],
-            "supported_architectures": ["amd64", "i386", "armhf"],
-            "expected": [
-                {
-                    "architecture": "amd64",
-                    "target_architectures": ["all"],
-                    "required": True
-                }
-            ],
-        }),
-        ("amd64 and i386", {
-            "architectures": [
-                {"build-on": "amd64", "build-for": "amd64"},
-                {"build-on": "i386", "build-for": "i386"},
-            ],
-            "supported_architectures": ["amd64", "i386", "armhf"],
-            "expected": [
-                {
-                    "architecture": "amd64",
-                    "target_architectures": ["amd64"],
-                    "required": True,
-                },
-                {
-                    "architecture": "i386",
-                    "target_architectures": ["i386"],
-                    "required": True,
-                },
-            ],
-        }),
-        ("amd64 and i386 shorthand", {
-            "architectures": [
-                {"build-on": "amd64"},
-                {"build-on": "i386"},
-            ],
-            "supported_architectures": ["amd64", "i386", "armhf"],
-            "expected": [
-                {
-                    "architecture": "amd64",
-                    "target_architectures": ["amd64"],
-                    "required": True,
-                },
-                {
-                    "architecture": "i386",
-                    "target_architectures": ["i386"],
-                    "required": True,
-                },
-            ],
-        }),
-        ("amd64, i386, and armhf", {
-            "architectures": [
-                {"build-on": "amd64", "build-for": "amd64"},
-                {"build-on": "i386", "build-for": "i386"},
-                {
-                    "build-on": "armhf",
-                    "build-for": "armhf",
-                    "build-error": "ignore",
-                },
-            ],
-            "supported_architectures": ["amd64", "i386", "armhf"],
-            "expected": [
-                {
-                    "architecture": "amd64",
-                    "target_architectures": ["amd64"],
-                    "required": True,
-                },
-                {
-                    "architecture": "i386",
-                    "target_architectures": ["i386"],
-                    "required": True,
-                },
-                {
-                    "architecture": "armhf",
-                    "target_architectures": ["armhf"],
-                    "required": False,
-                },
-            ],
-        }),
-        ("amd64 priority", {
-            "architectures": [
-                {"build-on": ["amd64", "i386"], "build-for": "all"},
-            ],
-            "supported_architectures": ["amd64", "i386", "armhf"],
-            "expected": [
-                {
-                    "architecture": "amd64",
-                    "target_architectures": ["all"],
-                    "required": True,
-                }
-            ],
-        }),
-        ("i386 priority", {
-            "architectures": [
-                {"build-on": ["amd64", "i386"], "build-for": "all"},
-            ],
-            "supported_architectures": ["i386", "amd64", "armhf"],
-            "expected": [
-                {
-                    "architecture": "i386",
-                    "target_architectures": ["all"],
-                    "required": True,
-                }
-            ],
-        }),
-        ("old style i386 priority", {
-            "architectures": ["amd64", "i386"],
-            "supported_architectures": ["i386", "amd64", "armhf"],
-            "expected": [
-                {
-                    "architecture": "i386",
-                    "target_architectures": ["amd64", "i386"],
-                    "required": True
-                }
-            ],
-        }),
-        ("old style amd64 priority", {
-            "architectures": ["amd64", "i386"],
-            "supported_architectures": ["amd64", "i386", "armhf"],
-            "expected": [
-                {
-                    "architecture": "amd64",
-                    "target_architectures": ["amd64", "i386"],
-                    "required": True,
-                }
-            ],
-        }),
-        ("more architectures listed than are supported", {
-            "architectures": [
-                {"build-on": "amd64"},
-                {"build-on": "i386"},
-                {"build-on": "armhf"},
-            ],
-            "supported_architectures": ["amd64", "i386"],
-            "expected": [
-                {
-                    "architecture": "amd64",
-                    "target_architectures": ["amd64"],
-                    "required": True,
-                },
-                {
-                    "architecture": "i386",
-                    "target_architectures": ["i386"],
-                    "required": True,
-                },
-            ],
-        })
+        (
+            "none",
+            {
+                "architectures": None,
+                "supported_architectures": ["amd64", "i386", "armhf"],
+                "expected": [
+                    {
+                        "architecture": "amd64",
+                        "target_architectures": ["amd64"],
+                        "required": True,
+                    },
+                    {
+                        "architecture": "i386",
+                        "target_architectures": ["i386"],
+                        "required": True,
+                    },
+                    {
+                        "architecture": "armhf",
+                        "target_architectures": ["armhf"],
+                        "required": True,
+                    },
+                ],
+            },
+        ),
+        (
+            "i386",
+            {
+                "architectures": [
+                    {"build-on": "i386", "build-for": ["amd64", "i386"]},
+                ],
+                "supported_architectures": ["amd64", "i386", "armhf"],
+                "expected": [
+                    {
+                        "architecture": "i386",
+                        "target_architectures": ["amd64", "i386"],
+                        "required": True,
+                    }
+                ],
+            },
+        ),
+        (
+            "amd64",
+            {
+                "architectures": [{"build-on": "amd64", "build-for": "all"}],
+                "supported_architectures": ["amd64", "i386", "armhf"],
+                "expected": [
+                    {
+                        "architecture": "amd64",
+                        "target_architectures": ["all"],
+                        "required": True,
+                    }
+                ],
+            },
+        ),
+        (
+            "amd64 and i386",
+            {
+                "architectures": [
+                    {"build-on": "amd64", "build-for": "amd64"},
+                    {"build-on": "i386", "build-for": "i386"},
+                ],
+                "supported_architectures": ["amd64", "i386", "armhf"],
+                "expected": [
+                    {
+                        "architecture": "amd64",
+                        "target_architectures": ["amd64"],
+                        "required": True,
+                    },
+                    {
+                        "architecture": "i386",
+                        "target_architectures": ["i386"],
+                        "required": True,
+                    },
+                ],
+            },
+        ),
+        (
+            "amd64 and i386 shorthand",
+            {
+                "architectures": [
+                    {"build-on": "amd64"},
+                    {"build-on": "i386"},
+                ],
+                "supported_architectures": ["amd64", "i386", "armhf"],
+                "expected": [
+                    {
+                        "architecture": "amd64",
+                        "target_architectures": ["amd64"],
+                        "required": True,
+                    },
+                    {
+                        "architecture": "i386",
+                        "target_architectures": ["i386"],
+                        "required": True,
+                    },
+                ],
+            },
+        ),
+        (
+            "amd64, i386, and armhf",
+            {
+                "architectures": [
+                    {"build-on": "amd64", "build-for": "amd64"},
+                    {"build-on": "i386", "build-for": "i386"},
+                    {
+                        "build-on": "armhf",
+                        "build-for": "armhf",
+                        "build-error": "ignore",
+                    },
+                ],
+                "supported_architectures": ["amd64", "i386", "armhf"],
+                "expected": [
+                    {
+                        "architecture": "amd64",
+                        "target_architectures": ["amd64"],
+                        "required": True,
+                    },
+                    {
+                        "architecture": "i386",
+                        "target_architectures": ["i386"],
+                        "required": True,
+                    },
+                    {
+                        "architecture": "armhf",
+                        "target_architectures": ["armhf"],
+                        "required": False,
+                    },
+                ],
+            },
+        ),
+        (
+            "amd64 priority",
+            {
+                "architectures": [
+                    {"build-on": ["amd64", "i386"], "build-for": "all"},
+                ],
+                "supported_architectures": ["amd64", "i386", "armhf"],
+                "expected": [
+                    {
+                        "architecture": "amd64",
+                        "target_architectures": ["all"],
+                        "required": True,
+                    }
+                ],
+            },
+        ),
+        (
+            "i386 priority",
+            {
+                "architectures": [
+                    {"build-on": ["amd64", "i386"], "build-for": "all"},
+                ],
+                "supported_architectures": ["i386", "amd64", "armhf"],
+                "expected": [
+                    {
+                        "architecture": "i386",
+                        "target_architectures": ["all"],
+                        "required": True,
+                    }
+                ],
+            },
+        ),
+        (
+            "old style i386 priority",
+            {
+                "architectures": ["amd64", "i386"],
+                "supported_architectures": ["i386", "amd64", "armhf"],
+                "expected": [
+                    {
+                        "architecture": "i386",
+                        "target_architectures": ["amd64", "i386"],
+                        "required": True,
+                    }
+                ],
+            },
+        ),
+        (
+            "old style amd64 priority",
+            {
+                "architectures": ["amd64", "i386"],
+                "supported_architectures": ["amd64", "i386", "armhf"],
+                "expected": [
+                    {
+                        "architecture": "amd64",
+                        "target_architectures": ["amd64", "i386"],
+                        "required": True,
+                    }
+                ],
+            },
+        ),
+        (
+            "more architectures listed than are supported",
+            {
+                "architectures": [
+                    {"build-on": "amd64"},
+                    {"build-on": "i386"},
+                    {"build-on": "armhf"},
+                ],
+                "supported_architectures": ["amd64", "i386"],
+                "expected": [
+                    {
+                        "architecture": "amd64",
+                        "target_architectures": ["amd64"],
+                        "required": True,
+                    },
+                    {
+                        "architecture": "i386",
+                        "target_architectures": ["i386"],
+                        "required": True,
+                    },
+                ],
+            },
+        ),
     ]
 
     def test_parser(self):
         snapcraft_data = {"architectures": self.architectures}
         build_instances = determine_architectures_to_build(
-            snapcraft_data, self.supported_architectures)
+            snapcraft_data, self.supported_architectures
+        )
         self.assertThat(build_instances, HasLength(len(self.expected)))
         for instance in build_instances:
             self.assertIn(instance.__dict__, self.expected)
diff --git a/lib/lp/snappy/browser/hassnaps.py b/lib/lp/snappy/browser/hassnaps.py
index f266a1f..7a2ee77 100644
--- a/lib/lp/snappy/browser/hassnaps.py
+++ b/lib/lp/snappy/browser/hassnaps.py
@@ -4,47 +4,44 @@
 """Mixins for browser classes for objects that implement IHasSnaps."""
 
 __all__ = [
-    'HasSnapsMenuMixin',
-    'HasSnapsViewMixin',
-    ]
+    "HasSnapsMenuMixin",
+    "HasSnapsViewMixin",
+]
 
 from zope.component import getUtility
 
 from lp.code.browser.decorations import DecoratedBranch
 from lp.code.interfaces.gitrepository import IGitRepository
 from lp.services.features import getFeatureFlag
-from lp.services.webapp import (
-    canonical_url,
-    Link,
-    )
+from lp.services.webapp import Link, canonical_url
 from lp.services.webapp.escaping import structured
-from lp.snappy.interfaces.snap import (
-    ISnapSet,
-    SNAP_PRIVATE_FEATURE_FLAG,
-    )
+from lp.snappy.interfaces.snap import SNAP_PRIVATE_FEATURE_FLAG, ISnapSet
 
 
 class HasSnapsMenuMixin:
     """A mixin for context menus for objects that implement IHasSnaps."""
 
     def view_snaps(self):
-        text = 'View snap packages'
+        text = "View snap packages"
         context = self.context
         if isinstance(context, DecoratedBranch):
             context = context.branch
-        enabled = not getUtility(ISnapSet).findByContext(
-            context, visible_by_user=self.user).is_empty()
-        return Link('+snaps', text, icon='info', enabled=enabled)
+        enabled = (
+            not getUtility(ISnapSet)
+            .findByContext(context, visible_by_user=self.user)
+            .is_empty()
+        )
+        return Link("+snaps", text, icon="info", enabled=enabled)
 
     def create_snap(self):
         # Only enabled if the snap_private flag is enabled for
         # private contexts.
-        enabled = (
-            not self.context.private or
-            bool(getFeatureFlag(SNAP_PRIVATE_FEATURE_FLAG)))
+        enabled = not self.context.private or bool(
+            getFeatureFlag(SNAP_PRIVATE_FEATURE_FLAG)
+        )
 
-        text = 'Create snap package'
-        return Link('+new-snap', text, enabled=enabled, icon='add')
+        text = "Create snap package"
+        return Link("+new-snap", text, enabled=enabled, icon="add")
 
 
 class HasSnapsViewMixin:
@@ -56,26 +53,31 @@ class HasSnapsViewMixin:
         if isinstance(context, DecoratedBranch):
             context = context.branch
         return getUtility(ISnapSet).findByContext(
-            context, visible_by_user=self.user)
+            context, visible_by_user=self.user
+        )
 
     @property
     def snaps_link(self):
         """A link to snap packages for this object."""
         count = self.snaps.count()
         if IGitRepository.providedBy(self.context):
-            context_type = 'repository'
+            context_type = "repository"
         else:
-            context_type = 'branch'
+            context_type = "branch"
         if count == 0:
             # Nothing to link to.
-            return 'No snap packages using this %s.' % context_type
+            return "No snap packages using this %s." % context_type
         elif count == 1:
             # Link to the single snap package.
             return structured(
                 '<a href="%s">1 snap package</a> using this %s.',
-                canonical_url(self.snaps.one()), context_type).escapedtext
+                canonical_url(self.snaps.one()),
+                context_type,
+            ).escapedtext
         else:
             # Link to a snap package listing.
             return structured(
                 '<a href="+snaps">%s snap packages</a> using this %s.',
-                count, context_type).escapedtext
+                count,
+                context_type,
+            ).escapedtext
diff --git a/lib/lp/snappy/browser/snap.py b/lib/lp/snappy/browser/snap.py
index eccbcd8..13fbe82 100644
--- a/lib/lp/snappy/browser/snap.py
+++ b/lib/lp/snappy/browser/snap.py
@@ -4,46 +4,35 @@
 """Snap views."""
 
 __all__ = [
-    'SnapAddView',
-    'SnapAuthorizeView',
-    'SnapContextMenu',
-    'SnapDeleteView',
-    'SnapEditView',
-    'SnapNavigation',
-    'SnapNavigationMenu',
-    'SnapRequestBuildsView',
-    'SnapView',
-    ]
+    "SnapAddView",
+    "SnapAuthorizeView",
+    "SnapContextMenu",
+    "SnapDeleteView",
+    "SnapEditView",
+    "SnapNavigation",
+    "SnapNavigationMenu",
+    "SnapRequestBuildsView",
+    "SnapView",
+]
 
 from urllib.parse import urlencode
 
 from lazr.restful.fields import Reference
-from lazr.restful.interface import (
-    copy_field,
-    use_template,
-    )
+from lazr.restful.interface import copy_field, use_template
 from zope.component import getUtility
 from zope.error.interfaces import IErrorReportingUtility
 from zope.formlib.widget import CustomWidgetFactory
-from zope.interface import (
-    implementer,
-    Interface,
-    )
-from zope.schema import (
-    Choice,
-    Dict,
-    List,
-    TextLine,
-    )
+from zope.interface import Interface, implementer
+from zope.schema import Choice, Dict, List, TextLine
 from zope.security.interfaces import Unauthorized
 
 from lp import _
 from lp.app.browser.launchpadform import (
-    action,
     LaunchpadEditFormView,
     LaunchpadFormView,
+    action,
     render_radio_widget_part,
-    )
+)
 from lp.app.browser.lazrjs import InlinePersonEditPickerWidget
 from lp.app.browser.tales import format_link
 from lp.app.enums import PRIVATE_INFORMATION_TYPES
@@ -55,7 +44,7 @@ from lp.app.widgets.itemswidgets import (
     LaunchpadDropdownWidget,
     LaunchpadRadioWidget,
     LaunchpadRadioWidgetWithDescription,
-    )
+)
 from lp.buildmaster.interfaces.processor import IProcessorSet
 from lp.code.browser.widgets.gitref import GitRefWidget
 from lp.code.interfaces.branch import IBranch
@@ -70,29 +59,25 @@ from lp.services.propertycache import cachedproperty
 from lp.services.scripts import log
 from lp.services.utils import seconds_since_epoch
 from lp.services.webapp import (
-    canonical_url,
     ContextMenu,
-    enabled_with_permission,
     LaunchpadView,
     Link,
     Navigation,
     NavigationMenu,
+    canonical_url,
+    enabled_with_permission,
     stepthrough,
     structured,
-    )
-from lp.services.webapp.breadcrumb import (
-    Breadcrumb,
-    NameBreadcrumb,
-    )
+)
+from lp.services.webapp.breadcrumb import Breadcrumb, NameBreadcrumb
 from lp.services.webapp.interfaces import ICanonicalUrlData
 from lp.services.webapp.url import urlappend
 from lp.services.webhooks.browser import WebhookTargetNavigationMixin
 from lp.snappy.browser.widgets.snaparchive import SnapArchiveWidget
-from lp.snappy.browser.widgets.snapbuildchannels import (
-    SnapBuildChannelsWidget,
-    )
+from lp.snappy.browser.widgets.snapbuildchannels import SnapBuildChannelsWidget
 from lp.snappy.browser.widgets.storechannels import StoreChannelsWidget
 from lp.snappy.interfaces.snap import (
+    SNAP_PRIVATE_FEATURE_FLAG,
     CannotAuthorizeStoreUploads,
     CannotFetchSnapcraftYaml,
     CannotParseSnapcraftYaml,
@@ -100,20 +85,16 @@ from lp.snappy.interfaces.snap import (
     ISnapSet,
     MissingSnapcraftYaml,
     NoSuchSnap,
-    SNAP_PRIVATE_FEATURE_FLAG,
     SnapPrivateFeatureDisabled,
-    )
-from lp.snappy.interfaces.snapbuild import (
-    ISnapBuild,
-    ISnapBuildSet,
-    )
+)
+from lp.snappy.interfaces.snapbuild import ISnapBuild, ISnapBuildSet
 from lp.snappy.interfaces.snappyseries import (
     ISnappyDistroSeriesSet,
     ISnappySeriesSet,
-    )
+)
 from lp.snappy.interfaces.snapstoreclient import (
     BadRequestPackageUploadResponse,
-    )
+)
 from lp.soyuz.browser.archive import EnableProcessorsMixin
 from lp.soyuz.browser.build import get_build_by_id_str
 from lp.soyuz.interfaces.archive import IArchive
@@ -122,7 +103,8 @@ from lp.soyuz.interfaces.archive import IArchive
 @implementer(ICanonicalUrlData)
 class SnapURL:
     """Snap URL creation rules."""
-    rootsite = 'mainsite'
+
+    rootsite = "mainsite"
 
     def __init__(self, snap):
         self.snap = snap
@@ -143,7 +125,7 @@ class SnapURL:
 class SnapNavigation(WebhookTargetNavigationMixin, Navigation):
     usedfor = ISnap
 
-    @stepthrough('+build-request')
+    @stepthrough("+build-request")
     def traverse_build_request(self, name):
         try:
             job_id = int(name)
@@ -151,7 +133,7 @@ class SnapNavigation(WebhookTargetNavigationMixin, Navigation):
             return None
         return self.context.getBuildRequest(job_id)
 
-    @stepthrough('+build')
+    @stepthrough("+build")
     def traverse_build(self, name):
         build = get_build_by_id_str(ISnapBuildSet, name)
         if build is None or build.snap != self.context:
@@ -170,23 +152,24 @@ class SnapFormMixin:
     def validateVCSWidgets(self, cls, data):
         """Validates if VCS sub-widgets."""
         # Set widgets as required or optional depending on the vcs field.
-        vcs = data.get('vcs')
+        vcs = data.get("vcs")
         if vcs == VCSType.BZR:
-            self.widgets['branch'].context.required = True
-            self.widgets['git_ref'].context.required = False
+            self.widgets["branch"].context.required = True
+            self.widgets["git_ref"].context.required = False
         elif vcs == VCSType.GIT:
-            self.widgets['branch'].context.required = False
-            self.widgets['git_ref'].context.required = True
+            self.widgets["branch"].context.required = False
+            self.widgets["git_ref"].context.required = True
         else:
             raise AssertionError("Unknown branch type %s" % vcs)
 
     def setUpVCSWidgets(self):
-        widget = self.widgets.get('vcs')
+        widget = self.widgets.get("vcs")
         if widget is not None:
             current_value = widget._getFormValue()
             self.vcs_bzr_radio, self.vcs_git_radio = (
                 render_radio_widget_part(widget, value, current_value)
-                for value in (VCSType.BZR, VCSType.GIT))
+                for value in (VCSType.BZR, VCSType.GIT)
+            )
 
 
 class SnapInformationTypeMixin:
@@ -207,42 +190,50 @@ class SnapInformationTypeMixin:
         When creating a new snap, `snap` should be None and the possible
         information types will be calculated based on the project.
         """
-        info_type = data.get('information_type')
+        info_type = data.get("information_type")
         if IProduct.providedBy(self.context):
             project = self.context
         else:
-            project = data.get('project')
+            project = data.get("project")
         if info_type is None and project is None:
             # Nothing to validate here. Move on.
             return
         if project is None and info_type in PRIVATE_INFORMATION_TYPES:
             self.setFieldError(
-                'information_type',
-                'Private snap recipes must be associated with a project.')
+                "information_type",
+                "Private snap recipes must be associated with a project.",
+            )
         elif project is not None:
             if snap is None:
                 snap_set = getUtility(ISnapSet)
                 possible_types = snap_set.getPossibleSnapInformationTypes(
-                    project)
+                    project
+                )
             else:
                 possible_types = self.getPossibleInformationTypes(
-                    snap, self.user)
+                    snap, self.user
+                )
             if info_type not in possible_types:
-                msg = ('Project %s only accepts the following information '
-                       'types: %s.')
-                msg %= (project.name,
-                        ", ".join(i.title for i in possible_types))
-                self.setFieldError('information_type', msg)
+                msg = (
+                    "Project %s only accepts the following information "
+                    "types: %s."
+                )
+                msg %= (
+                    project.name,
+                    ", ".join(i.title for i in possible_types),
+                )
+                self.setFieldError("information_type", msg)
 
 
 class SnapBreadcrumb(NameBreadcrumb):
-
     @property
     def inside(self):
         return Breadcrumb(
             self.context.owner,
             url=canonical_url(self.context.owner, view_name="+snaps"),
-            text="Snap packages", inside=self.context.owner)
+            text="Snap packages",
+            inside=self.context.owner,
+        )
 
 
 class SnapNavigationMenu(NavigationMenu):
@@ -250,35 +241,38 @@ class SnapNavigationMenu(NavigationMenu):
 
     usedfor = ISnap
 
-    facet = 'overview'
+    facet = "overview"
 
-    links = ('admin', 'edit', 'webhooks', 'authorize', 'delete')
+    links = ("admin", "edit", "webhooks", "authorize", "delete")
 
-    @enabled_with_permission('launchpad.Admin')
+    @enabled_with_permission("launchpad.Admin")
     def admin(self):
-        return Link('+admin', 'Administer snap package', icon='edit')
+        return Link("+admin", "Administer snap package", icon="edit")
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def edit(self):
-        return Link('+edit', 'Edit snap package', icon='edit')
+        return Link("+edit", "Edit snap package", icon="edit")
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def webhooks(self):
         return Link(
-            '+webhooks', 'Manage webhooks', icon='edit',
-            enabled=bool(getFeatureFlag('webhooks.new.enabled')))
+            "+webhooks",
+            "Manage webhooks",
+            icon="edit",
+            enabled=bool(getFeatureFlag("webhooks.new.enabled")),
+        )
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def authorize(self):
         if self.context.store_secrets:
-            text = 'Reauthorize store uploads'
+            text = "Reauthorize store uploads"
         else:
-            text = 'Authorize store uploads'
-        return Link('+authorize', text, icon='edit')
+            text = "Authorize store uploads"
+        return Link("+authorize", text, icon="edit")
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def delete(self):
-        return Link('+delete', 'Delete snap package', icon='trash-icon')
+        return Link("+delete", "Delete snap package", icon="trash-icon")
 
 
 class SnapContextMenu(ContextMenu):
@@ -286,13 +280,13 @@ class SnapContextMenu(ContextMenu):
 
     usedfor = ISnap
 
-    facet = 'overview'
+    facet = "overview"
 
-    links = ('request_builds', 'add_subscriber', 'subscription')
+    links = ("request_builds", "add_subscriber", "subscription")
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def request_builds(self):
-        return Link('+request-builds', 'Request builds', icon='add')
+        return Link("+request-builds", "Request builds", icon="add")
 
     @enabled_with_permission("launchpad.AnyPerson")
     def subscription(self):
@@ -322,18 +316,23 @@ class SnapView(LaunchpadView):
     @property
     def person_picker(self):
         field = copy_field(
-            ISnap['owner'],
-            vocabularyName='AllUserTeamsParticipationPlusSelfSimpleDisplay')
+            ISnap["owner"],
+            vocabularyName="AllUserTeamsParticipationPlusSelfSimpleDisplay",
+        )
         return InlinePersonEditPickerWidget(
-            self.context, field, format_link(self.context.owner),
-            header='Change owner', step_title='Select a new owner')
+            self.context,
+            field,
+            format_link(self.context.owner),
+            header="Change owner",
+            step_title="Select a new owner",
+        )
 
     @property
     def build_frequency(self):
         if self.context.auto_build:
-            return 'Built automatically'
+            return "Built automatically"
         else:
-            return 'Built on request'
+            return "Built on request"
 
     @property
     def sorted_auto_build_channels_items(self):
@@ -343,7 +342,7 @@ class SnapView(LaunchpadView):
 
     @property
     def store_channels(self):
-        return ', '.join(self.context.store_channels)
+        return ", ".join(self.context.store_channels)
 
     @property
     def user_can_see_source(self):
@@ -380,19 +379,24 @@ def builds_and_requests_for_snap(snap):
 
     items = sorted(
         list(snap.pending_builds) + list(snap.pending_build_requests),
-        key=make_sort_key("date_created", "date_requested"))
+        key=make_sort_key("date_created", "date_requested"),
+    )
     if len(items) < 10:
         # We need to interleave two unbounded result sets, but we only need
         # enough items from them to make the total count up to 10.  It's
         # simplest to just fetch the upper bound from each set and do our
         # own sorting.
         recent_items = sorted(
-            list(snap.completed_builds[:10 - len(items)]) +
-            list(snap.failed_build_requests[:10 - len(items)]),
+            list(snap.completed_builds[: 10 - len(items)])
+            + list(snap.failed_build_requests[: 10 - len(items)]),
             key=make_sort_key(
-                "date_finished", "date_started",
-                "date_created", "date_requested"))
-        items.extend(recent_items[:10 - len(items)])
+                "date_finished",
+                "date_started",
+                "date_created",
+                "date_requested",
+            ),
+        )
+        items.extend(recent_items[: 10 - len(items)])
     return items
 
 
@@ -401,26 +405,33 @@ class SnapRequestBuildsView(LaunchpadFormView):
 
     @property
     def label(self):
-        return 'Request builds for %s' % self.context.name
+        return "Request builds for %s" % self.context.name
 
-    page_title = 'Request builds'
+    page_title = "Request builds"
 
     class schema(Interface):
         """Schema for requesting a build."""
 
-        archive = Reference(IArchive, title='Source archive', required=True)
+        archive = Reference(IArchive, title="Source archive", required=True)
         distro_arch_series = List(
-            Choice(vocabulary='SnapDistroArchSeries'),
-            title='Architectures', required=True,
+            Choice(vocabulary="SnapDistroArchSeries"),
+            title="Architectures",
+            required=True,
             description=(
-                'If you do not explicitly select any architectures, then the '
-                'snap package will be built for all architectures allowed by '
-                'its configuration.'))
+                "If you do not explicitly select any architectures, then the "
+                "snap package will be built for all architectures allowed by "
+                "its configuration."
+            ),
+        )
         pocket = copy_field(
-            ISnapBuild['pocket'], title='Pocket', readonly=False)
+            ISnapBuild["pocket"], title="Pocket", readonly=False
+        )
         channels = Dict(
-            title='Source snap channels', key_type=TextLine(), required=True,
-            description=ISnap['auto_build_channels'].description)
+            title="Source snap channels",
+            key_type=TextLine(),
+            required=True,
+            description=ISnap["auto_build_channels"].description,
+        )
 
     custom_widget_archive = SnapArchiveWidget
     custom_widget_distro_arch_series = LabeledMultiCheckBoxWidget
@@ -429,7 +440,7 @@ class SnapRequestBuildsView(LaunchpadFormView):
 
     help_links = {
         "pocket": "/+help-snappy/snap-build-pocket.html",
-        }
+    }
 
     @property
     def cancel_url(self):
@@ -439,68 +450,78 @@ class SnapRequestBuildsView(LaunchpadFormView):
     def initial_values(self):
         """See `LaunchpadFormView`."""
         return {
-            'archive': (
+            "archive": (
                 # XXX cjwatson 2019-02-04: In order to support non-Ubuntu
                 # bases, we'd need to store this as None and infer it based
                 # on the inferred distro series; but this will do for now.
                 getUtility(ILaunchpadCelebrities).ubuntu.main_archive
                 if self.context.distro_series is None
-                else self.context.distro_series.main_archive),
-            'distro_arch_series': [],
-            'pocket': PackagePublishingPocket.UPDATES,
-            'channels': self.context.auto_build_channels,
-            }
+                else self.context.distro_series.main_archive
+            ),
+            "distro_arch_series": [],
+            "pocket": PackagePublishingPocket.UPDATES,
+            "channels": self.context.auto_build_channels,
+        }
 
-    @action('Request builds', name='request')
+    @action("Request builds", name="request")
     def request_action(self, action, data):
-        if data.get('distro_arch_series', []):
+        if data.get("distro_arch_series", []):
             architectures = [
-                arch.architecturetag for arch in data['distro_arch_series']]
+                arch.architecturetag for arch in data["distro_arch_series"]
+            ]
         else:
             architectures = None
         self.context.requestBuilds(
-            self.user, data['archive'], data['pocket'],
-            architectures=architectures, channels=data['channels'])
+            self.user,
+            data["archive"],
+            data["pocket"],
+            architectures=architectures,
+            channels=data["channels"],
+        )
         self.request.response.addNotification(
-            _('Builds will be dispatched soon.'))
+            _("Builds will be dispatched soon.")
+        )
         self.next_url = self.cancel_url
 
 
 class ISnapEditSchema(Interface):
     """Schema for adding or editing a snap package."""
 
-    use_template(ISnap, include=[
-        'owner',
-        'name',
-        'information_type',
-        'project',
-        'require_virtualized',
-        'allow_internet',
-        'build_source_tarball',
-        'auto_build',
-        'auto_build_channels',
-        'store_upload',
-        ])
+    use_template(
+        ISnap,
+        include=[
+            "owner",
+            "name",
+            "information_type",
+            "project",
+            "require_virtualized",
+            "allow_internet",
+            "build_source_tarball",
+            "auto_build",
+            "auto_build_channels",
+            "store_upload",
+        ],
+    )
 
     store_distro_series = Choice(
-        vocabulary='SnappyDistroSeries', required=True,
-        title='Series')
-    vcs = Choice(vocabulary=VCSType, required=True, title='VCS')
+        vocabulary="SnappyDistroSeries", required=True, title="Series"
+    )
+    vcs = Choice(vocabulary=VCSType, required=True, title="VCS")
 
     # Each of these is only required if vcs has an appropriate value.  Later
     # validation takes care of adjusting the required attribute.
-    branch = copy_field(ISnap['branch'], required=True)
-    git_ref = copy_field(ISnap['git_ref'], required=True)
+    branch = copy_field(ISnap["branch"], required=True)
+    git_ref = copy_field(ISnap["git_ref"], required=True)
 
     # These are only required if auto_build is True.  Later validation takes
     # care of adjusting the required attribute.
-    auto_build_archive = copy_field(ISnap['auto_build_archive'], required=True)
-    auto_build_pocket = copy_field(ISnap['auto_build_pocket'], required=True)
+    auto_build_archive = copy_field(ISnap["auto_build_archive"], required=True)
+    auto_build_pocket = copy_field(ISnap["auto_build_pocket"], required=True)
 
     # This is only required if store_upload is True.  Later validation takes
     # care of adjusting the required attribute.
-    store_name = copy_field(ISnap['store_name'], required=True)
-    store_channels = copy_field(ISnap['store_channels'], required=True)
+    store_name = copy_field(ISnap["store_name"], required=True)
+    store_channels = copy_field(ISnap["store_channels"], required=True)
 
 
 def log_oops(error, request):
@@ -510,29 +531,36 @@ def log_oops(error, request):
 
 
 class SnapAuthorizeMixin:
-
     def requestAuthorization(self, snap):
         try:
             self.next_url = SnapAuthorizeView.requestAuthorization(
-                snap, self.request)
+                snap, self.request
+            )
         except BadRequestPackageUploadResponse as e:
             self.setFieldError(
-                'store_upload',
-                'Cannot get permission from the store to upload this package.')
+                "store_upload",
+                "Cannot get permission from the store to upload this package.",
+            )
             log_oops(e, self.request)
 
 
-class SnapAddView(SnapAuthorizeMixin, EnableProcessorsMixin,
-                  SnapInformationTypeMixin, SnapFormMixin, LaunchpadFormView):
+class SnapAddView(
+    SnapAuthorizeMixin,
+    EnableProcessorsMixin,
+    SnapInformationTypeMixin,
+    SnapFormMixin,
+    LaunchpadFormView,
+):
     """View for creating snap packages."""
 
-    page_title = label = 'Create a new snap package'
+    page_title = label = "Create a new snap package"
 
     schema = ISnapEditSchema
 
     custom_widget_vcs = LaunchpadRadioWidget
     custom_widget_git_ref = CustomWidgetFactory(
-        GitRefWidget, allow_external=True)
+        GitRefWidget, allow_external=True
+    )
     custom_widget_store_distro_series = LaunchpadRadioWidget
     custom_widget_auto_build_archive = SnapArchiveWidget
     custom_widget_auto_build_pocket = LaunchpadDropdownWidget
@@ -541,27 +569,27 @@ class SnapAddView(SnapAuthorizeMixin, EnableProcessorsMixin,
 
     help_links = {
         "auto_build_pocket": "/+help-snappy/snap-build-pocket.html",
-        }
+    }
 
     @property
     def field_names(self):
-        fields = ['owner', 'name']
+        fields = ["owner", "name"]
         if self.is_project_context:
-            fields += ['vcs', 'branch', 'git_ref']
+            fields += ["vcs", "branch", "git_ref"]
         else:
-            fields += ['project']
+            fields += ["project"]
         return fields + [
-            'information_type',
-            'store_distro_series',
-            'build_source_tarball',
-            'auto_build',
-            'auto_build_archive',
-            'auto_build_pocket',
-            'auto_build_channels',
-            'store_upload',
-            'store_name',
-            'store_channels',
-            ]
+            "information_type",
+            "store_distro_series",
+            "build_source_tarball",
+            "auto_build",
+            "auto_build_archive",
+            "auto_build_pocket",
+            "auto_build_channels",
+            "store_upload",
+            "store_name",
+            "store_channels",
+        ]
 
     def initialize(self):
         """See `LaunchpadView`."""
@@ -570,8 +598,10 @@ class SnapAddView(SnapAuthorizeMixin, EnableProcessorsMixin,
         # Once initialized, if the private_snap flag is disabled, it
         # prevents snap creation for private contexts.
         if not getFeatureFlag(SNAP_PRIVATE_FEATURE_FLAG):
-            if (IInformationType.providedBy(self.context) and
-                self.context.information_type in PRIVATE_INFORMATION_TYPES):
+            if (
+                IInformationType.providedBy(self.context)
+                and self.context.information_type in PRIVATE_INFORMATION_TYPES
+            ):
                 raise SnapPrivateFeatureDisabled
 
     @property
@@ -585,19 +615,21 @@ class SnapAddView(SnapAuthorizeMixin, EnableProcessorsMixin,
             getUtility(IProcessorSet).getAll(),
             "The architectures that this snap package builds for. Some "
             "architectures are restricted and may only be enabled or "
-            "disabled by administrators.")
+            "disabled by administrators.",
+        )
 
     def setUpWidgets(self):
         """See `LaunchpadFormView`."""
         super().setUpWidgets()
-        self.widgets['processors'].widget_class = 'processors'
+        self.widgets["processors"].widget_class = "processors"
         if self.is_project_context:
             # If we are on Project:+new-snap page, we know which information
             # types the project supports. Let's filter out the ones that are
             # not supported.
             types = getUtility(ISnapSet).getPossibleSnapInformationTypes(
-                    self.context)
-            info_type_widget = self.widgets['information_type']
+                self.context
+            )
+            info_type_widget = self.widgets["information_type"]
             info_type_widget.vocabulary = InformationTypeVocabulary(types)
             self.setUpVCSWidgets()
 
@@ -612,12 +644,16 @@ class SnapAddView(SnapAuthorizeMixin, EnableProcessorsMixin,
             # Try to extract Snap store name from snapcraft.yaml file.
             try:
                 snapcraft_data = getUtility(ISnapSet).getSnapcraftYaml(
-                    self.context, logger=log)
-            except (MissingSnapcraftYaml, CannotFetchSnapcraftYaml,
-                    CannotParseSnapcraftYaml):
+                    self.context, logger=log
+                )
+            except (
+                MissingSnapcraftYaml,
+                CannotFetchSnapcraftYaml,
+                CannotParseSnapcraftYaml,
+            ):
                 pass
             else:
-                store_name = snapcraft_data.get('name')
+                store_name = snapcraft_data.get("name")
 
         store_series = getUtility(ISnappySeriesSet).getAll().first()
         if store_series.can_infer_distro_series:
@@ -628,24 +664,28 @@ class SnapAddView(SnapAuthorizeMixin, EnableProcessorsMixin,
             distro_series = store_series.usable_distro_series.first()
         sds_set = getUtility(ISnappyDistroSeriesSet)
         store_distro_series = sds_set.getByBothSeries(
-            store_series, distro_series)
+            store_series, distro_series
+        )
 
         return {
-            'store_name': store_name,
-            'owner': self.user,
-            'store_distro_series': store_distro_series,
-            'processors': [
-                p for p in getUtility(IProcessorSet).getAll()
-                if p.build_by_default],
-            'auto_build_archive': (
+            "store_name": store_name,
+            "owner": self.user,
+            "store_distro_series": store_distro_series,
+            "processors": [
+                p
+                for p in getUtility(IProcessorSet).getAll()
+                if p.build_by_default
+            ],
+            "auto_build_archive": (
                 # XXX cjwatson 2019-02-04: In order to support non-Ubuntu
                 # bases, we'd need to store this as None and infer it based
                 # on the inferred distro series; but this will do for now.
                 getUtility(ILaunchpadCelebrities).ubuntu.main_archive
                 if distro_series is None
-                else distro_series.main_archive),
-            'auto_build_pocket': PackagePublishingPocket.UPDATES,
-            }
+                else distro_series.main_archive
+            ),
+            "auto_build_pocket": PackagePublishingPocket.UPDATES,
+        }
 
     @property
     def has_snappy_distro_series(self):
@@ -653,76 +693,85 @@ class SnapAddView(SnapAuthorizeMixin, EnableProcessorsMixin,
 
     def validate_widgets(self, data, names=None):
         """See `LaunchpadFormView`."""
-        if self.widgets.get('vcs') is not None:
-            super().validate_widgets(data, ['vcs'])
+        if self.widgets.get("vcs") is not None:
+            super().validate_widgets(data, ["vcs"])
             self.validateVCSWidgets(SnapAddView, data)
-        if self.widgets.get('auto_build') is not None:
+        if self.widgets.get("auto_build") is not None:
             # Set widgets as required or optional depending on the
             # auto_build field.
-            super().validate_widgets(data, ['auto_build'])
-            auto_build = data.get('auto_build', False)
-            self.widgets['auto_build_archive'].context.required = auto_build
-            self.widgets['auto_build_pocket'].context.required = auto_build
-        if self.widgets.get('store_upload') is not None:
+            super().validate_widgets(data, ["auto_build"])
+            auto_build = data.get("auto_build", False)
+            self.widgets["auto_build_archive"].context.required = auto_build
+            self.widgets["auto_build_pocket"].context.required = auto_build
+        if self.widgets.get("store_upload") is not None:
             # Set widgets as required or optional depending on the
             # store_upload field.
-            super().validate_widgets(data, ['store_upload'])
-            store_upload = data.get('store_upload', False)
-            self.widgets['store_name'].context.required = store_upload
-            self.widgets['store_channels'].context.required = store_upload
+            super().validate_widgets(data, ["store_upload"])
+            store_upload = data.get("store_upload", False)
+            self.widgets["store_name"].context.required = store_upload
+            self.widgets["store_channels"].context.required = store_upload
         super().validate_widgets(data, names=names)
 
-    @action('Create snap package', name='create')
+    @action("Create snap package", name="create")
     def create_action(self, action, data):
         if IGitRef.providedBy(self.context):
-            kwargs = {'git_ref': self.context, 'project': data['project']}
+            kwargs = {"git_ref": self.context, "project": data["project"]}
         elif IBranch.providedBy(self.context):
-            kwargs = {'branch': self.context, 'project': data['project']}
+            kwargs = {"branch": self.context, "project": data["project"]}
         elif self.is_project_context:
-            if data['vcs'] == VCSType.GIT:
-                kwargs = {'git_ref': data['git_ref']}
+            if data["vcs"] == VCSType.GIT:
+                kwargs = {"git_ref": data["git_ref"]}
             else:
-                kwargs = {'branch': data['branch']}
-            kwargs['project'] = self.context
+                kwargs = {"branch": data["branch"]}
+            kwargs["project"] = self.context
         else:
             raise NotImplementedError("Unknown context for snap creation.")
-        if not data.get('auto_build', False):
-            data['auto_build_archive'] = None
-            data['auto_build_pocket'] = None
+        if not data.get("auto_build", False):
+            data["auto_build_archive"] = None
+            data["auto_build_pocket"] = None
         snap = getUtility(ISnapSet).new(
-            self.user, data['owner'],
-            data['store_distro_series'].distro_series, data['name'],
-            auto_build=data['auto_build'],
-            auto_build_archive=data['auto_build_archive'],
-            auto_build_pocket=data['auto_build_pocket'],
-            auto_build_channels=data['auto_build_channels'],
-            information_type=data['information_type'],
-            processors=data['processors'],
-            build_source_tarball=data['build_source_tarball'],
-            store_upload=data['store_upload'],
-            store_series=data['store_distro_series'].snappy_series,
-            store_name=data['store_name'],
-            store_channels=data.get('store_channels'), **kwargs)
-        if data['store_upload']:
+            self.user,
+            data["owner"],
+            data["store_distro_series"].distro_series,
+            data["name"],
+            auto_build=data["auto_build"],
+            auto_build_archive=data["auto_build_archive"],
+            auto_build_pocket=data["auto_build_pocket"],
+            auto_build_channels=data["auto_build_channels"],
+            information_type=data["information_type"],
+            processors=data["processors"],
+            build_source_tarball=data["build_source_tarball"],
+            store_upload=data["store_upload"],
+            store_series=data["store_distro_series"].snappy_series,
+            store_name=data["store_name"],
+            store_channels=data.get("store_channels"),
+            **kwargs,
+        )
+        if data["store_upload"]:
             self.requestAuthorization(snap)
         else:
             self.next_url = canonical_url(snap)
 
     def validate(self, data):
         super().validate(data)
-        owner = data.get('owner', None)
-        name = data.get('name', None)
+        owner = data.get("owner", None)
+        name = data.get("name", None)
         if owner and name:
             if getUtility(ISnapSet).exists(owner, name):
                 self.setFieldError(
-                    'name',
-                    'There is already a snap package owned by %s with this '
-                    'name.' % owner.displayname)
+                    "name",
+                    "There is already a snap package owned by %s with this "
+                    "name." % owner.displayname,
+                )
         self.validateInformationType(data)
 
 
-class BaseSnapEditView(SnapAuthorizeMixin, SnapInformationTypeMixin,
-                       SnapFormMixin, LaunchpadEditFormView):
+class BaseSnapEditView(
+    SnapAuthorizeMixin,
+    SnapInformationTypeMixin,
+    SnapFormMixin,
+    LaunchpadEditFormView,
+):
 
     schema = ISnapEditSchema
 
@@ -741,59 +790,65 @@ class BaseSnapEditView(SnapAuthorizeMixin, SnapInformationTypeMixin,
 
     def validate_widgets(self, data, names=None):
         """See `LaunchpadFormView`."""
-        if self.widgets.get('vcs') is not None:
-            super().validate_widgets(data, ['vcs'])
+        if self.widgets.get("vcs") is not None:
+            super().validate_widgets(data, ["vcs"])
             self.validateVCSWidgets(BaseSnapEditView, data)
-        if self.widgets.get('auto_build') is not None:
+        if self.widgets.get("auto_build") is not None:
             # Set widgets as required or optional depending on the
             # auto_build field.
-            super().validate_widgets(data, ['auto_build'])
-            auto_build = data.get('auto_build', False)
-            self.widgets['auto_build_archive'].context.required = auto_build
-            self.widgets['auto_build_pocket'].context.required = auto_build
-        if self.widgets.get('store_upload') is not None:
+            super().validate_widgets(data, ["auto_build"])
+            auto_build = data.get("auto_build", False)
+            self.widgets["auto_build_archive"].context.required = auto_build
+            self.widgets["auto_build_pocket"].context.required = auto_build
+        if self.widgets.get("store_upload") is not None:
             # Set widgets as required or optional depending on the
             # store_upload field.
-            super().validate_widgets(data, ['store_upload'])
-            store_upload = data.get('store_upload', False)
-            self.widgets['store_name'].context.required = store_upload
-            self.widgets['store_channels'].context.required = store_upload
+            super().validate_widgets(data, ["store_upload"])
+            store_upload = data.get("store_upload", False)
+            self.widgets["store_name"].context.required = store_upload
+            self.widgets["store_channels"].context.required = store_upload
         super().validate_widgets(data, names=names)
 
     def validate(self, data):
         super().validate(data)
-        info_type = data.get('information_type', self.context.information_type)
-        editing_info_type = 'information_type' in data
+        info_type = data.get("information_type", self.context.information_type)
+        editing_info_type = "information_type" in data
         private = info_type in PRIVATE_INFORMATION_TYPES
         if private is False:
             # These are the requirements for public snaps.
-            if 'information_type' in data or 'owner' in data:
-                owner = data.get('owner', self.context.owner)
+            if "information_type" in data or "owner" in data:
+                owner = data.get("owner", self.context.owner)
                 if owner is not None and owner.private:
                     self.setFieldError(
-                        'information_type' if editing_info_type else 'owner',
-                        'A public snap cannot have a private owner.')
-            if 'information_type' in data or 'branch' in data:
-                branch = data.get('branch', self.context.branch)
+                        "information_type" if editing_info_type else "owner",
+                        "A public snap cannot have a private owner.",
+                    )
+            if "information_type" in data or "branch" in data:
+                branch = data.get("branch", self.context.branch)
                 if branch is not None and branch.private:
                     self.setFieldError(
-                        'information_type' if editing_info_type else 'branch',
-                        'A public snap cannot have a private branch.')
-            if 'information_type' in data or 'git_ref' in data:
-                ref = data.get('git_ref', self.context.git_ref)
+                        "information_type" if editing_info_type else "branch",
+                        "A public snap cannot have a private branch.",
+                    )
+            if "information_type" in data or "git_ref" in data:
+                ref = data.get("git_ref", self.context.git_ref)
                 if ref is not None and ref.private:
                     self.setFieldError(
-                        'information_type' if editing_info_type else 'git_ref',
-                        'A public snap cannot have a private repository.')
+                        "information_type" if editing_info_type else "git_ref",
+                        "A public snap cannot have a private repository.",
+                    )
         self.validateInformationType(data, snap=self.context)
 
     def _needStoreReauth(self, data):
         """Does this change require reauthorizing to the store?"""
-        store_upload = data.get('store_upload', False)
-        store_distro_series = data.get('store_distro_series')
-        store_name = data.get('store_name')
-        if (not store_upload or
-                store_distro_series is None or store_name is None):
+        store_upload = data.get("store_upload", False)
+        store_distro_series = data.get("store_distro_series")
+        store_name = data.get("store_name")
+        if (
+            not store_upload
+            or store_distro_series is None
+            or store_name is None
+        ):
             return False
         if not self.context.store_upload:
             return True
@@ -803,37 +858,38 @@ class BaseSnapEditView(SnapAuthorizeMixin, SnapInformationTypeMixin,
             return True
         return False
 
-    @action('Update snap package', name='update')
+    @action("Update snap package", name="update")
     def request_action(self, action, data):
-        vcs = data.pop('vcs', None)
+        vcs = data.pop("vcs", None)
         if vcs == VCSType.BZR:
-            data['git_ref'] = None
+            data["git_ref"] = None
         elif vcs == VCSType.GIT:
-            data['branch'] = None
-        new_processors = data.get('processors')
+            data["branch"] = None
+        new_processors = data.get("processors")
         if new_processors is not None:
             if set(self.context.processors) != set(new_processors):
                 self.context.setProcessors(
-                    new_processors, check_permissions=True, user=self.user)
-            del data['processors']
-        if not data.get('auto_build', False):
-            if 'auto_build_archive' in data:
-                del data['auto_build_archive']
-            if 'auto_build_pocket' in data:
-                del data['auto_build_pocket']
-            if 'auto_build_channels' in data:
-                del data['auto_build_channels']
-        store_upload = data.get('store_upload', False)
+                    new_processors, check_permissions=True, user=self.user
+                )
+            del data["processors"]
+        if not data.get("auto_build", False):
+            if "auto_build_archive" in data:
+                del data["auto_build_archive"]
+            if "auto_build_pocket" in data:
+                del data["auto_build_pocket"]
+            if "auto_build_channels" in data:
+                del data["auto_build_channels"]
+        store_upload = data.get("store_upload", False)
         if not store_upload:
-            if 'store_name' in data:
-                del data['store_name']
-            if 'store_channels' in data:
-                del data['store_channels']
+            if "store_name" in data:
+                del data["store_name"]
+            if "store_channels" in data:
+                del data["store_channels"]
         need_store_reauth = self._needStoreReauth(data)
-        info_type = data.get('information_type')
+        info_type = data.get("information_type")
         if info_type and info_type != self.context.information_type:
             self.context.information_type = info_type
-            del data['information_type']
+            del data["information_type"]
         self.updateContextFromData(data)
         if need_store_reauth:
             self.requestAuthorization(self.context)
@@ -851,15 +907,19 @@ class SnapAdminView(BaseSnapEditView):
 
     @property
     def label(self):
-        return 'Administer %s snap package' % self.context.name
+        return "Administer %s snap package" % self.context.name
 
-    page_title = 'Administer'
+    page_title = "Administer"
 
     # XXX pappacena 2021-02-19: Once we have the whole privacy work in
     # place, we should move "project" and "information_type" from +admin
     # page to +edit, to allow common users to edit this.
     field_names = [
-        'project', 'information_type', 'require_virtualized', 'allow_internet']
+        "project",
+        "information_type",
+        "require_virtualized",
+        "allow_internet",
+    ]
 
     @property
     def initial_values(self):
@@ -868,11 +928,11 @@ class SnapAdminView(BaseSnapEditView):
         # database column, it will be NULL, but snap.information_type
         # property has a fallback to check "private" property. This should
         # be removed once we back fill snap.information_type.
-        return {'information_type': self.context.information_type}
+        return {"information_type": self.context.information_type}
 
     def updateContextFromData(self, data, context=None, notify_modified=True):
-        if 'project' in data:
-            project = data.pop('project')
+        if "project" in data:
+            project = data.pop("project")
             self.context.setProject(project)
         super().updateContextFromData(data, context, notify_modified)
 
@@ -882,32 +942,33 @@ class SnapEditView(BaseSnapEditView, EnableProcessorsMixin):
 
     @property
     def label(self):
-        return 'Edit %s snap package' % self.context.name
+        return "Edit %s snap package" % self.context.name
 
-    page_title = 'Edit'
+    page_title = "Edit"
 
     field_names = [
-        'owner',
-        'name',
-        'project',
-        'information_type',
-        'store_distro_series',
-        'vcs',
-        'branch',
-        'git_ref',
-        'build_source_tarball',
-        'auto_build',
-        'auto_build_archive',
-        'auto_build_pocket',
-        'auto_build_channels',
-        'store_upload',
-        'store_name',
-        'store_channels',
-        ]
+        "owner",
+        "name",
+        "project",
+        "information_type",
+        "store_distro_series",
+        "vcs",
+        "branch",
+        "git_ref",
+        "build_source_tarball",
+        "auto_build",
+        "auto_build_archive",
+        "auto_build_pocket",
+        "auto_build_channels",
+        "store_upload",
+        "store_name",
+        "store_channels",
+    ]
     custom_widget_store_distro_series = LaunchpadRadioWidget
     custom_widget_vcs = LaunchpadRadioWidget
     custom_widget_git_ref = CustomWidgetFactory(
-        GitRefWidget, allow_external=True)
+        GitRefWidget, allow_external=True
+    )
     custom_widget_auto_build_archive = SnapArchiveWidget
     custom_widget_auto_build_pocket = LaunchpadDropdownWidget
     custom_widget_auto_build_channels = SnapBuildChannelsWidget
@@ -915,11 +976,12 @@ class SnapEditView(BaseSnapEditView, EnableProcessorsMixin):
     # See `setUpWidgets` method.
     custom_widget_information_type = CustomWidgetFactory(
         LaunchpadRadioWidgetWithDescription,
-        vocabulary=InformationTypeVocabulary(types=[]))
+        vocabulary=InformationTypeVocabulary(types=[]),
+    )
 
     help_links = {
         "auto_build_pocket": "/+help-snappy/snap-build-pocket.html",
-        }
+    }
 
     def setUpFields(self):
         """See `LaunchpadFormView`."""
@@ -928,62 +990,66 @@ class SnapEditView(BaseSnapEditView, EnableProcessorsMixin):
             self.context.available_processors,
             "The architectures that this snap package builds for. Some "
             "architectures are restricted and may only be enabled or "
-            "disabled by administrators.")
+            "disabled by administrators.",
+        )
 
     def setUpWidgets(self, context=None):
         super().setUpWidgets(context)
-        info_type_widget = self.widgets['information_type']
+        info_type_widget = self.widgets["information_type"]
         info_type_widget.vocabulary = InformationTypeVocabulary(
-            types=self.getPossibleInformationTypes(self.context, self.user))
+            types=self.getPossibleInformationTypes(self.context, self.user)
+        )
 
     @property
     def initial_values(self):
         initial_values = {}
         if self.context.git_ref is not None:
-            initial_values['vcs'] = VCSType.GIT
+            initial_values["vcs"] = VCSType.GIT
         else:
-            initial_values['vcs'] = VCSType.BZR
+            initial_values["vcs"] = VCSType.BZR
         if self.context.auto_build_pocket is None:
-            initial_values['auto_build_pocket'] = (
-                PackagePublishingPocket.UPDATES)
+            initial_values[
+                "auto_build_pocket"
+            ] = PackagePublishingPocket.UPDATES
         # XXX pappacena 2021-02-12: Until we back fill information_type
         # database column, it will be NULL, but snap.information_type
         # property has a fallback to check "private" property. This should
         # be removed once we back fill snap.information_type.
-        initial_values['information_type'] = self.context.information_type
+        initial_values["information_type"] = self.context.information_type
         return initial_values
 
     def validate(self, data):
         super().validate(data)
-        owner = data.get('owner', None)
-        name = data.get('name', None)
+        owner = data.get("owner", None)
+        name = data.get("name", None)
         if owner and name:
             try:
                 snap = getUtility(ISnapSet).getByName(owner, name)
                 if snap != self.context:
                     self.setFieldError(
-                        'name',
-                        'There is already a snap package owned by %s with '
-                        'this name.' % owner.displayname)
+                        "name",
+                        "There is already a snap package owned by %s with "
+                        "this name." % owner.displayname,
+                    )
             except NoSuchSnap:
                 pass
-        if 'processors' in data:
+        if "processors" in data:
             available_processors = set(self.context.available_processors)
-            widget = self.widgets['processors']
+            widget = self.widgets["processors"]
             for processor in self.context.processors:
-                if processor not in data['processors']:
+                if processor not in data["processors"]:
                     if processor not in available_processors:
                         # This processor is not currently available for
                         # selection, but is enabled.  Leave it untouched.
-                        data['processors'].append(processor)
+                        data["processors"].append(processor)
                     elif processor.name in widget.disabled_items:
                         # This processor is restricted and currently
                         # enabled. Leave it untouched.
-                        data['processors'].append(processor)
+                        data["processors"].append(processor)
 
     def updateContextFromData(self, data, context=None, notify_modified=True):
-        if 'project' in data:
-            project = data.pop('project')
+        if "project" in data:
+            project = data.pop("project")
             self.context.setProject(project)
         super().updateContextFromData(data, context, notify_modified)
 
@@ -993,15 +1059,16 @@ class SnapAuthorizeView(LaunchpadEditFormView):
 
     @property
     def label(self):
-        return 'Authorize store uploads of %s' % self.context.name
+        return "Authorize store uploads of %s" % self.context.name
 
-    page_title = 'Authorize store uploads'
+    page_title = "Authorize store uploads"
 
     class schema(Interface):
         """Schema for authorizing snap package uploads to the store."""
 
         discharge_macaroon = TextLine(
-            title='Serialized discharge macaroon', required=True)
+            title="Serialized discharge macaroon", required=True
+        )
 
     render_context = False
 
@@ -1016,37 +1083,46 @@ class SnapAuthorizeView(LaunchpadEditFormView):
         """Begin the process of authorizing uploads of a snap package."""
         try:
             sso_caveat_id = snap.beginAuthorization()
-            base_url = canonical_url(snap, view_name='+authorize')
-            login_url = urlappend(base_url, '+login')
-            login_url += '?%s' % urlencode([
-                ('macaroon_caveat_id', sso_caveat_id),
-                ('discharge_macaroon_action', 'field.actions.complete'),
-                ('discharge_macaroon_field', 'field.discharge_macaroon'),
-                ])
+            base_url = canonical_url(snap, view_name="+authorize")
+            login_url = urlappend(base_url, "+login")
+            login_url += "?%s" % urlencode(
+                [
+                    ("macaroon_caveat_id", sso_caveat_id),
+                    ("discharge_macaroon_action", "field.actions.complete"),
+                    ("discharge_macaroon_field", "field.discharge_macaroon"),
+                ]
+            )
             return login_url
         except CannotAuthorizeStoreUploads as e:
             request.response.addInfoNotification(str(e))
             request.response.redirect(canonical_url(snap))
             return
 
-    @action('Begin authorization', name='begin')
+    @action("Begin authorization", name="begin")
     def begin_action(self, action, data):
         login_url = self.requestAuthorization(self.context, self.request)
         if login_url is not None:
             self.request.response.redirect(login_url)
 
-    @action('Complete authorization', name='complete')
+    @action("Complete authorization", name="complete")
     def complete_action(self, action, data):
-        if not data.get('discharge_macaroon'):
-            self.addError(structured(
-                _('Uploads of %(snap)s to the store were not authorized.'),
-                snap=self.context.name))
+        if not data.get("discharge_macaroon"):
+            self.addError(
+                structured(
+                    _("Uploads of %(snap)s to the store were not authorized."),
+                    snap=self.context.name,
+                )
+            )
             return
         self.context.completeAuthorization(
-            discharge_macaroon=data['discharge_macaroon'])
-        self.request.response.addInfoNotification(structured(
-            _('Uploads of %(snap)s to the store are now authorized.'),
-            snap=self.context.name))
+            discharge_macaroon=data["discharge_macaroon"]
+        )
+        self.request.response.addInfoNotification(
+            structured(
+                _("Uploads of %(snap)s to the store are now authorized."),
+                snap=self.context.name,
+            )
+        )
         self.request.response.redirect(canonical_url(self.context))
 
     @property
@@ -1060,14 +1136,14 @@ class SnapDeleteView(BaseSnapEditView):
 
     @property
     def label(self):
-        return 'Delete %s snap package' % self.context.name
+        return "Delete %s snap package" % self.context.name
 
-    page_title = 'Delete'
+    page_title = "Delete"
 
     field_names = []
 
-    @action('Delete snap package', name='delete')
+    @action("Delete snap package", name="delete")
     def delete_action(self, action, data):
         owner = self.context.owner
         self.context.destroySelf()
-        self.next_url = canonical_url(owner, view_name='+snaps')
+        self.next_url = canonical_url(owner, view_name="+snaps")
diff --git a/lib/lp/snappy/browser/snapbase.py b/lib/lp/snappy/browser/snapbase.py
index 3bd070a..719bb26 100644
--- a/lib/lp/snappy/browser/snapbase.py
+++ b/lib/lp/snappy/browser/snapbase.py
@@ -6,20 +6,13 @@
 __all__ = [
     "SnapBaseNavigation",
     "SnapBaseSetNavigation",
-    ]
+]
 
 from zope.component import getUtility
 
 from lp.services.database.sqlobject import SQLObjectNotFound
-from lp.services.webapp import (
-    GetitemNavigation,
-    Navigation,
-    stepthrough,
-    )
-from lp.snappy.interfaces.snapbase import (
-    ISnapBase,
-    ISnapBaseSet,
-    )
+from lp.services.webapp import GetitemNavigation, Navigation, stepthrough
+from lp.snappy.interfaces.snapbase import ISnapBase, ISnapBaseSet
 from lp.soyuz.interfaces.archive import IArchiveSet
 
 
@@ -52,4 +45,5 @@ class SnapBaseNavigation(Navigation):
 
 class SnapBaseSetNavigation(GetitemNavigation):
     """Navigation methods for `ISnapBaseSet`."""
+
     usedfor = ISnapBaseSet
diff --git a/lib/lp/snappy/browser/snapbuild.py b/lib/lp/snappy/browser/snapbuild.py
index ad54692..405000d 100644
--- a/lib/lp/snappy/browser/snapbuild.py
+++ b/lib/lp/snappy/browser/snapbuild.py
@@ -4,33 +4,30 @@
 """SnapBuild views."""
 
 __all__ = [
-    'SnapBuildContextMenu',
-    'SnapBuildNavigation',
-    'SnapBuildView',
-    ]
+    "SnapBuildContextMenu",
+    "SnapBuildNavigation",
+    "SnapBuildView",
+]
 
 from zope.interface import Interface
 
-from lp.app.browser.launchpadform import (
-    action,
-    LaunchpadFormView,
-    )
+from lp.app.browser.launchpadform import LaunchpadFormView, action
 from lp.services.librarian.browser import (
     FileNavigationMixin,
     ProxiedLibraryFileAlias,
-    )
+)
 from lp.services.propertycache import cachedproperty
 from lp.services.webapp import (
-    canonical_url,
     ContextMenu,
-    enabled_with_permission,
     Link,
     Navigation,
-    )
+    canonical_url,
+    enabled_with_permission,
+)
 from lp.snappy.interfaces.snapbuild import (
     CannotScheduleStoreUpload,
     ISnapBuild,
-    )
+)
 from lp.soyuz.interfaces.binarypackagebuild import IBuildRescoreForm
 
 
@@ -43,27 +40,36 @@ class SnapBuildContextMenu(ContextMenu):
 
     usedfor = ISnapBuild
 
-    facet = 'overview'
+    facet = "overview"
 
-    links = ('retry', 'cancel', 'rescore')
+    links = ("retry", "cancel", "rescore")
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def retry(self):
         return Link(
-            '+retry', 'Retry this build', icon='retry',
-            enabled=self.context.can_be_retried)
+            "+retry",
+            "Retry this build",
+            icon="retry",
+            enabled=self.context.can_be_retried,
+        )
 
-    @enabled_with_permission('launchpad.Edit')
+    @enabled_with_permission("launchpad.Edit")
     def cancel(self):
         return Link(
-            '+cancel', 'Cancel build', icon='remove',
-            enabled=self.context.can_be_cancelled)
+            "+cancel",
+            "Cancel build",
+            icon="remove",
+            enabled=self.context.can_be_cancelled,
+        )
 
-    @enabled_with_permission('launchpad.Admin')
+    @enabled_with_permission("launchpad.Admin")
     def rescore(self):
         return Link(
-            '+rescore', 'Rescore build', icon='edit',
-            enabled=self.context.can_be_rescored)
+            "+rescore",
+            "Rescore build",
+            icon="edit",
+            enabled=self.context.can_be_rescored,
+        )
 
 
 class SnapBuildView(LaunchpadFormView):
@@ -86,7 +92,9 @@ class SnapBuildView(LaunchpadFormView):
 
         return [
             ProxiedLibraryFileAlias(alias, self.context)
-            for _, alias, _ in self.context.getFiles() if not alias.deleted]
+            for _, alias, _ in self.context.getFiles()
+            if not alias.deleted
+        ]
 
     @cachedproperty
     def has_files(self):
@@ -96,7 +104,7 @@ class SnapBuildView(LaunchpadFormView):
     def next_url(self):
         return canonical_url(self.context)
 
-    @action('Upload build to store', name='upload')
+    @action("Upload build to store", name="upload")
     def upload_action(self, action, data):
         """Schedule an upload of this build to the store."""
         try:
@@ -106,7 +114,8 @@ class SnapBuildView(LaunchpadFormView):
         else:
             self.request.response.addInfoNotification(
                 "An upload has been scheduled and will run as soon as "
-                "possible.")
+                "possible."
+            )
 
 
 class SnapBuildRetryView(LaunchpadFormView):
@@ -115,22 +124,24 @@ class SnapBuildRetryView(LaunchpadFormView):
     class schema(Interface):
         """Schema for retrying a build."""
 
-    page_title = label = 'Retry build'
+    page_title = label = "Retry build"
 
     @property
     def cancel_url(self):
         return canonical_url(self.context)
+
     next_url = cancel_url
 
-    @action('Retry build', name='retry')
+    @action("Retry build", name="retry")
     def request_action(self, action, data):
         """Retry the build."""
         if not self.context.can_be_retried:
             self.request.response.addErrorNotification(
-                'Build cannot be retried')
+                "Build cannot be retried"
+            )
         else:
             self.context.retry()
-            self.request.response.addInfoNotification('Build has been queued')
+            self.request.response.addInfoNotification("Build has been queued")
 
         self.request.response.redirect(self.next_url)
 
@@ -141,14 +152,15 @@ class SnapBuildCancelView(LaunchpadFormView):
     class schema(Interface):
         """Schema for cancelling a build."""
 
-    page_title = label = 'Cancel build'
+    page_title = label = "Cancel build"
 
     @property
     def cancel_url(self):
         return canonical_url(self.context)
+
     next_url = cancel_url
 
-    @action('Cancel build', name='cancel')
+    @action("Cancel build", name="cancel")
     def request_action(self, action, data):
         """Cancel the build."""
         self.context.cancel()
@@ -159,27 +171,29 @@ class SnapBuildRescoreView(LaunchpadFormView):
 
     schema = IBuildRescoreForm
 
-    page_title = label = 'Rescore build'
+    page_title = label = "Rescore build"
 
     def __call__(self):
         if self.context.can_be_rescored:
             return super().__call__()
         self.request.response.addWarningNotification(
-            "Cannot rescore this build because it is not queued.")
+            "Cannot rescore this build because it is not queued."
+        )
         self.request.response.redirect(canonical_url(self.context))
 
     @property
     def cancel_url(self):
         return canonical_url(self.context)
+
     next_url = cancel_url
 
-    @action('Rescore build', name='rescore')
+    @action("Rescore build", name="rescore")
     def request_action(self, action, data):
         """Rescore the build."""
-        score = data.get('priority')
+        score = data.get("priority")
         self.context.rescore(score)
-        self.request.response.addNotification('Build rescored to %s.' % score)
+        self.request.response.addNotification("Build rescored to %s." % score)
 
     @property
     def initial_values(self):
-        return {'score': str(self.context.buildqueue_record.lastscore)}
+        return {"score": str(self.context.buildqueue_record.lastscore)}
diff --git a/lib/lp/snappy/browser/snaplisting.py b/lib/lp/snappy/browser/snaplisting.py
index 37098b5..410c8ab 100644
--- a/lib/lp/snappy/browser/snaplisting.py
+++ b/lib/lp/snappy/browser/snaplisting.py
@@ -4,10 +4,10 @@
 """Base class view for snap listings."""
 
 __all__ = [
-    'BranchSnapListingView',
-    'GitSnapListingView',
-    'PersonSnapListingView',
-    ]
+    "BranchSnapListingView",
+    "GitSnapListingView",
+    "PersonSnapListingView",
+]
 
 from functools import partial
 
@@ -31,19 +31,22 @@ class SnapListingView(LaunchpadView, FeedsMixin):
 
     @property
     def page_title(self):
-        return 'Snap packages'
+        return "Snap packages"
 
     @property
     def label(self):
-        return 'Snap packages for %(displayname)s' % {
-            'displayname': self.context.displayname}
+        return "Snap packages for %(displayname)s" % {
+            "displayname": self.context.displayname
+        }
 
     def initialize(self):
         super().initialize()
         snaps = getUtility(ISnapSet).findByContext(
-            self.context, visible_by_user=self.user)
+            self.context, visible_by_user=self.user
+        )
         loader = partial(
-            getUtility(ISnapSet).preloadDataForSnaps, user=self.user)
+            getUtility(ISnapSet).preloadDataForSnaps, user=self.user
+        )
         self.snaps = DecoratedResultSet(snaps, pre_iter_hook=loader)
 
     @cachedproperty
@@ -69,8 +72,9 @@ class GitSnapListingView(SnapListingView):
 
     @property
     def label(self):
-        return 'Snap packages for %(display_name)s' % {
-            'display_name': self.context.display_name}
+        return "Snap packages for %(display_name)s" % {
+            "display_name": self.context.display_name
+        }
 
 
 class PersonSnapListingView(SnapListingView):
diff --git a/lib/lp/snappy/browser/snappyseries.py b/lib/lp/snappy/browser/snappyseries.py
index 7939d33..281f8a0 100644
--- a/lib/lp/snappy/browser/snappyseries.py
+++ b/lib/lp/snappy/browser/snappyseries.py
@@ -4,8 +4,8 @@
 """SnappySeries views."""
 
 __all__ = [
-    'SnappySeriesSetNavigation',
-    ]
+    "SnappySeriesSetNavigation",
+]
 
 from lp.services.webapp import GetitemNavigation
 from lp.snappy.interfaces.snappyseries import ISnappySeriesSet
@@ -13,4 +13,5 @@ from lp.snappy.interfaces.snappyseries import ISnappySeriesSet
 
 class SnappySeriesSetNavigation(GetitemNavigation):
     """Navigation methods for `ISnappySeriesSet`."""
+
     usedfor = ISnappySeriesSet
diff --git a/lib/lp/snappy/browser/snapsubscription.py b/lib/lp/snappy/browser/snapsubscription.py
index dcb6a2c..7bbc80e 100644
--- a/lib/lp/snappy/browser/snapsubscription.py
+++ b/lib/lp/snappy/browser/snapsubscription.py
@@ -3,9 +3,7 @@
 
 """Snap subscription views."""
 
-__all__ = [
-    'SnapPortletSubscribersContent'
-]
+__all__ = ["SnapPortletSubscribersContent"]
 
 from zope.component import getUtility
 from zope.formlib.form import action
@@ -14,16 +12,13 @@ from zope.security.interfaces import ForbiddenAttribute
 from lp.app.browser.launchpadform import (
     LaunchpadEditFormView,
     LaunchpadFormView,
-    )
+)
 from lp.registry.interfaces.person import IPersonSet
-from lp.services.webapp import (
-    canonical_url,
-    LaunchpadView,
-    )
+from lp.services.webapp import LaunchpadView, canonical_url
 from lp.services.webapp.authorization import (
     check_permission,
     precache_permission_for_objects,
-    )
+)
 from lp.snappy.interfaces.snapsubscription import ISnapSubscription
 
 
@@ -38,20 +33,28 @@ class SnapPortletSubscribersContent(LaunchpadView):
         # need the expense of running several complex SQL queries.
         subscriptions = list(self.context.subscriptions)
         person_ids = [sub.person.id for sub in subscriptions]
-        list(getUtility(IPersonSet).getPrecachedPersonsFromIDs(
-            person_ids, need_validity=True))
+        list(
+            getUtility(IPersonSet).getPrecachedPersonsFromIDs(
+                person_ids, need_validity=True
+            )
+        )
         if self.user is not None:
             subscribers = [
-                subscription.person for subscription in subscriptions]
+                subscription.person for subscription in subscriptions
+            ]
             precache_permission_for_objects(
-                self.request, "launchpad.LimitedView", subscribers)
+                self.request, "launchpad.LimitedView", subscribers
+            )
 
         visible_subscriptions = [
-            subscription for subscription in subscriptions
-            if check_permission("launchpad.LimitedView", subscription.person)]
+            subscription
+            for subscription in subscriptions
+            if check_permission("launchpad.LimitedView", subscription.person)
+        ]
         return sorted(
             visible_subscriptions,
-            key=lambda subscription: subscription.person.displayname)
+            key=lambda subscription: subscription.person.displayname,
+        )
 
 
 class RedirectToSnapMixin:
@@ -75,20 +78,19 @@ class RedirectToSnapMixin:
 
 class SnapSubscriptionEditView(RedirectToSnapMixin, LaunchpadEditFormView):
     """The view for editing Snap recipe subscriptions."""
+
     schema = ISnapSubscription
     field_names = []
 
     @property
     def page_title(self):
-        return (
-            "Edit subscription to snap recipe %s" %
-            self.snap.displayname)
+        return "Edit subscription to snap recipe %s" % self.snap.displayname
 
     @property
     def label(self):
         return (
-            "Edit subscription to snap recipe for %s" %
-            self.person.displayname)
+            "Edit subscription to snap recipe for %s" % self.person.displayname
+        )
 
     def initialize(self):
         self.snap = self.context.snap
@@ -101,7 +103,8 @@ class SnapSubscriptionEditView(RedirectToSnapMixin, LaunchpadEditFormView):
         self.snap.unsubscribe(self.person, self.user)
         self.request.response.addNotification(
             "%s has been unsubscribed from this snap recipe."
-            % self.person.displayname)
+            % self.person.displayname
+        )
 
 
 class _SnapSubscriptionCreationView(RedirectToSnapMixin, LaunchpadFormView):
@@ -125,12 +128,14 @@ class SnapSubscriptionAddView(_SnapSubscriptionCreationView):
         # subscribed before continuing.
         if self.context.hasSubscription(self.user):
             self.request.response.addNotification(
-                "You are already subscribed to this snap recipe.")
+                "You are already subscribed to this snap recipe."
+            )
         else:
             self.context.subscribe(self.user, self.user)
 
             self.request.response.addNotification(
-                "You have subscribed to this snap recipe.")
+                "You have subscribed to this snap recipe."
+            )
 
 
 class SnapSubscriptionAddOtherView(_SnapSubscriptionCreationView):
@@ -149,12 +154,14 @@ class SnapSubscriptionAddOtherView(_SnapSubscriptionCreationView):
         if "person" in data:
             person = data["person"]
             subscription = self.context.getSubscription(person)
-            if (subscription is None
-                    and not self.context.userCanBeSubscribed(person)):
+            if subscription is None and not self.context.userCanBeSubscribed(
+                person
+            ):
                 self.setFieldError(
                     "person",
                     "Open and delegated teams cannot be subscribed to "
-                    "private snap recipes.")
+                    "private snap recipes.",
+                )
 
     @action("Subscribe", name="subscribe_action")
     def subscribe_action(self, action, data):
@@ -164,9 +171,11 @@ class SnapSubscriptionAddOtherView(_SnapSubscriptionCreationView):
         if subscription is None:
             self.context.subscribe(person, self.user)
             self.request.response.addNotification(
-                "%s has been subscribed to this snap recipe." %
-                person.displayname)
+                "%s has been subscribed to this snap recipe."
+                % person.displayname
+            )
         else:
             self.request.response.addNotification(
-                "%s was already subscribed to this snap recipe." %
-                person.displayname)
+                "%s was already subscribed to this snap recipe."
+                % person.displayname
+            )
diff --git a/lib/lp/snappy/browser/tests/test_hassnaps.py b/lib/lp/snappy/browser/tests/test_hassnaps.py
index 88608a6..edad008 100644
--- a/lib/lp/snappy/browser/tests/test_hassnaps.py
+++ b/lib/lp/snappy/browser/tests/test_hassnaps.py
@@ -4,10 +4,7 @@
 """Test views for objects that have snap packages."""
 
 import soupmatchers
-from testscenarios import (
-    load_tests_apply_scenarios,
-    WithScenarios,
-    )
+from testscenarios import WithScenarios, load_tests_apply_scenarios
 
 from lp.code.interfaces.branch import IBranch
 from lp.code.interfaces.gitrepository import IGitRepository
@@ -35,19 +32,28 @@ class TestHasSnapsView(WithScenarios, TestCaseWithFactory):
     layer = DatabaseFunctionalLayer
 
     scenarios = [
-        ("Branch", {
-            "context_type": "branch",
-            "context_factory": make_branch,
-            }),
-        ("GitRepository", {
-            "context_type": "repository",
-            "context_factory": make_git_repository,
-            }),
-        ("GitRef", {
-            "context_type": "branch",
-            "context_factory": make_git_ref,
-            }),
-        ]
+        (
+            "Branch",
+            {
+                "context_type": "branch",
+                "context_factory": make_branch,
+            },
+        ),
+        (
+            "GitRepository",
+            {
+                "context_type": "repository",
+                "context_factory": make_git_repository,
+            },
+        ),
+        (
+            "GitRef",
+            {
+                "context_type": "branch",
+                "context_factory": make_git_ref,
+            },
+        ),
+    ]
 
     def makeSnap(self, context):
         if IBranch.providedBy(context):
@@ -63,16 +69,18 @@ class TestHasSnapsView(WithScenarios, TestCaseWithFactory):
         view = create_initialized_view(context, "+index")
         self.assertEqual(
             "No snap packages using this %s." % self.context_type,
-            view.snaps_link)
+            view.snaps_link,
+        )
 
     def test_snaps_link_one_snap(self):
         # An object with one snap package shows a link to that snap package.
         context = self.context_factory(self)
         snap = self.makeSnap(context)
         view = create_initialized_view(context, "+index")
-        expected_link = (
-            '<a href="%s">1 snap package</a> using this %s.' %
-            (canonical_url(snap), self.context_type))
+        expected_link = '<a href="%s">1 snap package</a> using this %s.' % (
+            canonical_url(snap),
+            self.context_type,
+        )
         self.assertEqual(expected_link, view.snaps_link)
 
     def test_snaps_link_more_snaps(self):
@@ -82,8 +90,9 @@ class TestHasSnapsView(WithScenarios, TestCaseWithFactory):
         self.makeSnap(context)
         view = create_initialized_view(context, "+index")
         expected_link = (
-            '<a href="+snaps">2 snap packages</a> using this %s.' %
-            self.context_type)
+            '<a href="+snaps">2 snap packages</a> using this %s.'
+            % self.context_type
+        )
         self.assertEqual(expected_link, view.snaps_link)
 
 
@@ -94,14 +103,20 @@ class TestHasSnapsMenu(WithScenarios, TestCaseWithFactory):
     needs_git_hosting_fixture = False
 
     scenarios = [
-        ("Branch", {
-            "context_factory": make_branch,
-            }),
-        ("GitRef", {
-            "context_factory": make_git_ref,
-            "needs_git_hosting_fixture": True,
-            }),
-        ]
+        (
+            "Branch",
+            {
+                "context_factory": make_branch,
+            },
+        ),
+        (
+            "GitRef",
+            {
+                "context_factory": make_git_ref,
+                "needs_git_hosting_fixture": True,
+            },
+        ),
+    ]
 
     def setUp(self):
         super().setUp()
@@ -119,10 +134,17 @@ class TestHasSnapsMenu(WithScenarios, TestCaseWithFactory):
         context = self.context_factory(self)
         view = create_initialized_view(context, "+index")
         new_snap_url = canonical_url(context, view_name="+new-snap")
-        self.assertThat(view(), soupmatchers.HTMLContains(
-            soupmatchers.Tag(
-                "creation link", "a", attrs={"href": new_snap_url},
-                text="Create snap package")))
+        self.assertThat(
+            view(),
+            soupmatchers.HTMLContains(
+                soupmatchers.Tag(
+                    "creation link",
+                    "a",
+                    attrs={"href": new_snap_url},
+                    text="Create snap package",
+                )
+            ),
+        )
 
     def test_creation_link_snaps(self):
         # An object with snap packages shows a creation link.
@@ -130,10 +152,17 @@ class TestHasSnapsMenu(WithScenarios, TestCaseWithFactory):
         self.makeSnap(context)
         view = create_initialized_view(context, "+index")
         new_snap_url = canonical_url(context, view_name="+new-snap")
-        self.assertThat(view(), soupmatchers.HTMLContains(
-            soupmatchers.Tag(
-                "creation link", "a", attrs={"href": new_snap_url},
-                text="Create snap package")))
+        self.assertThat(
+            view(),
+            soupmatchers.HTMLContains(
+                soupmatchers.Tag(
+                    "creation link",
+                    "a",
+                    attrs={"href": new_snap_url},
+                    text="Create snap package",
+                )
+            ),
+        )
 
 
 load_tests = load_tests_apply_scenarios
diff --git a/lib/lp/snappy/browser/tests/test_snap.py b/lib/lp/snappy/browser/tests/test_snap.py
index fc683c4..74b502c 100644
--- a/lib/lp/snappy/browser/tests/test_snap.py
+++ b/lib/lp/snappy/browser/tests/test_snap.py
@@ -3,22 +3,17 @@
 
 """Test snap package views."""
 
-from datetime import (
-    datetime,
-    timedelta,
-    )
 import json
 import re
-from urllib.parse import (
-    parse_qs,
-    urlsplit,
-    )
+from datetime import datetime, timedelta
+from urllib.parse import parse_qs, urlsplit
 
-from fixtures import FakeLogger
-from pymacaroons import Macaroon
 import pytz
 import responses
 import soupmatchers
+import transaction
+from fixtures import FakeLogger
+from pymacaroons import Macaroon
 from testtools.matchers import (
     AfterPreprocessing,
     Equals,
@@ -28,8 +23,7 @@ from testtools.matchers import (
     MatchesSetwise,
     MatchesStructure,
     Not,
-    )
-import transaction
+)
 from zope.component import getUtility
 from zope.publisher.interfaces import NotFound
 from zope.security.interfaces import Unauthorized
@@ -40,19 +34,13 @@ from lp.app.enums import InformationType
 from lp.app.interfaces.launchpad import ILaunchpadCelebrities
 from lp.buildmaster.enums import BuildStatus
 from lp.buildmaster.interfaces.processor import IProcessorSet
-from lp.code.errors import (
-    BranchHostingFault,
-    GitRepositoryScanFault,
-    )
-from lp.code.tests.helpers import (
-    BranchHostingFixture,
-    GitHostingFixture,
-    )
+from lp.code.errors import BranchHostingFault, GitRepositoryScanFault
+from lp.code.tests.helpers import BranchHostingFixture, GitHostingFixture
 from lp.registry.enums import (
     BranchSharingPolicy,
     PersonVisibility,
     TeamMembershipPolicy,
-    )
+)
 from lp.registry.interfaces.pocket import PackagePublishingPocket
 from lp.registry.interfaces.series import SeriesStatus
 from lp.services.config import config
@@ -63,54 +51,41 @@ from lp.services.job.interfaces.job import JobStatus
 from lp.services.propertycache import get_property_cache
 from lp.services.webapp import canonical_url
 from lp.services.webapp.servers import LaunchpadTestRequest
-from lp.snappy.browser.snap import (
-    SnapAdminView,
-    SnapEditView,
-    SnapView,
-    )
+from lp.snappy.browser.snap import SnapAdminView, SnapEditView, SnapView
 from lp.snappy.interfaces.snap import (
-    CannotModifySnapProcessor,
-    ISnapSet,
     SNAP_PRIVATE_FEATURE_FLAG,
     SNAP_TESTING_FLAGS,
+    CannotModifySnapProcessor,
+    ISnapSet,
     SnapBuildRequestStatus,
     SnapPrivateFeatureDisabled,
-    )
+)
 from lp.snappy.interfaces.snappyseries import ISnappyDistroSeriesSet
 from lp.snappy.interfaces.snapstoreclient import ISnapStoreClient
 from lp.snappy.model.snap import Snap
 from lp.testing import (
-    admin_logged_in,
     BrowserTestCase,
+    TestCaseWithFactory,
+    admin_logged_in,
     login,
     login_admin,
     login_person,
     person_logged_in,
-    TestCaseWithFactory,
     time_counter,
-    )
+)
 from lp.testing.fakemethod import FakeMethod
 from lp.testing.fixture import ZopeUtilityFixture
-from lp.testing.layers import (
-    DatabaseFunctionalLayer,
-    LaunchpadFunctionalLayer,
-    )
-from lp.testing.matchers import (
-    MatchesPickerText,
-    MatchesTagText,
-    )
+from lp.testing.layers import DatabaseFunctionalLayer, LaunchpadFunctionalLayer
+from lp.testing.matchers import MatchesPickerText, MatchesTagText
 from lp.testing.pages import (
     extract_text,
     find_main_content,
     find_tag_by_id,
     find_tags_by_class,
     get_feedback_messages,
-    )
+)
 from lp.testing.publication import test_traverse
-from lp.testing.views import (
-    create_initialized_view,
-    create_view,
-    )
+from lp.testing.views import create_initialized_view, create_view
 
 
 class TestSnapNavigation(TestCaseWithFactory):
@@ -124,15 +99,17 @@ class TestSnapNavigation(TestCaseWithFactory):
     def test_canonical_url(self):
         owner = self.factory.makePerson(name="person")
         snap = self.factory.makeSnap(
-            registrant=owner, owner=owner, name="snap")
+            registrant=owner, owner=owner, name="snap"
+        )
         self.assertEqual(
-            "http://launchpad.test/~person/+snap/snap";, canonical_url(snap))
+            "http://launchpad.test/~person/+snap/snap";, canonical_url(snap)
+        )
 
     def test_snap(self):
         snap = self.factory.makeSnap()
         obj, _, _ = test_traverse(
-            "http://launchpad.test/~%s/+snap/%s"; % (
-                snap.owner.name, snap.name))
+            "http://launchpad.test/~%s/+snap/%s"; % (snap.owner.name, snap.name)
+        )
         self.assertEqual(snap, obj)
 
 
@@ -146,11 +123,15 @@ class TestSnapViewsFeatureFlag(TestCaseWithFactory):
         self.useFixture(BranchHostingFixture())
         owner = self.factory.makePerson()
         branch = self.factory.makeAnyBranch(
-            owner=owner, information_type=InformationType.USERDATA)
+            owner=owner, information_type=InformationType.USERDATA
+        )
         with person_logged_in(owner):
             self.assertRaises(
-                SnapPrivateFeatureDisabled, create_initialized_view,
-                branch, "+new-snap")
+                SnapPrivateFeatureDisabled,
+                create_initialized_view,
+                branch,
+                "+new-snap",
+            )
 
 
 class BaseTestSnapView(BrowserTestCase):
@@ -162,23 +143,27 @@ class BaseTestSnapView(BrowserTestCase):
         self.useFixture(FeatureFixture(SNAP_TESTING_FLAGS))
         self.useFixture(FakeLogger())
         self.snap_store_client = FakeMethod()
-        self.snap_store_client.requestPackageUploadPermission = (
-            getUtility(ISnapStoreClient).requestPackageUploadPermission)
+        self.snap_store_client.requestPackageUploadPermission = getUtility(
+            ISnapStoreClient
+        ).requestPackageUploadPermission
         self.useFixture(
-            ZopeUtilityFixture(self.snap_store_client, ISnapStoreClient))
+            ZopeUtilityFixture(self.snap_store_client, ISnapStoreClient)
+        )
         self.person = self.factory.makePerson(
-            name="test-person", displayname="Test Person")
+            name="test-person", displayname="Test Person"
+        )
 
 
 class TestSnapAddView(BaseTestSnapView):
-
     def setUp(self):
         super().setUp()
         self.distroseries = self.factory.makeUbuntuDistroSeries(
-            version="13.10")
+            version="13.10"
+        )
         with admin_logged_in():
             self.snappyseries = self.factory.makeSnappySeries(
-                preferred_distro_series=self.distroseries)
+                preferred_distro_series=self.distroseries
+            )
 
     def setUpDistroSeries(self):
         """Set up a distroseries with some available processors."""
@@ -187,8 +172,10 @@ class TestSnapAddView(BaseTestSnapView):
         for name in processor_names:
             processor = getUtility(IProcessorSet).getByName(name)
             self.factory.makeDistroArchSeries(
-                distroseries=distroseries, architecturetag=name,
-                processor=processor)
+                distroseries=distroseries,
+                architecturetag=name,
+                processor=processor,
+            )
         with admin_logged_in():
             self.factory.makeSnappySeries(preferred_distro_series=distroseries)
         return distroseries
@@ -196,10 +183,14 @@ class TestSnapAddView(BaseTestSnapView):
     def assertProcessorControls(self, processors_control, enabled, disabled):
         matchers = [
             MatchesStructure.byEquality(optionValue=name, disabled=False)
-            for name in enabled]
-        matchers.extend([
-            MatchesStructure.byEquality(optionValue=name, disabled=True)
-            for name in disabled])
+            for name in enabled
+        ]
+        matchers.extend(
+            [
+                MatchesStructure.byEquality(optionValue=name, disabled=True)
+                for name in disabled
+            ]
+        )
         self.assertThat(processors_control.controls, MatchesSetwise(*matchers))
 
     def test_initial_store_distro_series(self):
@@ -207,121 +198,152 @@ class TestSnapAddView(BaseTestSnapView):
         # series for the latest snappy series.
         self.useFixture(BranchHostingFixture(blob=b""))
         lts = self.factory.makeUbuntuDistroSeries(
-            version="16.04", status=SeriesStatus.CURRENT)
+            version="16.04", status=SeriesStatus.CURRENT
+        )
         current = self.factory.makeUbuntuDistroSeries(
-            version="16.10", status=SeriesStatus.CURRENT)
+            version="16.10", status=SeriesStatus.CURRENT
+        )
         with admin_logged_in():
             self.factory.makeSnappySeries(usable_distro_series=[lts, current])
             newest = self.factory.makeSnappySeries(
                 preferred_distro_series=lts,
-                usable_distro_series=[lts, current])
+                usable_distro_series=[lts, current],
+            )
         branch = self.factory.makeAnyBranch()
         with person_logged_in(self.person):
             view = create_initialized_view(branch, "+new-snap")
         self.assertThat(
             view.initial_values["store_distro_series"],
             MatchesStructure.byEquality(
-                snappy_series=newest, distro_series=lts))
+                snappy_series=newest, distro_series=lts
+            ),
+        )
 
     def test_initial_store_distro_series_can_infer_distro_series(self):
         # If the latest snappy series supports inferring the distro series
         # from snapcraft.yaml, then we default to that.
         self.useFixture(BranchHostingFixture(blob=b""))
         lts = self.factory.makeUbuntuDistroSeries(
-            version="16.04", status=SeriesStatus.CURRENT)
+            version="16.04", status=SeriesStatus.CURRENT
+        )
         with admin_logged_in():
             self.factory.makeSnappySeries(usable_distro_series=[lts])
             newest = self.factory.makeSnappySeries(
-                preferred_distro_series=lts, can_infer_distro_series=True)
+                preferred_distro_series=lts, can_infer_distro_series=True
+            )
         branch = self.factory.makeAnyBranch()
         with person_logged_in(self.person):
             view = create_initialized_view(branch, "+new-snap")
         self.assertThat(
             view.initial_values["store_distro_series"],
             MatchesStructure(
-                snappy_series=Equals(newest), distro_series=Is(None)))
+                snappy_series=Equals(newest), distro_series=Is(None)
+            ),
+        )
 
     def test_create_new_snap_not_logged_in(self):
         branch = self.factory.makeAnyBranch()
         self.assertRaises(
-            Unauthorized, self.getViewBrowser, branch, view_name="+new-snap",
-            no_login=True)
+            Unauthorized,
+            self.getViewBrowser,
+            branch,
+            view_name="+new-snap",
+            no_login=True,
+        )
 
     def test_create_new_snap_bzr(self):
         self.useFixture(BranchHostingFixture(blob=b""))
         branch = self.factory.makeAnyBranch()
         source_display = branch.display_name
         browser = self.getViewBrowser(
-            branch, view_name="+new-snap", user=self.person)
+            branch, view_name="+new-snap", user=self.person
+        )
         browser.getControl(name="field.name").value = "snap-name"
         browser.getControl("Create snap package").click()
 
         content = find_main_content(browser.contents)
         self.assertEqual("snap-name", extract_text(content.h1))
         self.assertThat(
-            "Test Person", MatchesPickerText(content, "edit-owner"))
+            "Test Person", MatchesPickerText(content, "edit-owner")
+        )
         self.assertThat(
-            "Distribution series:\n%s\nEdit snap package" %
-            self.distroseries.fullseriesname,
-            MatchesTagText(content, "distro_series"))
+            "Distribution series:\n%s\nEdit snap package"
+            % self.distroseries.fullseriesname,
+            MatchesTagText(content, "distro_series"),
+        )
         self.assertThat(
             "Source:\n%s\nEdit snap package" % source_display,
-            MatchesTagText(content, "source"))
+            MatchesTagText(content, "source"),
+        )
         self.assertThat(
             "Build source tarball:\nNo\nEdit snap package",
-            MatchesTagText(content, "build_source_tarball"))
+            MatchesTagText(content, "build_source_tarball"),
+        )
         self.assertThat(
             "Build schedule:\n(?)\nBuilt on request\nEdit snap package\n",
-            MatchesTagText(content, "auto_build"))
+            MatchesTagText(content, "auto_build"),
+        )
         self.assertThat(
             "Source archive for automatic builds:\n\nEdit snap package\n",
-            MatchesTagText(content, "auto_build_archive"))
+            MatchesTagText(content, "auto_build_archive"),
+        )
         self.assertThat(
             "Pocket for automatic builds:\n\nEdit snap package",
-            MatchesTagText(content, "auto_build_pocket"))
+            MatchesTagText(content, "auto_build_pocket"),
+        )
         self.assertIsNone(find_tag_by_id(content, "auto_build_channels"))
         self.assertThat(
             "Builds of this snap package are not automatically uploaded to "
             "the store.\nEdit snap package",
-            MatchesTagText(content, "store_upload"))
+            MatchesTagText(content, "store_upload"),
+        )
 
     def test_create_new_snap_git(self):
         self.useFixture(GitHostingFixture(blob=b""))
         [git_ref] = self.factory.makeGitRefs()
         source_display = git_ref.display_name
         browser = self.getViewBrowser(
-            git_ref, view_name="+new-snap", user=self.person)
+            git_ref, view_name="+new-snap", user=self.person
+        )
         browser.getControl(name="field.name").value = "snap-name"
         browser.getControl("Create snap package").click()
 
         content = find_main_content(browser.contents)
         self.assertEqual("snap-name", extract_text(content.h1))
         self.assertThat(
-            "Test Person", MatchesPickerText(content, "edit-owner"))
+            "Test Person", MatchesPickerText(content, "edit-owner")
+        )
         self.assertThat(
-            "Distribution series:\n%s\nEdit snap package" %
-            self.distroseries.fullseriesname,
-            MatchesTagText(content, "distro_series"))
+            "Distribution series:\n%s\nEdit snap package"
+            % self.distroseries.fullseriesname,
+            MatchesTagText(content, "distro_series"),
+        )
         self.assertThat(
             "Source:\n%s\nEdit snap package" % source_display,
-            MatchesTagText(content, "source"))
+            MatchesTagText(content, "source"),
+        )
         self.assertThat(
             "Build source tarball:\nNo\nEdit snap package",
-            MatchesTagText(content, "build_source_tarball"))
+            MatchesTagText(content, "build_source_tarball"),
+        )
         self.assertThat(
             "Build schedule:\n(?)\nBuilt on request\nEdit snap package\n",
-            MatchesTagText(content, "auto_build"))
+            MatchesTagText(content, "auto_build"),
+        )
         self.assertThat(
             "Source archive for automatic builds:\n\nEdit snap package\n",
-            MatchesTagText(content, "auto_build_archive"))
+            MatchesTagText(content, "auto_build_archive"),
+        )
         self.assertThat(
             "Pocket for automatic builds:\n\nEdit snap package",
-            MatchesTagText(content, "auto_build_pocket"))
+            MatchesTagText(content, "auto_build_pocket"),
+        )
         self.assertIsNone(find_tag_by_id(content, "auto_build_channels"))
         self.assertThat(
             "Builds of this snap package are not automatically uploaded to "
             "the store.\nEdit snap package",
-            MatchesTagText(content, "store_upload"))
+            MatchesTagText(content, "store_upload"),
+        )
 
     def test_create_new_snap_project(self):
         self.useFixture(GitHostingFixture(blob=b""))
@@ -329,55 +351,68 @@ class TestSnapAddView(BaseTestSnapView):
         [git_ref] = self.factory.makeGitRefs()
         source_display = git_ref.display_name
         browser = self.getViewBrowser(
-            project, view_name="+new-snap", user=self.person)
+            project, view_name="+new-snap", user=self.person
+        )
         browser.getControl(name="field.name").value = "snap-name"
         browser.getControl(name="field.vcs").value = "GIT"
-        browser.getControl(name="field.git_ref.repository").value = (
-            git_ref.repository.shortened_path)
+        browser.getControl(
+            name="field.git_ref.repository"
+        ).value = git_ref.repository.shortened_path
         browser.getControl(name="field.git_ref.path").value = git_ref.path
         browser.getControl("Create snap package").click()
 
         content = find_main_content(browser.contents)
         self.assertEqual("snap-name", extract_text(content.h1))
         self.assertThat(
-            "Test Person", MatchesPickerText(content, "edit-owner"))
+            "Test Person", MatchesPickerText(content, "edit-owner")
+        )
         self.assertThat(
-            "Distribution series:\n%s\nEdit snap package" %
-            self.distroseries.fullseriesname,
-            MatchesTagText(content, "distro_series"))
+            "Distribution series:\n%s\nEdit snap package"
+            % self.distroseries.fullseriesname,
+            MatchesTagText(content, "distro_series"),
+        )
         self.assertThat(
             "Source:\n%s\nEdit snap package" % source_display,
-            MatchesTagText(content, "source"))
+            MatchesTagText(content, "source"),
+        )
         self.assertThat(
             "Build source tarball:\nNo\nEdit snap package",
-            MatchesTagText(content, "build_source_tarball"))
+            MatchesTagText(content, "build_source_tarball"),
+        )
         self.assertThat(
             "Build schedule:\n(?)\nBuilt on request\nEdit snap package\n",
-            MatchesTagText(content, "auto_build"))
+            MatchesTagText(content, "auto_build"),
+        )
         self.assertThat(
             "Source archive for automatic builds:\n\nEdit snap package\n",
-            MatchesTagText(content, "auto_build_archive"))
+            MatchesTagText(content, "auto_build_archive"),
+        )
         self.assertThat(
             "Pocket for automatic builds:\n\nEdit snap package",
-            MatchesTagText(content, "auto_build_pocket"))
+            MatchesTagText(content, "auto_build_pocket"),
+        )
         self.assertIsNone(find_tag_by_id(content, "auto_build_channels"))
         self.assertThat(
             "Builds of this snap package are not automatically uploaded to "
             "the store.\nEdit snap package",
-            MatchesTagText(content, "store_upload"))
+            MatchesTagText(content, "store_upload"),
+        )
 
     def test_create_new_snap_users_teams_as_owner_options(self):
         # Teams that the user is in are options for the snap package owner.
         self.useFixture(BranchHostingFixture(blob=b""))
         self.factory.makeTeam(
-            name="test-team", displayname="Test Team", members=[self.person])
+            name="test-team", displayname="Test Team", members=[self.person]
+        )
         branch = self.factory.makeAnyBranch()
         browser = self.getViewBrowser(
-            branch, view_name="+new-snap", user=self.person)
+            branch, view_name="+new-snap", user=self.person
+        )
         options = browser.getControl("Owner").displayOptions
         self.assertEqual(
             ["Test Person (test-person)", "Test Team (test-team)"],
-            sorted(str(option) for option in options))
+            sorted(str(option) for option in options),
+        )
 
     def test_create_new_snap_public(self):
         # Public owner implies public snap.
@@ -385,15 +420,16 @@ class TestSnapAddView(BaseTestSnapView):
         branch = self.factory.makeAnyBranch()
 
         browser = self.getViewBrowser(
-            branch, view_name="+new-snap", user=self.person)
+            branch, view_name="+new-snap", user=self.person
+        )
         browser.getControl(name="field.name").value = "public-snap"
         browser.getControl("Create snap package").click()
 
         content = find_main_content(browser.contents)
         self.assertEqual("public-snap", extract_text(content.h1))
         self.assertEqual(
-            'This snap contains Public information',
-            extract_text(find_tag_by_id(browser.contents, "privacy"))
+            "This snap contains Public information",
+            extract_text(find_tag_by_id(browser.contents, "privacy")),
         )
 
     def test_create_new_snap_private_link(self):
@@ -401,13 +437,14 @@ class TestSnapAddView(BaseTestSnapView):
         # if the 'snap.allow_private' is enabled.
         login_person(self.person)
         branch = self.factory.makeAnyBranch(
-            owner=self.person,
-            information_type=InformationType.USERDATA)
+            owner=self.person, information_type=InformationType.USERDATA
+        )
 
         with FeatureFixture({SNAP_PRIVATE_FEATURE_FLAG: ""}):
             browser = self.getViewBrowser(branch, user=self.person)
             self.assertRaises(
-                LinkNotFoundError, browser.getLink, "Create snap package")
+                LinkNotFoundError, browser.getLink, "Create snap package"
+            )
         with FeatureFixture(SNAP_TESTING_FLAGS):
             browser = self.getViewBrowser(branch, user=self.person)
             browser.getLink("Create snap package")
@@ -416,14 +453,17 @@ class TestSnapAddView(BaseTestSnapView):
         # Creates a private snap for a private project.
         login_person(self.person)
         self.factory.makeProduct(
-            name='private-project',
-            owner=self.person, registrant=self.person,
+            name="private-project",
+            owner=self.person,
+            registrant=self.person,
             information_type=InformationType.PROPRIETARY,
-            branch_sharing_policy=BranchSharingPolicy.PROPRIETARY)
+            branch_sharing_policy=BranchSharingPolicy.PROPRIETARY,
+        )
         [git_ref] = self.factory.makeGitRefs()
 
         browser = self.getViewBrowser(
-            git_ref, view_name="+new-snap", user=self.person)
+            git_ref, view_name="+new-snap", user=self.person
+        )
         browser.getControl(name="field.name").value = "private-snap"
         browser.getControl(name="field.information_type").value = "PROPRIETARY"
         browser.getControl(name="field.project").value = "private-project"
@@ -432,12 +472,12 @@ class TestSnapAddView(BaseTestSnapView):
         content = find_main_content(browser.contents)
         self.assertEqual("private-snap", extract_text(content.h1))
         self.assertEqual(
-            'This snap contains Private information',
-            extract_text(find_tag_by_id(browser.contents, "privacy")))
+            "This snap contains Private information",
+            extract_text(find_tag_by_id(browser.contents, "privacy")),
+        )
         login_admin()
-        snap = getUtility(ISnapSet).getByName(self.person, 'private-snap')
-        self.assertEqual(
-            InformationType.PROPRIETARY, snap.information_type)
+        snap = getUtility(ISnapSet).getByName(self.person, "private-snap")
+        self.assertEqual(InformationType.PROPRIETARY, snap.information_type)
 
     def test_create_new_snap_private_without_project_fails(self):
         # It should not not be possible to create a private snap with
@@ -446,7 +486,8 @@ class TestSnapAddView(BaseTestSnapView):
         [git_ref] = self.factory.makeGitRefs()
 
         browser = self.getViewBrowser(
-            git_ref, view_name="+new-snap", user=self.person)
+            git_ref, view_name="+new-snap", user=self.person
+        )
         browser.getControl(name="field.name").value = "private-snap"
         browser.getControl(name="field.information_type").value = "PROPRIETARY"
         browser.getControl("Create snap package").click()
@@ -456,13 +497,13 @@ class TestSnapAddView(BaseTestSnapView):
         messages = find_tags_by_class(browser.contents, "message")
         self.assertEqual(2, len(messages))
         top_msg, field_msg = messages
+        self.assertEqual("There is 1 error.", extract_text(top_msg))
         self.assertEqual(
-            'There is 1 error.', extract_text(top_msg))
-        self.assertEqual(
-            'Private snap recipes must be associated with a project.',
-            extract_text(field_msg))
+            "Private snap recipes must be associated with a project.",
+            extract_text(field_msg),
+        )
         login_admin()
-        snap = IStore(Snap).find(Snap, Snap.name == 'private-snap').one()
+        snap = IStore(Snap).find(Snap, Snap.name == "private-snap").one()
         self.assertIsNone(snap)
 
     def test_create_new_snap_private_with_invalid_information_type_fails(self):
@@ -472,14 +513,17 @@ class TestSnapAddView(BaseTestSnapView):
         # The project is proprietary, with branch policy beign proprietary
         # too. We can only create proprietary snaps.
         self.factory.makeProduct(
-            name='private-project',
-            owner=self.person, registrant=self.person,
+            name="private-project",
+            owner=self.person,
+            registrant=self.person,
             information_type=InformationType.PROPRIETARY,
-            branch_sharing_policy=BranchSharingPolicy.PROPRIETARY)
+            branch_sharing_policy=BranchSharingPolicy.PROPRIETARY,
+        )
         [git_ref] = self.factory.makeGitRefs()
 
         browser = self.getViewBrowser(
-            git_ref, view_name="+new-snap", user=self.person)
+            git_ref, view_name="+new-snap", user=self.person
+        )
         browser.getControl(name="field.name").value = "private-snap"
         browser.getControl(name="field.information_type").value = "PUBLIC"
         browser.getControl(name="field.project").value = "private-project"
@@ -490,14 +534,14 @@ class TestSnapAddView(BaseTestSnapView):
         messages = find_tags_by_class(browser.contents, "message")
         self.assertEqual(2, len(messages))
         top_msg, field_msg = messages
-        self.assertEqual(
-            'There is 1 error.', extract_text(top_msg))
+        self.assertEqual("There is 1 error.", extract_text(top_msg))
         expected_msg = (
-            'Project private-project only accepts the following information '
-            'types: Proprietary.')
+            "Project private-project only accepts the following information "
+            "types: Proprietary."
+        )
         self.assertEqual(expected_msg, extract_text(field_msg))
         login_admin()
-        snap = IStore(Snap).find(Snap, Snap.name == 'private-snap').one()
+        snap = IStore(Snap).find(Snap, Snap.name == "private-snap").one()
         self.assertIsNone(snap)
 
     def test_create_new_snap_build_source_tarball(self):
@@ -505,7 +549,8 @@ class TestSnapAddView(BaseTestSnapView):
         self.useFixture(BranchHostingFixture(blob=b""))
         branch = self.factory.makeAnyBranch()
         browser = self.getViewBrowser(
-            branch, view_name="+new-snap", user=self.person)
+            branch, view_name="+new-snap", user=self.person
+        )
         browser.getControl(name="field.name").value = "snap-name"
         browser.getControl("Build source tarball").selected = True
         browser.getControl("Create snap package").click()
@@ -513,7 +558,8 @@ class TestSnapAddView(BaseTestSnapView):
         content = find_main_content(browser.contents)
         self.assertThat(
             "Build source tarball:\nYes\nEdit snap package",
-            MatchesTagText(content, "build_source_tarball"))
+            MatchesTagText(content, "build_source_tarball"),
+        )
 
     def test_create_new_snap_auto_build(self):
         # Creating a new snap and asking for it to be automatically built
@@ -522,40 +568,51 @@ class TestSnapAddView(BaseTestSnapView):
         branch = self.factory.makeAnyBranch()
         archive = self.factory.makeArchive()
         browser = self.getViewBrowser(
-            branch, view_name="+new-snap", user=self.person)
+            branch, view_name="+new-snap", user=self.person
+        )
         browser.getControl(name="field.name").value = "snap-name"
         browser.getControl(
-            "Automatically build when branch changes").selected = True
+            "Automatically build when branch changes"
+        ).selected = True
         browser.getControl("PPA").click()
-        browser.getControl(name="field.auto_build_archive.ppa").value = (
-            archive.reference)
+        browser.getControl(
+            name="field.auto_build_archive.ppa"
+        ).value = archive.reference
         browser.getControl("Pocket for automatic builds").value = ["SECURITY"]
         browser.getControl(
-            name="field.auto_build_channels.core").value = "stable"
+            name="field.auto_build_channels.core"
+        ).value = "stable"
         browser.getControl(
-            name="field.auto_build_channels.core18").value = "beta"
+            name="field.auto_build_channels.core18"
+        ).value = "beta"
         browser.getControl(
-            name="field.auto_build_channels.core20").value = "edge/feature"
+            name="field.auto_build_channels.core20"
+        ).value = "edge/feature"
         browser.getControl(
-            name="field.auto_build_channels.snapcraft").value = "edge"
+            name="field.auto_build_channels.snapcraft"
+        ).value = "edge"
         browser.getControl("Create snap package").click()
 
         content = find_main_content(browser.contents)
         self.assertThat(
             "Build schedule:\n(?)\nBuilt automatically\nEdit snap package\n",
-            MatchesTagText(content, "auto_build"))
+            MatchesTagText(content, "auto_build"),
+        )
         self.assertThat(
-            "Source archive for automatic builds:\n%s\nEdit snap package\n" %
-            archive.displayname,
-            MatchesTagText(content, "auto_build_archive"))
+            "Source archive for automatic builds:\n%s\nEdit snap package\n"
+            % archive.displayname,
+            MatchesTagText(content, "auto_build_archive"),
+        )
         self.assertThat(
             "Pocket for automatic builds:\nSecurity\nEdit snap package",
-            MatchesTagText(content, "auto_build_pocket"))
+            MatchesTagText(content, "auto_build_pocket"),
+        )
         self.assertThat(
             "Source snap channels for automatic builds:\nEdit snap package\n"
             "core\nstable\ncore18\nbeta\n"
             "core20\nedge/feature\nsnapcraft\nedge\n",
-            MatchesTagText(content, "auto_build_channels"))
+            MatchesTagText(content, "auto_build_channels"),
+        )
 
     @responses.activate
     def test_create_new_snap_store_upload(self):
@@ -568,51 +625,70 @@ class TestSnapAddView(BaseTestSnapView):
         browser = self.getNonRedirectingBrowser(url=view_url, user=self.person)
         browser.getControl(name="field.name").value = "snap-name"
         browser.getControl("Automatically upload to store").selected = True
-        browser.getControl("Registered store package name").value = (
-            "store-name")
+        browser.getControl(
+            "Registered store package name"
+        ).value = "store-name"
         self.assertFalse(browser.getControl("Stable").selected)
         browser.getControl(name="field.store_channels.track").value = "track"
         browser.getControl("Edge").selected = True
         root_macaroon = Macaroon()
         root_macaroon.add_third_party_caveat(
-            urlsplit(config.launchpad.openid_provider_root).netloc, "",
-            "dummy")
+            urlsplit(config.launchpad.openid_provider_root).netloc, "", "dummy"
+        )
         root_macaroon_raw = root_macaroon.serialize()
         self.pushConfig("snappy", store_url="http://sca.example/";)
         responses.add(
-            "POST", "http://sca.example/dev/api/acl/";,
-            json={"macaroon": root_macaroon_raw})
+            "POST",
+            "http://sca.example/dev/api/acl/";,
+            json={"macaroon": root_macaroon_raw},
+        )
         browser.getControl("Create snap package").click()
         login_person(self.person)
         snap = getUtility(ISnapSet).getByName(self.person, "snap-name")
-        self.assertThat(snap, MatchesStructure.byEquality(
-            owner=self.person, distro_series=self.distroseries,
-            name="snap-name", source=branch, store_upload=True,
-            store_series=self.snappyseries, store_name="store-name",
-            store_secrets={"root": root_macaroon_raw},
-            store_channels=["track/edge"]))
+        self.assertThat(
+            snap,
+            MatchesStructure.byEquality(
+                owner=self.person,
+                distro_series=self.distroseries,
+                name="snap-name",
+                source=branch,
+                store_upload=True,
+                store_series=self.snappyseries,
+                store_name="store-name",
+                store_secrets={"root": root_macaroon_raw},
+                store_channels=["track/edge"],
+            ),
+        )
         [call] = responses.calls
-        self.assertThat(call.request, MatchesStructure.byEquality(
-            url="http://sca.example/dev/api/acl/";, method="POST"))
+        self.assertThat(
+            call.request,
+            MatchesStructure.byEquality(
+                url="http://sca.example/dev/api/acl/";, method="POST"
+            ),
+        )
         expected_body = {
-            "packages": [{
-                "name": "store-name",
-                "series": self.snappyseries.name,
-                }],
+            "packages": [
+                {
+                    "name": "store-name",
+                    "series": self.snappyseries.name,
+                }
+            ],
             "permissions": ["package_upload"],
-            }
+        }
         self.assertEqual(
-            expected_body, json.loads(call.request.body.decode("UTF-8")))
+            expected_body, json.loads(call.request.body.decode("UTF-8"))
+        )
         self.assertEqual(303, int(browser.headers["Status"].split(" ", 1)[0]))
         parsed_location = urlsplit(browser.headers["Location"])
         self.assertEqual(
             urlsplit(canonical_url(snap) + "/+authorize/+login")[:3],
-            parsed_location[:3])
+            parsed_location[:3],
+        )
         expected_args = {
             "discharge_macaroon_action": ["field.actions.complete"],
             "discharge_macaroon_field": ["field.discharge_macaroon"],
             "macaroon_caveat_id": ["dummy"],
-            }
+        }
         self.assertEqual(expected_args, parse_qs(parsed_location[3]))
 
     def test_create_new_snap_display_processors(self):
@@ -620,11 +696,13 @@ class TestSnapAddView(BaseTestSnapView):
         branch = self.factory.makeAnyBranch()
         self.setUpDistroSeries()
         browser = self.getViewBrowser(
-            branch, view_name="+new-snap", user=self.person)
+            branch, view_name="+new-snap", user=self.person
+        )
         processors = browser.getControl(name="field.processors")
         self.assertContentEqual(
             ["Intel 386 (386)", "AMD 64bit (amd64)", "HPPA Processor (hppa)"],
-            [extract_text(option) for option in processors.displayOptions])
+            [extract_text(option) for option in processors.displayOptions],
+        )
         self.assertContentEqual(["386", "amd64", "hppa"], processors.options)
         self.assertContentEqual(["386", "amd64", "hppa"], processors.value)
 
@@ -635,22 +713,28 @@ class TestSnapAddView(BaseTestSnapView):
         branch = self.factory.makeAnyBranch()
         distroseries = self.setUpDistroSeries()
         proc_armhf = self.factory.makeProcessor(
-            name="armhf", restricted=True, build_by_default=False)
+            name="armhf", restricted=True, build_by_default=False
+        )
         self.factory.makeDistroArchSeries(
-            distroseries=distroseries, architecturetag="armhf",
-            processor=proc_armhf)
+            distroseries=distroseries,
+            architecturetag="armhf",
+            processor=proc_armhf,
+        )
         browser = self.getViewBrowser(
-            branch, view_name="+new-snap", user=self.person)
+            branch, view_name="+new-snap", user=self.person
+        )
         processors = browser.getControl(name="field.processors")
         self.assertProcessorControls(
-            processors, ["386", "amd64", "hppa"], ["armhf"])
+            processors, ["386", "amd64", "hppa"], ["armhf"]
+        )
 
     def test_create_new_snap_processors(self):
         self.useFixture(BranchHostingFixture(blob=b""))
         branch = self.factory.makeAnyBranch()
         self.setUpDistroSeries()
         browser = self.getViewBrowser(
-            branch, view_name="+new-snap", user=self.person)
+            branch, view_name="+new-snap", user=self.person
+        )
         processors = browser.getControl(name="field.processors")
         processors.value = ["386", "amd64"]
         browser.getControl(name="field.name").value = "snap-name"
@@ -658,7 +742,8 @@ class TestSnapAddView(BaseTestSnapView):
         login_person(self.person)
         snap = getUtility(ISnapSet).getByName(self.person, "snap-name")
         self.assertContentEqual(
-            ["386", "amd64"], [proc.name for proc in snap.processors])
+            ["386", "amd64"], [proc.name for proc in snap.processors]
+        )
 
     def test_create_new_snap_infer_distro_series(self):
         self.useFixture(BranchHostingFixture(blob=b""))
@@ -666,14 +751,17 @@ class TestSnapAddView(BaseTestSnapView):
             self.snappyseries.can_infer_distro_series = True
         branch = self.factory.makeAnyBranch()
         browser = self.getViewBrowser(
-            branch, view_name="+new-snap", user=self.person)
+            branch, view_name="+new-snap", user=self.person
+        )
         browser.getControl(name="field.name").value = "snap-name"
         self.assertEqual(
             [self.snappyseries.name],
-            browser.getControl(name="field.store_distro_series").value)
+            browser.getControl(name="field.store_distro_series").value,
+        )
         self.assertEqual(
             self.snappyseries.name,
-            browser.getControl(name="field.store_distro_series").options[0])
+            browser.getControl(name="field.store_distro_series").options[0],
+        )
         browser.getControl("Create snap package").click()
 
         content = find_main_content(browser.contents)
@@ -681,60 +769,68 @@ class TestSnapAddView(BaseTestSnapView):
         self.assertIsNone(find_tag_by_id(content, "distro_series"))
 
     def test_initial_name_extraction_bzr_success(self):
-        self.useFixture(BranchHostingFixture(
-            file_list={"snapcraft.yaml": "file-id"}, blob=b"name: test-snap"))
+        self.useFixture(
+            BranchHostingFixture(
+                file_list={"snapcraft.yaml": "file-id"},
+                blob=b"name: test-snap",
+            )
+        )
         branch = self.factory.makeBranch()
         view = create_initialized_view(branch, "+new-snap")
         initial_values = view.initial_values
-        self.assertIn('store_name', initial_values)
-        self.assertEqual('test-snap', initial_values['store_name'])
+        self.assertIn("store_name", initial_values)
+        self.assertEqual("test-snap", initial_values["store_name"])
 
     def test_initial_name_extraction_bzr_error(self):
         self.useFixture(BranchHostingFixture()).getInventory = FakeMethod(
-            failure=BranchHostingFault)
+            failure=BranchHostingFault
+        )
         branch = self.factory.makeBranch()
         view = create_initialized_view(branch, "+new-snap")
         initial_values = view.initial_values
-        self.assertIn('store_name', initial_values)
-        self.assertIsNone(initial_values['store_name'])
+        self.assertIn("store_name", initial_values)
+        self.assertIsNone(initial_values["store_name"])
 
     def test_initial_name_extraction_bzr_no_name(self):
-        self.useFixture(BranchHostingFixture(
-            file_list={"snapcraft.yaml": "file-id"}, blob=b"some: nonsense"))
+        self.useFixture(
+            BranchHostingFixture(
+                file_list={"snapcraft.yaml": "file-id"}, blob=b"some: nonsense"
+            )
+        )
         branch = self.factory.makeBranch()
         view = create_initialized_view(branch, "+new-snap")
         initial_values = view.initial_values
-        self.assertIn('store_name', initial_values)
-        self.assertIsNone(initial_values['store_name'])
+        self.assertIn("store_name", initial_values)
+        self.assertIsNone(initial_values["store_name"])
 
     def test_initial_name_extraction_git_success(self):
         self.useFixture(GitHostingFixture(blob=b"name: test-snap"))
         [git_ref] = self.factory.makeGitRefs()
         view = create_initialized_view(git_ref, "+new-snap")
         initial_values = view.initial_values
-        self.assertIn('store_name', initial_values)
-        self.assertEqual('test-snap', initial_values['store_name'])
+        self.assertIn("store_name", initial_values)
+        self.assertEqual("test-snap", initial_values["store_name"])
 
     def test_initial_name_extraction_git_error(self):
         self.useFixture(GitHostingFixture()).getBlob = FakeMethod(
-            failure=GitRepositoryScanFault)
+            failure=GitRepositoryScanFault
+        )
         [git_ref] = self.factory.makeGitRefs()
         view = create_initialized_view(git_ref, "+new-snap")
         initial_values = view.initial_values
-        self.assertIn('store_name', initial_values)
-        self.assertIsNone(initial_values['store_name'])
+        self.assertIn("store_name", initial_values)
+        self.assertIsNone(initial_values["store_name"])
 
     def test_initial_name_extraction_git_no_name(self):
         self.useFixture(GitHostingFixture(blob=b"some: nonsense"))
         [git_ref] = self.factory.makeGitRefs()
         view = create_initialized_view(git_ref, "+new-snap")
         initial_values = view.initial_values
-        self.assertIn('store_name', initial_values)
-        self.assertIsNone(initial_values['store_name'])
+        self.assertIn("store_name", initial_values)
+        self.assertIsNone(initial_values["store_name"])
 
 
 class TestSnapAdminView(BaseTestSnapView):
-
     def test_unauthorized(self):
         # A non-admin user cannot administer a snap package.
         login_person(self.person)
@@ -742,16 +838,21 @@ class TestSnapAdminView(BaseTestSnapView):
         snap_url = canonical_url(snap)
         browser = self.getViewBrowser(snap, user=self.person)
         self.assertRaises(
-            LinkNotFoundError, browser.getLink, "Administer snap package")
+            LinkNotFoundError, browser.getLink, "Administer snap package"
+        )
         self.assertRaises(
-            Unauthorized, self.getUserBrowser, snap_url + "/+admin",
-            user=self.person)
+            Unauthorized,
+            self.getUserBrowser,
+            snap_url + "/+admin",
+            user=self.person,
+        )
 
     def test_admin_snap(self):
         # Admins can change require_virtualized, privacy, and allow_internet.
         login("admin@xxxxxxxxxxxxx")
         admin = self.factory.makePerson(
-            member_of=[getUtility(ILaunchpadCelebrities).admin])
+            member_of=[getUtility(ILaunchpadCelebrities).admin]
+        )
         login_person(self.person)
         project = self.factory.makeProduct(name="my-project")
         with person_logged_in(project.owner):
@@ -763,11 +864,12 @@ class TestSnapAdminView(BaseTestSnapView):
         self.assertTrue(snap.allow_internet)
 
         self.factory.makeAccessPolicy(
-            pillar=project, type=InformationType.PRIVATESECURITY)
+            pillar=project, type=InformationType.PRIVATESECURITY
+        )
         private = InformationType.PRIVATESECURITY.name
         browser = self.getViewBrowser(snap, user=admin)
         browser.getLink("Administer snap package").click()
-        browser.getControl(name='field.project').value = "my-project"
+        browser.getControl(name="field.project").value = "my-project"
         browser.getControl("Require virtualized builders").selected = False
         browser.getControl(name="field.information_type").value = private
         browser.getControl("Allow external network access").selected = False
@@ -784,51 +886,63 @@ class TestSnapAdminView(BaseTestSnapView):
         login_person(self.person)
         snap = self.factory.makeSnap(registrant=self.person)
         admin = self.factory.makePerson(
-            member_of=[getUtility(ILaunchpadCelebrities).admin])
+            member_of=[getUtility(ILaunchpadCelebrities).admin]
+        )
         private = InformationType.PRIVATESECURITY.name
         browser = self.getViewBrowser(snap, user=admin)
         browser.getLink("Administer snap package").click()
-        browser.getControl(name='field.project').value = None
+        browser.getControl(name="field.project").value = None
         browser.getControl(name="field.information_type").value = private
         browser.getControl("Update snap package").click()
         self.assertEqual(
-            'Private snap recipes must be associated with a project.',
-            extract_text(find_tags_by_class(browser.contents, "message")[1]))
+            "Private snap recipes must be associated with a project.",
+            extract_text(find_tags_by_class(browser.contents, "message")[1]),
+        )
 
     def test_admin_snap_privacy_mismatch(self):
         # Cannot make snap public if it still contains private information.
         login_person(self.person)
         team = self.factory.makeTeam(
             membership_policy=TeamMembershipPolicy.MODERATED,
-            owner=self.person, visibility=PersonVisibility.PRIVATE)
+            owner=self.person,
+            visibility=PersonVisibility.PRIVATE,
+        )
         project = self.factory.makeProduct(
             information_type=InformationType.PUBLIC,
-            branch_sharing_policy=BranchSharingPolicy.PUBLIC_OR_PROPRIETARY)
+            branch_sharing_policy=BranchSharingPolicy.PUBLIC_OR_PROPRIETARY,
+        )
         snap = self.factory.makeSnap(
-            registrant=self.person, owner=team, project=project,
-            information_type=InformationType.PRIVATESECURITY)
+            registrant=self.person,
+            owner=team,
+            project=project,
+            information_type=InformationType.PRIVATESECURITY,
+        )
         # Note that only LP admins or, in this case, commercial_admins
         # can reach this snap because it's owned by a private team.
         admin = self.factory.makePerson(
-            member_of=[getUtility(ILaunchpadCelebrities).admin])
+            member_of=[getUtility(ILaunchpadCelebrities).admin]
+        )
         public = InformationType.PUBLIC.name
         browser = self.getViewBrowser(snap, user=admin)
         browser.getLink("Administer snap package").click()
         browser.getControl(name="field.information_type").value = public
         browser.getControl("Update snap package").click()
         self.assertEqual(
-            'A public snap cannot have a private owner.',
-            extract_text(find_tags_by_class(browser.contents, "message")[1]))
+            "A public snap cannot have a private owner.",
+            extract_text(find_tags_by_class(browser.contents, "message")[1]),
+        )
 
     def test_admin_snap_sets_date_last_modified(self):
         # Administering a snap package sets the date_last_modified property.
         login("admin@xxxxxxxxxxxxx")
         ppa_admin = self.factory.makePerson(
-            member_of=[getUtility(ILaunchpadCelebrities).ppa_admin])
+            member_of=[getUtility(ILaunchpadCelebrities).ppa_admin]
+        )
         login_person(self.person)
         date_created = datetime(2000, 1, 1, tzinfo=pytz.UTC)
         snap = self.factory.makeSnap(
-            registrant=self.person, date_created=date_created)
+            registrant=self.person, date_created=date_created
+        )
         login_person(ppa_admin)
         view = SnapAdminView(snap, LaunchpadTestRequest())
         view.initialize()
@@ -837,27 +951,33 @@ class TestSnapAdminView(BaseTestSnapView):
 
 
 class TestSnapEditView(BaseTestSnapView):
-
     def setUp(self):
         super().setUp()
         self.distroseries = self.factory.makeUbuntuDistroSeries(
-            version="13.10")
+            version="13.10"
+        )
         with admin_logged_in():
             self.snappyseries = self.factory.makeSnappySeries(
-                usable_distro_series=[self.distroseries])
+                usable_distro_series=[self.distroseries]
+            )
 
     def test_edit_snap(self):
         old_series = self.factory.makeUbuntuDistroSeries()
         old_branch = self.factory.makeAnyBranch()
         snap = self.factory.makeSnap(
-            registrant=self.person, owner=self.person, distroseries=old_series,
-            branch=old_branch)
+            registrant=self.person,
+            owner=self.person,
+            distroseries=old_series,
+            branch=old_branch,
+        )
         self.factory.makeTeam(
-            name="new-team", displayname="New Team", members=[self.person])
+            name="new-team", displayname="New Team", members=[self.person]
+        )
         new_series = self.factory.makeUbuntuDistroSeries()
         with admin_logged_in():
             new_snappy_series = self.factory.makeSnappySeries(
-                usable_distro_series=[new_series])
+                usable_distro_series=[new_series]
+            )
         [new_git_ref] = self.factory.makeGitRefs()
         archive = self.factory.makeArchive()
 
@@ -866,160 +986,200 @@ class TestSnapEditView(BaseTestSnapView):
         browser.getControl("Owner").value = ["new-team"]
         browser.getControl(name="field.name").value = "new-name"
         browser.getControl(name="field.store_distro_series").value = [
-            "ubuntu/%s/%s" % (new_series.name, new_snappy_series.name)]
+            "ubuntu/%s/%s" % (new_series.name, new_snappy_series.name)
+        ]
         browser.getControl("Git", index=0).click()
-        browser.getControl(name="field.git_ref.repository").value = (
-            new_git_ref.repository.identity)
+        browser.getControl(
+            name="field.git_ref.repository"
+        ).value = new_git_ref.repository.identity
         browser.getControl(name="field.git_ref.path").value = new_git_ref.path
         browser.getControl("Build source tarball").selected = True
         browser.getControl(
-            "Automatically build when branch changes").selected = True
+            "Automatically build when branch changes"
+        ).selected = True
         browser.getControl("PPA").click()
-        browser.getControl(name="field.auto_build_archive.ppa").value = (
-            archive.reference)
+        browser.getControl(
+            name="field.auto_build_archive.ppa"
+        ).value = archive.reference
         browser.getControl("Pocket for automatic builds").value = ["SECURITY"]
         browser.getControl(
-            name="field.auto_build_channels.snapcraft").value = "edge"
+            name="field.auto_build_channels.snapcraft"
+        ).value = "edge"
         browser.getControl("Update snap package").click()
 
         content = find_main_content(browser.contents)
         self.assertEqual("new-name", extract_text(content.h1))
         self.assertThat("New Team", MatchesPickerText(content, "edit-owner"))
         self.assertThat(
-            "Distribution series:\n%s\nEdit snap package" %
-            new_series.fullseriesname,
-            MatchesTagText(content, "distro_series"))
+            "Distribution series:\n%s\nEdit snap package"
+            % new_series.fullseriesname,
+            MatchesTagText(content, "distro_series"),
+        )
         self.assertThat(
             "Source:\n%s\nEdit snap package" % new_git_ref.display_name,
-            MatchesTagText(content, "source"))
+            MatchesTagText(content, "source"),
+        )
         self.assertThat(
             "Build source tarball:\nYes\nEdit snap package",
-            MatchesTagText(content, "build_source_tarball"))
+            MatchesTagText(content, "build_source_tarball"),
+        )
         self.assertThat(
             "Build schedule:\n(?)\nBuilt automatically\nEdit snap package\n",
-            MatchesTagText(content, "auto_build"))
+            MatchesTagText(content, "auto_build"),
+        )
         self.assertThat(
-            "Source archive for automatic builds:\n%s\nEdit snap package\n" %
-            archive.displayname,
-            MatchesTagText(content, "auto_build_archive"))
+            "Source archive for automatic builds:\n%s\nEdit snap package\n"
+            % archive.displayname,
+            MatchesTagText(content, "auto_build_archive"),
+        )
         self.assertThat(
             "Pocket for automatic builds:\nSecurity\nEdit snap package",
-            MatchesTagText(content, "auto_build_pocket"))
+            MatchesTagText(content, "auto_build_pocket"),
+        )
         self.assertThat(
             "Source snap channels for automatic builds:\nEdit snap package\n"
             "snapcraft\nedge",
-            MatchesTagText(content, "auto_build_channels"))
+            MatchesTagText(content, "auto_build_channels"),
+        )
         self.assertThat(
             "Builds of this snap package are not automatically uploaded to "
             "the store.\nEdit snap package",
-            MatchesTagText(content, "store_upload"))
+            MatchesTagText(content, "store_upload"),
+        )
 
     def test_edit_snap_built_for_older_store_series(self):
         distro_series = self.factory.makeUbuntuDistroSeries()
         with admin_logged_in():
             snappy_series = self.factory.makeSnappySeries(
                 usable_distro_series=[distro_series],
-                status=SeriesStatus.SUPPORTED)
+                status=SeriesStatus.SUPPORTED,
+            )
         snap = self.factory.makeSnap(
-            registrant=self.person, owner=self.person,
+            registrant=self.person,
+            owner=self.person,
             distroseries=distro_series,
             store_series=snappy_series,
-            branch=self.factory.makeAnyBranch())
+            branch=self.factory.makeAnyBranch(),
+        )
         browser = self.getViewBrowser(snap, view_name="+edit", user=snap.owner)
-        browser.getControl(name="field.store_distro_series").value = (
-            "ubuntu/%s/%s" % (distro_series.name, snappy_series.name))
+        browser.getControl(
+            name="field.store_distro_series"
+        ).value = "ubuntu/%s/%s" % (distro_series.name, snappy_series.name)
         browser.getControl("Update snap package").click()
 
         self.assertEqual([], find_tags_by_class(browser.contents, "message"))
         login_person(self.person)
-        self.assertThat(snap, MatchesStructure.byEquality(
-            distro_series=distro_series,
-            store_series=snappy_series))
+        self.assertThat(
+            snap,
+            MatchesStructure.byEquality(
+                distro_series=distro_series, store_series=snappy_series
+            ),
+        )
 
     def test_edit_snap_built_for_distro_series_None(self):
         with admin_logged_in():
             snappy_series = self.factory.makeSnappySeries(
-                status=SeriesStatus.CURRENT)
+                status=SeriesStatus.CURRENT
+            )
         snap = self.factory.makeSnap(
-            registrant=self.person, owner=self.person,
+            registrant=self.person,
+            owner=self.person,
             distroseries=None,
-            store_series=snappy_series)
+            store_series=snappy_series,
+        )
         browser = self.getViewBrowser(snap, user=self.person)
         browser.getLink("Edit snap package").click()
-        browser.getControl(
-            name="field.store_distro_series").value = (
-                        browser.getControl(
-                            name="field.store_distro_series"
-                        ).options[0].strip())
+        browser.getControl(name="field.store_distro_series").value = (
+            browser.getControl(name="field.store_distro_series")
+            .options[0]
+            .strip()
+        )
         browser.getControl("Update snap package").click()
         self.assertEqual([], find_tags_by_class(browser.contents, "message"))
         login_person(self.person)
-        self.assertThat(snap, MatchesStructure(
-            distro_series=Is(None),
-            store_series=Equals(snappy_series)))
+        self.assertThat(
+            snap,
+            MatchesStructure(
+                distro_series=Is(None), store_series=Equals(snappy_series)
+            ),
+        )
 
     def test_edit_snap_built_for_snappy_series_None(self):
         distro_series = self.factory.makeUbuntuDistroSeries()
 
         snap = self.factory.makeSnap(
-            registrant=self.person, owner=self.person,
+            registrant=self.person,
+            owner=self.person,
             distroseries=distro_series,
-            store_series=None)
+            store_series=None,
+        )
 
         browser = self.getViewBrowser(snap, view_name="+edit", user=snap.owner)
         self.assertIn(
             "ubuntu/%s" % distro_series.name,
-            browser.getControl(name="field.store_distro_series").options)
-        browser.getControl(
-            name="field.store_distro_series").value = (
-                        "ubuntu/%s" % distro_series.name)
+            browser.getControl(name="field.store_distro_series").options,
+        )
+        browser.getControl(name="field.store_distro_series").value = (
+            "ubuntu/%s" % distro_series.name
+        )
         browser.getControl("Update snap package").click()
         self.assertEqual([], find_tags_by_class(browser.contents, "message"))
         login_person(self.person)
-        self.assertThat(snap, MatchesStructure(
-            distro_series=Equals(distro_series),
-            store_series=Is(None)))
+        self.assertThat(
+            snap,
+            MatchesStructure(
+                distro_series=Equals(distro_series), store_series=Is(None)
+            ),
+        )
 
     def test_edit_snap_built_for_distro_snappy_series_None(self):
         snap = self.factory.makeSnap(
-            registrant=self.person, owner=self.person,
+            registrant=self.person,
+            owner=self.person,
             distroseries=None,
-            store_series=None)
+            store_series=None,
+        )
 
         browser = self.getViewBrowser(snap, view_name="+edit", user=snap.owner)
         self.assertIn(
             "(unset)",
-            browser.getControl(name="field.store_distro_series").options)
-        browser.getControl(
-            name="field.store_distro_series").value = '(unset)'
+            browser.getControl(name="field.store_distro_series").options,
+        )
+        browser.getControl(name="field.store_distro_series").value = "(unset)"
         browser.getControl("Update snap package").click()
         self.assertEqual([], find_tags_by_class(browser.contents, "message"))
 
         login_person(self.person)
-        self.assertThat(snap, MatchesStructure(
-            distro_series=Is(None),
-            store_series=Is(None)))
+        self.assertThat(
+            snap,
+            MatchesStructure(distro_series=Is(None), store_series=Is(None)),
+        )
 
     def test_edit_snap_sets_date_last_modified(self):
         # Editing a snap package sets the date_last_modified property.
         date_created = datetime(2000, 1, 1, tzinfo=pytz.UTC)
         snap = self.factory.makeSnap(
-            registrant=self.person, date_created=date_created)
+            registrant=self.person, date_created=date_created
+        )
         with person_logged_in(self.person):
             view = SnapEditView(snap, LaunchpadTestRequest())
             view.initialize()
-            view.request_action.success({
-                "owner": snap.owner,
-                "name": "changed",
-                "distro_series": snap.distro_series,
-                })
+            view.request_action.success(
+                {
+                    "owner": snap.owner,
+                    "name": "changed",
+                    "distro_series": snap.distro_series,
+                }
+            )
         self.assertSqlAttributeEqualsDate(snap, "date_last_modified", UTC_NOW)
 
     def test_edit_snap_already_exists(self):
         snap = self.factory.makeSnap(
-            registrant=self.person, owner=self.person, name="one")
+            registrant=self.person, owner=self.person, name="one"
+        )
         self.factory.makeSnap(
-            registrant=self.person, owner=self.person, name="two")
+            registrant=self.person, owner=self.person, name="two"
+        )
         browser = self.getViewBrowser(snap, user=self.person)
         browser.getLink("Edit snap package").click()
         browser.getControl(name="field.name").value = "two"
@@ -1027,28 +1187,38 @@ class TestSnapEditView(BaseTestSnapView):
         self.assertEqual(
             "There is already a snap package owned by Test Person with this "
             "name.",
-            extract_text(find_tags_by_class(browser.contents, "message")[1]))
+            extract_text(find_tags_by_class(browser.contents, "message")[1]),
+        )
 
     def test_edit_snap_project_and_info_type(self):
         series = self.factory.makeUbuntuDistroSeries()
         with admin_logged_in():
             snappy_series = self.factory.makeSnappySeries(
-                usable_distro_series=[series])
+                usable_distro_series=[series]
+            )
         login_person(self.person)
         initial_project = self.factory.makeProduct(
-            name='initial-project',
-            owner=self.person, registrant=self.person,
+            name="initial-project",
+            owner=self.person,
+            registrant=self.person,
             information_type=InformationType.PUBLIC,
-            branch_sharing_policy=BranchSharingPolicy.PUBLIC_OR_PROPRIETARY)
+            branch_sharing_policy=BranchSharingPolicy.PUBLIC_OR_PROPRIETARY,
+        )
         snap = self.factory.makeSnap(
-            registrant=self.person, owner=self.person, project=initial_project,
-            distroseries=series, store_series=snappy_series,
-            information_type=InformationType.PUBLIC)
+            registrant=self.person,
+            owner=self.person,
+            project=initial_project,
+            distroseries=series,
+            store_series=snappy_series,
+            information_type=InformationType.PUBLIC,
+        )
         final_project = self.factory.makeProduct(
-            name='final-project',
-            owner=self.person, registrant=self.person,
+            name="final-project",
+            owner=self.person,
+            registrant=self.person,
             information_type=InformationType.PROPRIETARY,
-            branch_sharing_policy=BranchSharingPolicy.PROPRIETARY)
+            branch_sharing_policy=BranchSharingPolicy.PROPRIETARY,
+        )
         browser = self.getViewBrowser(snap, user=self.person)
         browser.getLink("Edit snap package").click()
         browser.getControl(name="field.project").value = "final-project"
@@ -1064,68 +1234,86 @@ class TestSnapEditView(BaseTestSnapView):
         series = self.factory.makeUbuntuDistroSeries()
         with admin_logged_in():
             snappy_series = self.factory.makeSnappySeries(
-                usable_distro_series=[series])
+                usable_distro_series=[series]
+            )
         login_person(self.person)
         private_project = self.factory.makeProduct(
-            name='private-project',
-            owner=self.person, registrant=self.person,
+            name="private-project",
+            owner=self.person,
+            registrant=self.person,
             information_type=InformationType.PROPRIETARY,
-            branch_sharing_policy=BranchSharingPolicy.PROPRIETARY)
+            branch_sharing_policy=BranchSharingPolicy.PROPRIETARY,
+        )
         snap = self.factory.makeSnap(
-            name='foo-snap', registrant=self.person, owner=self.person,
-            distroseries=series, store_series=snappy_series,
+            name="foo-snap",
+            registrant=self.person,
+            owner=self.person,
+            distroseries=series,
+            store_series=snappy_series,
             information_type=InformationType.PROPRIETARY,
-            project=private_project)
+            project=private_project,
+        )
         browser = self.getViewBrowser(snap, user=self.person)
         browser.getLink("Edit snap package").click()
-        browser.getControl(name="field.project").value = ''
-        browser.getControl(name="field.information_type").value = (
-            "PROPRIETARY")
+        browser.getControl(name="field.project").value = ""
+        browser.getControl(name="field.information_type").value = "PROPRIETARY"
         browser.getControl("Update snap package").click()
 
         messages = find_tags_by_class(browser.contents, "message")
         self.assertEqual(2, len(messages))
         top_msg, field_msg = messages
+        self.assertEqual("There is 1 error.", extract_text(top_msg))
         self.assertEqual(
-            'There is 1 error.', extract_text(top_msg))
-        self.assertEqual(
-            'Private snap recipes must be associated with a project.',
-            extract_text(field_msg))
+            "Private snap recipes must be associated with a project.",
+            extract_text(field_msg),
+        )
 
     def test_edit_snap_private_information_type_matches_project(self):
         series = self.factory.makeUbuntuDistroSeries()
         with admin_logged_in():
             snappy_series = self.factory.makeSnappySeries(
-                usable_distro_series=[series])
+                usable_distro_series=[series]
+            )
         login_person(self.person)
         private_project = self.factory.makeProduct(
-            name='private-project',
-            owner=self.person, registrant=self.person,
+            name="private-project",
+            owner=self.person,
+            registrant=self.person,
             information_type=InformationType.PROPRIETARY,
-            branch_sharing_policy=BranchSharingPolicy.PROPRIETARY)
+            branch_sharing_policy=BranchSharingPolicy.PROPRIETARY,
+        )
         snap = self.factory.makeSnap(
-            name='foo-snap', registrant=self.person, owner=self.person,
-            distroseries=series, store_series=snappy_series,
+            name="foo-snap",
+            registrant=self.person,
+            owner=self.person,
+            distroseries=series,
+            store_series=snappy_series,
             information_type=InformationType.PROPRIETARY,
-            project=private_project)
+            project=private_project,
+        )
         browser = self.getViewBrowser(snap, user=self.person)
         browser.getLink("Edit snap package").click()
 
         # Make sure we are only showing valid information type options:
         info_type_selector = browser.getControl(name="field.information_type")
-        self.assertEqual(['PROPRIETARY'], info_type_selector.options)
+        self.assertEqual(["PROPRIETARY"], info_type_selector.options)
 
     def test_edit_public_snap_private_owner(self):
         series = self.factory.makeUbuntuDistroSeries()
         with admin_logged_in():
             snappy_series = self.factory.makeSnappySeries(
-                usable_distro_series=[series])
+                usable_distro_series=[series]
+            )
         login_person(self.person)
         snap = self.factory.makeSnap(
-            registrant=self.person, owner=self.person, distroseries=series,
-            store_series=snappy_series)
+            registrant=self.person,
+            owner=self.person,
+            distroseries=series,
+            store_series=snappy_series,
+        )
         private_team = self.factory.makeTeam(
-            owner=self.person, visibility=PersonVisibility.PRIVATE)
+            owner=self.person, visibility=PersonVisibility.PRIVATE
+        )
         private_team_name = private_team.name
         browser = self.getViewBrowser(snap, user=self.person)
         browser.getLink("Edit snap package").click()
@@ -1133,32 +1321,40 @@ class TestSnapEditView(BaseTestSnapView):
         browser.getControl("Update snap package").click()
         self.assertEqual(
             "A public snap cannot have a private owner.",
-            extract_text(find_tags_by_class(browser.contents, "message")[1]))
+            extract_text(find_tags_by_class(browser.contents, "message")[1]),
+        )
 
     def test_edit_public_snap_make_private_in_one_go(self):
         # Move a public snap to a private owner and mark it private in one go
         series = self.factory.makeUbuntuDistroSeries()
         with admin_logged_in():
             snappy_series = self.factory.makeSnappySeries(
-                usable_distro_series=[series])
+                usable_distro_series=[series]
+            )
         login_person(self.person)
         private_project = self.factory.makeProduct(
-            name='private-project',
-            owner=self.person, registrant=self.person,
+            name="private-project",
+            owner=self.person,
+            registrant=self.person,
             information_type=InformationType.PROPRIETARY,
-            branch_sharing_policy=BranchSharingPolicy.PROPRIETARY)
+            branch_sharing_policy=BranchSharingPolicy.PROPRIETARY,
+        )
         snap = self.factory.makeSnap(
-            registrant=self.person, owner=self.person, distroseries=series,
-            store_series=snappy_series, project=private_project)
+            registrant=self.person,
+            owner=self.person,
+            distroseries=series,
+            store_series=snappy_series,
+            project=private_project,
+        )
         private_team = self.factory.makeTeam(
-            owner=self.person, visibility=PersonVisibility.PRIVATE)
+            owner=self.person, visibility=PersonVisibility.PRIVATE
+        )
         private_team_name = private_team.name
 
         browser = self.getViewBrowser(snap, user=self.person)
         browser.getLink("Edit snap package").click()
         browser.getControl("Owner").value = [private_team_name]
-        browser.getControl(name="field.information_type").value = (
-            "PROPRIETARY")
+        browser.getControl(name="field.information_type").value = "PROPRIETARY"
         browser.getControl("Update snap package").click()
 
         login_admin()
@@ -1168,14 +1364,19 @@ class TestSnapEditView(BaseTestSnapView):
         series = self.factory.makeUbuntuDistroSeries()
         with admin_logged_in():
             snappy_series = self.factory.makeSnappySeries(
-                usable_distro_series=[series])
+                usable_distro_series=[series]
+            )
         login_person(self.person)
         snap = self.factory.makeSnap(
-            registrant=self.person, owner=self.person, distroseries=series,
-            branch=self.factory.makeAnyBranch(), store_series=snappy_series)
-        private_branch = self.factory.makeAnyBranch(
+            registrant=self.person,
             owner=self.person,
-            information_type=InformationType.PRIVATESECURITY)
+            distroseries=series,
+            branch=self.factory.makeAnyBranch(),
+            store_series=snappy_series,
+        )
+        private_branch = self.factory.makeAnyBranch(
+            owner=self.person, information_type=InformationType.PRIVATESECURITY
+        )
         private_branch_name = private_branch.unique_name
         browser = self.getViewBrowser(snap, user=self.person)
         browser.getLink("Edit snap package").click()
@@ -1183,56 +1384,71 @@ class TestSnapEditView(BaseTestSnapView):
         browser.getControl("Update snap package").click()
         self.assertEqual(
             "A public snap cannot have a private branch.",
-            extract_text(find_tags_by_class(browser.contents, "message")[1]))
+            extract_text(find_tags_by_class(browser.contents, "message")[1]),
+        )
 
     def test_edit_public_snap_private_git_ref(self):
         series = self.factory.makeUbuntuDistroSeries()
         with admin_logged_in():
             snappy_series = self.factory.makeSnappySeries(
-                usable_distro_series=[series])
+                usable_distro_series=[series]
+            )
         login_person(self.person)
         snap = self.factory.makeSnap(
-            registrant=self.person, owner=self.person, distroseries=series,
-            git_ref=self.factory.makeGitRefs()[0], store_series=snappy_series)
+            registrant=self.person,
+            owner=self.person,
+            distroseries=series,
+            git_ref=self.factory.makeGitRefs()[0],
+            store_series=snappy_series,
+        )
         login_person(self.person)
         [private_ref] = self.factory.makeGitRefs(
-            owner=self.person,
-            information_type=InformationType.PRIVATESECURITY)
+            owner=self.person, information_type=InformationType.PRIVATESECURITY
+        )
         private_ref_identity = private_ref.repository.identity
         private_ref_path = private_ref.path
         browser = self.getViewBrowser(snap, user=self.person)
         browser.getLink("Edit snap package").click()
-        browser.getControl(name="field.git_ref.repository").value = (
-            private_ref_identity)
+        browser.getControl(
+            name="field.git_ref.repository"
+        ).value = private_ref_identity
         browser.getControl(name="field.git_ref.path").value = private_ref_path
         browser.getControl("Update snap package").click()
         self.assertEqual(
             "A public snap cannot have a private repository.",
-            extract_text(find_tags_by_class(browser.contents, "message")[1]))
+            extract_text(find_tags_by_class(browser.contents, "message")[1]),
+        )
 
     def test_edit_snap_git_url(self):
         series = self.factory.makeUbuntuDistroSeries()
         with admin_logged_in():
             snappy_series = self.factory.makeSnappySeries(
-                usable_distro_series=[series])
+                usable_distro_series=[series]
+            )
         old_ref = self.factory.makeGitRefRemote()
         new_ref = self.factory.makeGitRefRemote()
         new_repository_url = new_ref.repository_url
         new_path = new_ref.path
         snap = self.factory.makeSnap(
-            registrant=self.person, owner=self.person, distroseries=series,
-            git_ref=old_ref, store_series=snappy_series)
+            registrant=self.person,
+            owner=self.person,
+            distroseries=series,
+            git_ref=old_ref,
+            store_series=snappy_series,
+        )
         browser = self.getViewBrowser(snap, user=self.person)
         browser.getLink("Edit snap package").click()
         browser.getControl(
-            name="field.git_ref.repository").value = new_repository_url
+            name="field.git_ref.repository"
+        ).value = new_repository_url
         browser.getControl(name="field.git_ref.path").value = new_path
         browser.getControl("Update snap package").click()
         login_person(self.person)
         content = find_main_content(browser.contents)
         self.assertThat(
             "Source:\n%s\nEdit snap package" % new_ref.display_name,
-            MatchesTagText(content, "source"))
+            MatchesTagText(content, "source"),
+        )
 
     def setUpSeries(self):
         """Set up {distro,snappy}series with some available processors."""
@@ -1241,43 +1457,58 @@ class TestSnapEditView(BaseTestSnapView):
         for name in processor_names:
             processor = getUtility(IProcessorSet).getByName(name)
             self.factory.makeDistroArchSeries(
-                distroseries=distroseries, architecturetag=name,
-                processor=processor)
+                distroseries=distroseries,
+                architecturetag=name,
+                processor=processor,
+            )
         with admin_logged_in():
             snappyseries = self.factory.makeSnappySeries(
-                usable_distro_series=[distroseries])
+                usable_distro_series=[distroseries]
+            )
         return distroseries, snappyseries
 
     def assertSnapProcessors(self, snap, names):
         self.assertContentEqual(
-            names, [processor.name for processor in snap.processors])
+            names, [processor.name for processor in snap.processors]
+        )
 
     def assertProcessorControls(self, processors_control, enabled, disabled):
         matchers = [
             MatchesStructure.byEquality(optionValue=name, disabled=False)
-            for name in enabled]
-        matchers.extend([
-            MatchesStructure.byEquality(optionValue=name, disabled=True)
-            for name in disabled])
+            for name in enabled
+        ]
+        matchers.extend(
+            [
+                MatchesStructure.byEquality(optionValue=name, disabled=True)
+                for name in disabled
+            ]
+        )
         self.assertThat(processors_control.controls, MatchesSetwise(*matchers))
 
     def test_display_processors(self):
         distroseries, snappyseries = self.setUpSeries()
         snap = self.factory.makeSnap(
-            registrant=self.person, owner=self.person,
-            distroseries=distroseries, store_series=snappyseries)
+            registrant=self.person,
+            owner=self.person,
+            distroseries=distroseries,
+            store_series=snappyseries,
+        )
         browser = self.getViewBrowser(snap, view_name="+edit", user=snap.owner)
         processors = browser.getControl(name="field.processors")
         self.assertContentEqual(
             ["Intel 386 (386)", "AMD 64bit (amd64)", "HPPA Processor (hppa)"],
-            [extract_text(option) for option in processors.displayOptions])
+            [extract_text(option) for option in processors.displayOptions],
+        )
         self.assertContentEqual(["386", "amd64", "hppa"], processors.options)
 
     def test_edit_processors(self):
         distroseries, snappyseries = self.setUpSeries()
         snap = self.factory.makeSnap(
-            registrant=self.person, owner=self.person,
-            distroseries=distroseries, store_series=snappyseries)
+            registrant=self.person,
+            owner=self.person,
+            distroseries=distroseries,
+            store_series=snappyseries,
+        )
         self.assertSnapProcessors(snap, ["386", "amd64", "hppa"])
         browser = self.getViewBrowser(snap, view_name="+edit", user=snap.owner)
         processors = browser.getControl(name="field.processors")
@@ -1297,11 +1528,15 @@ class TestSnapEditView(BaseTestSnapView):
         proc_386 = getUtility(IProcessorSet).getByName("386")
         proc_amd64 = getUtility(IProcessorSet).getByName("amd64")
         proc_armel = self.factory.makeProcessor(
-            name="armel", restricted=True, build_by_default=False)
+            name="armel", restricted=True, build_by_default=False
+        )
         distroseries, snappyseries = self.setUpSeries()
         snap = self.factory.makeSnap(
-            registrant=self.person, owner=self.person,
-            distroseries=distroseries, store_series=snappyseries)
+            registrant=self.person,
+            owner=self.person,
+            distroseries=distroseries,
+            store_series=snappyseries,
+        )
         snap.setProcessors([proc_386, proc_amd64, proc_armel])
         browser = self.getViewBrowser(snap, view_name="+edit", user=snap.owner)
         processors = browser.getControl(name="field.processors")
@@ -1316,19 +1551,26 @@ class TestSnapEditView(BaseTestSnapView):
         # checkbox in the UI, and the processor cannot be enabled.
         distroseries, snappyseries = self.setUpSeries()
         proc_armhf = self.factory.makeProcessor(
-            name="armhf", restricted=True, build_by_default=False)
+            name="armhf", restricted=True, build_by_default=False
+        )
         self.factory.makeDistroArchSeries(
-            distroseries=distroseries, architecturetag="armhf",
-            processor=proc_armhf)
+            distroseries=distroseries,
+            architecturetag="armhf",
+            processor=proc_armhf,
+        )
         snap = self.factory.makeSnap(
-            registrant=self.person, owner=self.person,
-            distroseries=distroseries, store_series=snappyseries)
+            registrant=self.person,
+            owner=self.person,
+            distroseries=distroseries,
+            store_series=snappyseries,
+        )
         self.assertSnapProcessors(snap, ["386", "amd64", "hppa"])
         browser = self.getViewBrowser(snap, view_name="+edit", user=snap.owner)
         processors = browser.getControl(name="field.processors")
         self.assertContentEqual(["386", "amd64", "hppa"], processors.value)
         self.assertProcessorControls(
-            processors, ["386", "amd64", "hppa"], ["armhf"])
+            processors, ["386", "amd64", "hppa"], ["armhf"]
+        )
         # Even if the user works around the disabled checkbox and forcibly
         # enables it, they can't enable the restricted processor.
         for control in processors.controls:
@@ -1337,7 +1579,8 @@ class TestSnapEditView(BaseTestSnapView):
         processors.value = ["386", "amd64", "armhf"]
         self.assertRaises(
             CannotModifySnapProcessor,
-            browser.getControl("Update snap package").click)
+            browser.getControl("Update snap package").click,
+        )
 
     def test_edit_processors_restricted_already_enabled(self):
         # A restricted processor that is already enabled is shown with a
@@ -1347,23 +1590,31 @@ class TestSnapEditView(BaseTestSnapView):
         proc_386 = getUtility(IProcessorSet).getByName("386")
         proc_amd64 = getUtility(IProcessorSet).getByName("amd64")
         proc_armhf = self.factory.makeProcessor(
-            name="armhf", restricted=True, build_by_default=False)
+            name="armhf", restricted=True, build_by_default=False
+        )
         distroseries, snappyseries = self.setUpSeries()
         self.factory.makeDistroArchSeries(
-            distroseries=distroseries, architecturetag="armhf",
-            processor=proc_armhf)
+            distroseries=distroseries,
+            architecturetag="armhf",
+            processor=proc_armhf,
+        )
         snap = self.factory.makeSnap(
-            registrant=self.person, owner=self.person,
-            distroseries=distroseries, store_series=snappyseries)
+            registrant=self.person,
+            owner=self.person,
+            distroseries=distroseries,
+            store_series=snappyseries,
+        )
         snap.setProcessors([proc_386, proc_amd64, proc_armhf])
         self.assertSnapProcessors(snap, ["386", "amd64", "armhf"])
         browser = self.getUserBrowser(
-            canonical_url(snap) + "/+edit", user=snap.owner)
+            canonical_url(snap) + "/+edit", user=snap.owner
+        )
         processors = browser.getControl(name="field.processors")
         # armhf is checked but disabled.
         self.assertContentEqual(["386", "amd64", "armhf"], processors.value)
         self.assertProcessorControls(
-            processors, ["386", "amd64", "hppa"], ["armhf"])
+            processors, ["386", "amd64", "hppa"], ["armhf"]
+        )
         processors.value = ["386"]
         browser.getControl("Update snap package").click()
         login_person(self.person)
@@ -1374,8 +1625,11 @@ class TestSnapEditView(BaseTestSnapView):
         initial_kwargs.setdefault("store_series", self.snappyseries)
         initial_kwargs.setdefault("store_name", "one")
         snap = self.factory.makeSnap(
-            registrant=self.person, owner=self.person,
-            distroseries=self.distroseries, **initial_kwargs)
+            registrant=self.person,
+            owner=self.person,
+            distroseries=self.distroseries,
+            **initial_kwargs,
+        )
         view = create_initialized_view(snap, "+edit", principal=self.person)
         data.setdefault("store_upload", snap.store_upload)
         data.setdefault("store_distro_series", snap.store_distro_series)
@@ -1391,9 +1645,11 @@ class TestSnapEditView(BaseTestSnapView):
         # Changing the store series requires reauthorization.
         with admin_logged_in():
             new_snappyseries = self.factory.makeSnappySeries(
-                usable_distro_series=[self.distroseries])
+                usable_distro_series=[self.distroseries]
+            )
         sds = getUtility(ISnappyDistroSeriesSet).getByBothSeries(
-            new_snappyseries, self.distroseries)
+            new_snappyseries, self.distroseries
+        )
         self.assertNeedStoreReauth(True, {}, {"store_distro_series": sds})
 
     def test__needStoreReauth_different_name(self):
@@ -1408,17 +1664,22 @@ class TestSnapEditView(BaseTestSnapView):
         # case, we can't tell if store_series or store_name were also
         # changed in between, so reauthorizing is the conservative course.)
         self.assertNeedStoreReauth(
-            True, {"store_upload": False}, {"store_upload": True})
+            True, {"store_upload": False}, {"store_upload": True}
+        )
 
     @responses.activate
     def test_edit_store_upload(self):
         # Changing store upload settings on a snap sets all the appropriate
         # fields and redirects to SSO for reauthorization.
         snap = self.factory.makeSnap(
-            registrant=self.person, owner=self.person,
-            distroseries=self.distroseries, store_upload=True,
-            store_series=self.snappyseries, store_name="one",
-            store_channels=["track/edge"])
+            registrant=self.person,
+            owner=self.person,
+            distroseries=self.distroseries,
+            store_upload=True,
+            store_series=self.snappyseries,
+            store_name="one",
+            store_channels=["track/edge"],
+        )
         view_url = canonical_url(snap, view_name="+edit")
         browser = self.getNonRedirectingBrowser(url=view_url, user=self.person)
         browser.getControl("Registered store package name").value = "two"
@@ -1428,60 +1689,79 @@ class TestSnapEditView(BaseTestSnapView):
         browser.getControl("Stable").selected = True
         root_macaroon = Macaroon()
         root_macaroon.add_third_party_caveat(
-            urlsplit(config.launchpad.openid_provider_root).netloc, "",
-            "dummy")
+            urlsplit(config.launchpad.openid_provider_root).netloc, "", "dummy"
+        )
         root_macaroon_raw = root_macaroon.serialize()
         self.pushConfig("snappy", store_url="http://sca.example/";)
         responses.add(
-            "POST", "http://sca.example/dev/api/acl/";,
-            json={"macaroon": root_macaroon_raw})
+            "POST",
+            "http://sca.example/dev/api/acl/";,
+            json={"macaroon": root_macaroon_raw},
+        )
         browser.getControl("Update snap package").click()
         login_person(self.person)
-        self.assertThat(snap, MatchesStructure.byEquality(
-            store_name="two", store_secrets={"root": root_macaroon_raw},
-            store_channels=["stable", "edge"]))
+        self.assertThat(
+            snap,
+            MatchesStructure.byEquality(
+                store_name="two",
+                store_secrets={"root": root_macaroon_raw},
+                store_channels=["stable", "edge"],
+            ),
+        )
         [call] = responses.calls
-        self.assertThat(call.request, MatchesStructure.byEquality(
-            url="http://sca.example/dev/api/acl/";, method="POST"))
+        self.assertThat(
+            call.request,
+            MatchesStructure.byEquality(
+                url="http://sca.example/dev/api/acl/";, method="POST"
+            ),
+        )
         expected_body = {
             "packages": [{"name": "two", "series": self.snappyseries.name}],
             "permissions": ["package_upload"],
-            }
+        }
         self.assertEqual(
-            expected_body, json.loads(call.request.body.decode("UTF-8")))
+            expected_body, json.loads(call.request.body.decode("UTF-8"))
+        )
         self.assertEqual(303, int(browser.headers["Status"].split(" ", 1)[0]))
         parsed_location = urlsplit(browser.headers["Location"])
         self.assertEqual(
             urlsplit(canonical_url(snap) + "/+authorize/+login")[:3],
-            parsed_location[:3])
+            parsed_location[:3],
+        )
         expected_args = {
             "discharge_macaroon_action": ["field.actions.complete"],
             "discharge_macaroon_field": ["field.discharge_macaroon"],
             "macaroon_caveat_id": ["dummy"],
-            }
+        }
         self.assertEqual(expected_args, parse_qs(parsed_location[3]))
 
 
 class TestSnapAuthorizeView(BaseTestSnapView):
-
     def setUp(self):
         super().setUp()
         self.distroseries = self.factory.makeUbuntuDistroSeries()
         with admin_logged_in():
             self.snappyseries = self.factory.makeSnappySeries(
-                usable_distro_series=[self.distroseries])
+                usable_distro_series=[self.distroseries]
+            )
         self.snap = self.factory.makeSnap(
-            registrant=self.person, owner=self.person,
-            distroseries=self.distroseries, store_upload=True,
+            registrant=self.person,
+            owner=self.person,
+            distroseries=self.distroseries,
+            store_upload=True,
             store_series=self.snappyseries,
-            store_name=self.factory.getUniqueUnicode())
+            store_name=self.factory.getUniqueUnicode(),
+        )
 
     def test_unauthorized(self):
         # A user without edit access cannot authorize snap package uploads.
         other_person = self.factory.makePerson()
         self.assertRaises(
-            Unauthorized, self.getUserBrowser,
-            canonical_url(self.snap) + "/+authorize", user=other_person)
+            Unauthorized,
+            self.getUserBrowser,
+            canonical_url(self.snap) + "/+authorize",
+            user=other_person,
+        )
 
     @responses.activate
     def test_begin_authorization(self):
@@ -1492,37 +1772,49 @@ class TestSnapAuthorizeView(BaseTestSnapView):
         owner = self.snap.owner
         root_macaroon = Macaroon()
         root_macaroon.add_third_party_caveat(
-            urlsplit(config.launchpad.openid_provider_root).netloc, '',
-            'dummy')
+            urlsplit(config.launchpad.openid_provider_root).netloc, "", "dummy"
+        )
         root_macaroon_raw = root_macaroon.serialize()
         self.pushConfig("snappy", store_url="http://sca.example/";)
         responses.add(
-            "POST", "http://sca.example/dev/api/acl/";,
-            json={"macaroon": root_macaroon_raw})
+            "POST",
+            "http://sca.example/dev/api/acl/";,
+            json={"macaroon": root_macaroon_raw},
+        )
         browser = self.getNonRedirectingBrowser(
-            url=snap_url + "/+authorize", user=self.snap.owner)
+            url=snap_url + "/+authorize", user=self.snap.owner
+        )
         browser.getControl("Begin authorization").click()
         [call] = responses.calls
-        self.assertThat(call.request, MatchesStructure.byEquality(
-            url="http://sca.example/dev/api/acl/";, method="POST"))
+        self.assertThat(
+            call.request,
+            MatchesStructure.byEquality(
+                url="http://sca.example/dev/api/acl/";, method="POST"
+            ),
+        )
         with person_logged_in(owner):
             expected_body = {
-                "packages": [{
-                    "name": self.snap.store_name,
-                    "series": self.snap.store_series.name,
-                    }],
+                "packages": [
+                    {
+                        "name": self.snap.store_name,
+                        "series": self.snap.store_series.name,
+                    }
+                ],
                 "permissions": ["package_upload"],
-                }
+            }
             self.assertEqual(
-                expected_body, json.loads(call.request.body.decode("UTF-8")))
+                expected_body, json.loads(call.request.body.decode("UTF-8"))
+            )
             self.assertEqual(
-                {"root": root_macaroon_raw}, self.snap.store_secrets)
+                {"root": root_macaroon_raw}, self.snap.store_secrets
+            )
         self.assertEqual(303, int(browser.headers["Status"].split(" ", 1)[0]))
         self.assertEqual(
             snap_url + "/+authorize/+login?macaroon_caveat_id=dummy&"
             "discharge_macaroon_action=field.actions.complete&"
             "discharge_macaroon_field=field.discharge_macaroon",
-            browser.headers["Location"])
+            browser.headers["Location"],
+        )
 
     def test_complete_authorization_missing_discharge_macaroon(self):
         # If the form does not include a discharge macaroon, the "complete"
@@ -1532,13 +1824,18 @@ class TestSnapAuthorizeView(BaseTestSnapView):
             transaction.commit()
             form = {"field.actions.complete": "1"}
             view = create_initialized_view(
-                self.snap, "+authorize", form=form, method="POST",
-                principal=self.snap.owner)
+                self.snap,
+                "+authorize",
+                form=form,
+                method="POST",
+                principal=self.snap.owner,
+            )
             html = view()
             self.assertEqual(
-                "Uploads of %s to the store were not authorized." %
-                self.snap.name,
-                get_feedback_messages(html)[1])
+                "Uploads of %s to the store were not authorized."
+                % self.snap.name,
+                get_feedback_messages(html)[1],
+            )
             self.assertNotIn("discharge", self.snap.store_secrets)
 
     def test_complete_authorization(self):
@@ -1552,27 +1849,35 @@ class TestSnapAuthorizeView(BaseTestSnapView):
             form = {
                 "field.actions.complete": "1",
                 "field.discharge_macaroon": discharge_macaroon.serialize(),
-                }
+            }
             view = create_initialized_view(
-                self.snap, "+authorize", form=form, method="POST",
-                principal=self.snap.owner)
+                self.snap,
+                "+authorize",
+                form=form,
+                method="POST",
+                principal=self.snap.owner,
+            )
             self.assertEqual("", view())
             self.assertEqual(302, view.request.response.getStatus())
             self.assertEqual(
                 canonical_url(self.snap),
-                view.request.response.getHeader("Location"))
+                view.request.response.getHeader("Location"),
+            )
             self.assertEqual(
-                "Uploads of %s to the store are now authorized." %
-                self.snap.name,
-                view.request.response.notifications[0].message)
+                "Uploads of %s to the store are now authorized."
+                % self.snap.name,
+                view.request.response.notifications[0].message,
+            )
             self.assertEqual(
-                {"root": root_macaroon.serialize(),
-                 "discharge": discharge_macaroon.serialize()},
-                self.snap.store_secrets)
+                {
+                    "root": root_macaroon.serialize(),
+                    "discharge": discharge_macaroon.serialize(),
+                },
+                self.snap.store_secrets,
+            )
 
 
 class TestSnapDeleteView(BaseTestSnapView):
-
     def test_unauthorized(self):
         # A user without edit access cannot delete a snap package.
         snap = self.factory.makeSnap(registrant=self.person, owner=self.person)
@@ -1580,10 +1885,14 @@ class TestSnapDeleteView(BaseTestSnapView):
         other_person = self.factory.makePerson()
         browser = self.getViewBrowser(snap, user=other_person)
         self.assertRaises(
-            LinkNotFoundError, browser.getLink, "Delete snap package")
+            LinkNotFoundError, browser.getLink, "Delete snap package"
+        )
         self.assertRaises(
-            Unauthorized, self.getUserBrowser, snap_url + "/+delete",
-            user=other_person)
+            Unauthorized,
+            self.getUserBrowser,
+            snap_url + "/+delete",
+            user=other_person,
+        )
 
     def test_delete_snap_without_builds(self):
         # A snap package without builds can be deleted.
@@ -1611,16 +1920,18 @@ class TestSnapDeleteView(BaseTestSnapView):
 
 
 class TestSnapView(BaseTestSnapView):
-
     def setUp(self):
         super().setUp()
         self.ubuntu = getUtility(ILaunchpadCelebrities).ubuntu
         self.distroseries = self.factory.makeDistroSeries(
-            distribution=self.ubuntu, name="shiny", displayname="Shiny")
+            distribution=self.ubuntu, name="shiny", displayname="Shiny"
+        )
         processor = getUtility(IProcessorSet).getByName("386")
         self.distroarchseries = self.factory.makeDistroArchSeries(
-            distroseries=self.distroseries, architecturetag="i386",
-            processor=processor)
+            distroseries=self.distroseries,
+            architecturetag="i386",
+            processor=processor,
+        )
         self.factory.makeBuilder(virtualized=True)
 
     def makeSnap(self, **kwargs):
@@ -1629,8 +1940,11 @@ class TestSnapView(BaseTestSnapView):
         if kwargs.get("branch") is None and kwargs.get("git_ref") is None:
             kwargs["branch"] = self.factory.makeAnyBranch()
         return self.factory.makeSnap(
-            registrant=self.person, owner=self.person, name="snap-name",
-            **kwargs)
+            registrant=self.person,
+            owner=self.person,
+            name="snap-name",
+            **kwargs,
+        )
 
     def makeBuild(self, snap=None, archive=None, date_created=None, **kwargs):
         if snap is None:
@@ -1640,9 +1954,13 @@ class TestSnapView(BaseTestSnapView):
         if date_created is None:
             date_created = datetime.now(pytz.UTC) - timedelta(hours=1)
         return self.factory.makeSnapBuild(
-            requester=self.person, snap=snap, archive=archive,
-            distroarchseries=self.distroarchseries, date_created=date_created,
-            **kwargs)
+            requester=self.person,
+            snap=snap,
+            archive=archive,
+            distroarchseries=self.distroarchseries,
+            date_created=date_created,
+            **kwargs,
+        )
 
     def test_breadcrumb(self):
         snap = self.makeSnap()
@@ -1651,41 +1969,55 @@ class TestSnapView(BaseTestSnapView):
         view.request.traversed_objects = [self.person, snap, view]
         view.initialize()
         breadcrumbs_tag = soupmatchers.Tag(
-            "breadcrumbs", "ol", attrs={"class": "breadcrumbs"})
+            "breadcrumbs", "ol", attrs={"class": "breadcrumbs"}
+        )
         self.assertThat(
             view(),
             soupmatchers.HTMLContains(
                 soupmatchers.Within(
                     breadcrumbs_tag,
                     soupmatchers.Tag(
-                        "snap collection breadcrumb", "a",
+                        "snap collection breadcrumb",
+                        "a",
                         text="Snap packages",
                         attrs={
                             "href": re.compile(r"/~test-person/\+snaps$"),
-                            })),
+                        },
+                    ),
+                ),
                 soupmatchers.Within(
                     breadcrumbs_tag,
                     soupmatchers.Tag(
-                        "snap breadcrumb", "li",
-                        text=re.compile(r"\ssnap-name\s")))))
+                        "snap breadcrumb",
+                        "li",
+                        text=re.compile(r"\ssnap-name\s"),
+                    ),
+                ),
+            ),
+        )
 
     def test_snap_with_project_pillar_url(self):
         project = self.factory.makeProduct()
         snap = self.factory.makeSnap(project=project)
         browser = self.getViewBrowser(snap)
         with admin_logged_in():
-            expected_url = 'http://launchpad.test/~{}/{}/+snap/{}'.format(
-                snap.owner.name, project.name, snap.name)
+            expected_url = "http://launchpad.test/~{}/{}/+snap/{}".format(
+                snap.owner.name, project.name, snap.name
+            )
         self.assertEqual(expected_url, browser.url)
 
     def test_index_bzr(self):
         branch = self.factory.makePersonalBranch(
-            owner=self.person, name="snap-branch")
+            owner=self.person, name="snap-branch"
+        )
         snap = self.makeSnap(branch=branch)
         build = self.makeBuild(
-            snap=snap, status=BuildStatus.FULLYBUILT,
-            duration=timedelta(minutes=30))
-        self.assertTextMatchesExpressionIgnoreWhitespace(r"""\
+            snap=snap,
+            status=BuildStatus.FULLYBUILT,
+            duration=timedelta(minutes=30),
+        )
+        self.assertTextMatchesExpressionIgnoreWhitespace(
+            r"""\
             Snap packages snap-name
             .*
             Snap package information
@@ -1703,17 +2035,25 @@ class TestSnapView(BaseTestSnapView):
             Status When complete Architecture Archive
             Successfully built 30 minutes ago i386
             Primary Archive for Ubuntu Linux
-            """, self.getMainText(build.snap))
+            """,
+            self.getMainText(build.snap),
+        )
 
     def test_index_git(self):
         [ref] = self.factory.makeGitRefs(
-            owner=self.person, target=self.person, name="snap-repository",
-            paths=["refs/heads/master"])
+            owner=self.person,
+            target=self.person,
+            name="snap-repository",
+            paths=["refs/heads/master"],
+        )
         snap = self.makeSnap(git_ref=ref)
         build = self.makeBuild(
-            snap=snap, status=BuildStatus.FULLYBUILT,
-            duration=timedelta(minutes=30))
-        self.assertTextMatchesExpressionIgnoreWhitespace(r"""\
+            snap=snap,
+            status=BuildStatus.FULLYBUILT,
+            duration=timedelta(minutes=30),
+        )
+        self.assertTextMatchesExpressionIgnoreWhitespace(
+            r"""\
             Snap packages snap-name
             .*
             Snap package information
@@ -1731,23 +2071,31 @@ class TestSnapView(BaseTestSnapView):
             Status When complete Architecture Archive
             Successfully built 30 minutes ago i386
             Primary Archive for Ubuntu Linux
-            """, self.getMainText(build.snap))
+            """,
+            self.getMainText(build.snap),
+        )
 
     def test_index_for_subscriber_without_git_repo_access(self):
         [ref] = self.factory.makeGitRefs(
-            owner=self.person, target=self.person, name="snap-repository",
+            owner=self.person,
+            target=self.person,
+            name="snap-repository",
             paths=["refs/heads/master"],
-            information_type=InformationType.PRIVATESECURITY)
+            information_type=InformationType.PRIVATESECURITY,
+        )
         snap = self.makeSnap(git_ref=ref, private=True)
         with admin_logged_in():
             self.makeBuild(
-                snap=snap, status=BuildStatus.FULLYBUILT,
-                duration=timedelta(minutes=30))
+                snap=snap,
+                status=BuildStatus.FULLYBUILT,
+                duration=timedelta(minutes=30),
+            )
 
         subscriber = self.factory.makePerson()
         with person_logged_in(self.person):
             snap.subscribe(subscriber, self.person)
-        self.assertTextMatchesExpressionIgnoreWhitespace(r"""\
+        self.assertTextMatchesExpressionIgnoreWhitespace(
+            r"""\
             Snap packages snap-name
             .*
             Snap package information
@@ -1765,24 +2113,33 @@ class TestSnapView(BaseTestSnapView):
             Status When complete Architecture Archive
             Successfully built 30 minutes ago i386
             Primary Archive for Ubuntu Linux
-            """, self.getMainText(snap, user=subscriber))
+            """,
+            self.getMainText(snap, user=subscriber),
+        )
 
     def test_index_for_subscriber_without_archive_access(self):
         [ref] = self.factory.makeGitRefs(
-            owner=self.person, target=self.person, name="snap-repository",
+            owner=self.person,
+            target=self.person,
+            name="snap-repository",
             paths=["refs/heads/master"],
-            information_type=InformationType.PRIVATESECURITY)
+            information_type=InformationType.PRIVATESECURITY,
+        )
         snap = self.makeSnap(git_ref=ref, private=True)
         with admin_logged_in():
             archive = self.factory.makeArchive(private=True)
             self.makeBuild(
-                snap=snap, status=BuildStatus.FULLYBUILT, archive=archive,
-                duration=timedelta(minutes=30))
+                snap=snap,
+                status=BuildStatus.FULLYBUILT,
+                archive=archive,
+                duration=timedelta(minutes=30),
+            )
 
         subscriber = self.factory.makePerson()
         with person_logged_in(self.person):
             snap.subscribe(subscriber, self.person)
-        self.assertTextMatchesExpressionIgnoreWhitespace(r"""\
+        self.assertTextMatchesExpressionIgnoreWhitespace(
+            r"""\
             Snap packages snap-name
             .*
             Snap package information
@@ -1799,17 +2156,23 @@ class TestSnapView(BaseTestSnapView):
             Latest builds
             Status When complete Architecture Archive
             This snap package has not been built yet.
-            """, self.getMainText(snap, user=subscriber))
+            """,
+            self.getMainText(snap, user=subscriber),
+        )
 
     def test_index_git_url(self):
         ref = self.factory.makeGitRefRemote(
             repository_url="https://git.example.org/foo";,
-            path="refs/heads/master")
+            path="refs/heads/master",
+        )
         snap = self.makeSnap(git_ref=ref)
         build = self.makeBuild(
-            snap=snap, status=BuildStatus.FULLYBUILT,
-            duration=timedelta(minutes=30))
-        self.assertTextMatchesExpressionIgnoreWhitespace(r"""\
+            snap=snap,
+            status=BuildStatus.FULLYBUILT,
+            duration=timedelta(minutes=30),
+        )
+        self.assertTextMatchesExpressionIgnoreWhitespace(
+            r"""\
             Snap packages snap-name
             .*
             Snap package information
@@ -1827,7 +2190,9 @@ class TestSnapView(BaseTestSnapView):
             Status When complete Architecture Archive
             Successfully built 30 minutes ago i386
             Primary Archive for Ubuntu Linux
-            """, self.getMainText(build.snap))
+            """,
+            self.getMainText(build.snap),
+        )
 
     def test_index_no_distro_series(self):
         # If the snap is configured to infer an appropriate distro series
@@ -1841,14 +2206,18 @@ class TestSnapView(BaseTestSnapView):
     def test_index_success_with_buildlog(self):
         # The build log is shown if it is there.
         build = self.makeBuild(
-            status=BuildStatus.FULLYBUILT, duration=timedelta(minutes=30))
+            status=BuildStatus.FULLYBUILT, duration=timedelta(minutes=30)
+        )
         build.setLog(self.factory.makeLibraryFileAlias())
-        self.assertTextMatchesExpressionIgnoreWhitespace(r"""\
+        self.assertTextMatchesExpressionIgnoreWhitespace(
+            r"""\
             Latest builds
             Status When complete Architecture Archive
             Successfully built 30 minutes ago buildlog \(.*\) i386
             Primary Archive for Ubuntu Linux
-            """, self.getMainText(build.snap))
+            """,
+            self.getMainText(build.snap),
+        )
 
     def test_index_hides_builds_into_private_archive(self):
         # The index page hides builds into archives the user can't view.
@@ -1856,81 +2225,108 @@ class TestSnapView(BaseTestSnapView):
         with person_logged_in(archive.owner):
             snap = self.makeBuild(archive=archive).snap
         self.assertIn(
-            "This snap package has not been built yet.",
-            self.getMainText(snap))
+            "This snap package has not been built yet.", self.getMainText(snap)
+        )
 
     def test_index_no_builds(self):
         # A message is shown when there are no builds.
         snap = self.factory.makeSnap()
         self.assertIn(
-            "This snap package has not been built yet.",
-            self.getMainText(snap))
+            "This snap package has not been built yet.", self.getMainText(snap)
+        )
 
     def test_index_pending_build(self):
         # A pending build is listed as such.
         build = self.makeBuild()
         build.queueBuild()
-        self.assertTextMatchesExpressionIgnoreWhitespace(r"""\
+        self.assertTextMatchesExpressionIgnoreWhitespace(
+            r"""\
             Latest builds
             Status When complete Architecture Archive
             Needs building in .* \(estimated\) i386
             Primary Archive for Ubuntu Linux
-            """, self.getMainText(build.snap))
+            """,
+            self.getMainText(build.snap),
+        )
 
     def test_index_pending_build_request(self):
         # A pending build request is listed as such.
         snap = self.makeSnap()
         with person_logged_in(snap.owner):
             snap.requestBuilds(
-                snap.owner, snap.distro_series.main_archive,
-                PackagePublishingPocket.UPDATES)
-        self.assertTextMatchesExpressionIgnoreWhitespace("""\
+                snap.owner,
+                snap.distro_series.main_archive,
+                PackagePublishingPocket.UPDATES,
+            )
+        self.assertTextMatchesExpressionIgnoreWhitespace(
+            """\
             Latest builds
             Status When complete Architecture Archive
             Pending build request
             Primary Archive for Ubuntu Linux
-            """, self.getMainText(snap))
+            """,
+            self.getMainText(snap),
+        )
 
     def test_index_failed_build_request(self):
         # A failed build request is listed as such, with its error message.
         snap = self.makeSnap()
         with person_logged_in(snap.owner):
             request = snap.requestBuilds(
-                snap.owner, snap.distro_series.main_archive,
-                PackagePublishingPocket.UPDATES)
+                snap.owner,
+                snap.distro_series.main_archive,
+                PackagePublishingPocket.UPDATES,
+            )
         job = removeSecurityProxy(removeSecurityProxy(request)._job)
         job.job._status = JobStatus.FAILED
         job.job.date_finished = datetime.now(pytz.UTC) - timedelta(hours=1)
         job.error_message = "Boom"
-        self.assertTextMatchesExpressionIgnoreWhitespace(r"""\
+        self.assertTextMatchesExpressionIgnoreWhitespace(
+            r"""\
             Latest builds
             Status When complete Architecture Archive
             Failed build request 1 hour ago \(Boom\)
             Primary Archive for Ubuntu Linux
-            """, self.getMainText(snap))
+            """,
+            self.getMainText(snap),
+        )
 
     def test_index_store_upload(self):
         # If the snap package is to be automatically uploaded to the store,
         # the index page shows details of this.
         with admin_logged_in():
             snappyseries = self.factory.makeSnappySeries(
-                usable_distro_series=[self.distroseries])
+                usable_distro_series=[self.distroseries]
+            )
         snap = self.makeSnap(
-            store_upload=True, store_series=snappyseries,
-            store_name=self.getUniqueString("store-name"))
+            store_upload=True,
+            store_series=snappyseries,
+            store_name=self.getUniqueString("store-name"),
+        )
         view = create_initialized_view(snap, "+index")
         store_upload_tag = soupmatchers.Tag(
-            "store upload", "div", attrs={"id": "store_upload"})
-        self.assertThat(view(), soupmatchers.HTMLContains(
-            soupmatchers.Tag(
-                "distribution series", "dl", attrs={"id": "distro_series"}),
-            soupmatchers.Within(
-                store_upload_tag,
+            "store upload", "div", attrs={"id": "store_upload"}
+        )
+        self.assertThat(
+            view(),
+            soupmatchers.HTMLContains(
                 soupmatchers.Tag(
-                    "store series name", "span", text=snappyseries.title)),
-            soupmatchers.Within(
-                store_upload_tag,
-                soupmatchers.Tag("store name", "span", text=snap.store_name))))
+                    "distribution series", "dl", attrs={"id": "distro_series"}
+                ),
+                soupmatchers.Within(
+                    store_upload_tag,
+                    soupmatchers.Tag(
+                        "store series name", "span", text=snappyseries.title
+                    ),
+                ),
+                soupmatchers.Within(
+                    store_upload_tag,
+                    soupmatchers.Tag(
+                        "store name", "span", text=snap.store_name
+                    ),
+                ),
+            ),
+        )
 
     def test_index_store_upload_no_distro_series(self):
         # If the snap package is to be automatically uploaded to the store
@@ -1939,39 +2335,62 @@ class TestSnapView(BaseTestSnapView):
         with admin_logged_in():
             snappyseries = self.factory.makeSnappySeries(
                 usable_distro_series=[self.distroseries],
-                can_infer_distro_series=True)
+                can_infer_distro_series=True,
+            )
         snap = self.makeSnap(
-            distroseries=None, store_upload=True, store_series=snappyseries,
-            store_name=self.getUniqueString("store-name"))
+            distroseries=None,
+            store_upload=True,
+            store_series=snappyseries,
+            store_name=self.getUniqueString("store-name"),
+        )
         view = create_initialized_view(snap, "+index")
         store_upload_tag = soupmatchers.Tag(
-            "store upload", "div", attrs={"id": "store_upload"})
-        self.assertThat(view(), soupmatchers.HTMLContains(
-            Not(soupmatchers.Tag(
-                "distribution series", "dl", attrs={"id": "distro_series"})),
-            soupmatchers.Within(
-                store_upload_tag,
-                soupmatchers.Tag(
-                    "store series name", "span", text=snappyseries.title)),
-            soupmatchers.Within(
-                store_upload_tag,
-                soupmatchers.Tag("store name", "span", text=snap.store_name))))
+            "store upload", "div", attrs={"id": "store_upload"}
+        )
+        self.assertThat(
+            view(),
+            soupmatchers.HTMLContains(
+                Not(
+                    soupmatchers.Tag(
+                        "distribution series",
+                        "dl",
+                        attrs={"id": "distro_series"},
+                    )
+                ),
+                soupmatchers.Within(
+                    store_upload_tag,
+                    soupmatchers.Tag(
+                        "store series name", "span", text=snappyseries.title
+                    ),
+                ),
+                soupmatchers.Within(
+                    store_upload_tag,
+                    soupmatchers.Tag(
+                        "store name", "span", text=snap.store_name
+                    ),
+                ),
+            ),
+        )
 
     def setStatus(self, build, status):
         build.updateStatus(
-            BuildStatus.BUILDING, date_started=build.date_created)
+            BuildStatus.BUILDING, date_started=build.date_created
+        )
         build.updateStatus(
-            status, date_finished=build.date_started + timedelta(minutes=30))
+            status, date_finished=build.date_started + timedelta(minutes=30)
+        )
 
     def test_builds_and_requests(self):
         # SnapView.builds_and_requests produces reasonable results.
         snap = self.makeSnap()
         # Create oldest builds first so that they sort properly by id.
         date_gen = time_counter(
-            datetime(2000, 1, 1, tzinfo=pytz.UTC), timedelta(days=1))
+            datetime(2000, 1, 1, tzinfo=pytz.UTC), timedelta(days=1)
+        )
         builds = [
             self.makeBuild(snap=snap, date_created=next(date_gen))
-            for i in range(11)]
+            for i in range(11)
+        ]
         view = SnapView(snap, None)
         self.assertEqual(list(reversed(builds)), view.builds_and_requests)
         self.setStatus(builds[10], BuildStatus.FULLYBUILT)
@@ -1980,8 +2399,8 @@ class TestSnapView(BaseTestSnapView):
         # When there are >= 9 pending builds, only the most recent of any
         # completed builds is returned.
         self.assertEqual(
-            list(reversed(builds[:9])) + [builds[10]],
-            view.builds_and_requests)
+            list(reversed(builds[:9])) + [builds[10]], view.builds_and_requests
+        )
         for build in builds[:9]:
             self.setStatus(build, BuildStatus.FULLYBUILT)
         del get_property_cache(view).builds_and_requests
@@ -1992,48 +2411,67 @@ class TestSnapView(BaseTestSnapView):
         # builds.
         snap = self.makeSnap()
         date_gen = time_counter(
-            datetime(2000, 1, 1, tzinfo=pytz.UTC), timedelta(days=1))
+            datetime(2000, 1, 1, tzinfo=pytz.UTC), timedelta(days=1)
+        )
         builds = [
             self.makeBuild(snap=snap, date_created=next(date_gen))
-            for i in range(3)]
+            for i in range(3)
+        ]
         self.setStatus(builds[2], BuildStatus.FULLYBUILT)
         with person_logged_in(snap.owner):
             request = snap.requestBuilds(
-                snap.owner, snap.distro_series.main_archive,
-                PackagePublishingPocket.UPDATES)
+                snap.owner,
+                snap.distro_series.main_archive,
+                PackagePublishingPocket.UPDATES,
+            )
         job = removeSecurityProxy(removeSecurityProxy(request)._job)
         job.job.date_created = next(date_gen)
         view = SnapView(snap, None)
         # The pending build request is interleaved in date order with
         # pending builds, and these are followed by completed builds.
-        self.assertThat(view.builds_and_requests, MatchesListwise([
-            MatchesStructure.byEquality(id=request.id),
-            Equals(builds[1]),
-            Equals(builds[0]),
-            Equals(builds[2]),
-            ]))
+        self.assertThat(
+            view.builds_and_requests,
+            MatchesListwise(
+                [
+                    MatchesStructure.byEquality(id=request.id),
+                    Equals(builds[1]),
+                    Equals(builds[0]),
+                    Equals(builds[2]),
+                ]
+            ),
+        )
         transaction.commit()
         builds.append(self.makeBuild(snap=snap))
         del get_property_cache(view).builds_and_requests
-        self.assertThat(view.builds_and_requests, MatchesListwise([
-            Equals(builds[3]),
-            MatchesStructure.byEquality(id=request.id),
-            Equals(builds[1]),
-            Equals(builds[0]),
-            Equals(builds[2]),
-            ]))
+        self.assertThat(
+            view.builds_and_requests,
+            MatchesListwise(
+                [
+                    Equals(builds[3]),
+                    MatchesStructure.byEquality(id=request.id),
+                    Equals(builds[1]),
+                    Equals(builds[0]),
+                    Equals(builds[2]),
+                ]
+            ),
+        )
         # If we pretend that the job failed, it is still listed, but after
         # any pending builds.
         job.job._status = JobStatus.FAILED
         job.job.date_finished = job.date_created + timedelta(minutes=30)
         del get_property_cache(view).builds_and_requests
-        self.assertThat(view.builds_and_requests, MatchesListwise([
-            Equals(builds[3]),
-            Equals(builds[1]),
-            Equals(builds[0]),
-            MatchesStructure.byEquality(id=request.id),
-            Equals(builds[2]),
-            ]))
+        self.assertThat(
+            view.builds_and_requests,
+            MatchesListwise(
+                [
+                    Equals(builds[3]),
+                    Equals(builds[1]),
+                    Equals(builds[0]),
+                    MatchesStructure.byEquality(id=request.id),
+                    Equals(builds[2]),
+                ]
+            ),
+        )
 
     def test_store_channels_empty(self):
         snap = self.factory.makeSnap()
@@ -2042,10 +2480,12 @@ class TestSnapView(BaseTestSnapView):
 
     def test_store_channels_display(self):
         snap = self.factory.makeSnap(
-            store_channels=["track/stable/fix-123", "track/edge/fix-123"])
+            store_channels=["track/stable/fix-123", "track/edge/fix-123"]
+        )
         view = create_initialized_view(snap, "+index")
         self.assertEqual(
-            "track/stable/fix-123, track/edge/fix-123", view.store_channels)
+            "track/stable/fix-123, track/edge/fix-123", view.store_channels
+        )
 
     def test_authorize_navigation_no_store_secrets(self):
         # A snap with no store secrets has an "Authorize store uploads"
@@ -2062,8 +2502,10 @@ class TestSnapView(BaseTestSnapView):
         # navigation link.
         owner = self.factory.makePerson()
         snap = self.factory.makeSnap(
-            registrant=owner, owner=owner,
-            store_secrets={"root": Macaroon().serialize()})
+            registrant=owner,
+            owner=owner,
+            store_secrets={"root": Macaroon().serialize()},
+        )
         authorize_url = canonical_url(snap, view_name="+authorize")
         browser = self.getViewBrowser(snap, user=owner)
         authorize_link = browser.getLink("Reauthorize store uploads")
@@ -2071,26 +2513,32 @@ class TestSnapView(BaseTestSnapView):
 
 
 class TestSnapRequestBuildsView(BaseTestSnapView):
-
     def setUp(self):
         super().setUp()
         self.ubuntu = getUtility(ILaunchpadCelebrities).ubuntu
         self.distroseries = self.factory.makeDistroSeries(
-            distribution=self.ubuntu, name="shiny", displayname="Shiny")
+            distribution=self.ubuntu, name="shiny", displayname="Shiny"
+        )
         self.architectures = []
         for processor, architecture in ("386", "i386"), ("amd64", "amd64"):
             das = self.factory.makeDistroArchSeries(
-                distroseries=self.distroseries, architecturetag=architecture,
-                processor=getUtility(IProcessorSet).getByName(processor))
+                distroseries=self.distroseries,
+                architecturetag=architecture,
+                processor=getUtility(IProcessorSet).getByName(processor),
+            )
             das.addOrUpdateChroot(self.factory.makeLibraryFileAlias())
             self.architectures.append(das)
         self.snap = self.factory.makeSnap(
-            registrant=self.person, owner=self.person,
-            distroseries=self.distroseries, name="snap-name")
+            registrant=self.person,
+            owner=self.person,
+            distroseries=self.distroseries,
+            name="snap-name",
+        )
 
     def test_request_builds_page(self):
         # The +request-builds page is sane.
-        self.assertTextMatchesExpressionIgnoreWhitespace(r"""
+        self.assertTextMatchesExpressionIgnoreWhitespace(
+            r"""
             Request builds for snap-name
             Snap packages
             snap-name
@@ -2130,45 +2578,56 @@ class TestSnapRequestBuildsView(BaseTestSnapView):
             or
             Cancel
             """,
-            self.getMainText(self.snap, "+request-builds", user=self.person))
+            self.getMainText(self.snap, "+request-builds", user=self.person),
+        )
 
     def test_request_builds_not_owner(self):
         # A user without launchpad.Edit cannot request builds.
         self.assertRaises(
-            Unauthorized, self.getViewBrowser, self.snap, "+request-builds")
+            Unauthorized, self.getViewBrowser, self.snap, "+request-builds"
+        )
 
     def test_request_builds_with_architectures_action(self):
         # Requesting a build with architectures selected creates a pending
         # build request limited to those architectures.
         browser = self.getViewBrowser(
-            self.snap, "+request-builds", user=self.person)
+            self.snap, "+request-builds", user=self.person
+        )
         browser.getControl("amd64").selected = True
         browser.getControl("i386").selected = True
         browser.getControl("Request builds").click()
 
         login_person(self.person)
         [request] = self.snap.pending_build_requests
-        self.assertThat(removeSecurityProxy(request), MatchesStructure(
-            snap=Equals(self.snap),
-            status=Equals(SnapBuildRequestStatus.PENDING),
-            error_message=Is(None),
-            builds=AfterPreprocessing(list, Equals([])),
-            archive=Equals(self.ubuntu.main_archive),
-            _job=MatchesStructure(
-                requester=Equals(self.person),
-                pocket=Equals(PackagePublishingPocket.UPDATES),
-                channels=Equals({}),
-                architectures=MatchesSetwise(Equals("amd64"), Equals("i386")),
-                )))
+        self.assertThat(
+            removeSecurityProxy(request),
+            MatchesStructure(
+                snap=Equals(self.snap),
+                status=Equals(SnapBuildRequestStatus.PENDING),
+                error_message=Is(None),
+                builds=AfterPreprocessing(list, Equals([])),
+                archive=Equals(self.ubuntu.main_archive),
+                _job=MatchesStructure(
+                    requester=Equals(self.person),
+                    pocket=Equals(PackagePublishingPocket.UPDATES),
+                    channels=Equals({}),
+                    architectures=MatchesSetwise(
+                        Equals("amd64"), Equals("i386")
+                    ),
+                ),
+            ),
+        )
 
     def test_request_builds_with_architectures_ppa(self):
         # Selecting a different archive with architectures selected creates
         # a build request targeting that archive and limited to those
         # architectures.
         ppa = self.factory.makeArchive(
-            distribution=self.ubuntu, owner=self.person, name="snap-ppa")
+            distribution=self.ubuntu, owner=self.person, name="snap-ppa"
+        )
         browser = self.getViewBrowser(
-            self.snap, "+request-builds", user=self.person)
+            self.snap, "+request-builds", user=self.person
+        )
         browser.getControl("PPA").click()
         browser.getControl(name="field.archive.ppa").value = ppa.reference
         browser.getControl("amd64").selected = True
@@ -2177,16 +2636,21 @@ class TestSnapRequestBuildsView(BaseTestSnapView):
 
         login_person(self.person)
         [request] = self.snap.pending_build_requests
-        self.assertThat(request, MatchesStructure(
-            archive=Equals(ppa),
-            architectures=MatchesSetwise(Equals("amd64"))))
+        self.assertThat(
+            request,
+            MatchesStructure(
+                archive=Equals(ppa),
+                architectures=MatchesSetwise(Equals("amd64")),
+            ),
+        )
 
     def test_request_builds_with_architectures_channels(self):
         # Selecting different channels with architectures selected creates a
         # build request using those channels and limited to those
         # architectures.
         browser = self.getViewBrowser(
-            self.snap, "+request-builds", user=self.person)
+            self.snap, "+request-builds", user=self.person
+        )
         browser.getControl(name="field.channels.core").value = "edge"
         browser.getControl("amd64").selected = True
         self.assertFalse(browser.getControl("i386").selected)
@@ -2194,40 +2658,52 @@ class TestSnapRequestBuildsView(BaseTestSnapView):
 
         login_person(self.person)
         [request] = self.snap.pending_build_requests
-        self.assertThat(request, MatchesStructure(
-            channels=MatchesDict({"core": Equals("edge")}),
-            architectures=MatchesSetwise(Equals("amd64"))))
+        self.assertThat(
+            request,
+            MatchesStructure(
+                channels=MatchesDict({"core": Equals("edge")}),
+                architectures=MatchesSetwise(Equals("amd64")),
+            ),
+        )
 
     def test_request_builds_no_architectures_action(self):
         # Requesting a build with no architectures selected creates a
         # pending build request.
         browser = self.getViewBrowser(
-            self.snap, "+request-builds", user=self.person)
+            self.snap, "+request-builds", user=self.person
+        )
         self.assertFalse(browser.getControl("amd64").selected)
         self.assertFalse(browser.getControl("i386").selected)
         browser.getControl("Request builds").click()
 
         login_person(self.person)
         [request] = self.snap.pending_build_requests
-        self.assertThat(removeSecurityProxy(request), MatchesStructure(
-            snap=Equals(self.snap),
-            status=Equals(SnapBuildRequestStatus.PENDING),
-            error_message=Is(None),
-            builds=AfterPreprocessing(list, Equals([])),
-            archive=Equals(self.ubuntu.main_archive),
-            _job=MatchesStructure(
-                requester=Equals(self.person),
-                pocket=Equals(PackagePublishingPocket.UPDATES),
-                channels=Equals({}),
-                architectures=Is(None))))
+        self.assertThat(
+            removeSecurityProxy(request),
+            MatchesStructure(
+                snap=Equals(self.snap),
+                status=Equals(SnapBuildRequestStatus.PENDING),
+                error_message=Is(None),
+                builds=AfterPreprocessing(list, Equals([])),
+                archive=Equals(self.ubuntu.main_archive),
+                _job=MatchesStructure(
+                    requester=Equals(self.person),
+                    pocket=Equals(PackagePublishingPocket.UPDATES),
+                    channels=Equals({}),
+                    architectures=Is(None),
+                ),
+            ),
+        )
 
     def test_request_builds_no_architectures_ppa(self):
         # Selecting a different archive with no architectures selected
         # creates a build request targeting that archive.
         ppa = self.factory.makeArchive(
-            distribution=self.ubuntu, owner=self.person, name="snap-ppa")
+            distribution=self.ubuntu, owner=self.person, name="snap-ppa"
+        )
         browser = self.getViewBrowser(
-            self.snap, "+request-builds", user=self.person)
+            self.snap, "+request-builds", user=self.person
+        )
         browser.getControl("PPA").click()
         browser.getControl(name="field.archive.ppa").value = ppa.reference
         self.assertFalse(browser.getControl("amd64").selected)
@@ -2236,15 +2712,17 @@ class TestSnapRequestBuildsView(BaseTestSnapView):
 
         login_person(self.person)
         [request] = self.snap.pending_build_requests
-        self.assertThat(request, MatchesStructure(
-            archive=Equals(ppa),
-            architectures=Is(None)))
+        self.assertThat(
+            request,
+            MatchesStructure(archive=Equals(ppa), architectures=Is(None)),
+        )
 
     def test_request_builds_no_architectures_channels(self):
         # Selecting different channels with no architectures selected
         # creates a build request using those channels.
         browser = self.getViewBrowser(
-            self.snap, "+request-builds", user=self.person)
+            self.snap, "+request-builds", user=self.person
+        )
         browser.getControl(name="field.channels.core").value = "edge"
         self.assertFalse(browser.getControl("amd64").selected)
         self.assertFalse(browser.getControl("i386").selected)
@@ -2260,18 +2738,24 @@ class TestSnapRequestBuildsView(BaseTestSnapView):
         login_person(self.person)
         self.snap.distro_series = None
         browser = self.getViewBrowser(
-            self.snap, "+request-builds", user=self.person)
+            self.snap, "+request-builds", user=self.person
+        )
         browser.getControl("Request builds").click()
 
         login_person(self.person)
         [request] = self.snap.pending_build_requests
-        self.assertThat(removeSecurityProxy(request), MatchesStructure(
-            snap=Equals(self.snap),
-            status=Equals(SnapBuildRequestStatus.PENDING),
-            error_message=Is(None),
-            builds=AfterPreprocessing(list, Equals([])),
-            archive=Equals(self.ubuntu.main_archive),
-            _job=MatchesStructure(
-                requester=Equals(self.person),
-                pocket=Equals(PackagePublishingPocket.UPDATES),
-                channels=Equals({}))))
+        self.assertThat(
+            removeSecurityProxy(request),
+            MatchesStructure(
+                snap=Equals(self.snap),
+                status=Equals(SnapBuildRequestStatus.PENDING),
+                error_message=Is(None),
+                builds=AfterPreprocessing(list, Equals([])),
+                archive=Equals(self.ubuntu.main_archive),
+                _job=MatchesStructure(
+                    requester=Equals(self.person),
+                    pocket=Equals(PackagePublishingPocket.UPDATES),
+                    channels=Equals({}),
+                ),
+            ),
+        )
diff --git a/lib/lp/snappy/browser/tests/test_snapbuild.py b/lib/lp/snappy/browser/tests/test_snapbuild.py
index c6a6c56..5c25aaf 100644
--- a/lib/lp/snappy/browser/tests/test_snapbuild.py
+++ b/lib/lp/snappy/browser/tests/test_snapbuild.py
@@ -5,15 +5,12 @@
 
 import re
 
+import soupmatchers
+import transaction
 from fixtures import FakeLogger
 from pymacaroons import Macaroon
-import soupmatchers
 from storm.locals import Store
-from testtools.matchers import (
-    Not,
-    StartsWith,
-    )
-import transaction
+from testtools.matchers import Not, StartsWith
 from zope.component import getUtility
 from zope.security.interfaces import Unauthorized
 from zope.security.proxy import removeSecurityProxy
@@ -26,23 +23,20 @@ from lp.services.job.interfaces.job import JobStatus
 from lp.services.webapp import canonical_url
 from lp.snappy.interfaces.snapbuildjob import ISnapStoreUploadJobSource
 from lp.testing import (
-    admin_logged_in,
     ANONYMOUS,
     BrowserTestCase,
+    TestCaseWithFactory,
+    admin_logged_in,
     login,
     person_logged_in,
-    TestCaseWithFactory,
-    )
-from lp.testing.layers import (
-    DatabaseFunctionalLayer,
-    LaunchpadFunctionalLayer,
-    )
+)
+from lp.testing.layers import DatabaseFunctionalLayer, LaunchpadFunctionalLayer
 from lp.testing.pages import (
     extract_text,
     find_main_content,
     find_tag_by_id,
     find_tags_by_class,
-    )
+)
 from lp.testing.views import create_initialized_view
 
 
@@ -53,11 +47,13 @@ class TestCanonicalUrlForSnapBuild(TestCaseWithFactory):
     def test_canonical_url(self):
         owner = self.factory.makePerson(name="person")
         snap = self.factory.makeSnap(
-            registrant=owner, owner=owner, name="snap")
+            registrant=owner, owner=owner, name="snap"
+        )
         build = self.factory.makeSnapBuild(requester=owner, snap=snap)
         self.assertThat(
             canonical_url(build),
-            StartsWith("http://launchpad.test/~person/+snap/snap/+build/";))
+            StartsWith("http://launchpad.test/~person/+snap/snap/+build/";),
+        )
 
 
 class TestSnapBuildView(TestCaseWithFactory):
@@ -71,7 +67,8 @@ class TestSnapBuildView(TestCaseWithFactory):
         build_view = create_initialized_view(build, "+index")
         self.assertEqual(
             [snapfile.libraryfile.filename],
-            [lfa.filename for lfa in build_view.files])
+            [lfa.filename for lfa in build_view.files],
+        )
         # Deleted files won't be included.
         self.assertFalse(snapfile.libraryfile.deleted)
         removeSecurityProxy(snapfile.libraryfile).content = None
@@ -82,12 +79,20 @@ class TestSnapBuildView(TestCaseWithFactory):
     def test_revision_id(self):
         build = self.factory.makeSnapBuild()
         build.updateStatus(
-            BuildStatus.FULLYBUILT, worker_status={"revision_id": "dummy"})
+            BuildStatus.FULLYBUILT, worker_status={"revision_id": "dummy"}
+        )
         build_view = create_initialized_view(build, "+index")
-        self.assertThat(build_view(), soupmatchers.HTMLContains(
-            soupmatchers.Tag(
-                "revision ID", "li", attrs={"id": "revision-id"},
-                text=re.compile(r"^\s*VCS revision: dummy\s*$"))))
+        self.assertThat(
+            build_view(),
+            soupmatchers.HTMLContains(
+                soupmatchers.Tag(
+                    "revision ID",
+                    "li",
+                    attrs={"id": "revision-id"},
+                    text=re.compile(r"^\s*VCS revision: dummy\s*$"),
+                )
+            ),
+        )
 
     def test_no_store_status_if_not_fully_built(self):
         build = self.factory.makeSnapBuild(status=BuildStatus.NEEDSBUILD)
@@ -103,125 +108,213 @@ class TestSnapBuildView(TestCaseWithFactory):
 
     def test_store_upload_status_in_progress(self):
         build = self.factory.makeSnapBuild(
-            status=BuildStatus.FULLYBUILT, store_name="foo")
+            status=BuildStatus.FULLYBUILT, store_name="foo"
+        )
         getUtility(ISnapStoreUploadJobSource).create(build)
         build_view = create_initialized_view(build, "+index")
         store_status = find_tag_by_id(build_view(), "store-status")
-        self.assertThat(store_status, soupmatchers.Tag(
-            "store upload status", "dd",
-            attrs={"id": "store-upload-status"},
-            text=re.compile(r"^\s*Store upload in progress\s*$")))
+        self.assertThat(
+            store_status,
+            soupmatchers.Tag(
+                "store upload status",
+                "dd",
+                attrs={"id": "store-upload-status"},
+                text=re.compile(r"^\s*Store upload in progress\s*$"),
+            ),
+        )
 
     def test_store_upload_status_completed(self):
         build = self.factory.makeSnapBuild(
-            status=BuildStatus.FULLYBUILT, store_name="foo")
+            status=BuildStatus.FULLYBUILT, store_name="foo"
+        )
         job = getUtility(ISnapStoreUploadJobSource).create(build)
         naked_job = removeSecurityProxy(job)
         naked_job.job._status = JobStatus.COMPLETED
         naked_job.store_url = "http://sca.example/dev/click-apps/1/rev/1/";
         build_view = create_initialized_view(build, "+index")
         store_status = find_tag_by_id(build_view(), "store-status")
-        self.assertThat(store_status, soupmatchers.Within(
-            soupmatchers.Tag(
-                "store upload status", "dd",
-                attrs={"id": "store-upload-status"}),
-            soupmatchers.Tag(
-                "store link", "a", attrs={"href": job.store_url},
-                text=re.compile(
-                    r"^\s*Manage this package in the store\s*$"))))
+        self.assertThat(
+            store_status,
+            soupmatchers.Within(
+                soupmatchers.Tag(
+                    "store upload status",
+                    "dd",
+                    attrs={"id": "store-upload-status"},
+                ),
+                soupmatchers.Tag(
+                    "store link",
+                    "a",
+                    attrs={"href": job.store_url},
+                    text=re.compile(
+                        r"^\s*Manage this package in the store\s*$"
+                    ),
+                ),
+            ),
+        )
 
     def test_store_upload_status_completed_no_url(self):
         # A package that has been uploaded to the store may lack a URL if
         # the upload was queued behind others for manual review.
         build = self.factory.makeSnapBuild(
-            status=BuildStatus.FULLYBUILT, store_name="foo")
+            status=BuildStatus.FULLYBUILT, store_name="foo"
+        )
         job = getUtility(ISnapStoreUploadJobSource).create(build)
         naked_job = removeSecurityProxy(job)
         naked_job.job._status = JobStatus.COMPLETED
         build_view = create_initialized_view(build, "+index")
         store_status = find_tag_by_id(build_view(), "store-status")
-        self.assertThat(store_status, soupmatchers.Within(
-            soupmatchers.Tag(
-                "store upload status", "dd",
-                attrs={"id": "store-upload-status"}),
-            soupmatchers.Tag(
-                "store link", "a", attrs={"href": config.snappy.store_url},
-                text=re.compile(r"^\s*Uploaded to the store\s*$"))))
+        self.assertThat(
+            store_status,
+            soupmatchers.Within(
+                soupmatchers.Tag(
+                    "store upload status",
+                    "dd",
+                    attrs={"id": "store-upload-status"},
+                ),
+                soupmatchers.Tag(
+                    "store link",
+                    "a",
+                    attrs={"href": config.snappy.store_url},
+                    text=re.compile(r"^\s*Uploaded to the store\s*$"),
+                ),
+            ),
+        )
 
     def test_store_upload_status_failed(self):
         build = self.factory.makeSnapBuild(
-            status=BuildStatus.FULLYBUILT, store_name="foo")
+            status=BuildStatus.FULLYBUILT, store_name="foo"
+        )
         job = getUtility(ISnapStoreUploadJobSource).create(build)
         naked_job = removeSecurityProxy(job)
         naked_job.job._status = JobStatus.FAILED
         naked_job.error_message = "Scan failed."
         build_view = create_initialized_view(build, "+index")
         store_status = find_tag_by_id(build_view(), "store-status")
-        self.assertThat(store_status, soupmatchers.Tag(
-            "store upload status", "dd",
-            attrs={"id": "store-upload-status"}))
-        self.assertThat(store_status, soupmatchers.Within(
-            soupmatchers.Tag(
-                "store upload error messages", "ul",
-                attrs={"id": "store-upload-error-messages"}),
+        self.assertThat(
+            store_status,
             soupmatchers.Tag(
-                "store upload error message", "li",
-                text=re.compile(r"^\s*Scan failed.\s*$"))))
+                "store upload status",
+                "dd",
+                attrs={"id": "store-upload-status"},
+            ),
+        )
+        self.assertThat(
+            store_status,
+            soupmatchers.Within(
+                soupmatchers.Tag(
+                    "store upload error messages",
+                    "ul",
+                    attrs={"id": "store-upload-error-messages"},
+                ),
+                soupmatchers.Tag(
+                    "store upload error message",
+                    "li",
+                    text=re.compile(r"^\s*Scan failed.\s*$"),
+                ),
+            ),
+        )
 
     def test_store_upload_status_failed_with_extended_error_message(self):
         build = self.factory.makeSnapBuild(
-            status=BuildStatus.FULLYBUILT, store_name="foo")
+            status=BuildStatus.FULLYBUILT, store_name="foo"
+        )
         job = getUtility(ISnapStoreUploadJobSource).create(build)
         naked_job = removeSecurityProxy(job)
         naked_job.job._status = JobStatus.FAILED
         naked_job.error_message = "This should not be shown."
         naked_job.error_messages = [
-            {"message": (
-                "The new version submitted for 'name' does not match the "
-                "upload ('other-name').")},
+            {
+                "message": (
+                    "The new version submitted for 'name' does not match the "
+                    "upload ('other-name')."
+                )
+            },
             {"message": "Scan failed.", "link": "link1"},
-            {"message": "Classic not allowed.", "link": "link2"}]
+            {"message": "Classic not allowed.", "link": "link2"},
+        ]
         build_view = create_initialized_view(build, "+index")
         store_status = find_tag_by_id(build_view(), "store-status")
-        self.assertThat(store_status, Not(soupmatchers.Tag(
-            "store upload status", "dd",
-            attrs={"id": "store-upload-status"},
-            text=re.compile('.*This should not be shown.*'))))
+        self.assertThat(
+            store_status,
+            Not(
+                soupmatchers.Tag(
+                    "store upload status",
+                    "dd",
+                    attrs={"id": "store-upload-status"},
+                    text=re.compile(".*This should not be shown.*"),
+                )
+            ),
+        )
         error_messages = soupmatchers.Tag(
-            "store upload error messages", "ul",
-            attrs={"id": "store-upload-error-messages"})
-        self.assertThat(store_status, soupmatchers.Within(
-            soupmatchers.Tag(
-                "store upload status", "dd",
-                attrs={"id": "store-upload-status"}),
-            error_messages))
-        self.assertThat(store_status, soupmatchers.Within(
-            error_messages,
-            soupmatchers.Tag(
-                "store upload error message", "li",
-                text=re.compile(".*The new version.*"))))
-        self.assertThat(store_status, soupmatchers.Within(
-            error_messages,
+            "store upload error messages",
+            "ul",
+            attrs={"id": "store-upload-error-messages"},
+        )
+        self.assertThat(
+            store_status,
             soupmatchers.Within(
                 soupmatchers.Tag(
-                    "store upload error message", "li",
-                    text=re.compile(r".*Scan failed\..*")),
-                soupmatchers.Tag(
-                    "store upload error link", "a",
-                    attrs={"href": "link1"}, text="(?)"))))
-        self.assertThat(store_status, soupmatchers.Within(
-            error_messages,
+                    "store upload status",
+                    "dd",
+                    attrs={"id": "store-upload-status"},
+                ),
+                error_messages,
+            ),
+        )
+        self.assertThat(
+            store_status,
             soupmatchers.Within(
+                error_messages,
                 soupmatchers.Tag(
-                    "store upload error message", "li",
-                    text=re.compile(r".*Classic not allowed\..*")),
-                soupmatchers.Tag(
-                    "store upload error link", "a",
-                    attrs={"href": "link2"}, text="(?)"))))
+                    "store upload error message",
+                    "li",
+                    text=re.compile(".*The new version.*"),
+                ),
+            ),
+        )
+        self.assertThat(
+            store_status,
+            soupmatchers.Within(
+                error_messages,
+                soupmatchers.Within(
+                    soupmatchers.Tag(
+                        "store upload error message",
+                        "li",
+                        text=re.compile(r".*Scan failed\..*"),
+                    ),
+                    soupmatchers.Tag(
+                        "store upload error link",
+                        "a",
+                        attrs={"href": "link1"},
+                        text="(?)",
+                    ),
+                ),
+            ),
+        )
+        self.assertThat(
+            store_status,
+            soupmatchers.Within(
+                error_messages,
+                soupmatchers.Within(
+                    soupmatchers.Tag(
+                        "store upload error message",
+                        "li",
+                        text=re.compile(r".*Classic not allowed\..*"),
+                    ),
+                    soupmatchers.Tag(
+                        "store upload error link",
+                        "a",
+                        attrs={"href": "link2"},
+                        text="(?)",
+                    ),
+                ),
+            ),
+        )
 
     def test_store_upload_status_release_failed(self):
         build = self.factory.makeSnapBuild(
-            status=BuildStatus.FULLYBUILT, store_name="foo")
+            status=BuildStatus.FULLYBUILT, store_name="foo"
+        )
         job = getUtility(ISnapStoreUploadJobSource).create(build)
         naked_job = removeSecurityProxy(job)
         naked_job.job._status = JobStatus.FAILED
@@ -229,15 +322,23 @@ class TestSnapBuildView(TestCaseWithFactory):
         naked_job.error_message = "Failed to publish"
         build_view = create_initialized_view(build, "+index")
         store_status = find_tag_by_id(build_view(), "store-status")
-        self.assertThat(store_status, soupmatchers.Within(
-            soupmatchers.Tag(
-                "store upload status", "dd",
-                attrs={"id": "store-upload-status"},
-                text=re.compile(
-                    r"^\s*Releasing package to channels failed:\s+"
-                    r"Failed to publish\s*$")),
-            soupmatchers.Tag(
-                "store link", "a", attrs={"href": job.store_url})))
+        self.assertThat(
+            store_status,
+            soupmatchers.Within(
+                soupmatchers.Tag(
+                    "store upload status",
+                    "dd",
+                    attrs={"id": "store-upload-status"},
+                    text=re.compile(
+                        r"^\s*Releasing package to channels failed:\s+"
+                        r"Failed to publish\s*$"
+                    ),
+                ),
+                soupmatchers.Tag(
+                    "store link", "a", attrs={"href": job.store_url}
+                ),
+            ),
+        )
 
 
 class TestSnapBuildOperations(BrowserTestCase):
@@ -251,7 +352,8 @@ class TestSnapBuildOperations(BrowserTestCase):
         self.build_url = canonical_url(self.build)
         self.requester = self.build.requester
         self.buildd_admin = self.factory.makePerson(
-            member_of=[getUtility(ILaunchpadCelebrities).buildd_admin])
+            member_of=[getUtility(ILaunchpadCelebrities).buildd_admin]
+        )
 
     def test_retry_build(self):
         # The requester of a build can retry it.
@@ -272,10 +374,14 @@ class TestSnapBuildOperations(BrowserTestCase):
         user = self.factory.makePerson()
         browser = self.getViewBrowser(self.build, user=user)
         self.assertRaises(
-            LinkNotFoundError, browser.getLink, "Retry this build")
+            LinkNotFoundError, browser.getLink, "Retry this build"
+        )
         self.assertRaises(
-            Unauthorized, self.getUserBrowser, self.build_url + "/+retry",
-            user=user)
+            Unauthorized,
+            self.getUserBrowser,
+            self.build_url + "/+retry",
+            user=user,
+        )
 
     def test_retry_build_wrong_state(self):
         # If the build isn't in an unsuccessful terminal state, you can't
@@ -283,7 +389,8 @@ class TestSnapBuildOperations(BrowserTestCase):
         self.build.updateStatus(BuildStatus.FULLYBUILT)
         browser = self.getViewBrowser(self.build, user=self.requester)
         self.assertRaises(
-            LinkNotFoundError, browser.getLink, "Retry this build")
+            LinkNotFoundError, browser.getLink, "Retry this build"
+        )
 
     def test_cancel_build(self):
         # The requester of a build can cancel it.
@@ -305,8 +412,11 @@ class TestSnapBuildOperations(BrowserTestCase):
         browser = self.getViewBrowser(self.build, user=user)
         self.assertRaises(LinkNotFoundError, browser.getLink, "Cancel build")
         self.assertRaises(
-            Unauthorized, self.getUserBrowser, self.build_url + "/+cancel",
-            user=user)
+            Unauthorized,
+            self.getUserBrowser,
+            self.build_url + "/+cancel",
+            user=user,
+        )
 
     def test_cancel_build_wrong_state(self):
         # If the build isn't queued, you can't cancel it.
@@ -337,7 +447,8 @@ class TestSnapBuildOperations(BrowserTestCase):
         browser.getControl("Rescore build").click()
         self.assertEqual(
             "Invalid integer data",
-            extract_text(find_tags_by_class(browser.contents, "message")[1]))
+            extract_text(find_tags_by_class(browser.contents, "message")[1]),
+        )
 
     def test_rescore_build_not_admin(self):
         # A non-admin user cannot cancel a build.
@@ -347,8 +458,11 @@ class TestSnapBuildOperations(BrowserTestCase):
         browser = self.getViewBrowser(self.build, user=user)
         self.assertRaises(LinkNotFoundError, browser.getLink, "Rescore build")
         self.assertRaises(
-            Unauthorized, self.getUserBrowser, self.build_url + "/+rescore",
-            user=user)
+            Unauthorized,
+            self.getUserBrowser,
+            self.build_url + "/+rescore",
+            user=user,
+        )
 
     def test_rescore_build_wrong_state(self):
         # If the build isn't NEEDSBUILD, you can't rescore it.
@@ -365,20 +479,31 @@ class TestSnapBuildOperations(BrowserTestCase):
         with person_logged_in(self.requester):
             self.build.cancel()
         browser = self.getViewBrowser(
-            self.build, "+rescore", user=self.buildd_admin)
+            self.build, "+rescore", user=self.buildd_admin
+        )
         self.assertEqual(self.build_url, browser.url)
-        self.assertThat(browser.contents, soupmatchers.HTMLContains(
-            soupmatchers.Tag(
-                "notification", "div", attrs={"class": "warning message"},
-                text="Cannot rescore this build because it is not queued.")))
+        self.assertThat(
+            browser.contents,
+            soupmatchers.HTMLContains(
+                soupmatchers.Tag(
+                    "notification",
+                    "div",
+                    attrs={"class": "warning message"},
+                    text="Cannot rescore this build because it is not queued.",
+                )
+            ),
+        )
 
     def setUpStoreUpload(self):
         self.pushConfig(
-            "snappy", store_url="http://sca.example/";,
-            store_upload_url="http://updown.example/";)
+            "snappy",
+            store_url="http://sca.example/";,
+            store_upload_url="http://updown.example/";,
+        )
         with admin_logged_in():
             snappyseries = self.factory.makeSnappySeries(
-                usable_distro_series=[self.build.snap.distro_series])
+                usable_distro_series=[self.build.snap.distro_series]
+            )
         with person_logged_in(self.requester):
             self.build.snap.store_series = snappyseries
             self.build.snap.store_name = self.factory.getUniqueUnicode()
@@ -391,7 +516,8 @@ class TestSnapBuildOperations(BrowserTestCase):
         self.build.updateStatus(BuildStatus.FULLYBUILT)
         self.factory.makeSnapFile(
             snapbuild=self.build,
-            libraryfile=self.factory.makeLibraryFileAlias(db_only=True))
+            libraryfile=self.factory.makeLibraryFileAlias(db_only=True),
+        )
         browser = self.getViewBrowser(self.build, user=self.requester)
         browser.getControl("Upload this package to the store").click()
         self.assertEqual(self.build_url, browser.url)
@@ -401,7 +527,8 @@ class TestSnapBuildOperations(BrowserTestCase):
         self.assertEqual(self.build, job.snapbuild)
         self.assertEqual(
             "An upload has been scheduled and will run as soon as possible.",
-            extract_text(find_tags_by_class(browser.contents, "message")[0]))
+            extract_text(find_tags_by_class(browser.contents, "message")[0]),
+        )
 
     def test_store_upload_retry(self):
         # A build with a previously-failed store upload can have the upload
@@ -410,7 +537,8 @@ class TestSnapBuildOperations(BrowserTestCase):
         self.build.updateStatus(BuildStatus.FULLYBUILT)
         self.factory.makeSnapFile(
             snapbuild=self.build,
-            libraryfile=self.factory.makeLibraryFileAlias(db_only=True))
+            libraryfile=self.factory.makeLibraryFileAlias(db_only=True),
+        )
         old_job = getUtility(ISnapStoreUploadJobSource).create(self.build)
         removeSecurityProxy(old_job).job._status = JobStatus.FAILED
         browser = self.getViewBrowser(self.build, user=self.requester)
@@ -422,7 +550,8 @@ class TestSnapBuildOperations(BrowserTestCase):
         self.assertEqual(self.build, job.snapbuild)
         self.assertEqual(
             "An upload has been scheduled and will run as soon as possible.",
-            extract_text(find_tags_by_class(browser.contents, "message")[0]))
+            extract_text(find_tags_by_class(browser.contents, "message")[0]),
+        )
 
     def test_store_upload_error_notifies(self):
         # If a build cannot be scheduled for uploading to the store, we
@@ -434,20 +563,24 @@ class TestSnapBuildOperations(BrowserTestCase):
         self.assertEqual(self.build_url, browser.url)
         login(ANONYMOUS)
         self.assertEqual(
-            [], list(getUtility(ISnapStoreUploadJobSource).iterReady()))
+            [], list(getUtility(ISnapStoreUploadJobSource).iterReady())
+        )
         self.assertEqual(
             "Cannot upload this package because it has no files.",
-            extract_text(find_tags_by_class(browser.contents, "message")[0]))
+            extract_text(find_tags_by_class(browser.contents, "message")[0]),
+        )
 
     def test_builder_history(self):
         Store.of(self.build).flush()
         self.build.updateStatus(
-            BuildStatus.FULLYBUILT, builder=self.factory.makeBuilder())
+            BuildStatus.FULLYBUILT, builder=self.factory.makeBuilder()
+        )
         title = self.build.title
         browser = self.getViewBrowser(self.build.builder, "+history")
         self.assertTextMatchesExpressionIgnoreWhitespace(
             "Build history.*%s" % title,
-            extract_text(find_main_content(browser.contents)))
+            extract_text(find_main_content(browser.contents)),
+        )
         self.assertEqual(self.build_url, browser.getLink(title).url)
 
     def makeBuildingSnap(self, archive=None):
diff --git a/lib/lp/snappy/browser/tests/test_snaplisting.py b/lib/lp/snappy/browser/tests/test_snaplisting.py
index 31e6394..a242f19 100644
--- a/lib/lp/snappy/browser/tests/test_snaplisting.py
+++ b/lib/lp/snappy/browser/tests/test_snaplisting.py
@@ -3,26 +3,16 @@
 
 """Test snap package listings."""
 
-from datetime import (
-    datetime,
-    timedelta,
-    )
+from datetime import datetime, timedelta
 from functools import partial
 
 import pytz
 import soupmatchers
-from testtools.matchers import (
-    MatchesAll,
-    Not,
-    )
+from testtools.matchers import MatchesAll, Not
 from zope.security.proxy import removeSecurityProxy
 
 from lp.code.tests.helpers import GitHostingFixture
-from lp.services.database.constants import (
-    ONE_DAY_AGO,
-    SEVEN_DAYS_AGO,
-    UTC_NOW,
-    )
+from lp.services.database.constants import ONE_DAY_AGO, SEVEN_DAYS_AGO, UTC_NOW
 from lp.services.features.testing import FeatureFixture
 from lp.services.webapp import canonical_url
 from lp.snappy.interfaces.snap import SNAP_TESTING_FLAGS
@@ -32,7 +22,7 @@ from lp.testing import (
     login,
     person_logged_in,
     record_two_runs,
-    )
+)
 from lp.testing.layers import LaunchpadFunctionalLayer
 from lp.testing.matchers import HasQueryCount
 from lp.testing.views import create_initialized_view
@@ -42,16 +32,21 @@ class TestSnapListing(BrowserTestCase):
 
     layer = LaunchpadFunctionalLayer
 
-    def assertSnapsLink(self, context, link_text, link_has_context=False,
-                        **kwargs):
+    def assertSnapsLink(
+        self, context, link_text, link_has_context=False, **kwargs
+    ):
         if link_has_context:
             expected_href = canonical_url(context, view_name="+snaps")
         else:
             expected_href = "+snaps"
         matcher = soupmatchers.HTMLContains(
             soupmatchers.Tag(
-                "View snap packages link", "a", text=link_text,
-                attrs={"href": expected_href}))
+                "View snap packages link",
+                "a",
+                text=link_text,
+                attrs={"href": expected_href},
+            )
+        )
         self.assertThat(self.getViewBrowser(context).contents, Not(matcher))
         login(ANONYMOUS)
         self.factory.makeSnap(**kwargs)
@@ -75,24 +70,32 @@ class TestSnapListing(BrowserTestCase):
     def test_person_links_to_snaps(self):
         person = self.factory.makePerson()
         self.assertSnapsLink(
-            person, "View snap packages", link_has_context=True,
-            registrant=person, owner=person)
+            person,
+            "View snap packages",
+            link_has_context=True,
+            registrant=person,
+            owner=person,
+        )
 
     def test_project_links_to_snaps(self):
         project = self.factory.makeProduct()
         [ref] = self.factory.makeGitRefs(target=project)
         self.assertSnapsLink(
-            project, "View snap packages", link_has_context=True, git_ref=ref)
+            project, "View snap packages", link_has_context=True, git_ref=ref
+        )
 
     def test_branch_snap_listing(self):
         # We can see snap packages for a Bazaar branch.
         branch = self.factory.makeAnyBranch()
         self.factory.makeSnap(branch=branch)
         text = self.getMainText(branch, "+snaps")
-        self.assertTextMatchesExpressionIgnoreWhitespace("""
+        self.assertTextMatchesExpressionIgnoreWhitespace(
+            """
             Snap packages for lp:.*
             Name            Owner           Registered
-            snap-name.*     Team Name.*     .*""", text)
+            snap-name.*     Team Name.*     .*""",
+            text,
+        )
 
     def test_git_repository_snap_listing(self):
         # We can see snap packages for a Git repository.
@@ -100,57 +103,79 @@ class TestSnapListing(BrowserTestCase):
         [ref] = self.factory.makeGitRefs(repository=repository)
         self.factory.makeSnap(git_ref=ref)
         text = self.getMainText(repository, "+snaps")
-        self.assertTextMatchesExpressionIgnoreWhitespace("""
+        self.assertTextMatchesExpressionIgnoreWhitespace(
+            """
             Snap packages for lp:~.*
             Name            Owner           Registered
-            snap-name.*     Team Name.*     .*""", text)
+            snap-name.*     Team Name.*     .*""",
+            text,
+        )
 
     def test_git_ref_snap_listing(self):
         # We can see snap packages for a Git reference.
         [ref] = self.factory.makeGitRefs()
         self.factory.makeSnap(git_ref=ref)
         text = self.getMainText(ref, "+snaps")
-        self.assertTextMatchesExpressionIgnoreWhitespace("""
+        self.assertTextMatchesExpressionIgnoreWhitespace(
+            """
             Snap packages for ~.*:.*
             Name            Owner           Registered
-            snap-name.*     Team Name.*     .*""", text)
+            snap-name.*     Team Name.*     .*""",
+            text,
+        )
 
     def test_person_snap_listing(self):
         # We can see snap packages for a person.
         owner = self.factory.makePerson(displayname="Snap Owner")
         self.factory.makeSnap(
-            registrant=owner, owner=owner, branch=self.factory.makeAnyBranch(),
-            date_created=SEVEN_DAYS_AGO)
+            registrant=owner,
+            owner=owner,
+            branch=self.factory.makeAnyBranch(),
+            date_created=SEVEN_DAYS_AGO,
+        )
         [ref] = self.factory.makeGitRefs()
         self.factory.makeSnap(
-            registrant=owner, owner=owner, git_ref=ref,
-            date_created=ONE_DAY_AGO)
+            registrant=owner,
+            owner=owner,
+            git_ref=ref,
+            date_created=ONE_DAY_AGO,
+        )
         remote_ref = self.factory.makeGitRefRemote()
         self.factory.makeSnap(
-            registrant=owner, owner=owner, git_ref=remote_ref,
-            date_created=UTC_NOW)
+            registrant=owner,
+            owner=owner,
+            git_ref=remote_ref,
+            date_created=UTC_NOW,
+        )
         text = self.getMainText(owner, "+snaps")
-        self.assertTextMatchesExpressionIgnoreWhitespace("""
+        self.assertTextMatchesExpressionIgnoreWhitespace(
+            """
             Snap packages for Snap Owner
             Name            Source                  Registered
             snap-name.*     http://.* path-.*       .*
             snap-name.*     ~.*:.*                  .*
-            snap-name.*     lp:.*                   .*""", text)
+            snap-name.*     lp:.*                   .*""",
+            text,
+        )
 
     def test_project_snap_listing(self):
         # We can see snap packages for a project.
         project = self.factory.makeProduct(displayname="Snappable")
         self.factory.makeSnap(
             branch=self.factory.makeProductBranch(product=project),
-            date_created=ONE_DAY_AGO)
+            date_created=ONE_DAY_AGO,
+        )
         [ref] = self.factory.makeGitRefs(target=project)
         self.factory.makeSnap(git_ref=ref, date_created=UTC_NOW)
         text = self.getMainText(project, "+snaps")
-        self.assertTextMatchesExpressionIgnoreWhitespace("""
+        self.assertTextMatchesExpressionIgnoreWhitespace(
+            """
             Snap packages for Snappable
             Name            Owner           Source          Registered
             snap-name.*     Team Name.*     ~.*:.*          .*
-            snap-name.*     Team Name.*     lp:.*           .*""", text)
+            snap-name.*     Team Name.*     lp:.*           .*""",
+            text,
+        )
 
     def test_project_private_snap_listing(self):
         # Only users with permission can see private snap packages in the list
@@ -162,9 +187,12 @@ class TestSnapListing(BrowserTestCase):
         someone_else = self.factory.makePerson()
         private_snap = self.factory.makeSnap(
             name="private-snap",
-            private=True, registrant=private_owner, owner=private_owner,
+            private=True,
+            registrant=private_owner,
+            owner=private_owner,
             branch=self.factory.makeProductBranch(product=project),
-            date_created=ONE_DAY_AGO)
+            date_created=ONE_DAY_AGO,
+        )
         with person_logged_in(private_owner):
             private_snap.subscribe(user_with_permission, private_owner)
         [ref] = self.factory.makeGitRefs(target=project)
@@ -183,18 +211,21 @@ class TestSnapListing(BrowserTestCase):
 
         # private_owner: full_list.
         self.assertTextMatchesExpressionIgnoreWhitespace(
-            full_list, self.getMainText(project, "+snaps", user=private_owner))
+            full_list, self.getMainText(project, "+snaps", user=private_owner)
+        )
         # user_with_permission: full_list.
         self.assertTextMatchesExpressionIgnoreWhitespace(
             full_list,
-            self.getMainText(project, "+snaps", user=user_with_permission))
+            self.getMainText(project, "+snaps", user=user_with_permission),
+        )
         # someone_else: public_list.
         self.assertTextMatchesExpressionIgnoreWhitespace(
-            public_list,
-            self.getMainText(project, "+snaps", user=someone_else))
+            public_list, self.getMainText(project, "+snaps", user=someone_else)
+        )
         # Not logged in: public_list.
         self.assertTextMatchesExpressionIgnoreWhitespace(
-            public_list, self.getMainText(project, "+snaps", user=None))
+            public_list, self.getMainText(project, "+snaps", user=None)
+        )
 
     def test_person_private_snap_listing(self):
         self.useFixture(FeatureFixture(SNAP_TESTING_FLAGS))
@@ -203,14 +234,21 @@ class TestSnapListing(BrowserTestCase):
         someone_else = self.factory.makePerson()
         private_snap = self.factory.makeSnap(
             name="private-snap",
-            private=True, registrant=private_owner, owner=private_owner,
-            date_created=ONE_DAY_AGO)
+            private=True,
+            registrant=private_owner,
+            owner=private_owner,
+            date_created=ONE_DAY_AGO,
+        )
         with person_logged_in(private_owner):
             private_snap.subscribe(user_with_permission, private_owner)
         [ref] = self.factory.makeGitRefs()
         self.factory.makeSnap(
-            private=False, registrant=private_owner, owner=private_owner,
-            git_ref=ref, date_created=UTC_NOW)
+            private=False,
+            registrant=private_owner,
+            owner=private_owner,
+            git_ref=ref,
+            date_created=UTC_NOW,
+        )
 
         full_list = """
             Snap packages for Random-user
@@ -225,19 +263,25 @@ class TestSnapListing(BrowserTestCase):
 
         # private_owner: full_list.
         self.assertTextMatchesExpressionIgnoreWhitespace(
-            full_list, self.getMainText(private_owner, "+snaps",
-                                        user=private_owner))
+            full_list,
+            self.getMainText(private_owner, "+snaps", user=private_owner),
+        )
         # user_with_permission: full_list.
         self.assertTextMatchesExpressionIgnoreWhitespace(
-            full_list, self.getMainText(private_owner, "+snaps",
-                                        user=user_with_permission))
+            full_list,
+            self.getMainText(
+                private_owner, "+snaps", user=user_with_permission
+            ),
+        )
         # someone_else: public_list.
         self.assertTextMatchesExpressionIgnoreWhitespace(
-            public_list, self.getMainText(private_owner, "+snaps",
-                                          user=someone_else))
+            public_list,
+            self.getMainText(private_owner, "+snaps", user=someone_else),
+        )
         # Not logged in: public_list.
         self.assertTextMatchesExpressionIgnoreWhitespace(
-            public_list, self.getMainText(private_owner, "+snaps", user=None))
+            public_list, self.getMainText(private_owner, "+snaps", user=None)
+        )
 
     def test_branch_private_snap_listing(self):
         # Only certain users can see private snaps on branch listing.
@@ -247,13 +291,20 @@ class TestSnapListing(BrowserTestCase):
         someone_else = self.factory.makePerson()
         branch = self.factory.makeAnyBranch()
         private_snap = self.factory.makeSnap(
-            private=True, name="private-snap",
-            owner=private_owner, registrant=private_owner, branch=branch)
+            private=True,
+            name="private-snap",
+            owner=private_owner,
+            registrant=private_owner,
+            branch=branch,
+        )
         with person_logged_in(private_owner):
             private_snap.subscribe(user_with_permission, private_owner)
         self.factory.makeSnap(
-            private=False, owner=private_owner, registrant=private_owner,
-            branch=branch)
+            private=False,
+            owner=private_owner,
+            registrant=private_owner,
+            branch=branch,
+        )
         full_list = """
             Snap packages for lp:.*
             Name            Owner           Registered
@@ -265,14 +316,18 @@ class TestSnapListing(BrowserTestCase):
             snap-name.*     Random-user.*     .*"""
 
         self.assertTextMatchesExpressionIgnoreWhitespace(
-            full_list, self.getMainText(branch, "+snaps", user=private_owner))
+            full_list, self.getMainText(branch, "+snaps", user=private_owner)
+        )
         self.assertTextMatchesExpressionIgnoreWhitespace(
-            full_list, self.getMainText(branch, "+snaps",
-                                        user=user_with_permission))
+            full_list,
+            self.getMainText(branch, "+snaps", user=user_with_permission),
+        )
         self.assertTextMatchesExpressionIgnoreWhitespace(
-            public_list, self.getMainText(branch, "+snaps", user=someone_else))
+            public_list, self.getMainText(branch, "+snaps", user=someone_else)
+        )
         self.assertTextMatchesExpressionIgnoreWhitespace(
-            public_list, self.getMainText(branch, "+snaps", user=None))
+            public_list, self.getMainText(branch, "+snaps", user=None)
+        )
 
     def test_git_repository_private_snap_listing(self):
         # Only certain users can see private snaps on git repo listing.
@@ -283,13 +338,20 @@ class TestSnapListing(BrowserTestCase):
         repository = self.factory.makeGitRepository()
         [ref] = self.factory.makeGitRefs(repository=repository)
         private_snap = self.factory.makeSnap(
-            private=True, name="private-snap",
-            owner=private_owner, registrant=private_owner, git_ref=ref)
+            private=True,
+            name="private-snap",
+            owner=private_owner,
+            registrant=private_owner,
+            git_ref=ref,
+        )
         with person_logged_in(private_owner):
             private_snap.subscribe(user_with_permission, private_owner)
         self.factory.makeSnap(
-            private=False, owner=private_owner, registrant=private_owner,
-            git_ref=ref)
+            private=False,
+            owner=private_owner,
+            registrant=private_owner,
+            git_ref=ref,
+        )
 
         full_list = """
             Snap packages for lp:~.*
@@ -303,15 +365,19 @@ class TestSnapListing(BrowserTestCase):
 
         self.assertTextMatchesExpressionIgnoreWhitespace(
             full_list,
-            self.getMainText(repository, "+snaps", user=private_owner))
+            self.getMainText(repository, "+snaps", user=private_owner),
+        )
         self.assertTextMatchesExpressionIgnoreWhitespace(
             full_list,
-            self.getMainText(repository, "+snaps", user=user_with_permission))
+            self.getMainText(repository, "+snaps", user=user_with_permission),
+        )
         self.assertTextMatchesExpressionIgnoreWhitespace(
             public_list,
-            self.getMainText(repository, "+snaps", user=someone_else))
+            self.getMainText(repository, "+snaps", user=someone_else),
+        )
         self.assertTextMatchesExpressionIgnoreWhitespace(
-            public_list, self.getMainText(repository, "+snaps", user=None))
+            public_list, self.getMainText(repository, "+snaps", user=None)
+        )
 
     def test_git_ref_private_snap_listing(self):
         # Only certain users can see private snaps on git ref listing.
@@ -322,13 +388,20 @@ class TestSnapListing(BrowserTestCase):
         repository = self.factory.makeGitRepository()
         [ref] = self.factory.makeGitRefs(repository=repository)
         private_snap = self.factory.makeSnap(
-            private=True, name="private-snap",
-            owner=private_owner, registrant=private_owner, git_ref=ref)
+            private=True,
+            name="private-snap",
+            owner=private_owner,
+            registrant=private_owner,
+            git_ref=ref,
+        )
         with person_logged_in(private_owner):
             private_snap.subscribe(user_with_permission, private_owner)
         self.factory.makeSnap(
-            private=False, owner=private_owner, registrant=private_owner,
-            git_ref=ref)
+            private=False,
+            owner=private_owner,
+            registrant=private_owner,
+            git_ref=ref,
+        )
 
         full_list = """
                     Snap packages for ~.*:.*
@@ -341,19 +414,24 @@ class TestSnapListing(BrowserTestCase):
                     snap-name.*     Random-user.*     .*"""
 
         self.assertTextMatchesExpressionIgnoreWhitespace(
-            full_list, self.getMainText(ref, "+snaps", user=private_owner))
+            full_list, self.getMainText(ref, "+snaps", user=private_owner)
+        )
         self.assertTextMatchesExpressionIgnoreWhitespace(
             full_list,
-            self.getMainText(ref, "+snaps", user=user_with_permission))
+            self.getMainText(ref, "+snaps", user=user_with_permission),
+        )
         self.assertTextMatchesExpressionIgnoreWhitespace(
-            public_list, self.getMainText(ref, "+snaps", user=someone_else))
+            public_list, self.getMainText(ref, "+snaps", user=someone_else)
+        )
         self.assertTextMatchesExpressionIgnoreWhitespace(
-            public_list, self.getMainText(ref, "+snaps", user=None))
+            public_list, self.getMainText(ref, "+snaps", user=None)
+        )
 
     def assertSnapsQueryCount(self, context, item_creator):
         self.pushConfig("launchpad", default_batch_size=10)
         recorder1, recorder2 = record_two_runs(
-            lambda: self.getMainText(context, "+snaps"), item_creator, 5)
+            lambda: self.getMainText(context, "+snaps"), item_creator, 5
+        )
         self.assertThat(recorder2, HasQueryCount.byEquality(recorder1))
 
     def test_branch_query_count(self):
@@ -408,11 +486,13 @@ class TestSnapListing(BrowserTestCase):
                 project = self.factory.makeProduct()
                 if (i % 2) == 0:
                     branch = self.factory.makeProductBranch(
-                        owner=person, product=project)
+                        owner=person, product=project
+                    )
                     self.factory.makeSnap(branch=branch)
                 else:
                     [ref] = self.factory.makeGitRefs(
-                        owner=person, target=project)
+                        owner=person, target=project
+                    )
                     self.factory.makeSnap(git_ref=ref)
 
         self.assertSnapsQueryCount(person, create_snap)
@@ -439,33 +519,46 @@ class TestSnapListing(BrowserTestCase):
     def makeSnapsAndMatchers(self, create_snap, count, start_time):
         snaps = [create_snap() for i in range(count)]
         for i, snap in enumerate(snaps):
-            removeSecurityProxy(snap).date_last_modified = (
-                start_time - timedelta(seconds=i))
+            removeSecurityProxy(
+                snap
+            ).date_last_modified = start_time - timedelta(seconds=i)
         return [
             soupmatchers.Tag(
-                "snap link", "a", text=snap.name,
+                "snap link",
+                "a",
+                text=snap.name,
                 attrs={
-                    "href": canonical_url(snap, path_only_if_possible=True)})
-            for snap in snaps]
+                    "href": canonical_url(snap, path_only_if_possible=True)
+                },
+            )
+            for snap in snaps
+        ]
 
     def assertBatches(self, context, link_matchers, batched, start, size):
         view = create_initialized_view(context, "+snaps")
         listing_tag = soupmatchers.Tag(
-            "snap listing", "table", attrs={"class": "listing sortable"})
+            "snap listing", "table", attrs={"class": "listing sortable"}
+        )
         batch_nav_tag = soupmatchers.Tag(
-            "batch nav links", "td",
-            attrs={"class": "batch-navigation-links"})
+            "batch nav links", "td", attrs={"class": "batch-navigation-links"}
+        )
         present_links = ([batch_nav_tag] if batched else []) + [
-            matcher for i, matcher in enumerate(link_matchers)
-            if i in range(start, start + size)]
+            matcher
+            for i, matcher in enumerate(link_matchers)
+            if i in range(start, start + size)
+        ]
         absent_links = ([] if batched else [batch_nav_tag]) + [
-            matcher for i, matcher in enumerate(link_matchers)
-            if i not in range(start, start + size)]
+            matcher
+            for i, matcher in enumerate(link_matchers)
+            if i not in range(start, start + size)
+        ]
         self.assertThat(
             view.render(),
             MatchesAll(
                 soupmatchers.HTMLContains(listing_tag, *present_links),
-                Not(soupmatchers.HTMLContains(*absent_links))))
+                Not(soupmatchers.HTMLContains(*absent_links)),
+            ),
+        )
 
     def test_branch_batches_snaps(self):
         branch = self.factory.makeAnyBranch()
@@ -473,8 +566,11 @@ class TestSnapListing(BrowserTestCase):
         now = datetime.now(pytz.UTC)
         link_matchers = self.makeSnapsAndMatchers(create_snap, 3, now)
         self.assertBatches(branch, link_matchers, False, 0, 3)
-        link_matchers.extend(self.makeSnapsAndMatchers(
-            create_snap, 7, now - timedelta(seconds=3)))
+        link_matchers.extend(
+            self.makeSnapsAndMatchers(
+                create_snap, 7, now - timedelta(seconds=3)
+            )
+        )
         self.assertBatches(branch, link_matchers, True, 0, 5)
 
     def test_git_repository_batches_snaps(self):
@@ -484,8 +580,11 @@ class TestSnapListing(BrowserTestCase):
         now = datetime.now(pytz.UTC)
         link_matchers = self.makeSnapsAndMatchers(create_snap, 3, now)
         self.assertBatches(repository, link_matchers, False, 0, 3)
-        link_matchers.extend(self.makeSnapsAndMatchers(
-            create_snap, 7, now - timedelta(seconds=3)))
+        link_matchers.extend(
+            self.makeSnapsAndMatchers(
+                create_snap, 7, now - timedelta(seconds=3)
+            )
+        )
         self.assertBatches(repository, link_matchers, True, 0, 5)
 
     def test_git_ref_batches_snaps(self):
@@ -494,19 +593,26 @@ class TestSnapListing(BrowserTestCase):
         now = datetime.now(pytz.UTC)
         link_matchers = self.makeSnapsAndMatchers(create_snap, 3, now)
         self.assertBatches(ref, link_matchers, False, 0, 3)
-        link_matchers.extend(self.makeSnapsAndMatchers(
-            create_snap, 7, now - timedelta(seconds=3)))
+        link_matchers.extend(
+            self.makeSnapsAndMatchers(
+                create_snap, 7, now - timedelta(seconds=3)
+            )
+        )
         self.assertBatches(ref, link_matchers, True, 0, 5)
 
     def test_person_batches_snaps(self):
         owner = self.factory.makePerson()
         create_snap = partial(
-            self.factory.makeSnap, registrant=owner, owner=owner)
+            self.factory.makeSnap, registrant=owner, owner=owner
+        )
         now = datetime.now(pytz.UTC)
         link_matchers = self.makeSnapsAndMatchers(create_snap, 3, now)
         self.assertBatches(owner, link_matchers, False, 0, 3)
-        link_matchers.extend(self.makeSnapsAndMatchers(
-            create_snap, 7, now - timedelta(seconds=3)))
+        link_matchers.extend(
+            self.makeSnapsAndMatchers(
+                create_snap, 7, now - timedelta(seconds=3)
+            )
+        )
         self.assertBatches(owner, link_matchers, True, 0, 5)
 
     def test_project_batches_snaps(self):
@@ -516,6 +622,9 @@ class TestSnapListing(BrowserTestCase):
         now = datetime.now(pytz.UTC)
         link_matchers = self.makeSnapsAndMatchers(create_snap, 3, now)
         self.assertBatches(project, link_matchers, False, 0, 3)
-        link_matchers.extend(self.makeSnapsAndMatchers(
-            create_snap, 7, now - timedelta(seconds=3)))
+        link_matchers.extend(
+            self.makeSnapsAndMatchers(
+                create_snap, 7, now - timedelta(seconds=3)
+            )
+        )
         self.assertBatches(project, link_matchers, True, 0, 5)
diff --git a/lib/lp/snappy/browser/tests/test_snapsubscription.py b/lib/lp/snappy/browser/tests/test_snapsubscription.py
index 575c0d6..b196192 100644
--- a/lib/lp/snappy/browser/tests/test_snapsubscription.py
+++ b/lib/lp/snappy/browser/tests/test_snapsubscription.py
@@ -11,18 +11,14 @@ from lp.registry.enums import BranchSharingPolicy
 from lp.services.features.testing import FeatureFixture
 from lp.services.webapp import canonical_url
 from lp.snappy.interfaces.snap import SNAP_TESTING_FLAGS
-from lp.testing import (
-    admin_logged_in,
-    BrowserTestCase,
-    person_logged_in,
-    )
+from lp.testing import BrowserTestCase, admin_logged_in, person_logged_in
 from lp.testing.layers import DatabaseFunctionalLayer
 from lp.testing.pages import (
     extract_text,
     find_main_content,
     find_tag_by_id,
     find_tags_by_class,
-    )
+)
 
 
 class BaseTestSnapView(BrowserTestCase):
@@ -33,53 +29,70 @@ class BaseTestSnapView(BrowserTestCase):
         super().setUp()
         self.useFixture(FeatureFixture(SNAP_TESTING_FLAGS))
         self.useFixture(FakeLogger())
-        self.person = self.factory.makePerson(name='snap-owner')
+        self.person = self.factory.makePerson(name="snap-owner")
 
     def makeSnap(self, project=None, **kwargs):
         [ref] = self.factory.makeGitRefs(
-            owner=self.person, target=self.person, name="snap-repository",
-            paths=["refs/heads/master"])
+            owner=self.person,
+            target=self.person,
+            name="snap-repository",
+            paths=["refs/heads/master"],
+        )
         if project is None:
             project = self.factory.makeProduct(
-                owner=self.person, registrant=self.person)
+                owner=self.person, registrant=self.person
+            )
         return self.factory.makeSnap(
-            registrant=self.person, owner=self.person, name="snap-name",
-            git_ref=ref, project=project, **kwargs)
+            registrant=self.person,
+            owner=self.person,
+            name="snap-name",
+            git_ref=ref,
+            project=project,
+            **kwargs,
+        )
 
     def getSubscriptionPortletText(self, browser):
         return extract_text(
-            find_tag_by_id(browser.contents, 'portlet-subscribers'))
+            find_tag_by_id(browser.contents, "portlet-subscribers")
+        )
 
     def extractMainText(self, browser):
         return extract_text(find_main_content(browser.contents))
 
     def extractInfoMessageContent(self, browser):
         return extract_text(
-            find_tags_by_class(browser.contents, 'informational message')[0])
+            find_tags_by_class(browser.contents, "informational message")[0]
+        )
 
 
 class TestPublicSnapSubscriptionViews(BaseTestSnapView):
-
     def test_subscribe_self(self):
         snap = self.makeSnap()
         another_user = self.factory.makePerson(name="another-user")
         browser = self.getViewBrowser(snap, user=another_user)
-        self.assertTextMatchesExpressionIgnoreWhitespace(r"""
+        self.assertTextMatchesExpressionIgnoreWhitespace(
+            r"""
             Subscribe yourself
             Subscribe someone else
             Subscribers
             Snap-owner
-            """, self.getSubscriptionPortletText(browser))
+            """,
+            self.getSubscriptionPortletText(browser),
+        )
 
         # Go to "subscribe myself" page, and click the button.
         browser = self.getViewBrowser(
-            snap, view_name="+subscribe", user=another_user)
-        self.assertTextMatchesExpressionIgnoreWhitespace(r"""
+            snap, view_name="+subscribe", user=another_user
+        )
+        self.assertTextMatchesExpressionIgnoreWhitespace(
+            r"""
             Subscribe to snap recipe
             Snap packages
             snap-name
             Subscribe to snap recipe or Cancel
-            """, self.extractMainText(browser))
+            """,
+            self.extractMainText(browser),
+        )
         browser.getControl("Subscribe").click()
 
         # We should be redirected back to snap page.
@@ -87,13 +100,16 @@ class TestPublicSnapSubscriptionViews(BaseTestSnapView):
             self.assertEqual(canonical_url(snap), browser.url)
 
         # And the new user should be listed in the subscribers list.
-        self.assertTextMatchesExpressionIgnoreWhitespace(r"""
+        self.assertTextMatchesExpressionIgnoreWhitespace(
+            r"""
             Edit your subscription
             Subscribe someone else
             Subscribers
             Another-user
             Snap-owner
-            """, self.getSubscriptionPortletText(browser))
+            """,
+            self.getSubscriptionPortletText(browser),
+        )
 
     def test_unsubscribe_self(self):
         snap = self.makeSnap()
@@ -102,17 +118,23 @@ class TestPublicSnapSubscriptionViews(BaseTestSnapView):
             snap.subscribe(another_user, snap.owner)
         subscription = snap.getSubscription(another_user)
         browser = self.getViewBrowser(subscription, user=another_user)
-        self.assertTextMatchesExpressionIgnoreWhitespace(r"""
+        self.assertTextMatchesExpressionIgnoreWhitespace(
+            r"""
             Edit subscription to snap recipe for Another-user
             Snap packages
             snap-name
             If you unsubscribe from a snap recipe it will no longer show up on
             your personal pages. or Cancel
-            """, self.extractMainText(browser))
+            """,
+            self.extractMainText(browser),
+        )
         browser.getControl("Unsubscribe").click()
-        self.assertTextMatchesExpressionIgnoreWhitespace(r"""
+        self.assertTextMatchesExpressionIgnoreWhitespace(
+            r"""
             Another-user has been unsubscribed from this snap recipe.
-            """, self.extractInfoMessageContent(browser))
+            """,
+            self.extractInfoMessageContent(browser),
+        )
         with person_logged_in(self.person):
             self.assertIsNone(snap.getSubscription(another_user))
 
@@ -120,17 +142,22 @@ class TestPublicSnapSubscriptionViews(BaseTestSnapView):
         snap = self.makeSnap()
         another_user = self.factory.makePerson(name="another-user")
         browser = self.getViewBrowser(snap, user=snap.owner)
-        self.assertTextMatchesExpressionIgnoreWhitespace(r"""
+        self.assertTextMatchesExpressionIgnoreWhitespace(
+            r"""
             Edit your subscription
             Subscribe someone else
             Subscribers
             Snap-owner
-            """, self.getSubscriptionPortletText(browser))
+            """,
+            self.getSubscriptionPortletText(browser),
+        )
 
         # Go to "subscribe" page, and click the button.
         browser = self.getViewBrowser(
-            snap, view_name="+addsubscriber", user=another_user)
-        self.assertTextMatchesExpressionIgnoreWhitespace(r"""
+            snap, view_name="+addsubscriber", user=another_user
+        )
+        self.assertTextMatchesExpressionIgnoreWhitespace(
+            r"""
             Subscribe to snap recipe
             Snap packages
             snap-name
@@ -140,8 +167,10 @@ class TestPublicSnapSubscriptionViews(BaseTestSnapView):
             The person subscribed to the related snap recipe.
             or
             Cancel
-            """, self.extractMainText(browser))
-        browser.getControl(name="field.person").value = 'another-user'
+            """,
+            self.extractMainText(browser),
+        )
+        browser.getControl(name="field.person").value = "another-user"
         browser.getControl("Subscribe").click()
 
         # We should be redirected back to snap page.
@@ -149,13 +178,16 @@ class TestPublicSnapSubscriptionViews(BaseTestSnapView):
             self.assertEqual(canonical_url(snap), browser.url)
 
         # And the new user should be listed in the subscribers list.
-        self.assertTextMatchesExpressionIgnoreWhitespace(r"""
+        self.assertTextMatchesExpressionIgnoreWhitespace(
+            r"""
             Edit your subscription
             Subscribe someone else
             Subscribers
             Another-user
             Snap-owner
-            """, self.getSubscriptionPortletText(browser))
+            """,
+            self.getSubscriptionPortletText(browser),
+        )
 
     def test_unsubscribe_someone_else(self):
         snap = self.makeSnap()
@@ -165,45 +197,61 @@ class TestPublicSnapSubscriptionViews(BaseTestSnapView):
 
         subscription = snap.getSubscription(another_user)
         browser = self.getViewBrowser(subscription, user=snap.owner)
-        self.assertTextMatchesExpressionIgnoreWhitespace(r"""
+        self.assertTextMatchesExpressionIgnoreWhitespace(
+            r"""
             Edit subscription to snap recipe for Another-user
             Snap packages
             snap-name
             If you unsubscribe from a snap recipe it will no longer show up on
             your personal pages. or Cancel
-            """, self.extractMainText(browser))
+            """,
+            self.extractMainText(browser),
+        )
         browser.getControl("Unsubscribe").click()
-        self.assertTextMatchesExpressionIgnoreWhitespace(r"""
+        self.assertTextMatchesExpressionIgnoreWhitespace(
+            r"""
             Another-user has been unsubscribed from this snap recipe.
-            """, self.extractInfoMessageContent(browser))
+            """,
+            self.extractInfoMessageContent(browser),
+        )
         with person_logged_in(self.person):
             self.assertIsNone(snap.getSubscription(another_user))
 
 
 class TestPrivateSnapSubscriptionViews(BaseTestSnapView):
-
     def makePrivateSnap(self, **kwargs):
         project = self.factory.makeProduct(
-            owner=self.person, registrant=self.person,
+            owner=self.person,
+            registrant=self.person,
             information_type=InformationType.PROPRIETARY,
-            branch_sharing_policy=BranchSharingPolicy.PROPRIETARY)
+            branch_sharing_policy=BranchSharingPolicy.PROPRIETARY,
+        )
         return self.makeSnap(
-            information_type=InformationType.PROPRIETARY,
-            project=project)
+            information_type=InformationType.PROPRIETARY, project=project
+        )
 
     def test_cannot_subscribe_to_private_snap(self):
         snap = self.makePrivateSnap()
         another_user = self.factory.makePerson(name="another-user")
         # Unsubscribed user should not see the snap page.
         self.assertRaises(
-            Unauthorized, self.getViewBrowser, snap, user=another_user)
+            Unauthorized, self.getViewBrowser, snap, user=another_user
+        )
         # Nor the subscribe pages.
         self.assertRaises(
-            Unauthorized, self.getViewBrowser,
-            snap, view_name="+subscribe", user=another_user)
+            Unauthorized,
+            self.getViewBrowser,
+            snap,
+            view_name="+subscribe",
+            user=another_user,
+        )
         self.assertRaises(
-            Unauthorized, self.getViewBrowser,
-            snap, view_name="+addsubscriber", user=another_user)
+            Unauthorized,
+            self.getViewBrowser,
+            snap,
+            view_name="+addsubscriber",
+            user=another_user,
+        )
 
     def test_snap_owner_can_subscribe_someone_to_private_snap(self):
         snap = self.makePrivateSnap()
@@ -211,8 +259,10 @@ class TestPrivateSnapSubscriptionViews(BaseTestSnapView):
 
         # Go to "subscribe" page, and click the button.
         browser = self.getViewBrowser(
-            snap, view_name="+addsubscriber", user=self.person)
-        self.assertTextMatchesExpressionIgnoreWhitespace(r"""
+            snap, view_name="+addsubscriber", user=self.person
+        )
+        self.assertTextMatchesExpressionIgnoreWhitespace(
+            r"""
             Subscribe to snap recipe
             Snap packages
             snap-name
@@ -222,20 +272,25 @@ class TestPrivateSnapSubscriptionViews(BaseTestSnapView):
             The person subscribed to the related snap recipe.
             or
             Cancel
-            """, self.extractMainText(browser))
-        browser.getControl(name="field.person").value = 'another-user'
+            """,
+            self.extractMainText(browser),
+        )
+        browser.getControl(name="field.person").value = "another-user"
         browser.getControl("Subscribe").click()
 
         # Now the new user should be listed in the subscribers list,
         # and have access to the snap page.
         browser = self.getViewBrowser(snap, user=another_user)
-        self.assertTextMatchesExpressionIgnoreWhitespace(r"""
+        self.assertTextMatchesExpressionIgnoreWhitespace(
+            r"""
             Edit your subscription
             Subscribe someone else
             Subscribers
             Another-user
             Snap-owner
-            """, self.getSubscriptionPortletText(browser))
+            """,
+            self.getSubscriptionPortletText(browser),
+        )
 
     def test_unsubscribe_self(self):
         snap = self.makePrivateSnap()
@@ -244,16 +299,22 @@ class TestPrivateSnapSubscriptionViews(BaseTestSnapView):
             snap.subscribe(another_user, self.person)
             subscription = snap.getSubscription(another_user)
         browser = self.getViewBrowser(subscription, user=another_user)
-        self.assertTextMatchesExpressionIgnoreWhitespace(r"""
+        self.assertTextMatchesExpressionIgnoreWhitespace(
+            r"""
             Edit subscription to snap recipe for Another-user
             Snap packages
             snap-name
             If you unsubscribe from a snap recipe it will no longer show up on
             your personal pages. or Cancel
-            """, self.extractMainText(browser))
+            """,
+            self.extractMainText(browser),
+        )
         browser.getControl("Unsubscribe").click()
-        self.assertTextMatchesExpressionIgnoreWhitespace(r"""
+        self.assertTextMatchesExpressionIgnoreWhitespace(
+            r"""
             Another-user has been unsubscribed from this snap recipe.
-            """, self.extractInfoMessageContent(browser))
+            """,
+            self.extractInfoMessageContent(browser),
+        )
         with person_logged_in(self.person):
             self.assertIsNone(snap.getSubscription(another_user))
diff --git a/lib/lp/snappy/browser/widgets/snaparchive.py b/lib/lp/snappy/browser/widgets/snaparchive.py
index 538123e..a1400c4 100644
--- a/lib/lp/snappy/browser/widgets/snaparchive.py
+++ b/lib/lp/snappy/browser/widgets/snaparchive.py
@@ -2,8 +2,8 @@
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 __all__ = [
-    'SnapArchiveWidget',
-    ]
+    "SnapArchiveWidget",
+]
 
 from zope.browserpage import ViewPageTemplateFile
 from zope.component import getUtility
@@ -12,14 +12,14 @@ from zope.formlib.interfaces import (
     IInputWidget,
     MissingInputError,
     WidgetInputError,
-    )
+)
 from zope.formlib.utility import setUpWidget
 from zope.formlib.widget import (
     BrowserWidget,
     InputErrors,
     InputWidget,
     renderElement,
-    )
+)
 from zope.interface import implementer
 from zope.schema import Choice
 
@@ -29,7 +29,7 @@ from lp.app.validators import LaunchpadValidationError
 from lp.services.webapp.interfaces import (
     IAlwaysSubmittedWidget,
     IMultiLineWidgetLayout,
-    )
+)
 from lp.snappy.interfaces.snap import ISnap
 from lp.soyuz.interfaces.archive import IArchive
 
@@ -46,11 +46,13 @@ class SnapArchiveWidget(BrowserWidget, InputWidget):
             return
         fields = [
             Choice(
-                __name__="ppa", title="PPA", required=True, vocabulary="PPA"),
-            ]
+                __name__="ppa", title="PPA", required=True, vocabulary="PPA"
+            ),
+        ]
         for field in fields:
             setUpWidget(
-                self, field.__name__, field, IInputWidget, prefix=self.name)
+                self, field.__name__, field, IInputWidget, prefix=self.name
+            )
         self._widgets_set_up = True
 
     def setUpOptions(self):
@@ -58,18 +60,25 @@ class SnapArchiveWidget(BrowserWidget, InputWidget):
         self.options = {}
         for option in ["primary", "ppa"]:
             attributes = dict(
-                type="radio", name=self.name, value=option,
-                id="%s.option.%s" % (self.name, option))
-            if (self.default_option is not None and
-                self.request.form_ng.getOne(
-                    self.name, self.default_option) == option):
+                type="radio",
+                name=self.name,
+                value=option,
+                id="%s.option.%s" % (self.name, option),
+            )
+            if (
+                self.default_option is not None
+                and self.request.form_ng.getOne(self.name, self.default_option)
+                == option
+            ):
                 attributes["checked"] = "checked"
             self.options[option] = renderElement("input", **attributes)
 
     @property
     def main_archive(self):
-        if (ISnap.providedBy(self.context.context) and
-                self.context.context.distro_series is not None):
+        if (
+            ISnap.providedBy(self.context.context)
+            and self.context.context.distro_series is not None
+        ):
             return self.context.context.distro_series.main_archive
         else:
             return getUtility(ILaunchpadCelebrities).ubuntu.main_archive
@@ -118,16 +127,22 @@ class SnapArchiveWidget(BrowserWidget, InputWidget):
                 ppa = self.ppa_widget.getInputValue()
             except MissingInputError:
                 raise WidgetInputError(
-                    self.name, self.label,
-                    LaunchpadValidationError("Please choose a PPA."))
+                    self.name,
+                    self.label,
+                    LaunchpadValidationError("Please choose a PPA."),
+                )
             except ConversionError:
                 entered_name = self.request.form_ng.getOne(
-                    "%s.ppa" % self.name)
+                    "%s.ppa" % self.name
+                )
                 raise WidgetInputError(
-                    self.name, self.label,
+                    self.name,
+                    self.label,
                     LaunchpadValidationError(
-                        "There is no PPA named '%s' registered in Launchpad." %
-                        entered_name))
+                        "There is no PPA named '%s' registered in Launchpad."
+                        % entered_name
+                    ),
+                )
             return ppa
 
     def error(self):
diff --git a/lib/lp/snappy/browser/widgets/snapbuildchannels.py b/lib/lp/snappy/browser/widgets/snapbuildchannels.py
index 8d0264d..17a6786 100644
--- a/lib/lp/snappy/browser/widgets/snapbuildchannels.py
+++ b/lib/lp/snappy/browser/widgets/snapbuildchannels.py
@@ -4,17 +4,13 @@
 """A widget for selecting source snap channels for builds."""
 
 __all__ = [
-    'SnapBuildChannelsWidget',
-    ]
+    "SnapBuildChannelsWidget",
+]
 
 from zope.browserpage import ViewPageTemplateFile
 from zope.formlib.interfaces import IInputWidget
 from zope.formlib.utility import setUpWidget
-from zope.formlib.widget import (
-    BrowserWidget,
-    InputErrors,
-    InputWidget,
-    )
+from zope.formlib.widget import BrowserWidget, InputErrors, InputWidget
 from zope.interface import implementer
 from zope.schema import TextLine
 from zope.security.proxy import isinstance as zope_isinstance
@@ -24,7 +20,7 @@ from lp.services.features import getFeatureFlag
 from lp.services.webapp.interfaces import (
     IAlwaysSubmittedWidget,
     ISingleLineWidgetLayout,
-    )
+)
 from lp.snappy.interfaces.snap import SNAP_SNAPCRAFT_CHANNEL_FEATURE_FLAG
 
 
@@ -39,34 +35,41 @@ class SnapBuildChannelsWidget(BrowserWidget, InputWidget):
     def __init__(self, context, request):
         super().__init__(context, request)
         self.hint = (
-            'The channels to use for build tools when building the snap '
-            'package.\n')
+            "The channels to use for build tools when building the snap "
+            "package.\n"
+        )
         default_snapcraft_channel = (
-            getFeatureFlag(SNAP_SNAPCRAFT_CHANNEL_FEATURE_FLAG) or "apt")
+            getFeatureFlag(SNAP_SNAPCRAFT_CHANNEL_FEATURE_FLAG) or "apt"
+        )
         if default_snapcraft_channel == "apt":
             self.hint += (
                 'If unset, or if the channel for snapcraft is set to "apt", '
-                'the default is to install snapcraft from the source archive '
-                'using apt.')
+                "the default is to install snapcraft from the source archive "
+                "using apt."
+            )
         else:
             self.hint += (
                 'If unset, the default is to install snapcraft from the "%s" '
                 'channel.  Setting the channel for snapcraft to "apt" causes '
-                'snapcraft to be installed from the source archive using '
-                'apt.' % default_snapcraft_channel)
+                "snapcraft to be installed from the source archive using "
+                "apt." % default_snapcraft_channel
+            )
 
     def setUpSubWidgets(self):
         if self._widgets_set_up:
             return
         fields = [
             TextLine(
-                __name__=snap_name, title="%s channel" % snap_name,
-                required=False)
+                __name__=snap_name,
+                title="%s channel" % snap_name,
+                required=False,
+            )
             for snap_name in self.snap_names
-            ]
+        ]
         for field in fields:
             setUpWidget(
-                self, field.__name__, field, IInputWidget, prefix=self.name)
+                self, field.__name__, field, IInputWidget, prefix=self.name
+            )
         self._widgets_set_up = True
 
     def setRenderedValue(self, value):
@@ -76,13 +79,15 @@ class SnapBuildChannelsWidget(BrowserWidget, InputWidget):
             value = {}
         for snap_name in self.snap_names:
             getattr(self, "%s_widget" % snap_name).setRenderedValue(
-                value.get(snap_name))
+                value.get(snap_name)
+            )
 
     def hasInput(self):
         """See `IInputWidget`."""
         return any(
             "%s.%s" % (self.name, snap_name) in self.request.form
-            for snap_name in self.snap_names)
+            for snap_name in self.snap_names
+        )
 
     def hasValidInput(self):
         """See `IInputWidget`."""
diff --git a/lib/lp/snappy/browser/widgets/storechannels.py b/lib/lp/snappy/browser/widgets/storechannels.py
index 225372a..271eac0 100644
--- a/lib/lp/snappy/browser/widgets/storechannels.py
+++ b/lib/lp/snappy/browser/widgets/storechannels.py
@@ -2,27 +2,20 @@
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 __all__ = [
-    'StoreChannelsWidget',
-    ]
+    "StoreChannelsWidget",
+]
 
 from zope.browserpage import ViewPageTemplateFile
-from zope.formlib.interfaces import (
-    IInputWidget,
-    WidgetInputError,
-    )
+from zope.formlib.interfaces import IInputWidget, WidgetInputError
 from zope.formlib.utility import setUpWidget
 from zope.formlib.widget import (
     BrowserWidget,
     CustomWidgetFactory,
     InputErrors,
     InputWidget,
-    )
+)
 from zope.interface import implementer
-from zope.schema import (
-    Choice,
-    List,
-    TextLine,
-    )
+from zope.schema import Choice, List, TextLine
 
 from lp import _
 from lp.app.errors import UnexpectedFormData
@@ -32,18 +25,18 @@ from lp.services.channels import (
     CHANNEL_COMPONENTS_DELIMITER,
     channel_list_to_string,
     channel_string_to_list,
-    )
+)
 from lp.services.webapp.interfaces import (
     IAlwaysSubmittedWidget,
     ISingleLineWidgetLayout,
-    )
+)
 
 
 @implementer(ISingleLineWidgetLayout, IAlwaysSubmittedWidget, IInputWidget)
 class StoreChannelsWidget(BrowserWidget, InputWidget):
 
     template = ViewPageTemplateFile("templates/storechannels.pt")
-    _default_track = 'latest'
+    _default_track = "latest"
     _widgets_set_up = False
 
     def __init__(self, field, value_type, request):
@@ -57,33 +50,45 @@ class StoreChannelsWidget(BrowserWidget, InputWidget):
             return
         fields = [
             TextLine(
-                __name__="track", title="Track", required=False,
+                __name__="track",
+                title="Track",
+                required=False,
                 description=_(
                     "Track defines a series for your software. "
                     "If not specified, the default track ('latest') is "
-                    "assumed.")),
+                    "assumed."
+                ),
+            ),
             List(
-                __name__="risks", title="Risk", required=False,
+                __name__="risks",
+                title="Risk",
+                required=False,
                 value_type=Choice(vocabulary="SnapStoreChannel"),
-                description=_("Risks denote the stability of your software.")),
+                description=_("Risks denote the stability of your software."),
+            ),
             TextLine(
-                __name__="branch", title="Branch", required=False,
+                __name__="branch",
+                title="Branch",
+                required=False,
                 description=_(
                     "Branches provide users with an easy way to test bug "
                     "fixes.  They are temporary and created on demand.  If "
-                    "not specified, no branch is used.")),
-            ]
+                    "not specified, no branch is used."
+                ),
+            ),
+        ]
 
         self.risks_widget = CustomWidgetFactory(LabeledMultiCheckBoxWidget)
         for field in fields:
             setUpWidget(
-                self, field.__name__, field, IInputWidget, prefix=self.name)
-        self.risks_widget.orientation = 'horizontal'
+                self, field.__name__, field, IInputWidget, prefix=self.name
+            )
+        self.risks_widget.orientation = "horizontal"
         self._widgets_set_up = True
 
     @property
     def has_risks_vocabulary(self):
-        risks_widget = getattr(self, 'risks_widget', None)
+        risks_widget = getattr(self, "risks_widget", None)
         return risks_widget and bool(risks_widget.vocabulary)
 
     def setRenderedValue(self, value):
@@ -102,10 +107,12 @@ class StoreChannelsWidget(BrowserWidget, InputWidget):
                 branches.add(branch)
             if len(tracks) != 1:
                 raise ValueError(
-                    "Channels belong to different tracks: %r" % value)
+                    "Channels belong to different tracks: %r" % value
+                )
             if len(branches) != 1:
                 raise ValueError(
-                    "Channels belong to different branches: %r" % value)
+                    "Channels belong to different branches: %r" % value
+                )
             track = tracks.pop()
             self.track_widget.setRenderedValue(track)
             self.risks_widget.setRenderedValue(risks)
@@ -138,16 +145,21 @@ class StoreChannelsWidget(BrowserWidget, InputWidget):
         branch = self.branch_widget.getInputValue()
         if track and CHANNEL_COMPONENTS_DELIMITER in track:
             error_msg = "Track name cannot include '%s'." % (
-                CHANNEL_COMPONENTS_DELIMITER)
+                CHANNEL_COMPONENTS_DELIMITER
+            )
             raise WidgetInputError(
-                self.name, self.label, LaunchpadValidationError(error_msg))
+                self.name, self.label, LaunchpadValidationError(error_msg)
+            )
         if branch and CHANNEL_COMPONENTS_DELIMITER in branch:
             error_msg = "Branch name cannot include '%s'." % (
-                CHANNEL_COMPONENTS_DELIMITER)
+                CHANNEL_COMPONENTS_DELIMITER
+            )
             raise WidgetInputError(
-                self.name, self.label, LaunchpadValidationError(error_msg))
+                self.name, self.label, LaunchpadValidationError(error_msg)
+            )
         channels = [
-            channel_list_to_string(track, risk, branch) for risk in risks]
+            channel_list_to_string(track, risk, branch) for risk in risks
+        ]
         return channels
 
     def error(self):
diff --git a/lib/lp/snappy/browser/widgets/tests/test_snaparchivewidget.py b/lib/lp/snappy/browser/widgets/tests/test_snaparchivewidget.py
index 5116684..bd11e85 100644
--- a/lib/lp/snappy/browser/widgets/tests/test_snaparchivewidget.py
+++ b/lib/lp/snappy/browser/widgets/tests/test_snaparchivewidget.py
@@ -1,20 +1,17 @@
 # Copyright 2015-2019 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
-from functools import partial
 import re
+from functools import partial
 
 from lazr.restful.fields import Reference
-from testscenarios import (
-    load_tests_apply_scenarios,
-    WithScenarios,
-    )
+from testscenarios import WithScenarios, load_tests_apply_scenarios
 from zope.component import getUtility
 from zope.formlib.interfaces import (
     IBrowserWidget,
     IInputWidget,
     WidgetInputError,
-    )
+)
 
 from lp.app.interfaces.launchpad import ILaunchpadCelebrities
 from lp.app.validators import LaunchpadValidationError
@@ -26,10 +23,7 @@ from lp.snappy.interfaces.snap import ISnap
 from lp.soyuz.enums import ArchivePurpose
 from lp.soyuz.interfaces.archive import IArchive
 from lp.soyuz.vocabularies import PPAVocabulary
-from lp.testing import (
-    TestCaseWithFactory,
-    verifyObject,
-    )
+from lp.testing import TestCaseWithFactory, verifyObject
 from lp.testing.layers import DatabaseFunctionalLayer
 
 
@@ -53,11 +47,13 @@ class TestSnapArchiveWidget(WithScenarios, TestCaseWithFactory):
 
     scenarios = [
         ("Snap", {"context_factory": make_snap}),
-        ("Snap with no distroseries",
-         {"context_factory": partial(make_snap, distroseries=None)}),
+        (
+            "Snap with no distroseries",
+            {"context_factory": partial(make_snap, distroseries=None)},
+        ),
         ("Branch", {"context_factory": make_branch}),
         ("GitRepository", {"context_factory": make_git_repository}),
-        ]
+    ]
 
     def setUp(self):
         super().setUp()
@@ -75,7 +71,8 @@ class TestSnapArchiveWidget(WithScenarios, TestCaseWithFactory):
     def test_template(self):
         self.assertTrue(
             self.widget.template.filename.endswith("snaparchive.pt"),
-            "Template was not set up.")
+            "Template was not set up.",
+        )
 
     def test_default_option(self):
         # The primary field is the default option.
@@ -86,7 +83,8 @@ class TestSnapArchiveWidget(WithScenarios, TestCaseWithFactory):
         self.widget.setUpSubWidgets()
         self.assertTrue(self.widget._widgets_set_up)
         self.assertIsInstance(
-            self.widget.ppa_widget.context.vocabulary, PPAVocabulary)
+            self.widget.ppa_widget.context.vocabulary, PPAVocabulary
+        )
 
     def test_setUpSubWidgets_second_call(self):
         # The setUpSubWidgets method exits early if a flag is set to
@@ -101,55 +99,61 @@ class TestSnapArchiveWidget(WithScenarios, TestCaseWithFactory):
         self.widget.setUpSubWidgets()
         self.widget.setUpOptions()
         self.assertEqual(
-            '<input class="radioType" checked="checked" ' +
-            'id="field.archive.option.primary" name="field.archive" '
+            '<input class="radioType" checked="checked" '
+            + 'id="field.archive.option.primary" name="field.archive" '
             'type="radio" value="primary" />',
-            self.widget.options["primary"])
+            self.widget.options["primary"],
+        )
         self.assertEqual(
-            '<input class="radioType" ' +
-            'id="field.archive.option.ppa" name="field.archive" '
+            '<input class="radioType" '
+            + 'id="field.archive.option.ppa" name="field.archive" '
             'type="radio" value="ppa" />',
-            self.widget.options["ppa"])
+            self.widget.options["ppa"],
+        )
 
     def test_setUpOptions_primary_checked(self):
         # The primary radio button is selected when the form is submitted
         # when the archive field's value is 'primary'.
         form = {
             "field.archive": "primary",
-            }
+        }
         self.widget.request = LaunchpadTestRequest(form=form)
         self.widget.setUpSubWidgets()
         self.widget.setUpOptions()
         self.assertEqual(
-            '<input class="radioType" checked="checked" ' +
-            'id="field.archive.option.primary" name="field.archive" '
+            '<input class="radioType" checked="checked" '
+            + 'id="field.archive.option.primary" name="field.archive" '
             'type="radio" value="primary" />',
-            self.widget.options["primary"])
+            self.widget.options["primary"],
+        )
         self.assertEqual(
-            '<input class="radioType" ' +
-            'id="field.archive.option.ppa" name="field.archive" '
+            '<input class="radioType" '
+            + 'id="field.archive.option.ppa" name="field.archive" '
             'type="radio" value="ppa" />',
-            self.widget.options["ppa"])
+            self.widget.options["ppa"],
+        )
 
     def test_setUpOptions_ppa_checked(self):
         # The ppa radio button is selected when the form is submitted when
         # the archive field's value is 'ppa'.
         form = {
             "field.archive": "ppa",
-            }
+        }
         self.widget.request = LaunchpadTestRequest(form=form)
         self.widget.setUpSubWidgets()
         self.widget.setUpOptions()
         self.assertEqual(
-            '<input class="radioType" ' +
-            'id="field.archive.option.primary" name="field.archive" '
+            '<input class="radioType" '
+            + 'id="field.archive.option.primary" name="field.archive" '
             'type="radio" value="primary" />',
-            self.widget.options["primary"])
+            self.widget.options["primary"],
+        )
         self.assertEqual(
-            '<input class="radioType" checked="checked" ' +
-            'id="field.archive.option.ppa" name="field.archive" '
+            '<input class="radioType" checked="checked" '
+            + 'id="field.archive.option.ppa" name="field.archive" '
             'type="radio" value="ppa" />',
-            self.widget.options["ppa"])
+            self.widget.options["ppa"],
+        )
 
     def test_setRenderedValue_primary(self):
         # Passing a primary archive will set the widget's render state to
@@ -165,7 +169,8 @@ class TestSnapArchiveWidget(WithScenarios, TestCaseWithFactory):
         self.widget.setUpSubWidgets()
         archive = self.factory.makeArchive(
             distribution=self.distroseries.distribution,
-            purpose=ArchivePurpose.PPA)
+            purpose=ArchivePurpose.PPA,
+        )
         self.widget.setRenderedValue(archive)
         self.widget.setRenderedValue(self.distroseries.main_archive)
         self.assertEqual("primary", self.widget.default_option)
@@ -176,7 +181,8 @@ class TestSnapArchiveWidget(WithScenarios, TestCaseWithFactory):
         self.widget.setUpSubWidgets()
         archive = self.factory.makeArchive(
             distribution=self.distroseries.distribution,
-            purpose=ArchivePurpose.PPA)
+            purpose=ArchivePurpose.PPA,
+        )
         self.widget.setRenderedValue(archive)
         self.assertEqual("ppa", self.widget.default_option)
         self.assertEqual(archive, self.widget.ppa_widget._getCurrentValue())
@@ -189,7 +195,8 @@ class TestSnapArchiveWidget(WithScenarios, TestCaseWithFactory):
     def test_hasInput_true(self):
         # hasInput is false when the widget's name is in the form data.
         self.widget.request = LaunchpadTestRequest(
-            form={"field.archive": "primary"})
+            form={"field.archive": "primary"}
+        )
         self.assertTrue(self.widget.hasInput())
 
     def test_hasValidInput_false(self):
@@ -198,7 +205,7 @@ class TestSnapArchiveWidget(WithScenarios, TestCaseWithFactory):
         form = {
             "field.archive": "ppa",
             "field.archive.ppa": "non-existent",
-            }
+        }
         self.widget.request = LaunchpadTestRequest(form=form)
         self.assertFalse(self.widget.hasValidInput())
 
@@ -208,7 +215,7 @@ class TestSnapArchiveWidget(WithScenarios, TestCaseWithFactory):
         form = {
             "field.archive": "ppa",
             "field.archive.ppa": archive.reference,
-            }
+        }
         self.widget.request = LaunchpadTestRequest(form=form)
         self.assertTrue(self.widget.hasValidInput())
 
@@ -223,13 +230,17 @@ class TestSnapArchiveWidget(WithScenarios, TestCaseWithFactory):
         # context's primary archive if the context is a Snap and has a
         # distroseries, or the Ubuntu primary archive otherwise.
         self.widget.request = LaunchpadTestRequest(
-            form={"field.archive": "primary"})
-        if (ISnap.providedBy(self.context) and
-                self.context.distro_series is not None):
+            form={"field.archive": "primary"}
+        )
+        if (
+            ISnap.providedBy(self.context)
+            and self.context.distro_series is not None
+        ):
             expected_main_archive = self.context.distro_series.main_archive
         else:
-            expected_main_archive = (
-                getUtility(ILaunchpadCelebrities).ubuntu.main_archive)
+            expected_main_archive = getUtility(
+                ILaunchpadCelebrities
+            ).ubuntu.main_archive
         self.assertEqual(expected_main_archive, self.widget.getInputValue())
 
     def test_getInputValue_ppa_missing(self):
@@ -242,10 +253,11 @@ class TestSnapArchiveWidget(WithScenarios, TestCaseWithFactory):
         form = {
             "field.archive": "ppa",
             "field.archive.ppa": "non-existent",
-            }
+        }
         self.assertGetInputValueError(
             form,
-            "There is no PPA named 'non-existent' registered in Launchpad.")
+            "There is no PPA named 'non-existent' registered in Launchpad.",
+        )
 
     def test_getInputValue_ppa(self):
         # The field value is the PPA when the ppa radio button is selected
@@ -254,7 +266,7 @@ class TestSnapArchiveWidget(WithScenarios, TestCaseWithFactory):
         form = {
             "field.archive": "ppa",
             "field.archive.ppa": archive.reference,
-            }
+        }
         self.widget.request = LaunchpadTestRequest(form=form)
         self.assertEqual(archive, self.widget.getInputValue())
 
@@ -270,7 +282,7 @@ class TestSnapArchiveWidget(WithScenarios, TestCaseWithFactory):
             "field.archive.option.primary",
             "field.archive.option.ppa",
             "field.archive.ppa",
-            ]
+        ]
         ids = [field["id"] for field in fields]
         self.assertContentEqual(expected_ids, ids)
 
diff --git a/lib/lp/snappy/browser/widgets/tests/test_snapbuildchannelswidget.py b/lib/lp/snappy/browser/widgets/tests/test_snapbuildchannelswidget.py
index 543d4ed..9b71b4c 100644
--- a/lib/lp/snappy/browser/widgets/tests/test_snapbuildchannelswidget.py
+++ b/lib/lp/snappy/browser/widgets/tests/test_snapbuildchannelswidget.py
@@ -3,23 +3,15 @@
 
 import re
 
-from zope.formlib.interfaces import (
-    IBrowserWidget,
-    IInputWidget,
-    )
+from zope.formlib.interfaces import IBrowserWidget, IInputWidget
 from zope.schema import Dict
 
 from lp.services.beautifulsoup import BeautifulSoup
 from lp.services.features.testing import FeatureFixture
 from lp.services.webapp.servers import LaunchpadTestRequest
-from lp.snappy.browser.widgets.snapbuildchannels import (
-    SnapBuildChannelsWidget,
-    )
+from lp.snappy.browser.widgets.snapbuildchannels import SnapBuildChannelsWidget
 from lp.snappy.interfaces.snap import SNAP_SNAPCRAFT_CHANNEL_FEATURE_FLAG
-from lp.testing import (
-    TestCaseWithFactory,
-    verifyObject,
-    )
+from lp.testing import TestCaseWithFactory, verifyObject
 from lp.testing.layers import DatabaseFunctionalLayer
 
 
@@ -31,7 +23,8 @@ class TestSnapBuildChannelsWidget(TestCaseWithFactory):
         super().setUp()
         field = Dict(
             __name__="auto_build_channels",
-            title="Source snap channels for automatic builds")
+            title="Source snap channels for automatic builds",
+        )
         self.context = self.factory.makeSnap()
         self.field = field.bind(self.context)
         self.request = LaunchpadTestRequest()
@@ -44,41 +37,47 @@ class TestSnapBuildChannelsWidget(TestCaseWithFactory):
     def test_template(self):
         self.assertTrue(
             self.widget.template.filename.endswith("snapbuildchannels.pt"),
-            "Template was not set up.")
+            "Template was not set up.",
+        )
 
     def test_hint_no_feature_flag(self):
         self.assertEqual(
-            'The channels to use for build tools when building the snap '
-            'package.\n'
+            "The channels to use for build tools when building the snap "
+            "package.\n"
             'If unset, or if the channel for snapcraft is set to "apt", '
-            'the default is to install snapcraft from the source archive '
-            'using apt.',
-            self.widget.hint)
+            "the default is to install snapcraft from the source archive "
+            "using apt.",
+            self.widget.hint,
+        )
 
     def test_hint_feature_flag_apt(self):
         self.useFixture(
-            FeatureFixture({SNAP_SNAPCRAFT_CHANNEL_FEATURE_FLAG: "apt"}))
+            FeatureFixture({SNAP_SNAPCRAFT_CHANNEL_FEATURE_FLAG: "apt"})
+        )
         widget = SnapBuildChannelsWidget(self.field, self.request)
         self.assertEqual(
-            'The channels to use for build tools when building the snap '
-            'package.\n'
+            "The channels to use for build tools when building the snap "
+            "package.\n"
             'If unset, or if the channel for snapcraft is set to "apt", '
-            'the default is to install snapcraft from the source archive '
-            'using apt.',
-            widget.hint)
+            "the default is to install snapcraft from the source archive "
+            "using apt.",
+            widget.hint,
+        )
 
     def test_hint_feature_flag_real_channel(self):
         self.useFixture(
-            FeatureFixture({SNAP_SNAPCRAFT_CHANNEL_FEATURE_FLAG: "stable"}))
+            FeatureFixture({SNAP_SNAPCRAFT_CHANNEL_FEATURE_FLAG: "stable"})
+        )
         widget = SnapBuildChannelsWidget(self.field, self.request)
         self.assertEqual(
-            'The channels to use for build tools when building the snap '
-            'package.\n'
+            "The channels to use for build tools when building the snap "
+            "package.\n"
             'If unset, the default is to install snapcraft from the "stable" '
             'channel.  Setting the channel for snapcraft to "apt" causes '
-            'snapcraft to be installed from the source archive using '
-            'apt.',
-            widget.hint)
+            "snapcraft to be installed from the source archive using "
+            "apt.",
+            widget.hint,
+        )
 
     def test_setUpSubWidgets_first_call(self):
         # The subwidgets are set up and a flag is set.
@@ -125,20 +124,30 @@ class TestSnapBuildChannelsWidget(TestCaseWithFactory):
         self.assertIsNone(self.widget.core20_widget._getCurrentValue())
         self.assertIsNone(self.widget.core22_widget._getCurrentValue())
         self.assertEqual(
-            "stable", self.widget.snapcraft_widget._getCurrentValue())
+            "stable", self.widget.snapcraft_widget._getCurrentValue()
+        )
 
     def test_setRenderedValue_all_channels(self):
         self.widget.setRenderedValue(
-            {"core": "candidate", "core18": "beta", "core20": "edge",
-             "core22": "edge/feature", "snapcraft": "stable"})
+            {
+                "core": "candidate",
+                "core18": "beta",
+                "core20": "edge",
+                "core22": "edge/feature",
+                "snapcraft": "stable",
+            }
+        )
         self.assertEqual(
-            "candidate", self.widget.core_widget._getCurrentValue())
+            "candidate", self.widget.core_widget._getCurrentValue()
+        )
         self.assertEqual("beta", self.widget.core18_widget._getCurrentValue())
         self.assertEqual("edge", self.widget.core20_widget._getCurrentValue())
         self.assertEqual(
-            "edge/feature", self.widget.core22_widget._getCurrentValue())
+            "edge/feature", self.widget.core22_widget._getCurrentValue()
+        )
         self.assertEqual(
-            "stable", self.widget.snapcraft_widget._getCurrentValue())
+            "stable", self.widget.snapcraft_widget._getCurrentValue()
+        )
 
     def test_hasInput_false(self):
         # hasInput is false when there are no channels in the form data.
@@ -148,7 +157,8 @@ class TestSnapBuildChannelsWidget(TestCaseWithFactory):
     def test_hasInput_true(self):
         # hasInput is true when there are channels in the form data.
         self.widget.request = LaunchpadTestRequest(
-            form={"field.auto_build_channels.snapcraft": "stable"})
+            form={"field.auto_build_channels.snapcraft": "stable"}
+        )
         self.assertTrue(self.widget.hasInput())
 
     def test_hasValidInput_true(self):
@@ -161,7 +171,7 @@ class TestSnapBuildChannelsWidget(TestCaseWithFactory):
             "field.auto_build_channels.core20": "edge",
             "field.auto_build_channels.core22": "edge/feature",
             "field.auto_build_channels.snapcraft": "stable",
-            }
+        }
         self.widget.request = LaunchpadTestRequest(form=form)
         self.assertTrue(self.widget.hasValidInput())
 
@@ -172,12 +182,17 @@ class TestSnapBuildChannelsWidget(TestCaseWithFactory):
             "field.auto_build_channels.core20": "edge",
             "field.auto_build_channels.core22": "edge/feature",
             "field.auto_build_channels.snapcraft": "stable",
-            }
+        }
         self.widget.request = LaunchpadTestRequest(form=form)
         self.assertEqual(
-            {"core18": "beta", "core20": "edge", "core22": "edge/feature",
-             "snapcraft": "stable"},
-            self.widget.getInputValue())
+            {
+                "core18": "beta",
+                "core20": "edge",
+                "core22": "edge/feature",
+                "snapcraft": "stable",
+            },
+            self.widget.getInputValue(),
+        )
 
     def test_call(self):
         # The __call__ method sets up the widgets.
@@ -195,6 +210,6 @@ class TestSnapBuildChannelsWidget(TestCaseWithFactory):
             "field.auto_build_channels.core20",
             "field.auto_build_channels.core22",
             "field.auto_build_channels.snapcraft",
-            ]
+        ]
         ids = [field["id"] for field in fields]
         self.assertContentEqual(expected_ids, ids)
diff --git a/lib/lp/snappy/browser/widgets/tests/test_storechannelswidget.py b/lib/lp/snappy/browser/widgets/tests/test_storechannelswidget.py
index 9c9c79f..83d42be 100644
--- a/lib/lp/snappy/browser/widgets/tests/test_storechannelswidget.py
+++ b/lib/lp/snappy/browser/widgets/tests/test_storechannelswidget.py
@@ -7,7 +7,7 @@ from zope.formlib.interfaces import (
     IBrowserWidget,
     IInputWidget,
     WidgetInputError,
-    )
+)
 from zope.schema import List
 
 from lp.app.validators import LaunchpadValidationError
@@ -17,10 +17,7 @@ from lp.services.webapp.escaping import html_escape
 from lp.services.webapp.servers import LaunchpadTestRequest
 from lp.snappy.browser.widgets.storechannels import StoreChannelsWidget
 from lp.snappy.vocabularies import SnapStoreChannelVocabulary
-from lp.testing import (
-    TestCaseWithFactory,
-    verifyObject,
-    )
+from lp.testing import TestCaseWithFactory, verifyObject
 from lp.testing.layers import DatabaseFunctionalLayer
 
 
@@ -43,7 +40,8 @@ class TestStoreChannelsWidget(TestCaseWithFactory):
     def test_template(self):
         self.assertTrue(
             self.widget.template.filename.endswith("storechannels.pt"),
-            "Template was not set up.")
+            "Template was not set up.",
+        )
 
     def test_setUpSubWidgets_first_call(self):
         # The subwidgets are set up and a flag is set.
@@ -51,7 +49,8 @@ class TestStoreChannelsWidget(TestCaseWithFactory):
         self.assertTrue(self.widget._widgets_set_up)
         self.assertIsNotNone(getattr(self.widget, "track_widget", None))
         self.assertIsInstance(
-            self.widget.risks_widget.vocabulary, SnapStoreChannelVocabulary)
+            self.widget.risks_widget.vocabulary, SnapStoreChannelVocabulary
+        )
         self.assertTrue(self.widget.has_risks_vocabulary)
         self.assertIsNotNone(getattr(self.widget, "branch_widget", None))
 
@@ -72,7 +71,7 @@ class TestStoreChannelsWidget(TestCaseWithFactory):
 
     def test_setRenderedValue_no_track_or_branch(self):
         # Channels do not include a track or branch
-        risks = ['candidate', 'edge']
+        risks = ["candidate", "edge"]
         self.widget.setRenderedValue(risks)
         self.assertIsNone(self.widget.track_widget._getCurrentValue())
         self.assertEqual(risks, self.widget.risks_widget._getCurrentValue())
@@ -80,32 +79,37 @@ class TestStoreChannelsWidget(TestCaseWithFactory):
 
     def test_setRenderedValue_with_track(self):
         # Channels including a track
-        channels = ['2.2/candidate', '2.2/edge']
+        channels = ["2.2/candidate", "2.2/edge"]
         self.widget.setRenderedValue(channels)
-        self.assertEqual('2.2', self.widget.track_widget._getCurrentValue())
+        self.assertEqual("2.2", self.widget.track_widget._getCurrentValue())
         self.assertEqual(
-            ['candidate', 'edge'], self.widget.risks_widget._getCurrentValue())
+            ["candidate", "edge"], self.widget.risks_widget._getCurrentValue()
+        )
         self.assertIsNone(self.widget.branch_widget._getCurrentValue())
 
     def test_setRenderedValue_with_branch(self):
         # Channels including a branch
-        channels = ['candidate/fix-123', 'edge/fix-123']
+        channels = ["candidate/fix-123", "edge/fix-123"]
         self.widget.setRenderedValue(channels)
         self.assertIsNone(self.widget.track_widget._getCurrentValue())
         self.assertEqual(
-            ['candidate', 'edge'], self.widget.risks_widget._getCurrentValue())
+            ["candidate", "edge"], self.widget.risks_widget._getCurrentValue()
+        )
         self.assertEqual(
-            'fix-123', self.widget.branch_widget._getCurrentValue())
+            "fix-123", self.widget.branch_widget._getCurrentValue()
+        )
 
     def test_setRenderedValue_with_track_and_branch(self):
         # Channels including a track and branch
-        channels = ['2.2/candidate/fix-123', '2.2/edge/fix-123']
+        channels = ["2.2/candidate/fix-123", "2.2/edge/fix-123"]
         self.widget.setRenderedValue(channels)
-        self.assertEqual('2.2', self.widget.track_widget._getCurrentValue())
+        self.assertEqual("2.2", self.widget.track_widget._getCurrentValue())
         self.assertEqual(
-            ['candidate', 'edge'], self.widget.risks_widget._getCurrentValue())
+            ["candidate", "edge"], self.widget.risks_widget._getCurrentValue()
+        )
         self.assertEqual(
-            'fix-123', self.widget.branch_widget._getCurrentValue())
+            "fix-123", self.widget.branch_widget._getCurrentValue()
+        )
 
     def test_setRenderedValue_invalid_value(self):
         # Multiple channels, different tracks or branches, unsupported
@@ -113,30 +117,36 @@ class TestStoreChannelsWidget(TestCaseWithFactory):
             ValueError,
             "Channels belong to different tracks: "
             "['2.2/candidate', '2.1/edge']",
-            self.widget.setRenderedValue, ['2.2/candidate', '2.1/edge'])
+            self.widget.setRenderedValue,
+            ["2.2/candidate", "2.1/edge"],
+        )
         self.assertRaisesWithContent(
             ValueError,
             "Channels belong to different branches: "
             "['candidate/fix-123', 'edge/fix-124']",
             self.widget.setRenderedValue,
-            ['candidate/fix-123', 'edge/fix-124'])
+            ["candidate/fix-123", "edge/fix-124"],
+        )
         self.assertRaisesWithContent(
             ValueError,
             "Channels belong to different tracks: "
             "['2.2/candidate', 'edge/fix-123']",
             self.widget.setRenderedValue,
-            ['2.2/candidate', 'edge/fix-123'])
+            ["2.2/candidate", "edge/fix-123"],
+        )
 
     def test_hasInput_false(self):
         # hasInput is false when there is no risk set in the form data.
         self.widget.request = LaunchpadTestRequest(
-            form={"field.channels.track": "track"})
+            form={"field.channels.track": "track"}
+        )
         self.assertFalse(self.widget.hasInput())
 
     def test_hasInput_true(self):
         # hasInput is true if there are risks set in the form data.
         self.widget.request = LaunchpadTestRequest(
-            form={"field.channels.risks": ["beta"]})
+            form={"field.channels.risks": ["beta"]}
+        )
         self.assertTrue(self.widget.hasInput())
 
     def test_hasValidInput_false(self):
@@ -146,7 +156,7 @@ class TestStoreChannelsWidget(TestCaseWithFactory):
             "field.channels.track": "",
             "field.channels.risks": ["invalid"],
             "field.channels.branch": "",
-            }
+        }
         self.widget.request = LaunchpadTestRequest(form=form)
         self.assertFalse(self.widget.hasValidInput())
 
@@ -156,7 +166,7 @@ class TestStoreChannelsWidget(TestCaseWithFactory):
             "field.channels.track": "track",
             "field.channels.risks": ["stable", "beta"],
             "field.channels.branch": "branch",
-            }
+        }
         self.widget.request = LaunchpadTestRequest(form=form)
         self.assertTrue(self.widget.hasValidInput())
 
@@ -172,7 +182,7 @@ class TestStoreChannelsWidget(TestCaseWithFactory):
             "field.channels.track": "tra/ck",
             "field.channels.risks": ["beta"],
             "field.channels.branch": "",
-            }
+        }
         self.assertGetInputValueError(form, "Track name cannot include '/'.")
 
     def test_getInputValue_invalid_branch(self):
@@ -181,7 +191,7 @@ class TestStoreChannelsWidget(TestCaseWithFactory):
             "field.channels.track": "",
             "field.channels.risks": ["beta"],
             "field.channels.branch": "bra/nch",
-            }
+        }
         self.assertGetInputValueError(form, "Branch name cannot include '/'.")
 
     def test_getInputValue_no_track_or_branch(self):
@@ -190,7 +200,8 @@ class TestStoreChannelsWidget(TestCaseWithFactory):
                 "field.channels.track": "",
                 "field.channels.risks": ["beta", "edge"],
                 "field.channels.branch": "",
-                })
+            }
+        )
         expected = ["beta", "edge"]
         self.assertEqual(expected, self.widget.getInputValue())
 
@@ -200,7 +211,8 @@ class TestStoreChannelsWidget(TestCaseWithFactory):
                 "field.channels.track": "track",
                 "field.channels.risks": ["beta", "edge"],
                 "field.channels.branch": "",
-                })
+            }
+        )
         expected = ["track/beta", "track/edge"]
         self.assertEqual(expected, self.widget.getInputValue())
 
@@ -210,7 +222,8 @@ class TestStoreChannelsWidget(TestCaseWithFactory):
                 "field.channels.track": "",
                 "field.channels.risks": ["beta", "edge"],
                 "field.channels.branch": "fix-123",
-                })
+            }
+        )
         expected = ["beta/fix-123", "edge/fix-123"]
         self.assertEqual(expected, self.widget.getInputValue())
 
@@ -220,7 +233,8 @@ class TestStoreChannelsWidget(TestCaseWithFactory):
                 "field.channels.track": "track",
                 "field.channels.risks": ["beta", "edge"],
                 "field.channels.branch": "fix-123",
-                })
+            }
+        )
         expected = ["track/beta/fix-123", "track/edge/fix-123"]
         self.assertEqual(expected, self.widget.getInputValue())
 
@@ -232,7 +246,8 @@ class TestStoreChannelsWidget(TestCaseWithFactory):
         soup = BeautifulSoup(markup)
         fields = soup.find_all(["input"], {"id": re.compile(".*")})
         expected_ids = [
-            "field.channels.risks.%d" % i for i in range(len(StoreRisk))]
+            "field.channels.risks.%d" % i for i in range(len(StoreRisk))
+        ]
         expected_ids.append("field.channels.track")
         expected_ids.append("field.channels.branch")
         ids = [field["id"] for field in fields]
diff --git a/lib/lp/snappy/interfaces/snap.py b/lib/lp/snappy/interfaces/snap.py
index 25b1f51..dd70dcd 100644
--- a/lib/lp/snappy/interfaces/snap.py
+++ b/lib/lp/snappy/interfaces/snap.py
@@ -4,46 +4,44 @@
 """Snap package interfaces."""
 
 __all__ = [
-    'BadMacaroon',
-    'BadSnapSearchContext',
-    'BadSnapSource',
-    'CannotAuthorizeStoreUploads',
-    'CannotFetchSnapcraftYaml',
-    'CannotModifySnapProcessor',
-    'CannotParseSnapcraftYaml',
-    'CannotRequestAutoBuilds',
-    'DuplicateSnapName',
-    'ISnap',
-    'ISnapBuildRequest',
-    'ISnapEdit',
-    'ISnapSet',
-    'ISnapView',
-    'MissingSnapcraftYaml',
-    'NoSourceForSnap',
-    'NoSuchSnap',
-    'SNAP_PRIVATE_FEATURE_FLAG',
-    'SNAP_SNAPCRAFT_CHANNEL_FEATURE_FLAG',
-    'SNAP_TESTING_FLAGS',
-    'SNAP_WEBHOOKS_FEATURE_FLAG',
-    'SnapAuthorizationBadGeneratedMacaroon',
-    'SnapBuildAlreadyPending',
-    'SnapBuildArchiveOwnerMismatch',
-    'SnapBuildDisallowedArchitecture',
-    'SnapBuildRequestStatus',
-    'SnapNotOwner',
-    'SnapPrivacyMismatch',
-    'SnapPrivacyPillarError',
-    'SnapPrivateFeatureDisabled',
-    ]
+    "BadMacaroon",
+    "BadSnapSearchContext",
+    "BadSnapSource",
+    "CannotAuthorizeStoreUploads",
+    "CannotFetchSnapcraftYaml",
+    "CannotModifySnapProcessor",
+    "CannotParseSnapcraftYaml",
+    "CannotRequestAutoBuilds",
+    "DuplicateSnapName",
+    "ISnap",
+    "ISnapBuildRequest",
+    "ISnapEdit",
+    "ISnapSet",
+    "ISnapView",
+    "MissingSnapcraftYaml",
+    "NoSourceForSnap",
+    "NoSuchSnap",
+    "SNAP_PRIVATE_FEATURE_FLAG",
+    "SNAP_SNAPCRAFT_CHANNEL_FEATURE_FLAG",
+    "SNAP_TESTING_FLAGS",
+    "SNAP_WEBHOOKS_FEATURE_FLAG",
+    "SnapAuthorizationBadGeneratedMacaroon",
+    "SnapBuildAlreadyPending",
+    "SnapBuildArchiveOwnerMismatch",
+    "SnapBuildDisallowedArchitecture",
+    "SnapBuildRequestStatus",
+    "SnapNotOwner",
+    "SnapPrivacyMismatch",
+    "SnapPrivacyPillarError",
+    "SnapPrivateFeatureDisabled",
+]
 
 import http.client
 
-from lazr.enum import (
-    EnumeratedType,
-    Item,
-    )
+from lazr.enum import EnumeratedType, Item
 from lazr.lifecycle.snapshot import doNotSnapshot
 from lazr.restful.declarations import (
+    REQUEST_USER,
     call_with,
     collection_default_content,
     error_status,
@@ -58,18 +56,10 @@ from lazr.restful.declarations import (
     operation_parameters,
     operation_returns_collection_of,
     operation_returns_entry,
-    REQUEST_USER,
-    )
-from lazr.restful.fields import (
-    CollectionField,
-    Reference,
-    ReferenceChoice,
-    )
+)
+from lazr.restful.fields import CollectionField, Reference, ReferenceChoice
 from lazr.restful.interface import copy_field
-from zope.interface import (
-    Attribute,
-    Interface,
-    )
+from zope.interface import Attribute, Interface
 from zope.schema import (
     Bool,
     Choice,
@@ -80,11 +70,8 @@ from zope.schema import (
     Set,
     Text,
     TextLine,
-    )
-from zope.security.interfaces import (
-    Forbidden,
-    Unauthorized,
-    )
+)
+from zope.security.interfaces import Forbidden, Unauthorized
 
 from lp import _
 from lp.app.enums import InformationType
@@ -101,22 +88,17 @@ from lp.registry.interfaces.person import IPerson
 from lp.registry.interfaces.pocket import PackagePublishingPocket
 from lp.registry.interfaces.product import IProduct
 from lp.registry.interfaces.role import IHasOwner
-from lp.services.fields import (
-    PersonChoice,
-    PublicPersonChoice,
-    URIField,
-    )
+from lp.services.fields import PersonChoice, PublicPersonChoice, URIField
 from lp.services.webhooks.interfaces import IWebhookTarget
 from lp.snappy.interfaces.snapbase import ISnapBase
 from lp.snappy.interfaces.snappyseries import (
     ISnappyDistroSeries,
     ISnappySeries,
-    )
+)
 from lp.snappy.validators.channels import channels_validator
 from lp.soyuz.interfaces.archive import IArchive
 from lp.soyuz.interfaces.distroarchseries import IDistroArchSeries
 
-
 SNAP_PRIVATE_FEATURE_FLAG = "snap.allow_private"
 SNAP_SNAPCRAFT_CHANNEL_FEATURE_FLAG = "snap.channels.snapcraft"
 SNAP_WEBHOOKS_FEATURE_FLAG = "snap.webhooks.enabled"
@@ -125,7 +107,7 @@ SNAP_WEBHOOKS_FEATURE_FLAG = "snap.webhooks.enabled"
 SNAP_TESTING_FLAGS = {
     SNAP_PRIVATE_FEATURE_FLAG: "on",
     SNAP_WEBHOOKS_FEATURE_FLAG: "on",
-    }
+}
 
 
 @error_status(http.client.BAD_REQUEST)
@@ -134,7 +116,8 @@ class SnapBuildAlreadyPending(Exception):
 
     def __init__(self):
         super().__init__(
-            "An identical build of this snap package is already pending.")
+            "An identical build of this snap package is already pending."
+        )
 
 
 @error_status(http.client.FORBIDDEN)
@@ -152,7 +135,8 @@ class SnapBuildArchiveOwnerMismatch(Forbidden):
     def __init__(self):
         super().__init__(
             "Snap package builds against private archives are only allowed "
-            "if the snap package owner and the archive owner are equal.")
+            "if the snap package owner and the archive owner are equal."
+        )
 
 
 @error_status(http.client.BAD_REQUEST)
@@ -161,8 +145,9 @@ class SnapBuildDisallowedArchitecture(Exception):
 
     def __init__(self, das, pocket):
         super().__init__(
-            "This snap package is not allowed to build for %s/%s." %
-            (das.distroseries.getSuite(pocket), das.architecturetag))
+            "This snap package is not allowed to build for %s/%s."
+            % (das.distroseries.getSuite(pocket), das.architecturetag)
+        )
 
 
 @error_status(http.client.UNAUTHORIZED)
@@ -179,7 +164,8 @@ class DuplicateSnapName(Exception):
 
     def __init__(self):
         super().__init__(
-            "There is already a snap package with the same name and owner.")
+            "There is already a snap package with the same name and owner."
+        )
 
 
 @error_status(http.client.UNAUTHORIZED)
@@ -189,6 +175,7 @@ class SnapNotOwner(Unauthorized):
 
 class NoSuchSnap(NameLookupFailed):
     """The requested snap package does not exist."""
+
     _message_prefix = "No such snap package with this owner"
 
 
@@ -199,7 +186,8 @@ class NoSourceForSnap(Exception):
     def __init__(self):
         super().__init__(
             "New snap packages must have either a Bazaar branch or a Git "
-            "branch.")
+            "branch."
+        )
 
 
 @error_status(http.client.BAD_REQUEST)
@@ -213,8 +201,9 @@ class SnapPrivacyMismatch(Exception):
 
     def __init__(self, message=None):
         super().__init__(
-            message or
-            "Snap recipe contains private information and cannot be public.")
+            message
+            or "Snap recipe contains private information and cannot be public."
+        )
 
 
 @error_status(http.client.BAD_REQUEST)
@@ -223,7 +212,8 @@ class SnapPrivacyPillarError(Exception):
 
     def __init__(self, message=None):
         super().__init__(
-            message or "Private Snap recipes should have a pillar.")
+            message or "Private Snap recipes should have a pillar."
+        )
 
 
 class BadSnapSearchContext(Exception):
@@ -235,11 +225,12 @@ class CannotModifySnapProcessor(Exception):
     """Tried to enable or disable a restricted processor on an snap package."""
 
     _fmt = (
-        '%(processor)s is restricted, and may only be enabled or disabled '
-        'by administrators.')
+        "%(processor)s is restricted, and may only be enabled or disabled "
+        "by administrators."
+    )
 
     def __init__(self, processor):
-        super().__init__(self._fmt % {'processor': processor.name})
+        super().__init__(self._fmt % {"processor": processor.name})
 
 
 @error_status(http.client.BAD_REQUEST)
@@ -264,7 +255,8 @@ class CannotRequestAutoBuilds(Exception):
     def __init__(self, field):
         super().__init__(
             "This snap package cannot have automatic builds created for it "
-            "because %s is not set." % field)
+            "because %s is not set." % field
+        )
 
 
 class MissingSnapcraftYaml(Exception):
@@ -289,23 +281,29 @@ class CannotParseSnapcraftYaml(Exception):
 class SnapBuildRequestStatus(EnumeratedType):
     """The status of a request to build a snap package."""
 
-    PENDING = Item("""
+    PENDING = Item(
+        """
         Pending
 
         This snap build request is pending.
-        """)
+        """
+    )
 
-    FAILED = Item("""
+    FAILED = Item(
+        """
         Failed
 
         This snap build request failed.
-        """)
+        """
+    )
 
-    COMPLETED = Item("""
+    COMPLETED = Item(
+        """
         Completed
 
         This snap build request completed successfully.
-        """)
+        """
+    )
 
 
 # XXX cjwatson 2018-06-14 bug=760849: "beta" is a lie to get WADL
@@ -317,52 +315,89 @@ class ISnapBuildRequest(Interface):
 
     id = Int(title=_("ID"), required=True, readonly=True)
 
-    date_requested = exported(Datetime(
-        title=_("The time when this request was made"),
-        required=True, readonly=True))
+    date_requested = exported(
+        Datetime(
+            title=_("The time when this request was made"),
+            required=True,
+            readonly=True,
+        )
+    )
 
-    date_finished = exported(Datetime(
-        title=_("The time when this request finished"),
-        required=False, readonly=True))
+    date_finished = exported(
+        Datetime(
+            title=_("The time when this request finished"),
+            required=False,
+            readonly=True,
+        )
+    )
 
-    snap = exported(Reference(
-        # Really ISnap, patched in lp.snappy.interfaces.webservice.
-        Interface,
-        title=_("Snap package"), required=True, readonly=True))
+    snap = exported(
+        Reference(
+            # Really ISnap, patched in lp.snappy.interfaces.webservice.
+            Interface,
+            title=_("Snap package"),
+            required=True,
+            readonly=True,
+        )
+    )
 
-    status = exported(Choice(
-        title=_("Status"), vocabulary=SnapBuildRequestStatus,
-        required=True, readonly=True))
+    status = exported(
+        Choice(
+            title=_("Status"),
+            vocabulary=SnapBuildRequestStatus,
+            required=True,
+            readonly=True,
+        )
+    )
 
-    error_message = exported(TextLine(
-        title=_("Error message"), required=True, readonly=True))
+    error_message = exported(
+        TextLine(title=_("Error message"), required=True, readonly=True)
+    )
 
-    builds = exported(CollectionField(
-        title=_("Builds produced by this request"),
-        # Really ISnapBuild, patched in lp.snappy.interfaces.webservice.
-        value_type=Reference(schema=Interface),
-        required=True, readonly=True))
+    builds = exported(
+        CollectionField(
+            title=_("Builds produced by this request"),
+            # Really ISnapBuild, patched in lp.snappy.interfaces.webservice.
+            value_type=Reference(schema=Interface),
+            required=True,
+            readonly=True,
+        )
+    )
 
     requester = Reference(
-        title=_("The person requesting the builds."), schema=IPerson,
-        required=True, readonly=True)
+        title=_("The person requesting the builds."),
+        schema=IPerson,
+        required=True,
+        readonly=True,
+    )
 
     archive = Reference(
         IArchive,
         title="The source archive for builds produced by this request",
-        required=True, readonly=True)
+        required=True,
+        readonly=True,
+    )
 
     pocket = Choice(
         title=_("The source pocket for builds produced by this request."),
-        vocabulary=PackagePublishingPocket, required=True, readonly=True)
+        vocabulary=PackagePublishingPocket,
+        required=True,
+        readonly=True,
+    )
 
     channels = Dict(
         title=_("Source snap channels for builds produced by this request"),
-        key_type=TextLine(), required=False, readonly=True)
+        key_type=TextLine(),
+        required=False,
+        readonly=True,
+    )
 
     architectures = Set(
         title=_("If set, this request is limited to these architecture tags"),
-        value_type=TextLine(), required=False, readonly=True)
+        value_type=TextLine(),
+        required=False,
+        readonly=True,
+    )
 
 
 class ISnapView(Interface):
@@ -370,25 +405,33 @@ class ISnapView(Interface):
 
     id = Int(title=_("ID"), required=True, readonly=True)
 
-    date_created = exported(Datetime(
-        title=_("Date created"), required=True, readonly=True))
+    date_created = exported(
+        Datetime(title=_("Date created"), required=True, readonly=True)
+    )
 
-    registrant = exported(PublicPersonChoice(
-        title=_("Registrant"), required=True, readonly=True,
-        vocabulary="ValidPersonOrTeam",
-        description=_("The person who registered this snap package.")))
+    registrant = exported(
+        PublicPersonChoice(
+            title=_("Registrant"),
+            required=True,
+            readonly=True,
+            vocabulary="ValidPersonOrTeam",
+            description=_("The person who registered this snap package."),
+        )
+    )
 
     source = Attribute(
-        "The source branch for this snap package (VCS-agnostic).")
+        "The source branch for this snap package (VCS-agnostic)."
+    )
 
     available_processors = Attribute(
         "The architectures that are available to be enabled or disabled for "
-        "this snap package.")
+        "this snap package."
+    )
 
     @call_with(check_permissions=True, user=REQUEST_USER)
     @operation_parameters(
-        processors=List(
-            value_type=Reference(schema=IProcessor), required=True))
+        processors=List(value_type=Reference(schema=IProcessor), required=True)
+    )
     @export_write_operation()
     @operation_for_version("devel")
     def setProcessors(processors, check_permissions=False, user=None):
@@ -400,11 +443,17 @@ class ISnapView(Interface):
         :return: Sequence of `IDistroArchSeries` instances.
         """
 
-    can_upload_to_store = exported(Bool(
-        title=_("Can upload to store"), required=True, readonly=True,
-        description=_(
-            "Whether everything is set up to allow uploading builds of this "
-            "snap package to the store.")))
+    can_upload_to_store = exported(
+        Bool(
+            title=_("Can upload to store"),
+            required=True,
+            readonly=True,
+            description=_(
+                "Whether everything is set up to allow uploading builds of "
+                "this snap package to the store."
+            ),
+        )
+    )
 
     @call_with(requester=REQUEST_USER)
     @operation_parameters(
@@ -417,14 +466,25 @@ class ISnapView(Interface):
             description=_(
                 "A dictionary mapping snap names to channels to use for this "
                 "build.  Currently only 'core', 'core18', 'core20', 'core22', "
-                "and 'snapcraft' keys are supported."),
-            key_type=TextLine(), required=False))
+                "and 'snapcraft' keys are supported."
+            ),
+            key_type=TextLine(),
+            required=False,
+        ),
+    )
     # Really ISnapBuild, patched in lp.snappy.interfaces.webservice.
     @export_factory_operation(Interface, [])
     @operation_for_version("devel")
-    def requestBuild(requester, archive, distro_arch_series, pocket,
-                     snap_base=None, channels=None, build_request=None,
-                     target_architectures=None):
+    def requestBuild(
+        requester,
+        archive,
+        distro_arch_series,
+        pocket,
+        snap_base=None,
+        channels=None,
+        build_request=None,
+        target_architectures=None,
+    ):
         """Request that the snap package be built.
 
         :param requester: The person requesting the build.
@@ -450,8 +510,12 @@ class ISnapView(Interface):
             description=_(
                 "A dictionary mapping snap names to channels to use for this "
                 "build.  Currently only 'core', 'core18', 'core20', 'core22', "
-                "and 'snapcraft' keys are supported."),
-            key_type=TextLine(), required=False))
+                "and 'snapcraft' keys are supported."
+            ),
+            key_type=TextLine(),
+            required=False,
+        ),
+    )
     @export_factory_operation(ISnapBuildRequest, [])
     @operation_for_version("devel")
     def requestBuilds(requester, archive, pocket, channels=None):
@@ -469,10 +533,17 @@ class ISnapView(Interface):
         :return: An `ISnapBuildRequest`.
         """
 
-    def requestBuildsFromJob(requester, archive, pocket, channels=None,
-                             architectures=None, allow_failures=False,
-                             fetch_snapcraft_yaml=True, build_request=None,
-                             logger=None):
+    def requestBuildsFromJob(
+        requester,
+        archive,
+        pocket,
+        channels=None,
+        architectures=None,
+        allow_failures=False,
+        fetch_snapcraft_yaml=True,
+        build_request=None,
+        logger=None,
+    ):
         """Synchronous part of `Snap.requestBuilds`.
 
         Request that the snap package be built for relevant architectures.
@@ -507,22 +578,36 @@ class ISnapView(Interface):
         :return: `ISnapBuildRequest`.
         """
 
-    pending_build_requests = exported(doNotSnapshot(CollectionField(
-        title=_("Pending build requests for this snap package."),
-        value_type=Reference(ISnapBuildRequest),
-        required=True, readonly=True)))
+    pending_build_requests = exported(
+        doNotSnapshot(
+            CollectionField(
+                title=_("Pending build requests for this snap package."),
+                value_type=Reference(ISnapBuildRequest),
+                required=True,
+                readonly=True,
+            )
+        )
+    )
 
-    failed_build_requests = exported(doNotSnapshot(CollectionField(
-        title=_("Failed build requests for this snap package."),
-        value_type=Reference(ISnapBuildRequest),
-        required=True, readonly=True)))
+    failed_build_requests = exported(
+        doNotSnapshot(
+            CollectionField(
+                title=_("Failed build requests for this snap package."),
+                value_type=Reference(ISnapBuildRequest),
+                required=True,
+                readonly=True,
+            )
+        )
+    )
 
     # XXX cjwatson 2018-06-20: Deprecated as an exported method; can become
     # an internal helper method once production JavaScript no longer uses
     # it.
     @operation_parameters(
         snap_build_ids=List(
-            title=_("A list of snap build IDs."), value_type=Int()))
+            title=_("A list of snap build IDs."), value_type=Int()
+        )
+    )
     @export_read_operation()
     @operation_for_version("devel")
     def getBuildSummariesForSnapBuildIds(snap_build_ids):
@@ -536,8 +621,8 @@ class ISnapView(Interface):
 
     @call_with(user=REQUEST_USER)
     @operation_parameters(
-        store_upload_revision=Int(title="Store revision",
-                                  required=True))
+        store_upload_revision=Int(title="Store revision", required=True)
+    )
     # Really ISnapBuild, patched in lp.snappy.interfaces.webservice.
     @operation_returns_entry(Interface)
     @export_read_operation()
@@ -554,11 +639,16 @@ class ISnapView(Interface):
     @call_with(user=REQUEST_USER)
     @operation_parameters(
         request_ids=List(
-            title=_("A list of snap build request IDs."), value_type=Int(),
-            required=False),
+            title=_("A list of snap build request IDs."),
+            value_type=Int(),
+            required=False,
+        ),
         build_ids=List(
-            title=_("A list of snap build IDs."), value_type=Int(),
-            required=False))
+            title=_("A list of snap build IDs."),
+            value_type=Int(),
+            required=False,
+        ),
+    )
     @export_read_operation()
     @operation_for_version("devel")
     def getBuildSummaries(request_ids=None, build_ids=None, user=None):
@@ -572,38 +662,66 @@ class ISnapView(Interface):
             builds respectively.
         """
 
-    builds = exported(doNotSnapshot(CollectionField(
-        title=_("All builds of this snap package."),
-        description=_(
-            "All builds of this snap package, sorted in descending order "
-            "of finishing (or starting if not completed successfully)."),
-        # Really ISnapBuild, patched in lp.snappy.interfaces.webservice.
-        value_type=Reference(schema=Interface), readonly=True)))
+    builds = exported(
+        doNotSnapshot(
+            CollectionField(
+                title=_("All builds of this snap package."),
+                description=_(
+                    "All builds of this snap package, sorted in descending "
+                    "order of finishing (or starting if not completed "
+                    "successfully)."
+                ),
+                # Really ISnapBuild, patched in
+                # lp.snappy.interfaces.webservice.
+                value_type=Reference(schema=Interface),
+                readonly=True,
+            )
+        )
+    )
 
-    completed_builds = exported(doNotSnapshot(CollectionField(
-        title=_("Completed builds of this snap package."),
-        description=_(
-            "Completed builds of this snap package, sorted in descending "
-            "order of finishing."),
-        # Really ISnapBuild, patched in lp.snappy.interfaces.webservice.
-        value_type=Reference(schema=Interface), readonly=True)))
+    completed_builds = exported(
+        doNotSnapshot(
+            CollectionField(
+                title=_("Completed builds of this snap package."),
+                description=_(
+                    "Completed builds of this snap package, sorted in "
+                    "descending order of finishing."
+                ),
+                # Really ISnapBuild, patched in
+                # lp.snappy.interfaces.webservice.
+                value_type=Reference(schema=Interface),
+                readonly=True,
+            )
+        )
+    )
 
-    pending_builds = exported(doNotSnapshot(CollectionField(
-        title=_("Pending builds of this snap package."),
-        description=_(
-            "Pending builds of this snap package, sorted in descending "
-            "order of creation."),
-        # Really ISnapBuild, patched in lp.snappy.interfaces.webservice.
-        value_type=Reference(schema=Interface), readonly=True)))
+    pending_builds = exported(
+        doNotSnapshot(
+            CollectionField(
+                title=_("Pending builds of this snap package."),
+                description=_(
+                    "Pending builds of this snap package, sorted in "
+                    "descending order of creation."
+                ),
+                # Really ISnapBuild, patched in
+                # lp.snappy.interfaces.webservice.
+                value_type=Reference(schema=Interface),
+                readonly=True,
+            )
+        )
+    )
 
     subscriptions = CollectionField(
         title=_("SnapSubscriptions associated with this snap recipe."),
         readonly=True,
-        value_type=Reference(Interface))
+        value_type=Reference(Interface),
+    )
 
     subscribers = CollectionField(
         title=_("Persons subscribed to this snap recipe."),
-        readonly=True, value_type=Reference(IPerson))
+        readonly=True,
+        value_type=Reference(IPerson),
+    )
 
     def getSubscription(person):
         """Returns the person's snap subscription for this snap recipe."""
@@ -634,8 +752,9 @@ class ISnapEdit(IWebhookTarget):
     @operation_returns_collection_of(Interface)
     @export_write_operation()
     @operation_for_version("devel")
-    def requestAutoBuilds(allow_failures=False, fetch_snapcraft_yaml=False,
-                          logger=None):
+    def requestAutoBuilds(
+        allow_failures=False, fetch_snapcraft_yaml=False, logger=None
+    ):
         """Create and return automatic builds for this snap package.
 
         This webservice API method is deprecated.  It is normally better to
@@ -684,13 +803,18 @@ class ISnapEdit(IWebhookTarget):
         root_macaroon=TextLine(
             title=_("Serialized root macaroon"),
             description=_(
-                "Only required if not already set by beginAuthorization."),
-            required=False),
+                "Only required if not already set by beginAuthorization."
+            ),
+            required=False,
+        ),
         discharge_macaroon=TextLine(
             title=_("Serialized discharge macaroon"),
             description=_(
-                "Only required if root macaroon has SSO third-party caveat."),
-            required=False))
+                "Only required if root macaroon has SSO third-party caveat."
+            ),
+            required=False,
+        ),
+    )
     @export_write_operation()
     @operation_for_version("devel")
     def completeAuthorization(root_macaroon=None, discharge_macaroon=None):
@@ -719,187 +843,322 @@ class ISnapEditableAttributes(IHasOwner):
 
     These attributes need launchpad.View to see, and launchpad.Edit to change.
     """
-    date_last_modified = exported(Datetime(
-        title=_("Date last modified"), required=True, readonly=True))
 
-    owner = exported(PersonChoice(
-        title=_("Owner"), required=True, readonly=False,
-        vocabulary="AllUserTeamsParticipationPlusSelf",
-        description=_("The owner of this snap package.")))
+    date_last_modified = exported(
+        Datetime(title=_("Date last modified"), required=True, readonly=True)
+    )
+
+    owner = exported(
+        PersonChoice(
+            title=_("Owner"),
+            required=True,
+            readonly=False,
+            vocabulary="AllUserTeamsParticipationPlusSelf",
+            description=_("The owner of this snap package."),
+        )
+    )
 
     project = ReferenceChoice(
-        title=_('The project that this Snap is associated with'),
-        schema=IProduct, vocabulary='Product',
-        required=False, readonly=False)
+        title=_("The project that this Snap is associated with"),
+        schema=IProduct,
+        vocabulary="Product",
+        required=False,
+        readonly=False,
+    )
 
-    private = exported(Bool(
-        title=_("Private"), required=False, readonly=False,
-        description=_("Whether or not this snap is private.")))
+    private = exported(
+        Bool(
+            title=_("Private"),
+            required=False,
+            readonly=False,
+            description=_("Whether or not this snap is private."),
+        )
+    )
 
-    information_type = exported(Choice(
-        title=_("Information type"), vocabulary=InformationType,
-        required=True, readonly=False, default=InformationType.PUBLIC,
-        description=_(
-            "The type of information contained in this Snap recipe.")))
+    information_type = exported(
+        Choice(
+            title=_("Information type"),
+            vocabulary=InformationType,
+            required=True,
+            readonly=False,
+            default=InformationType.PUBLIC,
+            description=_(
+                "The type of information contained in this Snap recipe."
+            ),
+        )
+    )
 
-    distro_series = exported(Reference(
-        IDistroSeries, title=_("Distro Series"),
-        required=False, readonly=False,
-        description=_(
-            "The series for which the snap package should be built.  If not "
-            "set, Launchpad will infer an appropriate series from "
-            "snapcraft.yaml.")))
-
-    name = exported(TextLine(
-        title=_("Snap recipe name"), required=True, readonly=False,
-        constraint=name_validator,
-        description=_("The name of the snap build recipe.")))
-
-    description = exported(Text(
-        title=_("Description"), required=False, readonly=False,
-        description=_("A description of the snap package.")))
-
-    branch = exported(ReferenceChoice(
-        title=_("Bazaar branch"), schema=IBranch, vocabulary="Branch",
-        required=False, readonly=False,
-        description=_(
-            "A Bazaar branch containing a snap/snapcraft.yaml, "
-            "build-aux/snap/snapcraft.yaml, snapcraft.yaml, or "
-            ".snapcraft.yaml recipe at the top level.")))
+    distro_series = exported(
+        Reference(
+            IDistroSeries,
+            title=_("Distro Series"),
+            required=False,
+            readonly=False,
+            description=_(
+                "The series for which the snap package should be built.  If "
+                "not set, Launchpad will infer an appropriate series from "
+                "snapcraft.yaml."
+            ),
+        )
+    )
 
-    git_repository = exported(ReferenceChoice(
-        title=_("Git repository"),
-        schema=IGitRepository, vocabulary="GitRepository",
-        required=False, readonly=True,
-        description=_(
-            "A Git repository with a branch containing a snap/snapcraft.yaml, "
-            "build-aux/snap/snapcraft.yaml, snapcraft.yaml, or "
-            ".snapcraft.yaml recipe at the top level.")))
+    name = exported(
+        TextLine(
+            title=_("Snap recipe name"),
+            required=True,
+            readonly=False,
+            constraint=name_validator,
+            description=_("The name of the snap build recipe."),
+        )
+    )
 
-    git_repository_url = exported(URIField(
-        title=_("Git repository URL"), required=False, readonly=True,
-        description=_(
-            "The URL of a Git repository with a branch containing a "
-            "snap/snapcraft.yaml, build-aux/snap/snapcraft.yaml, "
-            "snapcraft.yaml, or .snapcraft.yaml recipe at the top level."),
-        allowed_schemes=["git", "http", "https"],
-        allow_userinfo=True,
-        allow_port=True,
-        allow_query=False,
-        allow_fragment=False,
-        trailing_slash=False))
+    description = exported(
+        Text(
+            title=_("Description"),
+            required=False,
+            readonly=False,
+            description=_("A description of the snap package."),
+        )
+    )
+
+    branch = exported(
+        ReferenceChoice(
+            title=_("Bazaar branch"),
+            schema=IBranch,
+            vocabulary="Branch",
+            required=False,
+            readonly=False,
+            description=_(
+                "A Bazaar branch containing a snap/snapcraft.yaml, "
+                "build-aux/snap/snapcraft.yaml, snapcraft.yaml, or "
+                ".snapcraft.yaml recipe at the top level."
+            ),
+        )
+    )
+
+    git_repository = exported(
+        ReferenceChoice(
+            title=_("Git repository"),
+            schema=IGitRepository,
+            vocabulary="GitRepository",
+            required=False,
+            readonly=True,
+            description=_(
+                "A Git repository with a branch containing a "
+                "snap/snapcraft.yaml, build-aux/snap/snapcraft.yaml, "
+                "snapcraft.yaml, or .snapcraft.yaml recipe at the top level."
+            ),
+        )
+    )
+
+    git_repository_url = exported(
+        URIField(
+            title=_("Git repository URL"),
+            required=False,
+            readonly=True,
+            description=_(
+                "The URL of a Git repository with a branch containing a "
+                "snap/snapcraft.yaml, build-aux/snap/snapcraft.yaml, "
+                "snapcraft.yaml, or .snapcraft.yaml recipe at the top level."
+            ),
+            allowed_schemes=["git", "http", "https"],
+            allow_userinfo=True,
+            allow_port=True,
+            allow_query=False,
+            allow_fragment=False,
+            trailing_slash=False,
+        )
+    )
 
     git_path = TextLine(
-        title=_("Git branch path"), required=False, readonly=False,
+        title=_("Git branch path"),
+        required=False,
+        readonly=False,
         description=_(
             "The path of the Git branch containing a snap/snapcraft.yaml, "
             "build-aux/snap/snapcraft.yaml, snapcraft.yaml, or "
-            ".snapcraft.yaml recipe at the top level."))
+            ".snapcraft.yaml recipe at the top level."
+        ),
+    )
     _api_git_path = exported(
         TextLine(
-            title=git_path.title, required=False, readonly=False,
-            description=git_path.description),
-        exported_as="git_path")
+            title=git_path.title,
+            required=False,
+            readonly=False,
+            description=git_path.description,
+        ),
+        exported_as="git_path",
+    )
 
-    git_ref = exported(Reference(
-        IGitRef, title=_("Git branch"), required=False, readonly=False,
-        description=_(
-            "The Git branch containing a snap/snapcraft.yaml, "
-            "build-aux/snap/snapcraft.yaml, snapcraft.yaml, or "
-            ".snapcraft.yaml recipe at the top level.")))
+    git_ref = exported(
+        Reference(
+            IGitRef,
+            title=_("Git branch"),
+            required=False,
+            readonly=False,
+            description=_(
+                "The Git branch containing a snap/snapcraft.yaml, "
+                "build-aux/snap/snapcraft.yaml, snapcraft.yaml, or "
+                ".snapcraft.yaml recipe at the top level."
+            ),
+        )
+    )
 
-    build_source_tarball = exported(Bool(
-        title=_("Build source tarball"),
-        required=True, readonly=False,
-        description=_(
-            "Whether builds of this snap package should also build a tarball "
-            "containing all source code, including external dependencies.")))
+    build_source_tarball = exported(
+        Bool(
+            title=_("Build source tarball"),
+            required=True,
+            readonly=False,
+            description=_(
+                "Whether builds of this snap package should also build a "
+                "tarball containing all source code, including external "
+                "dependencies."
+            ),
+        )
+    )
 
-    auto_build = exported(Bool(
-        title=_("Automatically build when branch changes"),
-        required=True, readonly=False,
-        description=_(
-            "Whether this snap package is built automatically when the branch "
-            "containing its snap/snapcraft.yaml, "
-            "build-aux/snap/snapcraft.yaml, snapcraft.yaml, or "
-            ".snapcraft.yaml recipe changes.")))
+    auto_build = exported(
+        Bool(
+            title=_("Automatically build when branch changes"),
+            required=True,
+            readonly=False,
+            description=_(
+                "Whether this snap package is built automatically when the "
+                "branch containing its snap/snapcraft.yaml, "
+                "build-aux/snap/snapcraft.yaml, snapcraft.yaml, or "
+                ".snapcraft.yaml recipe changes."
+            ),
+        )
+    )
 
-    auto_build_archive = exported(Reference(
-        IArchive, title=_("Source archive for automatic builds"),
-        required=False, readonly=False,
-        description=_(
-            "The archive from which automatic builds of this snap package "
-            "should be built.")))
+    auto_build_archive = exported(
+        Reference(
+            IArchive,
+            title=_("Source archive for automatic builds"),
+            required=False,
+            readonly=False,
+            description=_(
+                "The archive from which automatic builds of this snap package "
+                "should be built."
+            ),
+        )
+    )
 
-    auto_build_pocket = exported(Choice(
-        title=_("Pocket for automatic builds"),
-        vocabulary=PackagePublishingPocket, required=False, readonly=False,
-        description=_(
-            "The package stream within the source archive and distribution "
-            "series to use when building the snap package.  If the source "
-            "archive is a PPA, then the PPA's archive dependencies will be "
-            "used to select the pocket in the distribution's primary "
-            "archive.")))
-
-    auto_build_channels = exported(Dict(
-        title=_("Source snap channels for automatic builds"),
-        key_type=TextLine(), required=False, readonly=False,
-        description=_(
-            "A dictionary mapping snap names to channels to use when building "
-            "this snap package.  Currently only 'core', 'core18', "
-            "'core20', 'core22', and 'snapcraft' keys are supported.")))
+    auto_build_pocket = exported(
+        Choice(
+            title=_("Pocket for automatic builds"),
+            vocabulary=PackagePublishingPocket,
+            required=False,
+            readonly=False,
+            description=_(
+                "The package stream within the source archive and "
+                "distribution series to use when building the snap package.  "
+                "If the source archive is a PPA, then the PPA's archive "
+                "dependencies will be used to select the pocket in the "
+                "distribution's primary archive."
+            ),
+        )
+    )
 
-    is_stale = exported(Bool(
-        title=_("Snap package is stale and is due to be rebuilt."),
-        required=True, readonly=True))
+    auto_build_channels = exported(
+        Dict(
+            title=_("Source snap channels for automatic builds"),
+            key_type=TextLine(),
+            required=False,
+            readonly=False,
+            description=_(
+                "A dictionary mapping snap names to channels to use when "
+                "building this snap package.  Currently only 'core', "
+                "'core18', 'core20', 'core22', and 'snapcraft' keys are "
+                "supported."
+            ),
+        )
+    )
 
-    store_upload = exported(Bool(
-        title=_("Automatically upload to store"),
-        required=True, readonly=False,
-        description=_(
-            "Whether builds of this snap package are automatically uploaded "
-            "to the store.")))
+    is_stale = exported(
+        Bool(
+            title=_("Snap package is stale and is due to be rebuilt."),
+            required=True,
+            readonly=True,
+        )
+    )
+
+    store_upload = exported(
+        Bool(
+            title=_("Automatically upload to store"),
+            required=True,
+            readonly=False,
+            description=_(
+                "Whether builds of this snap package are automatically "
+                "uploaded to the store."
+            ),
+        )
+    )
 
     # XXX cjwatson 2016-12-08: We should limit this to series that are
     # compatible with distro_series, but that entails validating the case
     # where both are changed in a single PATCH request in such a way that
     # neither is compatible with the old value of the other.  As far as I
     # can tell lazr.restful only supports per-field validation.
-    store_series = exported(ReferenceChoice(
-        title=_("Store series"),
-        schema=ISnappySeries, vocabulary="SnappySeries",
-        required=False, readonly=False,
-        description=_(
-            "The series in which this snap package should be published in the "
-            "store.")))
+    store_series = exported(
+        ReferenceChoice(
+            title=_("Store series"),
+            schema=ISnappySeries,
+            vocabulary="SnappySeries",
+            required=False,
+            readonly=False,
+            description=_(
+                "The series in which this snap package should be published in "
+                "the store."
+            ),
+        )
+    )
 
     store_distro_series = ReferenceChoice(
         title=_("Store and distro series"),
-        schema=ISnappyDistroSeries, vocabulary="SnappyDistroSeries",
-        required=False, readonly=False)
+        schema=ISnappyDistroSeries,
+        vocabulary="SnappyDistroSeries",
+        required=False,
+        readonly=False,
+    )
 
-    store_name = exported(TextLine(
-        title=_("Registered store package name"),
-        required=False, readonly=False,
-        description=_(
-            "The registered name of this snap package in the store.")))
+    store_name = exported(
+        TextLine(
+            title=_("Registered store package name"),
+            required=False,
+            readonly=False,
+            description=_(
+                "The registered name of this snap package in the store."
+            ),
+        )
+    )
 
     store_secrets = List(
-        value_type=TextLine(), title=_("Store upload tokens"),
-        required=False, readonly=False,
+        value_type=TextLine(),
+        title=_("Store upload tokens"),
+        required=False,
+        readonly=False,
         description=_(
             "Serialized secrets issued by the store and the login service to "
-            "authorize uploads of this snap package."))
+            "authorize uploads of this snap package."
+        ),
+    )
 
-    store_channels = exported(List(
-        title=_("Store channels"),
-        required=False, readonly=False, constraint=channels_validator,
-        description=_(
-            "Channels to release this snap package to after uploading it to "
-            "the store. A channel is defined by a combination of an optional "
-            " track, a risk, and an optional branch, e.g. "
-            "'2.1/stable/fix-123', '2.1/stable', 'stable/fix-123', or "
-            "'stable'.")))
+    store_channels = exported(
+        List(
+            title=_("Store channels"),
+            required=False,
+            readonly=False,
+            constraint=channels_validator,
+            description=_(
+                "Channels to release this snap package to after uploading it "
+                "to the store. A channel is defined by a combination of an "
+                "optional track, a risk, and an optional branch, e.g. "
+                "'2.1/stable/fix-123', '2.1/stable', 'stable/fix-123', or "
+                "'stable'."
+            ),
+        )
+    )
 
     def setProject(project):
         """Set the pillar project of this snap recipe."""
@@ -911,23 +1170,37 @@ class ISnapAdminAttributes(Interface):
     These attributes need launchpad.View to see, and launchpad.Admin to change.
     """
 
-    require_virtualized = exported(Bool(
-        title=_("Require virtualized builders"), required=True, readonly=False,
-        description=_("Only build this snap package on virtual builders.")))
+    require_virtualized = exported(
+        Bool(
+            title=_("Require virtualized builders"),
+            required=True,
+            readonly=False,
+            description=_("Only build this snap package on virtual builders."),
+        )
+    )
 
-    processors = exported(CollectionField(
-        title=_("Processors"),
-        description=_(
-            "The architectures for which the snap package should be built."),
-        value_type=Reference(schema=IProcessor),
-        readonly=False))
+    processors = exported(
+        CollectionField(
+            title=_("Processors"),
+            description=_(
+                "The architectures for which the snap package should be built."
+            ),
+            value_type=Reference(schema=IProcessor),
+            readonly=False,
+        )
+    )
 
-    allow_internet = exported(Bool(
-        title=_("Allow external network access"),
-        required=True, readonly=False,
-        description=_(
-            "Allow access to external network resources via a proxy.  "
-            "Resources hosted on Launchpad itself are always allowed.")))
+    allow_internet = exported(
+        Bool(
+            title=_("Allow external network access"),
+            required=True,
+            readonly=False,
+            description=_(
+                "Allow access to external network resources via a proxy.  "
+                "Resources hosted on Launchpad itself are always allowed."
+            ),
+        )
+    )
 
     def subscribe(person, subscribed_by):
         """Subscribe a person to this snap recipe."""
@@ -938,8 +1211,13 @@ class ISnapAdminAttributes(Interface):
 # "devel".
 @exported_as_webservice_entry(as_of="beta")
 class ISnap(
-    ISnapView, ISnapEdit, ISnapEditableAttributes, ISnapAdminAttributes,
-    IPrivacy, IInformationType):
+    ISnapView,
+    ISnapEdit,
+    ISnapEditableAttributes,
+    ISnapAdminAttributes,
+    IPrivacy,
+    IInformationType,
+):
     """A buildable snap package."""
 
 
@@ -951,24 +1229,57 @@ class ISnapSet(Interface):
     @operation_parameters(
         information_type=copy_field(ISnap["information_type"], required=False),
         processors=List(
-            value_type=Reference(schema=IProcessor), required=False))
+            value_type=Reference(schema=IProcessor), required=False
+        ),
+    )
     @export_factory_operation(
-        ISnap, [
-            "owner", "distro_series", "name", "description", "branch",
-            "git_repository", "git_repository_url", "git_path", "git_ref",
-            "auto_build", "auto_build_archive", "auto_build_pocket",
-            "store_upload", "store_series", "store_name", "store_channels",
-            "project"])
+        ISnap,
+        [
+            "owner",
+            "distro_series",
+            "name",
+            "description",
+            "branch",
+            "git_repository",
+            "git_repository_url",
+            "git_path",
+            "git_ref",
+            "auto_build",
+            "auto_build_archive",
+            "auto_build_pocket",
+            "store_upload",
+            "store_series",
+            "store_name",
+            "store_channels",
+            "project",
+        ],
+    )
     @operation_for_version("devel")
-    def new(registrant, owner, distro_series, name, description=None,
-            branch=None, git_repository=None, git_repository_url=None,
-            git_path=None, git_ref=None, auto_build=False,
-            auto_build_archive=None, auto_build_pocket=None,
-            require_virtualized=True, processors=None, date_created=None,
-            information_type=InformationType.PUBLIC, store_upload=False,
-            store_series=None,
-            store_name=None, store_secrets=None, store_channels=None,
-            project=None):
+    def new(
+        registrant,
+        owner,
+        distro_series,
+        name,
+        description=None,
+        branch=None,
+        git_repository=None,
+        git_repository_url=None,
+        git_path=None,
+        git_ref=None,
+        auto_build=False,
+        auto_build_archive=None,
+        auto_build_pocket=None,
+        require_virtualized=True,
+        processors=None,
+        date_created=None,
+        information_type=InformationType.PUBLIC,
+        store_upload=False,
+        store_series=None,
+        store_name=None,
+        store_secrets=None,
+        store_channels=None,
+        project=None,
+    ):
         """Create an `ISnap`."""
 
     def exists(owner, name):
@@ -983,12 +1294,14 @@ class ISnapSet(Interface):
         """Return all snap packages with the given ids."""
 
     def isValidInformationType(
-            information_type, owner, branch=None, git_ref=None):
+        information_type, owner, branch=None, git_ref=None
+    ):
         """Whether or not the information type context is valid."""
 
     @operation_parameters(
         owner=Reference(IPerson, title=_("Owner"), required=True),
-        name=TextLine(title=_("Snap name"), required=True))
+        name=TextLine(title=_("Snap name"), required=True),
+    )
     @operation_returns_entry(ISnap)
     @export_read_operation()
     @operation_for_version("devel")
@@ -999,7 +1312,8 @@ class ISnapSet(Interface):
         """Returns the appropriate `ISnap` for the given pillar and name."""
 
     @operation_parameters(
-        owner=Reference(IPerson, title=_("Owner"), required=True))
+        owner=Reference(IPerson, title=_("Owner"), required=True)
+    )
     @operation_returns_collection_of(ISnap)
     @export_read_operation()
     @operation_for_version("devel")
@@ -1053,7 +1367,8 @@ class ISnapSet(Interface):
 
     @operation_parameters(
         url=TextLine(title=_("The URL to search for.")),
-        owner=Reference(IPerson, title=_("Owner"), required=False))
+        owner=Reference(IPerson, title=_("Owner"), required=False),
+    )
     @call_with(visible_by_user=REQUEST_USER)
     @operation_returns_collection_of(ISnap)
     @export_read_operation()
@@ -1073,7 +1388,8 @@ class ISnapSet(Interface):
 
     @operation_parameters(
         url_prefix=TextLine(title=_("The URL prefix to search for.")),
-        owner=Reference(IPerson, title=_("Owner"), required=False))
+        owner=Reference(IPerson, title=_("Owner"), required=False),
+    )
     @call_with(visible_by_user=REQUEST_USER)
     @operation_returns_collection_of(ISnap)
     @export_read_operation()
@@ -1093,8 +1409,10 @@ class ISnapSet(Interface):
 
     @operation_parameters(
         url_prefixes=List(
-            title=_("The URL prefixes to search for."), value_type=TextLine()),
-        owner=Reference(IPerson, title=_("Owner"), required=False))
+            title=_("The URL prefixes to search for."), value_type=TextLine()
+        ),
+        owner=Reference(IPerson, title=_("Owner"), required=False),
+    )
     @call_with(visible_by_user=REQUEST_USER)
     @operation_returns_collection_of(ISnap)
     @export_read_operation()
@@ -1115,8 +1433,10 @@ class ISnapSet(Interface):
 
     @operation_parameters(
         store_name=TextLine(
-            title=_("The registered store package name to search for.")),
-        owner=Reference(IPerson, title=_("Owner"), required=False))
+            title=_("The registered store package name to search for.")
+        ),
+        owner=Reference(IPerson, title=_("Owner"), required=False),
+    )
     @call_with(visible_by_user=REQUEST_USER)
     @operation_returns_collection_of(ISnap)
     @export_read_operation()
diff --git a/lib/lp/snappy/interfaces/snapbase.py b/lib/lp/snappy/interfaces/snapbase.py
index d635bfb..08cfd79 100644
--- a/lib/lp/snappy/interfaces/snapbase.py
+++ b/lib/lp/snappy/interfaces/snapbase.py
@@ -8,11 +8,12 @@ __all__ = [
     "ISnapBase",
     "ISnapBaseSet",
     "NoSuchSnapBase",
-    ]
+]
 
 import http.client
 
 from lazr.restful.declarations import (
+    REQUEST_USER,
     call_with,
     collection_default_content,
     error_status,
@@ -27,33 +28,19 @@ from lazr.restful.declarations import (
     operation_for_version,
     operation_parameters,
     operation_returns_entry,
-    REQUEST_USER,
-    )
-from lazr.restful.fields import (
-    CollectionField,
-    Reference,
-    )
+)
+from lazr.restful.fields import CollectionField, Reference
 from lazr.restful.interface import copy_field
 from zope.component import getUtility
 from zope.interface import Interface
-from zope.schema import (
-    Bool,
-    Datetime,
-    Dict,
-    Int,
-    List,
-    TextLine,
-    )
+from zope.schema import Bool, Datetime, Dict, Int, List, TextLine
 
 from lp import _
 from lp.app.errors import NameLookupFailed
 from lp.app.validators.name import name_validator
 from lp.buildmaster.interfaces.processor import IProcessor
 from lp.registry.interfaces.distroseries import IDistroSeries
-from lp.services.fields import (
-    ContentNameField,
-    PublicPersonChoice,
-    )
+from lp.services.fields import ContentNameField, PublicPersonChoice
 from lp.soyuz.interfaces.archive import IArchive
 from lp.soyuz.interfaces.archivedependency import IArchiveDependency
 
@@ -92,24 +79,39 @@ class ISnapBaseView(Interface):
 
     id = Int(title=_("ID"), required=True, readonly=True)
 
-    date_created = exported(Datetime(
-        title=_("Date created"), required=True, readonly=True))
+    date_created = exported(
+        Datetime(title=_("Date created"), required=True, readonly=True)
+    )
 
-    registrant = exported(PublicPersonChoice(
-        title=_("Registrant"), required=True, readonly=True,
-        vocabulary="ValidPersonOrTeam",
-        description=_("The person who registered this base.")))
+    registrant = exported(
+        PublicPersonChoice(
+            title=_("Registrant"),
+            required=True,
+            readonly=True,
+            vocabulary="ValidPersonOrTeam",
+            description=_("The person who registered this base."),
+        )
+    )
 
-    is_default = exported(Bool(
-        title=_("Is default?"), required=True, readonly=True,
-        description=_(
-            "Whether this base is the default for snaps that do not specify a "
-            "base.")))
+    is_default = exported(
+        Bool(
+            title=_("Is default?"),
+            required=True,
+            readonly=True,
+            description=_(
+                "Whether this base is the default for snaps that do not "
+                "specify a base."
+            ),
+        )
+    )
 
-    dependencies = exported(CollectionField(
-        title=_("Archive dependencies for this snap base."),
-        value_type=Reference(schema=IArchiveDependency),
-        readonly=True))
+    dependencies = exported(
+        CollectionField(
+            title=_("Archive dependencies for this snap base."),
+            value_type=Reference(schema=IArchiveDependency),
+            readonly=True,
+        )
+    )
 
     @operation_parameters(dependency=Reference(schema=IArchive))
     @operation_returns_entry(schema=IArchiveDependency)
@@ -124,11 +126,14 @@ class ISnapBaseView(Interface):
             could not be found.
         """
 
-    processors = exported(CollectionField(
-        title=_("Processors"),
-        description=_("The architectures that the snap base supports."),
-        value_type=Reference(schema=IProcessor),
-        readonly=True))
+    processors = exported(
+        CollectionField(
+            title=_("Processors"),
+            description=_("The architectures that the snap base supports."),
+            value_type=Reference(schema=IProcessor),
+            readonly=True,
+        )
+    )
 
 
 class ISnapBaseEditableAttributes(Interface):
@@ -137,26 +142,44 @@ class ISnapBaseEditableAttributes(Interface):
     Anyone can view these attributes, but they need launchpad.Edit to change.
     """
 
-    name = exported(SnapBaseNameField(
-        title=_("Name"), required=True, readonly=False,
-        constraint=name_validator))
+    name = exported(
+        SnapBaseNameField(
+            title=_("Name"),
+            required=True,
+            readonly=False,
+            constraint=name_validator,
+        )
+    )
 
-    display_name = exported(TextLine(
-        title=_("Display name"), required=True, readonly=False))
+    display_name = exported(
+        TextLine(title=_("Display name"), required=True, readonly=False)
+    )
 
-    distro_series = exported(Reference(
-        IDistroSeries, title=_("Distro series"),
-        required=True, readonly=False))
+    distro_series = exported(
+        Reference(
+            IDistroSeries,
+            title=_("Distro series"),
+            required=True,
+            readonly=False,
+        )
+    )
 
-    build_channels = exported(Dict(
-        title=_("Source snap channels for builds"),
-        key_type=TextLine(), required=True, readonly=False,
-        description=_(
-            "A dictionary mapping snap names to channels to use when building "
-            "snaps that specify this base.  The special '_byarch' key may "
-            "have a mapping of architecture names to mappings of snap names "
-            "to channels, which if present override the channels declared at "
-            "the top level when building for those architectures.")))
+    build_channels = exported(
+        Dict(
+            title=_("Source snap channels for builds"),
+            key_type=TextLine(),
+            required=True,
+            readonly=False,
+            description=_(
+                "A dictionary mapping snap names to channels to use when "
+                "building snaps that specify this base.  The special "
+                "'_byarch' key may have a mapping of architecture names to "
+                "mappings of snap names to channels, which if present "
+                "override the channels declared at the top level when "
+                "building for those architectures."
+            ),
+        )
+    )
 
 
 class ISnapBaseEdit(Interface):
@@ -177,7 +200,8 @@ class ISnapBaseEdit(Interface):
         """
 
     @operation_parameters(
-        component=copy_field(IArchiveDependency["component_name"]))
+        component=copy_field(IArchiveDependency["component_name"])
+    )
     @export_operation_as("addArchiveDependency")
     @export_factory_operation(IArchiveDependency, ["dependency", "pocket"])
     @operation_for_version("devel")
@@ -195,8 +219,7 @@ class ISnapBaseEdit(Interface):
         :return: an `IArchiveDependency`.
         """
 
-    @operation_parameters(
-        dependency=Reference(schema=IArchive, required=True))
+    @operation_parameters(dependency=Reference(schema=IArchive, required=True))
     @export_write_operation()
     @operation_for_version("devel")
     def removeArchiveDependency(dependency):
@@ -206,8 +229,8 @@ class ISnapBaseEdit(Interface):
         """
 
     @operation_parameters(
-        processors=List(
-            value_type=Reference(schema=IProcessor), required=True))
+        processors=List(value_type=Reference(schema=IProcessor), required=True)
+    )
     @export_write_operation()
     @operation_for_version("devel")
     def setProcessors(processors):
@@ -236,16 +259,27 @@ class ISnapBaseSetEdit(Interface):
     @call_with(registrant=REQUEST_USER)
     @operation_parameters(
         processors=List(
-            value_type=Reference(schema=IProcessor), required=False))
+            value_type=Reference(schema=IProcessor), required=False
+        )
+    )
     @export_factory_operation(
-        ISnapBase, ["name", "display_name", "distro_series", "build_channels"])
+        ISnapBase, ["name", "display_name", "distro_series", "build_channels"]
+    )
     @operation_for_version("devel")
-    def new(registrant, name, display_name, distro_series, build_channels,
-            processors=None, date_created=None):
+    def new(
+        registrant,
+        name,
+        display_name,
+        distro_series,
+        build_channels,
+        processors=None,
+        date_created=None,
+    ):
         """Create an `ISnapBase`."""
 
     @operation_parameters(
-        snap_base=Reference(title=_("Base"), required=True, schema=ISnapBase))
+        snap_base=Reference(title=_("Base"), required=True, schema=ISnapBase)
+    )
     @export_write_operation()
     @operation_for_version("devel")
     def setDefault(snap_base):
@@ -268,8 +302,7 @@ class ISnapBaseSet(ISnapBaseSetEdit):
     def __getitem__(name):
         """Return the `ISnapBase` with this name."""
 
-    @operation_parameters(
-        name=TextLine(title=_("Base name"), required=True))
+    @operation_parameters(name=TextLine(title=_("Base name"), required=True))
     @operation_returns_entry(ISnapBase)
     @export_read_operation()
     @operation_for_version("devel")
diff --git a/lib/lp/snappy/interfaces/snapbuild.py b/lib/lp/snappy/interfaces/snapbuild.py
index 60dd0f4..fe3fe2a 100644
--- a/lib/lp/snappy/interfaces/snapbuild.py
+++ b/lib/lp/snappy/interfaces/snapbuild.py
@@ -4,20 +4,17 @@
 """Snap package build interfaces."""
 
 __all__ = [
-    'CannotScheduleStoreUpload',
-    'ISnapBuild',
-    'ISnapBuildSet',
-    'ISnapBuildStatusChangedEvent',
-    'ISnapFile',
-    'SnapBuildStoreUploadStatus',
-    ]
+    "CannotScheduleStoreUpload",
+    "ISnapBuild",
+    "ISnapBuildSet",
+    "ISnapBuildStatusChangedEvent",
+    "ISnapFile",
+    "SnapBuildStoreUploadStatus",
+]
 
 import http.client
 
-from lazr.enum import (
-    EnumeratedType,
-    Item,
-    )
+from lazr.enum import EnumeratedType, Item
 from lazr.restful.declarations import (
     error_status,
     export_read_operation,
@@ -25,25 +22,11 @@ from lazr.restful.declarations import (
     exported,
     exported_as_webservice_entry,
     operation_for_version,
-    )
-from lazr.restful.fields import (
-    CollectionField,
-    Reference,
-    )
-from zope.interface import (
-    Attribute,
-    Interface,
-    )
+)
+from lazr.restful.fields import CollectionField, Reference
+from zope.interface import Attribute, Interface
 from zope.interface.interfaces import IObjectEvent
-from zope.schema import (
-    Bool,
-    Choice,
-    Datetime,
-    Dict,
-    Int,
-    List,
-    TextLine,
-    )
+from zope.schema import Bool, Choice, Datetime, Dict, Int, List, TextLine
 
 from lp import _
 from lp.app.interfaces.launchpad import IPrivacy
@@ -51,19 +34,16 @@ from lp.buildmaster.interfaces.buildfarmjob import (
     IBuildFarmJobAdmin,
     IBuildFarmJobEdit,
     ISpecificBuildFarmJobSource,
-    )
+)
 from lp.buildmaster.interfaces.packagebuild import (
     IPackageBuild,
     IPackageBuildView,
-    )
+)
 from lp.registry.interfaces.person import IPerson
 from lp.registry.interfaces.pocket import PackagePublishingPocket
 from lp.services.database.constants import DEFAULT
 from lp.services.librarian.interfaces import ILibraryFileAlias
-from lp.snappy.interfaces.snap import (
-    ISnap,
-    ISnapBuildRequest,
-    )
+from lp.snappy.interfaces.snap import ISnap, ISnapBuildRequest
 from lp.snappy.interfaces.snapbase import ISnapBase
 from lp.soyuz.interfaces.archive import IArchive
 from lp.soyuz.interfaces.distroarchseries import IDistroArchSeries
@@ -85,11 +65,16 @@ class ISnapFile(Interface):
         # Really ISnapBuild, patched in _schema_circular_imports.py.
         Interface,
         title=_("The snap package build producing this file."),
-        required=True, readonly=True)
+        required=True,
+        readonly=True,
+    )
 
     libraryfile = Reference(
-        ILibraryFileAlias, title=_("The library file alias for this file."),
-        required=True, readonly=True)
+        ILibraryFileAlias,
+        title=_("The library file alias for this file."),
+        required=True,
+        readonly=True,
+    )
 
 
 class SnapBuildStoreUploadStatus(EnumeratedType):
@@ -99,39 +84,49 @@ class SnapBuildStoreUploadStatus(EnumeratedType):
     that process.
     """
 
-    UNSCHEDULED = Item("""
+    UNSCHEDULED = Item(
+        """
         Unscheduled
 
         No upload of this snap build to the store is scheduled.
-        """)
+        """
+    )
 
-    PENDING = Item("""
+    PENDING = Item(
+        """
         Pending
 
         This snap build is queued for upload to the store.
-        """)
+        """
+    )
 
-    FAILEDTOUPLOAD = Item("""
+    FAILEDTOUPLOAD = Item(
+        """
         Failed to upload
 
         The last attempt to upload this snap build to the store failed.
-        """)
+        """
+    )
 
     # This is an impossible state for new releases (2019-06-19), due
     # to the store handling releases for us, however historical tasks
     # can have this status, so it is maintained here.
-    FAILEDTORELEASE = Item("""
+    FAILEDTORELEASE = Item(
+        """
         Failed to release to channels
 
         The last attempt to release this snap build to its intended set of
         channels failed.
-        """)
+        """
+    )
 
-    UPLOADED = Item("""
+    UPLOADED = Item(
+        """
         Uploaded
 
         This snap build was successfully uploaded to the store.
-        """)
+        """
+    )
 
 
 class ISnapBuildView(IPackageBuildView, IPrivacy):
@@ -140,131 +135,210 @@ class ISnapBuildView(IPackageBuildView, IPrivacy):
     build_request = Reference(
         ISnapBuildRequest,
         title=_("The build request that caused this build to be created."),
-        required=False, readonly=True)
+        required=False,
+        readonly=True,
+    )
 
-    requester = exported(Reference(
-        IPerson,
-        title=_("The person who requested this build."),
-        required=True, readonly=True))
+    requester = exported(
+        Reference(
+            IPerson,
+            title=_("The person who requested this build."),
+            required=True,
+            readonly=True,
+        )
+    )
 
-    snap = exported(Reference(
-        ISnap,
-        title=_("The snap package to build."),
-        required=True, readonly=True))
+    snap = exported(
+        Reference(
+            ISnap,
+            title=_("The snap package to build."),
+            required=True,
+            readonly=True,
+        )
+    )
 
-    archive = exported(Reference(
-        IArchive,
-        title=_("The archive from which to build the snap package."),
-        required=True, readonly=True))
+    archive = exported(
+        Reference(
+            IArchive,
+            title=_("The archive from which to build the snap package."),
+            required=True,
+            readonly=True,
+        )
+    )
 
-    distro_arch_series = exported(Reference(
-        IDistroArchSeries,
-        title=_("The series and architecture to build on."),
-        required=True, readonly=True))
+    distro_arch_series = exported(
+        Reference(
+            IDistroArchSeries,
+            title=_("The series and architecture to build on."),
+            required=True,
+            readonly=True,
+        )
+    )
 
     arch_tag = exported(
-        TextLine(title=_("Architecture tag"), required=True, readonly=True))
+        TextLine(title=_("Architecture tag"), required=True, readonly=True)
+    )
 
     target_architectures = exported(
         List(
             TextLine(),
             title=_("The target architectures to build for."),
-            required=False, readonly=True,
+            required=False,
+            readonly=True,
         )
     )
 
-    pocket = exported(Choice(
-        title=_("The pocket for which to build."),
-        description=(
-            "The package stream within the source archive and distribution "
-            "series to use when building the snap package.  If the source "
-            "archive is a PPA, then the PPA's archive dependencies will be "
-            "used to select the pocket in the distribution's primary "
-            "archive."),
-        vocabulary=PackagePublishingPocket, required=True, readonly=True))
-
-    snap_base = exported(Reference(
-        ISnapBase,
-        title=_("The snap base to use for this build."),
-        required=False, readonly=True))
-
-    channels = exported(Dict(
-        title=_("Source snap channels to use for this build."),
-        description=_(
-            "A dictionary mapping snap names to channels to use for this "
-            "build.  Currently only 'core', 'core18', 'core20', 'core22', "
-            "and 'snapcraft' keys are supported."),
-        key_type=TextLine()))
+    pocket = exported(
+        Choice(
+            title=_("The pocket for which to build."),
+            description=(
+                "The package stream within the source archive and "
+                "distribution series to use when building the snap package.  "
+                "If the source archive is a PPA, then the PPA's archive "
+                "dependencies will be used to select the pocket in the "
+                "distribution's primary archive."
+            ),
+            vocabulary=PackagePublishingPocket,
+            required=True,
+            readonly=True,
+        )
+    )
+
+    snap_base = exported(
+        Reference(
+            ISnapBase,
+            title=_("The snap base to use for this build."),
+            required=False,
+            readonly=True,
+        )
+    )
+
+    channels = exported(
+        Dict(
+            title=_("Source snap channels to use for this build."),
+            description=_(
+                "A dictionary mapping snap names to channels to use for this "
+                "build.  Currently only 'core', 'core18', 'core20', 'core22', "
+                "and 'snapcraft' keys are supported."
+            ),
+            key_type=TextLine(),
+        )
+    )
 
     virtualized = Bool(
-        title=_("If True, this build is virtualized."), readonly=True)
+        title=_("If True, this build is virtualized."), readonly=True
+    )
 
-    score = exported(Int(
-        title=_("Score of the related build farm job (if any)."),
-        required=False, readonly=True))
+    score = exported(
+        Int(
+            title=_("Score of the related build farm job (if any)."),
+            required=False,
+            readonly=True,
+        )
+    )
 
     eta = Datetime(
         title=_("The datetime when the build job is estimated to complete."),
-        readonly=True)
+        readonly=True,
+    )
 
     estimate = Bool(
-        title=_("If true, the date value is an estimate."), readonly=True)
+        title=_("If true, the date value is an estimate."), readonly=True
+    )
 
     date = Datetime(
-        title=_("The date when the build completed or is estimated to "
-            "complete."), readonly=True)
+        title=_(
+            "The date when the build completed or is estimated to " "complete."
+        ),
+        readonly=True,
+    )
 
-    revision_id = exported(TextLine(
-        title=_("Revision ID"), required=False, readonly=True,
-        description=_(
-            "The revision ID of the branch used for this build, if "
-            "available.")))
+    revision_id = exported(
+        TextLine(
+            title=_("Revision ID"),
+            required=False,
+            readonly=True,
+            description=_(
+                "The revision ID of the branch used for this build, if "
+                "available."
+            ),
+        )
+    )
 
     store_upload_jobs = CollectionField(
         title=_("Store upload jobs for this build."),
         # Really ISnapStoreUploadJob.
         value_type=Reference(schema=Interface),
-        readonly=True)
+        readonly=True,
+    )
 
     # Really ISnapStoreUploadJob.
     last_store_upload_job = Reference(
-        title=_("Last store upload job for this build."), schema=Interface)
-
-    store_upload_status = exported(Choice(
-        title=_("Store upload status"),
-        vocabulary=SnapBuildStoreUploadStatus, required=True, readonly=False))
-
-    store_upload_url = exported(TextLine(
-        title=_("Store URL"),
-        description=_(
-            "The URL to use for managing this package in the store."),
-        required=False, readonly=True))
-
-    store_upload_revision = exported(Int(
-        title=_("Store revision"),
-        description=_("The revision assigned to this package by the store."),
-        required=False, readonly=True))
-
-    store_upload_error_message = exported(TextLine(
-        title=_("Store upload error message"),
-        description=_(
-            "The error message, if any, from the last attempt to upload "
-            "this snap build to the store.  (Deprecated; use "
-            "store_upload_error_messages instead.)"),
-        required=False, readonly=True))
-
-    store_upload_error_messages = exported(List(
-        title=_("Store upload error messages"),
-        description=_(
-            "A list of dict(message, link) where message is an error "
-            "description and link, if any, is an external link to extra "
-            "details, from the last attempt to upload this snap build "
-            "to the store."),
-        value_type=Dict(key_type=TextLine()),
-        required=False, readonly=True))
+        title=_("Last store upload job for this build."), schema=Interface
+    )
+
+    store_upload_status = exported(
+        Choice(
+            title=_("Store upload status"),
+            vocabulary=SnapBuildStoreUploadStatus,
+            required=True,
+            readonly=False,
+        )
+    )
+
+    store_upload_url = exported(
+        TextLine(
+            title=_("Store URL"),
+            description=_(
+                "The URL to use for managing this package in the store."
+            ),
+            required=False,
+            readonly=True,
+        )
+    )
+
+    store_upload_revision = exported(
+        Int(
+            title=_("Store revision"),
+            description=_(
+                "The revision assigned to this package by the store."
+            ),
+            required=False,
+            readonly=True,
+        )
+    )
+
+    store_upload_error_message = exported(
+        TextLine(
+            title=_("Store upload error message"),
+            description=_(
+                "The error message, if any, from the last attempt to upload "
+                "this snap build to the store.  (Deprecated; use "
+                "store_upload_error_messages instead.)"
+            ),
+            required=False,
+            readonly=True,
+        )
+    )
+
+    store_upload_error_messages = exported(
+        List(
+            title=_("Store upload error messages"),
+            description=_(
+                "A list of dict(message, link) where message is an error "
+                "description and link, if any, is an external link to extra "
+                "details, from the last attempt to upload this snap build "
+                "to the store."
+            ),
+            value_type=Dict(key_type=TextLine()),
+            required=False,
+            readonly=True,
+        )
+    )
 
     store_upload_metadata = Attribute(
-        _("A dict of data about store upload progress."))
+        _("A dict of data about store upload progress.")
+    )
 
     def getFiles():
         """Retrieve the build's `ISnapFile` records.
@@ -326,17 +400,27 @@ class ISnapBuildAdmin(IBuildFarmJobAdmin):
 # "devel".
 @exported_as_webservice_entry(as_of="beta")
 class ISnapBuild(
-        ISnapBuildView, ISnapBuildEdit, ISnapBuildAdmin, IPackageBuild):
+    ISnapBuildView, ISnapBuildEdit, ISnapBuildAdmin, IPackageBuild
+):
     """Build information for snap package builds."""
 
 
 class ISnapBuildSet(ISpecificBuildFarmJobSource):
     """Utility for `ISnapBuild`."""
 
-    def new(requester, snap, archive, distro_arch_series, pocket,
-            snap_base=None, channels=None, date_created=DEFAULT,
-            store_upload_metadata=None, build_request=None,
-            target_architectures=None):
+    def new(
+        requester,
+        snap,
+        archive,
+        distro_arch_series,
+        pocket,
+        snap_base=None,
+        channels=None,
+        date_created=DEFAULT,
+        store_upload_metadata=None,
+        build_request=None,
+        target_architectures=None,
+    ):
         """Create an `ISnapBuild`."""
 
     def preloadBuildsData(builds):
diff --git a/lib/lp/snappy/interfaces/snapbuildjob.py b/lib/lp/snappy/interfaces/snapbuildjob.py
index 456e061..12e4b8b 100644
--- a/lib/lp/snappy/interfaces/snapbuildjob.py
+++ b/lib/lp/snappy/interfaces/snapbuildjob.py
@@ -4,29 +4,19 @@
 """Snap build job interfaces."""
 
 __all__ = [
-    'ISnapBuildJob',
-    'ISnapBuildStoreUploadStatusChangedEvent',
-    'ISnapStoreUploadJob',
-    'ISnapStoreUploadJobSource',
-    ]
+    "ISnapBuildJob",
+    "ISnapBuildStoreUploadStatusChangedEvent",
+    "ISnapStoreUploadJob",
+    "ISnapStoreUploadJobSource",
+]
 
 from lazr.restful.fields import Reference
-from zope.interface import (
-    Attribute,
-    Interface,
-    )
+from zope.interface import Attribute, Interface
 from zope.interface.interfaces import IObjectEvent
-from zope.schema import (
-    Int,
-    TextLine,
-    )
+from zope.schema import Int, TextLine
 
 from lp import _
-from lp.services.job.interfaces.job import (
-    IJob,
-    IJobSource,
-    IRunnableJob,
-    )
+from lp.services.job.interfaces.job import IJob, IJobSource, IRunnableJob
 from lp.snappy.interfaces.snapbuild import ISnapBuild
 
 
@@ -34,12 +24,18 @@ class ISnapBuildJob(Interface):
     """A job related to a snap package."""
 
     job = Reference(
-        title=_("The common Job attributes."), schema=IJob,
-        required=True, readonly=True)
+        title=_("The common Job attributes."),
+        schema=IJob,
+        required=True,
+        readonly=True,
+    )
 
     snapbuild = Reference(
         title=_("The snap build to use for this job."),
-        schema=ISnapBuild, required=True, readonly=True)
+        schema=ISnapBuild,
+        required=True,
+        readonly=True,
+    )
 
     metadata = Attribute(_("A dict of data about the job."))
 
@@ -52,35 +48,46 @@ class ISnapStoreUploadJob(IRunnableJob):
     """A Job that uploads a snap build to the store."""
 
     store_metadata = Attribute(
-        _("Combined metadata for this job and matching snapbuild"))
+        _("Combined metadata for this job and matching snapbuild")
+    )
 
     error_message = TextLine(
-        title=_("Error message"), required=False, readonly=True)
+        title=_("Error message"), required=False, readonly=True
+    )
 
     error_detail = TextLine(
-        title=_("Error message detail"), required=False, readonly=True)
+        title=_("Error message detail"), required=False, readonly=True
+    )
 
     upload_id = Int(
         title=_(
             "The ID returned by the store when uploading this build's snap "
-            "file."),
-        required=False, readonly=True)
+            "file."
+        ),
+        required=False,
+        readonly=True,
+    )
 
     status_url = TextLine(
         title=_("The URL on the store to get the status of this build"),
-        required=False, readonly=True)
+        required=False,
+        readonly=True,
+    )
 
     store_url = TextLine(
         title=_("The URL on the store corresponding to this build"),
-        required=False, readonly=True)
+        required=False,
+        readonly=True,
+    )
 
     store_revision = Int(
         title=_("The revision assigned to this build by the store"),
-        required=False, readonly=True)
+        required=False,
+        readonly=True,
+    )
 
 
 class ISnapStoreUploadJobSource(IJobSource):
-
     def create(snapbuild):
         """Upload a snap build to the store.
 
diff --git a/lib/lp/snappy/interfaces/snapjob.py b/lib/lp/snappy/interfaces/snapjob.py
index 9a5b1f2..6a185df 100644
--- a/lib/lp/snappy/interfaces/snapjob.py
+++ b/lib/lp/snappy/interfaces/snapjob.py
@@ -4,37 +4,20 @@
 """Snap job interfaces."""
 
 __all__ = [
-    'ISnapJob',
-    'ISnapRequestBuildsJob',
-    'ISnapRequestBuildsJobSource',
-    ]
+    "ISnapJob",
+    "ISnapRequestBuildsJob",
+    "ISnapRequestBuildsJobSource",
+]
 
 from lazr.restful.fields import Reference
-from zope.interface import (
-    Attribute,
-    Interface,
-    )
-from zope.schema import (
-    Choice,
-    Datetime,
-    Dict,
-    List,
-    Set,
-    TextLine,
-    )
+from zope.interface import Attribute, Interface
+from zope.schema import Choice, Datetime, Dict, List, Set, TextLine
 
 from lp import _
 from lp.registry.interfaces.person import IPerson
 from lp.registry.interfaces.pocket import PackagePublishingPocket
-from lp.services.job.interfaces.job import (
-    IJob,
-    IJobSource,
-    IRunnableJob,
-    )
-from lp.snappy.interfaces.snap import (
-    ISnap,
-    ISnapBuildRequest,
-    )
+from lp.services.job.interfaces.job import IJob, IJobSource, IRunnableJob
+from lp.snappy.interfaces.snap import ISnap, ISnapBuildRequest
 from lp.snappy.interfaces.snapbuild import ISnapBuild
 from lp.soyuz.interfaces.archive import IArchive
 
@@ -43,12 +26,18 @@ class ISnapJob(Interface):
     """A job related to a snap package."""
 
     job = Reference(
-        title=_("The common Job attributes."), schema=IJob,
-        required=True, readonly=True)
+        title=_("The common Job attributes."),
+        schema=IJob,
+        required=True,
+        readonly=True,
+    )
 
     snap = Reference(
         title=_("The snap package to use for this job."),
-        schema=ISnap, required=True, readonly=True)
+        schema=ISnap,
+        required=True,
+        readonly=True,
+    )
 
     metadata = Attribute(_("A dict of data about the job."))
 
@@ -57,52 +46,77 @@ class ISnapRequestBuildsJob(IRunnableJob):
     """A Job that processes a request for builds of a snap package."""
 
     requester = Reference(
-        title=_("The person requesting the builds."), schema=IPerson,
-        required=True, readonly=True)
+        title=_("The person requesting the builds."),
+        schema=IPerson,
+        required=True,
+        readonly=True,
+    )
 
     archive = Reference(
-        title=_("The archive to associate the builds with."), schema=IArchive,
-        required=True, readonly=True)
+        title=_("The archive to associate the builds with."),
+        schema=IArchive,
+        required=True,
+        readonly=True,
+    )
 
     pocket = Choice(
         title=_("The pocket that should be targeted."),
-        vocabulary=PackagePublishingPocket, required=True, readonly=True)
+        vocabulary=PackagePublishingPocket,
+        required=True,
+        readonly=True,
+    )
 
     channels = Dict(
         title=_("Source snap channels to use for these builds."),
         description=_(
             "A dictionary mapping snap names to channels to use for these "
             "builds.  Currently only 'core', 'core18', 'core20', 'core22', "
-            "and 'snapcraft' keys are supported."),
-        key_type=TextLine(), required=False, readonly=True)
+            "and 'snapcraft' keys are supported."
+        ),
+        key_type=TextLine(),
+        required=False,
+        readonly=True,
+    )
 
     architectures = Set(
         title=_("If set, limit builds to these architecture tags."),
-        value_type=TextLine(), required=False, readonly=True)
+        value_type=TextLine(),
+        required=False,
+        readonly=True,
+    )
 
     date_created = Datetime(
         title=_("Time when this job was created."),
-        required=True, readonly=True)
+        required=True,
+        readonly=True,
+    )
 
     date_finished = Datetime(
-        title=_("Time when this job finished."),
-        required=True, readonly=True)
+        title=_("Time when this job finished."), required=True, readonly=True
+    )
 
     error_message = TextLine(
         title=_("Error message resulting from running this job."),
-        required=False, readonly=True)
+        required=False,
+        readonly=True,
+    )
 
     build_request = Reference(
         title=_("The build request corresponding to this job."),
-        schema=ISnapBuildRequest, required=True, readonly=True)
+        schema=ISnapBuildRequest,
+        required=True,
+        readonly=True,
+    )
 
     builds = List(
         title=_("The builds created by this request."),
-        value_type=Reference(schema=ISnapBuild), required=True, readonly=True)
+        value_type=Reference(schema=ISnapBuild),
+        required=True,
+        readonly=True,
+    )
 
 
 class ISnapRequestBuildsJobSource(IJobSource):
-
     def create(snap, requester, archive, pocket, channels, architectures=None):
         """Request builds of a snap package.
 
diff --git a/lib/lp/snappy/interfaces/snappyseries.py b/lib/lp/snappy/interfaces/snappyseries.py
index 57af216..f53dc2f 100644
--- a/lib/lp/snappy/interfaces/snappyseries.py
+++ b/lib/lp/snappy/interfaces/snappyseries.py
@@ -4,14 +4,15 @@
 """Snappy series interfaces."""
 
 __all__ = [
-    'ISnappyDistroSeries',
-    'ISnappyDistroSeriesSet',
-    'ISnappySeries',
-    'ISnappySeriesSet',
-    'NoSuchSnappySeries',
-    ]
+    "ISnappyDistroSeries",
+    "ISnappyDistroSeriesSet",
+    "ISnappySeries",
+    "ISnappySeriesSet",
+    "NoSuchSnappySeries",
+]
 
 from lazr.restful.declarations import (
+    REQUEST_USER,
     call_with,
     collection_default_content,
     export_factory_operation,
@@ -22,30 +23,18 @@ from lazr.restful.declarations import (
     operation_for_version,
     operation_parameters,
     operation_returns_entry,
-    REQUEST_USER,
-    )
+)
 from lazr.restful.fields import Reference
 from zope.component import getUtility
 from zope.interface import Interface
-from zope.schema import (
-    Bool,
-    Choice,
-    Datetime,
-    Int,
-    List,
-    TextLine,
-    )
+from zope.schema import Bool, Choice, Datetime, Int, List, TextLine
 
 from lp import _
 from lp.app.errors import NameLookupFailed
 from lp.app.validators.name import name_validator
 from lp.registry.interfaces.distroseries import IDistroSeries
 from lp.registry.interfaces.series import SeriesStatus
-from lp.services.fields import (
-    ContentNameField,
-    PublicPersonChoice,
-    Title,
-    )
+from lp.services.fields import ContentNameField, PublicPersonChoice, Title
 
 
 class NoSuchSnappySeries(NameLookupFailed):
@@ -77,13 +66,19 @@ class ISnappySeriesView(Interface):
 
     id = Int(title=_("ID"), required=True, readonly=True)
 
-    date_created = exported(Datetime(
-        title=_("Date created"), required=True, readonly=True))
+    date_created = exported(
+        Datetime(title=_("Date created"), required=True, readonly=True)
+    )
 
-    registrant = exported(PublicPersonChoice(
-        title=_("Registrant"), required=True, readonly=True,
-        vocabulary="ValidPersonOrTeam",
-        description=_("The person who registered this snappy series.")))
+    registrant = exported(
+        PublicPersonChoice(
+            title=_("Registrant"),
+            required=True,
+            readonly=True,
+            vocabulary="ValidPersonOrTeam",
+            description=_("The person who registered this snappy series."),
+        )
+    )
 
 
 class ISnappySeriesEditableAttributes(Interface):
@@ -92,34 +87,57 @@ class ISnappySeriesEditableAttributes(Interface):
     Anyone can view these attributes, but they need launchpad.Edit to change.
     """
 
-    name = exported(SnappySeriesNameField(
-        title=_("Name"), required=True, readonly=False,
-        constraint=name_validator))
+    name = exported(
+        SnappySeriesNameField(
+            title=_("Name"),
+            required=True,
+            readonly=False,
+            constraint=name_validator,
+        )
+    )
 
-    display_name = exported(TextLine(
-        title=_("Display name"), required=True, readonly=False))
+    display_name = exported(
+        TextLine(title=_("Display name"), required=True, readonly=False)
+    )
 
     title = Title(title=_("Title"), required=True, readonly=True)
 
-    status = exported(Choice(
-        title=_("Status"), required=True, vocabulary=SeriesStatus))
+    status = exported(
+        Choice(title=_("Status"), required=True, vocabulary=SeriesStatus)
+    )
 
-    preferred_distro_series = exported(Reference(
-        IDistroSeries, title=_("Preferred distro series"),
-        required=False, readonly=False))
+    preferred_distro_series = exported(
+        Reference(
+            IDistroSeries,
+            title=_("Preferred distro series"),
+            required=False,
+            readonly=False,
+        )
+    )
 
-    usable_distro_series = exported(List(
-        title=_("Usable distro series"),
-        description=_(
-            "The distro series that can be used for this snappy series."),
-        value_type=Reference(schema=IDistroSeries),
-        required=True, readonly=False))
+    usable_distro_series = exported(
+        List(
+            title=_("Usable distro series"),
+            description=_(
+                "The distro series that can be used for this snappy series."
+            ),
+            value_type=Reference(schema=IDistroSeries),
+            required=True,
+            readonly=False,
+        )
+    )
 
-    can_infer_distro_series = exported(Bool(
-        title=_("Can infer distro series?"), required=True, readonly=False,
-        description=_(
-            "True if inferring a distro series from snapcraft.yaml is "
-            "supported for this snappy series.")))
+    can_infer_distro_series = exported(
+        Bool(
+            title=_("Can infer distro series?"),
+            required=True,
+            readonly=False,
+            description=_(
+                "True if inferring a distro series from snapcraft.yaml is "
+                "supported for this snappy series."
+            ),
+        )
+    )
 
 
 # XXX cjwatson 2016-04-13 bug=760849: "beta" is a lie to get WADL
@@ -134,15 +152,20 @@ class ISnappyDistroSeries(Interface):
     """A snappy/distro series link."""
 
     snappy_series = Reference(
-        ISnappySeries, title=_("Snappy series"), readonly=True)
+        ISnappySeries, title=_("Snappy series"), readonly=True
+    )
     distro_series = Reference(
-        IDistroSeries, title=_("Distro series"), required=False, readonly=True)
+        IDistroSeries, title=_("Distro series"), required=False, readonly=True
+    )
     preferred = Bool(
         title=_("Preferred"),
-        required=True, readonly=False,
+        required=True,
+        readonly=False,
         description=_(
             "True if this identifies the default distro series for builds "
-            "for this snappy series."))
+            "for this snappy series."
+        ),
+    )
 
     title = Title(title=_("Title"), required=True, readonly=True)
 
@@ -152,7 +175,8 @@ class ISnappySeriesSetEdit(Interface):
 
     @call_with(registrant=REQUEST_USER)
     @export_factory_operation(
-        ISnappySeries, ["name", "display_name", "status"])
+        ISnappySeries, ["name", "display_name", "status"]
+    )
     @operation_for_version("devel")
     def new(registrant, name, display_name, status, date_created=None):
         """Create an `ISnappySeries`."""
@@ -169,7 +193,8 @@ class ISnappySeriesSet(ISnappySeriesSetEdit):
         """Return the `ISnappySeries` with this name."""
 
     @operation_parameters(
-        name=TextLine(title=_("Snappy series name"), required=True))
+        name=TextLine(title=_("Snappy series name"), required=True)
+    )
     @operation_returns_entry(ISnappySeries)
     @export_read_operation()
     @operation_for_version("devel")
diff --git a/lib/lp/snappy/interfaces/snapstoreclient.py b/lib/lp/snappy/interfaces/snapstoreclient.py
index f809b99..e45e8a9 100644
--- a/lib/lp/snappy/interfaces/snapstoreclient.py
+++ b/lib/lp/snappy/interfaces/snapstoreclient.py
@@ -4,17 +4,17 @@
 """Interface for communication with the snap store."""
 
 __all__ = [
-    'BadRefreshResponse',
-    'BadRequestPackageUploadResponse',
-    'BadScanStatusResponse',
-    'ISnapStoreClient',
-    'NeedsRefreshResponse',
-    'ScanFailedResponse',
-    'SnapStoreError',
-    'UnauthorizedUploadResponse',
-    'UploadFailedResponse',
-    'UploadNotScannedYetResponse',
-    ]
+    "BadRefreshResponse",
+    "BadRequestPackageUploadResponse",
+    "BadScanStatusResponse",
+    "ISnapStoreClient",
+    "NeedsRefreshResponse",
+    "ScanFailedResponse",
+    "SnapStoreError",
+    "UnauthorizedUploadResponse",
+    "UploadFailedResponse",
+    "UploadNotScannedYetResponse",
+]
 
 import http.client
 
@@ -23,9 +23,9 @@ from zope.interface import Interface
 
 
 class SnapStoreError(Exception):
-
     def __init__(
-            self, message="", detail=None, messages=None, can_retry=False):
+        self, message="", detail=None, messages=None, can_retry=False
+    ):
         super().__init__(message)
         self.message = message
         self.detail = detail
diff --git a/lib/lp/snappy/interfaces/snapsubscription.py b/lib/lp/snappy/interfaces/snapsubscription.py
index 184aa74..81491be 100644
--- a/lib/lp/snappy/interfaces/snapsubscription.py
+++ b/lib/lp/snappy/interfaces/snapsubscription.py
@@ -3,16 +3,11 @@
 
 """Snap subscription model."""
 
-__all__ = [
-    'ISnapSubscription'
-]
+__all__ = ["ISnapSubscription"]
 
 from lazr.restful.fields import Reference
 from zope.interface import Interface
-from zope.schema import (
-    Datetime,
-    Int,
-    )
+from zope.schema import Datetime, Int
 
 from lp import _
 from lp.services.fields import PersonChoice
@@ -22,18 +17,25 @@ from lp.snappy.interfaces.snap import ISnap
 class ISnapSubscription(Interface):
     """A person subscription to a specific Snap recipe."""
 
-    id = Int(title=_('ID'), readonly=True, required=True)
+    id = Int(title=_("ID"), readonly=True, required=True)
     person = PersonChoice(
-        title=_('Person'), required=True, vocabulary='ValidPersonOrTeam',
+        title=_("Person"),
+        required=True,
+        vocabulary="ValidPersonOrTeam",
         readonly=True,
-        description=_("The person subscribed to the related snap recipe."))
+        description=_("The person subscribed to the related snap recipe."),
+    )
     snap = Reference(ISnap, title=_("Snap"), required=True, readonly=True)
     subscribed_by = PersonChoice(
-        title=_('Subscribed by'), required=True,
-        vocabulary='ValidPersonOrTeam', readonly=True,
-        description=_("The person who created this subscription."))
+        title=_("Subscribed by"),
+        required=True,
+        vocabulary="ValidPersonOrTeam",
+        readonly=True,
+        description=_("The person who created this subscription."),
+    )
     date_created = Datetime(
-        title=_('Date subscribed'), required=True, readonly=True)
+        title=_("Date subscribed"), required=True, readonly=True
+    )
 
     def canBeUnsubscribedByUser(user):
         """Can the user unsubscribe the subscriber from the snap recipe?"""
diff --git a/lib/lp/snappy/interfaces/webservice.py b/lib/lp/snappy/interfaces/webservice.py
index d96695c..0b34a82 100644
--- a/lib/lp/snappy/interfaces/webservice.py
+++ b/lib/lp/snappy/interfaces/webservice.py
@@ -10,56 +10,46 @@ which tells `lazr.restful` that it should look for webservice exports here.
 """
 
 __all__ = [
-    'ISnap',
-    'ISnapBase',
-    'ISnapBaseSet',
-    'ISnapBuild',
-    'ISnapBuildRequest',
-    'ISnappySeries',
-    'ISnappySeriesSet',
-    'ISnapSet',
-    ]
+    "ISnap",
+    "ISnapBase",
+    "ISnapBaseSet",
+    "ISnapBuild",
+    "ISnapBuildRequest",
+    "ISnappySeries",
+    "ISnappySeriesSet",
+    "ISnapSet",
+]
 
 from lp.services.webservice.apihelpers import (
     patch_collection_property,
     patch_collection_return_type,
     patch_entry_return_type,
     patch_reference_property,
-    )
+)
 from lp.snappy.interfaces.snap import (
     ISnap,
     ISnapBuildRequest,
     ISnapEdit,
     ISnapSet,
     ISnapView,
-    )
-from lp.snappy.interfaces.snapbase import (
-    ISnapBase,
-    ISnapBaseSet,
-    )
-from lp.snappy.interfaces.snapbuild import (
-    ISnapBuild,
-    ISnapFile,
-    )
-from lp.snappy.interfaces.snappyseries import (
-    ISnappySeries,
-    ISnappySeriesSet,
-    )
-
+)
+from lp.snappy.interfaces.snapbase import ISnapBase, ISnapBaseSet
+from lp.snappy.interfaces.snapbuild import ISnapBuild, ISnapFile
+from lp.snappy.interfaces.snappyseries import ISnappySeries, ISnappySeriesSet
 
 # ISnapFile
-patch_reference_property(ISnapFile, 'snapbuild', ISnapBuild)
+patch_reference_property(ISnapFile, "snapbuild", ISnapBuild)
 
 # ISnapBuildRequest
-patch_reference_property(ISnapBuildRequest, 'snap', ISnap)
-patch_collection_property(ISnapBuildRequest, 'builds', ISnapBuild)
+patch_reference_property(ISnapBuildRequest, "snap", ISnap)
+patch_collection_property(ISnapBuildRequest, "builds", ISnapBuild)
 
 # ISnapView
-patch_entry_return_type(ISnapView, 'requestBuild', ISnapBuild)
-patch_collection_property(ISnapView, 'builds', ISnapBuild)
-patch_collection_property(ISnapView, 'completed_builds', ISnapBuild)
-patch_collection_property(ISnapView, 'pending_builds', ISnapBuild)
-patch_entry_return_type(ISnapView, 'getBuildByStoreRevision', ISnapBuild)
+patch_entry_return_type(ISnapView, "requestBuild", ISnapBuild)
+patch_collection_property(ISnapView, "builds", ISnapBuild)
+patch_collection_property(ISnapView, "completed_builds", ISnapBuild)
+patch_collection_property(ISnapView, "pending_builds", ISnapBuild)
+patch_entry_return_type(ISnapView, "getBuildByStoreRevision", ISnapBuild)
 
 # ISnapEdit
-patch_collection_return_type(ISnapEdit, 'requestAutoBuilds', ISnapBuild)
+patch_collection_return_type(ISnapEdit, "requestAutoBuilds", ISnapBuild)
diff --git a/lib/lp/snappy/mail/snapbuild.py b/lib/lp/snappy/mail/snapbuild.py
index aee1685..ca70774 100644
--- a/lib/lp/snappy/mail/snapbuild.py
+++ b/lib/lp/snappy/mail/snapbuild.py
@@ -2,21 +2,18 @@
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 __all__ = [
-    'SnapBuildMailer',
-    ]
+    "SnapBuildMailer",
+]
 
 from lp.app.browser.tales import DurationFormatterAPI
 from lp.services.config import config
-from lp.services.mail.basemailer import (
-    BaseMailer,
-    RecipientReason,
-    )
+from lp.services.mail.basemailer import BaseMailer, RecipientReason
 from lp.services.webapp import canonical_url
 
 
 class SnapBuildMailer(BaseMailer):
 
-    app = 'snappy'
+    app = "snappy"
 
     @classmethod
     def forStatus(cls, build):
@@ -28,8 +25,12 @@ class SnapBuildMailer(BaseMailer):
         recipients = {requester: RecipientReason.forBuildRequester(requester)}
         return cls(
             "[Snap build #%(build_id)d] %(build_title)s",
-            "snapbuild-notification.txt", recipients,
-            config.canonical.noreply_from_address, "snap-build-status", build)
+            "snapbuild-notification.txt",
+            recipients,
+            config.canonical.noreply_from_address,
+            "snap-build-status",
+            build,
+        )
 
     @classmethod
     def forUnauthorizedUpload(cls, build):
@@ -41,9 +42,12 @@ class SnapBuildMailer(BaseMailer):
         recipients = {requester: RecipientReason.forBuildRequester(requester)}
         return cls(
             "Store authorization failed for %(snap_name)s",
-            "snapbuild-unauthorized.txt", recipients,
+            "snapbuild-unauthorized.txt",
+            recipients,
             config.canonical.noreply_from_address,
-            "snap-build-upload-unauthorized", build)
+            "snap-build-upload-unauthorized",
+            build,
+        )
 
     @classmethod
     def forRefreshFailure(cls, build):
@@ -55,9 +59,12 @@ class SnapBuildMailer(BaseMailer):
         recipients = {requester: RecipientReason.forBuildRequester(requester)}
         return cls(
             "Refreshing store authorization failed for %(snap_name)s",
-            "snapbuild-refreshfailed.txt", recipients,
+            "snapbuild-refreshfailed.txt",
+            recipients,
             config.canonical.noreply_from_address,
-            "snap-build-upload-refresh-failed", build)
+            "snap-build-upload-refresh-failed",
+            build,
+        )
 
     @classmethod
     def forUploadFailure(cls, build):
@@ -69,9 +76,12 @@ class SnapBuildMailer(BaseMailer):
         recipients = {requester: RecipientReason.forBuildRequester(requester)}
         return cls(
             "Store upload failed for %(snap_name)s",
-            "snapbuild-uploadfailed.txt", recipients,
+            "snapbuild-uploadfailed.txt",
+            recipients,
             config.canonical.noreply_from_address,
-            "snap-build-upload-failed", build)
+            "snap-build-upload-failed",
+            build,
+        )
 
     @classmethod
     def forUploadScanFailure(cls, build):
@@ -83,15 +93,29 @@ class SnapBuildMailer(BaseMailer):
         recipients = {requester: RecipientReason.forBuildRequester(requester)}
         return cls(
             "Store upload scan failed for %(snap_name)s",
-            "snapbuild-scanfailed.txt", recipients,
+            "snapbuild-scanfailed.txt",
+            recipients,
             config.canonical.noreply_from_address,
-            "snap-build-upload-scan-failed", build)
-
-    def __init__(self, subject, template_name, recipients, from_address,
-                 notification_type, build):
+            "snap-build-upload-scan-failed",
+            build,
+        )
+
+    def __init__(
+        self,
+        subject,
+        template_name,
+        recipients,
+        from_address,
+        notification_type,
+        build,
+    ):
         super().__init__(
-            subject, template_name, recipients, from_address,
-            notification_type=notification_type)
+            subject,
+            template_name,
+            recipients,
+            from_address,
+            notification_type=notification_type,
+        )
         self.build = build
 
     def _getHeaders(self, email, recipient):
@@ -111,25 +135,28 @@ class SnapBuildMailer(BaseMailer):
             error_message = upload_job.error_message or ""
             store_url = upload_job.store_url or ""
         params = super()._getTemplateParams(email, recipient)
-        params.update({
-            "archive_tag": build.archive.reference,
-            "build_id": build.id,
-            "build_title": build.title,
-            "snap_name": build.snap.name,
-            "distroseries": build.snap.distro_series,
-            "architecturetag": build.distro_arch_series.architecturetag,
-            "pocket": build.pocket.name,
-            "build_state": build.status.title,
-            "build_duration": "",
-            "log_url": "",
-            "upload_log_url": "",
-            "builder_url": "",
-            "build_url": canonical_url(build),
-            "snap_authorize_url": canonical_url(
-                build.snap, view_name="+authorize"),
-            "store_error_message": error_message,
-            "store_url": store_url,
-            })
+        params.update(
+            {
+                "archive_tag": build.archive.reference,
+                "build_id": build.id,
+                "build_title": build.title,
+                "snap_name": build.snap.name,
+                "distroseries": build.snap.distro_series,
+                "architecturetag": build.distro_arch_series.architecturetag,
+                "pocket": build.pocket.name,
+                "build_state": build.status.title,
+                "build_duration": "",
+                "log_url": "",
+                "upload_log_url": "",
+                "builder_url": "",
+                "build_url": canonical_url(build),
+                "snap_authorize_url": canonical_url(
+                    build.snap, view_name="+authorize"
+                ),
+                "store_error_message": error_message,
+                "store_url": store_url,
+            }
+        )
         if build.duration is not None:
             duration_formatter = DurationFormatterAPI(build.duration)
             params["build_duration"] = duration_formatter.approximateduration()
@@ -143,5 +170,4 @@ class SnapBuildMailer(BaseMailer):
 
     def _getFooter(self, email, recipient, params):
         """See `BaseMailer`."""
-        return ("%(build_url)s\n"
-                "%(reason)s\n" % params)
+        return "%(build_url)s\n" "%(reason)s\n" % params
diff --git a/lib/lp/snappy/model/snap.py b/lib/lp/snappy/model/snap.py
index bfc5795..28e1d28 100644
--- a/lib/lp/snappy/model/snap.py
+++ b/lib/lp/snappy/model/snap.py
@@ -2,26 +2,25 @@
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 __all__ = [
-    'get_snap_privacy_filter',
-    'Snap',
-    ]
+    "get_snap_privacy_filter",
+    "Snap",
+]
 
 import base64
+import typing as t
 from collections import OrderedDict
-from datetime import (
-    datetime,
-    timedelta,
-    )
+from datetime import datetime, timedelta
 from operator import attrgetter
-import typing as t
 from urllib.parse import urlsplit
 
+import pytz
+import six
+import yaml
 from breezy import urlutils
 from lazr.lifecycle.event import ObjectCreatedEvent
 from pymacaroons import Macaroon
-import pytz
-import six
 from storm.expr import (
+    SQL,
     And,
     Coalesce,
     Desc,
@@ -31,46 +30,35 @@ from storm.expr import (
     Not,
     Or,
     Select,
-    SQL,
-    )
+)
 from storm.locals import (
+    JSON,
     Bool,
     DateTime,
     Int,
-    JSON,
     Reference,
     Store,
     Storm,
     Unicode,
-    )
-import yaml
-from zope.component import (
-    getAdapter,
-    getUtility,
-    )
+)
+from zope.component import getAdapter, getUtility
 from zope.event import notify
-from zope.interface import (
-    directlyProvides,
-    implementer,
-    )
+from zope.interface import directlyProvides, implementer
 from zope.security.interfaces import Unauthorized
 from zope.security.proxy import removeSecurityProxy
 
-from lp.app.browser.tales import (
-    ArchiveFormatterAPI,
-    DateTimeFormatterAPI,
-    )
+from lp.app.browser.tales import ArchiveFormatterAPI, DateTimeFormatterAPI
 from lp.app.enums import (
     FREE_INFORMATION_TYPES,
-    InformationType,
     PRIVATE_INFORMATION_TYPES,
     PUBLIC_INFORMATION_TYPES,
-    )
+    InformationType,
+)
 from lp.app.errors import (
     IncompatibleArguments,
     SubscriptionPrivacyViolation,
     UserCannotUnsubscribePerson,
-    )
+)
 from lp.app.interfaces.launchpad import ILaunchpadCelebrities
 from lp.app.interfaces.security import IAuthorization
 from lp.app.interfaces.services import IService
@@ -86,53 +74,44 @@ from lp.code.errors import (
     GitRepositoryBlobNotFound,
     GitRepositoryBlobUnsupportedRemote,
     GitRepositoryScanFault,
-    )
+)
 from lp.code.interfaces.branch import IBranch
-from lp.code.interfaces.branchcollection import (
-    IAllBranches,
-    IBranchCollection,
-    )
+from lp.code.interfaces.branchcollection import IAllBranches, IBranchCollection
 from lp.code.interfaces.gitcollection import (
     IAllGitRepositories,
     IGitCollection,
-    )
-from lp.code.interfaces.gitref import (
-    IGitRef,
-    IGitRefRemoteSet,
-    )
+)
+from lp.code.interfaces.gitref import IGitRef, IGitRefRemoteSet
 from lp.code.interfaces.gitrepository import (
     IGitRepository,
     IHasGitRepositoryURL,
-    )
+)
 from lp.code.model.branch import Branch
 from lp.code.model.branchcollection import GenericBranchCollection
 from lp.code.model.branchnamespace import (
     BRANCH_POLICY_ALLOWED_TYPES,
     BRANCH_POLICY_REQUIRED_GRANTS,
-    )
+)
 from lp.code.model.gitcollection import GenericGitCollection
 from lp.code.model.gitrepository import GitRepository
 from lp.registry.errors import PrivatePersonLinkageError
 from lp.registry.interfaces.accesspolicy import (
     IAccessArtifactGrantSource,
     IAccessArtifactSource,
-    )
+)
 from lp.registry.interfaces.distroseries import IDistroSeries
 from lp.registry.interfaces.person import (
     IPerson,
     IPersonSet,
     validate_public_person,
-    )
+)
 from lp.registry.interfaces.pocket import PackagePublishingPocket
 from lp.registry.interfaces.product import IProduct
-from lp.registry.interfaces.role import (
-    IHasOwner,
-    IPersonRoles,
-    )
+from lp.registry.interfaces.role import IHasOwner, IPersonRoles
 from lp.registry.model.accesspolicy import (
     AccessPolicyGrant,
     reconcile_access_for_artifacts,
-    )
+)
 from lp.registry.model.distroseries import DistroSeries
 from lp.registry.model.person import Person
 from lp.registry.model.series import ACTIVE_STATUSES
@@ -141,16 +120,10 @@ from lp.services.config import config
 from lp.services.crypto.interfaces import IEncryptedContainer
 from lp.services.crypto.model import NaClEncryptedContainerBase
 from lp.services.database.bulk import load_related
-from lp.services.database.constants import (
-    DEFAULT,
-    UTC_NOW,
-    )
+from lp.services.database.constants import DEFAULT, UTC_NOW
 from lp.services.database.decoratedresultset import DecoratedResultSet
 from lp.services.database.enumcol import DBEnum
-from lp.services.database.interfaces import (
-    IMasterStore,
-    IStore,
-    )
+from lp.services.database.interfaces import IMasterStore, IStore
 from lp.services.database.stormexpr import (
     Array,
     ArrayAgg,
@@ -158,19 +131,13 @@ from lp.services.database.stormexpr import (
     Greatest,
     IsDistinctFrom,
     NullsLast,
-    )
+)
 from lp.services.features import getFeatureFlag
 from lp.services.job.interfaces.job import JobStatus
 from lp.services.job.model.job import Job
-from lp.services.librarian.model import (
-    LibraryFileAlias,
-    LibraryFileContent,
-    )
+from lp.services.librarian.model import LibraryFileAlias, LibraryFileContent
 from lp.services.openid.adapters.openid import CurrentOpenIDEndPoint
-from lp.services.propertycache import (
-    cachedproperty,
-    get_property_cache,
-    )
+from lp.services.propertycache import cachedproperty, get_property_cache
 from lp.services.webapp.authorization import precache_permission_for_objects
 from lp.services.webapp.interfaces import ILaunchBag
 from lp.services.webapp.publisher import canonical_url
@@ -178,6 +145,7 @@ from lp.services.webhooks.interfaces import IWebhookSet
 from lp.services.webhooks.model import WebhookTargetMixin
 from lp.snappy.adapters.buildarch import determine_architectures_to_build
 from lp.snappy.interfaces.snap import (
+    SNAP_PRIVATE_FEATURE_FLAG,
     BadMacaroon,
     BadSnapSearchContext,
     BadSnapSource,
@@ -193,7 +161,6 @@ from lp.snappy.interfaces.snap import (
     MissingSnapcraftYaml,
     NoSourceForSnap,
     NoSuchSnap,
-    SNAP_PRIVATE_FEATURE_FLAG,
     SnapAuthorizationBadGeneratedMacaroon,
     SnapBuildAlreadyPending,
     SnapBuildArchiveOwnerMismatch,
@@ -202,15 +169,9 @@ from lp.snappy.interfaces.snap import (
     SnapNotOwner,
     SnapPrivacyMismatch,
     SnapPrivateFeatureDisabled,
-    )
-from lp.snappy.interfaces.snapbase import (
-    ISnapBaseSet,
-    NoSuchSnapBase,
-    )
-from lp.snappy.interfaces.snapbuild import (
-    ISnapBuild,
-    ISnapBuildSet,
-    )
+)
+from lp.snappy.interfaces.snapbase import ISnapBaseSet, NoSuchSnapBase
+from lp.snappy.interfaces.snapbuild import ISnapBuild, ISnapBuildSet
 from lp.snappy.interfaces.snapjob import ISnapRequestBuildsJobSource
 from lp.snappy.interfaces.snappyseries import ISnappyDistroSeriesSet
 from lp.snappy.interfaces.snapstoreclient import ISnapStoreClient
@@ -219,10 +180,7 @@ from lp.snappy.model.snapjob import SnapJob
 from lp.snappy.model.snapsubscription import SnapSubscription
 from lp.soyuz.interfaces.archive import ArchiveDisabled
 from lp.soyuz.interfaces.distroarchseries import IDistroArchSeries
-from lp.soyuz.model.archive import (
-    Archive,
-    get_enabled_archive_filter,
-    )
+from lp.soyuz.model.archive import Archive, get_enabled_archive_filter
 from lp.soyuz.model.distroarchseries import DistroArchSeries
 
 
@@ -289,7 +247,7 @@ class SnapBuildRequest:
             JobStatus.COMPLETED: SnapBuildRequestStatus.COMPLETED,
             JobStatus.FAILED: SnapBuildRequestStatus.FAILED,
             JobStatus.SUSPENDED: SnapBuildRequestStatus.PENDING,
-            }
+        }
         return status_map[self._job.job.status]
 
     @property
@@ -332,17 +290,19 @@ class SnapBuildRequest:
 class Snap(Storm, WebhookTargetMixin):
     """See `ISnap`."""
 
-    __storm_table__ = 'Snap'
+    __storm_table__ = "Snap"
 
     id = Int(primary=True)
 
     date_created = DateTime(
-        name='date_created', tzinfo=pytz.UTC, allow_none=False)
+        name="date_created", tzinfo=pytz.UTC, allow_none=False
+    )
     date_last_modified = DateTime(
-        name='date_last_modified', tzinfo=pytz.UTC, allow_none=False)
+        name="date_last_modified", tzinfo=pytz.UTC, allow_none=False
+    )
 
-    registrant_id = Int(name='registrant', allow_none=False)
-    registrant = Reference(registrant_id, 'Person.id')
+    registrant_id = Int(name="registrant", allow_none=False)
+    registrant = Reference(registrant_id, "Person.id")
 
     def _validate_owner(self, attr, value):
         if not self.private:
@@ -350,97 +310,122 @@ class Snap(Storm, WebhookTargetMixin):
                 validate_public_person(self, attr, value)
             except PrivatePersonLinkageError:
                 raise SnapPrivacyMismatch(
-                    "A public snap cannot have a private owner.")
+                    "A public snap cannot have a private owner."
+                )
         return value
 
-    owner_id = Int(name='owner', allow_none=False, validator=_validate_owner)
-    owner = Reference(owner_id, 'Person.id')
+    owner_id = Int(name="owner", allow_none=False, validator=_validate_owner)
+    owner = Reference(owner_id, "Person.id")
 
-    project_id = Int(name='project', allow_none=True)
-    project = Reference(project_id, 'Product.id')
+    project_id = Int(name="project", allow_none=True)
+    project = Reference(project_id, "Product.id")
 
-    distro_series_id = Int(name='distro_series', allow_none=True)
-    distro_series = Reference(distro_series_id, 'DistroSeries.id')
+    distro_series_id = Int(name="distro_series", allow_none=True)
+    distro_series = Reference(distro_series_id, "DistroSeries.id")
 
-    name = Unicode(name='name', allow_none=False)
+    name = Unicode(name="name", allow_none=False)
 
-    description = Unicode(name='description', allow_none=True)
+    description = Unicode(name="description", allow_none=True)
 
     def _validate_branch(self, attr, value):
         if not self.private and value is not None:
             if IStore(Branch).get(Branch, value).private:
                 raise SnapPrivacyMismatch(
-                    "A public snap cannot have a private branch.")
+                    "A public snap cannot have a private branch."
+                )
         return value
 
-    branch_id = Int(name='branch', allow_none=True, validator=_validate_branch)
-    branch = Reference(branch_id, 'Branch.id')
+    branch_id = Int(name="branch", allow_none=True, validator=_validate_branch)
+    branch = Reference(branch_id, "Branch.id")
 
     def _validate_git_repository(self, attr, value):
         if not self.private and value is not None:
             if IStore(GitRepository).get(GitRepository, value).private:
                 raise SnapPrivacyMismatch(
-                    "A public snap cannot have a private repository.")
+                    "A public snap cannot have a private repository."
+                )
         return value
 
     git_repository_id = Int(
-        name='git_repository', allow_none=True,
-        validator=_validate_git_repository)
-    git_repository = Reference(git_repository_id, 'GitRepository.id')
+        name="git_repository",
+        allow_none=True,
+        validator=_validate_git_repository,
+    )
+    git_repository = Reference(git_repository_id, "GitRepository.id")
 
-    git_repository_url = Unicode(name='git_repository_url', allow_none=True)
+    git_repository_url = Unicode(name="git_repository_url", allow_none=True)
 
-    git_path = Unicode(name='git_path', allow_none=True)
+    git_path = Unicode(name="git_path", allow_none=True)
 
-    auto_build = Bool(name='auto_build', allow_none=False)
+    auto_build = Bool(name="auto_build", allow_none=False)
 
-    auto_build_archive_id = Int(name='auto_build_archive', allow_none=True)
-    auto_build_archive = Reference(auto_build_archive_id, 'Archive.id')
+    auto_build_archive_id = Int(name="auto_build_archive", allow_none=True)
+    auto_build_archive = Reference(auto_build_archive_id, "Archive.id")
 
     auto_build_pocket = DBEnum(enum=PackagePublishingPocket, allow_none=True)
 
-    auto_build_channels = JSON('auto_build_channels', allow_none=True)
+    auto_build_channels = JSON("auto_build_channels", allow_none=True)
 
-    is_stale = Bool(name='is_stale', allow_none=False)
+    is_stale = Bool(name="is_stale", allow_none=False)
 
-    require_virtualized = Bool(name='require_virtualized')
+    require_virtualized = Bool(name="require_virtualized")
 
-    _private = Bool(name='private')
+    _private = Bool(name="private")
 
     def _valid_information_type(self, attr, value):
         if not getUtility(ISnapSet).isValidInformationType(
-                value, self.owner, self.branch, self.git_ref):
+            value, self.owner, self.branch, self.git_ref
+        ):
             raise SnapPrivacyMismatch
         return value
 
     _information_type = DBEnum(
-        enum=InformationType, default=InformationType.PUBLIC,
+        enum=InformationType,
+        default=InformationType.PUBLIC,
         name="information_type",
-        validator=_valid_information_type)
+        validator=_valid_information_type,
+    )
 
-    allow_internet = Bool(name='allow_internet', allow_none=False)
+    allow_internet = Bool(name="allow_internet", allow_none=False)
 
-    build_source_tarball = Bool(name='build_source_tarball', allow_none=False)
+    build_source_tarball = Bool(name="build_source_tarball", allow_none=False)
 
-    store_upload = Bool(name='store_upload', allow_none=False)
+    store_upload = Bool(name="store_upload", allow_none=False)
 
-    store_series_id = Int(name='store_series', allow_none=True)
-    store_series = Reference(store_series_id, 'SnappySeries.id')
+    store_series_id = Int(name="store_series", allow_none=True)
+    store_series = Reference(store_series_id, "SnappySeries.id")
 
-    store_name = Unicode(name='store_name', allow_none=True)
+    store_name = Unicode(name="store_name", allow_none=True)
 
-    store_secrets = JSON('store_secrets', allow_none=True)
+    store_secrets = JSON("store_secrets", allow_none=True)
 
-    _store_channels = JSON('store_channels', allow_none=True)
+    _store_channels = JSON("store_channels", allow_none=True)
 
-    def __init__(self, registrant, owner, distro_series, name,
-                 description=None, branch=None, git_ref=None, auto_build=False,
-                 auto_build_archive=None, auto_build_pocket=None,
-                 auto_build_channels=None, require_virtualized=True,
-                 date_created=DEFAULT, information_type=InformationType.PUBLIC,
-                 allow_internet=True, build_source_tarball=False,
-                 store_upload=False, store_series=None, store_name=None,
-                 store_secrets=None, store_channels=None, project=None):
+    def __init__(
+        self,
+        registrant,
+        owner,
+        distro_series,
+        name,
+        description=None,
+        branch=None,
+        git_ref=None,
+        auto_build=False,
+        auto_build_archive=None,
+        auto_build_pocket=None,
+        auto_build_channels=None,
+        require_virtualized=True,
+        date_created=DEFAULT,
+        information_type=InformationType.PUBLIC,
+        allow_internet=True,
+        build_source_tarball=False,
+        store_upload=False,
+        store_series=None,
+        store_name=None,
+        store_secrets=None,
+        store_channels=None,
+        project=None,
+    ):
         """Construct a `Snap`."""
         super().__init__()
 
@@ -481,8 +466,11 @@ class Snap(Storm, WebhookTargetMixin):
     @property
     def information_type(self):
         if self._information_type is None:
-            return (InformationType.PROPRIETARY if self._private
-                    else InformationType.PUBLIC)
+            return (
+                InformationType.PROPRIETARY
+                if self._private
+                else InformationType.PUBLIC
+            )
         return self._information_type
 
     @information_type.setter
@@ -506,7 +494,8 @@ class Snap(Storm, WebhookTargetMixin):
     def _api_git_path(self, value):
         if self.git_repository is None and self.git_repository_url is None:
             raise BadSnapSource(
-                "git_path may only be set on a Git-based snap.")
+                "git_path may only be set on a Git-based snap."
+            )
         if value is None:
             raise BadSnapSource("git_path may not be set to None.")
         self.git_path = value
@@ -518,7 +507,8 @@ class Snap(Storm, WebhookTargetMixin):
             return self.git_repository.getRefByPath(self.git_path)
         elif self.git_repository_url is not None:
             return getUtility(IGitRefRemoteSet).new(
-                self.git_repository_url, self.git_path)
+                self.git_repository_url, self.git_path
+            )
         else:
             return None
 
@@ -560,32 +550,42 @@ class Snap(Storm, WebhookTargetMixin):
             self.project = pillar
         else:
             raise ValueError(
-                'The pillar of a Snap must be an IProduct instance.')
+                "The pillar of a Snap must be an IProduct instance."
+            )
 
     @property
     def available_processors(self):
         """See `ISnap`."""
         clauses = [Processor.id == DistroArchSeries.processor_id]
         if self.distro_series is not None:
-            clauses.append(DistroArchSeries.id.is_in(
-                self.distro_series.enabled_architectures.get_select_expr(
-                    DistroArchSeries.id)))
+            clauses.append(
+                DistroArchSeries.id.is_in(
+                    self.distro_series.enabled_architectures.get_select_expr(
+                        DistroArchSeries.id
+                    )
+                )
+            )
         else:
             # We don't know the series until we've looked at snapcraft.yaml
             # to dispatch a build, so enabled architectures for any active
             # series will do.
-            clauses.extend([
-                DistroArchSeries.enabled,
-                DistroArchSeries.distroseriesID == DistroSeries.id,
-                DistroSeries.status.is_in(ACTIVE_STATUSES),
-                ])
+            clauses.extend(
+                [
+                    DistroArchSeries.enabled,
+                    DistroArchSeries.distroseriesID == DistroSeries.id,
+                    DistroSeries.status.is_in(ACTIVE_STATUSES),
+                ]
+            )
         return Store.of(self).find(Processor, *clauses).config(distinct=True)
 
     def _getProcessors(self):
-        return list(Store.of(self).find(
-            Processor,
-            Processor.id == SnapArch.processor_id,
-            SnapArch.snap == self))
+        return list(
+            Store.of(self).find(
+                Processor,
+                Processor.id == SnapArch.processor_id,
+                SnapArch.snap == self,
+            )
+        )
 
     def setProcessors(self, processors, check_permissions=False, user=None):
         """See `ISnap`."""
@@ -594,21 +594,25 @@ class Snap(Storm, WebhookTargetMixin):
             if user is not None:
                 roles = IPersonRoles(user)
                 authz = lambda perm: getAdapter(self, IAuthorization, perm)
-                if authz('launchpad.Admin').checkAuthenticated(roles):
+                if authz("launchpad.Admin").checkAuthenticated(roles):
                     can_modify = lambda proc: True
-                elif authz('launchpad.Edit').checkAuthenticated(roles):
+                elif authz("launchpad.Edit").checkAuthenticated(roles):
                     can_modify = lambda proc: not proc.restricted
             if can_modify is None:
                 raise Unauthorized(
-                    'Permission launchpad.Admin or launchpad.Edit required '
-                    'on %s.' % self)
+                    "Permission launchpad.Admin or launchpad.Edit required "
+                    "on %s." % self
+                )
         else:
             can_modify = lambda proc: True
 
-        enablements = dict(Store.of(self).find(
-            (Processor, SnapArch),
-            Processor.id == SnapArch.processor_id,
-            SnapArch.snap == self))
+        enablements = dict(
+            Store.of(self).find(
+                (Processor, SnapArch),
+                Processor.id == SnapArch.processor_id,
+                SnapArch.snap == self,
+            )
+        )
         for proc in enablements:
             if proc not in processors:
                 if not can_modify(proc):
@@ -637,32 +641,37 @@ class Snap(Storm, WebhookTargetMixin):
             and das.processor in self.processors
             and (
                 das.processor.supports_virtualized
-                or not self.require_virtualized)
-            and (snap_base is None or das.processor in snap_base.processors))
+                or not self.require_virtualized
+            )
+            and (snap_base is None or das.processor in snap_base.processors)
+        )
 
     def _isArchitectureAllowed(self, das, pocket, snap_base=None):
-        return (
-            das.getChroot(pocket=pocket) is not None
-            and self._isBuildableArchitectureAllowed(das, snap_base=snap_base))
+        return das.getChroot(
+            pocket=pocket
+        ) is not None and self._isBuildableArchitectureAllowed(
+            das, snap_base=snap_base
+        )
 
     def getAllowedArchitectures(
-        self,
-        distro_series: t.Optional[IDistroSeries] = None,
-        snap_base = None
+        self, distro_series: t.Optional[IDistroSeries] = None, snap_base=None
     ) -> t.List[IDistroArchSeries]:
         """See `ISnap`."""
         if distro_series is None:
             distro_series = self.distro_series
         return [
-            das for das in distro_series.buildable_architectures
-            if self._isBuildableArchitectureAllowed(das, snap_base=snap_base)]
+            das
+            for das in distro_series.buildable_architectures
+            if self._isBuildableArchitectureAllowed(das, snap_base=snap_base)
+        ]
 
     @property
     def store_distro_series(self):
         if self.store_series is None:
             return None
         return getUtility(ISnappyDistroSeriesSet).getByBothSeries(
-            self.store_series, self.distro_series)
+            self.store_series, self.distro_series
+        )
 
     @store_distro_series.setter
     def store_distro_series(self, value):
@@ -685,13 +694,20 @@ class Snap(Storm, WebhookTargetMixin):
         if self.pillar is None:
             return set(FREE_INFORMATION_TYPES)
         required_grant = BRANCH_POLICY_REQUIRED_GRANTS[
-            self.project.branch_sharing_policy]
-        if (required_grant is not None
-                and not getUtility(IService, 'sharing').checkPillarAccess(
-                    [self.project], required_grant, self.owner)
-                and (user is None
-                     or not getUtility(IService, 'sharing').checkPillarAccess(
-                            [self.project], required_grant, user))):
+            self.project.branch_sharing_policy
+        ]
+        if (
+            required_grant is not None
+            and not getUtility(IService, "sharing").checkPillarAccess(
+                [self.project], required_grant, self.owner
+            )
+            and (
+                user is None
+                or not getUtility(IService, "sharing").checkPillarAccess(
+                    [self.project], required_grant, user
+                )
+            )
+        ):
             return []
         return BRANCH_POLICY_ALLOWED_TYPES[self.project.branch_sharing_policy]
 
@@ -699,26 +715,33 @@ class Snap(Storm, WebhookTargetMixin):
     def extractSSOCaveats(macaroon):
         locations = [
             urlsplit(root).netloc
-            for root in CurrentOpenIDEndPoint.getAllRootURLs()]
+            for root in CurrentOpenIDEndPoint.getAllRootURLs()
+        ]
         return [
-            c for c in macaroon.third_party_caveats()
-            if c.location in locations]
+            c
+            for c in macaroon.third_party_caveats()
+            if c.location in locations
+        ]
 
     def beginAuthorization(self):
         """See `ISnap`."""
         if self.store_series is None:
             raise CannotAuthorizeStoreUploads(
                 "Cannot authorize uploads of a snap package with no store "
-                "series.")
+                "series."
+            )
         if self.store_name is None:
             raise CannotAuthorizeStoreUploads(
                 "Cannot authorize uploads of a snap package with no store "
-                "name.")
+                "name."
+            )
         snap_store_client = getUtility(ISnapStoreClient)
         root_macaroon_raw = snap_store_client.requestPackageUploadPermission(
-            self.store_series, self.store_name)
+            self.store_series, self.store_name
+        )
         sso_caveats = self.extractSSOCaveats(
-            Macaroon.deserialize(root_macaroon_raw))
+            Macaroon.deserialize(root_macaroon_raw)
+        )
         # We must have exactly one SSO caveat; more than one should never be
         # required and could be an attempt to substitute weaker caveats.  We
         # might as well OOPS here, even though the cause of this is probably
@@ -726,15 +749,18 @@ class Snap(Storm, WebhookTargetMixin):
         # and it should show up in our OOPS reports.
         if not sso_caveats:
             raise SnapAuthorizationBadGeneratedMacaroon(
-                "Macaroon has no SSO caveats")
+                "Macaroon has no SSO caveats"
+            )
         elif len(sso_caveats) > 1:
             raise SnapAuthorizationBadGeneratedMacaroon(
-                "Macaroon has multiple SSO caveats")
-        self.store_secrets = {'root': root_macaroon_raw}
+                "Macaroon has multiple SSO caveats"
+            )
+        self.store_secrets = {"root": root_macaroon_raw}
         return sso_caveats[0].caveat_id
 
-    def completeAuthorization(self, root_macaroon=None,
-                              discharge_macaroon=None):
+    def completeAuthorization(
+        self, root_macaroon=None, discharge_macaroon=None
+    ):
         """See `ISnap`."""
         if root_macaroon is not None:
             try:
@@ -746,7 +772,8 @@ class Snap(Storm, WebhookTargetMixin):
             if self.store_secrets is None or "root" not in self.store_secrets:
                 raise CannotAuthorizeStoreUploads(
                     "beginAuthorization must be called before "
-                    "completeAuthorization.")
+                    "completeAuthorization."
+                )
         if discharge_macaroon is not None:
             try:
                 Macaroon.deserialize(discharge_macaroon)
@@ -754,9 +781,11 @@ class Snap(Storm, WebhookTargetMixin):
                 raise BadMacaroon("discharge_macaroon is invalid.")
             container = getUtility(IEncryptedContainer, "snap-store-secrets")
             if container.can_encrypt:
-                self.store_secrets["discharge_encrypted"] = (
-                    removeSecurityProxy(container.encrypt(
-                        discharge_macaroon.encode("UTF-8"))))
+                self.store_secrets[
+                    "discharge_encrypted"
+                ] = removeSecurityProxy(
+                    container.encrypt(discharge_macaroon.encode("UTF-8"))
+                )
                 self.store_secrets.pop("discharge", None)
             else:
                 self.store_secrets["discharge"] = discharge_macaroon
@@ -767,17 +796,21 @@ class Snap(Storm, WebhookTargetMixin):
 
     @property
     def can_upload_to_store(self):
-        if (config.snappy.store_upload_url is None or
-                config.snappy.store_url is None or
-                self.store_series is None or
-                self.store_name is None or
-                self.store_secrets is None or
-                "root" not in self.store_secrets):
+        if (
+            config.snappy.store_upload_url is None
+            or config.snappy.store_url is None
+            or self.store_series is None
+            or self.store_name is None
+            or self.store_secrets is None
+            or "root" not in self.store_secrets
+        ):
             return False
         root_macaroon = Macaroon.deserialize(self.store_secrets["root"])
-        if (self.extractSSOCaveats(root_macaroon) and
-                "discharge" not in self.store_secrets and
-                "discharge_encrypted" not in self.store_secrets):
+        if (
+            self.extractSSOCaveats(root_macaroon)
+            and "discharge" not in self.store_secrets
+            and "discharge_encrypted" not in self.store_secrets
+        ):
             return False
         return True
 
@@ -785,8 +818,9 @@ class Snap(Storm, WebhookTargetMixin):
         """May `requester` request builds of this snap from `archive`?"""
         if not requester.inTeam(self.owner):
             raise SnapNotOwner(
-                "%s cannot create snap package builds owned by %s." %
-                (requester.displayname, self.owner.displayname))
+                "%s cannot create snap package builds owned by %s."
+                % (requester.displayname, self.owner.displayname)
+            )
         if not archive.enabled:
             raise ArchiveDisabled(archive.displayname)
         if archive.private and self.owner != archive.owner:
@@ -802,12 +836,13 @@ class Snap(Storm, WebhookTargetMixin):
         snap_base=None,
         channels=None,
         build_request=None,
-        target_architectures: t.Optional[t.List[str]] = None
+        target_architectures: t.Optional[t.List[str]] = None,
     ) -> ISnapBuild:
         """See `ISnap`."""
         self._checkRequestBuild(requester, archive)
         if not self._isArchitectureAllowed(
-                distro_arch_series, pocket, snap_base=snap_base):
+            distro_arch_series, pocket, snap_base=snap_base
+        ):
             raise SnapBuildDisallowedArchitecture(distro_arch_series, pocket)
 
         if target_architectures:
@@ -815,7 +850,8 @@ class Snap(Storm, WebhookTargetMixin):
 
         if not channels:
             channels_clause = Or(
-                SnapBuild.channels == None, SnapBuild.channels == {})
+                SnapBuild.channels == None, SnapBuild.channels == {}
+            )
         else:
             channels_clause = SnapBuild.channels == channels
         pending = IStore(self).find(
@@ -826,26 +862,39 @@ class Snap(Storm, WebhookTargetMixin):
             SnapBuild.pocket == pocket,
             SnapBuild.target_architectures == target_architectures,
             channels_clause,
-            SnapBuild.status == BuildStatus.NEEDSBUILD)
+            SnapBuild.status == BuildStatus.NEEDSBUILD,
+        )
         if pending.any() is not None:
             raise SnapBuildAlreadyPending
 
         build = getUtility(ISnapBuildSet).new(
-            requester, self, archive, distro_arch_series, pocket,
-            snap_base=snap_base, channels=channels,
+            requester,
+            self,
+            archive,
+            distro_arch_series,
+            pocket,
+            snap_base=snap_base,
+            channels=channels,
             build_request=build_request,
-            target_architectures=target_architectures)
+            target_architectures=target_architectures,
+        )
         build.queueBuild()
         notify(ObjectCreatedEvent(build, user=requester))
         return build
 
-    def requestBuilds(self, requester, archive, pocket, channels=None,
-                      architectures=None):
+    def requestBuilds(
+        self, requester, archive, pocket, channels=None, architectures=None
+    ):
         """See `ISnap`."""
         self._checkRequestBuild(requester, archive)
         job = getUtility(ISnapRequestBuildsJobSource).create(
-            self, requester, archive, pocket, channels,
-            architectures=architectures)
+            self,
+            requester,
+            archive,
+            pocket,
+            channels,
+            architectures=architectures,
+        )
         return self.getBuildRequest(job.job_id)
 
     @staticmethod
@@ -880,7 +929,8 @@ class Snap(Storm, WebhookTargetMixin):
         elif self.distro_series is None:
             # A base is mandatory if there's no configured distro series.
             raise NoSuchSnapBase(
-                snap_base_name if snap_base_name is not None else "<default>")
+                snap_base_name if snap_base_name is not None else "<default>"
+            )
         else:
             return self.distro_series
 
@@ -894,10 +944,18 @@ class Snap(Storm, WebhookTargetMixin):
         else:
             return channels
 
-    def requestBuildsFromJob(self, requester, archive, pocket,
-                             channels=None, architectures=None,
-                             allow_failures=False, fetch_snapcraft_yaml=True,
-                             build_request=None, logger=None):
+    def requestBuildsFromJob(
+        self,
+        requester,
+        archive,
+        pocket,
+        channels=None,
+        architectures=None,
+        allow_failures=False,
+        fetch_snapcraft_yaml=True,
+        build_request=None,
+        logger=None,
+    ):
         """See `ISnap`."""
         if not fetch_snapcraft_yaml and self.distro_series is None:
             # Slightly misleading, but requestAutoBuilds is the only place
@@ -907,7 +965,8 @@ class Snap(Storm, WebhookTargetMixin):
             if fetch_snapcraft_yaml:
                 try:
                     snapcraft_data = removeSecurityProxy(
-                        getUtility(ISnapSet).getSnapcraftYaml(self))
+                        getUtility(ISnapSet).getSnapcraftYaml(self)
+                    )
                 except CannotFetchSnapcraftYaml as e:
                     if not e.unsupported_remote:
                         raise
@@ -934,14 +993,21 @@ class Snap(Storm, WebhookTargetMixin):
             # the same order as in BinaryPackageBuildSet.createForSource, to
             # minimise confusion.
             supported_arches = OrderedDict(
-                (das.architecturetag, das) for das in sorted(
+                (das.architecturetag, das)
+                for das in sorted(
                     self.getAllowedArchitectures(
-                        distro_series, snap_base=snap_base),
-                    key=attrgetter("processor.id"))
-                if (architectures is None or
-                    das.architecturetag in architectures))
+                        distro_series, snap_base=snap_base
+                    ),
+                    key=attrgetter("processor.id"),
+                )
+                if (
+                    architectures is None
+                    or das.architecturetag in architectures
+                )
+            )
             architectures_to_build = determine_architectures_to_build(
-                snapcraft_data, list(supported_arches.keys()))
+                snapcraft_data, list(supported_arches.keys())
+            )
         except Exception as e:
             if not allow_failures:
                 raise
@@ -960,15 +1026,22 @@ class Snap(Storm, WebhookTargetMixin):
                 arch_channels = None
             try:
                 build = self.requestBuild(
-                    requester, archive, supported_arches[arch], pocket,
-                    snap_base=snap_base, channels=arch_channels,
+                    requester,
+                    archive,
+                    supported_arches[arch],
+                    pocket,
+                    snap_base=snap_base,
+                    channels=arch_channels,
                     build_request=build_request,
-                    target_architectures=build_instance.target_architectures
+                    target_architectures=build_instance.target_architectures,
                 )
                 if logger is not None:
                     logger.debug(
                         " - %s/%s/%s: Build requested.",
-                        self.owner.name, self.name, arch)
+                        self.owner.name,
+                        self.name,
+                        arch,
+                    )
                 builds.append(build)
             except SnapBuildAlreadyPending:
                 pass
@@ -977,12 +1050,13 @@ class Snap(Storm, WebhookTargetMixin):
                     raise
                 elif logger is not None:
                     logger.exception(
-                        " - %s/%s/%s: %s",
-                        self.owner.name, self.name, arch, e)
+                        " - %s/%s/%s: %s", self.owner.name, self.name, arch, e
+                    )
         return builds
 
-    def requestAutoBuilds(self, allow_failures=False,
-                          fetch_snapcraft_yaml=False, logger=None):
+    def requestAutoBuilds(
+        self, allow_failures=False, fetch_snapcraft_yaml=False, logger=None
+    ):
         """See `ISnap`."""
         if self.auto_build_archive is None:
             raise CannotRequestAutoBuilds("auto_build_archive")
@@ -992,16 +1066,24 @@ class Snap(Storm, WebhookTargetMixin):
             raise IncompatibleArguments(
                 "Cannot use requestAutoBuilds for a snap package without "
                 "inferring from snapcraft.yaml or distro_series being set.  "
-                "Consider using requestBuilds instead.")
+                "Consider using requestBuilds instead."
+            )
         self.is_stale = False
         if logger is not None:
             logger.debug(
                 "Scheduling builds of snap package %s/%s",
-                self.owner.name, self.name)
+                self.owner.name,
+                self.name,
+            )
         return self.requestBuildsFromJob(
-            self.owner, self.auto_build_archive, self.auto_build_pocket,
-            channels=self.auto_build_channels, allow_failures=allow_failures,
-            fetch_snapcraft_yaml=fetch_snapcraft_yaml, logger=logger)
+            self.owner,
+            self.auto_build_archive,
+            self.auto_build_pocket,
+            channels=self.auto_build_channels,
+            allow_failures=allow_failures,
+            fetch_snapcraft_yaml=fetch_snapcraft_yaml,
+            logger=logger,
+        )
 
     def getBuildRequest(self, job_id):
         """See `ISnap`."""
@@ -1013,9 +1095,11 @@ class Snap(Storm, WebhookTargetMixin):
         job_source = getUtility(ISnapRequestBuildsJobSource)
         # The returned jobs are ordered by descending ID.
         jobs = job_source.findBySnap(
-            self, statuses=(JobStatus.WAITING, JobStatus.RUNNING))
+            self, statuses=(JobStatus.WAITING, JobStatus.RUNNING)
+        )
         return DecoratedResultSet(
-            jobs, result_decorator=SnapBuildRequest.fromJob)
+            jobs, result_decorator=SnapBuildRequest.fromJob
+        )
 
     @property
     def failed_build_requests(self):
@@ -1024,7 +1108,8 @@ class Snap(Storm, WebhookTargetMixin):
         # The returned jobs are ordered by descending ID.
         jobs = job_source.findBySnap(self, statuses=(JobStatus.FAILED,))
         return DecoratedResultSet(
-            jobs, result_decorator=SnapBuildRequest.fromJob)
+            jobs, result_decorator=SnapBuildRequest.fromJob
+        )
 
     def _getBuilds(self, filter_term, order_by):
         """The actual query to get the builds."""
@@ -1033,9 +1118,11 @@ class Snap(Storm, WebhookTargetMixin):
             SnapBuild.archive_id == Archive.id,
             Archive._enabled,
             get_enabled_archive_filter(
-                getUtility(ILaunchBag).user, include_public=True,
-                include_subscribed=True)
-            ]
+                getUtility(ILaunchBag).user,
+                include_public=True,
+                include_subscribed=True,
+            ),
+        ]
         if filter_term is not None:
             query_args.append(filter_term)
         result = Store.of(self).find(SnapBuild, *query_args)
@@ -1044,7 +1131,7 @@ class Snap(Storm, WebhookTargetMixin):
         def eager_load(rows):
             getUtility(ISnapBuildSet).preloadBuildsData(rows)
             getUtility(IBuildQueueSet).preloadForBuildFarmJobs(rows)
-            load_related(Builder, rows, ['builder_id'])
+            load_related(Builder, rows, ["builder_id"])
 
         return DecoratedResultSet(result, pre_iter_hook=eager_load)
 
@@ -1083,7 +1170,7 @@ class Snap(Storm, WebhookTargetMixin):
                 "when_complete_estimate": build.estimate,
                 "build_log_url": build.log_url,
                 "build_log_size": build_log_size,
-                }
+            }
         return result
 
     def getBuildSummaries(self, request_ids=None, build_ids=None, user=None):
@@ -1101,7 +1188,8 @@ class Snap(Storm, WebhookTargetMixin):
                 # go through Snap._getBuilds which checks visibility.  This
                 # saves an Archive query per build in the security adapter.
                 all_build_ids.extend(
-                    [removeSecurityProxy(build).id for build in builds])
+                    [removeSecurityProxy(build).id for build in builds]
+                )
         else:
             requests = []
 
@@ -1109,66 +1197,77 @@ class Snap(Storm, WebhookTargetMixin):
             all_build_ids.extend(build_ids)
 
         all_build_summaries = self.getBuildSummariesForSnapBuildIds(
-            all_build_ids)
+            all_build_ids
+        )
 
         for request in requests:
             build_summaries = []
             for build in sorted(
-                    builds_by_request[request.id], key=attrgetter("id"),
-                    reverse=True):
+                builds_by_request[request.id],
+                key=attrgetter("id"),
+                reverse=True,
+            ):
                 if build.id in all_build_summaries:
                     # Include enough information for
                     # snap.update_build_statuses.js to populate new build
                     # rows.
                     build_summary = {
                         "self_link": canonical_url(
-                            build, path_only_if_possible=True),
+                            build, path_only_if_possible=True
+                        ),
                         "id": build.id,
                         "distro_arch_series_link": canonical_url(
                             build.distro_arch_series,
-                            path_only_if_possible=True),
+                            path_only_if_possible=True,
+                        ),
                         "architecture_tag": (
-                            build.distro_arch_series.architecturetag),
+                            build.distro_arch_series.architecturetag
+                        ),
                         "archive_link": ArchiveFormatterAPI(
-                            build.archive).link(None),
-                        }
+                            build.archive
+                        ).link(None),
+                    }
                     build_summary.update(all_build_summaries[build.id])
                     build_summaries.append(build_summary)
             result["requests"][request.id] = {
                 "status": request.status.name,
                 "error_message": request.error_message,
                 "builds": build_summaries,
-                }
+            }
 
-        for build_id in (build_ids or []):
+        for build_id in build_ids or []:
             if build_id in all_build_summaries:
                 result["builds"][build_id] = all_build_summaries[build_id]
 
         return result
 
     def getBuildByStoreRevision(self, store_upload_revision, user=None):
-        build = Store.of(self).find(
-            SnapBuild,
-            SnapBuild.snap == self,
-            SnapBuild._store_upload_revision == store_upload_revision,
-            SnapBuild.archive == Archive.id,
-            Archive._enabled,
-            get_enabled_archive_filter(
-                user,
-                include_public=True,
-                include_subscribed=True)
-        ).one()
+        build = (
+            Store.of(self)
+            .find(
+                SnapBuild,
+                SnapBuild.snap == self,
+                SnapBuild._store_upload_revision == store_upload_revision,
+                SnapBuild.archive == Archive.id,
+                Archive._enabled,
+                get_enabled_archive_filter(
+                    user, include_public=True, include_subscribed=True
+                ),
+            )
+            .one()
+        )
         return build
 
     @property
     def builds(self):
         """See `ISnap`."""
         order_by = (
-            NullsLast(Desc(Greatest(
-                SnapBuild.date_started,
-                SnapBuild.date_finished))),
+            NullsLast(
+                Desc(Greatest(SnapBuild.date_started, SnapBuild.date_finished))
+            ),
             Desc(SnapBuild.date_created),
-            Desc(SnapBuild.id))
+            Desc(SnapBuild.id),
+        )
         return self._getBuilds(None, order_by)
 
     @property
@@ -1179,23 +1278,24 @@ class Snap(Storm, WebhookTargetMixin):
             BuildStatus.BUILDING,
             BuildStatus.UPLOADING,
             BuildStatus.CANCELLING,
-            ]
+        ]
 
     @property
     def completed_builds(self):
         """See `ISnap`."""
-        filter_term = (Not(SnapBuild.status.is_in(self._pending_states)))
+        filter_term = Not(SnapBuild.status.is_in(self._pending_states))
         order_by = (
-            NullsLast(Desc(Greatest(
-                SnapBuild.date_started,
-                SnapBuild.date_finished))),
-            Desc(SnapBuild.id))
+            NullsLast(
+                Desc(Greatest(SnapBuild.date_started, SnapBuild.date_finished))
+            ),
+            Desc(SnapBuild.id),
+        )
         return self._getBuilds(filter_term, order_by)
 
     @property
     def pending_builds(self):
         """See `ISnap`."""
-        filter_term = (SnapBuild.status.is_in(self._pending_states))
+        filter_term = SnapBuild.status.is_in(self._pending_states)
         # We want to order by date_created but this is the same as ordering
         # by id (since id increases monotonically) and is less expensive.
         order_by = Desc(SnapBuild.id)
@@ -1204,14 +1304,16 @@ class Snap(Storm, WebhookTargetMixin):
     @property
     def subscriptions(self):
         return Store.of(self).find(
-            SnapSubscription, SnapSubscription.snap == self)
+            SnapSubscription, SnapSubscription.snap == self
+        )
 
     @property
     def subscribers(self):
         return Store.of(self).find(
             Person,
             SnapSubscription.person_id == Person.id,
-            SnapSubscription.snap == self)
+            SnapSubscription.snap == self,
+        )
 
     def visibleByUser(self, user):
         """See `ISnap`."""
@@ -1221,9 +1323,8 @@ class Snap(Storm, WebhookTargetMixin):
             return False
         store = IStore(self)
         return not store.find(
-            Snap,
-            Snap.id == self.id,
-            get_snap_privacy_filter(user)).is_empty()
+            Snap, Snap.id == self.id, get_snap_privacy_filter(user)
+        ).is_empty()
 
     def hasSubscription(self, person):
         """See `ISnap`."""
@@ -1235,51 +1336,64 @@ class Snap(Storm, WebhookTargetMixin):
         """
         if person is None:
             return None
-        return Store.of(self).find(
-            SnapSubscription,
-            SnapSubscription.person == person,
-            SnapSubscription.snap == self).one()
+        return (
+            Store.of(self)
+            .find(
+                SnapSubscription,
+                SnapSubscription.person == person,
+                SnapSubscription.snap == self,
+            )
+            .one()
+        )
 
     def userCanBeSubscribed(self, person):
         """Checks if the given person can subscribe to this snap recipe."""
         return not (
-            self.private and
-            person.is_team and
-            person.anyone_can_join())
+            self.private and person.is_team and person.anyone_can_join()
+        )
 
     def subscribe(self, person, subscribed_by, ignore_permissions=False):
         """See `ISnap`."""
         if not self.userCanBeSubscribed(person):
             raise SubscriptionPrivacyViolation(
                 "Open and delegated teams cannot be subscribed to private "
-                "snap recipes.")
+                "snap recipes."
+            )
         subscription = self.getSubscription(person)
         if subscription is None:
             subscription = SnapSubscription(
-                person=person, snap=self, subscribed_by=subscribed_by)
+                person=person, snap=self, subscribed_by=subscribed_by
+            )
             Store.of(subscription).flush()
         service = getUtility(IService, "sharing")
         snaps = service.getVisibleArtifacts(
-            person, snaps=[self], ignore_permissions=True)["snaps"]
+            person, snaps=[self], ignore_permissions=True
+        )["snaps"]
         if not snaps:
             service.ensureAccessGrants(
-                [person], subscribed_by, snaps=[self],
-                ignore_permissions=ignore_permissions)
+                [person],
+                subscribed_by,
+                snaps=[self],
+                ignore_permissions=ignore_permissions,
+            )
 
     def unsubscribe(self, person, unsubscribed_by, ignore_permissions=False):
         """See `ISnap`."""
         subscription = self.getSubscription(person)
         if subscription is None:
             return
-        if (not ignore_permissions
-                and not subscription.canBeUnsubscribedByUser(unsubscribed_by)):
+        if (
+            not ignore_permissions
+            and not subscription.canBeUnsubscribedByUser(unsubscribed_by)
+        ):
             raise UserCannotUnsubscribePerson(
-                '%s does not have permission to unsubscribe %s.' % (
-                    unsubscribed_by.displayname,
-                    person.displayname))
+                "%s does not have permission to unsubscribe %s."
+                % (unsubscribed_by.displayname, person.displayname)
+            )
         artifact = getUtility(IAccessArtifactSource).find([self])
         getUtility(IAccessArtifactGrantSource).revokeByArtifact(
-            artifact, [person])
+            artifact, [person]
+        )
         store = Store.of(subscription)
         store.remove(subscription)
         IStore(self).flush()
@@ -1305,7 +1419,8 @@ class Snap(Storm, WebhookTargetMixin):
 
     def _deleteSnapSubscriptions(self):
         subscriptions = Store.of(self).find(
-            SnapSubscription, SnapSubscription.snap == self)
+            SnapSubscription, SnapSubscription.snap == self
+        )
         subscriptions.remove()
 
     def destroySelf(self):
@@ -1318,92 +1433,135 @@ class Snap(Storm, WebhookTargetMixin):
         buildqueue_records = store.find(
             BuildQueue,
             BuildQueue._build_farm_job_id == SnapBuild.build_farm_job_id,
-            SnapBuild.snap == self)
+            SnapBuild.snap == self,
+        )
         for buildqueue_record in buildqueue_records:
             buildqueue_record.destroySelf()
-        build_farm_job_ids = list(store.find(
-            SnapBuild.build_farm_job_id, SnapBuild.snap == self))
+        build_farm_job_ids = list(
+            store.find(SnapBuild.build_farm_job_id, SnapBuild.snap == self)
+        )
         # XXX cjwatson 2016-02-27 bug=322972: Requires manual SQL due to
         # lack of support for DELETE FROM ... USING ... in Storm.
-        store.execute("""
+        store.execute(
+            """
             DELETE FROM SnapFile
             USING SnapBuild
             WHERE
                 SnapFile.snapbuild = SnapBuild.id AND
                 SnapBuild.snap = ?
-            """, (self.id,))
-        store.execute("""
+            """,
+            (self.id,),
+        )
+        store.execute(
+            """
             DELETE FROM SnapBuildJob
             USING SnapBuild
             WHERE
                 SnapBuildJob.snapbuild = SnapBuild.id AND
                 SnapBuild.snap = ?
-            """, (self.id,))
+            """,
+            (self.id,),
+        )
         store.find(SnapBuild, SnapBuild.snap == self).remove()
         affected_jobs = Select(
-            [SnapJob.job_id], And(SnapJob.job == Job.id, SnapJob.snap == self))
+            [SnapJob.job_id], And(SnapJob.job == Job.id, SnapJob.snap == self)
+        )
         store.find(Job, Job.id.is_in(affected_jobs)).remove()
         getUtility(IWebhookSet).delete(self.webhooks)
         self._deleteAccessGrants()
         self._deleteSnapSubscriptions()
         store.remove(self)
         store.find(
-            BuildFarmJob, BuildFarmJob.id.is_in(build_farm_job_ids)).remove()
+            BuildFarmJob, BuildFarmJob.id.is_in(build_farm_job_ids)
+        ).remove()
 
 
 class SnapArch(Storm):
     """Link table to back `Snap.processors`."""
 
-    __storm_table__ = 'SnapArch'
-    __storm_primary__ = ('snap_id', 'processor_id')
+    __storm_table__ = "SnapArch"
+    __storm_primary__ = ("snap_id", "processor_id")
 
-    snap_id = Int(name='snap', allow_none=False)
-    snap = Reference(snap_id, 'Snap.id')
+    snap_id = Int(name="snap", allow_none=False)
+    snap = Reference(snap_id, "Snap.id")
 
-    processor_id = Int(name='processor', allow_none=False)
-    processor = Reference(processor_id, 'Processor.id')
+    processor_id = Int(name="processor", allow_none=False)
+    processor = Reference(processor_id, "Processor.id")
 
 
 @implementer(ISnapSet)
 class SnapSet:
     """See `ISnapSet`."""
 
-    def new(self, registrant, owner, distro_series, name, description=None,
-            branch=None, git_repository=None, git_repository_url=None,
-            git_path=None, git_ref=None, auto_build=False,
-            auto_build_archive=None, auto_build_pocket=None,
-            auto_build_channels=None, require_virtualized=True,
-            processors=None, date_created=DEFAULT,
-            information_type=InformationType.PUBLIC, allow_internet=True,
-            build_source_tarball=False, store_upload=False,
-            store_series=None, store_name=None, store_secrets=None,
-            store_channels=None, project=None):
+    def new(
+        self,
+        registrant,
+        owner,
+        distro_series,
+        name,
+        description=None,
+        branch=None,
+        git_repository=None,
+        git_repository_url=None,
+        git_path=None,
+        git_ref=None,
+        auto_build=False,
+        auto_build_archive=None,
+        auto_build_pocket=None,
+        auto_build_channels=None,
+        require_virtualized=True,
+        processors=None,
+        date_created=DEFAULT,
+        information_type=InformationType.PUBLIC,
+        allow_internet=True,
+        build_source_tarball=False,
+        store_upload=False,
+        store_series=None,
+        store_name=None,
+        store_secrets=None,
+        store_channels=None,
+        project=None,
+    ):
         """See `ISnapSet`."""
         if not registrant.inTeam(owner):
             if owner.is_team:
                 raise SnapNotOwner(
-                    "%s is not a member of %s." %
-                    (registrant.displayname, owner.displayname))
+                    "%s is not a member of %s."
+                    % (registrant.displayname, owner.displayname)
+                )
             else:
                 raise SnapNotOwner(
-                    "%s cannot create snap packages owned by %s." %
-                    (registrant.displayname, owner.displayname))
+                    "%s cannot create snap packages owned by %s."
+                    % (registrant.displayname, owner.displayname)
+                )
 
-        if sum([git_repository is not None, git_repository_url is not None,
-                git_ref is not None]) > 1:
+        if (
+            sum(
+                [
+                    git_repository is not None,
+                    git_repository_url is not None,
+                    git_ref is not None,
+                ]
+            )
+            > 1
+        ):
             raise IncompatibleArguments(
                 "You cannot specify more than one of 'git_repository', "
-                "'git_repository_url', and 'git_ref'.")
-        if ((git_repository is None and git_repository_url is None) !=
-                (git_path is None)):
+                "'git_repository_url', and 'git_ref'."
+            )
+        if (git_repository is None and git_repository_url is None) != (
+            git_path is None
+        ):
             raise IncompatibleArguments(
                 "You must specify both or neither of "
-                "'git_repository'/'git_repository_url' and 'git_path'.")
+                "'git_repository'/'git_repository_url' and 'git_path'."
+            )
         if git_repository is not None:
             git_ref = git_repository.getRefByPath(git_path)
         elif git_repository_url is not None:
             git_ref = getUtility(IGitRefRemoteSet).new(
-                git_repository_url, git_path)
+                git_repository_url, git_path
+            )
         if branch is None and git_ref is None:
             raise NoSourceForSnap
         if self.exists(owner, name):
@@ -1415,22 +1573,35 @@ class SnapSet:
         # creation and to ensure that everything relevant is in the Storm
         # cache.
         if not self.isValidInformationType(
-                information_type, owner, branch, git_ref):
+            information_type, owner, branch, git_ref
+        ):
             raise SnapPrivacyMismatch
 
         store = IMasterStore(Snap)
         snap = Snap(
-            registrant, owner, distro_series, name, description=description,
-            branch=branch, git_ref=git_ref, auto_build=auto_build,
+            registrant,
+            owner,
+            distro_series,
+            name,
+            description=description,
+            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,
-            require_virtualized=require_virtualized, date_created=date_created,
-            information_type=information_type, allow_internet=allow_internet,
+            require_virtualized=require_virtualized,
+            date_created=date_created,
+            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,
+        )
         store.add(snap)
         snap._reconcileAccess()
 
@@ -1439,7 +1610,8 @@ class SnapSet:
 
         if processors is None:
             processors = [
-                p for p in snap.available_processors if p.build_by_default]
+                p for p in snap.available_processors if p.build_by_default
+            ]
         snap.setProcessors(processors)
 
         return snap
@@ -1448,8 +1620,9 @@ class SnapSet:
         """See `ISnapSet`."""
         return BRANCH_POLICY_ALLOWED_TYPES[project.branch_sharing_policy]
 
-    def isValidInformationType(self, information_type, owner, branch=None,
-                               git_ref=None):
+    def isValidInformationType(
+        self, information_type, owner, branch=None, git_ref=None
+    ):
         private = information_type not in PUBLIC_INFORMATION_TYPES
         if private:
             # If appropriately enabled via feature flag.
@@ -1469,8 +1642,11 @@ class SnapSet:
         return True
 
     def _getByName(self, owner, name):
-        return IStore(Snap).find(
-            Snap, Snap.owner == owner, Snap.name == name).one()
+        return (
+            IStore(Snap)
+            .find(Snap, Snap.owner == owner, Snap.name == name)
+            .one()
+        )
 
     def exists(self, owner, name):
         """See `ISnapSet`."""
@@ -1495,8 +1671,9 @@ class SnapSet:
             raise NotImplementedError("Unknown pillar for snap: %s" % pillar)
         return IStore(Snap).find(Snap, *conditions).one()
 
-    def _getSnapsFromCollection(self, collection, owner=None,
-                                visible_by_user=None):
+    def _getSnapsFromCollection(
+        self, collection, owner=None, visible_by_user=None
+    ):
         if IBranchCollection.providedBy(collection):
             id_column = Snap.branch_id
             ids = collection.getBranchIds()
@@ -1522,12 +1699,15 @@ class SnapSet:
 
     def findByPerson(self, person, visible_by_user=None):
         """See `ISnapSet`."""
+
         def _getSnaps(collection):
             collection = collection.visibleByUser(visible_by_user)
             owned = self._getSnapsFromCollection(
-                collection.ownedBy(person), visible_by_user=visible_by_user)
+                collection.ownedBy(person), visible_by_user=visible_by_user
+            )
             packaged = self._getSnapsFromCollection(
-                collection, owner=person, visible_by_user=visible_by_user)
+                collection, owner=person, visible_by_user=visible_by_user
+            )
             return owned.union(packaged)
 
         bzr_collection = removeSecurityProxy(getUtility(IAllBranches))
@@ -1535,37 +1715,46 @@ class SnapSet:
         git_collection = removeSecurityProxy(getUtility(IAllGitRepositories))
         git_snaps = _getSnaps(git_collection)
         git_url_snaps = IStore(Snap).find(
-            Snap, Snap.owner == person, Snap.git_repository_url != None)
+            Snap, Snap.owner == person, Snap.git_repository_url != None
+        )
         return bzr_snaps.union(git_snaps).union(git_url_snaps)
 
     def findByProject(self, project, visible_by_user=None):
         """See `ISnapSet`."""
+
         def _getSnaps(collection):
             return self._getSnapsFromCollection(
                 collection.visibleByUser(visible_by_user),
-                visible_by_user=visible_by_user)
+                visible_by_user=visible_by_user,
+            )
 
         snaps_for_project = IStore(Snap).find(
             Snap,
             Snap.project == project,
-            get_snap_privacy_filter(visible_by_user))
+            get_snap_privacy_filter(visible_by_user),
+        )
         bzr_collection = removeSecurityProxy(IBranchCollection(project))
         git_collection = removeSecurityProxy(IGitCollection(project))
-        return snaps_for_project.union(
-            _getSnaps(bzr_collection)).union(_getSnaps(git_collection))
+        return snaps_for_project.union(_getSnaps(bzr_collection)).union(
+            _getSnaps(git_collection)
+        )
 
-    def findByBranch(self, branch, visible_by_user=None,
-                     check_permissions=True):
+    def findByBranch(
+        self, branch, visible_by_user=None, check_permissions=True
+    ):
         """See `ISnapSet`."""
         clauses = [Snap.branch == branch]
         if check_permissions:
             clauses.append(get_snap_privacy_filter(visible_by_user))
-        return IStore(Snap).find(
-            Snap,
-            *clauses)
+        return IStore(Snap).find(Snap, *clauses)
 
-    def findByGitRepository(self, repository, paths=None,
-                            visible_by_user=None, check_permissions=True):
+    def findByGitRepository(
+        self,
+        repository,
+        paths=None,
+        visible_by_user=None,
+        check_permissions=True,
+    ):
         """See `ISnapSet`."""
         clauses = [Snap.git_repository == repository]
         if paths is not None:
@@ -1578,20 +1767,24 @@ class SnapSet:
         """See `ISnapSet`."""
         return IStore(Snap).find(
             Snap,
-            Snap.git_repository == ref.repository, Snap.git_path == ref.path,
-            get_snap_privacy_filter(visible_by_user))
+