reformat codebase with black

Bug: 343563953
Test: unittests
Change-Id: I709596674717d56cb5d7f8ba0904a23117e97e82
Reviewed-on: https://gerrit-review.googlesource.com/c/git-repohooks/+/467921
Tested-by: Mike Frysinger <vapier@google.com>
Commit-Queue: Mike Frysinger <vapier@google.com>
Reviewed-by: Raul Rangel <rrangel@google.com>
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 4cedfd1..85b9d65 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -13,6 +13,7 @@
 
 [Builtin Hooks]
 aosp_license = true
+black = true
 commit_msg_bug_field = true
 commit_msg_changeid_field = true
 commit_msg_test_field = true
diff --git a/pre-upload.py b/pre-upload.py
index c852c29..a51707c 100755
--- a/pre-upload.py
+++ b/pre-upload.py
@@ -31,7 +31,7 @@
 # Assert some minimum Python versions as we don't test or support any others.
 # See README.md for what version we may require.
 if sys.version_info < (3, 6):
-    print('repohooks: error: Python-3.6+ is required', file=sys.stderr)
+    print("repohooks: error: Python-3.6+ is required", file=sys.stderr)
     sys.exit(1)
 
 
@@ -53,19 +53,19 @@
 
 
 # Repohooks homepage.
-REPOHOOKS_URL = 'https://android.googlesource.com/platform/tools/repohooks/'
+REPOHOOKS_URL = "https://android.googlesource.com/platform/tools/repohooks/"
 
 
 class Output(object):
     """Class for reporting hook status."""
 
     COLOR = rh.terminal.Color()
-    COMMIT = COLOR.color(COLOR.CYAN, 'COMMIT')
-    RUNNING = COLOR.color(COLOR.YELLOW, 'RUNNING')
-    PASSED = COLOR.color(COLOR.GREEN, 'PASSED')
-    FAILED = COLOR.color(COLOR.RED, 'FAILED')
-    WARNING = COLOR.color(COLOR.YELLOW, 'WARNING')
-    FIXUP = COLOR.color(COLOR.MAGENTA, 'FIXUP')
+    COMMIT = COLOR.color(COLOR.CYAN, "COMMIT")
+    RUNNING = COLOR.color(COLOR.YELLOW, "RUNNING")
+    PASSED = COLOR.color(COLOR.GREEN, "PASSED")
+    FAILED = COLOR.color(COLOR.RED, "FAILED")
+    WARNING = COLOR.color(COLOR.YELLOW, "WARNING")
+    FIXUP = COLOR.color(COLOR.MAGENTA, "FIXUP")
 
     # How long a hook is allowed to run before we warn that it is "too slow".
     _SLOW_HOOK_DURATION = datetime.timedelta(seconds=30)
@@ -85,7 +85,7 @@
         self.start_time = datetime.datetime.now()
         self.hook_start_time = None
         # Cache number of invisible characters in our banner.
-        self._banner_esc_chars = len(self.COLOR.color(self.COLOR.YELLOW, ''))
+        self._banner_esc_chars = len(self.COLOR.color(self.COLOR.YELLOW, ""))
 
     def set_num_commits(self, num_commits: int) -> None:
         """Keep track of how many commits we'll be running.
@@ -105,9 +105,9 @@
           commit_summary: commit summary.
         """
         status_line = (
-            f'[{self.COMMIT} '
-            f'{self.commit_index}/{self.num_commits} '
-            f'{commit[0:12]}] {commit_summary}'
+            f"[{self.COMMIT} "
+            f"{self.commit_index}/{self.num_commits} "
+            f"{commit[0:12]}] {commit_summary}"
         )
         rh.terminal.print_status_line(status_line, print_newline=True)
         self.commit_index += 1
@@ -119,15 +119,15 @@
 
     def hook_banner(self):
         """Display the banner for current set of hooks."""
-        pending = ', '.join(x.name for x in self.hooks)
+        pending = ", ".join(x.name for x in self.hooks)
         status_line = (
-            f'[{self.RUNNING} '
-            f'{self.num_hooks - len(self.hooks)}/{self.num_hooks}] '
-            f'{pending}'
+            f"[{self.RUNNING} "
+            f"{self.num_hooks - len(self.hooks)}/{self.num_hooks}] "
+            f"{pending}"
         )
         if self._banner_esc_chars and sys.stderr.isatty():
             cols = os.get_terminal_size(sys.stderr.fileno()).columns
-            status_line = status_line[0:cols + self._banner_esc_chars]
+            status_line = status_line[0 : cols + self._banner_esc_chars]
         rh.terminal.print_status_line(status_line)
 
     def hook_finish(self, hook, duration):
@@ -137,9 +137,10 @@
             d = rh.utils.timedelta_str(duration)
             self.hook_warning(
                 hook,
-                f'This hook took {d} to finish which is fairly slow for '
-                'developers.\nPlease consider moving the check to the '
-                'server/CI system instead.')
+                f"This hook took {d} to finish which is fairly slow for "
+                "developers.\nPlease consider moving the check to the "
+                "server/CI system instead.",
+            )
 
         # Show any hooks still pending.
         if self.hooks:
@@ -152,7 +153,7 @@
           hook: The hook that generated the output.
           error: error string.
         """
-        self.error(f'{hook.name} hook', error)
+        self.error(f"{hook.name} hook", error)
 
     def hook_warning(self, hook, warning):
         """Print a warning for a single hook.
@@ -161,7 +162,7 @@
           hook: The hook that generated the output.
           warning: warning string.
         """
-        status_line = f'[{self.WARNING}] {hook.name}'
+        status_line = f"[{self.WARNING}] {hook.name}"
         rh.terminal.print_status_line(status_line, print_newline=True)
         print(warning, file=sys.stderr)
 
@@ -172,7 +173,7 @@
           header: A unique identifier for the source of this error.
           error: error string.
         """
-        status_line = f'[{self.FAILED}] {header}'
+        status_line = f"[{self.FAILED}] {header}"
         rh.terminal.print_status_line(status_line, print_newline=True)
         print(error, file=sys.stderr)
         self.success = False
@@ -186,20 +187,21 @@
         for result in (x for x in hook_results if x.fixup_cmd):
             cmd = result.fixup_cmd + list(result.files)
             for line in (
-                f'[{self.FIXUP}] {result.hook} has automated fixups available',
-                f'  cd {rh.shell.quote(project_results.workdir)} && \\',
-                f'    {rh.shell.cmd_to_str(cmd)}',
+                f"[{self.FIXUP}] {result.hook} has automated fixups available",
+                f"  cd {rh.shell.quote(project_results.workdir)} && \\",
+                f"    {rh.shell.cmd_to_str(cmd)}",
             ):
                 rh.terminal.print_status_line(line, print_newline=True)
 
     def finish(self):
         """Print summary for all the hooks."""
         header = self.PASSED if self.success else self.FAILED
-        status = 'passed' if self.success else 'failed'
+        status = "passed" if self.success else "failed"
         d = rh.utils.timedelta_str(datetime.datetime.now() - self.start_time)
         rh.terminal.print_status_line(
-            f'[{header}] repohooks for {self.project_name} {status} in {d}',
-            print_newline=True)
+            f"[{header}] repohooks for {self.project_name} {status} in {d}",
+            print_newline=True,
+        )
 
 
 def _process_hook_results(results):
@@ -220,15 +222,15 @@
     has_error = False
     has_warning = False
 
-    error_ret = ''
-    warning_ret = ''
+    error_ret = ""
+    warning_ret = ""
     for result in results:
         if result or result.is_warning():
-            ret = ''
+            ret = ""
             if result.files:
-                ret += f'  FILES: {rh.shell.cmd_to_str(result.files)}\n'
+                ret += f"  FILES: {rh.shell.cmd_to_str(result.files)}\n"
             lines = result.error.splitlines()
-            ret += '\n'.join(f'    {x}' for x in lines)
+            ret += "\n".join(f"    {x}" for x in lines)
             if result.is_warning():
                 has_warning = True
                 warning_ret += ret
@@ -236,8 +238,10 @@
                 has_error = True
                 error_ret += ret
 
-    return (error_ret if has_error else None,
-            warning_ret if has_warning else None)
+    return (
+        error_ret if has_error else None,
+        warning_ret if has_warning else None,
+    )
 
 
 def _get_project_config(from_git=False):
@@ -253,14 +257,14 @@
     else:
         global_paths = (
             # Load the global config found in the manifest repo.
-            (os.path.join(rh.git.find_repo_root(), '.repo', 'manifests')),
+            (os.path.join(rh.git.find_repo_root(), ".repo", "manifests")),
             # Load the global config found in the root of the repo checkout.
             rh.git.find_repo_root(),
         )
 
     paths = (
         # Load the config for this git repo.
-        '.',
+        ".",
     )
     return rh.config.PreUploadSettings(paths=paths, global_paths=global_paths)
 
@@ -270,65 +274,75 @@
     # Filter out any result that has a fixup.
     fixups = []
     for project_results in projects_results:
-        fixups.extend((project_results.workdir, x)
-                      for x in project_results.fixups)
+        fixups.extend(
+            (project_results.workdir, x) for x in project_results.fixups
+        )
     if not fixups:
         return
 
     if len(fixups) > 1:
-        banner = f'Multiple fixups ({len(fixups)}) are available.'
+        banner = f"Multiple fixups ({len(fixups)}) are available."
     else:
-        banner = 'Automated fixups are available.'
+        banner = "Automated fixups are available."
     print(Output.COLOR.color(Output.COLOR.MAGENTA, banner), file=sys.stderr)
 
     # If there's more than one fixup available, ask if they want to blindly run
     # them all, or prompt for them one-by-one.
-    mode = 'some'
+    mode = "some"
     if len(fixups) > 1:
         while True:
             response = rh.terminal.str_prompt(
-                'What would you like to do',
-                ('Run (A)ll', 'Run (S)ome', '(D)ry-run', '(N)othing [default]'))
+                "What would you like to do",
+                ("Run (A)ll", "Run (S)ome", "(D)ry-run", "(N)othing [default]"),
+            )
             if not response:
-                print('', file=sys.stderr)
+                print("", file=sys.stderr)
                 return
-            if response.startswith('a') or response.startswith('y'):
-                mode = 'all'
+            if response.startswith("a") or response.startswith("y"):
+                mode = "all"
                 break
-            elif response.startswith('s'):
-                mode = 'some'
+            elif response.startswith("s"):
+                mode = "some"
                 break
-            elif response.startswith('d'):
-                mode = 'dry-run'
+            elif response.startswith("d"):
+                mode = "dry-run"
                 break
-            elif response.startswith('n'):
-                print('', file=sys.stderr)
+            elif response.startswith("n"):
+                print("", file=sys.stderr)
                 return
 
     # Walk all the fixups and run them one-by-one.
     for workdir, result in fixups:
-        if mode == 'some':
+        if mode == "some":
             if not rh.terminal.boolean_prompt(
-                f'Run {result.hook} fixup for {result.commit}'
+                f"Run {result.hook} fixup for {result.commit}"
             ):
                 continue
 
         cmd = tuple(result.fixup_cmd) + tuple(result.files)
         print(
-            f'\n[{Output.RUNNING}] cd {rh.shell.quote(workdir)} && '
-            f'{rh.shell.cmd_to_str(cmd)}', file=sys.stderr)
-        if mode == 'dry-run':
+            f"\n[{Output.RUNNING}] cd {rh.shell.quote(workdir)} && "
+            f"{rh.shell.cmd_to_str(cmd)}",
+            file=sys.stderr,
+        )
+        if mode == "dry-run":
             continue
 
         cmd_result = rh.utils.run(cmd, cwd=workdir, check=False)
         if cmd_result.returncode:
-            print(f'[{Output.WARNING}] command exited {cmd_result.returncode}',
-                  file=sys.stderr)
+            print(
+                f"[{Output.WARNING}] command exited {cmd_result.returncode}",
+                file=sys.stderr,
+            )
         else:
-            print(f'[{Output.PASSED}] great success', file=sys.stderr)
+            print(f"[{Output.PASSED}] great success", file=sys.stderr)
 
-    print(f'\n[{Output.FIXUP}] Please amend & rebase your tree before '
-          'attempting to upload again.\n', file=sys.stderr)
+    print(
+        f"\n[{Output.FIXUP}] Please amend & rebase your tree before "
+        "attempting to upload again.\n",
+        file=sys.stderr,
+    )
+
 
 def _run_project_hooks_in_cwd(
     project_name: str,
@@ -359,7 +373,7 @@
     try:
         config = _get_project_config(from_git)
     except rh.config.ValidationError as e:
-        output.error('Loading config files', str(e))
+        output.error("Loading config files", str(e))
         return ret._replace(internal_failure=True)
 
     builtin_hooks = list(config.callable_builtin_hooks())
@@ -374,8 +388,10 @@
         remote = rh.git.get_upstream_remote()
         upstream_branch = rh.git.get_upstream_branch()
     except rh.utils.CalledProcessError as e:
-        output.error('Upstream remote/tracking branch lookup',
-                     f'{e}\nDid you run repo start?  Is your HEAD detached?')
+        output.error(
+            "Upstream remote/tracking branch lookup",
+            f"{e}\nDid you run repo start?  Is your HEAD detached?",
+        )
         return ret._replace(internal_failure=True)
 
     project = rh.Project(name=project_name, dir=proj_dir)
@@ -388,17 +404,20 @@
     if not builtin_hooks and not custom_hooks:
         return ret
 
-    os.environ.update({
-        'REPO_LREV': rh.git.get_commit_for_ref(upstream_branch),
-        'REPO_PATH': rel_proj_dir,
-        'REPO_PROJECT': project_name,
-        'REPO_REMOTE': remote,
-        'REPO_RREV': rh.git.get_remote_revision(upstream_branch, remote),
-    })
+    os.environ.update(
+        {
+            "REPO_LREV": rh.git.get_commit_for_ref(upstream_branch),
+            "REPO_PATH": rel_proj_dir,
+            "REPO_PROJECT": project_name,
+            "REPO_REMOTE": remote,
+            "REPO_RREV": rh.git.get_remote_revision(upstream_branch, remote),
+        }
+    )
 
     if not commit_list:
         commit_list = rh.git.get_commits(
-            ignore_merged_commits=config.ignore_merged_commits)
+            ignore_merged_commits=config.ignore_merged_commits
+        )
     output.set_num_commits(len(commit_list))
 
     def _run_hook(hook, project, commit, desc, diff):
@@ -412,23 +431,33 @@
     with concurrent.futures.ThreadPoolExecutor(max_workers=jobs) as executor:
         for commit in commit_list:
             # Mix in some settings for our hooks.
-            os.environ['PREUPLOAD_COMMIT'] = commit
+            os.environ["PREUPLOAD_COMMIT"] = commit
             diff = rh.git.get_affected_files(commit)
             desc = rh.git.get_commit_desc(commit)
-            os.environ['PREUPLOAD_COMMIT_MESSAGE'] = desc
+            os.environ["PREUPLOAD_COMMIT_MESSAGE"] = desc
 
-            commit_summary = desc.split('\n', 1)[0]
-            output.commit_start(builtin_hooks + custom_hooks, commit, commit_summary)
+            commit_summary = desc.split("\n", 1)[0]
+            output.commit_start(
+                builtin_hooks + custom_hooks, commit, commit_summary
+            )
 
             def run_hooks(hooks):
                 futures = (
-                    executor.submit(_run_hook, hook, project, commit, desc, diff)
+                    executor.submit(
+                        _run_hook, hook, project, commit, desc, diff
+                    )
                     for hook in hooks
                 )
                 future_results = (
                     x.result() for x in concurrent.futures.as_completed(futures)
                 )
-                for hook, hook_results, error, warning, duration in future_results:
+                for (
+                    hook,
+                    hook_results,
+                    error,
+                    warning,
+                    duration,
+                ) in future_results:
                     ret.add_results(hook_results)
                     if error is not None or warning is not None:
                         if warning is not None:
@@ -470,18 +499,22 @@
     output = Output(project_name)
 
     if proj_dir is None:
-        cmd = ['repo', 'forall', project_name, '-c', 'pwd']
+        cmd = ["repo", "forall", project_name, "-c", "pwd"]
         result = rh.utils.run(cmd, capture_output=True)
         proj_dirs = result.stdout.split()
         if not proj_dirs:
-            print(f'{project_name} cannot be found.', file=sys.stderr)
-            print('Please specify a valid project.', file=sys.stderr)
+            print(f"{project_name} cannot be found.", file=sys.stderr)
+            print("Please specify a valid project.", file=sys.stderr)
             return False
         if len(proj_dirs) > 1:
-            print(f'{project_name} is associated with multiple directories.',
-                  file=sys.stderr)
-            print('Please specify a directory to help disambiguate.',
-                  file=sys.stderr)
+            print(
+                f"{project_name} is associated with multiple directories.",
+                file=sys.stderr,
+            )
+            print(
+                "Please specify a directory to help disambiguate.",
+                file=sys.stderr,
+            )
             return False
         proj_dir = proj_dirs[0]
 
@@ -490,8 +523,13 @@
         # Hooks assume they are run from the root of the project.
         os.chdir(proj_dir)
         return _run_project_hooks_in_cwd(
-            project_name, proj_dir, output, jobs=jobs, from_git=from_git,
-            commit_list=commit_list)
+            project_name,
+            proj_dir,
+            output,
+            jobs=jobs,
+            from_git=from_git,
+            commit_list=commit_list,
+        )
     finally:
         output.finish()
         os.chdir(pwd)
@@ -533,7 +571,7 @@
             # If a repo had failures, add a blank line to help break up the
             # output.  If there were no failures, then the output should be
             # very minimal, so we don't add it then.
-            print('', file=sys.stderr)
+            print("", file=sys.stderr)
 
     _attempt_fixes(results)
     return not any(results)
@@ -559,10 +597,12 @@
         worktree_list = [None] * len(project_list)
     if not _run_projects_hooks(project_list, worktree_list):
         color = rh.terminal.Color()
-        print(color.color(color.RED, 'FATAL') +
-              ': Preupload failed due to above error(s).\n'
-              f'For more info, see: {REPOHOOKS_URL}',
-              file=sys.stderr)
+        print(
+            color.color(color.RED, "FATAL")
+            + ": Preupload failed due to above error(s).\n"
+            f"For more info, see: {REPOHOOKS_URL}",
+            file=sys.stderr,
+        )
         sys.exit(1)
 
 
@@ -574,20 +614,29 @@
       a blank string upon failure.
     """
     if from_git:
-        cmd = ['git', 'rev-parse', '--show-toplevel']
+        cmd = ["git", "rev-parse", "--show-toplevel"]
         project_path = rh.utils.run(cmd, capture_output=True).stdout.strip()
-        cmd = ['git', 'rev-parse', '--show-superproject-working-tree']
+        cmd = ["git", "rev-parse", "--show-superproject-working-tree"]
         superproject_path = rh.utils.run(
-            cmd, capture_output=True).stdout.strip()
-        module_path = project_path[len(superproject_path) + 1:]
-        cmd = ['git', 'config', '-f', '.gitmodules',
-               '--name-only', '--get-regexp', r'^submodule\..*\.path$',
-               f"^{module_path}$"]
-        module_name = rh.utils.run(cmd, cwd=superproject_path,
-                                   capture_output=True).stdout.strip()
-        return module_name[len('submodule.'):-len(".path")]
+            cmd, capture_output=True
+        ).stdout.strip()
+        module_path = project_path[len(superproject_path) + 1 :]
+        cmd = [
+            "git",
+            "config",
+            "-f",
+            ".gitmodules",
+            "--name-only",
+            "--get-regexp",
+            r"^submodule\..*\.path$",
+            f"^{module_path}$",
+        ]
+        module_name = rh.utils.run(
+            cmd, cwd=superproject_path, capture_output=True
+        ).stdout.strip()
+        return module_name[len("submodule.") : -len(".path")]
     else:
-        cmd = ['repo', 'forall', '.', '-c', 'echo ${REPO_PROJECT}']
+        cmd = ["repo", "forall", ".", "-c", "echo ${REPO_PROJECT}"]
         return rh.utils.run(cmd, capture_output=True, cwd=path).stdout.strip()
 
 
@@ -604,37 +653,49 @@
       BadInvocation: On some types of invocation errors.
     """
     parser = argparse.ArgumentParser(description=__doc__)
-    parser.add_argument('--git', action='store_true',
-                        help='This hook is called from git instead of repo')
-    parser.add_argument('--dir', default=None,
-                        help='The directory that the project lives in.  If not '
-                        'specified, use the git project root based on the cwd.')
-    parser.add_argument('--project', default=None,
-                        help='The project repo path; this can affect how the '
-                        'hooks get run, since some hooks are project-specific.'
-                        'If not specified, `repo` will be used to figure this '
-                        'out based on the dir.')
-    parser.add_argument('-j', '--jobs', type=int,
-                        help='Run up to this many hooks in parallel. Setting '
-                        'to 1 forces serial execution, and the default '
-                        'automatically chooses an appropriate number for the '
-                        'current system.')
-    parser.add_argument('commits', nargs='*',
-                        help='Check specific commits')
+    parser.add_argument(
+        "--git",
+        action="store_true",
+        help="This hook is called from git instead of repo",
+    )
+    parser.add_argument(
+        "--dir",
+        default=None,
+        help="The directory that the project lives in.  If not "
+        "specified, use the git project root based on the cwd.",
+    )
+    parser.add_argument(
+        "--project",
+        default=None,
+        help="The project repo path; this can affect how the "
+        "hooks get run, since some hooks are project-specific."
+        "If not specified, `repo` will be used to figure this "
+        "out based on the dir.",
+    )
+    parser.add_argument(
+        "-j",
+        "--jobs",
+        type=int,
+        help="Run up to this many hooks in parallel. Setting "
+        "to 1 forces serial execution, and the default "
+        "automatically chooses an appropriate number for the "
+        "current system.",
+    )
+    parser.add_argument("commits", nargs="*", help="Check specific commits")
     opts = parser.parse_args(argv)
 
     # Check/normalize git dir; if unspecified, we'll use the root of the git
     # project from CWD.
     if opts.dir is None:
-        cmd = ['git', 'rev-parse', '--git-dir']
+        cmd = ["git", "rev-parse", "--git-dir"]
         git_dir = rh.utils.run(cmd, capture_output=True).stdout.strip()
         if not git_dir:
-            parser.error('The current directory is not part of a git project.')
+            parser.error("The current directory is not part of a git project.")
         opts.dir = os.path.dirname(os.path.abspath(git_dir))
     elif not os.path.isdir(opts.dir):
-        parser.error(f'Invalid dir: {opts.dir}')
+        parser.error(f"Invalid dir: {opts.dir}")
     elif not rh.git.is_git_repository(opts.dir):
-        parser.error(f'Not a git repository: {opts.dir}')
+        parser.error(f"Not a git repository: {opts.dir}")
 
     # Identify the project if it wasn't specified; this _requires_ the repo
     # tool to be installed and for the project to be part of a repo checkout.
@@ -644,14 +705,19 @@
             parser.error(f"Couldn't identify the project of {opts.dir}")
 
     try:
-        if _run_projects_hooks([opts.project], [opts.dir], jobs=opts.jobs,
-                               from_git=opts.git, commit_list=opts.commits):
+        if _run_projects_hooks(
+            [opts.project],
+            [opts.dir],
+            jobs=opts.jobs,
+            from_git=opts.git,
+            commit_list=opts.commits,
+        ):
             return 0
     except KeyboardInterrupt:
-        print('Aborting execution early due to user interrupt', file=sys.stderr)
+        print("Aborting execution early due to user interrupt", file=sys.stderr)
         return 128 + signal.SIGINT
     return 1
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     sys.exit(direct_main(sys.argv[1:]))
diff --git a/pyproject.toml b/pyproject.toml
index 2cf4616..2a923fc 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -12,6 +12,11 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+[tool.black]
+line-length = 80
+target-version = ['py36']
+exclude = 'tools/cpplint\.py$'
+
 [tool.pytest.ini_options]
 python_files = "*_unittest.py"
 markers = """
diff --git a/rh/config.py b/rh/config.py
index 3671a3f..04e1d69 100644
--- a/rh/config.py
+++ b/rh/config.py
@@ -21,7 +21,7 @@
 import shlex
 import sys
 
-_path = os.path.realpath(__file__ + '/../..')
+_path = os.path.realpath(__file__ + "/../..")
 if sys.path[0] != _path:
     sys.path.insert(0, _path)
 del _path
@@ -79,12 +79,12 @@
 class PreUploadConfig(object):
     """A single (abstract) config used for `repo upload` hooks."""
 
-    CUSTOM_HOOKS_SECTION = 'Hook Scripts'
-    BUILTIN_HOOKS_SECTION = 'Builtin Hooks'
-    BUILTIN_HOOKS_OPTIONS_SECTION = 'Builtin Hooks Options'
-    BUILTIN_HOOKS_EXCLUDE_SECTION = 'Builtin Hooks Exclude Paths'
-    TOOL_PATHS_SECTION = 'Tool Paths'
-    OPTIONS_SECTION = 'Options'
+    CUSTOM_HOOKS_SECTION = "Hook Scripts"
+    BUILTIN_HOOKS_SECTION = "Builtin Hooks"
+    BUILTIN_HOOKS_OPTIONS_SECTION = "Builtin Hooks Options"
+    BUILTIN_HOOKS_EXCLUDE_SECTION = "Builtin Hooks Exclude Paths"
+    TOOL_PATHS_SECTION = "Tool Paths"
+    OPTIONS_SECTION = "Options"
     VALID_SECTIONS = {
         CUSTOM_HOOKS_SECTION,
         BUILTIN_HOOKS_SECTION,
@@ -94,7 +94,7 @@
         OPTIONS_SECTION,
     }
 
-    OPTION_IGNORE_MERGED_COMMITS = 'ignore_merged_commits'
+    OPTION_IGNORE_MERGED_COMMITS = "ignore_merged_commits"
     VALID_OPTIONS = {OPTION_IGNORE_MERGED_COMMITS}
 
     def __init__(self, config=None, source=None):
@@ -117,24 +117,34 @@
 
     def custom_hook(self, hook):
         """The command to execute for |hook|."""
-        return shlex.split(self.config.get(
-            self.CUSTOM_HOOKS_SECTION, hook, fallback=''))
+        return shlex.split(
+            self.config.get(self.CUSTOM_HOOKS_SECTION, hook, fallback="")
+        )
 
     @property
     def builtin_hooks(self):
         """List of all enabled builtin hooks (their keys/names)."""
-        return [k for k, v in self.config.items(self.BUILTIN_HOOKS_SECTION, ())
-                if rh.shell.boolean_shell_value(v, None)]
+        return [
+            k
+            for k, v in self.config.items(self.BUILTIN_HOOKS_SECTION, ())
+            if rh.shell.boolean_shell_value(v, None)
+        ]
 
     def builtin_hook_option(self, hook):
         """The options to pass to |hook|."""
-        return shlex.split(self.config.get(
-            self.BUILTIN_HOOKS_OPTIONS_SECTION, hook, fallback=''))
+        return shlex.split(
+            self.config.get(
+                self.BUILTIN_HOOKS_OPTIONS_SECTION, hook, fallback=""
+            )
+        )
 
     def builtin_hook_exclude_paths(self, hook):
         """List of paths for which |hook| should not be executed."""
-        return shlex.split(self.config.get(
-            self.BUILTIN_HOOKS_EXCLUDE_SECTION, hook, fallback=''))
+        return shlex.split(
+            self.config.get(
+                self.BUILTIN_HOOKS_EXCLUDE_SECTION, hook, fallback=""
+            )
+        )
 
     @property
     def tool_paths(self):
@@ -145,9 +155,9 @@
         """Yield a CallableHook for each hook to be executed."""
         scope = rh.hooks.ExclusionScope([])
         for hook in self.custom_hooks:
-            options = rh.hooks.HookOptions(hook,
-                                           self.custom_hook(hook),
-                                           self.tool_paths)
+            options = rh.hooks.HookOptions(
+                hook, self.custom_hook(hook), self.tool_paths
+            )
             func = functools.partial(rh.hooks.check_custom, options=options)
             yield rh.hooks.CallableHook(hook, func, scope)
 
@@ -155,22 +165,28 @@
         """Yield a CallableHook for each hook to be executed."""
         scope = rh.hooks.ExclusionScope([])
         for hook in self.builtin_hooks:
-            options = rh.hooks.HookOptions(hook,
-                                           self.builtin_hook_option(hook),
-                                           self.tool_paths)
-            func = functools.partial(rh.hooks.BUILTIN_HOOKS[hook],
-                                     options=options)
+            options = rh.hooks.HookOptions(
+                hook, self.builtin_hook_option(hook), self.tool_paths
+            )
+            func = functools.partial(
+                rh.hooks.BUILTIN_HOOKS[hook], options=options
+            )
             scope = rh.hooks.ExclusionScope(
-                self.builtin_hook_exclude_paths(hook))
+                self.builtin_hook_exclude_paths(hook)
+            )
             yield rh.hooks.CallableHook(hook, func, scope)
 
     @property
     def ignore_merged_commits(self):
         """Whether to skip hooks for merged commits."""
         return rh.shell.boolean_shell_value(
-            self.config.get(self.OPTIONS_SECTION,
-                            self.OPTION_IGNORE_MERGED_COMMITS, fallback=None),
-            False)
+            self.config.get(
+                self.OPTIONS_SECTION,
+                self.OPTION_IGNORE_MERGED_COMMITS,
+                fallback=None,
+            ),
+            False,
+        )
 
     def update(self, preupload_config):
         """Merge settings from |preupload_config| into ourself."""
@@ -184,13 +200,15 @@
         bad_sections = set(config.sections()) - self.VALID_SECTIONS
         if bad_sections:
             raise ValidationError(
-                f'{self.source}: unknown sections: {bad_sections}')
+                f"{self.source}: unknown sections: {bad_sections}"
+            )
 
         # Reject blank custom hooks.
         for hook in self.custom_hooks:
             if not config.get(self.CUSTOM_HOOKS_SECTION, hook):
                 raise ValidationError(
-                    f'{self.source}: custom hook "{hook}" cannot be blank')
+                    f'{self.source}: custom hook "{hook}" cannot be blank'
+                )
 
         # Reject unknown builtin hooks.
         valid_builtin_hooks = set(rh.hooks.BUILTIN_HOOKS.keys())
@@ -199,17 +217,21 @@
             bad_hooks = hooks - valid_builtin_hooks
             if bad_hooks:
                 raise ValidationError(
-                    f'{self.source}: unknown builtin hooks: {bad_hooks}')
+                    f"{self.source}: unknown builtin hooks: {bad_hooks}"
+                )
         elif config.has_section(self.BUILTIN_HOOKS_OPTIONS_SECTION):
-            raise ValidationError('Builtin hook options specified, but missing '
-                                  'builtin hook settings')
+            raise ValidationError(
+                "Builtin hook options specified, but missing "
+                "builtin hook settings"
+            )
 
         if config.has_section(self.BUILTIN_HOOKS_OPTIONS_SECTION):
             hooks = set(config.options(self.BUILTIN_HOOKS_OPTIONS_SECTION))
             bad_hooks = hooks - valid_builtin_hooks
             if bad_hooks:
                 raise ValidationError(
-                    f'{self.source}: unknown builtin hook options: {bad_hooks}')
+                    f"{self.source}: unknown builtin hook options: {bad_hooks}"
+                )
 
         # Verify hooks are valid shell strings.
         for hook in self.custom_hooks:
@@ -236,7 +258,8 @@
             bad_tools = tools - valid_tools
             if bad_tools:
                 raise ValidationError(
-                    f'{self.source}: unknown tools: {bad_tools}')
+                    f"{self.source}: unknown tools: {bad_tools}"
+                )
 
         # Reject unknown options.
         if config.has_section(self.OPTIONS_SECTION):
@@ -244,7 +267,8 @@
             bad_options = options - self.VALID_OPTIONS
             if bad_options:
                 raise ValidationError(
-                    f'{self.source}: unknown options: {bad_options}')
+                    f"{self.source}: unknown options: {bad_options}"
+                )
 
 
 class PreUploadFile(PreUploadConfig):
@@ -256,6 +280,7 @@
     Attributes:
       path: The path of the file.
     """
+
     FILENAME = None
 
     def __init__(self, path):
@@ -270,7 +295,7 @@
         try:
             self.config.read(path)
         except configparser.ParsingError as e:
-            raise ValidationError(f'{path}: {e}') from e
+            raise ValidationError(f"{path}: {e}") from e
 
         self._validate()
 
