From de5723e931e217f677af9c88afa8872871ca83c5 Mon Sep 17 00:00:00 2001 From: Giacomo Tagliabue Date: Tue, 3 Mar 2020 10:38:29 -0500 Subject: [PATCH 01/11] add --force-exclude option --- black.py | 53 +++++++++++++++++++------- docs/reference/reference_functions.rst | 2 +- tests/test_black.py | 24 ++++++------ 3 files changed, 53 insertions(+), 26 deletions(-) diff --git a/black.py b/black.py index b7cacf77678..1e55e7d2ca2 100644 --- a/black.py +++ b/black.py @@ -360,6 +360,14 @@ def target_version_option_callback( ), show_default=True, ) +@click.option( + "--force-exclude", + type=str, + help=( + "Like --exclude, but in these case files and directories will be excluded " + "even when they are passed explicitly as arguments" + ), +) @click.option( "-q", "--quiet", @@ -412,6 +420,7 @@ def main( verbose: bool, include: str, exclude: str, + force_exclude: Optional[str], src: Tuple[str, ...], config: Optional[str], ) -> None: @@ -453,6 +462,13 @@ def main( except re.error: err(f"Invalid regular expression for exclude given: {exclude!r}") ctx.exit(2) + try: + force_exclude_regex = ( + re_compile_maybe_verbose(force_exclude) if force_exclude else None + ) + except re.error: + err(f"Invalid regular expression for exclude given: {exclude!r}") + ctx.exit(2) report = Report(check=check, diff=diff, quiet=quiet, verbose=verbose) root = find_project_root(src) sources: Set[Path] = set() @@ -461,15 +477,26 @@ def main( p = Path(s) if p.is_dir(): sources.update( - gen_python_files_in_dir( - p, root, include_regex, exclude_regex, report, get_gitignore(root) + gen_python_files( + p.iterdir(), + root, + include_regex, + exclude_regex, + report, + get_gitignore(root), ) ) - elif p.is_file() or s == "-": - # if a file was explicitly given, we don't care about its extension + elif s == "-": sources.add(p) + elif p.is_file(): + sources.update( + gen_python_files( + [p], root, None, force_exclude_regex, report, get_gitignore(root) + ) + ) else: err(f"invalid path: {s}") + if len(sources) == 0: if verbose or not quiet: out("No Python files are present to be formatted. Nothing to do 😴") @@ -3527,11 +3554,11 @@ def get_gitignore(root: Path) -> PathSpec: return PathSpec.from_lines("gitwildmatch", lines) -def gen_python_files_in_dir( - path: Path, +def gen_python_files( + paths: Iterable[Path], root: Path, - include: Pattern[str], - exclude: Pattern[str], + include: Optional[Pattern[str]], + exclude: Optional[Pattern[str]], report: "Report", gitignore: PathSpec, ) -> Iterator[Path]: @@ -3543,7 +3570,7 @@ def gen_python_files_in_dir( `report` is where output about exclusions goes. """ assert root.is_absolute(), f"INTERNAL ERROR: `root` must be absolute but is {root}" - for child in path.iterdir(): + for child in paths: # First ignore files matching .gitignore if gitignore.match_file(child.as_posix()): report.path_ignored(child, f"matches the .gitignore file content") @@ -3568,18 +3595,18 @@ def gen_python_files_in_dir( if child.is_dir(): normalized_path += "/" - exclude_match = exclude.search(normalized_path) + exclude_match = exclude.search(normalized_path) if exclude else None if exclude_match and exclude_match.group(0): report.path_ignored(child, f"matches the --exclude regular expression") continue if child.is_dir(): - yield from gen_python_files_in_dir( - child, root, include, exclude, report, gitignore + yield from gen_python_files( + child.iterdir(), root, include, exclude, report, gitignore, ) elif child.is_file(): - include_match = include.search(normalized_path) + include_match = include.search(normalized_path) if include else True if include_match: yield child diff --git a/docs/reference/reference_functions.rst b/docs/reference/reference_functions.rst index 459e4980ae7..d9607d0ad56 100644 --- a/docs/reference/reference_functions.rst +++ b/docs/reference/reference_functions.rst @@ -61,7 +61,7 @@ File operations .. autofunction:: black.find_project_root -.. autofunction:: black.gen_python_files_in_dir +.. autofunction:: black.gen_python_files .. autofunction:: black.read_pyproject_toml diff --git a/tests/test_black.py b/tests/test_black.py index acbaade0a50..52164809c35 100644 --- a/tests/test_black.py +++ b/tests/test_black.py @@ -1442,8 +1442,8 @@ def test_include_exclude(self) -> None: ] this_abs = THIS_DIR.resolve() sources.extend( - black.gen_python_files_in_dir( - path, this_abs, include, exclude, report, gitignore + black.gen_python_files( + path.iterdir(), this_abs, include, exclude, report, gitignore ) ) self.assertEqual(sorted(expected), sorted(sources)) @@ -1463,8 +1463,8 @@ def test_gitignore_exclude(self) -> None: ] this_abs = THIS_DIR.resolve() sources.extend( - black.gen_python_files_in_dir( - path, this_abs, include, exclude, report, gitignore + black.gen_python_files( + path.iterdir(), this_abs, include, exclude, report, gitignore ) ) self.assertEqual(sorted(expected), sorted(sources)) @@ -1488,8 +1488,8 @@ def test_empty_include(self) -> None: ] this_abs = THIS_DIR.resolve() sources.extend( - black.gen_python_files_in_dir( - path, + black.gen_python_files( + path.iterdir(), this_abs, empty, re.compile(black.DEFAULT_EXCLUDES), @@ -1515,8 +1515,8 @@ def test_empty_exclude(self) -> None: ] this_abs = THIS_DIR.resolve() sources.extend( - black.gen_python_files_in_dir( - path, + black.gen_python_files( + path.iterdir(), this_abs, re.compile(black.DEFAULT_INCLUDES), empty, @@ -1575,8 +1575,8 @@ def test_symlink_out_of_root_directory(self) -> None: child.is_symlink.return_value = True try: list( - black.gen_python_files_in_dir( - path, root, include, exclude, report, gitignore + black.gen_python_files( + path.iterdir(), root, include, exclude, report, gitignore ) ) except ValueError as ve: @@ -1589,8 +1589,8 @@ def test_symlink_out_of_root_directory(self) -> None: child.is_symlink.return_value = False with self.assertRaises(ValueError): list( - black.gen_python_files_in_dir( - path, root, include, exclude, report, gitignore + black.gen_python_files( + path.iterdir(), root, include, exclude, report, gitignore ) ) path.iterdir.assert_called() From eba70462e948b36ed33ab4fb68262d1a086565a4 Mon Sep 17 00:00:00 2001 From: Peter Yu <2057325+yukw777@users.noreply.github.com> Date: Tue, 28 Apr 2020 09:14:48 -0400 Subject: [PATCH 02/11] use path_empty() to reduce the complexity of main() --- black.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/black.py b/black.py index 1e55e7d2ca2..6242c55936e 100644 --- a/black.py +++ b/black.py @@ -32,6 +32,7 @@ Pattern, Sequence, Set, + Sized, Tuple, TypeVar, Union, @@ -472,7 +473,7 @@ def main( report = Report(check=check, diff=diff, quiet=quiet, verbose=verbose) root = find_project_root(src) sources: Set[Path] = set() - path_empty(src, quiet, verbose, ctx) + path_empty(src, "No Path provided. Nothing to do 😴", quiet, verbose, ctx) for s in src: p = Path(s) if p.is_dir(): @@ -497,10 +498,13 @@ def main( else: err(f"invalid path: {s}") - if len(sources) == 0: - if verbose or not quiet: - out("No Python files are present to be formatted. Nothing to do 😴") - ctx.exit(0) + path_empty( + sources, + "No Python files are present to be formatted. Nothing to do 😴", + quiet, + verbose, + ctx, + ) if len(sources) == 1: reformat_one( @@ -522,14 +526,14 @@ def main( def path_empty( - src: Tuple[str, ...], quiet: bool, verbose: bool, ctx: click.Context + src: Sized, msg: str, quiet: bool, verbose: bool, ctx: click.Context ) -> None: """ Exit if there is no `src` provided for formatting """ - if not src: + if len(src) == 0: if verbose or not quiet: - out("No Path provided. Nothing to do 😴") + out(msg) ctx.exit(0) From e35a10f1b4f0947719504b7f50f96bed28eed3d8 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Fri, 8 May 2020 05:35:43 -0700 Subject: [PATCH 03/11] make force-exclude also exclude --- black.py | 26 +++++++++++++++++--------- tests/test_black.py | 8 ++++---- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/black.py b/black.py index 374f087b9dd..b04c5aa764f 100644 --- a/black.py +++ b/black.py @@ -369,7 +369,7 @@ def target_version_option_callback( "--force-exclude", type=str, help=( - "Like --exclude, but in these case files and directories will be excluded " + "Like --exclude, but files and directories matching this regex will be excluded " "even when they are passed explicitly as arguments" ), ) @@ -472,12 +472,15 @@ def main( re_compile_maybe_verbose(force_exclude) if force_exclude else None ) except re.error: - err(f"Invalid regular expression for exclude given: {exclude!r}") + err(f"Invalid regular expression for force_exclude given: {force_exclude!r}") ctx.exit(2) report = Report(check=check, diff=diff, quiet=quiet, verbose=verbose) root = find_project_root(src) sources: Set[Path] = set() path_empty(src, "No Path provided. Nothing to do 😴", quiet, verbose, ctx) + exclude_regexes = [exclude_regex] + if force_exclude_regex is not None: + exclude_regexes.append(force_exclude_regex) for s in src: p = Path(s) if p.is_dir(): @@ -486,7 +489,7 @@ def main( p.iterdir(), root, include_regex, - exclude_regex, + exclude_regexes, report, get_gitignore(root), ) @@ -496,7 +499,7 @@ def main( elif p.is_file(): sources.update( gen_python_files( - [p], root, None, force_exclude_regex, report, get_gitignore(root) + [p], root, None, exclude_regexes, report, get_gitignore(root) ) ) else: @@ -3583,7 +3586,7 @@ def gen_python_files( paths: Iterable[Path], root: Path, include: Optional[Pattern[str]], - exclude: Optional[Pattern[str]], + exclude_regexes: Iterable[Pattern[str]], report: "Report", gitignore: PathSpec, ) -> Iterator[Path]: @@ -3620,14 +3623,19 @@ def gen_python_files( if child.is_dir(): normalized_path += "/" - exclude_match = exclude.search(normalized_path) if exclude else None - if exclude_match and exclude_match.group(0): - report.path_ignored(child, "matches the --exclude regular expression") + is_excluded = False + for exclude in exclude_regexes: + exclude_match = exclude.search(normalized_path) if exclude else None + if exclude_match and exclude_match.group(0): + report.path_ignored(child, "matches the --exclude regular expression") + is_excluded = True + break + if is_excluded: continue if child.is_dir(): yield from gen_python_files( - child.iterdir(), root, include, exclude, report, gitignore, + child.iterdir(), root, include, exclude_regexes, report, gitignore ) elif child.is_file(): diff --git a/tests/test_black.py b/tests/test_black.py index d63cb65adfc..fe72c63a459 100644 --- a/tests/test_black.py +++ b/tests/test_black.py @@ -1443,7 +1443,7 @@ def test_include_exclude(self) -> None: this_abs = THIS_DIR.resolve() sources.extend( black.gen_python_files( - path.iterdir(), this_abs, include, exclude, report, gitignore + path.iterdir(), this_abs, include, [exclude], report, gitignore ) ) self.assertEqual(sorted(expected), sorted(sources)) @@ -1464,7 +1464,7 @@ def test_gitignore_exclude(self) -> None: this_abs = THIS_DIR.resolve() sources.extend( black.gen_python_files( - path.iterdir(), this_abs, include, exclude, report, gitignore + path.iterdir(), this_abs, include, [exclude], report, gitignore ) ) self.assertEqual(sorted(expected), sorted(sources)) @@ -1492,7 +1492,7 @@ def test_empty_include(self) -> None: path.iterdir(), this_abs, empty, - re.compile(black.DEFAULT_EXCLUDES), + [re.compile(black.DEFAULT_EXCLUDES)], report, gitignore, ) @@ -1519,7 +1519,7 @@ def test_empty_exclude(self) -> None: path.iterdir(), this_abs, re.compile(black.DEFAULT_INCLUDES), - empty, + [empty], report, gitignore, ) From dea24fb8cb8c1132d1afcd859a0296b94404fd93 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Fri, 8 May 2020 05:48:11 -0700 Subject: [PATCH 04/11] fix lint errors --- black.py | 85 +++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 56 insertions(+), 29 deletions(-) diff --git a/black.py b/black.py index b04c5aa764f..efde044a06f 100644 --- a/black.py +++ b/black.py @@ -369,8 +369,8 @@ def target_version_option_callback( "--force-exclude", type=str, help=( - "Like --exclude, but files and directories matching this regex will be excluded " - "even when they are passed explicitly as arguments" + "Like --exclude, but files and directories matching this regex will be " + "excluded even when they are passed explicitly as arguments" ), ) @click.option( @@ -457,6 +457,57 @@ def main( if code is not None: print(format_str(code, mode=mode)) ctx.exit(0) + report = Report(check=check, diff=diff, quiet=quiet, verbose=verbose) + sources = get_sources( + ctx=ctx, + src=src, + quiet=quiet, + verbose=verbose, + include=include, + exclude=exclude, + force_exclude=force_exclude, + report=report, + ) + + path_empty( + sources, + "No Python files are present to be formatted. Nothing to do 😴", + quiet, + verbose, + ctx, + ) + + if len(sources) == 1: + reformat_one( + src=sources.pop(), + fast=fast, + write_back=write_back, + mode=mode, + report=report, + ) + else: + reformat_many( + sources=sources, fast=fast, write_back=write_back, mode=mode, report=report + ) + + if verbose or not quiet: + out("Oh no! 💥 💔 💥" if report.return_code else "All done! ✨ 🍰 ✨") + click.secho(str(report), err=True) + ctx.exit(report.return_code) + + +def get_sources( + *, + ctx: click.Context, + src: Tuple[str, ...], + quiet: bool, + verbose: bool, + include: str, + exclude: str, + force_exclude: Optional[str], + report: "Report", +) -> Set[Path]: + """Compute the set of files to be formatted.""" try: include_regex = re_compile_maybe_verbose(include) except re.error: @@ -474,13 +525,14 @@ def main( except re.error: err(f"Invalid regular expression for force_exclude given: {force_exclude!r}") ctx.exit(2) - report = Report(check=check, diff=diff, quiet=quiet, verbose=verbose) + root = find_project_root(src) sources: Set[Path] = set() path_empty(src, "No Path provided. Nothing to do 😴", quiet, verbose, ctx) exclude_regexes = [exclude_regex] if force_exclude_regex is not None: exclude_regexes.append(force_exclude_regex) + for s in src: p = Path(s) if p.is_dir(): @@ -504,32 +556,7 @@ def main( ) else: err(f"invalid path: {s}") - - path_empty( - sources, - "No Python files are present to be formatted. Nothing to do 😴", - quiet, - verbose, - ctx, - ) - - if len(sources) == 1: - reformat_one( - src=sources.pop(), - fast=fast, - write_back=write_back, - mode=mode, - report=report, - ) - else: - reformat_many( - sources=sources, fast=fast, write_back=write_back, mode=mode, report=report - ) - - if verbose or not quiet: - out("Oh no! 💥 💔 💥" if report.return_code else "All done! ✨ 🍰 ✨") - click.secho(str(report), err=True) - ctx.exit(report.return_code) + return sources def path_empty( From e41a2db1c8a735f6f21ebf570dda7652251cdab1 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Fri, 8 May 2020 06:31:47 -0700 Subject: [PATCH 05/11] debug travis failure --- tests/test_black.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/test_black.py b/tests/test_black.py index e53938b64e4..f830cd4ee72 100644 --- a/tests/test_black.py +++ b/tests/test_black.py @@ -159,7 +159,11 @@ def invokeBlack( if ignore_config: args = ["--config", str(THIS_DIR / "empty.toml"), *args] result = runner.invoke(black.main, args) - self.assertEqual(result.exit_code, exit_code, msg=runner.stderr_bytes.decode()) + self.assertEqual( + result.exit_code, + exit_code, + msg=f"Failed with args: {args}. Stderr: {runner.stderr_bytes.decode()!r}", + ) @patch("black.dump_to_file", dump_to_stderr) def checkSourceFile(self, name: str) -> None: From 3f2acae5279fa2f6babdae571714d9c525195d63 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Fri, 8 May 2020 06:45:37 -0700 Subject: [PATCH 06/11] more travis debugging can't reproduce these failures locally --- tests/test_black.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/test_black.py b/tests/test_black.py index f830cd4ee72..100a782b5d6 100644 --- a/tests/test_black.py +++ b/tests/test_black.py @@ -157,12 +157,13 @@ def invokeBlack( ) -> None: runner = BlackRunner() if ignore_config: - args = ["--config", str(THIS_DIR / "empty.toml"), *args] + args = ["--verbose", "--config", str(THIS_DIR / "empty.toml"), *args] result = runner.invoke(black.main, args) self.assertEqual( result.exit_code, exit_code, - msg=f"Failed with args: {args}. Stderr: {runner.stderr_bytes.decode()!r}", + msg=f"Failed with args: {args} {os.path.exist(arg) for arg in args}. " + f"Stderr: {runner.stderr_bytes.decode()!r}", ) @patch("black.dump_to_file", dump_to_stderr) From 6945ced345692f7469f77f2b9e57dcebdae491a6 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Fri, 8 May 2020 06:49:50 -0700 Subject: [PATCH 07/11] fix typo --- tests/test_black.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/test_black.py b/tests/test_black.py index 100a782b5d6..7df9666232d 100644 --- a/tests/test_black.py +++ b/tests/test_black.py @@ -162,8 +162,10 @@ def invokeBlack( self.assertEqual( result.exit_code, exit_code, - msg=f"Failed with args: {args} {os.path.exist(arg) for arg in args}. " - f"Stderr: {runner.stderr_bytes.decode()!r}", + msg=( + f"Failed with args: {args} {os.path.exists(arg) for arg in args}. " + f"Stderr: {runner.stderr_bytes.decode()!r}" + ), ) @patch("black.dump_to_file", dump_to_stderr) From 4bb9f065c8317e4d5df097c754dc45463365336c Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Fri, 8 May 2020 07:03:50 -0700 Subject: [PATCH 08/11] what's in gitignore? --- black.py | 1 + tests/test_black.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/black.py b/black.py index bbd4a4b9cee..6f402f41ba2 100644 --- a/black.py +++ b/black.py @@ -5682,6 +5682,7 @@ def get_gitignore(root: Path) -> PathSpec: if gitignore.is_file(): with gitignore.open() as gf: lines = gf.readlines() + out(f"gitignore file {gitignore} contained lines {lines}") return PathSpec.from_lines("gitwildmatch", lines) diff --git a/tests/test_black.py b/tests/test_black.py index 7df9666232d..d922da42957 100644 --- a/tests/test_black.py +++ b/tests/test_black.py @@ -163,7 +163,7 @@ def invokeBlack( result.exit_code, exit_code, msg=( - f"Failed with args: {args} {os.path.exists(arg) for arg in args}. " + f"Failed with args: {args} {[os.path.exists(arg) for arg in args]}. " f"Stderr: {runner.stderr_bytes.decode()!r}" ), ) From 6b88e77d83fba86269dd2f1ccd99121ecbf6c944 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Fri, 8 May 2020 07:11:52 -0700 Subject: [PATCH 09/11] clear the lru cache --- tests/test_black.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_black.py b/tests/test_black.py index d922da42957..2b32528e5dc 100644 --- a/tests/test_black.py +++ b/tests/test_black.py @@ -159,6 +159,7 @@ def invokeBlack( if ignore_config: args = ["--verbose", "--config", str(THIS_DIR / "empty.toml"), *args] result = runner.invoke(black.main, args) + black.get_gitignore.cache_clear() self.assertEqual( result.exit_code, exit_code, From 3d53be65a838fbfb1d8cc77e094c864a4a57d3c6 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Fri, 8 May 2020 07:20:32 -0700 Subject: [PATCH 10/11] make gitignore match only the part of the path within the project --- black.py | 2 +- tests/test_black.py | 6 +----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/black.py b/black.py index 6f402f41ba2..cb609a5ea0a 100644 --- a/black.py +++ b/black.py @@ -5704,7 +5704,7 @@ def gen_python_files( assert root.is_absolute(), f"INTERNAL ERROR: `root` must be absolute but is {root}" for child in paths: # First ignore files matching .gitignore - if gitignore.match_file(child.as_posix()): + if gitignore.match_file(child.relative_to(root).as_posix()): report.path_ignored(child, "matches the .gitignore file content") continue diff --git a/tests/test_black.py b/tests/test_black.py index 2b32528e5dc..380e6905701 100644 --- a/tests/test_black.py +++ b/tests/test_black.py @@ -159,14 +159,10 @@ def invokeBlack( if ignore_config: args = ["--verbose", "--config", str(THIS_DIR / "empty.toml"), *args] result = runner.invoke(black.main, args) - black.get_gitignore.cache_clear() self.assertEqual( result.exit_code, exit_code, - msg=( - f"Failed with args: {args} {[os.path.exists(arg) for arg in args]}. " - f"Stderr: {runner.stderr_bytes.decode()!r}" - ), + msg=(f"Failed with args: {args}. Stderr: {runner.stderr_bytes.decode()!r}"), ) @patch("black.dump_to_file", dump_to_stderr) From 5cd220cb5a6974030ea00946eb0be20beaba10e1 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Fri, 8 May 2020 07:26:45 -0700 Subject: [PATCH 11/11] normalize the path first --- black.py | 15 +++++++-------- tests/test_black.py | 2 +- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/black.py b/black.py index cb609a5ea0a..e55fad957ae 100644 --- a/black.py +++ b/black.py @@ -5682,7 +5682,6 @@ def get_gitignore(root: Path) -> PathSpec: if gitignore.is_file(): with gitignore.open() as gf: lines = gf.readlines() - out(f"gitignore file {gitignore} contained lines {lines}") return PathSpec.from_lines("gitwildmatch", lines) @@ -5703,18 +5702,12 @@ def gen_python_files( """ assert root.is_absolute(), f"INTERNAL ERROR: `root` must be absolute but is {root}" for child in paths: - # First ignore files matching .gitignore - if gitignore.match_file(child.relative_to(root).as_posix()): - report.path_ignored(child, "matches the .gitignore file content") - continue - # Then ignore with `exclude` option. try: - normalized_path = "/" + child.resolve().relative_to(root).as_posix() + normalized_path = child.resolve().relative_to(root).as_posix() except OSError as e: report.path_ignored(child, f"cannot be read because {e}") continue - except ValueError: if child.is_symlink(): report.path_ignored( @@ -5724,6 +5717,12 @@ def gen_python_files( raise + # First ignore files matching .gitignore + if gitignore.match_file(normalized_path): + report.path_ignored(child, "matches the .gitignore file content") + continue + + normalized_path = "/" + normalized_path if child.is_dir(): normalized_path += "/" diff --git a/tests/test_black.py b/tests/test_black.py index 380e6905701..c1de629b5fe 100644 --- a/tests/test_black.py +++ b/tests/test_black.py @@ -162,7 +162,7 @@ def invokeBlack( self.assertEqual( result.exit_code, exit_code, - msg=(f"Failed with args: {args}. Stderr: {runner.stderr_bytes.decode()!r}"), + msg=f"Failed with args: {args}. Stderr: {runner.stderr_bytes.decode()!r}", ) @patch("black.dump_to_file", dump_to_stderr)