@@ -292,7 +317,8 @@
 
 class LocalPreUploadFile(PreUploadFile):
     """A single config file for a project (PREUPLOAD.cfg)."""
-    FILENAME = 'PREUPLOAD.cfg'
+
+    FILENAME = "PREUPLOAD.cfg"
 
     def _validate(self):
         super()._validate()
@@ -300,13 +326,15 @@
         # Reject Exclude Paths section for local config.
         if self.config.has_section(self.BUILTIN_HOOKS_EXCLUDE_SECTION):
             raise ValidationError(
-                f'{self.path}: [{self.BUILTIN_HOOKS_EXCLUDE_SECTION}] is not '
-                'valid in local files')
+                f"{self.path}: [{self.BUILTIN_HOOKS_EXCLUDE_SECTION}] is not "
+                "valid in local files"
+            )
 
 
 class GlobalPreUploadFile(PreUploadFile):
     """A single config file for a repo (GLOBAL-PREUPLOAD.cfg)."""
-    FILENAME = 'GLOBAL-PREUPLOAD.cfg'
+
+    FILENAME = "GLOBAL-PREUPLOAD.cfg"
 
 
 class PreUploadSettings(PreUploadConfig):
@@ -316,7 +344,7 @@
     settings for a particular project.
     """
 
-    def __init__(self, paths=('',), global_paths=()):
+    def __init__(self, paths=("",), global_paths=()):
         """Initialize.
 
         All the config files found will be merged together in order.
@@ -329,12 +357,12 @@
 
         self.paths = []
         for config in itertools.chain(
-                GlobalPreUploadFile.from_paths(global_paths),
-                LocalPreUploadFile.from_paths(paths)):
+            GlobalPreUploadFile.from_paths(global_paths),
+            LocalPreUploadFile.from_paths(paths),
+        ):
             self.paths.append(config.path)
             self.update(config)
 
-
         # We validated configs in isolation, now do one final pass altogether.
-        self.source = '{' + '|'.join(self.paths) + '}'
+        self.source = "{" + "|".join(self.paths) + "}"
         self._validate()
diff --git a/rh/config_test.py b/rh/config_test.py
index df3afb6..b63a657 100755
--- a/rh/config_test.py
+++ b/rh/config_test.py
@@ -32,25 +32,26 @@
 
 def assertEnv(var, value):
     """Assert |var| is set in the environment as |value|."""
-    assert var in os.environ, f'${var} missing in environment'
-    assertEqual(f'env[{var}]', value, os.environ[var])
+    assert var in os.environ, f"${var} missing in environment"
+    assertEqual(f"env[{var}]", value, os.environ[var])
 
 
 def check_commit_id(commit):
     """Check |commit| looks like a git commit id."""
     assert len(commit) == 40, f'commit "{commit}" must be 40 chars'
-    assert re.match(r'^[a-f0-9]+$', commit), \
-        f'commit "{commit}" must be all hex'
+    assert re.match(
+        r"^[a-f0-9]+$", commit
+    ), f'commit "{commit}" must be all hex'
 
 
 def check_commit_msg(msg):
     """Check the ${PREUPLOAD_COMMIT_MESSAGE} setting."""
-    assert len(msg) > 1, f'commit message must be at least 2 bytes: {msg}'
+    assert len(msg) > 1, f"commit message must be at least 2 bytes: {msg}"
 
 
 def check_repo_root(root):
     """Check the ${REPO_ROOT} setting."""
-    assertEqual('REPO_ROOT', REPO_ROOT, root)
+    assertEqual("REPO_ROOT", REPO_ROOT, root)
 
 
 def check_files(files):
@@ -60,27 +61,26 @@
 
 def check_env():
     """Verify all exported env vars look sane."""
-    assertEnv('REPO_PROJECT', 'platform/tools/repohooks')
-    assertEnv('REPO_PATH', 'tools/repohooks')
-    assertEnv('REPO_REMOTE', 'aosp')
-    check_commit_id(os.environ['REPO_LREV'])
-    print(os.environ['REPO_RREV'])
-    check_commit_id(os.environ['PREUPLOAD_COMMIT'])
+    assertEnv("REPO_PROJECT", "platform/tools/repohooks")
+    assertEnv("REPO_PATH", "tools/repohooks")
+    assertEnv("REPO_REMOTE", "aosp")
+    check_commit_id(os.environ["REPO_LREV"])
+    print(os.environ["REPO_RREV"])
+    check_commit_id(os.environ["PREUPLOAD_COMMIT"])
 
 
 def get_parser():
     """Return a command line parser."""
     parser = argparse.ArgumentParser(description=__doc__)
-    parser.add_argument('--check-env', action='store_true',
-                        help='Check all exported env vars.')
-    parser.add_argument('--commit-id',
-                        help='${PREUPLOAD_COMMIT} setting.')
-    parser.add_argument('--commit-msg',
-                        help='${PREUPLOAD_COMMIT_MESSAGE} setting.')
-    parser.add_argument('--repo-root',
-                        help='${REPO_ROOT} setting.')
-    parser.add_argument('files', nargs='+',
-                        help='${PREUPLOAD_FILES} paths.')
+    parser.add_argument(
+        "--check-env", action="store_true", help="Check all exported env vars."
+    )
+    parser.add_argument("--commit-id", help="${PREUPLOAD_COMMIT} setting.")
+    parser.add_argument(
+        "--commit-msg", help="${PREUPLOAD_COMMIT_MESSAGE} setting."
+    )
+    parser.add_argument("--repo-root", help="${REPO_ROOT} setting.")
+    parser.add_argument("files", nargs="+", help="${PREUPLOAD_FILES} paths.")
     return parser
 
 
@@ -100,11 +100,11 @@
             check_repo_root(opts.repo_root)
         check_files(opts.files)
     except AssertionError as e:
-        print(f'error: {e}', file=sys.stderr)
+        print(f"error: {e}", file=sys.stderr)
         return 1
 
     return 0
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     sys.exit(main(sys.argv[1:]))
diff --git a/rh/config_unittest.py b/rh/config_unittest.py
index 475dc22..b645320 100755
--- a/rh/config_unittest.py
+++ b/rh/config_unittest.py
@@ -21,7 +21,7 @@
 import tempfile
 import unittest
 
-_path = os.path.realpath(__file__ + '/../..')
+_path = os.path.realpath(__file__ + "/../..")
 if sys.path[0] != _path:
     sys.path.insert(0, _path)
 del _path
@@ -50,26 +50,28 @@
     def tearDown(self):
         shutil.rmtree(self.tempdir)
 
-    def _write_config(self, data, filename='temp.cfg'):
+    def _write_config(self, data, filename="temp.cfg"):
         """Helper to write out a config file for testing.
 
         Returns:
           Path to the file where the configuration was written.
         """
         path = os.path.join(self.tempdir, filename)
-        with open(path, 'w', encoding='utf-8') as fp:
+        with open(path, "w", encoding="utf-8") as fp:
             fp.write(data)
         return path
 
     def _write_local_config(self, data):
         """Helper to write out a local config file for testing."""
         return self._write_config(
-            data, filename=rh.config.LocalPreUploadFile.FILENAME)
+            data, filename=rh.config.LocalPreUploadFile.FILENAME
+        )
 
     def _write_global_config(self, data):
         """Helper to write out a global config file for testing."""
         return self._write_config(
-            data, filename=rh.config.GlobalPreUploadFile.FILENAME)
+            data, filename=rh.config.GlobalPreUploadFile.FILENAME
+        )
 
 
 class PreUploadFileTests(FileTestCase):
@@ -77,12 +79,13 @@
 
     def testEmpty(self):
         """Instantiating an empty config file should be fine."""
-        path = self._write_config('')
+        path = self._write_config("")
         rh.config.PreUploadFile(path)
 
     def testValid(self):
         """Verify a fully valid file works."""
-        path = self._write_config("""# This be a comment me matey.
+        path = self._write_config(
+            """# This be a comment me matey.
 [Hook Scripts]
 name = script --with "some args"
 
@@ -94,40 +97,48 @@
 
 [Options]
 ignore_merged_commits = true
-""")
+"""
+        )
         rh.config.PreUploadFile(path)
 
     def testUnknownSection(self):
         """Reject unknown sections."""
-        path = self._write_config('[BOOGA]')
-        self.assertRaises(rh.config.ValidationError, rh.config.PreUploadFile,
-                          path)
+        path = self._write_config("[BOOGA]")
+        self.assertRaises(
+            rh.config.ValidationError, rh.config.PreUploadFile, path
+        )
 
     def testUnknownBuiltin(self):
         """Reject unknown builtin hooks."""
-        path = self._write_config('[Builtin Hooks]\nbooga = borg!')
-        self.assertRaises(rh.config.ValidationError, rh.config.PreUploadFile,
-                          path)
+        path = self._write_config("[Builtin Hooks]\nbooga = borg!")
+        self.assertRaises(
+            rh.config.ValidationError, rh.config.PreUploadFile, path
+        )
 
     def testEmptyCustomHook(self):
         """Reject empty custom hooks."""
-        path = self._write_config('[Hook Scripts]\nbooga = \t \n')
-        self.assertRaises(rh.config.ValidationError, rh.config.PreUploadFile,
-                          path)
+        path = self._write_config("[Hook Scripts]\nbooga = \t \n")
+        self.assertRaises(
+            rh.config.ValidationError, rh.config.PreUploadFile, path
+        )
 
     def testInvalidIni(self):
         """Reject invalid ini files."""
-        path = self._write_config('[Hook Scripts]\n =')
-        self.assertRaises(rh.config.ValidationError, rh.config.PreUploadFile,
-                          path)
+        path = self._write_config("[Hook Scripts]\n =")
+        self.assertRaises(
+            rh.config.ValidationError, rh.config.PreUploadFile, path
+        )
 
     def testInvalidString(self):
         """Catch invalid string quoting."""
-        path = self._write_config("""[Hook Scripts]
+        path = self._write_config(
+            """[Hook Scripts]
 name = script --'bad-quotes
-""")
-        self.assertRaises(rh.config.ValidationError, rh.config.PreUploadFile,
-                          path)
+"""
+        )
+        self.assertRaises(
+            rh.config.ValidationError, rh.config.PreUploadFile, path
+        )
 
 
 class LocalPreUploadFileTests(FileTestCase):
@@ -135,12 +146,14 @@
 
     def testInvalidSectionConfig(self):
         """Reject local config that uses invalid sections."""
-        path = self._write_config("""[Builtin Hooks Exclude Paths]
+        path = self._write_config(
+            """[Builtin Hooks Exclude Paths]
 cpplint = external/ 'test directory' ^vendor/(?!google/)
-""")
-        self.assertRaises(rh.config.ValidationError,
-                          rh.config.LocalPreUploadFile,
-                          path)
+"""
+        )
+        self.assertRaises(
+            rh.config.ValidationError, rh.config.LocalPreUploadFile, path
+        )
 
 
 class PreUploadSettingsTests(FileTestCase):
@@ -148,25 +161,34 @@
 
     def testGlobalConfigs(self):
         """Verify global configs stack properly."""
-        self._write_global_config("""[Builtin Hooks]
+        self._write_global_config(
+            """[Builtin Hooks]
 commit_msg_bug_field = true
 commit_msg_changeid_field = true
-commit_msg_test_field = false""")
-        self._write_local_config("""[Builtin Hooks]
+commit_msg_test_field = false"""
+        )
+        self._write_local_config(
+            """[Builtin Hooks]
 commit_msg_bug_field = false
-commit_msg_test_field = true""")
-        config = rh.config.PreUploadSettings(paths=(self.tempdir,),
-                                             global_paths=(self.tempdir,))
-        self.assertEqual(config.builtin_hooks,
-                         ['commit_msg_changeid_field', 'commit_msg_test_field'])
+commit_msg_test_field = true"""
+        )
+        config = rh.config.PreUploadSettings(
+            paths=(self.tempdir,), global_paths=(self.tempdir,)
+        )
+        self.assertEqual(
+            config.builtin_hooks,
+            ["commit_msg_changeid_field", "commit_msg_test_field"],
+        )
 
     def testGlobalExcludeScope(self):
         """Verify exclude scope is valid for global config."""
-        self._write_global_config("""[Builtin Hooks Exclude Paths]
+        self._write_global_config(
+            """[Builtin Hooks Exclude Paths]
 cpplint = external/ 'test directory' ^vendor/(?!google/)
-""")
+"""
+        )
         rh.config.PreUploadSettings(global_paths=(self.tempdir,))
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     unittest.main()
diff --git a/rh/git.py b/rh/git.py
index 5496164..1b3143d 100644
--- a/rh/git.py
+++ b/rh/git.py
@@ -18,7 +18,7 @@
 import re
 import sys
 
-_path = os.path.realpath(__file__ + '/../..')
+_path = os.path.realpath(__file__ + "/../..")
 if sys.path[0] != _path:
     sys.path.insert(0, _path)
 del _path
@@ -30,12 +30,12 @@
 def get_upstream_remote():
     """Returns the current upstream remote name."""
     # First get the current branch name.
-    cmd = ['git', 'rev-parse', '--abbrev-ref', 'HEAD']
+    cmd = ["git", "rev-parse", "--abbrev-ref", "HEAD"]
     result = rh.utils.run(cmd, capture_output=True)
     branch = result.stdout.strip()
 
     # Then get the remote associated with this branch.
-    cmd = ['git', 'config', f'branch.{branch}.remote']
+    cmd = ["git", "config", f"branch.{branch}.remote"]
     result = rh.utils.run(cmd, capture_output=True)
     return result.stdout.strip()
 
@@ -46,46 +46,46 @@
     Raises:
       Error if there is no tracking branch
     """
-    cmd = ['git', 'symbolic-ref', 'HEAD']
+    cmd = ["git", "symbolic-ref", "HEAD"]
     result = rh.utils.run(cmd, capture_output=True)
-    current_branch = result.stdout.strip().replace('refs/heads/', '')
+    current_branch = result.stdout.strip().replace("refs/heads/", "")
     if not current_branch:
-        raise ValueError('Need to be on a tracking branch')
+        raise ValueError("Need to be on a tracking branch")
 
-    cfg_option = 'branch.' + current_branch + '.'
-    cmd = ['git', 'config', cfg_option + 'merge']
+    cfg_option = "branch." + current_branch + "."
+    cmd = ["git", "config", cfg_option + "merge"]
     result = rh.utils.run(cmd, capture_output=True)
     full_upstream = result.stdout.strip()
     # If remote is not fully qualified, add an implicit namespace.
-    if '/' not in full_upstream:
-        full_upstream = f'refs/heads/{full_upstream}'
-    cmd = ['git', 'config', cfg_option + 'remote']
+    if "/" not in full_upstream:
+        full_upstream = f"refs/heads/{full_upstream}"
+    cmd = ["git", "config", cfg_option + "remote"]
     result = rh.utils.run(cmd, capture_output=True)
     remote = result.stdout.strip()
     if not remote or not full_upstream:
-        raise ValueError('Need to be on a tracking branch')
+        raise ValueError("Need to be on a tracking branch")
 
-    return full_upstream.replace('heads', 'remotes/' + remote)
+    return full_upstream.replace("heads", "remotes/" + remote)
 
 
 def get_commit_for_ref(ref):
     """Returns the latest commit for this ref."""
-    cmd = ['git', 'rev-parse', ref]
+    cmd = ["git", "rev-parse", ref]
     result = rh.utils.run(cmd, capture_output=True)
     return result.stdout.strip()
 
 
 def get_remote_revision(ref, remote):
     """Returns the remote revision for this ref."""
-    prefix = f'refs/remotes/{remote}/'
+    prefix = f"refs/remotes/{remote}/"
     if ref.startswith(prefix):
-        return ref[len(prefix):]
+        return ref[len(prefix) :]
     return ref
 
 
 def get_patch(commit):
     """Returns the patch for this commit."""
-    cmd = ['git', 'format-patch', '--stdout', '-1', commit]
+    cmd = ["git", "format-patch", "--stdout", "-1", commit]
     return rh.utils.run(cmd, capture_output=True).stdout
 
 
@@ -99,7 +99,7 @@
     a full file, you should check that first.  One way to detect is that the
     content will not have any newlines.
     """
-    cmd = ['git', 'show', f'{commit}:{path}']
+    cmd = ["git", "show", f"{commit}:{path}"]
     return rh.utils.run(cmd, capture_output=True).stdout
 
 
@@ -107,9 +107,18 @@
     """Representation of a line from raw formatted git diff output."""
 
     # pylint: disable=redefined-builtin
-    def __init__(self, src_mode=0, dst_mode=0, src_sha=None, dst_sha=None,
-                 status=None, score=None, src_file=None, dst_file=None,
-                 file=None):
+    def __init__(
+        self,
+        src_mode=0,
+        dst_mode=0,
+        src_sha=None,
+        dst_sha=None,
+        status=None,
+        score=None,
+        src_file=None,
+        dst_file=None,
+        file=None,
+    ):
         self.src_mode = src_mode
         self.dst_mode = dst_mode
         self.src_sha = src_sha
@@ -123,10 +132,11 @@
 
 # This regular expression pulls apart a line of raw formatted git diff output.
 DIFF_RE = re.compile(
-    r':(?P<src_mode>[0-7]*) (?P<dst_mode>[0-7]*) '
-    r'(?P<src_sha>[0-9a-f]*)(\.)* (?P<dst_sha>[0-9a-f]*)(\.)* '
-    r'(?P<status>[ACDMRTUX])(?P<score>[0-9]+)?\t'
-    r'(?P<src_file>[^\t]+)\t?(?P<dst_file>[^\t]+)?')
+    r":(?P<src_mode>[0-7]*) (?P<dst_mode>[0-7]*) "
+    r"(?P<src_sha>[0-9a-f]*)(\.)* (?P<dst_sha>[0-9a-f]*)(\.)* "
+    r"(?P<status>[ACDMRTUX])(?P<score>[0-9]+)?\t"
+    r"(?P<src_file>[^\t]+)\t?(?P<dst_file>[^\t]+)?"
+)
 
 
 def raw_diff(path, target):
@@ -141,18 +151,19 @@
     """
     entries = []
 
-    cmd = ['git', 'diff', '--no-ext-diff', '-M', '--raw', target]
+    cmd = ["git", "diff", "--no-ext-diff", "-M", "--raw", target]
     diff = rh.utils.run(cmd, cwd=path, capture_output=True).stdout
     diff_lines = diff.strip().splitlines()
     for line in diff_lines:
         match = DIFF_RE.match(line)
         if not match:
-            raise ValueError(f'Failed to parse diff output: {line}')
+            raise ValueError(f"Failed to parse diff output: {line}")
         rawdiff = RawDiffEntry(**match.groupdict())
         rawdiff.src_mode = int(rawdiff.src_mode)
         rawdiff.dst_mode = int(rawdiff.dst_mode)
-        rawdiff.file = (rawdiff.dst_file
-                        if rawdiff.dst_file else rawdiff.src_file)
+        rawdiff.file = (
+            rawdiff.dst_file if rawdiff.dst_file else rawdiff.src_file
+        )
         entries.append(rawdiff)
 
     return entries
@@ -164,20 +175,20 @@
     Returns:
       A list of modified/added (and perhaps deleted) files
     """
-    return raw_diff(os.getcwd(), f'{commit}^-')
+    return raw_diff(os.getcwd(), f"{commit}^-")
 
 
 def get_commits(ignore_merged_commits=False):
     """Returns a list of commits for this review."""
-    cmd = ['git', 'rev-list', f'{get_upstream_branch()}..']
+    cmd = ["git", "rev-list", f"{get_upstream_branch()}.."]
     if ignore_merged_commits:
-        cmd.append('--first-parent')
+        cmd.append("--first-parent")
     return rh.utils.run(cmd, capture_output=True).stdout.split()
 
 
 def get_commit_desc(commit):
     """Returns the full commit message of a commit."""
-    cmd = ['git', 'diff-tree', '-s', '--always', '--format=%B', commit]
+    cmd = ["git", "diff-tree", "-s", "--always", "--format=%B", commit]
     return rh.utils.run(cmd, capture_output=True).stdout
 
 
@@ -196,24 +207,27 @@
     # If we are working on a superproject instead of a repo client, use the
     # result from git directly.  For regular repo client, this would return
     # empty string.
-    cmd = ['git', 'rev-parse', '--show-superproject-working-tree']
-    git_worktree_path = rh.utils.run(cmd, cwd=path, capture_output=True).stdout.strip()
+    cmd = ["git", "rev-parse", "--show-superproject-working-tree"]
+    git_worktree_path = rh.utils.run(
+        cmd, cwd=path, capture_output=True
+    ).stdout.strip()
     if git_worktree_path:
         return git_worktree_path
 
-    while not os.path.exists(os.path.join(path, '.repo')):
+    while not os.path.exists(os.path.join(path, ".repo")):
         path = os.path.dirname(path)
-        if path == '/':
-            raise ValueError(f'Could not locate .repo in {orig_path}')
+        if path == "/":
+            raise ValueError(f"Could not locate .repo in {orig_path}")
 
     root = path
-    if not outer and os.path.isdir(os.path.join(root, '.repo', 'submanifests')):
+    if not outer and os.path.isdir(os.path.join(root, ".repo", "submanifests")):
         # If there are submanifests, walk backward from path until we find the
         # corresponding submanifest root.
         abs_orig_path = os.path.abspath(orig_path)
         parts = os.path.relpath(abs_orig_path, root).split(os.path.sep)
         while parts and not os.path.isdir(
-            os.path.join(root, '.repo', 'submanifests', *parts, 'manifests')):
+            os.path.join(root, ".repo", "submanifests", *parts, "manifests")
+        ):
             parts.pop()
         path = os.path.join(root, *parts)
 
@@ -222,6 +236,6 @@
 
 def is_git_repository(path):
     """Returns True if the path is a valid git repository."""
-    cmd = ['git', 'rev-parse', '--resolve-git-dir', os.path.join(path, '.git')]
+    cmd = ["git", "rev-parse", "--resolve-git-dir", os.path.join(path, ".git")]
     result = rh.utils.run(cmd, capture_output=True, check=False)
     return result.returncode == 0
diff --git a/rh/hooks.py b/rh/hooks.py
index b23ce3d..8f128b0 100644
--- a/rh/hooks.py
+++ b/rh/hooks.py
@@ -22,7 +22,7 @@
 import sys
 from typing import Callable, NamedTuple
 
-_path = os.path.realpath(__file__ + '/../..')
+_path = os.path.realpath(__file__ + "/../..")
 if sys.path[0] != _path:
     sys.path.insert(0, _path)
 del _path
@@ -67,23 +67,26 @@
 
         ret = []
         for arg in args:
-            if arg.endswith('${PREUPLOAD_FILES_PREFIXED}'):
-                if arg == '${PREUPLOAD_FILES_PREFIXED}':
-                    assert len(ret) > 1, ('PREUPLOAD_FILES_PREFIXED cannot be '
-                                          'the 1st or 2nd argument')
+            if arg.endswith("${PREUPLOAD_FILES_PREFIXED}"):
+                if arg == "${PREUPLOAD_FILES_PREFIXED}":
+                    assert len(ret) > 1, (
+                        "PREUPLOAD_FILES_PREFIXED cannot be "
+                        "the 1st or 2nd argument"
+                    )
                     prev_arg = ret[-1]
                     ret = ret[0:-1]
-                    for file in self.get('PREUPLOAD_FILES'):
+                    for file in self.get("PREUPLOAD_FILES"):
                         ret.append(prev_arg)
                         ret.append(file)
                 else:
-                    prefix = arg[0:-len('${PREUPLOAD_FILES_PREFIXED}')]
+                    prefix = arg[0 : -len("${PREUPLOAD_FILES_PREFIXED}")]
                     ret.extend(
-                        prefix + file for file in self.get('PREUPLOAD_FILES'))
+                        prefix + file for file in self.get("PREUPLOAD_FILES")
+                    )
             else:
                 # First scan for exact matches
                 for key, val in replacements.items():
-                    var = '${' + key + '}'
+                    var = "${" + key + "}"
                     if arg == var:
                         if isinstance(val, str):
                             ret.append(val)
@@ -97,46 +100,50 @@
                         val = self.get(m.group(1))
                         if isinstance(val, str):
                             return val
-                        return ' '.join(val)
-                    ret.append(re.sub(r'\$\{(' + '|'.join(all_vars) + r')\}',
-                                      replace, arg))
+                        return " ".join(val)
+
+                    ret.append(
+                        re.sub(
+                            r"\$\{(" + "|".join(all_vars) + r")\}", replace, arg
+                        )
+                    )
         return ret
 
     @classmethod
     def vars(cls):
         """Yield all replacement variable names."""
         for key in dir(cls):
-            if key.startswith('var_'):
+            if key.startswith("var_"):
                 yield key[4:]
 
     def get(self, var):
         """Helper function to get the replacement |var| value."""
-        return getattr(self, f'var_{var}')
+        return getattr(self, f"var_{var}")
 
     @property
     def var_PREUPLOAD_COMMIT_MESSAGE(self):
         """The git commit message."""
-        return os.environ.get('PREUPLOAD_COMMIT_MESSAGE', '')
+        return os.environ.get("PREUPLOAD_COMMIT_MESSAGE", "")
 
     @property
     def var_PREUPLOAD_COMMIT(self):
         """The git commit sha1."""
-        return os.environ.get('PREUPLOAD_COMMIT', '')
+        return os.environ.get("PREUPLOAD_COMMIT", "")
 
     @property
     def var_PREUPLOAD_FILES(self):
         """List of files modified in this git commit."""
-        return [x.file for x in self.diff if x.status != 'D']
+        return [x.file for x in self.diff if x.status != "D"]
 
     @property
     def var_REPO_PATH(self):
         """The path to the project relative to the root"""
-        return os.environ.get('REPO_PATH', '')
+        return os.environ.get("REPO_PATH", "")
 
     @property
     def var_REPO_PROJECT(self):
         """The name of the project"""
-        return os.environ.get('REPO_PROJECT', '')
+        return os.environ.get("REPO_PROJECT", "")
 
     @property
     def var_REPO_ROOT(self):
@@ -170,7 +177,7 @@
         """
         self._scope = []
         for path in scope:
-            if path.startswith('^'):
+            if path.startswith("^"):
                 self._scope.append(re.compile(path))
             else:
                 self._scope.append(path)
@@ -182,7 +189,7 @@
           proj_dir: The relative path of the project.
         """
         for exclusion_path in self._scope:
-            if hasattr(exclusion_path, 'match'):
+            if hasattr(exclusion_path, "match"):
                 if exclusion_path.match(proj_dir):
                     return True
             elif fnmatch.fnmatch(proj_dir, exclusion_path):
@@ -250,6 +257,7 @@
 
 class CallableHook(NamedTuple):
     """A callable hook."""
+
     name: str
     hook: Callable
     scope: ExclusionScope
@@ -257,12 +265,12 @@
 
 def _run(cmd, **kwargs):
     """Helper command for checks that tend to gather output."""
-    kwargs.setdefault('combine_stdout_stderr', True)
-    kwargs.setdefault('capture_output', True)
-    kwargs.setdefault('check', False)
+    kwargs.setdefault("combine_stdout_stderr", True)
+    kwargs.setdefault("capture_output", True)
+    kwargs.setdefault("check", False)
     # Make sure hooks run with stdin disconnected to avoid accidentally
     # interactive tools causing pauses.
-    kwargs.setdefault('input', '')
+    kwargs.setdefault("input", "")
     return rh.utils.run(cmd, **kwargs)
 
 
@@ -300,9 +308,11 @@
     """
     filtered = []
     for d in diff:
-        if (d.status != 'D' and
-                _match_regex_list(d.file, include_list) and
-                not _match_regex_list(d.file, exclude_list)):
+        if (
+            d.status != "D"
+            and _match_regex_list(d.file, include_list)
+            and not _match_regex_list(d.file, exclude_list)
+        ):
             # We've got a match!
             filtered.append(d)
     return filtered
@@ -315,22 +325,25 @@
       A string in a format usable to get prebuilt tool paths.
     """
     system = platform.system()
-    if 'Darwin' in system or 'Macintosh' in system:
-        return 'darwin-x86'
+    if "Darwin" in system or "Macintosh" in system:
+        return "darwin-x86"
 
     # TODO: Add more values if needed.
-    return 'linux-x86'
+    return "linux-x86"
 
 
 def _check_cmd(hook_name, project, commit, cmd, fixup_cmd=None, **kwargs):
     """Runs |cmd| and returns its result as a HookCommandResult."""
-    return [rh.results.HookCommandResult(hook_name, project, commit,
-                                         _run(cmd, **kwargs),
-                                         fixup_cmd=fixup_cmd)]
+    return [
+        rh.results.HookCommandResult(
+            hook_name, project, commit, _run(cmd, **kwargs), fixup_cmd=fixup_cmd
+        )
+    ]
 
 
 # Where helper programs exist.
-TOOLS_DIR = os.path.realpath(__file__ + '/../../tools')
+TOOLS_DIR = os.path.realpath(__file__ + "/../../tools")
+
 
 def get_helper_path(tool):
     """Return the full path to the helper |tool|."""
@@ -339,134 +352,157 @@
 
 def check_custom(project, commit, _desc, diff, options=None, **kwargs):
     """Run a custom hook."""
-    return _check_cmd(options.name, project, commit, options.args((), diff),
-                      **kwargs)
+    return _check_cmd(
+        options.name, project, commit, options.args((), diff), **kwargs
+    )
 
 
 def check_aosp_license(project, commit, _desc, diff, options=None):
     """Checks that if all new added files has AOSP licenses"""
 
-    exclude_dir_args = [x for x in options.args()
-                        if x.startswith('--exclude-dirs=')]
-    exclude_dirs = [x[len('--exclude-dirs='):].split(',')
-                    for x in exclude_dir_args]
-    exclude_list = [fr'^{x}/.*$' for dir_list in exclude_dirs for x in dir_list]
+    exclude_dir_args = [
+        x for x in options.args() if x.startswith("--exclude-dirs=")
+    ]
+    exclude_dirs = [
+        x[len("--exclude-dirs=") :].split(",") for x in exclude_dir_args
+    ]
+    exclude_list = [rf"^{x}/.*$" for dir_list in exclude_dirs for x in dir_list]
 
     # Filter diff based on extension.
-    extensions = frozenset((
-        # Coding languages and scripts.
-        'c',
-        'cc',
-        'cpp',
-        'h',
-        'java',
-        'kt',
-        'rs',
-        'py',
-        'sh',
-
-        # Build and config files.
-        'bp',
-        'mk',
-        'xml',
-    ))
-    diff = _filter_diff(diff, [r'\.(' + '|'.join(extensions) + r')$'], exclude_list)
+    extensions = frozenset(
+        (
+            # Coding languages and scripts.
+            "c",
+            "cc",
+            "cpp",
+            "h",
+            "java",
+            "kt",
+            "rs",
+            "py",
+            "sh",
+            # Build and config files.
+            "bp",
+            "mk",
+            "xml",
+        )
+    )
+    diff = _filter_diff(
+        diff, [r"\.(" + "|".join(extensions) + r")$"], exclude_list
+    )
 
     # Only check the new-added files.
-    diff = [d for d in diff if d.status == 'A']
+    diff = [d for d in diff if d.status == "A"]
 
     if not diff:
         return None
 
-    cmd = [get_helper_path('check_aosp_license.py'), '--commit-hash', commit]
-    cmd += HookOptions.expand_vars(('${PREUPLOAD_FILES}',), diff)
-    return _check_cmd('aosp_license', project, commit, cmd)
+    cmd = [get_helper_path("check_aosp_license.py"), "--commit-hash", commit]
+    cmd += HookOptions.expand_vars(("${PREUPLOAD_FILES}",), diff)
+    return _check_cmd("aosp_license", project, commit, cmd)
 
 
 def check_black(project, commit, _desc, diff, options=None):
     """Checks that Python files are formatted with black."""
-    filtered = _filter_diff(diff, [r'\.py$'])
+    filtered = _filter_diff(diff, [r"\.py$"])
     if not filtered:
         return None
 
-    tool = options.tool_path('black')
+    tool = options.tool_path("black")
     tool_options = options.args((), filtered)
-    cmd = [tool, '--check'] + tool_options
-    fixup_cmd = [tool] + tool_options + ['--']
+    cmd = [tool, "--check"] + tool_options
+    fixup_cmd = [tool] + tool_options + ["--"]
 
     ret = []
     for d in filtered:
         data = rh.git.get_file_content(commit, d.file)
         result = _run(cmd, input=data)
         if result.stdout:
-            ret.append(rh.results.HookResult(
-                'black', project, commit,
-                error=result.stdout,
-                files=(d.file,),
-                fixup_cmd=fixup_cmd))
+            ret.append(
+                rh.results.HookResult(
+                    "black",
+                    project,
+                    commit,
+                    error=result.stdout,
+                    files=(d.file,),
+                    fixup_cmd=fixup_cmd,
+                )
+            )
     return ret
 
 
 def check_bpfmt(project, commit, _desc, diff, options=None):
     """Checks that Blueprint files are formatted with bpfmt."""
-    filtered = _filter_diff(diff, [r'\.bp$'])
+    filtered = _filter_diff(diff, [r"\.bp$"])
     if not filtered:
         return None
 
-    bpfmt = options.tool_path('bpfmt')
+    bpfmt = options.tool_path("bpfmt")
     bpfmt_options = options.args((), filtered)
-    cmd = [bpfmt, '-d'] + bpfmt_options
-    fixup_cmd = [bpfmt, '-w']
-    if '-s' in bpfmt_options:
-        fixup_cmd.append('-s')
-    fixup_cmd.append('--')
+    cmd = [bpfmt, "-d"] + bpfmt_options
+    fixup_cmd = [bpfmt, "-w"]
+    if "-s" in bpfmt_options:
+        fixup_cmd.append("-s")
+    fixup_cmd.append("--")
 
     ret = []
     for d in filtered:
         data = rh.git.get_file_content(commit, d.file)
         result = _run(cmd, input=data)
         if result.stdout:
-            ret.append(rh.results.HookResult(
-                'bpfmt', project, commit,
-                error=result.stdout,
-                files=(d.file,),
-                fixup_cmd=fixup_cmd))
+            ret.append(
+                rh.results.HookResult(
+                    "bpfmt",
+                    project,
+                    commit,
+                    error=result.stdout,
+                    files=(d.file,),
+                    fixup_cmd=fixup_cmd,
+                )
+            )
     return ret
 
 
 def check_checkpatch(project, commit, _desc, diff, options=None):
     """Run |diff| through the kernel's checkpatch.pl tool."""
-    tool = get_helper_path('checkpatch.pl')
-    cmd = ([tool, '-', '--root', project.dir] +
-           options.args(('--ignore=GERRIT_CHANGE_ID',), diff))
-    return _check_cmd('checkpatch.pl', project, commit, cmd,
-                      input=rh.git.get_patch(commit))
+    tool = get_helper_path("checkpatch.pl")
+    cmd = [tool, "-", "--root", project.dir] + options.args(
+        ("--ignore=GERRIT_CHANGE_ID",), diff
+    )
+    return _check_cmd(
+        "checkpatch.pl", project, commit, cmd, input=rh.git.get_patch(commit)
+    )
 
 
 def check_clang_format(project, commit, _desc, diff, options=None):
     """Run git clang-format on the commit."""
-    tool = get_helper_path('clang-format.py')
-    clang_format = options.tool_path('clang-format')
-    git_clang_format = options.tool_path('git-clang-format')
-    tool_args = (['--clang-format', clang_format, '--git-clang-format',
-                  git_clang_format] +
-                 options.args(('--style', 'file', '--commit', commit), diff))
+    tool = get_helper_path("clang-format.py")
+    clang_format = options.tool_path("clang-format")
+    git_clang_format = options.tool_path("git-clang-format")
+    tool_args = [
+        "--clang-format",
+        clang_format,
+        "--git-clang-format",
+        git_clang_format,
+    ] + options.args(("--style", "file", "--commit", commit), diff)
     cmd = [tool] + tool_args
-    fixup_cmd = [tool, '--fix'] + tool_args
-    return _check_cmd('clang-format', project, commit, cmd,
-                      fixup_cmd=fixup_cmd)
+    fixup_cmd = [tool, "--fix"] + tool_args
+    return _check_cmd("clang-format", project, commit, cmd, fixup_cmd=fixup_cmd)
 
 
 def check_google_java_format(project, commit, _desc, _diff, options=None):
     """Run google-java-format on the commit."""
-    include_dir_args = [x for x in options.args()
-                        if x.startswith('--include-dirs=')]
-    include_dirs = [x[len('--include-dirs='):].split(',')
-                    for x in include_dir_args]
-    patterns = [fr'^{x}/.*\.java$' for dir_list in include_dirs
-                for x in dir_list]
+    include_dir_args = [
+        x for x in options.args() if x.startswith("--include-dirs=")
+    ]
+    include_dirs = [
+        x[len("--include-dirs=") :].split(",") for x in include_dir_args
+    ]
+    patterns = [
+        rf"^{x}/.*\.java$" for dir_list in include_dirs for x in dir_list
+    ]
     if not patterns:
-        patterns = [r'\.java$']
+        patterns = [r"\.java$"]
 
     filtered = _filter_diff(_diff, patterns)
 
@@ -475,32 +511,47 @@
 
     args = [x for x in options.args() if x not in include_dir_args]
 
-    tool = get_helper_path('google-java-format.py')
-    google_java_format = options.tool_path('google-java-format')
-    google_java_format_diff = options.tool_path('google-java-format-diff')
-    tool_args = ['--google-java-format', google_java_format,
-                 '--google-java-format-diff', google_java_format_diff,
-                 '--commit', commit] + args
-    cmd = [tool] + tool_args + HookOptions.expand_vars(
-                   ('${PREUPLOAD_FILES}',), filtered)
-    fixup_cmd = [tool, '--fix'] + tool_args
-    return [rh.results.HookCommandResult('google-java-format', project, commit,
-                                         _run(cmd),
-                                         files=[x.file for x in filtered],
-                                         fixup_cmd=fixup_cmd)]
+    tool = get_helper_path("google-java-format.py")
+    google_java_format = options.tool_path("google-java-format")
+    google_java_format_diff = options.tool_path("google-java-format-diff")
+    tool_args = [
+        "--google-java-format",
+        google_java_format,
+        "--google-java-format-diff",
+        google_java_format_diff,
+        "--commit",
+        commit,
+    ] + args
+    cmd = (
+        [tool]
+        + tool_args
+        + HookOptions.expand_vars(("${PREUPLOAD_FILES}",), filtered)
+    )
+    fixup_cmd = [tool, "--fix"] + tool_args
+    return [
+        rh.results.HookCommandResult(
+            "google-java-format",
+            project,
+            commit,
+            _run(cmd),
+            files=[x.file for x in filtered],
+            fixup_cmd=fixup_cmd,
+        )
+    ]
 
 
 def check_ktfmt(project, commit, _desc, diff, options=None):
     """Checks that kotlin files are formatted with ktfmt."""
 
-    include_dir_args = [x for x in options.args()
-                        if x.startswith('--include-dirs=')]
-    include_dirs = [x[len('--include-dirs='):].split(',')
-                    for x in include_dir_args]
-    patterns = [fr'^{x}/.*\.kt$' for dir_list in include_dirs
-                for x in dir_list]
+    include_dir_args = [
+        x for x in options.args() if x.startswith("--include-dirs=")
+    ]
+    include_dirs = [
+        x[len("--include-dirs=") :].split(",") for x in include_dir_args
+    ]
+    patterns = [rf"^{x}/.*\.kt$" for dir_list in include_dirs for x in dir_list]
     if not patterns:
-        patterns = [r'\.kt$']
+        patterns = [r"\.kt$"]
 
     filtered = _filter_diff(diff, patterns)
 
@@ -509,26 +560,36 @@
 
     args = [x for x in options.args() if x not in include_dir_args]
 
-    ktfmt = options.tool_path('ktfmt')
-    cmd = [ktfmt, '--dry-run'] + args + HookOptions.expand_vars(
-        ('${PREUPLOAD_FILES}',), filtered)
+    ktfmt = options.tool_path("ktfmt")
+    cmd = (
+        [ktfmt, "--dry-run"]
+        + args
+        + HookOptions.expand_vars(("${PREUPLOAD_FILES}",), filtered)
+    )
     result = _run(cmd)
     if result.stdout:
         fixup_cmd = [ktfmt] + args
-        return [rh.results.HookResult(
-            'ktfmt', project, commit, error='Formatting errors detected',
-            files=[x.file for x in filtered], fixup_cmd=fixup_cmd)]
+        return [
+            rh.results.HookResult(
+                "ktfmt",
+                project,
+                commit,
+                error="Formatting errors detected",
+                files=[x.file for x in filtered],
+                fixup_cmd=fixup_cmd,
+            )
+        ]
     return None
 
 
 def check_commit_msg_bug_field(project, commit, desc, _diff, options=None):
     """Check the commit message for a 'Bug:' or 'Fix:' line."""
-    field = 'Bug'
-    regex = r'^(Bug|Fix): (None|[0-9]+(, [0-9]+)*)$'
+    field = "Bug"
+    regex = r"^(Bug|Fix): (None|[0-9]+(, [0-9]+)*)$"
     check_re = re.compile(regex)
 
     if options.args():
-        raise ValueError(f'commit msg {field} check takes no options')
+        raise ValueError(f"commit msg {field} check takes no options")
 
     found = []
     for line in desc.splitlines():
@@ -538,23 +599,26 @@
     if not found:
         error = (
             f'Commit message is missing a "{field}:" line.  It must match the\n'
-            f'following case-sensitive regex:\n\n    {regex}'
+            f"following case-sensitive regex:\n\n    {regex}"
         )
     else:
         return None
 
-    return [rh.results.HookResult(f'commit msg: "{field}:" check',
-                                  project, commit, error=error)]
+    return [
+        rh.results.HookResult(
+            f'commit msg: "{field}:" check', project, commit, error=error
+        )
+    ]
 
 
 def check_commit_msg_changeid_field(project, commit, desc, _diff, options=None):
     """Check the commit message for a 'Change-Id:' line."""
-    field = 'Change-Id'
-    regex = fr'^{field}: I[a-f0-9]+$'
+    field = "Change-Id"
+    regex = rf"^{field}: I[a-f0-9]+$"
     check_re = re.compile(regex)
 
     if options.args():
-        raise ValueError(f'commit msg {field} check takes no options')
+        raise ValueError(f"commit msg {field} check takes no options")
 
     found = []
     for line in desc.splitlines():
@@ -564,16 +628,21 @@
     if not found:
         error = (
             f'Commit message is missing a "{field}:" line.  It must match the\n'
-            f'following case-sensitive regex:\n\n    {regex}'
+            f"following case-sensitive regex:\n\n    {regex}"
         )
     elif len(found) > 1:
-        error = (f'Commit message has too many "{field}:" lines.  There can be '
-                 'only one.')
+        error = (
+            f'Commit message has too many "{field}:" lines.  There can be '
+            "only one."
+        )
     else:
         return None
 
-    return [rh.results.HookResult(f'commit msg: "{field}:" check',
-                                  project, commit, error=error)]
+    return [
+        rh.results.HookResult(
+            f'commit msg: "{field}:" check', project, commit, error=error
+        )
+    ]
 
 
 PREBUILT_APK_MSG = """Commit message is missing required prebuilt APK
@@ -598,25 +667,28 @@
 """
 
 
-def check_commit_msg_prebuilt_apk_fields(project, commit, desc, diff,
-                                         options=None):
+def check_commit_msg_prebuilt_apk_fields(
+    project, commit, desc, diff, options=None
+):
     """Check that prebuilt APK commits contain the required lines."""
 
     if options.args():
-        raise ValueError('prebuilt apk check takes no options')
+        raise ValueError("prebuilt apk check takes no options")
 
-    filtered = _filter_diff(diff, [r'\.apk$'])
+    filtered = _filter_diff(diff, [r"\.apk$"])
     if not filtered:
         return None
 
     regexes = [
-        r'^package: .*$',
-        r'^sdkVersion:.*$',
-        r'^targetSdkVersion:.*$',
-        r'^Built here:.*$',
-        (r'^This build IS( NOT)? suitable for'
-         r'( preview|( preview or)? public) release'
-         r'( but IS NOT suitable for public release)?\.$')
+        r"^package: .*$",
+        r"^sdkVersion:.*$",
+        r"^targetSdkVersion:.*$",
+        r"^Built here:.*$",
+        (
+            r"^This build IS( NOT)? suitable for"
+            r"( preview|( preview or)? public) release"
+            r"( but IS NOT suitable for public release)?\.$"
+        ),
     ]
 
     missing = []
@@ -625,12 +697,15 @@
             missing.append(regex)
 
     if missing:
-        error = PREBUILT_APK_MSG % '\n    '.join(missing)
+        error = PREBUILT_APK_MSG % "\n    ".join(missing)
     else:
         return None
 
-    return [rh.results.HookResult('commit msg: "prebuilt apk:" check',
-                                  project, commit, error=error)]
+    return [
+        rh.results.HookResult(
+            'commit msg: "prebuilt apk:" check', project, commit, error=error
+        )
+    ]
 
 
 TEST_MSG = """Commit message is missing a "Test:" line.  It must match the
@@ -664,12 +739,12 @@
 
 def check_commit_msg_test_field(project, commit, desc, _diff, options=None):
     """Check the commit message for a 'Test:' line."""
-    field = 'Test'
-    regex = fr'^{field}: .*$'
+    field = "Test"
+    regex = rf"^{field}: .*$"
     check_re = re.compile(regex)
 
     if options.args():
-        raise ValueError(f'commit msg {field} check takes no options')
+        raise ValueError(f"commit msg {field} check takes no options")
 
     found = []
     for line in desc.splitlines():
@@ -681,8 +756,11 @@
     else:
         return None
 
-    return [rh.results.HookResult(f'commit msg: "{field}:" check',
-                                  project, commit, error=error)]
+    return [
+        rh.results.HookResult(
+            f'commit msg: "{field}:" check', project, commit, error=error
+        )
+    ]
 
 
 RELNOTE_MISSPELL_MSG = """Commit message contains something that looks
@@ -729,8 +807,10 @@
 Relnote: Added a new API to handle strings like \"foo\"
 """
 
-def check_commit_msg_relnote_field_format(project, commit, desc, _diff,
-                                          options=None):
+
+def check_commit_msg_relnote_field_format(
+    project, commit, desc, _diff, options=None
+):
     """Check the commit for one correctly formatted 'Relnote:' line.
 
     Checks the commit message for two things:
@@ -740,59 +820,74 @@
     (3) Checks that release notes that contain non-starting or non-ending
     quotes are escaped with a backslash.
     """
-    field = 'Relnote'
-    regex_relnote = fr'^{field}:.*$'
+    field = "Relnote"
+    regex_relnote = rf"^{field}:.*$"
     check_re_relnote = re.compile(regex_relnote, re.IGNORECASE)
 
     if options.args():
-        raise ValueError(f'commit msg {field} check takes no options')
+        raise ValueError(f"commit msg {field} check takes no options")
 
     # Check 1: Check for possible misspellings of the `Relnote:` field.
 
     # Regex for misspelled fields.
     possible_field_misspells = {
-        'Relnotes', 'ReleaseNote',
-        'Rel-note', 'Rel note',
-        'rel-notes', 'releasenotes',
-        'release-note', 'release-notes',
+        "Relnotes",
+        "ReleaseNote",
+        "Rel-note",
+        "Rel note",
+        "rel-notes",
+        "releasenotes",
+        "release-note",
+        "release-notes",
     }
-    re_possible_field_misspells = '|'.join(possible_field_misspells)
-    regex_field_misspells = fr'^({re_possible_field_misspells}): .*$'
+    re_possible_field_misspells = "|".join(possible_field_misspells)
+    regex_field_misspells = rf"^({re_possible_field_misspells}): .*$"
     check_re_field_misspells = re.compile(regex_field_misspells, re.IGNORECASE)
 
     ret = []
     for line in desc.splitlines():
         if check_re_field_misspells.match(line):
-            error = RELNOTE_MISSPELL_MSG % (regex_relnote, )
+            error = RELNOTE_MISSPELL_MSG % (regex_relnote,)
             ret.append(
                 rh.results.HookResult(
                     f'commit msg: "{field}:" tag spelling error',
-                    project, commit, error=error))
+                    project,
+                    commit,
+                    error=error,
+                )
+            )
 
     # Check 2: Check that multiline Relnotes are quoted.
 
-    check_re_empty_string = re.compile(r'^$')
+    check_re_empty_string = re.compile(r"^$")
 
     # Regex to find other fields that could be used.
-    regex_other_fields = r'^[a-zA-Z0-9-]+:'
+    regex_other_fields = r"^[a-zA-Z0-9-]+:"
     check_re_other_fields = re.compile(regex_other_fields)
 
     desc_lines = desc.splitlines()
     for i, cur_line in enumerate(desc_lines):
         # Look for a Relnote tag that is before the last line and
         # lacking any quotes.
-        if (check_re_relnote.match(cur_line) and
-                i < len(desc_lines) - 1 and
-                '"' not in cur_line):
+        if (
+            check_re_relnote.match(cur_line)
+            and i < len(desc_lines) - 1
+            and '"' not in cur_line
+        ):
             next_line = desc_lines[i + 1]
             # Check that the next line does not contain any other field
             # and it's not an empty string.
-            if (not check_re_other_fields.findall(next_line) and
-                    not check_re_empty_string.match(next_line)):
+            if not check_re_other_fields.findall(
+                next_line
+            ) and not check_re_empty_string.match(next_line):
                 ret.append(
                     rh.results.HookResult(
                         f'commit msg: "{field}:" tag missing quotes',
-                        project, commit, error=RELNOTE_MISSING_QUOTES_MSG))
+                        project,
+                        commit,
+                        error=RELNOTE_MISSING_QUOTES_MSG,
+                    )
+                )
                 break
 
     # Check 3: Check that multiline Relnotes contain matching quotes.
@@ -826,7 +921,11 @@
         ret.append(
             rh.results.HookResult(
                 f'commit msg: "{field}:" tag missing closing quote',
-                project, commit, error=RELNOTE_MISSING_QUOTES_MSG))
+                project,
+                commit,
+                error=RELNOTE_MISSING_QUOTES_MSG,
+            )
+        )
 
     # Check 4: Check that non-starting or non-ending quotes are escaped with a
     # backslash.
@@ -844,8 +943,9 @@
             if '"""' in cur_line:
                 break
         if line_needs_checking:
-            stripped_line = re.sub(fr'^{field}:', '', cur_line,
-                                   flags=re.IGNORECASE).strip()
+            stripped_line = re.sub(
+                rf"^{field}:", "", cur_line, flags=re.IGNORECASE
+            ).strip()
             for i, character in enumerate(stripped_line):
                 if i == 0:
                     # Case 1: Valid quote at the beginning of the
@@ -858,17 +958,24 @@
                         uses_invalid_quotes = True
                         break
                 # Case 3: Check all other cases.
-                if (character == '"'
-                        and 0 < i < len(stripped_line) - 1
-                        and stripped_line[i-1] != '"'
-                        and stripped_line[i-1] != "\\"):
+                if (
+                    character == '"'
+                    and 0 < i < len(stripped_line) - 1
+                    and stripped_line[i - 1] != '"'
+                    and stripped_line[i - 1] != "\\"
+                ):
                     uses_invalid_quotes = True
                     break
 
     if uses_invalid_quotes:
-        ret.append(rh.results.HookResult(
-            f'commit msg: "{field}:" tag using unescaped quotes',
-            project, commit, error=RELNOTE_INVALID_QUOTES_MSG))
+        ret.append(
+            rh.results.HookResult(
+                f'commit msg: "{field}:" tag using unescaped quotes',
+                project,
+                commit,
+                error=RELNOTE_INVALID_QUOTES_MSG,
+            )
+        )
     return ret
 
 
@@ -894,19 +1001,20 @@
 Check the git history for more examples.
 """
 
-def check_commit_msg_relnote_for_current_txt(project, commit, desc, diff,
-                                             options=None):
+
+def check_commit_msg_relnote_for_current_txt(
+    project, commit, desc, diff, options=None
+):
     """Check changes to current.txt contain the 'Relnote:' stanza."""
-    field = 'Relnote'
-    regex = fr'^{field}: .+$'
+    field = "Relnote"
+    regex = rf"^{field}: .+$"
     check_re = re.compile(regex, re.IGNORECASE)
 
     if options.args():
-        raise ValueError(f'commit msg {field} check takes no options')
+        raise ValueError(f"commit msg {field} check takes no options")
 
     filtered = _filter_diff(
-        diff,
-        [r'(^|/)(public_plus_experimental_current|current)\.txt$']
+        diff, [r"(^|/)(public_plus_experimental_current|current)\.txt$"]
     )
     # If the commit does not contain a change to *current.txt, then this repo
     # hook check no longer applies.
@@ -923,50 +1031,60 @@
     else:
         return None
 
-    return [rh.results.HookResult(f'commit msg: "{field}:" check',
-                                  project, commit, error=error)]
+    return [
+        rh.results.HookResult(
+            f'commit msg: "{field}:" check', project, commit, error=error
+        )
+    ]
 
 
 def check_cpplint(project, commit, _desc, diff, options=None):
     """Run cpplint."""
     # This list matches what cpplint expects.  We could run on more (like .cxx),
     # but cpplint would just ignore them.
-    filtered = _filter_diff(diff, [r'\.(cc|h|cpp|cu|cuh)$'])
+    filtered = _filter_diff(diff, [r"\.(cc|h|cpp|cu|cuh)$"])
     if not filtered:
         return None
 
-    cpplint = options.tool_path('cpplint')
-    cmd = [cpplint] + options.args(('${PREUPLOAD_FILES}',), filtered)
-    return _check_cmd('cpplint', project, commit, cmd)
+    cpplint = options.tool_path("cpplint")
+    cmd = [cpplint] + options.args(("${PREUPLOAD_FILES}",), filtered)
+    return _check_cmd("cpplint", project, commit, cmd)
 
 
 def check_gofmt(project, commit, _desc, diff, options=None):
     """Checks that Go files are formatted with gofmt."""
-    filtered = _filter_diff(diff, [r'\.go$'])
+    filtered = _filter_diff(diff, [r"\.go$"])
     if not filtered:
         return None
 
-    gofmt = options.tool_path('gofmt')
-    cmd = [gofmt, '-l'] + options.args()
-    fixup_cmd = [gofmt, '-w'] + options.args()
+    gofmt = options.tool_path("gofmt")
+    cmd = [gofmt, "-l"] + options.args()
+    fixup_cmd = [gofmt, "-w"] + options.args()
 
     ret = []
     for d in filtered:
         data = rh.git.get_file_content(commit, d.file)
         result = _run(cmd, input=data)
         if result.stdout:
-            ret.append(rh.results.HookResult(
-                'gofmt', project, commit, error=result.stdout,
-                files=(d.file,), fixup_cmd=fixup_cmd))
+            ret.append(
+                rh.results.HookResult(
+                    "gofmt",
+                    project,
+                    commit,
+                    error=result.stdout,
+                    files=(d.file,),
+                    fixup_cmd=fixup_cmd,
+                )
+            )
     return ret
 
 
 def check_json(project, commit, _desc, diff, options=None):
     """Verify json files are valid."""
     if options.args():
-        raise ValueError('json check takes no options')
+        raise ValueError("json check takes no options")
 
-    filtered = _filter_diff(diff, [r'\.json$'])
+    filtered = _filter_diff(diff, [r"\.json$"])
     if not filtered:
         return None
 
@@ -976,27 +1094,34 @@
         try:
             json.loads(data)
         except ValueError as e:
-            ret.append(rh.results.HookResult(
-                'json', project, commit, error=str(e),
-                files=(d.file,)))
+            ret.append(
+                rh.results.HookResult(
+                    "json", project, commit, error=str(e), files=(d.file,)
+                )
+            )
     return ret
 
 
 def _check_pylint(project, commit, _desc, diff, extra_args=None, options=None):
     """Run pylint."""
-    filtered = _filter_diff(diff, [r'\.py$'])
+    filtered = _filter_diff(diff, [r"\.py$"])
     if not filtered:
         return None
 
     if extra_args is None:
         extra_args = []
 
-    pylint = options.tool_path('pylint')
-    cmd = [
-        get_helper_path('pylint.py'),
-        '--executable-path', pylint,
-    ] + extra_args + options.args(('${PREUPLOAD_FILES}',), filtered)
-    return _check_cmd('pylint', project, commit, cmd)
+    pylint = options.tool_path("pylint")
+    cmd = (
+        [
+            get_helper_path("pylint.py"),
+            "--executable-path",
+            pylint,
+        ]
+        + extra_args
+        + options.args(("${PREUPLOAD_FILES}",), filtered)
+    )
+    return _check_cmd("pylint", project, commit, cmd)
 
 
 def check_pylint2(project, commit, desc, diff, options=None):
@@ -1006,11 +1131,18 @@
     breaking in older branches with old configs that still have it.
     """
     del desc, diff, options
-    return [rh.results.HookResult(
-        'pylint2', project, commit,
-        ('The pylint2 check is no longer supported.  '
-         'Please delete from PREUPLOAD.cfg.'),
-        warning=True)]
+    return [
+        rh.results.HookResult(
+            "pylint2",
+            project,
+            commit,
+            (
+                "The pylint2 check is no longer supported.  "
+                "Please delete from PREUPLOAD.cfg."
+            ),
+            warning=True,
+        )
+    ]
 
 
 def check_pylint3(project, commit, desc, diff, options=None):
@@ -1020,11 +1152,11 @@
 
 def check_rustfmt(project, commit, _desc, diff, options=None):
     """Run "rustfmt --check" on diffed rust files"""
-    filtered = _filter_diff(diff, [r'\.rs$'])
+    filtered = _filter_diff(diff, [r"\.rs$"])
     if not filtered:
         return None
 
-    rustfmt = options.tool_path('rustfmt')
+    rustfmt = options.tool_path("rustfmt")
     cmd = [rustfmt] + options.args((), filtered)
     ret = []
     for d in filtered:
@@ -1033,144 +1165,173 @@
         # If the parsing failed, stdout will contain enough details on the
         # location of the error.
         if result.returncode:
-            ret.append(rh.results.HookResult(
-                'rustfmt', project, commit, error=result.stdout,
-                files=(d.file,)))
+            ret.append(
+                rh.results.HookResult(
+                    "rustfmt",
+                    project,
+                    commit,
+                    error=result.stdout,
+                    files=(d.file,),
+                )
+            )
             continue
         # TODO(b/164111102): rustfmt stable does not support --check on stdin.
         # If no error is reported, compare stdin with stdout.
         if data != result.stdout:
-            ret.append(rh.results.HookResult(
-                'rustfmt', project, commit, error='Files not formatted',
-                files=(d.file,), fixup_cmd=cmd))
+            ret.append(
+                rh.results.HookResult(
+                    "rustfmt",
+                    project,
+                    commit,
+                    error="Files not formatted",
+                    files=(d.file,),
+                    fixup_cmd=cmd,
+                )
+            )
     return ret
 
 
 def check_xmllint(project, commit, _desc, diff, options=None):
     """Run xmllint."""
     # XXX: Should we drop most of these and probe for <?xml> tags?
-    extensions = frozenset((
-        'dbus-xml',  # Generated DBUS interface.
-        'dia',       # File format for Dia.
-        'dtd',       # Document Type Definition.
-        'fml',       # Fuzzy markup language.
-        'form',      # Forms created by IntelliJ GUI Designer.
-        'fxml',      # JavaFX user interfaces.
-        'glade',     # Glade user interface design.
-        'grd',       # GRIT translation files.
-        'iml',       # Android build modules?
-        'kml',       # Keyhole Markup Language.
-        'mxml',      # Macromedia user interface markup language.
-        'nib',       # OS X Cocoa Interface Builder.
-        'plist',     # Property list (for OS X).
-        'pom',       # Project Object Model (for Apache Maven).
-        'rng',       # RELAX NG schemas.
-        'sgml',      # Standard Generalized Markup Language.
-        'svg',       # Scalable Vector Graphics.
-        'uml',       # Unified Modeling Language.
-        'vcproj',    # Microsoft Visual Studio project.
-        'vcxproj',   # Microsoft Visual Studio project.
-        'wxs',       # WiX Transform File.
-        'xhtml',     # XML HTML.
-        'xib',       # OS X Cocoa Interface Builder.
-        'xlb',       # Android locale bundle.
-        'xml',       # Extensible Markup Language.
-        'xsd',       # XML Schema Definition.
-        'xsl',       # Extensible Stylesheet Language.
-    ))
+    extensions = frozenset(
+        (
+            "dbus-xml",  # Generated DBUS interface.
+            "dia",  # File format for Dia.
+            "dtd",  # Document Type Definition.
+            "fml",  # Fuzzy markup language.
+            "form",  # Forms created by IntelliJ GUI Designer.
+            "fxml",  # JavaFX user interfaces.
+            "glade",  # Glade user interface design.
+            "grd",  # GRIT translation files.
+            "iml",  # Android build modules?
+            "kml",  # Keyhole Markup Language.
+            "mxml",  # Macromedia user interface markup language.
+            "nib",  # OS X Cocoa Interface Builder.
+            "plist",  # Property list (for OS X).
+            "pom",  # Project Object Model (for Apache Maven).
+            "rng",  # RELAX NG schemas.
+            "sgml",  # Standard Generalized Markup Language.
+            "svg",  # Scalable Vector Graphics.
+            "uml",  # Unified Modeling Language.
+            "vcproj",  # Microsoft Visual Studio project.
+            "vcxproj",  # Microsoft Visual Studio project.
+            "wxs",  # WiX Transform File.
+            "xhtml",  # XML HTML.
+            "xib",  # OS X Cocoa Interface Builder.
+            "xlb",  # Android locale bundle.
+            "xml",  # Extensible Markup Language.
+            "xsd",  # XML Schema Definition.
+            "xsl",  # Extensible Stylesheet Language.
+        )
+    )
 
-    filtered = _filter_diff(diff, [r'\.(' + '|'.join(extensions) + r')$'])
+    filtered = _filter_diff(diff, [r"\.(" + "|".join(extensions) + r")$"])
     if not filtered:
         return None
 
     # TODO: Figure out how to integrate schema validation.
     # XXX: Should we use python's XML libs instead?
-    cmd = ['xmllint'] + options.args(('${PREUPLOAD_FILES}',), filtered)
+    cmd = ["xmllint"] + options.args(("${PREUPLOAD_FILES}",), filtered)
 
-    return _check_cmd('xmllint', project, commit, cmd)
+    return _check_cmd("xmllint", project, commit, cmd)
 
 
 def check_android_test_mapping(project, commit, _desc, diff, options=None):
     """Verify Android TEST_MAPPING files are valid."""
     if options.args():
-        raise ValueError('Android TEST_MAPPING check takes no options')
-    filtered = _filter_diff(diff, [r'(^|.*/)TEST_MAPPING$'])
+        raise ValueError("Android TEST_MAPPING check takes no options")
+    filtered = _filter_diff(diff, [r"(^|.*/)TEST_MAPPING$"])
     if not filtered:
         return None
 
-    testmapping_format = options.tool_path('android-test-mapping-format')
-    testmapping_args = ['--commit', commit]
-    cmd = [testmapping_format] + options.args(
-        (project.dir, '${PREUPLOAD_FILES}'), filtered) + testmapping_args
-    return _check_cmd('android-test-mapping-format', project, commit, cmd)
+    testmapping_format = options.tool_path("android-test-mapping-format")
+    testmapping_args = ["--commit", commit]
+    cmd = (
+        [testmapping_format]
+        + options.args((project.dir, "${PREUPLOAD_FILES}"), filtered)
+        + testmapping_args
+    )
+    return _check_cmd("android-test-mapping-format", project, commit, cmd)
 
 
 def check_aidl_format(project, commit, _desc, diff, options=None):
     """Checks that AIDL files are formatted with aidl-format."""
     # All *.aidl files except for those under aidl_api directory.
-    filtered = _filter_diff(diff, [r'\.aidl$'], [r'(^|/)aidl_api/'])
+    filtered = _filter_diff(diff, [r"\.aidl$"], [r"(^|/)aidl_api/"])
     if not filtered:
         return None
-    aidl_format = options.tool_path('aidl-format')
-    clang_format = options.tool_path('clang-format')
-    diff_cmd = [aidl_format, '-d', '--clang-format-path', clang_format] + \
-            options.args((), filtered)
+    aidl_format = options.tool_path("aidl-format")
+    clang_format = options.tool_path("clang-format")
+    diff_cmd = [
+        aidl_format,
+        "-d",
+        "--clang-format-path",
+        clang_format,
+    ] + options.args((), filtered)
     ret = []
     for d in filtered:
         data = rh.git.get_file_content(commit, d.file)
         result = _run(diff_cmd, input=data)
         if result.stdout:
-            fixup_cmd = [aidl_format, '-w', '--clang-format-path', clang_format]
-            ret.append(rh.results.HookResult(
-                'aidl-format', project, commit, error=result.stdout,
-                files=(d.file,), fixup_cmd=fixup_cmd))
+            fixup_cmd = [aidl_format, "-w", "--clang-format-path", clang_format]
+            ret.append(
+                rh.results.HookResult(
+                    "aidl-format",
+                    project,
+                    commit,
+                    error=result.stdout,
+                    files=(d.file,),
+                    fixup_cmd=fixup_cmd,
+                )
+            )
     return ret
 
 
 # Hooks that projects can opt into.
 # Note: Make sure to keep the top level README.md up to date when adding more!
 BUILTIN_HOOKS = {
-    'aidl_format': check_aidl_format,
-    'android_test_mapping_format': check_android_test_mapping,
-    'aosp_license': check_aosp_license,
-    'black': check_black,
-    'bpfmt': check_bpfmt,
-    'checkpatch': check_checkpatch,
-    'clang_format': check_clang_format,
-    'commit_msg_bug_field': check_commit_msg_bug_field,
-    'commit_msg_changeid_field': check_commit_msg_changeid_field,
-    'commit_msg_prebuilt_apk_fields': check_commit_msg_prebuilt_apk_fields,
-    'commit_msg_relnote_field_format': check_commit_msg_relnote_field_format,
-    'commit_msg_relnote_for_current_txt':
-        check_commit_msg_relnote_for_current_txt,
-    'commit_msg_test_field': check_commit_msg_test_field,
-    'cpplint': check_cpplint,
-    'gofmt': check_gofmt,
-    'google_java_format': check_google_java_format,
-    'jsonlint': check_json,
-    'ktfmt': check_ktfmt,
-    'pylint': check_pylint3,
-    'pylint2': check_pylint2,
-    'pylint3': check_pylint3,
-    'rustfmt': check_rustfmt,
-    'xmllint': check_xmllint,
+    "aidl_format": check_aidl_format,
+    "android_test_mapping_format": check_android_test_mapping,
+    "aosp_license": check_aosp_license,
+    "black": check_black,
+    "bpfmt": check_bpfmt,
+    "checkpatch": check_checkpatch,
+    "clang_format": check_clang_format,
+    "commit_msg_bug_field": check_commit_msg_bug_field,
+    "commit_msg_changeid_field": check_commit_msg_changeid_field,
+    "commit_msg_prebuilt_apk_fields": check_commit_msg_prebuilt_apk_fields,
+    "commit_msg_relnote_field_format": check_commit_msg_relnote_field_format,
+    "commit_msg_relnote_for_current_txt": check_commit_msg_relnote_for_current_txt,
+    "commit_msg_test_field": check_commit_msg_test_field,
+    "cpplint": check_cpplint,
+    "gofmt": check_gofmt,
+    "google_java_format": check_google_java_format,
+    "jsonlint": check_json,
+    "ktfmt": check_ktfmt,
+    "pylint": check_pylint3,
+    "pylint2": check_pylint2,
+    "pylint3": check_pylint3,
+    "rustfmt": check_rustfmt,
+    "xmllint": check_xmllint,
 }
 
 # Additional tools that the hooks can call with their default values.
 # Note: Make sure to keep the top level README.md up to date when adding more!
 TOOL_PATHS = {
-    'aidl-format': 'aidl-format',
-    'android-test-mapping-format':
-        os.path.join(TOOLS_DIR, 'android_test_mapping_format.py'),
-    'black': 'black',
-    'bpfmt': 'bpfmt',
-    'clang-format': 'clang-format',
-    'cpplint': os.path.join(TOOLS_DIR, 'cpplint.py'),
-    'git-clang-format': 'git-clang-format',
-    'gofmt': 'gofmt',
-    'google-java-format': 'google-java-format',
-    'google-java-format-diff': 'google-java-format-diff.py',
-    'ktfmt': 'ktfmt',
-    'pylint': 'pylint',
-    'rustfmt': 'rustfmt',
+    "aidl-format": "aidl-format",
+    "android-test-mapping-format": os.path.join(
+        TOOLS_DIR, "android_test_mapping_format.py"
+    ),
+    "black": "black",
+    "bpfmt": "bpfmt",
+    "clang-format": "clang-format",
+    "cpplint": os.path.join(TOOLS_DIR, "cpplint.py"),
+    "git-clang-format": "git-clang-format",
+    "gofmt": "gofmt",
+    "google-java-format": "google-java-format",
+    "google-java-format-diff": "google-java-format-diff.py",
+    "ktfmt": "ktfmt",
+    "pylint": "pylint",
+    "rustfmt": "rustfmt",
 }
diff --git a/rh/hooks_unittest.py b/rh/hooks_unittest.py
index 47e14d2..77a1880 100755
--- a/rh/hooks_unittest.py
+++ b/rh/hooks_unittest.py
@@ -22,7 +22,7 @@
 
 import pytest
 
-_path = os.path.realpath(__file__ + '/../..')
+_path = os.path.realpath(__file__ + "/../..")
 if sys.path[0] != _path:
     sys.path.insert(0, _path)
 del _path
@@ -37,7 +37,7 @@
 
 # pylint: disable=unused-argument
 def mock_find_repo_root(path=None, outer=False):
-    return '/ ${BUILD_OS}' if outer else '/ ${BUILD_OS}/sub'
+    return "/ ${BUILD_OS}" if outer else "/ ${BUILD_OS}/sub"
 
 
 class HooksDocsTests(unittest.TestCase):
@@ -48,47 +48,60 @@
     """
 
     def setUp(self):
-        self.readme = os.path.join(os.path.dirname(os.path.dirname(
-            os.path.realpath(__file__))), 'README.md')
+        self.readme = os.path.join(
+            os.path.dirname(os.path.dirname(os.path.realpath(__file__))),
+            "README.md",
+        )
 
     def _grab_section(self, section):
         """Extract the |section| text out of the readme."""
         ret = []
         in_section = False
-        with open(self.readme, encoding='utf-8') as fp:
+        with open(self.readme, encoding="utf-8") as fp:
             for line in fp:
                 if not in_section:
                     # Look for the section like "## [Tool Paths]".
-                    if (line.startswith('#') and
-                            line.lstrip('#').strip() == section):
+                    if (
+                        line.startswith("#")
+                        and line.lstrip("#").strip() == section
+                    ):
                         in_section = True
                 else:
                     # Once we hit the next section (higher or lower), break.
-                    if line[0] == '#':
+                    if line[0] == "#":
                         break
                     ret.append(line)
-        return ''.join(ret)
+        return "".join(ret)
 
     def testBuiltinHooks(self):
         """Verify builtin hooks are documented."""
-        data = self._grab_section('[Builtin Hooks]')
+        data = self._grab_section("[Builtin Hooks]")
         for hook in rh.hooks.BUILTIN_HOOKS:
-            self.assertIn(f'* `{hook}`:', data,
-                          msg=f'README.md missing docs for hook "{hook}"')
+            self.assertIn(
+                f"* `{hook}`:",
+                data,
+                msg=f'README.md missing docs for hook "{hook}"',
+            )
 
     def testToolPaths(self):
         """Verify tools are documented."""
-        data = self._grab_section('[Tool Paths]')
+        data = self._grab_section("[Tool Paths]")
         for tool in rh.hooks.TOOL_PATHS:
-            self.assertIn(f'* `{tool}`:', data,
-                          msg=f'README.md missing docs for tool "{tool}"')
+            self.assertIn(
+                f"* `{tool}`:",
+                data,
+                msg=f'README.md missing docs for tool "{tool}"',
+            )
 
     def testPlaceholders(self):
         """Verify placeholder replacement vars are documented."""
-        data = self._grab_section('Placeholders')
+        data = self._grab_section("Placeholders")
         for var in rh.hooks.Placeholders.vars():
-            self.assertIn('* `${' + var + '}`:', data,
-                          msg=f'README.md missing docs for var "{var}"')
+            self.assertIn(
+                "* `${" + var + "}`:",
+                data,
+                msg=f'README.md missing docs for var "{var}"',
+            )
 
 
 class PlaceholderTests(unittest.TestCase):
@@ -96,13 +109,18 @@
 
     def setUp(self):
         self._saved_environ = os.environ.copy()
-        os.environ.update({
-            'PREUPLOAD_COMMIT_MESSAGE': 'commit message',
-            'PREUPLOAD_COMMIT': '5c4c293174bb61f0f39035a71acd9084abfa743d',
-        })
+        os.environ.update(
+            {
+                "PREUPLOAD_COMMIT_MESSAGE": "commit message",
+                "PREUPLOAD_COMMIT": "5c4c293174bb61f0f39035a71acd9084abfa743d",
+            }
+        )
         self.replacer = rh.hooks.Placeholders(
-            [rh.git.RawDiffEntry(file=x)
-             for x in ['path1/file1', 'path2/file2']])
+            [
+                rh.git.RawDiffEntry(file=x)
+                for x in ["path1/file1", "path2/file2"]
+            ]
+        )
 
     def tearDown(self):
         os.environ.clear()
@@ -112,104 +130,116 @@
         """Light test for the vars inspection generator."""
         ret = list(self.replacer.vars())
         self.assertGreater(len(ret), 4)
-        self.assertIn('PREUPLOAD_COMMIT', ret)
+        self.assertIn("PREUPLOAD_COMMIT", ret)
 
-    @mock.patch.object(rh.git, 'find_repo_root',
-                       side_effect=mock_find_repo_root)
+    @mock.patch.object(
+        rh.git, "find_repo_root", side_effect=mock_find_repo_root
+    )
     def testExpandVars(self, _m):
         """Verify the replacement actually works."""
         input_args = [
             # Verify ${REPO_ROOT} is updated, but not REPO_ROOT.
             # We also make sure that things in ${REPO_ROOT} are not double
             # expanded (which is why the return includes ${BUILD_OS}).
-            '${REPO_ROOT}/some/prog/REPO_ROOT/ok',
+            "${REPO_ROOT}/some/prog/REPO_ROOT/ok",
             # Verify that ${REPO_OUTER_ROOT} is expanded.
-            '${REPO_OUTER_ROOT}/some/prog/REPO_OUTER_ROOT/ok',
+            "${REPO_OUTER_ROOT}/some/prog/REPO_OUTER_ROOT/ok",
             # Verify lists are merged rather than inserted.
-            '${PREUPLOAD_FILES}',
+            "${PREUPLOAD_FILES}",
             # Verify each file is preceded with '--file=' prefix.
-            '--file=${PREUPLOAD_FILES_PREFIXED}',
+            "--file=${PREUPLOAD_FILES_PREFIXED}",
             # Verify each file is preceded with '--file' argument.
-            '--file',
-            '${PREUPLOAD_FILES_PREFIXED}',
+            "--file",
+            "${PREUPLOAD_FILES_PREFIXED}",
             # Verify values with whitespace don't expand into multiple args.
-            '${PREUPLOAD_COMMIT_MESSAGE}',
+            "${PREUPLOAD_COMMIT_MESSAGE}",
             # Verify multiple values get replaced.
-            '${PREUPLOAD_COMMIT}^${PREUPLOAD_COMMIT_MESSAGE}',
+            "${PREUPLOAD_COMMIT}^${PREUPLOAD_COMMIT_MESSAGE}",
             # Unknown vars should be left alone.
-            '${THIS_VAR_IS_GOOD}',
+            "${THIS_VAR_IS_GOOD}",
         ]
         output_args = self.replacer.expand_vars(input_args)
         exp_args = [
-            '/ ${BUILD_OS}/sub/some/prog/REPO_ROOT/ok',
-            '/ ${BUILD_OS}/some/prog/REPO_OUTER_ROOT/ok',
-            'path1/file1',
-            'path2/file2',
-            '--file=path1/file1',
-            '--file=path2/file2',
-            '--file',
-            'path1/file1',
-            '--file',
-            'path2/file2',
-            'commit message',
-            '5c4c293174bb61f0f39035a71acd9084abfa743d^commit message',
-            '${THIS_VAR_IS_GOOD}',
+            "/ ${BUILD_OS}/sub/some/prog/REPO_ROOT/ok",
+            "/ ${BUILD_OS}/some/prog/REPO_OUTER_ROOT/ok",
+            "path1/file1",
+            "path2/file2",
+            "--file=path1/file1",
+            "--file=path2/file2",
+            "--file",
+            "path1/file1",
+            "--file",
+            "path2/file2",
+            "commit message",
+            "5c4c293174bb61f0f39035a71acd9084abfa743d^commit message",
+            "${THIS_VAR_IS_GOOD}",
         ]
         self.assertEqual(output_args, exp_args)
 
     def testTheTester(self):
         """Make sure we have a test for every variable."""
         for var in self.replacer.vars():
-            self.assertIn(f'test{var}', dir(self),
-                          msg=f'Missing unittest for variable {var}')
+            self.assertIn(
+                f"test{var}",
+                dir(self),
+                msg=f"Missing unittest for variable {var}",
+            )
 
     def testPREUPLOAD_COMMIT_MESSAGE(self):
         """Verify handling of PREUPLOAD_COMMIT_MESSAGE."""
-        self.assertEqual(self.replacer.get('PREUPLOAD_COMMIT_MESSAGE'),
-                         'commit message')
+        self.assertEqual(
+            self.replacer.get("PREUPLOAD_COMMIT_MESSAGE"), "commit message"
+        )
 
     def testPREUPLOAD_COMMIT(self):
         """Verify handling of PREUPLOAD_COMMIT."""
-        self.assertEqual(self.replacer.get('PREUPLOAD_COMMIT'),
-                         '5c4c293174bb61f0f39035a71acd9084abfa743d')
+        self.assertEqual(
+            self.replacer.get("PREUPLOAD_COMMIT"),
+            "5c4c293174bb61f0f39035a71acd9084abfa743d",
+        )
 
     def testPREUPLOAD_FILES(self):
         """Verify handling of PREUPLOAD_FILES."""
-        self.assertEqual(self.replacer.get('PREUPLOAD_FILES'),
-                         ['path1/file1', 'path2/file2'])
+        self.assertEqual(
+            self.replacer.get("PREUPLOAD_FILES"), ["path1/file1", "path2/file2"]
+        )
 
-    @mock.patch.object(rh.git, 'find_repo_root')
+    @mock.patch.object(rh.git, "find_repo_root")
     def testREPO_OUTER_ROOT(self, m):
         """Verify handling of REPO_OUTER_ROOT."""
         m.side_effect = mock_find_repo_root
-        self.assertEqual(self.replacer.get('REPO_OUTER_ROOT'),
-                         mock_find_repo_root(path=None, outer=True))
+        self.assertEqual(
+            self.replacer.get("REPO_OUTER_ROOT"),
+            mock_find_repo_root(path=None, outer=True),
+        )
 
-    @mock.patch.object(rh.git, 'find_repo_root')
+    @mock.patch.object(rh.git, "find_repo_root")
     def testREPO_ROOT(self, m):
         """Verify handling of REPO_ROOT."""
         m.side_effect = mock_find_repo_root
-        self.assertEqual(self.replacer.get('REPO_ROOT'),
-                         mock_find_repo_root(path=None, outer=False))
+        self.assertEqual(
+            self.replacer.get("REPO_ROOT"),
+            mock_find_repo_root(path=None, outer=False),
+        )
 
     def testREPO_PATH(self):
         """Verify handling of REPO_PATH."""
-        os.environ['REPO_PATH'] = ''
-        self.assertEqual(self.replacer.get('REPO_PATH'), '')
-        os.environ['REPO_PATH'] = 'foo/bar'
-        self.assertEqual(self.replacer.get('REPO_PATH'), 'foo/bar')
+        os.environ["REPO_PATH"] = ""
+        self.assertEqual(self.replacer.get("REPO_PATH"), "")
+        os.environ["REPO_PATH"] = "foo/bar"
+        self.assertEqual(self.replacer.get("REPO_PATH"), "foo/bar")
 
     def testREPO_PROJECT(self):
         """Verify handling of REPO_PROJECT."""
-        os.environ['REPO_PROJECT'] = ''
-        self.assertEqual(self.replacer.get('REPO_PROJECT'), '')
-        os.environ['REPO_PROJECT'] = 'platform/foo/bar'
-        self.assertEqual(self.replacer.get('REPO_PROJECT'), 'platform/foo/bar')
+        os.environ["REPO_PROJECT"] = ""
+        self.assertEqual(self.replacer.get("REPO_PROJECT"), "")
+        os.environ["REPO_PROJECT"] = "platform/foo/bar"
+        self.assertEqual(self.replacer.get("REPO_PROJECT"), "platform/foo/bar")
 
-    @mock.patch.object(rh.hooks, '_get_build_os_name', return_value='vapier os')
+    @mock.patch.object(rh.hooks, "_get_build_os_name", return_value="vapier os")
     def testBUILD_OS(self, m):
         """Verify handling of BUILD_OS."""
-        self.assertEqual(self.replacer.get('BUILD_OS'), m.return_value)
+        self.assertEqual(self.replacer.get("BUILD_OS"), m.return_value)
 
 
 class ExclusionScopeTests(unittest.TestCase):
@@ -218,65 +248,68 @@
     def testEmpty(self):
         """Verify the in operator for an empty scope."""
         scope = rh.hooks.ExclusionScope([])
-        self.assertNotIn('external/*', scope)
+        self.assertNotIn("external/*", scope)
 
     def testGlob(self):
         """Verify the in operator for a scope using wildcards."""
-        scope = rh.hooks.ExclusionScope(['vendor/*', 'external/*'])
-        self.assertIn('external/tools', scope)
+        scope = rh.hooks.ExclusionScope(["vendor/*", "external/*"])
+        self.assertIn("external/tools", scope)
 
     def testRegex(self):
         """Verify the in operator for a scope using regular expressions."""
-        scope = rh.hooks.ExclusionScope(['^vendor/(?!google)',
-                                         'external/*'])
-        self.assertIn('vendor/', scope)
-        self.assertNotIn('vendor/google/', scope)
-        self.assertIn('vendor/other/', scope)
+        scope = rh.hooks.ExclusionScope(["^vendor/(?!google)", "external/*"])
+        self.assertIn("vendor/", scope)
+        self.assertNotIn("vendor/google/", scope)
+        self.assertIn("vendor/other/", scope)
 
 
 class HookOptionsTests(unittest.TestCase):
     """Verify behavior of HookOptions object."""
 
     @pytest.mark.skip_cq("TODO: Relies on .repo dir")
-    @mock.patch.object(rh.hooks, '_get_build_os_name', return_value='vapier os')
+    @mock.patch.object(rh.hooks, "_get_build_os_name", return_value="vapier os")
     def testExpandVars(self, m):
         """Verify expand_vars behavior."""
         # Simple pass through.
-        args = ['who', 'goes', 'there ?']
+        args = ["who", "goes", "there ?"]
         self.assertEqual(args, rh.hooks.HookOptions.expand_vars(args))
 
         # At least one replacement.  Most real testing is in PlaceholderTests.
-        args = ['who', 'goes', 'there ?', '${BUILD_OS} is great']
-        exp_args = ['who', 'goes', 'there ?', f'{m.return_value} is great']
+        args = ["who", "goes", "there ?", "${BUILD_OS} is great"]
+        exp_args = ["who", "goes", "there ?", f"{m.return_value} is great"]
         self.assertEqual(exp_args, rh.hooks.HookOptions.expand_vars(args))
 
     @pytest.mark.skip_cq("TODO: Relies on .repo dir")
     def testArgs(self):
         """Verify args behavior."""
         # Verify initial args to __init__ has higher precedent.
-        args = ['start', 'args']
-        options = rh.hooks.HookOptions('hook name', args, {})
+        args = ["start", "args"]
+        options = rh.hooks.HookOptions("hook name", args, {})
         self.assertEqual(options.args(), args)
-        self.assertEqual(options.args(default_args=['moo']), args)
+        self.assertEqual(options.args(default_args=["moo"]), args)
 
         # Verify we fall back to default_args.
-        args = ['default', 'args']
-        options = rh.hooks.HookOptions('hook name', [], {})
+        args = ["default", "args"]
+        options = rh.hooks.HookOptions("hook name", [], {})
         self.assertEqual(options.args(), [])
         self.assertEqual(options.args(default_args=args), args)
 
     @pytest.mark.skip_cq("TODO: Relies on .repo dir")
     def testToolPath(self):
         """Verify tool_path behavior."""
-        options = rh.hooks.HookOptions('hook name', [], {
-            'cpplint': 'my cpplint',
-        })
+        options = rh.hooks.HookOptions(
+            "hook name",
+            [],
+            {
+                "cpplint": "my cpplint",
+            },
+        )
         # Check a builtin (and not overridden) tool.
-        self.assertEqual(options.tool_path('pylint'), 'pylint')
+        self.assertEqual(options.tool_path("pylint"), "pylint")
         # Check an overridden tool.
-        self.assertEqual(options.tool_path('cpplint'), 'my cpplint')
+        self.assertEqual(options.tool_path("cpplint"), "my cpplint")
         # Check an unknown tool fails.
-        self.assertRaises(AssertionError, options.tool_path, 'extra_tool')
+        self.assertRaises(AssertionError, options.tool_path, "extra_tool")
 
 
 class UtilsTests(unittest.TestCase):
@@ -286,7 +319,7 @@
         """Check _run behavior."""
         # Most testing is done against the utils.RunCommand already.
         # pylint: disable=protected-access
-        ret = rh.hooks._run(['true'])
+        ret = rh.hooks._run(["true"])
         self.assertEqual(ret.returncode, 0)
 
     def testBuildOs(self):
@@ -295,14 +328,14 @@
         # pylint: disable=protected-access
         ret = rh.hooks._get_build_os_name()
         self.assertTrue(isinstance(ret, str))
-        self.assertNotEqual(ret, '')
+        self.assertNotEqual(ret, "")
 
     def testGetHelperPath(self):
         """Check get_helper_path behavior."""
         # Just verify it doesn't crash.  It's a dirt simple func.
-        ret = rh.hooks.get_helper_path('booga')
+        ret = rh.hooks.get_helper_path("booga")
         self.assertTrue(isinstance(ret, str))
-        self.assertNotEqual(ret, '')
+        self.assertNotEqual(ret, "")
 
     def testSortedToolPaths(self):
         """Check TOOL_PATHS is sorted."""
@@ -317,17 +350,18 @@
         # order which Python 3.7+ has codified.
         # https://docs.python.org/3.7/library/stdtypes.html#dict
         self.assertEqual(
-            list(rh.hooks.BUILTIN_HOOKS), sorted(rh.hooks.BUILTIN_HOOKS))
+            list(rh.hooks.BUILTIN_HOOKS), sorted(rh.hooks.BUILTIN_HOOKS)
+        )
 
 
-@mock.patch.object(rh.utils, 'run')
-@mock.patch.object(rh.hooks, '_check_cmd', return_value=['check_cmd'])
+@mock.patch.object(rh.utils, "run")
+@mock.patch.object(rh.hooks, "_check_cmd", return_value=["check_cmd"])
 class BuiltinHooksTests(unittest.TestCase):
     """Verify the builtin hooks."""
 
     def setUp(self):
-        self.project = rh.Project(name='project-name', dir='/.../repo/dir')
-        self.options = rh.hooks.HookOptions('hook name', [], {})
+        self.project = rh.Project(name="project-name", dir="/.../repo/dir")
+        self.options = rh.hooks.HookOptions("hook name", [], {})
 
     def _test_commit_messages(self, func, accept, msgs, files=None):
         """Helper for testing commit message hooks.
@@ -343,13 +377,15 @@
         else:
             diff = []
         for desc in msgs:
-            ret = func(self.project, 'commit', desc, diff, options=self.options)
+            ret = func(self.project, "commit", desc, diff, options=self.options)
             if accept:
                 self.assertFalse(
-                    bool(ret), msg='Should have accepted: {{{' + desc + '}}}')
+                    bool(ret), msg="Should have accepted: {{{" + desc + "}}}"
+                )
             else:
                 self.assertTrue(
-                    bool(ret), msg='Should have rejected: {{{' + desc + '}}}')
+                    bool(ret), msg="Should have rejected: {{{" + desc + "}}}"
+                )
 
     def _test_file_filter(self, mock_check, func, files):
         """Helper for testing hooks that filter by files and run external tools.
@@ -360,62 +396,74 @@
           files: A list of files that we'd check.
         """
         # First call should do nothing as there are no files to check.
-        ret = func(self.project, 'commit', 'desc', (), options=self.options)
+        ret = func(self.project, "commit", "desc", (), options=self.options)
         self.assertIsNone(ret)
         self.assertFalse(mock_check.called)
 
         # Second call should include some checks.
         diff = [rh.git.RawDiffEntry(file=x) for x in files]
-        ret = func(self.project, 'commit', 'desc', diff, options=self.options)
+        ret = func(self.project, "commit", "desc", diff, options=self.options)
         self.assertEqual(ret, mock_check.return_value)
 
     def testTheTester(self, _mock_check, _mock_run):
         """Make sure we have a test for every hook."""
         for hook in rh.hooks.BUILTIN_HOOKS:
-            self.assertIn(f'test_{hook}', dir(self),
-                          msg=f'Missing unittest for builtin hook {hook}')
+            self.assertIn(
+                f"test_{hook}",
+                dir(self),
+                msg=f"Missing unittest for builtin hook {hook}",
+            )
 
     def test_aosp_license(self, mock_check, _mock_run):
         """Verify the aosp_license builtin hook."""
         # First call should do nothing as there are no files to check.
         diff = [
-            rh.git.RawDiffEntry(file='d.bp', status='D'),
-            rh.git.RawDiffEntry(file='m.bp', status='M'),
-            rh.git.RawDiffEntry(file='non-interested', status='A'),
+            rh.git.RawDiffEntry(file="d.bp", status="D"),
+            rh.git.RawDiffEntry(file="m.bp", status="M"),
+            rh.git.RawDiffEntry(file="non-interested", status="A"),
         ]
         ret = rh.hooks.check_aosp_license(
-            self.project, 'commit', 'desc', diff, options=self.options)
+            self.project, "commit", "desc", diff, options=self.options
+        )
         self.assertIsNone(ret)
         self.assertFalse(mock_check.called)
 
         # Second call will have some results.
         diff = [
-            rh.git.RawDiffEntry(file='a.bp', status='A'),
+            rh.git.RawDiffEntry(file="a.bp", status="A"),
         ]
         ret = rh.hooks.check_aosp_license(
-            self.project, 'commit', 'desc', diff, options=self.options)
+            self.project, "commit", "desc", diff, options=self.options
+        )
         self.assertIsNotNone(ret)
 
         # No result since all paths are excluded.
         diff = [
-            rh.git.RawDiffEntry(file='a/a.bp', status='A'),
-            rh.git.RawDiffEntry(file='b/a.bp', status='A'),
-            rh.git.RawDiffEntry(file='c/d/a.bp', status='A'),
+            rh.git.RawDiffEntry(file="a/a.bp", status="A"),
+            rh.git.RawDiffEntry(file="b/a.bp", status="A"),
+            rh.git.RawDiffEntry(file="c/d/a.bp", status="A"),
         ]
         ret = rh.hooks.check_aosp_license(
-            self.project, 'commit', 'desc', diff,
-            options=rh.hooks.HookOptions('hook name',
-                ['--exclude-dirs=a,b', '--exclude-dirs=c/d'], {})
+            self.project,
+            "commit",
+            "desc",
+            diff,
+            options=rh.hooks.HookOptions(
+                "hook name", ["--exclude-dirs=a,b", "--exclude-dirs=c/d"], {}
+            ),
         )
         self.assertIsNone(ret)
 
         # Make sure that `--exclude-dir` doesn't match the path in the middle.
         diff = [
-            rh.git.RawDiffEntry(file='a/b/c.bp', status='A'),
+            rh.git.RawDiffEntry(file="a/b/c.bp", status="A"),
         ]
         ret = rh.hooks.check_aosp_license(
-            self.project, 'commit', 'desc', diff,
-            options=rh.hooks.HookOptions('hook name', ['--exclude-dirs=b'], {})
+            self.project,
+            "commit",
+            "desc",
+            diff,
+            options=rh.hooks.HookOptions("hook name", ["--exclude-dirs=b"], {}),
         )
         self.assertIsNotNone(ret)
 
@@ -423,14 +471,16 @@
         """Verify the black builtin hook."""
         # First call should do nothing as there are no files to check.
         ret = rh.hooks.check_black(
-            self.project, 'commit', 'desc', (), options=self.options)
+            self.project, "commit", "desc", (), options=self.options
+        )
         self.assertIsNone(ret)
         self.assertFalse(mock_check.called)
 
         # Second call will have some results.
-        diff = [rh.git.RawDiffEntry(file='main.py')]
+        diff = [rh.git.RawDiffEntry(file="main.py")]
         ret = rh.hooks.check_black(
-            self.project, 'commit', 'desc', diff, options=self.options)
+            self.project, "commit", "desc", diff, options=self.options
+        )
         self.assertIsNotNone(ret)
         for result in ret:
             self.assertIsNotNone(result.fixup_cmd)
@@ -439,14 +489,16 @@
         """Verify the bpfmt builtin hook."""
         # First call should do nothing as there are no files to check.
         ret = rh.hooks.check_bpfmt(
-            self.project, 'commit', 'desc', (), options=self.options)
+            self.project, "commit", "desc", (), options=self.options
+        )
         self.assertIsNone(ret)
         self.assertFalse(mock_check.called)
 
         # Second call will have some results.
-        diff = [rh.git.RawDiffEntry(file='Android.bp')]
+        diff = [rh.git.RawDiffEntry(file="Android.bp")]
         ret = rh.hooks.check_bpfmt(
-            self.project, 'commit', 'desc', diff, options=self.options)
+            self.project, "commit", "desc", diff, options=self.options
+        )
         self.assertIsNotNone(ret)
         for result in ret:
             self.assertIsNotNone(result.fixup_cmd)
@@ -454,74 +506,97 @@
     def test_checkpatch(self, mock_check, _mock_run):
         """Verify the checkpatch builtin hook."""
         ret = rh.hooks.check_checkpatch(
-            self.project, 'commit', 'desc', (), options=self.options)
+            self.project, "commit", "desc", (), options=self.options
+        )
         self.assertEqual(ret, mock_check.return_value)
 
     def test_clang_format(self, mock_check, _mock_run):
         """Verify the clang_format builtin hook."""
         ret = rh.hooks.check_clang_format(
-            self.project, 'commit', 'desc', (), options=self.options)
+            self.project, "commit", "desc", (), options=self.options
+        )
         self.assertEqual(ret, mock_check.return_value)
 
     def test_google_java_format(self, mock_check, _mock_run):
         """Verify the google_java_format builtin hook."""
         # First call should do nothing as there are no files to check.
         ret = rh.hooks.check_google_java_format(
-            self.project, 'commit', 'desc', (), options=self.options)
+            self.project, "commit", "desc", (), options=self.options
+        )
         self.assertIsNone(ret)
         self.assertFalse(mock_check.called)
         # Check that .java files are included by default.
-        diff = [rh.git.RawDiffEntry(file='foo.java'),
-                rh.git.RawDiffEntry(file='bar.kt'),
-                rh.git.RawDiffEntry(file='baz/blah.java')]
+        diff = [
+            rh.git.RawDiffEntry(file="foo.java"),
+            rh.git.RawDiffEntry(file="bar.kt"),
+            rh.git.RawDiffEntry(file="baz/blah.java"),
+        ]
         ret = rh.hooks.check_google_java_format(
-            self.project, 'commit', 'desc', diff, options=self.options)
-        self.assertListEqual(ret[0].files, ['foo.java', 'baz/blah.java'])
-        diff = [rh.git.RawDiffEntry(file='foo/f1.java'),
-                rh.git.RawDiffEntry(file='bar/f2.java'),
-                rh.git.RawDiffEntry(file='baz/f2.java')]
+            self.project, "commit", "desc", diff, options=self.options
+        )
+        self.assertListEqual(ret[0].files, ["foo.java", "baz/blah.java"])
+        diff = [
+            rh.git.RawDiffEntry(file="foo/f1.java"),
+            rh.git.RawDiffEntry(file="bar/f2.java"),
+            rh.git.RawDiffEntry(file="baz/f2.java"),
+        ]
         ret = rh.hooks.check_google_java_format(
-            self.project, 'commit', 'desc', diff,
-            options=rh.hooks.HookOptions('hook name',
-            ['--include-dirs=foo,baz'], {}))
-        self.assertListEqual(ret[0].files, ['foo/f1.java', 'baz/f2.java'])
+            self.project,
+            "commit",
+            "desc",
+            diff,
+            options=rh.hooks.HookOptions(
+                "hook name", ["--include-dirs=foo,baz"], {}
+            ),
+        )
+        self.assertListEqual(ret[0].files, ["foo/f1.java", "baz/f2.java"])
 
     def test_commit_msg_bug_field(self, _mock_check, _mock_run):
         """Verify the commit_msg_bug_field builtin hook."""
         # Check some good messages.
         self._test_commit_messages(
-            rh.hooks.check_commit_msg_bug_field, True, (
-                'subj\n\nBug: 1234\n',
-                'subj\n\nBug: 1234\nChange-Id: blah\n',
-                'subj\n\nFix: 1234\n',
-            ))
+            rh.hooks.check_commit_msg_bug_field,
+            True,
+            (
+                "subj\n\nBug: 1234\n",
+                "subj\n\nBug: 1234\nChange-Id: blah\n",
+                "subj\n\nFix: 1234\n",
+            ),
+        )
 
         # Check some bad messages.
         self._test_commit_messages(
-            rh.hooks.check_commit_msg_bug_field, False, (
-                'subj',
-                'subj\n\nBUG=1234\n',
-                'subj\n\nBUG: 1234\n',
-                'subj\n\nBug: N/A\n',
-                'subj\n\nBug:\n',
-                'subj\n\nFIX=1234\n',
-            ))
+            rh.hooks.check_commit_msg_bug_field,
+            False,
+            (
+                "subj",
+                "subj\n\nBUG=1234\n",
+                "subj\n\nBUG: 1234\n",
+                "subj\n\nBug: N/A\n",
+                "subj\n\nBug:\n",
+                "subj\n\nFIX=1234\n",
+            ),
+        )
 
     def test_commit_msg_changeid_field(self, _mock_check, _mock_run):
         """Verify the commit_msg_changeid_field builtin hook."""
         # Check some good messages.
         self._test_commit_messages(
-            rh.hooks.check_commit_msg_changeid_field, True, (
-                'subj\n\nChange-Id: I1234\n',
-            ))
+            rh.hooks.check_commit_msg_changeid_field,
+            True,
+            ("subj\n\nChange-Id: I1234\n",),
+        )
 
         # Check some bad messages.
         self._test_commit_messages(
-            rh.hooks.check_commit_msg_changeid_field, False, (
-                'subj',
-                'subj\n\nChange-Id: 1234\n',
-                'subj\n\nChange-ID: I1234\n',
-            ))
+            rh.hooks.check_commit_msg_changeid_field,
+            False,
+            (
+                "subj",
+                "subj\n\nChange-Id: 1234\n",
+                "subj\n\nChange-ID: I1234\n",
+            ),
+        )
 
     def test_commit_msg_prebuilt_apk_fields(self, _mock_check, _mock_run):
         """Verify the check_commit_msg_prebuilt_apk_fields builtin hook."""
@@ -529,10 +604,11 @@
         self._test_commit_messages(
             rh.hooks.check_commit_msg_prebuilt_apk_fields,
             True,
-            (
-                'subj\nTest: test case\nBug: bug id\n',
-            ),
-            ['foo.cpp', 'bar.py',]
+            ("subj\nTest: test case\nBug: bug id\n",),
+            [
+                "foo.cpp",
+                "bar.py",
+            ],
         )
 
         # Commits with APKs and all the required messages should pass.
@@ -540,41 +616,53 @@
             rh.hooks.check_commit_msg_prebuilt_apk_fields,
             True,
             (
-                ('Test App\n\nbar.apk\npackage: name=\'com.foo.bar\'\n'
-                 'versionCode=\'1001\'\nversionName=\'1.0.1001-A\'\n'
-                 'platformBuildVersionName=\'\'\ncompileSdkVersion=\'28\'\n'
-                 'compileSdkVersionCodename=\'9\'\nsdkVersion:\'16\'\n'
-                 'targetSdkVersion:\'28\'\n\nBuilt here:\n'
-                 'http://foo.bar.com/builder\n\n'
-                 'This build IS suitable for public release.\n\n'
-                 'Bug: 123\nTest: test\nChange-Id: XXXXXXX\n'),
-                ('Test App\n\nBuilt here:\nhttp://foo.bar.com/builder\n\n'
-                 'This build IS NOT suitable for public release.\n\n'
-                 'bar.apk\npackage: name=\'com.foo.bar\'\n'
-                 'versionCode=\'1001\'\nversionName=\'1.0.1001-A\'\n'
-                 'platformBuildVersionName=\'\'\ncompileSdkVersion=\'28\'\n'
-                 'compileSdkVersionCodename=\'9\'\nsdkVersion:\'16\'\n'
-                 'targetSdkVersion:\'28\'\n\nBug: 123\nTest: test\n'
-                 'Change-Id: XXXXXXX\n'),
-                ('Test App\n\nbar.apk\npackage: name=\'com.foo.bar\'\n'
-                 'versionCode=\'1001\'\nversionName=\'1.0.1001-A\'\n'
-                 'platformBuildVersionName=\'\'\ncompileSdkVersion=\'28\'\n'
-                 'compileSdkVersionCodename=\'9\'\nsdkVersion:\'16\'\n'
-                 'targetSdkVersion:\'28\'\n\nBuilt here:\n'
-                 'http://foo.bar.com/builder\n\n'
-                 'This build IS suitable for preview release but IS NOT '
-                 'suitable for public release.\n\n'
-                 'Bug: 123\nTest: test\nChange-Id: XXXXXXX\n'),
-                ('Test App\n\nbar.apk\npackage: name=\'com.foo.bar\'\n'
-                 'versionCode=\'1001\'\nversionName=\'1.0.1001-A\'\n'
-                 'platformBuildVersionName=\'\'\ncompileSdkVersion=\'28\'\n'
-                 'compileSdkVersionCodename=\'9\'\nsdkVersion:\'16\'\n'
-                 'targetSdkVersion:\'28\'\n\nBuilt here:\n'
-                 'http://foo.bar.com/builder\n\n'
-                 'This build IS NOT suitable for preview or public release.\n\n'
-                 'Bug: 123\nTest: test\nChange-Id: XXXXXXX\n'),
+                (
+                    "Test App\n\nbar.apk\npackage: name='com.foo.bar'\n"
+                    "versionCode='1001'\nversionName='1.0.1001-A'\n"
+                    "platformBuildVersionName=''\ncompileSdkVersion='28'\n"
+                    "compileSdkVersionCodename='9'\nsdkVersion:'16'\n"
+                    "targetSdkVersion:'28'\n\nBuilt here:\n"
+                    "http://foo.bar.com/builder\n\n"
+                    "This build IS suitable for public release.\n\n"
+                    "Bug: 123\nTest: test\nChange-Id: XXXXXXX\n"
+                ),
+                (
+                    "Test App\n\nBuilt here:\nhttp://foo.bar.com/builder\n\n"
+                    "This build IS NOT suitable for public release.\n\n"
+                    "bar.apk\npackage: name='com.foo.bar'\n"
+                    "versionCode='1001'\nversionName='1.0.1001-A'\n"
+                    "platformBuildVersionName=''\ncompileSdkVersion='28'\n"
+                    "compileSdkVersionCodename='9'\nsdkVersion:'16'\n"
+                    "targetSdkVersion:'28'\n\nBug: 123\nTest: test\n"
+                    "Change-Id: XXXXXXX\n"
+                ),
+                (
+                    "Test App\n\nbar.apk\npackage: name='com.foo.bar'\n"
+                    "versionCode='1001'\nversionName='1.0.1001-A'\n"
+                    "platformBuildVersionName=''\ncompileSdkVersion='28'\n"
+                    "compileSdkVersionCodename='9'\nsdkVersion:'16'\n"
+                    "targetSdkVersion:'28'\n\nBuilt here:\n"
+                    "http://foo.bar.com/builder\n\n"
+                    "This build IS suitable for preview release but IS NOT "
+                    "suitable for public release.\n\n"
+                    "Bug: 123\nTest: test\nChange-Id: XXXXXXX\n"
+                ),
+                (
+                    "Test App\n\nbar.apk\npackage: name='com.foo.bar'\n"
+                    "versionCode='1001'\nversionName='1.0.1001-A'\n"
+                    "platformBuildVersionName=''\ncompileSdkVersion='28'\n"
+                    "compileSdkVersionCodename='9'\nsdkVersion:'16'\n"
+                    "targetSdkVersion:'28'\n\nBuilt here:\n"
+                    "http://foo.bar.com/builder\n\n"
+                    "This build IS NOT suitable for preview or public "
+                    "release.\n\n"
+                    "Bug: 123\nTest: test\nChange-Id: XXXXXXX\n"
+                ),
             ),
-            ['foo.apk', 'bar.py',]
+            [
+                "foo.apk",
+                "bar.py",
+            ],
         )
 
         # Commits with APKs and without all the required messages should fail.
@@ -582,68 +670,85 @@
             rh.hooks.check_commit_msg_prebuilt_apk_fields,
             False,
             (
-                'subj\nTest: test case\nBug: bug id\n',
+                "subj\nTest: test case\nBug: bug id\n",
                 # Missing 'package'.
-                ('Test App\n\nbar.apk\n'
-                 'versionCode=\'1001\'\nversionName=\'1.0.1001-A\'\n'
-                 'platformBuildVersionName=\'\'\ncompileSdkVersion=\'28\'\n'
-                 'compileSdkVersionCodename=\'9\'\nsdkVersion:\'16\'\n'
-                 'targetSdkVersion:\'28\'\n\nBuilt here:\n'
-                 'http://foo.bar.com/builder\n\n'
-                 'This build IS suitable for public release.\n\n'
-                 'Bug: 123\nTest: test\nChange-Id: XXXXXXX\n'),
+                (
+                    "Test App\n\nbar.apk\n"
+                    "versionCode='1001'\nversionName='1.0.1001-A'\n"
+                    "platformBuildVersionName=''\ncompileSdkVersion='28'\n"
+                    "compileSdkVersionCodename='9'\nsdkVersion:'16'\n"
+                    "targetSdkVersion:'28'\n\nBuilt here:\n"
+                    "http://foo.bar.com/builder\n\n"
+                    "This build IS suitable for public release.\n\n"
+                    "Bug: 123\nTest: test\nChange-Id: XXXXXXX\n"
+                ),
                 # Missing 'sdkVersion'.
-                ('Test App\n\nbar.apk\npackage: name=\'com.foo.bar\'\n'
-                 'versionCode=\'1001\'\nversionName=\'1.0.1001-A\'\n'
-                 'platformBuildVersionName=\'\'\ncompileSdkVersion=\'28\'\n'
-                 'compileSdkVersionCodename=\'9\'\n'
-                 'targetSdkVersion:\'28\'\n\nBuilt here:\n'
-                 'http://foo.bar.com/builder\n\n'
-                 'This build IS suitable for public release.\n\n'
-                 'Bug: 123\nTest: test\nChange-Id: XXXXXXX\n'),
+                (
+                    "Test App\n\nbar.apk\npackage: name='com.foo.bar'\n"
+                    "versionCode='1001'\nversionName='1.0.1001-A'\n"
+                    "platformBuildVersionName=''\ncompileSdkVersion='28'\n"
+                    "compileSdkVersionCodename='9'\n"
+                    "targetSdkVersion:'28'\n\nBuilt here:\n"
+                    "http://foo.bar.com/builder\n\n"
+                    "This build IS suitable for public release.\n\n"
+                    "Bug: 123\nTest: test\nChange-Id: XXXXXXX\n"
+                ),
                 # Missing 'targetSdkVersion'.
-                ('Test App\n\nbar.apk\npackage: name=\'com.foo.bar\'\n'
-                 'versionCode=\'1001\'\nversionName=\'1.0.1001-A\'\n'
-                 'platformBuildVersionName=\'\'\ncompileSdkVersion=\'28\'\n'
-                 'compileSdkVersionCodename=\'9\'\nsdkVersion:\'16\'\n'
-                 'Built here:\nhttp://foo.bar.com/builder\n\n'
-                 'This build IS suitable for public release.\n\n'
-                 'Bug: 123\nTest: test\nChange-Id: XXXXXXX\n'),
+                (
+                    "Test App\n\nbar.apk\npackage: name='com.foo.bar'\n"
+                    "versionCode='1001'\nversionName='1.0.1001-A'\n"
+                    "platformBuildVersionName=''\ncompileSdkVersion='28'\n"
+                    "compileSdkVersionCodename='9'\nsdkVersion:'16'\n"
+                    "Built here:\nhttp://foo.bar.com/builder\n\n"
+                    "This build IS suitable for public release.\n\n"
+                    "Bug: 123\nTest: test\nChange-Id: XXXXXXX\n"
+                ),
                 # Missing build location.
-                ('Test App\n\nbar.apk\npackage: name=\'com.foo.bar\'\n'
-                 'versionCode=\'1001\'\nversionName=\'1.0.1001-A\'\n'
-                 'platformBuildVersionName=\'\'\ncompileSdkVersion=\'28\'\n'
-                 'compileSdkVersionCodename=\'9\'\nsdkVersion:\'16\'\n'
-                 'targetSdkVersion:\'28\'\n\n'
-                 'This build IS suitable for public release.\n\n'
-                 'Bug: 123\nTest: test\nChange-Id: XXXXXXX\n'),
+                (
+                    "Test App\n\nbar.apk\npackage: name='com.foo.bar'\n"
+                    "versionCode='1001'\nversionName='1.0.1001-A'\n"
+                    "platformBuildVersionName=''\ncompileSdkVersion='28'\n"
+                    "compileSdkVersionCodename='9'\nsdkVersion:'16'\n"
+                    "targetSdkVersion:'28'\n\n"
+                    "This build IS suitable for public release.\n\n"
+                    "Bug: 123\nTest: test\nChange-Id: XXXXXXX\n"
+                ),
                 # Missing public release indication.
-                ('Test App\n\nbar.apk\npackage: name=\'com.foo.bar\'\n'
-                 'versionCode=\'1001\'\nversionName=\'1.0.1001-A\'\n'
-                 'platformBuildVersionName=\'\'\ncompileSdkVersion=\'28\'\n'
-                 'compileSdkVersionCodename=\'9\'\nsdkVersion:\'16\'\n'
-                 'targetSdkVersion:\'28\'\n\nBuilt here:\n'
-                 'http://foo.bar.com/builder\n\n'
-                 'Bug: 123\nTest: test\nChange-Id: XXXXXXX\n'),
+                (
+                    "Test App\n\nbar.apk\npackage: name='com.foo.bar'\n"
+                    "versionCode='1001'\nversionName='1.0.1001-A'\n"
+                    "platformBuildVersionName=''\ncompileSdkVersion='28'\n"
+                    "compileSdkVersionCodename='9'\nsdkVersion:'16'\n"
+                    "targetSdkVersion:'28'\n\nBuilt here:\n"
+                    "http://foo.bar.com/builder\n\n"
+                    "Bug: 123\nTest: test\nChange-Id: XXXXXXX\n"
+                ),
             ),
-            ['foo.apk', 'bar.py',]
+            [
+                "foo.apk",
+                "bar.py",
+            ],
         )
 
     def test_commit_msg_test_field(self, _mock_check, _mock_run):
         """Verify the commit_msg_test_field builtin hook."""
         # Check some good messages.
         self._test_commit_messages(
-            rh.hooks.check_commit_msg_test_field, True, (
-                'subj\n\nTest: i did done dood it\n',
-            ))
+            rh.hooks.check_commit_msg_test_field,
+            True,
+            ("subj\n\nTest: i did done dood it\n",),
+        )
 
         # Check some bad messages.
         self._test_commit_messages(
-            rh.hooks.check_commit_msg_test_field, False, (
-                'subj',
-                'subj\n\nTEST=1234\n',
-                'subj\n\nTEST: I1234\n',
-            ))
+            rh.hooks.check_commit_msg_test_field,
+            False,
+            (
+                "subj",
+                "subj\n\nTEST=1234\n",
+                "subj\n\nTEST: I1234\n",
+            ),
+        )
 
     def test_commit_msg_relnote_field_format(self, _mock_check, _mock_run):
         """Verify the commit_msg_relnote_field_format builtin hook."""
@@ -652,151 +757,211 @@
             rh.hooks.check_commit_msg_relnote_field_format,
             True,
             (
-                'subj',
-                'subj\n\nTest: i did done dood it\nBug: 1234',
-                'subj\n\nMore content\n\nTest: i did done dood it\nBug: 1234',
-                'subj\n\nRelnote: This is a release note\nBug: 1234',
-                'subj\n\nRelnote:This is a release note\nBug: 1234',
-                'subj\n\nRelnote: This is a release note.\nBug: 1234',
+                "subj",
+                "subj\n\nTest: i did done dood it\nBug: 1234",
+                "subj\n\nMore content\n\nTest: i did done dood it\nBug: 1234",
+                "subj\n\nRelnote: This is a release note\nBug: 1234",
+                "subj\n\nRelnote:This is a release note\nBug: 1234",
+                "subj\n\nRelnote: This is a release note.\nBug: 1234",
                 'subj\n\nRelnote: "This is a release note."\nBug: 1234',
                 'subj\n\nRelnote: "This is a \\"release note\\"."\n\nBug: 1234',
-                'subj\n\nRelnote: This is a release note.\nChange-Id: 1234',
-                'subj\n\nRelnote: This is a release note.\n\nChange-Id: 1234',
-                ('subj\n\nRelnote: "This is a release note."\n\n'
-                 'Change-Id: 1234'),
-                ('subj\n\nRelnote: This is a release note.\n\n'
-                 'It has more info, but it is not part of the release note'
-                 '\nChange-Id: 1234'),
-                ('subj\n\nRelnote: "This is a release note.\n'
-                 'It contains a correct second line."'),
-                ('subj\n\nRelnote:"This is a release note.\n'
-                 'It contains a correct second line."'),
-                ('subj\n\nRelnote: "This is a release note.\n'
-                 'It contains a correct second line.\n'
-                 'And even a third line."\n'
-                 'Bug: 1234'),
-                ('subj\n\nRelnote: "This is a release note.\n'
-                 'It contains a correct second line.\n'
-                 '\\"Quotes\\" are even used on the third line."\n'
-                 'Bug: 1234'),
-                ('subj\n\nRelnote: This is release note 1.\n'
-                 'Relnote: This is release note 2.\n'
-                 'Bug: 1234'),
-                ('subj\n\nRelnote: This is release note 1.\n'
-                 'Relnote: "This is release note 2, and it\n'
-                 'contains a correctly formatted third line."\n'
-                 'Bug: 1234'),
-                ('subj\n\nRelnote: "This is release note 1 with\n'
-                 'a correctly formatted second line."\n\n'
-                 'Relnote: "This is release note 2, and it\n'
-                 'contains a correctly formatted second line."\n'
-                 'Bug: 1234'),
-                ('subj\n\nRelnote: "This is a release note with\n'
-                 'a correctly formatted second line."\n\n'
-                 'Bug: 1234'
-                 'Here is some extra "quoted" content.'),
-                ('subj\n\nRelnote: """This is a release note.\n\n'
-                 'This relnote contains an empty line.\n'
-                 'Then a non-empty line.\n\n'
-                 'And another empty line."""\n\n'
-                 'Bug: 1234'),
-                ('subj\n\nRelnote: """This is a release note.\n\n'
-                 'This relnote contains an empty line.\n'
-                 'Then an acceptable "quoted" line.\n\n'
-                 'And another empty line."""\n\n'
-                 'Bug: 1234'),
-                ('subj\n\nRelnote: """This is a release note."""\n\n'
-                 'Bug: 1234'),
-                ('subj\n\nRelnote: """This is a release note.\n'
-                 'It has a second line."""\n\n'
-                 'Bug: 1234'),
-                ('subj\n\nRelnote: """This is a release note.\n'
-                 'It has a second line, but does not end here.\n'
-                 '"""\n\n'
-                 'Bug: 1234'),
-                ('subj\n\nRelnote: """This is a release note.\n'
-                 '"It" has a second line, but does not end here.\n'
-                 '"""\n\n'
-                 'Bug: 1234'),
-                ('subj\n\nRelnote: "This is a release note.\n'
-                 'It has a second line, but does not end here.\n'
-                 '"\n\n'
-                 'Bug: 1234'),
-            ))
+                "subj\n\nRelnote: This is a release note.\nChange-Id: 1234",
+                "subj\n\nRelnote: This is a release note.\n\nChange-Id: 1234",
+                (
+                    'subj\n\nRelnote: "This is a release note."\n\n'
+                    "Change-Id: 1234"
+                ),
+                (
+                    "subj\n\nRelnote: This is a release note.\n\n"
+                    "It has more info, but it is not part of the release note"
+                    "\nChange-Id: 1234"
+                ),
+                (
+                    'subj\n\nRelnote: "This is a release note.\n'
+                    'It contains a correct second line."'
+                ),
+                (
+                    'subj\n\nRelnote:"This is a release note.\n'
+                    'It contains a correct second line."'
+                ),
+                (
+                    'subj\n\nRelnote: "This is a release note.\n'
+                    "It contains a correct second line.\n"
+                    'And even a third line."\n'
+                    "Bug: 1234"
+                ),
+                (
+                    'subj\n\nRelnote: "This is a release note.\n'
+                    "It contains a correct second line.\n"
+                    '\\"Quotes\\" are even used on the third line."\n'
+                    "Bug: 1234"
+                ),
+                (
+                    "subj\n\nRelnote: This is release note 1.\n"
+                    "Relnote: This is release note 2.\n"
+                    "Bug: 1234"
+                ),
+                (
+                    "subj\n\nRelnote: This is release note 1.\n"
+                    'Relnote: "This is release note 2, and it\n'
+                    'contains a correctly formatted third line."\n'
+                    "Bug: 1234"
+                ),
+                (
+                    'subj\n\nRelnote: "This is release note 1 with\n'
+                    'a correctly formatted second line."\n\n'
+                    'Relnote: "This is release note 2, and it\n'
+                    'contains a correctly formatted second line."\n'
+                    "Bug: 1234"
+                ),
+                (
+                    'subj\n\nRelnote: "This is a release note with\n'
+                    'a correctly formatted second line."\n\n'
+                    "Bug: 1234"
+                    'Here is some extra "quoted" content.'
+                ),
+                (
+                    'subj\n\nRelnote: """This is a release note.\n\n'
+                    "This relnote contains an empty line.\n"
+                    "Then a non-empty line.\n\n"
+                    'And another empty line."""\n\n'
+                    "Bug: 1234"
+                ),
+                (
+                    'subj\n\nRelnote: """This is a release note.\n\n'
+                    "This relnote contains an empty line.\n"
+                    'Then an acceptable "quoted" line.\n\n'
+                    'And another empty line."""\n\n'
+                    "Bug: 1234"
+                ),
+                (
+                    'subj\n\nRelnote: """This is a release note."""\n\n'
+                    "Bug: 1234"
+                ),
+                (
+                    'subj\n\nRelnote: """This is a release note.\n'
+                    'It has a second line."""\n\n'
+                    "Bug: 1234"
+                ),
+                (
+                    'subj\n\nRelnote: """This is a release note.\n'
+                    "It has a second line, but does not end here.\n"
+                    '"""\n\n'
+                    "Bug: 1234"
+                ),
+                (
+                    'subj\n\nRelnote: """This is a release note.\n'
+                    '"It" has a second line, but does not end here.\n'
+                    '"""\n\n'
+                    "Bug: 1234"
+                ),
+                (
+                    'subj\n\nRelnote: "This is a release note.\n'
+                    "It has a second line, but does not end here.\n"
+                    '"\n\n'
+                    "Bug: 1234"
+                ),
+            ),
+        )
 
         # Check some bad messages.
         self._test_commit_messages(
             rh.hooks.check_commit_msg_relnote_field_format,
             False,
             (
-                'subj\n\nReleaseNote: This is a release note.\n',
-                'subj\n\nRelnotes: This is a release note.\n',
-                'subj\n\nRel-note: This is a release note.\n',
-                'subj\n\nrelnoTes: This is a release note.\n',
-                'subj\n\nrel-Note: This is a release note.\n',
+                "subj\n\nReleaseNote: This is a release note.\n",
+                "subj\n\nRelnotes: This is a release note.\n",
+                "subj\n\nRel-note: This is a release note.\n",
+                "subj\n\nrelnoTes: This is a release note.\n",
+                "subj\n\nrel-Note: This is a release note.\n",
                 'subj\n\nRelnote: "This is a "release note"."\nBug: 1234',
                 'subj\n\nRelnote: This is a "release note".\nBug: 1234',
-                ('subj\n\nRelnote: This is a release note.\n'
-                 'It contains an incorrect second line.'),
-                ('subj\n\nRelnote: "This is a release note.\n'
-                 'It contains multiple lines.\n'
-                 'But it does not provide an ending quote.\n'),
-                ('subj\n\nRelnote: "This is a release note.\n'
-                 'It contains multiple lines but no closing quote.\n'
-                 'Test: my test "hello world"\n'),
-                ('subj\n\nRelnote: This is release note 1.\n'
-                 'Relnote: "This is release note 2, and it\n'
-                 'contains an incorrectly formatted third line.\n'
-                 'Bug: 1234'),
-                ('subj\n\nRelnote: This is release note 1 with\n'
-                 'an incorrectly formatted second line.\n\n'
-                 'Relnote: "This is release note 2, and it\n'
-                 'contains a correctly formatted second line."\n'
-                 'Bug: 1234'),
-                ('subj\n\nRelnote: "This is release note 1 with\n'
-                 'a correctly formatted second line."\n\n'
-                 'Relnote: This is release note 2, and it\n'
-                 'contains an incorrectly formatted second line.\n'
-                 'Bug: 1234'),
-                ('subj\n\nRelnote: "This is a release note.\n'
-                 'It contains a correct second line.\n'
-                 'But incorrect "quotes" on the third line."\n'
-                 'Bug: 1234'),
-                ('subj\n\nRelnote: """This is a release note.\n'
-                 'It has a second line, but no closing triple quote.\n\n'
-                 'Bug: 1234'),
-                ('subj\n\nRelnote: "This is a release note.\n'
-                 '"It" has a second line, but does not end here.\n'
-                 '"\n\n'
-                 'Bug: 1234'),
-            ))
+                (
+                    "subj\n\nRelnote: This is a release note.\n"
+                    "It contains an incorrect second line."
+                ),
+                (
+                    'subj\n\nRelnote: "This is a release note.\n'
+                    "It contains multiple lines.\n"
+                    "But it does not provide an ending quote.\n"
+                ),
+                (
+                    'subj\n\nRelnote: "This is a release note.\n'
+                    "It contains multiple lines but no closing quote.\n"
+                    'Test: my test "hello world"\n'
+                ),
+                (
+                    "subj\n\nRelnote: This is release note 1.\n"
+                    'Relnote: "This is release note 2, and it\n'
+                    "contains an incorrectly formatted third line.\n"
+                    "Bug: 1234"
+                ),
+                (
+                    "subj\n\nRelnote: This is release note 1 with\n"
+                    "an incorrectly formatted second line.\n\n"
+                    'Relnote: "This is release note 2, and it\n'
+                    'contains a correctly formatted second line."\n'
+                    "Bug: 1234"
+                ),
+                (
+                    'subj\n\nRelnote: "This is release note 1 with\n'
+                    'a correctly formatted second line."\n\n'
+                    "Relnote: This is release note 2, and it\n"
+                    "contains an incorrectly formatted second line.\n"
+                    "Bug: 1234"
+                ),
+                (
+                    'subj\n\nRelnote: "This is a release note.\n'
+                    "It contains a correct second line.\n"
+                    'But incorrect "quotes" on the third line."\n'
+                    "Bug: 1234"
+                ),
+                (
+                    'subj\n\nRelnote: """This is a release note.\n'
+                    "It has a second line, but no closing triple quote.\n\n"
+                    "Bug: 1234"
+                ),
+                (
+                    'subj\n\nRelnote: "This is a release note.\n'
+                    '"It" has a second line, but does not end here.\n'
+                    '"\n\n'
+                    "Bug: 1234"
+                ),
+            ),
+        )
 
     def test_commit_msg_relnote_for_current_txt(self, _mock_check, _mock_run):
         """Verify the commit_msg_relnote_for_current_txt builtin hook."""
-        diff_without_current_txt = ['bar/foo.txt',
-                                    'foo.cpp',
-                                    'foo.java',
-                                    'foo_current.java',
-                                    'foo_current.txt',
-                                    'baz/current.java',
-                                    'baz/foo_current.txt']
-        diff_with_current_txt = diff_without_current_txt + ['current.txt']
-        diff_with_subdir_current_txt = \
-            diff_without_current_txt + ['foo/current.txt']
-        diff_with_experimental_current_txt = \
-            diff_without_current_txt + ['public_plus_experimental_current.txt']
+        diff_without_current_txt = [
+            "bar/foo.txt",
+            "foo.cpp",
+            "foo.java",
+            "foo_current.java",
+            "foo_current.txt",
+            "baz/current.java",
+            "baz/foo_current.txt",
+        ]
+        diff_with_current_txt = diff_without_current_txt + ["current.txt"]
+        diff_with_subdir_current_txt = diff_without_current_txt + [
+            "foo/current.txt"
+        ]
+        diff_with_experimental_current_txt = diff_without_current_txt + [
+            "public_plus_experimental_current.txt"
+        ]
         # Check some good messages.
         self._test_commit_messages(
             rh.hooks.check_commit_msg_relnote_for_current_txt,
             True,
             (
-                'subj\n\nRelnote: This is a release note\n',
-                'subj\n\nRelnote: This is a release note.\n\nChange-Id: 1234',
-                ('subj\n\nRelnote: This is release note 1 with\n'
-                 'an incorrectly formatted second line.\n\n'
-                 'Relnote: "This is release note 2, and it\n'
-                 'contains a correctly formatted second line."\n'
-                 'Bug: 1234'),
+                "subj\n\nRelnote: This is a release note\n",
+                "subj\n\nRelnote: This is a release note.\n\nChange-Id: 1234",
+                (
+                    "subj\n\nRelnote: This is release note 1 with\n"
+                    "an incorrectly formatted second line.\n\n"
+                    'Relnote: "This is release note 2, and it\n'
+                    'contains a correctly formatted second line."\n'
+                    "Bug: 1234"
+                ),
             ),
             files=diff_with_current_txt,
         )
@@ -805,13 +970,15 @@
             rh.hooks.check_commit_msg_relnote_for_current_txt,
             True,
             (
-                'subj\n\nRelnote: This is a release note\n',
-                'subj\n\nRelnote: This is a release note.\n\nChange-Id: 1234',
-                ('subj\n\nRelnote: This is release note 1 with\n'
-                 'an incorrectly formatted second line.\n\n'
-                 'Relnote: "This is release note 2, and it\n'
-                 'contains a correctly formatted second line."\n'
-                 'Bug: 1234'),
+                "subj\n\nRelnote: This is a release note\n",
+                "subj\n\nRelnote: This is a release note.\n\nChange-Id: 1234",
+                (
+                    "subj\n\nRelnote: This is release note 1 with\n"
+                    "an incorrectly formatted second line.\n\n"
+                    'Relnote: "This is release note 2, and it\n'
+                    'contains a correctly formatted second line."\n'
+                    "Bug: 1234"
+                ),
             ),
             files=diff_with_experimental_current_txt,
         )
@@ -820,13 +987,15 @@
             rh.hooks.check_commit_msg_relnote_for_current_txt,
             True,
             (
-                'subj\n\nRelnote: This is a release note\n',
-                'subj\n\nRelnote: This is a release note.\n\nChange-Id: 1234',
-                ('subj\n\nRelnote: This is release note 1 with\n'
-                 'an incorrectly formatted second line.\n\n'
-                 'Relnote: "This is release note 2, and it\n'
-                 'contains a correctly formatted second line."\n'
-                 'Bug: 1234'),
+                "subj\n\nRelnote: This is a release note\n",
+                "subj\n\nRelnote: This is a release note.\n\nChange-Id: 1234",
+                (
+                    "subj\n\nRelnote: This is release note 1 with\n"
+                    "an incorrectly formatted second line.\n\n"
+                    'Relnote: "This is release note 2, and it\n'
+                    'contains a correctly formatted second line."\n'
+                    "Bug: 1234"
+                ),
             ),
             files=diff_with_subdir_current_txt,
         )
@@ -835,15 +1004,17 @@
             rh.hooks.check_commit_msg_relnote_for_current_txt,
             True,
             (
-                'subj',
-                'subj\nBug: 12345\nChange-Id: 1234',
-                'subj\n\nRelnote: This is a release note\n',
-                'subj\n\nRelnote: This is a release note.\n\nChange-Id: 1234',
-                ('subj\n\nRelnote: This is release note 1 with\n'
-                 'an incorrectly formatted second line.\n\n'
-                 'Relnote: "This is release note 2, and it\n'
-                 'contains a correctly formatted second line."\n'
-                 'Bug: 1234'),
+                "subj",
+                "subj\nBug: 12345\nChange-Id: 1234",
+                "subj\n\nRelnote: This is a release note\n",
+                "subj\n\nRelnote: This is a release note.\n\nChange-Id: 1234",
+                (
+                    "subj\n\nRelnote: This is release note 1 with\n"
+                    "an incorrectly formatted second line.\n\n"
+                    'Relnote: "This is release note 2, and it\n'
+                    'contains a correctly formatted second line."\n'
+                    "Bug: 1234"
+                ),
             ),
             files=diff_without_current_txt,
         )
@@ -851,57 +1022,52 @@
         self._test_commit_messages(
             rh.hooks.check_commit_msg_relnote_for_current_txt,
             False,
-            (
-                'subj'
-                'subj\nBug: 12345\nChange-Id: 1234',
-            ),
+            ("subjsubj\nBug: 12345\nChange-Id: 1234",),
             files=diff_with_current_txt,
         )
         # Check some bad messages.
         self._test_commit_messages(
             rh.hooks.check_commit_msg_relnote_for_current_txt,
             False,
-            (
-                'subj'
-                'subj\nBug: 12345\nChange-Id: 1234',
-            ),
+            ("subjsubj\nBug: 12345\nChange-Id: 1234",),
             files=diff_with_experimental_current_txt,
         )
         # Check some bad messages.
         self._test_commit_messages(
             rh.hooks.check_commit_msg_relnote_for_current_txt,
             False,
-            (
-                'subj'
-                'subj\nBug: 12345\nChange-Id: 1234',
-            ),
+            ("subjsubj\nBug: 12345\nChange-Id: 1234",),
             files=diff_with_subdir_current_txt,
         )
 
     def test_cpplint(self, mock_check, _mock_run):
         """Verify the cpplint builtin hook."""
-        self._test_file_filter(mock_check, rh.hooks.check_cpplint,
-                               ('foo.cpp', 'foo.cxx'))
+        self._test_file_filter(
+            mock_check, rh.hooks.check_cpplint, ("foo.cpp", "foo.cxx")
+        )
 
     def test_gofmt(self, mock_check, _mock_run):
         """Verify the gofmt builtin hook."""
         # First call should do nothing as there are no files to check.
         ret = rh.hooks.check_gofmt(
-            self.project, 'commit', 'desc', (), options=self.options)
+            self.project, "commit", "desc", (), options=self.options
+        )
         self.assertIsNone(ret)
         self.assertFalse(mock_check.called)
 
         # Second call will have some results.
-        diff = [rh.git.RawDiffEntry(file='foo.go')]
+        diff = [rh.git.RawDiffEntry(file="foo.go")]
         ret = rh.hooks.check_gofmt(
-            self.project, 'commit', 'desc', diff, options=self.options)
+            self.project, "commit", "desc", diff, options=self.options
+        )
         self.assertIsNotNone(ret)
 
     def test_jsonlint(self, mock_check, _mock_run):
         """Verify the jsonlint builtin hook."""
         # First call should do nothing as there are no files to check.
         ret = rh.hooks.check_json(
-            self.project, 'commit', 'desc', (), options=self.options)
+            self.project, "commit", "desc", (), options=self.options
+        )
         self.assertIsNone(ret)
         self.assertFalse(mock_check.called)
 
@@ -911,87 +1077,103 @@
         """Verify the ktfmt builtin hook."""
         # First call should do nothing as there are no files to check.
         ret = rh.hooks.check_ktfmt(
-            self.project, 'commit', 'desc', (), options=self.options)
+            self.project, "commit", "desc", (), options=self.options
+        )
         self.assertIsNone(ret)
         self.assertFalse(mock_check.called)
         # Check that .kt files are included by default.
-        diff = [rh.git.RawDiffEntry(file='foo.kt'),
-                rh.git.RawDiffEntry(file='bar.java'),
-                rh.git.RawDiffEntry(file='baz/blah.kt')]
+        diff = [
+            rh.git.RawDiffEntry(file="foo.kt"),
+            rh.git.RawDiffEntry(file="bar.java"),
+            rh.git.RawDiffEntry(file="baz/blah.kt"),
+        ]
         ret = rh.hooks.check_ktfmt(
-            self.project, 'commit', 'desc', diff, options=self.options)
-        self.assertListEqual(ret[0].files, ['foo.kt', 'baz/blah.kt'])
-        diff = [rh.git.RawDiffEntry(file='foo/f1.kt'),
-                rh.git.RawDiffEntry(file='bar/f2.kt'),
-                rh.git.RawDiffEntry(file='baz/f2.kt')]
-        ret = rh.hooks.check_ktfmt(self.project, 'commit', 'desc', diff,
-                                   options=rh.hooks.HookOptions('hook name', [
-                                       '--include-dirs=foo,baz'], {}))
-        self.assertListEqual(ret[0].files, ['foo/f1.kt', 'baz/f2.kt'])
+            self.project, "commit", "desc", diff, options=self.options
+        )
+        self.assertListEqual(ret[0].files, ["foo.kt", "baz/blah.kt"])
+        diff = [
+            rh.git.RawDiffEntry(file="foo/f1.kt"),
+            rh.git.RawDiffEntry(file="bar/f2.kt"),
+            rh.git.RawDiffEntry(file="baz/f2.kt"),
+        ]
+        ret = rh.hooks.check_ktfmt(
+            self.project,
+            "commit",
+            "desc",
+            diff,
+            options=rh.hooks.HookOptions(
+                "hook name", ["--include-dirs=foo,baz"], {}
+            ),
+        )
+        self.assertListEqual(ret[0].files, ["foo/f1.kt", "baz/f2.kt"])
 
     def test_pylint(self, mock_check, _mock_run):
         """Verify the pylint builtin hook."""
-        self._test_file_filter(mock_check, rh.hooks.check_pylint3,
-                               ('foo.py',))
+        self._test_file_filter(mock_check, rh.hooks.check_pylint3, ("foo.py",))
 
     def test_pylint2(self, mock_check, _mock_run):
         """Verify the pylint2 builtin hook."""
         ret = rh.hooks.check_pylint2(
-            self.project, 'commit', 'desc', (), options=self.options)
+            self.project, "commit", "desc", (), options=self.options
+        )
         self.assertEqual(len(ret), 1)
         self.assertTrue(ret[0].is_warning())
 
     def test_pylint3(self, mock_check, _mock_run):
         """Verify the pylint3 builtin hook."""
-        self._test_file_filter(mock_check, rh.hooks.check_pylint3,
-                               ('foo.py',))
+        self._test_file_filter(mock_check, rh.hooks.check_pylint3, ("foo.py",))
 
     def test_rustfmt(self, mock_check, _mock_run):
         # First call should do nothing as there are no files to check.
         ret = rh.hooks.check_rustfmt(
-            self.project, 'commit', 'desc', (), options=self.options)
+            self.project, "commit", "desc", (), options=self.options
+        )
         self.assertEqual(ret, None)
         self.assertFalse(mock_check.called)
 
         # Second call will have some results.
-        diff = [rh.git.RawDiffEntry(file='lib.rs')]
+        diff = [rh.git.RawDiffEntry(file="lib.rs")]
         ret = rh.hooks.check_rustfmt(
-            self.project, 'commit', 'desc', diff, options=self.options)
+            self.project, "commit", "desc", diff, options=self.options
+        )
         self.assertNotEqual(ret, None)
 
     def test_xmllint(self, mock_check, _mock_run):
         """Verify the xmllint builtin hook."""
-        self._test_file_filter(mock_check, rh.hooks.check_xmllint,
-                               ('foo.xml',))
+        self._test_file_filter(mock_check, rh.hooks.check_xmllint, ("foo.xml",))
 
     def test_android_test_mapping_format(self, mock_check, _mock_run):
         """Verify the android_test_mapping_format builtin hook."""
         # First call should do nothing as there are no files to check.
         ret = rh.hooks.check_android_test_mapping(
-            self.project, 'commit', 'desc', (), options=self.options)
+            self.project, "commit", "desc", (), options=self.options
+        )
         self.assertIsNone(ret)
         self.assertFalse(mock_check.called)
 
         # Second call will have some results.
-        diff = [rh.git.RawDiffEntry(file='TEST_MAPPING')]
+        diff = [rh.git.RawDiffEntry(file="TEST_MAPPING")]
         ret = rh.hooks.check_android_test_mapping(
-            self.project, 'commit', 'desc', diff, options=self.options)
+            self.project, "commit", "desc", diff, options=self.options
+        )
         self.assertIsNotNone(ret)
 
     def test_aidl_format(self, mock_check, _mock_run):
         """Verify the aidl_format builtin hook."""
         # First call should do nothing as there are no files to check.
         ret = rh.hooks.check_aidl_format(
-            self.project, 'commit', 'desc', (), options=self.options)
+            self.project, "commit", "desc", (), options=self.options
+        )
         self.assertIsNone(ret)
         self.assertFalse(mock_check.called)
 
         # Second call will have some results.
-        diff = [rh.git.RawDiffEntry(file='IFoo.go')]
+        diff = [rh.git.RawDiffEntry(file="IFoo.go")]
         ret = rh.hooks.check_gofmt(
-            self.project, 'commit', 'desc', diff, options=self.options)
+            self.project, "commit", "desc", diff, options=self.options
+        )
         self.assertIsNotNone(ret)
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     unittest.main()
diff --git a/rh/results.py b/rh/results.py
index 236387e..4a7143b 100644
--- a/rh/results.py
+++ b/rh/results.py
@@ -18,7 +18,7 @@
 import sys
 from typing import List, NamedTuple, Optional
 
-_path = os.path.realpath(__file__ + '/../..')
+_path = os.path.realpath(__file__ + "/../..")
 if sys.path[0] != _path:
     sys.path.insert(0, _path)
 del _path
@@ -71,11 +71,16 @@
 class HookCommandResult(HookResult):
     """A single hook result based on a CompletedProcess."""
 
-    def __init__(self, hook, project, commit, result, files=(),
-                 fixup_cmd=None):
-        HookResult.__init__(self, hook, project, commit,
-                            result.stderr if result.stderr else result.stdout,
-                            files=files, fixup_cmd=fixup_cmd)
+    def __init__(self, hook, project, commit, result, files=(), fixup_cmd=None):
+        HookResult.__init__(
+            self,
+            hook,
+            project,
+            commit,
+            result.stderr if result.stderr else result.stdout,
+            files=files,
+            fixup_cmd=fixup_cmd,
+        )
         self.result = result
 
     def __bool__(self):
diff --git a/rh/results_unittest.py b/rh/results_unittest.py
index 93d909e..a02e77a 100755
--- a/rh/results_unittest.py
+++ b/rh/results_unittest.py
@@ -19,7 +19,7 @@
 import sys
 import unittest
 
-_path = os.path.realpath(__file__ + '/../..')
+_path = os.path.realpath(__file__ + "/../..")
 if sys.path[0] != _path:
     sys.path.insert(0, _path)
 del _path
@@ -43,12 +43,12 @@
     def test_error_warning(self):
         """Check error & warning handling."""
         # No errors.
-        result = rh.results.HookResult('hook', 'project', 'HEAD', False)
+        result = rh.results.HookResult("hook", "project", "HEAD", False)
         self.assertFalse(result)
         self.assertFalse(result.is_warning())
 
         # An error.
-        result = rh.results.HookResult('hook', 'project', 'HEAD', True)
+        result = rh.results.HookResult("hook", "project", "HEAD", True)
         self.assertTrue(result)
         self.assertFalse(result.is_warning())
 
@@ -60,19 +60,22 @@
         """Check error & warning handling."""
         # No errors.
         result = rh.results.HookCommandResult(
-            'hook', 'project', 'HEAD', COMPLETED_PROCESS_PASS)
+            "hook", "project", "HEAD", COMPLETED_PROCESS_PASS
+        )
         self.assertFalse(result)
         self.assertFalse(result.is_warning())
 
         # An error.
         result = rh.results.HookCommandResult(
-            'hook', 'project', 'HEAD', COMPLETED_PROCESS_FAIL)
+            "hook", "project", "HEAD", COMPLETED_PROCESS_FAIL
+        )
         self.assertTrue(result)
         self.assertFalse(result.is_warning())
 
         # A warning.
         result = rh.results.HookCommandResult(
-            'hook', 'project', 'HEAD', COMPLETED_PROCESS_WARN)
+            "hook", "project", "HEAD", COMPLETED_PROCESS_WARN
+        )
         self.assertFalse(result)
         self.assertTrue(result.is_warning())
 
@@ -83,23 +86,28 @@
     def test_error_warning(self):
         """Check error & warning handling."""
         # No errors.
-        result = rh.results.ProjectResults('project', 'workdir')
+        result = rh.results.ProjectResults("project", "workdir")
         self.assertFalse(result)
 
         # Warnings are not errors.
-        result.add_results([
-            rh.results.HookResult('hook', 'project', 'HEAD', False),
-            rh.results.HookCommandResult(
-                'hook', 'project', 'HEAD', COMPLETED_PROCESS_WARN),
-        ])
+        result.add_results(
+            [
+                rh.results.HookResult("hook", "project", "HEAD", False),
+                rh.results.HookCommandResult(
+                    "hook", "project", "HEAD", COMPLETED_PROCESS_WARN
+                ),
+            ]
+        )
         self.assertFalse(result)
 
         # Errors are errors.
-        result.add_results([
-            rh.results.HookResult('hook', 'project', 'HEAD', True),
-        ])
+        result.add_results(
+            [
+                rh.results.HookResult("hook", "project", "HEAD", True),
+            ]
+        )
         self.assertTrue(result)
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     unittest.main()
diff --git a/rh/shell.py b/rh/shell.py
index bc66f37..6ea8fc1 100644
--- a/rh/shell.py
+++ b/rh/shell.py
@@ -18,7 +18,7 @@
 import pathlib
 import sys
 
-_path = os.path.realpath(__file__ + '/../..')
+_path = os.path.realpath(__file__ + "/../..")
 if sys.path[0] != _path:
     sys.path.insert(0, _path)
 del _path
@@ -33,10 +33,10 @@
 # See the bash man page as well as the POSIX shell documentation for more info:
 #   http://www.gnu.org/software/bash/manual/bashref.html
 #   http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html
-_SHELL_QUOTABLE_CHARS = frozenset('[|&;()<> \t!{}[]=*?~$"\'\\#^')
+_SHELL_QUOTABLE_CHARS = frozenset("[|&;()<> \t!{}[]=*?~$\"'\\#^")
 # The chars that, when used inside of double quotes, need escaping.
 # Order here matters as we need to escape backslashes first.
-_SHELL_ESCAPE_CHARS = r'\"`$'
+_SHELL_ESCAPE_CHARS = r"\"`$"
 
 
 def quote(s):
@@ -68,7 +68,7 @@
     """
     # If callers pass down bad types, don't blow up.
     if isinstance(s, bytes):
-        s = s.encode('utf-8')
+        s = s.encode("utf-8")
     elif isinstance(s, pathlib.PurePath):
         return str(s)
     elif not isinstance(s, str):
@@ -89,7 +89,7 @@
     # used inside of double quotes.
     for c in _SHELL_ESCAPE_CHARS:
         if c in s:
-            s = s.replace(c, fr'\{c}')
+            s = s.replace(c, rf"\{c}")
     return f'"{s}"'
 
 
@@ -106,7 +106,7 @@
       The unescaped version of the string.
     """
     if not s:
-        return ''
+        return ""
 
     if s[0] == "'":
         return s[1:-1]
@@ -115,11 +115,11 @@
         return s
 
     s = s[1:-1]
-    output = ''
+    output = ""
     i = 0
     while i < len(s) - 1:
         # Skip the backslash when it makes sense.
-        if s[i] == '\\' and s[i + 1] in _SHELL_ESCAPE_CHARS:
+        if s[i] == "\\" and s[i + 1] in _SHELL_ESCAPE_CHARS:
             i += 1
         output += s[i]
         i += 1
@@ -148,7 +148,7 @@
       String representing full command.
     """
     # Use str before repr to translate unicode strings to regular strings.
-    return ' '.join(quote(arg) for arg in cmd)
+    return " ".join(quote(arg) for arg in cmd)
 
 
 def boolean_shell_value(sval, default):
@@ -158,9 +158,9 @@
 
     if isinstance(sval, str):
         s = sval.lower()
-        if s in ('yes', 'y', '1', 'true'):
+        if s in ("yes", "y", "1", "true"):
             return True
-        if s in ('no', 'n', '0', 'false'):
+        if s in ("no", "n", "0", "false"):
             return False
 
-    raise ValueError(f'Could not decode as a boolean value: {sval!r}')
+    raise ValueError(f"Could not decode as a boolean value: {sval!r}")
diff --git a/rh/shell_unittest.py b/rh/shell_unittest.py
index fec8710..4d0bbf1 100755
--- a/rh/shell_unittest.py
+++ b/rh/shell_unittest.py
@@ -21,7 +21,7 @@
 import sys
 import unittest
 
-_path = os.path.realpath(__file__ + '/../..')
+_path = os.path.realpath(__file__ + "/../..")
 if sys.path[0] != _path:
     sys.path.insert(0, _path)
 del _path
@@ -40,9 +40,11 @@
 
     def _assertEqual(self, func, test_input, test_output, result):
         """Like assertEqual but with built in diff support."""
-        diff = '\n'.join(list(self.differ.compare([test_output], [result])))
-        msg = (f'Expected {func} to translate {test_input!r} to '
-               f'{test_output!r}, but got {result!r}\n{diff}')
+        diff = "\n".join(list(self.differ.compare([test_output], [result])))
+        msg = (
+            f"Expected {func} to translate {test_input!r} to "
+            f"{test_output!r}, but got {result!r}\n{diff}"
+        )
         self.assertEqual(test_output, result, msg)
 
     def _testData(self, functor, tests, check_type=True):
@@ -65,15 +67,15 @@
         """Basic ShellQuote tests."""
         # Dict of expected output strings to input lists.
         tests_quote = {
-            "''": '',
-            'a': 'a',
-            "'a b c'": 'a b c',
-            "'a\tb'": 'a\tb',
-            "'/a$file'": '/a$file',
-            "'/a#file'": '/a#file',
+            "''": "",
+            "a": "a",
+            "'a b c'": "a b c",
+            "'a\tb'": "a\tb",
+            "'/a$file'": "/a$file",
+            "'/a#file'": "/a#file",
             """'b"c'""": 'b"c',
-            "'a@()b'": 'a@()b',
-            'j%k': 'j%k',
+            "'a@()b'": "a@()b",
+            "j%k": "j%k",
             r'''"s'a\$va\\rs"''': r"s'a$va\rs",
             r'''"\\'\\\""''': r'''\'\"''',
             r'''"'\\\$"''': r"""'\$""",
@@ -82,7 +84,7 @@
         # Expected input output specific to ShellUnquote.  This string cannot
         # be produced by ShellQuote but is still a valid bash escaped string.
         tests_unquote = {
-            r'''\$''': r'''"\\$"''',
+            r"""\$""": r'''"\\$"''',
         }
 
         def aux(s):
@@ -97,13 +99,13 @@
 
     def testPathlib(self):
         """Verify pathlib is handled."""
-        self.assertEqual(rh.shell.quote(Path('/')), '/')
+        self.assertEqual(rh.shell.quote(Path("/")), "/")
 
     def testBadInputs(self):
         """Verify bad inputs do not crash."""
         for arg, exp in (
-            (1234, '1234'),
-            (Exception('hi'), "Exception('hi')"),
+            (1234, "1234"),
+            (Exception("hi"), "Exception('hi')"),
         ):
             self.assertEqual(rh.shell.quote(arg), exp)
 
@@ -114,12 +116,11 @@
     def testCmdToStr(self):
         # Dict of expected output strings to input lists.
         tests = {
-            r"a b": ['a', 'b'],
-            r"'a b' c": ['a b', 'c'],
-            r'''a "b'c"''': ['a', "b'c"],
-            r'''a "/'\$b" 'a b c' "xy'z"''':
-                ['a', "/'$b", 'a b c', "xy'z"],
-            '': [],
+            r"a b": ["a", "b"],
+            r"'a b' c": ["a b", "c"],
+            r'''a "b'c"''': ["a", "b'c"],
+            r'''a "/'\$b" 'a b c' "xy'z"''': ["a", "/'$b", "a b c", "xy'z"],
+            "": [],
         }
         self._testData(rh.shell.cmd_to_str, tests)
 
@@ -133,17 +134,37 @@
             self.assertTrue(rh.shell.boolean_shell_value(v, True))
             self.assertFalse(rh.shell.boolean_shell_value(v, False))
 
-        for v in (1234, '', 'akldjsf', '"'):
+        for v in (1234, "", "akldjsf", '"'):
             self.assertRaises(ValueError, rh.shell.boolean_shell_value, v, True)
 
-        for v in ('yes', 'YES', 'YeS', 'y', 'Y', '1', 'true', 'True', 'TRUE',):
+        for v in (
+            "yes",
+            "YES",
+            "YeS",
+            "y",
+            "Y",
+            "1",
+            "true",
+            "True",
+            "TRUE",
+        ):
             self.assertTrue(rh.shell.boolean_shell_value(v, True))
             self.assertTrue(rh.shell.boolean_shell_value(v, False))
 
-        for v in ('no', 'NO', 'nO', 'n', 'N', '0', 'false', 'False', 'FALSE',):
+        for v in (
+            "no",
+            "NO",
+            "nO",
+            "n",
+            "N",
+            "0",
+            "false",
+            "False",
+            "FALSE",
+        ):
             self.assertFalse(rh.shell.boolean_shell_value(v, True))
             self.assertFalse(rh.shell.boolean_shell_value(v, False))
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     unittest.main()
diff --git a/rh/signals.py b/rh/signals.py
index c8a8d81..75ec87c 100644
--- a/rh/signals.py
+++ b/rh/signals.py
@@ -18,7 +18,7 @@
 import signal
 import sys
 
-_path = os.path.realpath(__file__ + '/../..')
+_path = os.path.realpath(__file__ + "/../..")
 if sys.path[0] != _path:
     sys.path.insert(0, _path)
 del _path
diff --git a/rh/terminal.py b/rh/terminal.py
index a6f31d9..1ef3425 100644
--- a/rh/terminal.py
+++ b/rh/terminal.py
@@ -21,7 +21,7 @@
 import sys
 from typing import List, Optional
 
-_path = os.path.realpath(__file__ + '/../..')
+_path = os.path.realpath(__file__ + "/../..")
 if sys.path[0] != _path:
     sys.path.insert(0, _path)
 del _path
@@ -33,7 +33,7 @@
 # This will erase all content in the current line after the cursor.  This is
 # useful for partial updates & progress messages as the terminal can display
 # it better.
-CSI_ERASE_LINE_AFTER = '\x1b[K'
+CSI_ERASE_LINE_AFTER = "\x1b[K"
 
 
 class Color(object):
@@ -41,9 +41,9 @@
 
     BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8)
     BOLD = -1
-    COLOR_START = '\033[1;%dm'
-    BOLD_START = '\033[1m'
-    RESET = '\033[m'
+    COLOR_START = "\033[1;%dm"
+    BOLD_START = "\033[1m"
+    RESET = "\033[m"
 
     def __init__(self, enabled=None):
         """Create a new Color object, optionally disabling color output.
@@ -66,7 +66,7 @@
         """
         if self.enabled:
             return self.COLOR_START % (color + 30)
-        return ''
+        return ""
 
     def stop(self):
         """Returns a stop color code.
@@ -77,7 +77,7 @@
         """
         if self.enabled:
             return self.RESET
-        return ''
+        return ""
 
     def color(self, color, text):
         """Returns text with conditionally added color escape sequences.
@@ -102,9 +102,10 @@
     def enabled(self):
         """See if the colorization is enabled."""
         if self._enabled is None:
-            if 'NOCOLOR' in os.environ:
+            if "NOCOLOR" in os.environ:
                 self._enabled = not rh.shell.boolean_shell_value(
-                    os.environ['NOCOLOR'], False)
+                    os.environ["NOCOLOR"], False
+                )
             else:
                 self._enabled = sys.stderr.isatty()
         return self._enabled
@@ -118,11 +119,11 @@
       print_newline: Print a newline at the end, if sys.stderr is a TTY.
     """
     if sys.stderr.isatty():
-        output = '\r' + line + CSI_ERASE_LINE_AFTER
+        output = "\r" + line + CSI_ERASE_LINE_AFTER
         if print_newline:
-            output += '\n'
+            output += "\n"
     else:
-        output = line + '\n'
+        output = line + "\n"
 
     sys.stderr.write(output)
     sys.stderr.flush()
@@ -156,8 +157,13 @@
         raise
 
 
-def boolean_prompt(prompt='Do you want to continue?', default=True,
-                   true_value='yes', false_value='no', prolog=None):
+def boolean_prompt(
+    prompt="Do you want to continue?",
+    default=True,
+    true_value="yes",
+    false_value="no",
+    prolog=None,
+):
     """Helper function for processing boolean choice prompts.
 
     Args:
@@ -174,7 +180,8 @@
     true_text, false_text = true_value, false_value
     if true_value == false_value:
         raise ValueError(
-            f'true_value and false_value must differ: got {true_value!r}')
+            f"true_value and false_value must differ: got {true_value!r}"
+        )
 
     if default:
         true_text = true_text[0].upper() + true_text[1:]
@@ -182,8 +189,8 @@
         false_text = false_text[0].upper() + false_text[1:]
 
     if prolog:
-        prompt = f'\n{prolog}\n{prompt}'
-    prompt = '\n' + prompt
+        prompt = f"\n{prolog}\n{prompt}"
+    prompt = "\n" + prompt
 
     while True:
         response = str_prompt(prompt, choices=(true_text, false_text))
diff --git a/rh/terminal_unittest.py b/rh/terminal_unittest.py
index b76b907..9f67bba 100755
--- a/rh/terminal_unittest.py
+++ b/rh/terminal_unittest.py
@@ -21,7 +21,7 @@
 import sys
 import unittest
 
-_path = os.path.realpath(__file__ + '/../..')
+_path = os.path.realpath(__file__ + "/../..")
 if sys.path[0] != _path:
     sys.path.insert(0, _path)
 del _path
@@ -36,7 +36,7 @@
     """Verify behavior of Color class."""
 
     def setUp(self):
-        os.environ.pop('NOCOLOR', None)
+        os.environ.pop("NOCOLOR", None)
 
     def test_enabled_auto_tty(self):
         """Test automatic enable behavior based on tty."""
@@ -53,11 +53,11 @@
         """Test automatic enable behavior based on $NOCOLOR."""
         stderr = io.StringIO()
         with contextlib.redirect_stderr(stderr):
-            os.environ['NOCOLOR'] = 'yes'
+            os.environ["NOCOLOR"] = "yes"
             c = rh.terminal.Color()
             self.assertFalse(c.enabled)
 
-            os.environ['NOCOLOR'] = 'no'
+            os.environ["NOCOLOR"] = "no"
             c = rh.terminal.Color()
             self.assertTrue(c.enabled)
 
@@ -66,14 +66,14 @@
         stderr = io.StringIO()
         with contextlib.redirect_stderr(stderr):
             stderr.isatty = lambda: True
-            os.environ['NOCOLOR'] = 'no'
+            os.environ["NOCOLOR"] = "no"
             c = rh.terminal.Color()
             self.assertTrue(c.enabled)
             c = rh.terminal.Color(False)
             self.assertFalse(c.enabled)
 
             stderr.isatty = lambda: False
-            os.environ['NOCOLOR'] = 'yes'
+            os.environ["NOCOLOR"] = "yes"
             c = rh.terminal.Color()
             self.assertFalse(c.enabled)
             c = rh.terminal.Color(True)
@@ -82,17 +82,18 @@
     def test_output_disabled(self):
         """Test output when coloring is disabled."""
         c = rh.terminal.Color(False)
-        self.assertEqual(c.start(rh.terminal.Color.BLACK), '')
-        self.assertEqual(c.color(rh.terminal.Color.BLACK, 'foo'), 'foo')
-        self.assertEqual(c.stop(), '')
+        self.assertEqual(c.start(rh.terminal.Color.BLACK), "")
+        self.assertEqual(c.color(rh.terminal.Color.BLACK, "foo"), "foo")
+        self.assertEqual(c.stop(), "")
 
     def test_output_enabled(self):
         """Test output when coloring is enabled."""
         c = rh.terminal.Color(True)
-        self.assertEqual(c.start(rh.terminal.Color.BLACK), '\x1b[1;30m')
-        self.assertEqual(c.color(rh.terminal.Color.BLACK, 'foo'),
-                         '\x1b[1;30mfoo\x1b[m')
-        self.assertEqual(c.stop(), '\x1b[m')
+        self.assertEqual(c.start(rh.terminal.Color.BLACK), "\x1b[1;30m")
+        self.assertEqual(
+            c.color(rh.terminal.Color.BLACK, "foo"), "\x1b[1;30mfoo\x1b[m"
+        )
+        self.assertEqual(c.stop(), "\x1b[m")
 
 
 class PrintStatusLine(unittest.TestCase):
@@ -103,18 +104,18 @@
         stderr = io.StringIO()
         stderr.isatty = lambda: True
         with contextlib.redirect_stderr(stderr):
-            rh.terminal.print_status_line('foo')
-            rh.terminal.print_status_line('bar', print_newline=True)
+            rh.terminal.print_status_line("foo")
+            rh.terminal.print_status_line("bar", print_newline=True)
         csi = rh.terminal.CSI_ERASE_LINE_AFTER
-        self.assertEqual(stderr.getvalue(), f'\rfoo{csi}\rbar{csi}\n')
+        self.assertEqual(stderr.getvalue(), f"\rfoo{csi}\rbar{csi}\n")
 
     def test_no_terminal(self):
         """Check tty-less behavior."""
         stderr = io.StringIO()
         with contextlib.redirect_stderr(stderr):
-            rh.terminal.print_status_line('foo')
-            rh.terminal.print_status_line('bar', print_newline=True)
-        self.assertEqual(stderr.getvalue(), 'foo\nbar\n')
+            rh.terminal.print_status_line("foo")
+            rh.terminal.print_status_line("bar", print_newline=True)
+        self.assertEqual(stderr.getvalue(), "foo\nbar\n")
 
 
 @contextlib.contextmanager
@@ -146,20 +147,21 @@
         stdout = io.StringIO()
         with redirect_stdin(self.stdin), contextlib.redirect_stdout(stdout):
             # Test EOF behavior.
-            self.assertIsNone(rh.terminal.str_prompt('foo', ('a', 'b')))
+            self.assertIsNone(rh.terminal.str_prompt("foo", ("a", "b")))
 
             # Test enter behavior.
-            self.set_stdin('\n')
-            self.assertEqual(rh.terminal.str_prompt('foo', ('a', 'b')), '')
+            self.set_stdin("\n")
+            self.assertEqual(rh.terminal.str_prompt("foo", ("a", "b")), "")
 
             # Lowercase inputs.
-            self.set_stdin('Ok')
-            self.assertEqual(rh.terminal.str_prompt('foo', ('a', 'b')), 'ok')
+            self.set_stdin("Ok")
+            self.assertEqual(rh.terminal.str_prompt("foo", ("a", "b")), "ok")
 
             # Don't lowercase inputs.
-            self.set_stdin('Ok')
+            self.set_stdin("Ok")
             self.assertEqual(
-                rh.terminal.str_prompt('foo', ('a', 'b'), lower=False), 'Ok')
+                rh.terminal.str_prompt("foo", ("a", "b"), lower=False), "Ok"
+            )
 
 
 class BooleanPromptTests(unittest.TestCase):
@@ -180,20 +182,20 @@
         stdout = io.StringIO()
         with redirect_stdin(self.stdin), contextlib.redirect_stdout(stdout):
             # Default values.  Will loop to EOF when it doesn't match anything.
-            for v in ('', '\n', 'oops'):
+            for v in ("", "\n", "oops"):
                 self.set_stdin(v)
                 self.assertTrue(rh.terminal.boolean_prompt())
 
             # False values.
-            for v in ('n', 'N', 'no', 'NO'):
+            for v in ("n", "N", "no", "NO"):
                 self.set_stdin(v)
                 self.assertFalse(rh.terminal.boolean_prompt())
 
             # True values.
-            for v in ('y', 'Y', 'ye', 'yes', 'YES'):
+            for v in ("y", "Y", "ye", "yes", "YES"):
                 self.set_stdin(v)
                 self.assertTrue(rh.terminal.boolean_prompt())
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     unittest.main()
diff --git a/rh/utils.py b/rh/utils.py
index d4001d6..0a599f3 100644
--- a/rh/utils.py
+++ b/rh/utils.py
@@ -23,7 +23,7 @@
 import tempfile
 import time
 
-_path = os.path.realpath(__file__ + '/../..')
+_path = os.path.realpath(__file__ + "/../..")
 if sys.path[0] != _path:
     sys.path.insert(0, _path)
 del _path
@@ -42,11 +42,11 @@
     total = delta.total_seconds()
     hours, rem = divmod(total, 3600)
     mins, secs = divmod(rem, 60)
-    ret = f'{int(secs)}.{delta.microseconds // 1000:03}s'
+    ret = f"{int(secs)}.{delta.microseconds // 1000:03}s"
     if mins:
-        ret = f'{int(mins)}m{ret}'
+        ret = f"{int(mins)}m{ret}"
     if hours:
-        ret = f'{int(hours)}h{ret}'
+        ret = f"{int(hours)}h{ret}"
     return ret
 
 
@@ -58,7 +58,8 @@
 
     def __init__(self, args=None, returncode=None, stdout=None, stderr=None):
         super().__init__(
-            args=args, returncode=returncode, stdout=stdout, stderr=stderr)
+            args=args, returncode=returncode, stdout=stdout, stderr=stderr
+        )
 
     @property
     def cmd(self):
@@ -106,7 +107,7 @@
     @property
     def cmdstr(self):
         """Return self.cmd as a well shell-quoted string for debugging."""
-        return '' if self.cmd is None else rh.shell.cmd_to_str(self.cmd)
+        return "" if self.cmd is None else rh.shell.cmd_to_str(self.cmd)
 
     def stringify(self, stdout=True, stderr=True):
         """Custom method for controlling what is included in stringifying this.
@@ -119,7 +120,7 @@
           A summary string for this result.
         """
         items = [
-            f'return code: {self.returncode}; command: {self.cmdstr}',
+            f"return code: {self.returncode}; command: {self.cmdstr}",
         ]
         if stderr and self.stderr:
             items.append(self.stderr)
@@ -127,7 +128,7 @@
             items.append(self.stdout)
         if self.msg:
             items.append(self.msg)
-        return '\n'.join(items)
+        return "\n".join(items)
 
     def __str__(self):
         return self.stringify()
@@ -141,8 +142,9 @@
     """
 
 
-def _kill_child_process(proc, int_timeout, kill_timeout, cmd, original_handler,
-                        signum, frame):
+def _kill_child_process(
+    proc, int_timeout, kill_timeout, cmd, original_handler, signum, frame
+):
     """Used as a signal handler by RunCommand.
 
     This is internal to Runcommand.  No other code should use this.
@@ -173,8 +175,10 @@
                 # Still doesn't want to die.  Too bad, so sad, time to die.
                 proc.kill()
         except EnvironmentError as e:
-            print(f'Ignoring unhandled exception in _kill_child_process: {e}',
-                  file=sys.stderr)
+            print(
+                f"Ignoring unhandled exception in _kill_child_process: {e}",
+                file=sys.stderr,
+            )
 
         # Ensure our child process has been reaped, but don't wait forever.
         proc.wait_lock_breaker(timeout=60)
@@ -182,7 +186,8 @@
     if not rh.signals.relay_signal(original_handler, signum, frame):
         # Mock up our own, matching exit code for signaling.
         raise TerminateCalledProcessError(
-            signum << 8, cmd, msg=f'Received signal {signum}')
+            signum << 8, cmd, msg=f"Received signal {signum}"
+        )
 
 
 class _Popen(subprocess.Popen):
@@ -226,7 +231,7 @@
         Workaround https://bugs.python.org/issue25960.
         """
         # If the lock doesn't exist, or is not locked, call the func directly.
-        lock = getattr(self, '_waitpid_lock', None)
+        lock = getattr(self, "_waitpid_lock", None)
         if lock is not None and lock.locked():
             try:
                 lock.release()
@@ -248,9 +253,21 @@
 
 # We use the keyword arg |input| which trips up pylint checks.
 # pylint: disable=redefined-builtin
-def run(cmd, redirect_stdout=False, redirect_stderr=False, cwd=None, input=None,
-        shell=False, env=None, extra_env=None, combine_stdout_stderr=False,
-        check=True, int_timeout=1, kill_timeout=1, capture_output=False):
+def run(
+    cmd,
+    redirect_stdout=False,
+    redirect_stderr=False,
+    cwd=None,
+    input=None,
+    shell=False,
+    env=None,
+    extra_env=None,
+    combine_stdout_stderr=False,
+    check=True,
+    int_timeout=1,
+    kill_timeout=1,
+    capture_output=False,
+):
     """Runs a command.
 
     Args:
@@ -307,7 +324,7 @@
             # issue in this particular case since our usage gurantees deletion,
             # and since this is primarily triggered during hard cgroups
             # shutdown.
-            return tempfile.TemporaryFile(dir='/tmp', buffering=0)
+            return tempfile.TemporaryFile(dir="/tmp", buffering=0)
 
     # Modify defaults based on parameters.
     # Note that tempfiles must be unbuffered else attempts to read
@@ -334,18 +351,18 @@
     # Otherwise we assume it's a file object that can be read from directly.
     if isinstance(input, str):
         stdin = subprocess.PIPE
-        input = input.encode('utf-8')
+        input = input.encode("utf-8")
     elif input is not None:
         stdin = input
         input = None
 
     if isinstance(cmd, str):
         if not shell:
-            raise Exception('Cannot run a string command without a shell')
-        cmd = ['/bin/bash', '-c', cmd]
+            raise Exception("Cannot run a string command without a shell")
+        cmd = ["/bin/bash", "-c", cmd]
         shell = False
     elif shell:
-        raise Exception('Cannot run an array command with a shell')
+        raise Exception("Cannot run an array command with a shell")
 
     # If we are using enter_chroot we need to use enterchroot pass env through
     # to the final command.
@@ -355,20 +372,33 @@
     def ensure_text(s):
         """Make sure |s| is a string if it's bytes."""
         if isinstance(s, bytes):
-            s = s.decode('utf-8', 'replace')
+            s = s.decode("utf-8", "replace")
         return s
 
     result.args = cmd
 
     proc = None
     try:
-        proc = _Popen(cmd, cwd=cwd, stdin=stdin, stdout=popen_stdout,
-                      stderr=popen_stderr, shell=False, env=env,
-                      close_fds=True)
+        proc = _Popen(
+            cmd,
+            cwd=cwd,
+            stdin=stdin,
+            stdout=popen_stdout,
+            stderr=popen_stderr,
+            shell=False,
+            env=env,
+            close_fds=True,
+        )
 
         old_sigint = signal.getsignal(signal.SIGINT)
-        handler = functools.partial(_kill_child_process, proc, int_timeout,
-                                    kill_timeout, cmd, old_sigint)
+        handler = functools.partial(
+            _kill_child_process,
+            proc,
+            int_timeout,
+            kill_timeout,
+            cmd,
+            old_sigint,
+        )
         # We have to ignore ValueError in case we're run from a thread.
         try:
             signal.signal(signal.SIGINT, handler)
@@ -376,8 +406,14 @@
             old_sigint = None
 
         old_sigterm = signal.getsignal(signal.SIGTERM)
-        handler = functools.partial(_kill_child_process, proc, int_timeout,
-                                    kill_timeout, cmd, old_sigterm)
+        handler = functools.partial(
+            _kill_child_process,
+            proc,
+            int_timeout,
+            kill_timeout,
+            cmd,
+            old_sigterm,
+        )
         try:
             signal.signal(signal.SIGTERM, handler)
         except ValueError:
@@ -408,13 +444,16 @@
         result.returncode = proc.returncode
 
         if check and proc.returncode:
-            msg = f'cwd={cwd}'
+            msg = f"cwd={cwd}"
             if extra_env:
-                msg += f', extra env={extra_env}'
+                msg += f", extra env={extra_env}"
             raise CalledProcessError(
-                result.returncode, result.cmd, msg=msg,
+                result.returncode,
+                result.cmd,
+                msg=msg,
                 stdout=ensure_text(result.stdout),
-                stderr=ensure_text(result.stderr))
+                stderr=ensure_text(result.stderr),
+            )
     except OSError as e:
         # Avoid leaking tempfiles.
         if popen_stdout is not None and not isinstance(popen_stdout, int):
@@ -424,7 +463,7 @@
 
         estr = str(e)
         if e.errno == errno.EACCES:
-            estr += '; does the program need `chmod a+x`?'
+            estr += "; does the program need `chmod a+x`?"
         if not check:
             result = CompletedProcess(args=cmd, returncode=255)
             if combine_stdout_stderr:
@@ -433,20 +472,26 @@
                 result.stderr = estr
         else:
             raise CalledProcessError(
-                result.returncode, result.cmd, msg=estr,
+                result.returncode,
+                result.cmd,
+                msg=estr,
                 stdout=ensure_text(result.stdout),
-                stderr=ensure_text(result.stderr)) from e
+                stderr=ensure_text(result.stderr),
+            ) from e
     finally:
         if proc is not None:
             # Ensure the process is dead.
             # Some pylint3 versions are confused here.
             # pylint: disable=too-many-function-args
-            _kill_child_process(proc, int_timeout, kill_timeout, cmd, None,
-                                None, None)
+            _kill_child_process(
+                proc, int_timeout, kill_timeout, cmd, None, None, None
+            )
 
     # Make sure output is returned as a string rather than bytes.
     result.stdout = ensure_text(result.stdout)
     result.stderr = ensure_text(result.stderr)
 
     return result
+
+
 # pylint: enable=redefined-builtin
diff --git a/rh/utils_unittest.py b/rh/utils_unittest.py
index bf720a7..5b977cd 100755
--- a/rh/utils_unittest.py
+++ b/rh/utils_unittest.py
@@ -21,7 +21,7 @@
 import sys
 import unittest
 
-_path = os.path.realpath(__file__ + '/../..')
+_path = os.path.realpath(__file__ + "/../..")
 if sys.path[0] != _path:
     sys.path.insert(0, _path)
 del _path
@@ -39,27 +39,27 @@
     def test_same(self):
         """Check timedelta of 0 seconds."""
         delta = datetime.timedelta(0)
-        self.assertEqual('0.000s', rh.utils.timedelta_str(delta))
+        self.assertEqual("0.000s", rh.utils.timedelta_str(delta))
 
     def test_millisecondss(self):
         """Check timedelta of milliseconds."""
         delta = datetime.timedelta(seconds=0.123456)
-        self.assertEqual('0.123s', rh.utils.timedelta_str(delta))
+        self.assertEqual("0.123s", rh.utils.timedelta_str(delta))
 
     def test_seconds(self):
         """Check timedelta of seconds."""
         delta = datetime.timedelta(seconds=12.3)
-        self.assertEqual('12.300s', rh.utils.timedelta_str(delta))
+        self.assertEqual("12.300s", rh.utils.timedelta_str(delta))
 
     def test_minutes(self):
         """Check timedelta of minutes."""
         delta = datetime.timedelta(seconds=72.3)
-        self.assertEqual('1m12.300s', rh.utils.timedelta_str(delta))
+        self.assertEqual("1m12.300s", rh.utils.timedelta_str(delta))
 
     def test_hours(self):
         """Check timedelta of hours."""
         delta = datetime.timedelta(seconds=4000.3)
-        self.assertEqual('1h6m40.300s', rh.utils.timedelta_str(delta))
+        self.assertEqual("1h6m40.300s", rh.utils.timedelta_str(delta))
 
 
 class CompletedProcessTests(unittest.TestCase):
@@ -68,28 +68,28 @@
     def test_empty_cmdstr(self):
         """Check cmdstr with an empty command."""
         result = rh.utils.CompletedProcess(args=[])
-        self.assertEqual('', result.cmdstr)
+        self.assertEqual("", result.cmdstr)
 
     def test_basic_cmdstr(self):
         """Check cmdstr with a basic command command."""
-        result = rh.utils.CompletedProcess(args=['ls', 'a b'])
+        result = rh.utils.CompletedProcess(args=["ls", "a b"])
         self.assertEqual("ls 'a b'", result.cmdstr)
 
     def test_str(self):
         """Check str() handling."""
         # We don't enforce much, just that it doesn't crash.
         result = rh.utils.CompletedProcess()
-        self.assertNotEqual('', str(result))
+        self.assertNotEqual("", str(result))
         result = rh.utils.CompletedProcess(args=[])
-        self.assertNotEqual('', str(result))
+        self.assertNotEqual("", str(result))
 
     def test_repr(self):
         """Check repr() handling."""
         # We don't enforce much, just that it doesn't crash.
         result = rh.utils.CompletedProcess()
-        self.assertNotEqual('', repr(result))
+        self.assertNotEqual("", repr(result))
         result = rh.utils.CompletedProcess(args=[])
-        self.assertNotEqual('', repr(result))
+        self.assertNotEqual("", repr(result))
 
 
 class CalledProcessErrorTests(unittest.TestCase):
@@ -97,41 +97,42 @@
 
     def test_basic(self):
         """Basic test we can create a normal instance."""
-        rh.utils.CalledProcessError(0, ['mycmd'])
+        rh.utils.CalledProcessError(0, ["mycmd"])
 
     def test_stringify(self):
         """Check stringify() handling."""
         # We don't assert much so we leave flexibility in changing format.
-        err = rh.utils.CalledProcessError(0, ['mycmd'])
-        self.assertIn('mycmd', err.stringify())
+        err = rh.utils.CalledProcessError(0, ["mycmd"])
+        self.assertIn("mycmd", err.stringify())
 
     def test_str(self):
         """Check str() handling."""
         # We don't assert much so we leave flexibility in changing format.
-        err = rh.utils.CalledProcessError(0, ['mycmd'])
-        self.assertIn('mycmd', str(err))
+        err = rh.utils.CalledProcessError(0, ["mycmd"])
+        self.assertIn("mycmd", str(err))
 
     def test_repr(self):
         """Check repr() handling."""
         # We don't assert much so we leave flexibility in changing format.
-        err = rh.utils.CalledProcessError(0, ['mycmd'])
-        self.assertNotEqual('', repr(err))
+        err = rh.utils.CalledProcessError(0, ["mycmd"])
+        self.assertNotEqual("", repr(err))
 
     def test_output(self):
         """Make sure .output is removed and .stdout works."""
         e = rh.utils.CalledProcessError(
-            0, ['true'], stdout='STDOUT', stderr='STDERR')
+            0, ["true"], stdout="STDOUT", stderr="STDERR"
+        )
         with self.assertRaises(AttributeError):
             assert e.output is None
-        assert e.stdout == 'STDOUT'
-        assert e.stderr == 'STDERR'
+        assert e.stdout == "STDOUT"
+        assert e.stderr == "STDERR"
 
-        e.stdout = 'STDout'
-        e.stderr = 'STDerr'
+        e.stdout = "STDout"
+        e.stderr = "STDerr"
         with self.assertRaises(AttributeError):
             assert e.output is None
-        assert e.stdout == 'STDout'
-        assert e.stderr == 'STDerr'
+        assert e.stdout == "STDout"
+        assert e.stderr == "STDerr"
 
 
 class RunCommandTests(unittest.TestCase):
@@ -139,96 +140,100 @@
 
     def test_basic(self):
         """Simple basic test."""
-        ret = rh.utils.run(['true'])
-        self.assertEqual('true', ret.cmdstr)
+        ret = rh.utils.run(["true"])
+        self.assertEqual("true", ret.cmdstr)
         self.assertIsNone(ret.stdout)
         self.assertIsNone(ret.stderr)
 
     def test_stdout_capture(self):
         """Verify output capturing works."""
-        ret = rh.utils.run(['echo', 'hi'], redirect_stdout=True)
-        self.assertEqual('hi\n', ret.stdout)
+        ret = rh.utils.run(["echo", "hi"], redirect_stdout=True)
+        self.assertEqual("hi\n", ret.stdout)
         self.assertIsNone(ret.stderr)
 
     def test_stderr_capture(self):
         """Verify stderr capturing works."""
-        ret = rh.utils.run(['sh', '-c', 'echo hi >&2'], redirect_stderr=True)
+        ret = rh.utils.run(["sh", "-c", "echo hi >&2"], redirect_stderr=True)
         self.assertIsNone(ret.stdout)
-        self.assertEqual('hi\n', ret.stderr)
+        self.assertEqual("hi\n", ret.stderr)
 
     def test_stdout_utf8(self):
         """Verify reading UTF-8 data works."""
-        ret = rh.utils.run(['printf', r'\xc3\x9f'], redirect_stdout=True)
-        self.assertEqual('ß', ret.stdout)
+        ret = rh.utils.run(["printf", r"\xc3\x9f"], redirect_stdout=True)
+        self.assertEqual("ß", ret.stdout)
         self.assertIsNone(ret.stderr)
 
     def test_stdin_utf8(self):
         """Verify writing UTF-8 data works."""
-        ret = rh.utils.run(['cat'], redirect_stdout=True, input='ß')
-        self.assertEqual('ß', ret.stdout)
+        ret = rh.utils.run(["cat"], redirect_stdout=True, input="ß")
+        self.assertEqual("ß", ret.stdout)
         self.assertIsNone(ret.stderr)
 
     def test_check_false(self):
         """Verify handling of check=False."""
-        ret = rh.utils.run(['false'], check=False)
+        ret = rh.utils.run(["false"], check=False)
         self.assertNotEqual(0, ret.returncode)
-        self.assertIn('false', str(ret))
+        self.assertIn("false", str(ret))
 
-        ret = rh.utils.run(['true'], check=False)
+        ret = rh.utils.run(["true"], check=False)
         self.assertEqual(0, ret.returncode)
-        self.assertIn('true', str(ret))
+        self.assertIn("true", str(ret))
 
     def test_check_true(self):
         """Verify handling of check=True."""
         with self.assertRaises(rh.utils.CalledProcessError) as e:
-            rh.utils.run(['false'], check=True)
+            rh.utils.run(["false"], check=True)
         err = e.exception
         self.assertNotEqual(0, err.returncode)
-        self.assertIn('false', str(err))
+        self.assertIn("false", str(err))
 
-        ret = rh.utils.run(['true'], check=True)
+        ret = rh.utils.run(["true"], check=True)
         self.assertEqual(0, ret.returncode)
-        self.assertIn('true', str(ret))
+        self.assertIn("true", str(ret))
 
     def test_check_false_output(self):
         """Verify handling of output capturing w/check=False."""
         with self.assertRaises(rh.utils.CalledProcessError) as e:
-            rh.utils.run(['sh', '-c', 'echo out; echo err >&2; false'],
-                         check=True, capture_output=True)
+            rh.utils.run(
+                ["sh", "-c", "echo out; echo err >&2; false"],
+                check=True,
+                capture_output=True,
+            )
         err = e.exception
         self.assertNotEqual(0, err.returncode)
-        self.assertIn('false', str(err))
+        self.assertIn("false", str(err))
 
     def test_check_true_missing_prog_output(self):
         """Verify handling of output capturing w/missing progs."""
         with self.assertRaises(rh.utils.CalledProcessError) as e:
-            rh.utils.run(['./!~a/b/c/d/'], check=True, capture_output=True)
+            rh.utils.run(["./!~a/b/c/d/"], check=True, capture_output=True)
         err = e.exception
         self.assertNotEqual(0, err.returncode)
-        self.assertIn('a/b/c/d', str(err))
+        self.assertIn("a/b/c/d", str(err))
 
     def test_check_false_missing_prog_output(self):
         """Verify handling of output capturing w/missing progs."""
-        ret = rh.utils.run(['./!~a/b/c/d/'], check=False, capture_output=True)
+        ret = rh.utils.run(["./!~a/b/c/d/"], check=False, capture_output=True)
         self.assertNotEqual(0, ret.returncode)
-        self.assertIn('a/b/c/d', str(ret))
+        self.assertIn("a/b/c/d", str(ret))
 
     def test_check_false_missing_prog_combined_output(self):
         """Verify handling of combined output capturing w/missing progs."""
         with self.assertRaises(rh.utils.CalledProcessError) as e:
-            rh.utils.run(['./!~a/b/c/d/'], check=True,
-                         combine_stdout_stderr=True)
+            rh.utils.run(
+                ["./!~a/b/c/d/"], check=True, combine_stdout_stderr=True
+            )
         err = e.exception
         self.assertNotEqual(0, err.returncode)
-        self.assertIn('a/b/c/d', str(err))
+        self.assertIn("a/b/c/d", str(err))
 
     def test_pathlib(self):
         """Verify pathlib arguments work."""
-        result = rh.utils.run(['true', Path('/')])
+        result = rh.utils.run(["true", Path("/")])
         # Verify stringify behavior.
         str(result)
-        self.assertEqual(result.cmdstr, 'true /')
+        self.assertEqual(result.cmdstr, "true /")
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     unittest.main()
diff --git a/run_tests b/run_tests
index 2682dac..605116e 100755
--- a/run_tests
+++ b/run_tests
@@ -42,10 +42,26 @@
     return pytest.main(argv)
 
 
+def run_black() -> int:
+    """Returns the exit code from black."""
+    # Black by default only matches .py files.  We have to list standalone
+    # scripts manually.
+    extra_programs = [
+        "run_tests",
+    ]
+    return subprocess.run(
+        [sys.executable, "-m", "black", "--verbose", "--check", ROOT_DIR]
+        + extra_programs,
+        check=False,
+        cwd=ROOT_DIR,
+    ).returncode
+
+
 def main(argv):
     """The main entry."""
     checks = (
         functools.partial(run_pytest, argv),
+        run_black,
     )
     # Run all the tests all the time to get full feedback.  Don't exit on the
     # first error as that makes it more difficult to iterate in the CQ.
diff --git a/tools/android_test_mapping_format.py b/tools/android_test_mapping_format.py
index 7780859..d09e52b 100755
--- a/tools/android_test_mapping_format.py
+++ b/tools/android_test_mapping_format.py
@@ -29,7 +29,7 @@
 import sys
 from typing import Any, Dict
 
-_path = os.path.realpath(__file__ + '/../..')
+_path = os.path.realpath(__file__ + "/../..")
 if sys.path[0] != _path:
     sys.path.insert(0, _path)
 del _path
@@ -39,21 +39,21 @@
 # pylint: disable=wrong-import-position
 import rh.git
 
-_IMPORTS = 'imports'
-_NAME = 'name'
-_OPTIONS = 'options'
-_PATH = 'path'
-_HOST = 'host'
-_PREFERRED_TARGETS = 'preferred_targets'
-_FILE_PATTERNS = 'file_patterns'
-_INVALID_IMPORT_CONFIG = 'Invalid import config in TEST_MAPPING file'
-_INVALID_TEST_CONFIG = 'Invalid test config in TEST_MAPPING file'
+_IMPORTS = "imports"
+_NAME = "name"
+_OPTIONS = "options"
+_PATH = "path"
+_HOST = "host"
+_PREFERRED_TARGETS = "preferred_targets"
+_FILE_PATTERNS = "file_patterns"
+_INVALID_IMPORT_CONFIG = "Invalid import config in TEST_MAPPING file"
+_INVALID_TEST_CONFIG = "Invalid test config in TEST_MAPPING file"
 _TEST_MAPPING_URL = (
-    'https://source.android.com/compatibility/tests/development/'
-    'test-mapping')
+    "https://source.android.com/compatibility/tests/development/test-mapping"
+)
 
 # Pattern used to identify line-level '//'-format comment in TEST_MAPPING file.
-_COMMENTS_RE = re.compile(r'^\s*//')
+_COMMENTS_RE = re.compile(r"^\s*//")
 
 
 class Error(Exception):
@@ -73,8 +73,9 @@
     Returns:
         Valid json string without comments.
     """
-    return ''.join(
-        '\n' if _COMMENTS_RE.match(x) else x for x in json_data.splitlines())
+    return "".join(
+        "\n" if _COMMENTS_RE.match(x) else x for x in json_data.splitlines()
+    )
 
 
 def _validate_import(entry: Dict[str, Any], test_mapping_file: str):
@@ -89,12 +90,14 @@
     """
     if len(entry) != 1:
         raise InvalidTestMappingError(
-            f'{_INVALID_IMPORT_CONFIG} {test_mapping_file}. Each import can '
-            f'only have one `path` setting. Failed entry: {entry}')
+            f"{_INVALID_IMPORT_CONFIG} {test_mapping_file}. Each import can "
+            f"only have one `path` setting. Failed entry: {entry}"
+        )
     if _PATH not in entry:
         raise InvalidTestMappingError(
-            f'{_INVALID_IMPORT_CONFIG} {test_mapping_file}. Import can '
-            f'only have one `path` setting. Failed entry: {entry}')
+            f"{_INVALID_IMPORT_CONFIG} {test_mapping_file}. Import can "
+            f"only have one `path` setting. Failed entry: {entry}"
+        )
 
 
 def _validate_test(test: Dict[str, Any], test_mapping_file: str) -> bool:
@@ -109,36 +112,41 @@
     """
     if _NAME not in test:
         raise InvalidTestMappingError(
-
-            f'{_INVALID_TEST_CONFIG} {test_mapping_file}. Test config must '
-            f'have a `name` setting. Failed test config: {test}')
+            f"{_INVALID_TEST_CONFIG} {test_mapping_file}. Test config must "
+            f"have a `name` setting. Failed test config: {test}"
+        )
 
     if not isinstance(test.get(_HOST, False), bool):
         raise InvalidTestMappingError(
-            f'{_INVALID_TEST_CONFIG} {test_mapping_file}. `host` setting in '
-            f'test config can only have boolean value of `true` or `false`. '
-            f'Failed test config: {test}')
+            f"{_INVALID_TEST_CONFIG} {test_mapping_file}. `host` setting in "
+            f"test config can only have boolean value of `true` or `false`. "
+            f"Failed test config: {test}"
+        )
 
     for key in (_PREFERRED_TARGETS, _FILE_PATTERNS):
         value = test.get(key, [])
-        if (not isinstance(value, list) or
-            any(not isinstance(t, str) for t in value)):
+        if not isinstance(value, list) or any(
+            not isinstance(t, str) for t in value
+        ):
             raise InvalidTestMappingError(
-                f'{_INVALID_TEST_CONFIG} {test_mapping_file}. `{key}` setting '
-                f'in test config can only be a list of strings. '
-                f'Failed test config: {test}')
+                f"{_INVALID_TEST_CONFIG} {test_mapping_file}. `{key}` setting "
+                f"in test config can only be a list of strings. "
+                f"Failed test config: {test}"
+            )
 
     for option in test.get(_OPTIONS, []):
         if not isinstance(option, dict):
             raise InvalidTestMappingError(
-                f'{_INVALID_TEST_CONFIG} {test_mapping_file}. Option setting '
-                f'in test config can only be a dictionary of key-val setting. '
-                f'Failed entry: {option}')
+                f"{_INVALID_TEST_CONFIG} {test_mapping_file}. Option setting "
+                f"in test config can only be a dictionary of key-val setting. "
+                f"Failed entry: {option}"
+            )
         if len(option) != 1:
             raise InvalidTestMappingError(
-                f'{_INVALID_TEST_CONFIG} {test_mapping_file}. Each option '
-                f'setting can only have one key-val setting. '
-                f'Failed entry: {option}')
+                f"{_INVALID_TEST_CONFIG} {test_mapping_file}. Each option "
+                f"setting can only have one key-val setting. "
+                f"Failed entry: {option}"
+            )
 
 
 def process_file(test_mapping_file: str):
@@ -148,10 +156,11 @@
     except ValueError as exception:
         # The file is not a valid JSON file.
         print(
-            f'Invalid JSON data in TEST_MAPPING file '
-            f'Failed to parse JSON data: {test_mapping_file}, '
-            f'error: {exception}',
-            file=sys.stderr)
+            f"Invalid JSON data in TEST_MAPPING file "
+            f"Failed to parse JSON data: {test_mapping_file}, "
+            f"error: {exception}",
+            file=sys.stderr,
+        )
         raise
 
     for group, value in test_mapping_data.items():
@@ -168,10 +177,11 @@
 def get_parser():
     """Returns a command line parser."""
     parser = argparse.ArgumentParser(description=__doc__)
-    parser.add_argument('--commit', type=str,
-                        help='Specify the commit to validate.')
-    parser.add_argument('project_dir')
-    parser.add_argument('files', nargs='+')
+    parser.add_argument(
+        "--commit", type=str, help="Specify the commit to validate."
+    )
+    parser.add_argument("project_dir")
+    parser.add_argument("files", nargs="+")
     return parser
 
 
@@ -184,15 +194,19 @@
             if opts.commit:
                 json_data = rh.git.get_file_content(opts.commit, filename)
             else:
-                with open(os.path.join(opts.project_dir, filename),
-                          encoding='utf-8') as file:
+                with open(
+                    os.path.join(opts.project_dir, filename), encoding="utf-8"
+                ) as file:
                     json_data = file.read()
             process_file(json_data)
     except:
-        print(f'Visit {_TEST_MAPPING_URL} for details about the format of '
-              'TEST_MAPPING file.', file=sys.stderr)
+        print(
+            f"Visit {_TEST_MAPPING_URL} for details about the format of "
+            "TEST_MAPPING file.",
+            file=sys.stderr,
+        )
         raise
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     sys.exit(main(sys.argv[1:]))
diff --git a/tools/android_test_mapping_format_unittest.py b/tools/android_test_mapping_format_unittest.py
index cf3c3ca..c06a535 100755
--- a/tools/android_test_mapping_format_unittest.py
+++ b/tools/android_test_mapping_format_unittest.py
@@ -190,121 +190,132 @@
 
     def setUp(self):
         self.tempdir = tempfile.mkdtemp()
-        self.test_mapping_file = os.path.join(self.tempdir, 'TEST_MAPPING')
+        self.test_mapping_file = os.path.join(self.tempdir, "TEST_MAPPING")
 
     def tearDown(self):
         shutil.rmtree(self.tempdir)
 
     def test_valid_test_mapping(self):
-        """Verify that the check doesn't raise any error for valid test mapping.
-        """
-        with open(self.test_mapping_file, 'w', encoding='utf-8') as file:
+        """Verify that the check doesn't raise errors for valid test mapping."""
+        with open(self.test_mapping_file, "w", encoding="utf-8") as file:
             file.write(_VALID_TEST_MAPPING)
-        with open(self.test_mapping_file, 'r', encoding='utf-8') as file:
+        with open(self.test_mapping_file, "r", encoding="utf-8") as file:
             android_test_mapping_format.process_file(file.read())
 
     def test_invalid_test_mapping_bad_json(self):
         """Verify that TEST_MAPPING file with bad json can be detected."""
-        with open(self.test_mapping_file, 'w', encoding='utf-8') as file:
+        with open(self.test_mapping_file, "w", encoding="utf-8") as file:
             file.write(_BAD_JSON)
-        with open(self.test_mapping_file, 'r', encoding='utf-8') as file:
+        with open(self.test_mapping_file, "r", encoding="utf-8") as file:
             self.assertRaises(
-                ValueError, android_test_mapping_format.process_file,
-                file.read())
+                ValueError,
+                android_test_mapping_format.process_file,
+                file.read(),
+            )
 
     def test_invalid_test_mapping_wrong_test_key(self):
         """Verify that test config using wrong key can be detected."""
-        with open(self.test_mapping_file, 'w', encoding='utf-8') as file:
+        with open(self.test_mapping_file, "w", encoding="utf-8") as file:
             file.write(_BAD_TEST_WRONG_KEY)
-        with open(self.test_mapping_file, 'r', encoding='utf-8') as file:
+        with open(self.test_mapping_file, "r", encoding="utf-8") as file:
             self.assertRaises(
                 android_test_mapping_format.InvalidTestMappingError,
                 android_test_mapping_format.process_file,
-                file.read())
+                file.read(),
+            )
 
     def test_invalid_test_mapping_wrong_test_value(self):
         """Verify that test config using wrong host value can be detected."""
-        with open(self.test_mapping_file, 'w', encoding='utf-8') as file:
+        with open(self.test_mapping_file, "w", encoding="utf-8") as file:
             file.write(_BAD_TEST_WRONG_HOST_VALUE)
-        with open(self.test_mapping_file, 'r', encoding='utf-8') as file:
+        with open(self.test_mapping_file, "r", encoding="utf-8") as file:
             self.assertRaises(
                 android_test_mapping_format.InvalidTestMappingError,
                 android_test_mapping_format.process_file,
-                file.read())
+                file.read(),
+            )
 
     def test_invalid_test_mapping_wrong_preferred_targets_value(self):
         """Verify invalid preferred_targets are rejected."""
-        with open(self.test_mapping_file, 'w', encoding='utf-8') as file:
+        with open(self.test_mapping_file, "w", encoding="utf-8") as file:
             file.write(_BAD_TEST_WRONG_PREFERRED_TARGETS_VALUE_NONE_LIST)
-        with open(self.test_mapping_file, 'r', encoding='utf-8') as file:
+        with open(self.test_mapping_file, "r", encoding="utf-8") as file:
             self.assertRaises(
                 android_test_mapping_format.InvalidTestMappingError,
                 android_test_mapping_format.process_file,
-                file.read())
-        with open(self.test_mapping_file, 'w', encoding='utf-8') as file:
+                file.read(),
+            )
+        with open(self.test_mapping_file, "w", encoding="utf-8") as file:
             file.write(_BAD_TEST_WRONG_PREFERRED_TARGETS_VALUE_WRONG_TYPE)
-        with open(self.test_mapping_file, 'r', encoding='utf-8') as file:
+        with open(self.test_mapping_file, "r", encoding="utf-8") as file:
             self.assertRaises(
                 android_test_mapping_format.InvalidTestMappingError,
                 android_test_mapping_format.process_file,
-                file.read())
+                file.read(),
+            )
 
     def test_invalid_test_mapping_wrong_test_option(self):
         """Verify that test config using wrong option can be detected."""
-        with open(self.test_mapping_file, 'w', encoding='utf-8') as file:
+        with open(self.test_mapping_file, "w", encoding="utf-8") as file:
             file.write(_BAD_TEST_WRONG_OPTION)
-        with open(self.test_mapping_file, 'r', encoding='utf-8') as file:
+        with open(self.test_mapping_file, "r", encoding="utf-8") as file:
             self.assertRaises(
                 android_test_mapping_format.InvalidTestMappingError,
                 android_test_mapping_format.process_file,
-                file.read())
+                file.read(),
+            )
 
     def test_invalid_test_mapping_wrong_import_key(self):
         """Verify that import setting using wrong key can be detected."""
-        with open(self.test_mapping_file, 'w', encoding='utf-8') as file:
+        with open(self.test_mapping_file, "w", encoding="utf-8") as file:
             file.write(_BAD_IMPORT_WRONG_KEY)
-        with open(self.test_mapping_file, 'r', encoding='utf-8') as file:
+        with open(self.test_mapping_file, "r", encoding="utf-8") as file:
             self.assertRaises(
                 android_test_mapping_format.InvalidTestMappingError,
                 android_test_mapping_format.process_file,
-                file.read())
+                file.read(),
+            )
 
     def test_invalid_test_mapping_wrong_import_value(self):
         """Verify that import setting using wrong value can be detected."""
-        with open(self.test_mapping_file, 'w', encoding='utf-8') as file:
+        with open(self.test_mapping_file, "w", encoding="utf-8") as file:
             file.write(_BAD_IMPORT_WRONG_IMPORT_VALUE)
-        with open(self.test_mapping_file, 'r', encoding='utf-8') as file:
+        with open(self.test_mapping_file, "r", encoding="utf-8") as file:
             self.assertRaises(
                 android_test_mapping_format.InvalidTestMappingError,
                 android_test_mapping_format.process_file,
-                file.read())
+                file.read(),
+            )
 
     def test_invalid_test_mapping_file_patterns_value(self):
         """Verify that file_patterns using wrong value can be detected."""
-        with open(self.test_mapping_file, 'w', encoding='utf-8') as file:
+        with open(self.test_mapping_file, "w", encoding="utf-8") as file:
             file.write(_BAD_FILE_PATTERNS)
-        with open(self.test_mapping_file, 'r', encoding='utf-8') as file:
+        with open(self.test_mapping_file, "r", encoding="utf-8") as file:
             self.assertRaises(
                 android_test_mapping_format.InvalidTestMappingError,
                 android_test_mapping_format.process_file,
-                file.read())
+                file.read(),
+            )
 
     def test_valid_test_mapping_file_with_supported_comments(self):
         """Verify that '//'-format comment can be filtered."""
-        with open(self.test_mapping_file, 'w', encoding='utf-8') as file:
+        with open(self.test_mapping_file, "w", encoding="utf-8") as file:
             file.write(_TEST_MAPPING_WITH_SUPPORTED_COMMENTS)
-        with open(self.test_mapping_file, 'r', encoding='utf-8') as file:
+        with open(self.test_mapping_file, "r", encoding="utf-8") as file:
             android_test_mapping_format.process_file(file.read())
 
     def test_valid_test_mapping_file_with_non_supported_comments(self):
         """Verify that non-supported comment can be detected."""
-        with open(self.test_mapping_file, 'w', encoding='utf-8') as file:
+        with open(self.test_mapping_file, "w", encoding="utf-8") as file:
             file.write(_TEST_MAPPING_WITH_NON_SUPPORTED_COMMENTS)
-        with open(self.test_mapping_file, 'r', encoding='utf-8') as file:
+        with open(self.test_mapping_file, "r", encoding="utf-8") as file:
             self.assertRaises(
-                ValueError, android_test_mapping_format.process_file,
-                file.read())
+                ValueError,
+                android_test_mapping_format.process_file,
+                file.read(),
+            )
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     unittest.main()
diff --git a/tools/check_aosp_license.py b/tools/check_aosp_license.py
index ffeed21..d00bde4 100755
--- a/tools/check_aosp_license.py
+++ b/tools/check_aosp_license.py
@@ -1,5 +1,4 @@
 #!/usr/bin/env python3
-#
 # Copyright (C) 2024 The Android Open Source Project
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,7 +21,7 @@
 import sys
 from typing import List
 
-_path = os.path.realpath(__file__ + '/../..')
+_path = os.path.realpath(__file__ + "/../..")
 if sys.path[0] != _path:
     sys.path.insert(0, _path)
 del _path
@@ -58,7 +57,7 @@
 LICENSE_RE = re.compile(AOSP_LICENSE_HEADER, re.MULTILINE)
 
 
-AOSP_LICENSE_SUBSTR = 'Licensed under the Apache License'
+AOSP_LICENSE_SUBSTR = "Licensed under the Apache License"
 
 
 def check_license(contents: str) -> bool:
@@ -70,17 +69,17 @@
     """Returns a command line parser."""
     parser = argparse.ArgumentParser(description=__doc__)
     parser.add_argument(
-        'files',
-        nargs='+',
-        help='The file paths to check.',
+        "files",
+        nargs="+",
+        help="The file paths to check.",
     )
     parser.add_argument(
-        '--commit-hash',
-        '-c',
-        help='The commit hash to check.',
+        "--commit-hash",
+        "-c",
+        help="The commit hash to check.",
         # TODO(b/370907797): Read the contents on the file system by default
         # instead.
-        default='HEAD',
+        default="HEAD",
     )
     return parser
 
@@ -97,12 +96,12 @@
         contents = rh.git.get_file_content(commit_hash, file_path)
         if not check_license(contents):
             if AOSP_LICENSE_SUBSTR in contents:
-                print(f'{file_path}: Malformed AOSP license', file=sys.stderr)
+                print(f"{file_path}: Malformed AOSP license", file=sys.stderr)
             else:
-                print(f'{file_path}: Missing AOSP license', file=sys.stderr)
+                print(f"{file_path}: Missing AOSP license", file=sys.stderr)
             all_passed = False
     return 0 if all_passed else 1
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     sys.exit(main(sys.argv[1:]))
diff --git a/tools/check_aosp_license_unittest.py b/tools/check_aosp_license_unittest.py
index bcd98f8..fc35dc3 100755
--- a/tools/check_aosp_license_unittest.py
+++ b/tools/check_aosp_license_unittest.py
@@ -1,5 +1,4 @@
 #!/usr/bin/env python3
-#
 # Copyright (C) 2024 The Android Open Source Project
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
@@ -155,5 +154,5 @@
         self.assertFalse(check_aosp_license.check_license(invalid_header))
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     unittest.main()
diff --git a/tools/clang-format.py b/tools/clang-format.py
index 1d5f1ac..7b57d5d 100755
--- a/tools/clang-format.py
+++ b/tools/clang-format.py
@@ -19,7 +19,7 @@
 import os
 import sys
 
-_path = os.path.realpath(__file__ + '/../..')
+_path = os.path.realpath(__file__ + "/../..")
 if sys.path[0] != _path:
     sys.path.insert(0, _path)
 del _path
@@ -33,34 +33,60 @@
 
 # Since we're asking git-clang-format to print a diff, all modified filenames
 # that have formatting errors are printed with this prefix.
-DIFF_MARKER_PREFIX = '+++ b/'
+DIFF_MARKER_PREFIX = "+++ b/"
 
 
 def get_parser():
     """Return a command line parser."""
     parser = argparse.ArgumentParser(description=__doc__)
-    parser.add_argument('--clang-format', default='clang-format',
-                        help='The path of the clang-format executable.')
-    parser.add_argument('--git-clang-format', default='git-clang-format',
-                        help='The path of the git-clang-format executable.')
-    parser.add_argument('--style', metavar='STYLE', type=str,
-                        help='The style that clang-format will use.')
-    parser.add_argument('--extensions', metavar='EXTENSIONS', type=str,
-                        help='Comma-separated list of file extensions to '
-                             'format.')
-    parser.add_argument('--fix', action='store_true',
-                        help='Fix any formatting errors automatically.')
+    parser.add_argument(
+        "--clang-format",
+        default="clang-format",
+        help="The path of the clang-format executable.",
+    )
+    parser.add_argument(
+        "--git-clang-format",
+        default="git-clang-format",
+        help="The path of the git-clang-format executable.",
+    )
+    parser.add_argument(
+        "--style",
+        metavar="STYLE",
+        type=str,
+        help="The style that clang-format will use.",
+    )
+    parser.add_argument(
+        "--extensions",
+        metavar="EXTENSIONS",
+        type=str,
+        help="Comma-separated list of file extensions to " "format.",
+    )
+    parser.add_argument(
+        "--fix",
+        action="store_true",
+        help="Fix any formatting errors automatically.",
+    )
 
     scope = parser.add_mutually_exclusive_group(required=True)
-    scope.add_argument('--commit', type=str, default='HEAD',
-                       help='Specify the commit to validate.')
-    scope.add_argument('--working-tree', action='store_true',
-                       help='Validates the files that have changed from '
-                            'HEAD in the working directory.')
+    scope.add_argument(
+        "--commit",
+        type=str,
+        default="HEAD",
+        help="Specify the commit to validate.",
+    )
+    scope.add_argument(
+        "--working-tree",
+        action="store_true",
+        help="Validates the files that have changed from "
+        "HEAD in the working directory.",
+    )
 
-    parser.add_argument('files', type=str, nargs='*',
-                        help='If specified, only consider differences in '
-                             'these files.')
+    parser.add_argument(
+        "files",
+        type=str,
+        nargs="*",
+        help="If specified, only consider differences in " "these files.",
+    )
     return parser
 
 
@@ -69,14 +95,14 @@
     parser = get_parser()
     opts = parser.parse_args(argv)
 
-    cmd = [opts.git_clang_format, '--binary', opts.clang_format, '--diff']
+    cmd = [opts.git_clang_format, "--binary", opts.clang_format, "--diff"]
     if opts.style:
-        cmd.extend(['--style', opts.style])
+        cmd.extend(["--style", opts.style])
     if opts.extensions:
-        cmd.extend(['--extensions', opts.extensions])
+        cmd.extend(["--extensions", opts.extensions])
     if not opts.working_tree:
-        cmd.extend([f'{opts.commit}^', opts.commit])
-    cmd.extend(['--'] + opts.files)
+        cmd.extend([f"{opts.commit}^", opts.commit])
+    cmd.extend(["--"] + opts.files)
 
     # Fail gracefully if clang-format itself aborts/fails.
     result = rh.utils.run(cmd, capture_output=True, check=False)
@@ -85,32 +111,44 @@
     # it exited 1 and produce useful format diffs to stdout.  If it exited 0,
     # then assume all is well and we'll attempt to parse its output below.
     ret_code = None
-    if (result.returncode > 1 or result.stderr or
-        (result.stdout and result.returncode)):
+    if (
+        result.returncode > 1
+        or result.stderr
+        or (result.stdout and result.returncode)
+    ):
         # Apply fix if the flag is set and clang-format shows it is fixible.
         if opts.fix and result.stdout and result.returncode:
-            result = rh.utils.run(['git', 'apply'], input=result.stdout,
-                                   check=False)
+            result = rh.utils.run(
+                ["git", "apply"], input=result.stdout, check=False
+            )
             ret_code = result.returncode
             if ret_code:
-                print('Error: Unable to automatically fix things.\n'
-                      '  Make sure your checkout is clean first.\n'
-                      '  If you have multiple commits, you might have to '
-                      'manually rebase your tree first.',
-                      file=sys.stderr)
+                print(
+                    "Error: Unable to automatically fix things.\n"
+                    "  Make sure your checkout is clean first.\n"
+                    "  If you have multiple commits, you might have to "
+                    "manually rebase your tree first.",
+                    file=sys.stderr,
+                )
 
         else:  # Regular clang-format aborts/fails.
-            print(f'clang-format failed:\ncmd: {result.cmdstr}\n'
-                  f'stdout:\n{result.stdout}\n', file=sys.stderr)
+            print(
+                f"clang-format failed:\ncmd: {result.cmdstr}\n"
+                f"stdout:\n{result.stdout}\n",
+                file=sys.stderr,
+            )
             if result.returncode > 1 or result.stderr:
-                print('\nPlease report this to the clang team.\n',
-                      f'stderr:\n{result.stderr}', file=sys.stderr)
+                print(
+                    "\nPlease report this to the clang team.\n",
+                    f"stderr:\n{result.stderr}",
+                    file=sys.stderr,
+                )
             ret_code = 1
 
         return ret_code
 
     stdout = result.stdout
-    if stdout.rstrip('\n') == 'no modified files to format':
+    if stdout.rstrip("\n") == "no modified files to format":
         # This is always printed when only files that clang-format does not
         # understand were modified.
         return 0
@@ -118,28 +156,32 @@
     diff_filenames = []
     for line in stdout.splitlines():
         if line.startswith(DIFF_MARKER_PREFIX):
-            diff_filenames.append(line[len(DIFF_MARKER_PREFIX):].rstrip())
+            diff_filenames.append(line[len(DIFF_MARKER_PREFIX) :].rstrip())
 
     if diff_filenames:
         if opts.fix:
-            result = rh.utils.run(['git', 'apply'], input=stdout, check=False)
+            result = rh.utils.run(["git", "apply"], input=stdout, check=False)
             if result.returncode:
-                print('Error: Unable to automatically fix things.\n'
-                      '  Make sure your checkout is clean first.\n'
-                      '  If you have multiple commits, you might have to '
-                      'manually rebase your tree first.',
-                      file=sys.stderr)
+                print(
+                    "Error: Unable to automatically fix things.\n"
+                    "  Make sure your checkout is clean first.\n"
+                    "  If you have multiple commits, you might have to "
+                    "manually rebase your tree first.",
+                    file=sys.stderr,
+                )
                 return result.returncode
         else:
-            print('The following files have formatting errors:')
+            print("The following files have formatting errors:")
             for filename in diff_filenames:
-                print(f'\t{filename}')
-            print('You can try to fix this by running:\n'
-                  f'{sys.argv[0]} --fix {rh.shell.cmd_to_str(argv)}')
+                print(f"\t{filename}")
+            print(
+                "You can try to fix this by running:\n"
+                f"{sys.argv[0]} --fix {rh.shell.cmd_to_str(argv)}"
+            )
             return 1
 
     return 0
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     sys.exit(main(sys.argv[1:]))
diff --git a/tools/clang-format_unittest.py b/tools/clang-format_unittest.py
index 8dfb5cf..eac0e34 100755
--- a/tools/clang-format_unittest.py
+++ b/tools/clang-format_unittest.py
@@ -31,25 +31,26 @@
 import rh.utils
 
 
-CLANG_FORMAT = DIR / 'clang-format.py'
+CLANG_FORMAT = DIR / "clang-format.py"
 
 
 @contextlib.contextmanager
 def git_clang_format(data: str):
     """Create a fake git-clang-format script."""
-    with tempfile.TemporaryDirectory(prefix='repohooks-tests') as tempdir:
+    with tempfile.TemporaryDirectory(prefix="repohooks-tests") as tempdir:
         tempdir = Path(tempdir)
-        script = tempdir / 'git-clang-format-fake.sh'
-        script.write_text(f'#!/bin/sh\n{data}', encoding='utf-8')
+        script = tempdir / "git-clang-format-fake.sh"
+        script.write_text(f"#!/bin/sh\n{data}", encoding="utf-8")
         script.chmod(0o755)
         yield script
 
 
 def run_clang_format(script, args, **kwargs):
     """Helper to run clang-format.py with fake git-clang-format script."""
-    kwargs.setdefault('capture_output', True)
+    kwargs.setdefault("capture_output", True)
     return rh.utils.run(
-        [CLANG_FORMAT, '--git-clang-format', script] + args, **kwargs)
+        [CLANG_FORMAT, "--git-clang-format", script] + args, **kwargs
+    )
 
 
 class GitClangFormatExit(unittest.TestCase):
@@ -57,52 +58,53 @@
 
     def test_diff_exit_0_no_output(self):
         """Test exit 0 w/no output."""
-        with git_clang_format('exit 0') as script:
-            result = run_clang_format(script, ['--working-tree'])
-            self.assertEqual(result.stdout, '')
+        with git_clang_format("exit 0") as script:
+            result = run_clang_format(script, ["--working-tree"])
+            self.assertEqual(result.stdout, "")
 
     def test_diff_exit_0_stderr(self):
         """Test exit 0 w/stderr output."""
-        with git_clang_format('echo bad >&2; exit 0') as script:
+        with git_clang_format("echo bad >&2; exit 0") as script:
             with self.assertRaises(rh.utils.CalledProcessError) as e:
-                run_clang_format(script, ['--working-tree'])
-            self.assertIn('clang-format failed', e.exception.stderr)
+                run_clang_format(script, ["--working-tree"])
+            self.assertIn("clang-format failed", e.exception.stderr)
 
     def test_diff_exit_1_no_output(self):
         """Test exit 1 w/no output."""
-        with git_clang_format('exit 1') as script:
-            result = run_clang_format(script, ['--working-tree'])
-            self.assertEqual(result.stdout, '')
+        with git_clang_format("exit 1") as script:
+            result = run_clang_format(script, ["--working-tree"])
+            self.assertEqual(result.stdout, "")
 
     def test_diff_exit_1_output(self):
         """Test exit 1 with output."""
-        with git_clang_format('echo bad; exit 1') as script:
+        with git_clang_format("echo bad; exit 1") as script:
             with self.assertRaises(rh.utils.CalledProcessError) as e:
-                run_clang_format(script, ['--working-tree'])
-            self.assertIn('clang-format failed', e.exception.stderr)
+                run_clang_format(script, ["--working-tree"])
+            self.assertIn("clang-format failed", e.exception.stderr)
 
     def test_diff_exit_1_stderr(self):
         """Test exit 1 w/stderr."""
-        with git_clang_format('echo bad >&2; exit 1') as script:
+        with git_clang_format("echo bad >&2; exit 1") as script:
             with self.assertRaises(rh.utils.CalledProcessError) as e:
-                run_clang_format(script, ['--working-tree'])
-            self.assertIn('clang-format failed', e.exception.stderr)
+                run_clang_format(script, ["--working-tree"])
+            self.assertIn("clang-format failed", e.exception.stderr)
 
     def test_diff_exit_2(self):
         """Test exit 2."""
-        with git_clang_format('exit 2') as script:
+        with git_clang_format("exit 2") as script:
             with self.assertRaises(rh.utils.CalledProcessError) as e:
-                run_clang_format(script, ['--working-tree'])
-            self.assertIn('clang-format failed', e.exception.stderr)
+                run_clang_format(script, ["--working-tree"])
+            self.assertIn("clang-format failed", e.exception.stderr)
 
     def test_fix_exit_1_output(self):
         """Test fix with incorrect patch syntax."""
-        with git_clang_format('echo bad patch; exit 1') as script:
+        with git_clang_format("echo bad patch; exit 1") as script:
             with self.assertRaises(rh.utils.CalledProcessError) as e:
-                run_clang_format(script, ['--working-tree', '--fix'])
-            self.assertIn('Error: Unable to automatically fix things',
-                          e.exception.stderr)
+                run_clang_format(script, ["--working-tree", "--fix"])
+            self.assertIn(
+                "Error: Unable to automatically fix things", e.exception.stderr
+            )
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     unittest.main()
diff --git a/tools/google-java-format.py b/tools/google-java-format.py
index ebb9475..cf5df66 100755
--- a/tools/google-java-format.py
+++ b/tools/google-java-format.py
@@ -20,7 +20,7 @@
 import shutil
 import sys
 
-_path = os.path.realpath(__file__ + '/../..')
+_path = os.path.realpath(__file__ + "/../..")
 if sys.path[0] != _path:
     sys.path.insert(0, _path)
 del _path
@@ -35,20 +35,37 @@
 def get_parser():
     """Return a command line parser."""
     parser = argparse.ArgumentParser(description=__doc__)
-    parser.add_argument('--google-java-format', default='google-java-format',
-                        help='The path of the google-java-format executable.')
-    parser.add_argument('--google-java-format-diff',
-                        default='google-java-format-diff.py',
-                        help='The path of the google-java-format-diff script.')
-    parser.add_argument('--fix', action='store_true',
-                        help='Fix any formatting errors automatically.')
-    parser.add_argument('--commit', type=str, default='HEAD',
-                        help='Specify the commit to validate.')
-    parser.add_argument('--skip-sorting-imports', action='store_true',
-                        help='If true, imports will not be sorted.')
-    parser.add_argument('files', nargs='*',
-                        help='If specified, only consider differences in '
-                             'these files.')
+    parser.add_argument(
+        "--google-java-format",
+        default="google-java-format",
+        help="The path of the google-java-format executable.",
+    )
+    parser.add_argument(
+        "--google-java-format-diff",
+        default="google-java-format-diff.py",
+        help="The path of the google-java-format-diff script.",
+    )
+    parser.add_argument(
+        "--fix",
+        action="store_true",
+        help="Fix any formatting errors automatically.",
+    )
+    parser.add_argument(
+        "--commit",
+        type=str,
+        default="HEAD",
+        help="Specify the commit to validate.",
+    )
+    parser.add_argument(
+        "--skip-sorting-imports",
+        action="store_true",
+        help="If true, imports will not be sorted.",
+    )
+    parser.add_argument(
+        "files",
+        nargs="*",
+        help="If specified, only consider differences in " "these files.",
+    )
     return parser
 
 
@@ -60,31 +77,31 @@
     format_path = shutil.which(opts.google_java_format)
     if not format_path:
         print(
-            f'Unable to find google-java-format at: {opts.google_java_format}',
-            file=sys.stderr
+            f"Unable to find google-java-format at: {opts.google_java_format}",
+            file=sys.stderr,
         )
         return 1
 
     # TODO: Delegate to the tool once this issue is resolved:
     # https://github.com/google/google-java-format/issues/107
-    diff_cmd = ['git', 'diff', '--no-ext-diff', '-U0', f'{opts.commit}^!']
-    diff_cmd.extend(['--'] + opts.files)
+    diff_cmd = ["git", "diff", "--no-ext-diff", "-U0", f"{opts.commit}^!"]
+    diff_cmd.extend(["--"] + opts.files)
     diff = rh.utils.run(diff_cmd, capture_output=True).stdout
 
-    cmd = [opts.google_java_format_diff, '-p1', '--aosp', '-b', format_path]
+    cmd = [opts.google_java_format_diff, "-p1", "--aosp", "-b", format_path]
     if opts.fix:
-        cmd.extend(['-i'])
+        cmd.extend(["-i"])
     if opts.skip_sorting_imports:
-        cmd.extend(['--skip-sorting-imports'])
+        cmd.extend(["--skip-sorting-imports"])
 
     stdout = rh.utils.run(cmd, input=diff, capture_output=True).stdout
     if stdout:
-        print('One or more files in your commit have Java formatting errors.')
-        print(f'You can run: {sys.argv[0]} --fix {rh.shell.cmd_to_str(argv)}')
+        print("One or more files in your commit have Java formatting errors.")
+        print(f"You can run: {sys.argv[0]} --fix {rh.shell.cmd_to_str(argv)}")
         return 1
 
     return 0
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     sys.exit(main(sys.argv[1:]))
diff --git a/tools/pylint.py b/tools/pylint.py
index 8b27da8..556f2ff 100755
--- a/tools/pylint.py
+++ b/tools/pylint.py
@@ -25,17 +25,24 @@
 
 # This script is run by repohooks users.
 # See README.md for what version we may require.
-assert (sys.version_info.major, sys.version_info.minor) >= (3, 6), (
-    f'Python 3.6 or newer is required; found {sys.version}')
+assert (sys.version_info.major, sys.version_info.minor) >= (
+    3,
+    6,
+), f"Python 3.6 or newer is required; found {sys.version}"
 
 
 DEFAULT_PYLINTRC_PATH = os.path.join(
-    os.path.dirname(os.path.realpath(__file__)), 'pylintrc')
+    os.path.dirname(os.path.realpath(__file__)), "pylintrc"
+)
 
 
-def run_lint(pylint: str, unknown: Optional[List[str]],
-             files: Optional[List[str]], init_hook: str,
-             pylintrc: Optional[str] = None) -> bool:
+def run_lint(
+    pylint: str,
+    unknown: Optional[List[str]],
+    files: Optional[List[str]],
+    init_hook: str,
+    pylintrc: Optional[str] = None,
+) -> bool:
     """Run lint command.
 
     Upon error the stdout from pylint will be dumped to stdout and
@@ -48,37 +55,41 @@
         return True
 
     if pylintrc:
-        cmd += ['--rcfile', pylintrc]
+        cmd += ["--rcfile", pylintrc]
 
     files.sort()
     cmd += unknown + files
 
     if init_hook:
-        cmd += ['--init-hook', init_hook]
+        cmd += ["--init-hook", init_hook]
 
     try:
-        result = subprocess.run(cmd, stdout=subprocess.PIPE, text=True,
-                                check=False)
+        result = subprocess.run(
+            cmd, stdout=subprocess.PIPE, text=True, check=False
+        )
     except OSError as e:
         if e.errno == errno.ENOENT:
-            print(f'{__file__}: unable to run `{cmd[0]}`: {e}',
-                  file=sys.stderr)
-            print(f'{__file__}: Try installing pylint: sudo apt-get install '
-                  f'{os.path.basename(cmd[0])}', file=sys.stderr)
+            print(f"{__file__}: unable to run `{cmd[0]}`: {e}", file=sys.stderr)
+            print(
+                f"{__file__}: Try installing pylint: sudo apt-get install "
+                f"{os.path.basename(cmd[0])}",
+                file=sys.stderr,
+            )
             return False
 
         raise
 
     if result.returncode:
-        print(f'{__file__}: Using pylintrc: {pylintrc}')
+        print(f"{__file__}: Using pylintrc: {pylintrc}")
         print(result.stdout)
         return False
 
     return True
 
 
-def find_parent_dirs_with_pylintrc(leafdir: str,
-                                   pylintrc_map: Dict[str, Set[str]]) -> None:
+def find_parent_dirs_with_pylintrc(
+    leafdir: str, pylintrc_map: Dict[str, Set[str]]
+) -> None:
     """Find all dirs containing a pylintrc between root dir and leafdir."""
 
     # Find all pylintrc files, store the path. The path must end with '/'
@@ -89,15 +100,17 @@
     key = os.path.abspath(leafdir) + os.sep
 
     if not key.startswith(rootdir):
-        sys.exit(f'{__file__}: The search directory {key} is outside the '
-                 f'repo dir {rootdir}')
+        sys.exit(
+            f"{__file__}: The search directory {key} is outside the "
+            f"repo dir {rootdir}"
+        )
 
     while rootdir != key:
         # This subdirectory has already been handled, skip it.
         if key in pylintrc_map:
             break
 
-        if os.path.exists(os.path.join(key, 'pylintrc')):
+        if os.path.exists(os.path.join(key, "pylintrc")):
             pylintrc_map.setdefault(key, set())
             break
 
@@ -106,7 +119,7 @@
 
 
 def map_pyfiles_to_pylintrc(files: List[str]) -> Dict[str, Set[str]]:
-    """ Map all python files to a pylintrc file.
+    """Map all python files to a pylintrc file.
 
     Generate dictionary with pylintrc-file dirnames (including trailing /)
     as key containing sets with corresponding python files.
@@ -115,15 +128,15 @@
     pylintrc_map = {}
     # We assume pylint is running in the top directory of the project,
     # so load the pylintrc file from there if it is available.
-    pylintrc = os.path.abspath('pylintrc')
+    pylintrc = os.path.abspath("pylintrc")
     if not os.path.exists(pylintrc):
         pylintrc = DEFAULT_PYLINTRC_PATH
         # If we pass a non-existent rcfile to pylint, it'll happily ignore
         # it.
-        assert os.path.exists(pylintrc), f'Could not find {pylintrc}'
+        assert os.path.exists(pylintrc), f"Could not find {pylintrc}"
     # Always add top directory, either there is a pylintrc or fallback to
     # default.
-    key = os.path.abspath('.') + os.sep
+    key = os.path.abspath(".") + os.sep
     pylintrc_map[key] = set()
 
     search_dirs = {os.path.dirname(x) for x in files}
@@ -142,7 +155,7 @@
                 pylintrc_map[rc_dir].add(f)
                 break
         else:
-            sys.exit(f'{__file__}: Failed to map file {f} to a pylintrc file.')
+            sys.exit(f"{__file__}: Failed to map file {f} to a pylintrc file.")
 
     return pylintrc_map
 
@@ -150,14 +163,19 @@
 def get_parser():
     """Return a command line parser."""
     parser = argparse.ArgumentParser(description=__doc__)
-    parser.add_argument('--init-hook', help='Init hook commands to run.')
-    parser.add_argument('--executable-path', default='pylint',
-                        help='The path of the pylint executable.')
-    parser.add_argument('--no-rcfile', dest='use_default_conf',
-                        help='Specify to use the executable\'s default '
-                        'configuration.',
-                        action='store_true')
-    parser.add_argument('files', nargs='+')
+    parser.add_argument("--init-hook", help="Init hook commands to run.")
+    parser.add_argument(
+        "--executable-path",
+        default="pylint",
+        help="The path of the pylint executable.",
+    )
+    parser.add_argument(
+        "--no-rcfile",
+        dest="use_default_conf",
+        help="Specify to use the executable's default " "configuration.",
+        action="store_true",
+    )
+    parser.add_argument("files", nargs="+")
     return parser
 
 
@@ -172,15 +190,17 @@
         pylintrc_map = map_pyfiles_to_pylintrc(opts.files)
         first = True
         for rc_dir, files in sorted(pylintrc_map.items()):
-            pylintrc = os.path.join(rc_dir, 'pylintrc')
+            pylintrc = os.path.join(rc_dir, "pylintrc")
             if first:
                 first = False
-                assert os.path.abspath(rc_dir) == os.path.abspath('.'), (
-                    f'{__file__}: pylintrc in top dir not first in list')
+                assert os.path.abspath(rc_dir) == os.path.abspath(
+                    "."
+                ), f"{__file__}: pylintrc in top dir not first in list"
                 if not os.path.exists(pylintrc):
                     pylintrc = DEFAULT_PYLINTRC_PATH
-            if not run_lint(pylint, unknown, sorted(files),
-                            opts.init_hook, pylintrc):
+            if not run_lint(
+                pylint, unknown, sorted(files), opts.init_hook, pylintrc
+            ):
                 ret = 1
     # Not using rc files, pylint default behaviour.
     elif not run_lint(pylint, unknown, sorted(opts.files), opts.init_hook):
@@ -189,5 +209,5 @@
     return ret
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     sys.exit(main(sys.argv[1:]))