All patches and comments are welcome. Please squash your changes to logical
commits before using git-format-patch and git-send-email to
patches@git.madduck.net.
If you'd read over the Git project's submission guidelines and adhered to them,
I'd be especially grateful.
5 from concurrent.futures import ThreadPoolExecutor
6 from contextlib import contextmanager
7 from dataclasses import replace
8 from functools import partial
10 from io import BytesIO, TextIOWrapper
12 from pathlib import Path
13 from platform import system
16 from tempfile import TemporaryDirectory
30 from unittest.mock import patch, MagicMock
33 from click import unstyle
34 from click.testing import CliRunner
37 from black import Feature, TargetVersion
41 from aiohttp.test_utils import AioHTTPTestCase, unittest_run_loop
42 from aiohttp import web
44 has_blackd_deps = False
46 has_blackd_deps = True
48 from pathspec import PathSpec
50 # Import other test classes
51 from .test_primer import PrimerCLITests # noqa: F401
54 DEFAULT_MODE = black.FileMode(experimental_string_processing=True)
55 ff = partial(black.format_file_in_place, mode=DEFAULT_MODE, fast=True)
56 fs = partial(black.format_str, mode=DEFAULT_MODE)
57 THIS_FILE = Path(__file__)
58 THIS_DIR = THIS_FILE.parent
59 PROJECT_ROOT = THIS_DIR.parent
60 DETERMINISTIC_HEADER = "[Deterministic header]"
61 EMPTY_LINE = "# EMPTY LINE WITH WHITESPACE" + " (this comment will be removed)"
68 PY36_ARGS = [f"--target-version={version.name.lower()}" for version in PY36_VERSIONS]
73 def dump_to_stderr(*output: str) -> str:
74 return "\n" + "\n".join(output) + "\n"
77 def read_data(name: str, data: bool = True) -> Tuple[str, str]:
78 """read_data('test_name') -> 'input', 'output'"""
79 if not name.endswith((".py", ".pyi", ".out", ".diff")):
81 _input: List[str] = []
82 _output: List[str] = []
83 base_dir = THIS_DIR / "data" if data else PROJECT_ROOT
84 with open(base_dir / name, "r", encoding="utf8") as test:
85 lines = test.readlines()
88 line = line.replace(EMPTY_LINE, "")
89 if line.rstrip() == "# output":
94 if _input and not _output:
95 # If there's no output marker, treat the entire file as already pre-formatted.
97 return "".join(_input).strip() + "\n", "".join(_output).strip() + "\n"
101 def cache_dir(exists: bool = True) -> Iterator[Path]:
102 with TemporaryDirectory() as workspace:
103 cache_dir = Path(workspace)
105 cache_dir = cache_dir / "new"
106 with patch("black.CACHE_DIR", cache_dir):
111 def event_loop() -> Iterator[None]:
112 policy = asyncio.get_event_loop_policy()
113 loop = policy.new_event_loop()
114 asyncio.set_event_loop(loop)
123 def skip_if_exception(e: str) -> Iterator[None]:
126 except Exception as exc:
127 if exc.__class__.__name__ == e:
128 unittest.skip(f"Encountered expected exception {exc}, skipping")
133 class FakeContext(click.Context):
134 """A fake click Context for when calling functions that need it."""
136 def __init__(self) -> None:
137 self.default_map: Dict[str, Any] = {}
140 class FakeParameter(click.Parameter):
141 """A fake click Parameter for when calling functions that need it."""
143 def __init__(self) -> None:
147 class BlackRunner(CliRunner):
148 """Modify CliRunner so that stderr is not merged with stdout.
150 This is a hack that can be removed once we depend on Click 7.x"""
152 def __init__(self) -> None:
153 self.stderrbuf = BytesIO()
154 self.stdoutbuf = BytesIO()
155 self.stdout_bytes = b""
156 self.stderr_bytes = b""
160 def isolation(self, *args: Any, **kwargs: Any) -> Generator[BinaryIO, None, None]:
161 with super().isolation(*args, **kwargs) as output:
163 hold_stderr = sys.stderr
164 sys.stderr = TextIOWrapper(self.stderrbuf, encoding=self.charset)
167 self.stdout_bytes = sys.stdout.buffer.getvalue() # type: ignore
168 self.stderr_bytes = sys.stderr.buffer.getvalue() # type: ignore
169 sys.stderr = hold_stderr
172 class BlackTestCase(unittest.TestCase):
174 _diffThreshold = 2 ** 20
176 def assertFormatEqual(self, expected: str, actual: str) -> None:
177 if actual != expected and not os.environ.get("SKIP_AST_PRINT"):
178 bdv: black.DebugVisitor[Any]
179 black.out("Expected tree:", fg="green")
181 exp_node = black.lib2to3_parse(expected)
182 bdv = black.DebugVisitor()
183 list(bdv.visit(exp_node))
184 except Exception as ve:
186 black.out("Actual tree:", fg="red")
188 exp_node = black.lib2to3_parse(actual)
189 bdv = black.DebugVisitor()
190 list(bdv.visit(exp_node))
191 except Exception as ve:
193 self.assertMultiLineEqual(expected, actual)
196 self, args: List[str], exit_code: int = 0, ignore_config: bool = True
198 runner = BlackRunner()
200 args = ["--verbose", "--config", str(THIS_DIR / "empty.toml"), *args]
201 result = runner.invoke(black.main, args)
206 f"Failed with args: {args}\n"
207 f"stdout: {runner.stdout_bytes.decode()!r}\n"
208 f"stderr: {runner.stderr_bytes.decode()!r}\n"
209 f"exception: {result.exception}"
213 @patch("black.dump_to_file", dump_to_stderr)
214 def checkSourceFile(self, name: str, mode: black.FileMode = DEFAULT_MODE) -> None:
215 path = THIS_DIR.parent / name
216 source, expected = read_data(str(path), data=False)
217 actual = fs(source, mode=mode)
218 self.assertFormatEqual(expected, actual)
219 black.assert_equivalent(source, actual)
220 black.assert_stable(source, actual, mode)
221 self.assertFalse(ff(path))
223 @patch("black.dump_to_file", dump_to_stderr)
224 def test_empty(self) -> None:
225 source = expected = ""
227 self.assertFormatEqual(expected, actual)
228 black.assert_equivalent(source, actual)
229 black.assert_stable(source, actual, DEFAULT_MODE)
231 def test_empty_ff(self) -> None:
233 tmp_file = Path(black.dump_to_file())
235 self.assertFalse(ff(tmp_file, write_back=black.WriteBack.YES))
236 with open(tmp_file, encoding="utf8") as f:
240 self.assertFormatEqual(expected, actual)
242 def test_self(self) -> None:
243 self.checkSourceFile("tests/test_black.py")
245 def test_black(self) -> None:
246 self.checkSourceFile("src/black/__init__.py")
248 def test_pygram(self) -> None:
249 self.checkSourceFile("src/blib2to3/pygram.py")
251 def test_pytree(self) -> None:
252 self.checkSourceFile("src/blib2to3/pytree.py")
254 def test_conv(self) -> None:
255 self.checkSourceFile("src/blib2to3/pgen2/conv.py")
257 def test_driver(self) -> None:
258 self.checkSourceFile("src/blib2to3/pgen2/driver.py")
260 def test_grammar(self) -> None:
261 self.checkSourceFile("src/blib2to3/pgen2/grammar.py")
263 def test_literals(self) -> None:
264 self.checkSourceFile("src/blib2to3/pgen2/literals.py")
266 def test_parse(self) -> None:
267 self.checkSourceFile("src/blib2to3/pgen2/parse.py")
269 def test_pgen(self) -> None:
270 self.checkSourceFile("src/blib2to3/pgen2/pgen.py")
272 def test_tokenize(self) -> None:
273 self.checkSourceFile("src/blib2to3/pgen2/tokenize.py")
275 def test_token(self) -> None:
276 self.checkSourceFile("src/blib2to3/pgen2/token.py")
278 def test_setup(self) -> None:
279 self.checkSourceFile("setup.py")
281 def test_piping(self) -> None:
282 source, expected = read_data("src/black/__init__", data=False)
283 result = BlackRunner().invoke(
285 ["-", "--fast", f"--line-length={black.DEFAULT_LINE_LENGTH}"],
286 input=BytesIO(source.encode("utf8")),
288 self.assertEqual(result.exit_code, 0)
289 self.assertFormatEqual(expected, result.output)
290 black.assert_equivalent(source, result.output)
291 black.assert_stable(source, result.output, DEFAULT_MODE)
293 def test_piping_diff(self) -> None:
294 diff_header = re.compile(
295 r"(STDIN|STDOUT)\t\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d\.\d\d\d\d\d\d "
298 source, _ = read_data("expression.py")
299 expected, _ = read_data("expression.diff")
300 config = THIS_DIR / "data" / "empty_pyproject.toml"
304 f"--line-length={black.DEFAULT_LINE_LENGTH}",
306 f"--config={config}",
308 result = BlackRunner().invoke(
309 black.main, args, input=BytesIO(source.encode("utf8"))
311 self.assertEqual(result.exit_code, 0)
312 actual = diff_header.sub(DETERMINISTIC_HEADER, result.output)
313 actual = actual.rstrip() + "\n" # the diff output has a trailing space
314 self.assertEqual(expected, actual)
316 def test_piping_diff_with_color(self) -> None:
317 source, _ = read_data("expression.py")
318 config = THIS_DIR / "data" / "empty_pyproject.toml"
322 f"--line-length={black.DEFAULT_LINE_LENGTH}",
325 f"--config={config}",
327 result = BlackRunner().invoke(
328 black.main, args, input=BytesIO(source.encode("utf8"))
330 actual = result.output
331 # Again, the contents are checked in a different test, so only look for colors.
332 self.assertIn("\033[1;37m", actual)
333 self.assertIn("\033[36m", actual)
334 self.assertIn("\033[32m", actual)
335 self.assertIn("\033[31m", actual)
336 self.assertIn("\033[0m", actual)
338 @patch("black.dump_to_file", dump_to_stderr)
339 def test_function(self) -> None:
340 source, expected = read_data("function")
342 self.assertFormatEqual(expected, actual)
343 black.assert_equivalent(source, actual)
344 black.assert_stable(source, actual, DEFAULT_MODE)
346 @patch("black.dump_to_file", dump_to_stderr)
347 def test_function2(self) -> None:
348 source, expected = read_data("function2")
350 self.assertFormatEqual(expected, actual)
351 black.assert_equivalent(source, actual)
352 black.assert_stable(source, actual, DEFAULT_MODE)
354 @patch("black.dump_to_file", dump_to_stderr)
355 def _test_wip(self) -> None:
356 source, expected = read_data("wip")
357 sys.settrace(tracefunc)
360 experimental_string_processing=False,
361 target_versions={black.TargetVersion.PY38},
363 actual = fs(source, mode=mode)
365 self.assertFormatEqual(expected, actual)
366 black.assert_equivalent(source, actual)
367 black.assert_stable(source, actual, black.FileMode())
369 @patch("black.dump_to_file", dump_to_stderr)
370 def test_function_trailing_comma(self) -> None:
371 source, expected = read_data("function_trailing_comma")
373 self.assertFormatEqual(expected, actual)
374 black.assert_equivalent(source, actual)
375 black.assert_stable(source, actual, DEFAULT_MODE)
377 @unittest.expectedFailure
378 @patch("black.dump_to_file", dump_to_stderr)
379 def test_trailing_comma_optional_parens_stability1(self) -> None:
380 source, _expected = read_data("trailing_comma_optional_parens1")
382 black.assert_stable(source, actual, DEFAULT_MODE)
384 @unittest.expectedFailure
385 @patch("black.dump_to_file", dump_to_stderr)
386 def test_trailing_comma_optional_parens_stability2(self) -> None:
387 source, _expected = read_data("trailing_comma_optional_parens2")
389 black.assert_stable(source, actual, DEFAULT_MODE)
391 @unittest.expectedFailure
392 @patch("black.dump_to_file", dump_to_stderr)
393 def test_trailing_comma_optional_parens_stability3(self) -> None:
394 source, _expected = read_data("trailing_comma_optional_parens3")
396 black.assert_stable(source, actual, DEFAULT_MODE)
398 @patch("black.dump_to_file", dump_to_stderr)
399 def test_expression(self) -> None:
400 source, expected = read_data("expression")
402 self.assertFormatEqual(expected, actual)
403 black.assert_equivalent(source, actual)
404 black.assert_stable(source, actual, DEFAULT_MODE)
406 @patch("black.dump_to_file", dump_to_stderr)
407 def test_pep_572(self) -> None:
408 source, expected = read_data("pep_572")
410 self.assertFormatEqual(expected, actual)
411 black.assert_stable(source, actual, DEFAULT_MODE)
412 if sys.version_info >= (3, 8):
413 black.assert_equivalent(source, actual)
415 def test_pep_572_version_detection(self) -> None:
416 source, _ = read_data("pep_572")
417 root = black.lib2to3_parse(source)
418 features = black.get_features_used(root)
419 self.assertIn(black.Feature.ASSIGNMENT_EXPRESSIONS, features)
420 versions = black.detect_target_versions(root)
421 self.assertIn(black.TargetVersion.PY38, versions)
423 def test_expression_ff(self) -> None:
424 source, expected = read_data("expression")
425 tmp_file = Path(black.dump_to_file(source))
427 self.assertTrue(ff(tmp_file, write_back=black.WriteBack.YES))
428 with open(tmp_file, encoding="utf8") as f:
432 self.assertFormatEqual(expected, actual)
433 with patch("black.dump_to_file", dump_to_stderr):
434 black.assert_equivalent(source, actual)
435 black.assert_stable(source, actual, DEFAULT_MODE)
437 def test_expression_diff(self) -> None:
438 source, _ = read_data("expression.py")
439 expected, _ = read_data("expression.diff")
440 tmp_file = Path(black.dump_to_file(source))
441 diff_header = re.compile(
442 rf"{re.escape(str(tmp_file))}\t\d\d\d\d-\d\d-\d\d "
443 r"\d\d:\d\d:\d\d\.\d\d\d\d\d\d \+\d\d\d\d"
446 result = BlackRunner().invoke(black.main, ["--diff", str(tmp_file)])
447 self.assertEqual(result.exit_code, 0)
450 actual = result.output
451 actual = diff_header.sub(DETERMINISTIC_HEADER, actual)
452 actual = actual.rstrip() + "\n" # the diff output has a trailing space
453 if expected != actual:
454 dump = black.dump_to_file(actual)
456 "Expected diff isn't equal to the actual. If you made changes to"
457 " expression.py and this is an anticipated difference, overwrite"
458 f" tests/data/expression.diff with {dump}"
460 self.assertEqual(expected, actual, msg)
462 def test_expression_diff_with_color(self) -> None:
463 source, _ = read_data("expression.py")
464 expected, _ = read_data("expression.diff")
465 tmp_file = Path(black.dump_to_file(source))
467 result = BlackRunner().invoke(
468 black.main, ["--diff", "--color", str(tmp_file)]
472 actual = result.output
473 # We check the contents of the diff in `test_expression_diff`. All
474 # we need to check here is that color codes exist in the result.
475 self.assertIn("\033[1;37m", actual)
476 self.assertIn("\033[36m", actual)
477 self.assertIn("\033[32m", actual)
478 self.assertIn("\033[31m", actual)
479 self.assertIn("\033[0m", actual)
481 @patch("black.dump_to_file", dump_to_stderr)
482 def test_fstring(self) -> None:
483 source, expected = read_data("fstring")
485 self.assertFormatEqual(expected, actual)
486 black.assert_equivalent(source, actual)
487 black.assert_stable(source, actual, DEFAULT_MODE)
489 @patch("black.dump_to_file", dump_to_stderr)
490 def test_pep_570(self) -> None:
491 source, expected = read_data("pep_570")
493 self.assertFormatEqual(expected, actual)
494 black.assert_stable(source, actual, DEFAULT_MODE)
495 if sys.version_info >= (3, 8):
496 black.assert_equivalent(source, actual)
498 def test_detect_pos_only_arguments(self) -> None:
499 source, _ = read_data("pep_570")
500 root = black.lib2to3_parse(source)
501 features = black.get_features_used(root)
502 self.assertIn(black.Feature.POS_ONLY_ARGUMENTS, features)
503 versions = black.detect_target_versions(root)
504 self.assertIn(black.TargetVersion.PY38, versions)
506 @patch("black.dump_to_file", dump_to_stderr)
507 def test_string_quotes(self) -> None:
508 source, expected = read_data("string_quotes")
510 self.assertFormatEqual(expected, actual)
511 black.assert_equivalent(source, actual)
512 black.assert_stable(source, actual, DEFAULT_MODE)
513 mode = replace(DEFAULT_MODE, string_normalization=False)
514 not_normalized = fs(source, mode=mode)
515 self.assertFormatEqual(source.replace("\\\n", ""), not_normalized)
516 black.assert_equivalent(source, not_normalized)
517 black.assert_stable(source, not_normalized, mode=mode)
519 @patch("black.dump_to_file", dump_to_stderr)
520 def test_docstring(self) -> None:
521 source, expected = read_data("docstring")
523 self.assertFormatEqual(expected, actual)
524 black.assert_equivalent(source, actual)
525 black.assert_stable(source, actual, DEFAULT_MODE)
527 @patch("black.dump_to_file", dump_to_stderr)
528 def test_docstring_no_string_normalization(self) -> None:
529 """Like test_docstring but with string normalization off."""
530 source, expected = read_data("docstring_no_string_normalization")
531 mode = replace(DEFAULT_MODE, string_normalization=False)
532 actual = fs(source, mode=mode)
533 self.assertFormatEqual(expected, actual)
534 black.assert_equivalent(source, actual)
535 black.assert_stable(source, actual, mode)
537 def test_long_strings(self) -> None:
538 """Tests for splitting long strings."""
539 source, expected = read_data("long_strings")
541 self.assertFormatEqual(expected, actual)
542 black.assert_equivalent(source, actual)
543 black.assert_stable(source, actual, DEFAULT_MODE)
545 def test_long_strings_flag_disabled(self) -> None:
546 """Tests for turning off the string processing logic."""
547 source, expected = read_data("long_strings_flag_disabled")
548 mode = replace(DEFAULT_MODE, experimental_string_processing=False)
549 actual = fs(source, mode=mode)
550 self.assertFormatEqual(expected, actual)
551 black.assert_stable(expected, actual, mode)
553 @patch("black.dump_to_file", dump_to_stderr)
554 def test_long_strings__edge_case(self) -> None:
555 """Edge-case tests for splitting long strings."""
556 source, expected = read_data("long_strings__edge_case")
558 self.assertFormatEqual(expected, actual)
559 black.assert_equivalent(source, actual)
560 black.assert_stable(source, actual, DEFAULT_MODE)
562 @patch("black.dump_to_file", dump_to_stderr)
563 def test_long_strings__regression(self) -> None:
564 """Regression tests for splitting long strings."""
565 source, expected = read_data("long_strings__regression")
567 self.assertFormatEqual(expected, actual)
568 black.assert_equivalent(source, actual)
569 black.assert_stable(source, actual, DEFAULT_MODE)
571 @patch("black.dump_to_file", dump_to_stderr)
572 def test_slices(self) -> None:
573 source, expected = read_data("slices")
575 self.assertFormatEqual(expected, actual)
576 black.assert_equivalent(source, actual)
577 black.assert_stable(source, actual, DEFAULT_MODE)
579 @patch("black.dump_to_file", dump_to_stderr)
580 def test_percent_precedence(self) -> None:
581 source, expected = read_data("percent_precedence")
583 self.assertFormatEqual(expected, actual)
584 black.assert_equivalent(source, actual)
585 black.assert_stable(source, actual, DEFAULT_MODE)
587 @patch("black.dump_to_file", dump_to_stderr)
588 def test_comments(self) -> None:
589 source, expected = read_data("comments")
591 self.assertFormatEqual(expected, actual)
592 black.assert_equivalent(source, actual)
593 black.assert_stable(source, actual, DEFAULT_MODE)
595 @patch("black.dump_to_file", dump_to_stderr)
596 def test_comments2(self) -> None:
597 source, expected = read_data("comments2")
599 self.assertFormatEqual(expected, actual)
600 black.assert_equivalent(source, actual)
601 black.assert_stable(source, actual, DEFAULT_MODE)
603 @patch("black.dump_to_file", dump_to_stderr)
604 def test_comments3(self) -> None:
605 source, expected = read_data("comments3")
607 self.assertFormatEqual(expected, actual)
608 black.assert_equivalent(source, actual)
609 black.assert_stable(source, actual, DEFAULT_MODE)
611 @patch("black.dump_to_file", dump_to_stderr)
612 def test_comments4(self) -> None:
613 source, expected = read_data("comments4")
615 self.assertFormatEqual(expected, actual)
616 black.assert_equivalent(source, actual)
617 black.assert_stable(source, actual, DEFAULT_MODE)
619 @patch("black.dump_to_file", dump_to_stderr)
620 def test_comments5(self) -> None:
621 source, expected = read_data("comments5")
623 self.assertFormatEqual(expected, actual)
624 black.assert_equivalent(source, actual)
625 black.assert_stable(source, actual, DEFAULT_MODE)
627 @patch("black.dump_to_file", dump_to_stderr)
628 def test_comments6(self) -> None:
629 source, expected = read_data("comments6")
631 self.assertFormatEqual(expected, actual)
632 black.assert_equivalent(source, actual)
633 black.assert_stable(source, actual, DEFAULT_MODE)
635 @patch("black.dump_to_file", dump_to_stderr)
636 def test_comments7(self) -> None:
637 source, expected = read_data("comments7")
638 mode = replace(DEFAULT_MODE, target_versions={black.TargetVersion.PY38})
639 actual = fs(source, mode=mode)
640 self.assertFormatEqual(expected, actual)
641 black.assert_equivalent(source, actual)
642 black.assert_stable(source, actual, DEFAULT_MODE)
644 @patch("black.dump_to_file", dump_to_stderr)
645 def test_comment_after_escaped_newline(self) -> None:
646 source, expected = read_data("comment_after_escaped_newline")
648 self.assertFormatEqual(expected, actual)
649 black.assert_equivalent(source, actual)
650 black.assert_stable(source, actual, DEFAULT_MODE)
652 @patch("black.dump_to_file", dump_to_stderr)
653 def test_cantfit(self) -> None:
654 source, expected = read_data("cantfit")
656 self.assertFormatEqual(expected, actual)
657 black.assert_equivalent(source, actual)
658 black.assert_stable(source, actual, DEFAULT_MODE)
660 @patch("black.dump_to_file", dump_to_stderr)
661 def test_import_spacing(self) -> None:
662 source, expected = read_data("import_spacing")
664 self.assertFormatEqual(expected, actual)
665 black.assert_equivalent(source, actual)
666 black.assert_stable(source, actual, DEFAULT_MODE)
668 @patch("black.dump_to_file", dump_to_stderr)
669 def test_composition(self) -> None:
670 source, expected = read_data("composition")
672 self.assertFormatEqual(expected, actual)
673 black.assert_equivalent(source, actual)
674 black.assert_stable(source, actual, DEFAULT_MODE)
676 @patch("black.dump_to_file", dump_to_stderr)
677 def test_composition_no_trailing_comma(self) -> None:
678 source, expected = read_data("composition_no_trailing_comma")
679 mode = replace(DEFAULT_MODE, target_versions={black.TargetVersion.PY38})
680 actual = fs(source, mode=mode)
681 self.assertFormatEqual(expected, actual)
682 black.assert_equivalent(source, actual)
683 black.assert_stable(source, actual, DEFAULT_MODE)
685 @patch("black.dump_to_file", dump_to_stderr)
686 def test_empty_lines(self) -> None:
687 source, expected = read_data("empty_lines")
689 self.assertFormatEqual(expected, actual)
690 black.assert_equivalent(source, actual)
691 black.assert_stable(source, actual, DEFAULT_MODE)
693 @patch("black.dump_to_file", dump_to_stderr)
694 def test_remove_parens(self) -> None:
695 source, expected = read_data("remove_parens")
697 self.assertFormatEqual(expected, actual)
698 black.assert_equivalent(source, actual)
699 black.assert_stable(source, actual, DEFAULT_MODE)
701 @patch("black.dump_to_file", dump_to_stderr)
702 def test_string_prefixes(self) -> None:
703 source, expected = read_data("string_prefixes")
705 self.assertFormatEqual(expected, actual)
706 black.assert_equivalent(source, actual)
707 black.assert_stable(source, actual, DEFAULT_MODE)
709 @patch("black.dump_to_file", dump_to_stderr)
710 def test_numeric_literals(self) -> None:
711 source, expected = read_data("numeric_literals")
712 mode = replace(DEFAULT_MODE, target_versions=PY36_VERSIONS)
713 actual = fs(source, mode=mode)
714 self.assertFormatEqual(expected, actual)
715 black.assert_equivalent(source, actual)
716 black.assert_stable(source, actual, mode)
718 @patch("black.dump_to_file", dump_to_stderr)
719 def test_numeric_literals_ignoring_underscores(self) -> None:
720 source, expected = read_data("numeric_literals_skip_underscores")
721 mode = replace(DEFAULT_MODE, target_versions=PY36_VERSIONS)
722 actual = fs(source, mode=mode)
723 self.assertFormatEqual(expected, actual)
724 black.assert_equivalent(source, actual)
725 black.assert_stable(source, actual, mode)
727 @patch("black.dump_to_file", dump_to_stderr)
728 def test_numeric_literals_py2(self) -> None:
729 source, expected = read_data("numeric_literals_py2")
731 self.assertFormatEqual(expected, actual)
732 black.assert_stable(source, actual, DEFAULT_MODE)
734 @patch("black.dump_to_file", dump_to_stderr)
735 def test_python2(self) -> None:
736 source, expected = read_data("python2")
738 self.assertFormatEqual(expected, actual)
739 black.assert_equivalent(source, actual)
740 black.assert_stable(source, actual, DEFAULT_MODE)
742 @patch("black.dump_to_file", dump_to_stderr)
743 def test_python2_print_function(self) -> None:
744 source, expected = read_data("python2_print_function")
745 mode = replace(DEFAULT_MODE, target_versions={TargetVersion.PY27})
746 actual = fs(source, mode=mode)
747 self.assertFormatEqual(expected, actual)
748 black.assert_equivalent(source, actual)
749 black.assert_stable(source, actual, mode)
751 @patch("black.dump_to_file", dump_to_stderr)
752 def test_python2_unicode_literals(self) -> None:
753 source, expected = read_data("python2_unicode_literals")
755 self.assertFormatEqual(expected, actual)
756 black.assert_equivalent(source, actual)
757 black.assert_stable(source, actual, DEFAULT_MODE)
759 @patch("black.dump_to_file", dump_to_stderr)
760 def test_stub(self) -> None:
761 mode = replace(DEFAULT_MODE, is_pyi=True)
762 source, expected = read_data("stub.pyi")
763 actual = fs(source, mode=mode)
764 self.assertFormatEqual(expected, actual)
765 black.assert_stable(source, actual, mode)
767 @patch("black.dump_to_file", dump_to_stderr)
768 def test_async_as_identifier(self) -> None:
769 source_path = (THIS_DIR / "data" / "async_as_identifier.py").resolve()
770 source, expected = read_data("async_as_identifier")
772 self.assertFormatEqual(expected, actual)
773 major, minor = sys.version_info[:2]
774 if major < 3 or (major <= 3 and minor < 7):
775 black.assert_equivalent(source, actual)
776 black.assert_stable(source, actual, DEFAULT_MODE)
777 # ensure black can parse this when the target is 3.6
778 self.invokeBlack([str(source_path), "--target-version", "py36"])
779 # but not on 3.7, because async/await is no longer an identifier
780 self.invokeBlack([str(source_path), "--target-version", "py37"], exit_code=123)
782 @patch("black.dump_to_file", dump_to_stderr)
783 def test_python37(self) -> None:
784 source_path = (THIS_DIR / "data" / "python37.py").resolve()
785 source, expected = read_data("python37")
787 self.assertFormatEqual(expected, actual)
788 major, minor = sys.version_info[:2]
789 if major > 3 or (major == 3 and minor >= 7):
790 black.assert_equivalent(source, actual)
791 black.assert_stable(source, actual, DEFAULT_MODE)
792 # ensure black can parse this when the target is 3.7
793 self.invokeBlack([str(source_path), "--target-version", "py37"])
794 # but not on 3.6, because we use async as a reserved keyword
795 self.invokeBlack([str(source_path), "--target-version", "py36"], exit_code=123)
797 @patch("black.dump_to_file", dump_to_stderr)
798 def test_python38(self) -> None:
799 source, expected = read_data("python38")
801 self.assertFormatEqual(expected, actual)
802 major, minor = sys.version_info[:2]
803 if major > 3 or (major == 3 and minor >= 8):
804 black.assert_equivalent(source, actual)
805 black.assert_stable(source, actual, DEFAULT_MODE)
807 @patch("black.dump_to_file", dump_to_stderr)
808 def test_python39(self) -> None:
809 source, expected = read_data("python39")
811 self.assertFormatEqual(expected, actual)
812 major, minor = sys.version_info[:2]
813 if major > 3 or (major == 3 and minor >= 9):
814 black.assert_equivalent(source, actual)
815 black.assert_stable(source, actual, DEFAULT_MODE)
817 @patch("black.dump_to_file", dump_to_stderr)
818 def test_fmtonoff(self) -> None:
819 source, expected = read_data("fmtonoff")
821 self.assertFormatEqual(expected, actual)
822 black.assert_equivalent(source, actual)
823 black.assert_stable(source, actual, DEFAULT_MODE)
825 @patch("black.dump_to_file", dump_to_stderr)
826 def test_fmtonoff2(self) -> None:
827 source, expected = read_data("fmtonoff2")
829 self.assertFormatEqual(expected, actual)
830 black.assert_equivalent(source, actual)
831 black.assert_stable(source, actual, DEFAULT_MODE)
833 @patch("black.dump_to_file", dump_to_stderr)
834 def test_fmtonoff3(self) -> None:
835 source, expected = read_data("fmtonoff3")
837 self.assertFormatEqual(expected, actual)
838 black.assert_equivalent(source, actual)
839 black.assert_stable(source, actual, DEFAULT_MODE)
841 @patch("black.dump_to_file", dump_to_stderr)
842 def test_fmtonoff4(self) -> None:
843 source, expected = read_data("fmtonoff4")
845 self.assertFormatEqual(expected, actual)
846 black.assert_equivalent(source, actual)
847 black.assert_stable(source, actual, DEFAULT_MODE)
849 @patch("black.dump_to_file", dump_to_stderr)
850 def test_remove_empty_parentheses_after_class(self) -> None:
851 source, expected = read_data("class_blank_parentheses")
853 self.assertFormatEqual(expected, actual)
854 black.assert_equivalent(source, actual)
855 black.assert_stable(source, actual, DEFAULT_MODE)
857 @patch("black.dump_to_file", dump_to_stderr)
858 def test_new_line_between_class_and_code(self) -> None:
859 source, expected = read_data("class_methods_new_line")
861 self.assertFormatEqual(expected, actual)
862 black.assert_equivalent(source, actual)
863 black.assert_stable(source, actual, DEFAULT_MODE)
865 @patch("black.dump_to_file", dump_to_stderr)
866 def test_bracket_match(self) -> None:
867 source, expected = read_data("bracketmatch")
869 self.assertFormatEqual(expected, actual)
870 black.assert_equivalent(source, actual)
871 black.assert_stable(source, actual, DEFAULT_MODE)
873 @patch("black.dump_to_file", dump_to_stderr)
874 def test_tuple_assign(self) -> None:
875 source, expected = read_data("tupleassign")
877 self.assertFormatEqual(expected, actual)
878 black.assert_equivalent(source, actual)
879 black.assert_stable(source, actual, DEFAULT_MODE)
881 @patch("black.dump_to_file", dump_to_stderr)
882 def test_beginning_backslash(self) -> None:
883 source, expected = read_data("beginning_backslash")
885 self.assertFormatEqual(expected, actual)
886 black.assert_equivalent(source, actual)
887 black.assert_stable(source, actual, DEFAULT_MODE)
889 def test_tab_comment_indentation(self) -> None:
890 contents_tab = "if 1:\n\tif 2:\n\t\tpass\n\t# comment\n\tpass\n"
891 contents_spc = "if 1:\n if 2:\n pass\n # comment\n pass\n"
892 self.assertFormatEqual(contents_spc, fs(contents_spc))
893 self.assertFormatEqual(contents_spc, fs(contents_tab))
895 contents_tab = "if 1:\n\tif 2:\n\t\tpass\n\t\t# comment\n\tpass\n"
896 contents_spc = "if 1:\n if 2:\n pass\n # comment\n pass\n"
897 self.assertFormatEqual(contents_spc, fs(contents_spc))
898 self.assertFormatEqual(contents_spc, fs(contents_tab))
900 # mixed tabs and spaces (valid Python 2 code)
901 contents_tab = "if 1:\n if 2:\n\t\tpass\n\t# comment\n pass\n"
902 contents_spc = "if 1:\n if 2:\n pass\n # comment\n pass\n"
903 self.assertFormatEqual(contents_spc, fs(contents_spc))
904 self.assertFormatEqual(contents_spc, fs(contents_tab))
906 contents_tab = "if 1:\n if 2:\n\t\tpass\n\t\t# comment\n pass\n"
907 contents_spc = "if 1:\n if 2:\n pass\n # comment\n pass\n"
908 self.assertFormatEqual(contents_spc, fs(contents_spc))
909 self.assertFormatEqual(contents_spc, fs(contents_tab))
911 def test_report_verbose(self) -> None:
912 report = black.Report(verbose=True)
916 def out(msg: str, **kwargs: Any) -> None:
917 out_lines.append(msg)
919 def err(msg: str, **kwargs: Any) -> None:
920 err_lines.append(msg)
922 with patch("black.out", out), patch("black.err", err):
923 report.done(Path("f1"), black.Changed.NO)
924 self.assertEqual(len(out_lines), 1)
925 self.assertEqual(len(err_lines), 0)
926 self.assertEqual(out_lines[-1], "f1 already well formatted, good job.")
927 self.assertEqual(unstyle(str(report)), "1 file left unchanged.")
928 self.assertEqual(report.return_code, 0)
929 report.done(Path("f2"), black.Changed.YES)
930 self.assertEqual(len(out_lines), 2)
931 self.assertEqual(len(err_lines), 0)
932 self.assertEqual(out_lines[-1], "reformatted f2")
934 unstyle(str(report)), "1 file reformatted, 1 file left unchanged."
936 report.done(Path("f3"), black.Changed.CACHED)
937 self.assertEqual(len(out_lines), 3)
938 self.assertEqual(len(err_lines), 0)
940 out_lines[-1], "f3 wasn't modified on disk since last run."
943 unstyle(str(report)), "1 file reformatted, 2 files left unchanged."
945 self.assertEqual(report.return_code, 0)
947 self.assertEqual(report.return_code, 1)
949 report.failed(Path("e1"), "boom")
950 self.assertEqual(len(out_lines), 3)
951 self.assertEqual(len(err_lines), 1)
952 self.assertEqual(err_lines[-1], "error: cannot format e1: boom")
954 unstyle(str(report)),
955 "1 file reformatted, 2 files left unchanged, 1 file failed to"
958 self.assertEqual(report.return_code, 123)
959 report.done(Path("f3"), black.Changed.YES)
960 self.assertEqual(len(out_lines), 4)
961 self.assertEqual(len(err_lines), 1)
962 self.assertEqual(out_lines[-1], "reformatted f3")
964 unstyle(str(report)),
965 "2 files reformatted, 2 files left unchanged, 1 file failed to"
968 self.assertEqual(report.return_code, 123)
969 report.failed(Path("e2"), "boom")
970 self.assertEqual(len(out_lines), 4)
971 self.assertEqual(len(err_lines), 2)
972 self.assertEqual(err_lines[-1], "error: cannot format e2: boom")
974 unstyle(str(report)),
975 "2 files reformatted, 2 files left unchanged, 2 files failed to"
978 self.assertEqual(report.return_code, 123)
979 report.path_ignored(Path("wat"), "no match")
980 self.assertEqual(len(out_lines), 5)
981 self.assertEqual(len(err_lines), 2)
982 self.assertEqual(out_lines[-1], "wat ignored: no match")
984 unstyle(str(report)),
985 "2 files reformatted, 2 files left unchanged, 2 files failed to"
988 self.assertEqual(report.return_code, 123)
989 report.done(Path("f4"), black.Changed.NO)
990 self.assertEqual(len(out_lines), 6)
991 self.assertEqual(len(err_lines), 2)
992 self.assertEqual(out_lines[-1], "f4 already well formatted, good job.")
994 unstyle(str(report)),
995 "2 files reformatted, 3 files left unchanged, 2 files failed to"
998 self.assertEqual(report.return_code, 123)
1001 unstyle(str(report)),
1002 "2 files would be reformatted, 3 files would be left unchanged, 2 files"
1003 " would fail to reformat.",
1005 report.check = False
1008 unstyle(str(report)),
1009 "2 files would be reformatted, 3 files would be left unchanged, 2 files"
1010 " would fail to reformat.",
1013 def test_report_quiet(self) -> None:
1014 report = black.Report(quiet=True)
1018 def out(msg: str, **kwargs: Any) -> None:
1019 out_lines.append(msg)
1021 def err(msg: str, **kwargs: Any) -> None:
1022 err_lines.append(msg)
1024 with patch("black.out", out), patch("black.err", err):
1025 report.done(Path("f1"), black.Changed.NO)
1026 self.assertEqual(len(out_lines), 0)
1027 self.assertEqual(len(err_lines), 0)
1028 self.assertEqual(unstyle(str(report)), "1 file left unchanged.")
1029 self.assertEqual(report.return_code, 0)
1030 report.done(Path("f2"), black.Changed.YES)
1031 self.assertEqual(len(out_lines), 0)
1032 self.assertEqual(len(err_lines), 0)
1034 unstyle(str(report)), "1 file reformatted, 1 file left unchanged."
1036 report.done(Path("f3"), black.Changed.CACHED)
1037 self.assertEqual(len(out_lines), 0)
1038 self.assertEqual(len(err_lines), 0)
1040 unstyle(str(report)), "1 file reformatted, 2 files left unchanged."
1042 self.assertEqual(report.return_code, 0)
1044 self.assertEqual(report.return_code, 1)
1045 report.check = False
1046 report.failed(Path("e1"), "boom")
1047 self.assertEqual(len(out_lines), 0)
1048 self.assertEqual(len(err_lines), 1)
1049 self.assertEqual(err_lines[-1], "error: cannot format e1: boom")
1051 unstyle(str(report)),
1052 "1 file reformatted, 2 files left unchanged, 1 file failed to"
1055 self.assertEqual(report.return_code, 123)
1056 report.done(Path("f3"), black.Changed.YES)
1057 self.assertEqual(len(out_lines), 0)
1058 self.assertEqual(len(err_lines), 1)
1060 unstyle(str(report)),
1061 "2 files reformatted, 2 files left unchanged, 1 file failed to"
1064 self.assertEqual(report.return_code, 123)
1065 report.failed(Path("e2"), "boom")
1066 self.assertEqual(len(out_lines), 0)
1067 self.assertEqual(len(err_lines), 2)
1068 self.assertEqual(err_lines[-1], "error: cannot format e2: boom")
1070 unstyle(str(report)),
1071 "2 files reformatted, 2 files left unchanged, 2 files failed to"
1074 self.assertEqual(report.return_code, 123)
1075 report.path_ignored(Path("wat"), "no match")
1076 self.assertEqual(len(out_lines), 0)
1077 self.assertEqual(len(err_lines), 2)
1079 unstyle(str(report)),
1080 "2 files reformatted, 2 files left unchanged, 2 files failed to"
1083 self.assertEqual(report.return_code, 123)
1084 report.done(Path("f4"), black.Changed.NO)
1085 self.assertEqual(len(out_lines), 0)
1086 self.assertEqual(len(err_lines), 2)
1088 unstyle(str(report)),
1089 "2 files reformatted, 3 files left unchanged, 2 files failed to"
1092 self.assertEqual(report.return_code, 123)
1095 unstyle(str(report)),
1096 "2 files would be reformatted, 3 files would be left unchanged, 2 files"
1097 " would fail to reformat.",
1099 report.check = False
1102 unstyle(str(report)),
1103 "2 files would be reformatted, 3 files would be left unchanged, 2 files"
1104 " would fail to reformat.",
1107 def test_report_normal(self) -> None:
1108 report = black.Report()
1112 def out(msg: str, **kwargs: Any) -> None:
1113 out_lines.append(msg)
1115 def err(msg: str, **kwargs: Any) -> None:
1116 err_lines.append(msg)
1118 with patch("black.out", out), patch("black.err", err):
1119 report.done(Path("f1"), black.Changed.NO)
1120 self.assertEqual(len(out_lines), 0)
1121 self.assertEqual(len(err_lines), 0)
1122 self.assertEqual(unstyle(str(report)), "1 file left unchanged.")
1123 self.assertEqual(report.return_code, 0)
1124 report.done(Path("f2"), black.Changed.YES)
1125 self.assertEqual(len(out_lines), 1)
1126 self.assertEqual(len(err_lines), 0)
1127 self.assertEqual(out_lines[-1], "reformatted f2")
1129 unstyle(str(report)), "1 file reformatted, 1 file left unchanged."
1131 report.done(Path("f3"), black.Changed.CACHED)
1132 self.assertEqual(len(out_lines), 1)
1133 self.assertEqual(len(err_lines), 0)
1134 self.assertEqual(out_lines[-1], "reformatted f2")
1136 unstyle(str(report)), "1 file reformatted, 2 files left unchanged."
1138 self.assertEqual(report.return_code, 0)
1140 self.assertEqual(report.return_code, 1)
1141 report.check = False
1142 report.failed(Path("e1"), "boom")
1143 self.assertEqual(len(out_lines), 1)
1144 self.assertEqual(len(err_lines), 1)
1145 self.assertEqual(err_lines[-1], "error: cannot format e1: boom")
1147 unstyle(str(report)),
1148 "1 file reformatted, 2 files left unchanged, 1 file failed to"
1151 self.assertEqual(report.return_code, 123)
1152 report.done(Path("f3"), black.Changed.YES)
1153 self.assertEqual(len(out_lines), 2)
1154 self.assertEqual(len(err_lines), 1)
1155 self.assertEqual(out_lines[-1], "reformatted f3")
1157 unstyle(str(report)),
1158 "2 files reformatted, 2 files left unchanged, 1 file failed to"
1161 self.assertEqual(report.return_code, 123)
1162 report.failed(Path("e2"), "boom")
1163 self.assertEqual(len(out_lines), 2)
1164 self.assertEqual(len(err_lines), 2)
1165 self.assertEqual(err_lines[-1], "error: cannot format e2: boom")
1167 unstyle(str(report)),
1168 "2 files reformatted, 2 files left unchanged, 2 files failed to"
1171 self.assertEqual(report.return_code, 123)
1172 report.path_ignored(Path("wat"), "no match")
1173 self.assertEqual(len(out_lines), 2)
1174 self.assertEqual(len(err_lines), 2)
1176 unstyle(str(report)),
1177 "2 files reformatted, 2 files left unchanged, 2 files failed to"
1180 self.assertEqual(report.return_code, 123)
1181 report.done(Path("f4"), black.Changed.NO)
1182 self.assertEqual(len(out_lines), 2)
1183 self.assertEqual(len(err_lines), 2)
1185 unstyle(str(report)),
1186 "2 files reformatted, 3 files left unchanged, 2 files failed to"
1189 self.assertEqual(report.return_code, 123)
1192 unstyle(str(report)),
1193 "2 files would be reformatted, 3 files would be left unchanged, 2 files"
1194 " would fail to reformat.",
1196 report.check = False
1199 unstyle(str(report)),
1200 "2 files would be reformatted, 3 files would be left unchanged, 2 files"
1201 " would fail to reformat.",
1204 def test_lib2to3_parse(self) -> None:
1205 with self.assertRaises(black.InvalidInput):
1206 black.lib2to3_parse("invalid syntax")
1208 straddling = "x + y"
1209 black.lib2to3_parse(straddling)
1210 black.lib2to3_parse(straddling, {TargetVersion.PY27})
1211 black.lib2to3_parse(straddling, {TargetVersion.PY36})
1212 black.lib2to3_parse(straddling, {TargetVersion.PY27, TargetVersion.PY36})
1214 py2_only = "print x"
1215 black.lib2to3_parse(py2_only)
1216 black.lib2to3_parse(py2_only, {TargetVersion.PY27})
1217 with self.assertRaises(black.InvalidInput):
1218 black.lib2to3_parse(py2_only, {TargetVersion.PY36})
1219 with self.assertRaises(black.InvalidInput):
1220 black.lib2to3_parse(py2_only, {TargetVersion.PY27, TargetVersion.PY36})
1222 py3_only = "exec(x, end=y)"
1223 black.lib2to3_parse(py3_only)
1224 with self.assertRaises(black.InvalidInput):
1225 black.lib2to3_parse(py3_only, {TargetVersion.PY27})
1226 black.lib2to3_parse(py3_only, {TargetVersion.PY36})
1227 black.lib2to3_parse(py3_only, {TargetVersion.PY27, TargetVersion.PY36})
1229 def test_get_features_used_decorator(self) -> None:
1230 # Test the feature detection of new decorator syntax
1231 # since this makes some test cases of test_get_features_used()
1232 # fails if it fails, this is tested first so that a useful case
1234 simples, relaxed = read_data("decorators")
1235 # skip explanation comments at the top of the file
1236 for simple_test in simples.split("##")[1:]:
1237 node = black.lib2to3_parse(simple_test)
1238 decorator = str(node.children[0].children[0]).strip()
1240 Feature.RELAXED_DECORATORS,
1241 black.get_features_used(node),
1243 f"decorator '{decorator}' follows python<=3.8 syntax"
1244 "but is detected as 3.9+"
1245 # f"The full node is\n{node!r}"
1248 # skip the '# output' comment at the top of the output part
1249 for relaxed_test in relaxed.split("##")[1:]:
1250 node = black.lib2to3_parse(relaxed_test)
1251 decorator = str(node.children[0].children[0]).strip()
1253 Feature.RELAXED_DECORATORS,
1254 black.get_features_used(node),
1256 f"decorator '{decorator}' uses python3.9+ syntax"
1257 "but is detected as python<=3.8"
1258 # f"The full node is\n{node!r}"
1262 def test_get_features_used(self) -> None:
1263 node = black.lib2to3_parse("def f(*, arg): ...\n")
1264 self.assertEqual(black.get_features_used(node), set())
1265 node = black.lib2to3_parse("def f(*, arg,): ...\n")
1266 self.assertEqual(black.get_features_used(node), {Feature.TRAILING_COMMA_IN_DEF})
1267 node = black.lib2to3_parse("f(*arg,)\n")
1269 black.get_features_used(node), {Feature.TRAILING_COMMA_IN_CALL}
1271 node = black.lib2to3_parse("def f(*, arg): f'string'\n")
1272 self.assertEqual(black.get_features_used(node), {Feature.F_STRINGS})
1273 node = black.lib2to3_parse("123_456\n")
1274 self.assertEqual(black.get_features_used(node), {Feature.NUMERIC_UNDERSCORES})
1275 node = black.lib2to3_parse("123456\n")
1276 self.assertEqual(black.get_features_used(node), set())
1277 source, expected = read_data("function")
1278 node = black.lib2to3_parse(source)
1279 expected_features = {
1280 Feature.TRAILING_COMMA_IN_CALL,
1281 Feature.TRAILING_COMMA_IN_DEF,
1284 self.assertEqual(black.get_features_used(node), expected_features)
1285 node = black.lib2to3_parse(expected)
1286 self.assertEqual(black.get_features_used(node), expected_features)
1287 source, expected = read_data("expression")
1288 node = black.lib2to3_parse(source)
1289 self.assertEqual(black.get_features_used(node), set())
1290 node = black.lib2to3_parse(expected)
1291 self.assertEqual(black.get_features_used(node), set())
1293 def test_get_future_imports(self) -> None:
1294 node = black.lib2to3_parse("\n")
1295 self.assertEqual(set(), black.get_future_imports(node))
1296 node = black.lib2to3_parse("from __future__ import black\n")
1297 self.assertEqual({"black"}, black.get_future_imports(node))
1298 node = black.lib2to3_parse("from __future__ import multiple, imports\n")
1299 self.assertEqual({"multiple", "imports"}, black.get_future_imports(node))
1300 node = black.lib2to3_parse("from __future__ import (parenthesized, imports)\n")
1301 self.assertEqual({"parenthesized", "imports"}, black.get_future_imports(node))
1302 node = black.lib2to3_parse(
1303 "from __future__ import multiple\nfrom __future__ import imports\n"
1305 self.assertEqual({"multiple", "imports"}, black.get_future_imports(node))
1306 node = black.lib2to3_parse("# comment\nfrom __future__ import black\n")
1307 self.assertEqual({"black"}, black.get_future_imports(node))
1308 node = black.lib2to3_parse('"""docstring"""\nfrom __future__ import black\n')
1309 self.assertEqual({"black"}, black.get_future_imports(node))
1310 node = black.lib2to3_parse("some(other, code)\nfrom __future__ import black\n")
1311 self.assertEqual(set(), black.get_future_imports(node))
1312 node = black.lib2to3_parse("from some.module import black\n")
1313 self.assertEqual(set(), black.get_future_imports(node))
1314 node = black.lib2to3_parse(
1315 "from __future__ import unicode_literals as _unicode_literals"
1317 self.assertEqual({"unicode_literals"}, black.get_future_imports(node))
1318 node = black.lib2to3_parse(
1319 "from __future__ import unicode_literals as _lol, print"
1321 self.assertEqual({"unicode_literals", "print"}, black.get_future_imports(node))
1323 def test_debug_visitor(self) -> None:
1324 source, _ = read_data("debug_visitor.py")
1325 expected, _ = read_data("debug_visitor.out")
1329 def out(msg: str, **kwargs: Any) -> None:
1330 out_lines.append(msg)
1332 def err(msg: str, **kwargs: Any) -> None:
1333 err_lines.append(msg)
1335 with patch("black.out", out), patch("black.err", err):
1336 black.DebugVisitor.show(source)
1337 actual = "\n".join(out_lines) + "\n"
1339 if expected != actual:
1340 log_name = black.dump_to_file(*out_lines)
1344 f"AST print out is different. Actual version dumped to {log_name}",
1347 def test_format_file_contents(self) -> None:
1350 with self.assertRaises(black.NothingChanged):
1351 black.format_file_contents(empty, mode=mode, fast=False)
1353 with self.assertRaises(black.NothingChanged):
1354 black.format_file_contents(just_nl, mode=mode, fast=False)
1355 same = "j = [1, 2, 3]\n"
1356 with self.assertRaises(black.NothingChanged):
1357 black.format_file_contents(same, mode=mode, fast=False)
1358 different = "j = [1,2,3]"
1360 actual = black.format_file_contents(different, mode=mode, fast=False)
1361 self.assertEqual(expected, actual)
1362 invalid = "return if you can"
1363 with self.assertRaises(black.InvalidInput) as e:
1364 black.format_file_contents(invalid, mode=mode, fast=False)
1365 self.assertEqual(str(e.exception), "Cannot parse: 1:7: return if you can")
1367 def test_endmarker(self) -> None:
1368 n = black.lib2to3_parse("\n")
1369 self.assertEqual(n.type, black.syms.file_input)
1370 self.assertEqual(len(n.children), 1)
1371 self.assertEqual(n.children[0].type, black.token.ENDMARKER)
1373 @unittest.skipIf(os.environ.get("SKIP_AST_PRINT"), "user set SKIP_AST_PRINT")
1374 def test_assertFormatEqual(self) -> None:
1378 def out(msg: str, **kwargs: Any) -> None:
1379 out_lines.append(msg)
1381 def err(msg: str, **kwargs: Any) -> None:
1382 err_lines.append(msg)
1384 with patch("black.out", out), patch("black.err", err):
1385 with self.assertRaises(AssertionError):
1386 self.assertFormatEqual("j = [1, 2, 3]", "j = [1, 2, 3,]")
1388 out_str = "".join(out_lines)
1389 self.assertTrue("Expected tree:" in out_str)
1390 self.assertTrue("Actual tree:" in out_str)
1391 self.assertEqual("".join(err_lines), "")
1393 def test_cache_broken_file(self) -> None:
1395 with cache_dir() as workspace:
1396 cache_file = black.get_cache_file(mode)
1397 with cache_file.open("w") as fobj:
1398 fobj.write("this is not a pickle")
1399 self.assertEqual(black.read_cache(mode), {})
1400 src = (workspace / "test.py").resolve()
1401 with src.open("w") as fobj:
1402 fobj.write("print('hello')")
1403 self.invokeBlack([str(src)])
1404 cache = black.read_cache(mode)
1405 self.assertIn(src, cache)
1407 def test_cache_single_file_already_cached(self) -> None:
1409 with cache_dir() as workspace:
1410 src = (workspace / "test.py").resolve()
1411 with src.open("w") as fobj:
1412 fobj.write("print('hello')")
1413 black.write_cache({}, [src], mode)
1414 self.invokeBlack([str(src)])
1415 with src.open("r") as fobj:
1416 self.assertEqual(fobj.read(), "print('hello')")
1419 def test_cache_multiple_files(self) -> None:
1421 with cache_dir() as workspace, patch(
1422 "black.ProcessPoolExecutor", new=ThreadPoolExecutor
1424 one = (workspace / "one.py").resolve()
1425 with one.open("w") as fobj:
1426 fobj.write("print('hello')")
1427 two = (workspace / "two.py").resolve()
1428 with two.open("w") as fobj:
1429 fobj.write("print('hello')")
1430 black.write_cache({}, [one], mode)
1431 self.invokeBlack([str(workspace)])
1432 with one.open("r") as fobj:
1433 self.assertEqual(fobj.read(), "print('hello')")
1434 with two.open("r") as fobj:
1435 self.assertEqual(fobj.read(), 'print("hello")\n')
1436 cache = black.read_cache(mode)
1437 self.assertIn(one, cache)
1438 self.assertIn(two, cache)
1440 def test_no_cache_when_writeback_diff(self) -> None:
1442 with cache_dir() as workspace:
1443 src = (workspace / "test.py").resolve()
1444 with src.open("w") as fobj:
1445 fobj.write("print('hello')")
1446 with patch("black.read_cache") as read_cache, patch(
1449 self.invokeBlack([str(src), "--diff"])
1450 cache_file = black.get_cache_file(mode)
1451 self.assertFalse(cache_file.exists())
1452 write_cache.assert_not_called()
1453 read_cache.assert_not_called()
1455 def test_no_cache_when_writeback_color_diff(self) -> None:
1457 with cache_dir() as workspace:
1458 src = (workspace / "test.py").resolve()
1459 with src.open("w") as fobj:
1460 fobj.write("print('hello')")
1461 with patch("black.read_cache") as read_cache, patch(
1464 self.invokeBlack([str(src), "--diff", "--color"])
1465 cache_file = black.get_cache_file(mode)
1466 self.assertFalse(cache_file.exists())
1467 write_cache.assert_not_called()
1468 read_cache.assert_not_called()
1471 def test_output_locking_when_writeback_diff(self) -> None:
1472 with cache_dir() as workspace:
1473 for tag in range(0, 4):
1474 src = (workspace / f"test{tag}.py").resolve()
1475 with src.open("w") as fobj:
1476 fobj.write("print('hello')")
1477 with patch("black.Manager", wraps=multiprocessing.Manager) as mgr:
1478 self.invokeBlack(["--diff", str(workspace)], exit_code=0)
1479 # this isn't quite doing what we want, but if it _isn't_
1480 # called then we cannot be using the lock it provides
1484 def test_output_locking_when_writeback_color_diff(self) -> None:
1485 with cache_dir() as workspace:
1486 for tag in range(0, 4):
1487 src = (workspace / f"test{tag}.py").resolve()
1488 with src.open("w") as fobj:
1489 fobj.write("print('hello')")
1490 with patch("black.Manager", wraps=multiprocessing.Manager) as mgr:
1491 self.invokeBlack(["--diff", "--color", str(workspace)], exit_code=0)
1492 # this isn't quite doing what we want, but if it _isn't_
1493 # called then we cannot be using the lock it provides
1496 def test_no_cache_when_stdin(self) -> None:
1499 result = CliRunner().invoke(
1500 black.main, ["-"], input=BytesIO(b"print('hello')")
1502 self.assertEqual(result.exit_code, 0)
1503 cache_file = black.get_cache_file(mode)
1504 self.assertFalse(cache_file.exists())
1506 def test_read_cache_no_cachefile(self) -> None:
1509 self.assertEqual(black.read_cache(mode), {})
1511 def test_write_cache_read_cache(self) -> None:
1513 with cache_dir() as workspace:
1514 src = (workspace / "test.py").resolve()
1516 black.write_cache({}, [src], mode)
1517 cache = black.read_cache(mode)
1518 self.assertIn(src, cache)
1519 self.assertEqual(cache[src], black.get_cache_info(src))
1521 def test_filter_cached(self) -> None:
1522 with TemporaryDirectory() as workspace:
1523 path = Path(workspace)
1524 uncached = (path / "uncached").resolve()
1525 cached = (path / "cached").resolve()
1526 cached_but_changed = (path / "changed").resolve()
1529 cached_but_changed.touch()
1530 cache = {cached: black.get_cache_info(cached), cached_but_changed: (0.0, 0)}
1531 todo, done = black.filter_cached(
1532 cache, {uncached, cached, cached_but_changed}
1534 self.assertEqual(todo, {uncached, cached_but_changed})
1535 self.assertEqual(done, {cached})
1537 def test_write_cache_creates_directory_if_needed(self) -> None:
1539 with cache_dir(exists=False) as workspace:
1540 self.assertFalse(workspace.exists())
1541 black.write_cache({}, [], mode)
1542 self.assertTrue(workspace.exists())
1545 def test_failed_formatting_does_not_get_cached(self) -> None:
1547 with cache_dir() as workspace, patch(
1548 "black.ProcessPoolExecutor", new=ThreadPoolExecutor
1550 failing = (workspace / "failing.py").resolve()
1551 with failing.open("w") as fobj:
1552 fobj.write("not actually python")
1553 clean = (workspace / "clean.py").resolve()
1554 with clean.open("w") as fobj:
1555 fobj.write('print("hello")\n')
1556 self.invokeBlack([str(workspace)], exit_code=123)
1557 cache = black.read_cache(mode)
1558 self.assertNotIn(failing, cache)
1559 self.assertIn(clean, cache)
1561 def test_write_cache_write_fail(self) -> None:
1563 with cache_dir(), patch.object(Path, "open") as mock:
1564 mock.side_effect = OSError
1565 black.write_cache({}, [], mode)
1568 @patch("black.ProcessPoolExecutor", MagicMock(side_effect=OSError))
1569 def test_works_in_mono_process_only_environment(self) -> None:
1570 with cache_dir() as workspace:
1572 (workspace / "one.py").resolve(),
1573 (workspace / "two.py").resolve(),
1575 f.write_text('print("hello")\n')
1576 self.invokeBlack([str(workspace)])
1579 def test_check_diff_use_together(self) -> None:
1581 # Files which will be reformatted.
1582 src1 = (THIS_DIR / "data" / "string_quotes.py").resolve()
1583 self.invokeBlack([str(src1), "--diff", "--check"], exit_code=1)
1584 # Files which will not be reformatted.
1585 src2 = (THIS_DIR / "data" / "composition.py").resolve()
1586 self.invokeBlack([str(src2), "--diff", "--check"])
1587 # Multi file command.
1588 self.invokeBlack([str(src1), str(src2), "--diff", "--check"], exit_code=1)
1590 def test_no_files(self) -> None:
1592 # Without an argument, black exits with error code 0.
1593 self.invokeBlack([])
1595 def test_broken_symlink(self) -> None:
1596 with cache_dir() as workspace:
1597 symlink = workspace / "broken_link.py"
1599 symlink.symlink_to("nonexistent.py")
1600 except OSError as e:
1601 self.skipTest(f"Can't create symlinks: {e}")
1602 self.invokeBlack([str(workspace.resolve())])
1604 def test_read_cache_line_lengths(self) -> None:
1606 short_mode = replace(DEFAULT_MODE, line_length=1)
1607 with cache_dir() as workspace:
1608 path = (workspace / "file.py").resolve()
1610 black.write_cache({}, [path], mode)
1611 one = black.read_cache(mode)
1612 self.assertIn(path, one)
1613 two = black.read_cache(short_mode)
1614 self.assertNotIn(path, two)
1616 def test_tricky_unicode_symbols(self) -> None:
1617 source, expected = read_data("tricky_unicode_symbols")
1619 self.assertFormatEqual(expected, actual)
1620 black.assert_equivalent(source, actual)
1621 black.assert_stable(source, actual, DEFAULT_MODE)
1623 def test_single_file_force_pyi(self) -> None:
1624 pyi_mode = replace(DEFAULT_MODE, is_pyi=True)
1625 contents, expected = read_data("force_pyi")
1626 with cache_dir() as workspace:
1627 path = (workspace / "file.py").resolve()
1628 with open(path, "w") as fh:
1630 self.invokeBlack([str(path), "--pyi"])
1631 with open(path, "r") as fh:
1633 # verify cache with --pyi is separate
1634 pyi_cache = black.read_cache(pyi_mode)
1635 self.assertIn(path, pyi_cache)
1636 normal_cache = black.read_cache(DEFAULT_MODE)
1637 self.assertNotIn(path, normal_cache)
1638 self.assertFormatEqual(expected, actual)
1639 black.assert_equivalent(contents, actual)
1640 black.assert_stable(contents, actual, pyi_mode)
1643 def test_multi_file_force_pyi(self) -> None:
1644 reg_mode = DEFAULT_MODE
1645 pyi_mode = replace(DEFAULT_MODE, is_pyi=True)
1646 contents, expected = read_data("force_pyi")
1647 with cache_dir() as workspace:
1649 (workspace / "file1.py").resolve(),
1650 (workspace / "file2.py").resolve(),
1653 with open(path, "w") as fh:
1655 self.invokeBlack([str(p) for p in paths] + ["--pyi"])
1657 with open(path, "r") as fh:
1659 self.assertEqual(actual, expected)
1660 # verify cache with --pyi is separate
1661 pyi_cache = black.read_cache(pyi_mode)
1662 normal_cache = black.read_cache(reg_mode)
1664 self.assertIn(path, pyi_cache)
1665 self.assertNotIn(path, normal_cache)
1667 def test_pipe_force_pyi(self) -> None:
1668 source, expected = read_data("force_pyi")
1669 result = CliRunner().invoke(
1670 black.main, ["-", "-q", "--pyi"], input=BytesIO(source.encode("utf8"))
1672 self.assertEqual(result.exit_code, 0)
1673 actual = result.output
1674 self.assertFormatEqual(actual, expected)
1676 def test_single_file_force_py36(self) -> None:
1677 reg_mode = DEFAULT_MODE
1678 py36_mode = replace(DEFAULT_MODE, target_versions=PY36_VERSIONS)
1679 source, expected = read_data("force_py36")
1680 with cache_dir() as workspace:
1681 path = (workspace / "file.py").resolve()
1682 with open(path, "w") as fh:
1684 self.invokeBlack([str(path), *PY36_ARGS])
1685 with open(path, "r") as fh:
1687 # verify cache with --target-version is separate
1688 py36_cache = black.read_cache(py36_mode)
1689 self.assertIn(path, py36_cache)
1690 normal_cache = black.read_cache(reg_mode)
1691 self.assertNotIn(path, normal_cache)
1692 self.assertEqual(actual, expected)
1695 def test_multi_file_force_py36(self) -> None:
1696 reg_mode = DEFAULT_MODE
1697 py36_mode = replace(DEFAULT_MODE, target_versions=PY36_VERSIONS)
1698 source, expected = read_data("force_py36")
1699 with cache_dir() as workspace:
1701 (workspace / "file1.py").resolve(),
1702 (workspace / "file2.py").resolve(),
1705 with open(path, "w") as fh:
1707 self.invokeBlack([str(p) for p in paths] + PY36_ARGS)
1709 with open(path, "r") as fh:
1711 self.assertEqual(actual, expected)
1712 # verify cache with --target-version is separate
1713 pyi_cache = black.read_cache(py36_mode)
1714 normal_cache = black.read_cache(reg_mode)
1716 self.assertIn(path, pyi_cache)
1717 self.assertNotIn(path, normal_cache)
1719 def test_collections(self) -> None:
1720 source, expected = read_data("collections")
1722 self.assertFormatEqual(expected, actual)
1723 black.assert_equivalent(source, actual)
1724 black.assert_stable(source, actual, DEFAULT_MODE)
1726 def test_pipe_force_py36(self) -> None:
1727 source, expected = read_data("force_py36")
1728 result = CliRunner().invoke(
1730 ["-", "-q", "--target-version=py36"],
1731 input=BytesIO(source.encode("utf8")),
1733 self.assertEqual(result.exit_code, 0)
1734 actual = result.output
1735 self.assertFormatEqual(actual, expected)
1737 def test_include_exclude(self) -> None:
1738 path = THIS_DIR / "data" / "include_exclude_tests"
1739 include = re.compile(r"\.pyi?$")
1740 exclude = re.compile(r"/exclude/|/\.definitely_exclude/")
1741 report = black.Report()
1742 gitignore = PathSpec.from_lines("gitwildmatch", [])
1743 sources: List[Path] = []
1745 Path(path / "b/dont_exclude/a.py"),
1746 Path(path / "b/dont_exclude/a.pyi"),
1748 this_abs = THIS_DIR.resolve()
1750 black.gen_python_files(
1751 path.iterdir(), this_abs, include, exclude, None, report, gitignore
1754 self.assertEqual(sorted(expected), sorted(sources))
1756 @patch("black.find_project_root", lambda *args: THIS_DIR.resolve())
1757 def test_exclude_for_issue_1572(self) -> None:
1758 # Exclude shouldn't touch files that were explicitly given to Black through the
1759 # CLI. Exclude is supposed to only apply to the recursive discovery of files.
1760 # https://github.com/psf/black/issues/1572
1761 path = THIS_DIR / "data" / "include_exclude_tests"
1763 exclude = r"/exclude/|a\.py"
1764 src = str(path / "b/exclude/a.py")
1765 report = black.Report()
1766 expected = [Path(path / "b/exclude/a.py")]
1779 self.assertEqual(sorted(expected), sorted(sources))
1781 def test_gitignore_exclude(self) -> None:
1782 path = THIS_DIR / "data" / "include_exclude_tests"
1783 include = re.compile(r"\.pyi?$")
1784 exclude = re.compile(r"")
1785 report = black.Report()
1786 gitignore = PathSpec.from_lines(
1787 "gitwildmatch", ["exclude/", ".definitely_exclude"]
1789 sources: List[Path] = []
1791 Path(path / "b/dont_exclude/a.py"),
1792 Path(path / "b/dont_exclude/a.pyi"),
1794 this_abs = THIS_DIR.resolve()
1796 black.gen_python_files(
1797 path.iterdir(), this_abs, include, exclude, None, report, gitignore
1800 self.assertEqual(sorted(expected), sorted(sources))
1802 def test_empty_include(self) -> None:
1803 path = THIS_DIR / "data" / "include_exclude_tests"
1804 report = black.Report()
1805 gitignore = PathSpec.from_lines("gitwildmatch", [])
1806 empty = re.compile(r"")
1807 sources: List[Path] = []
1809 Path(path / "b/exclude/a.pie"),
1810 Path(path / "b/exclude/a.py"),
1811 Path(path / "b/exclude/a.pyi"),
1812 Path(path / "b/dont_exclude/a.pie"),
1813 Path(path / "b/dont_exclude/a.py"),
1814 Path(path / "b/dont_exclude/a.pyi"),
1815 Path(path / "b/.definitely_exclude/a.pie"),
1816 Path(path / "b/.definitely_exclude/a.py"),
1817 Path(path / "b/.definitely_exclude/a.pyi"),
1819 this_abs = THIS_DIR.resolve()
1821 black.gen_python_files(
1825 re.compile(black.DEFAULT_EXCLUDES),
1831 self.assertEqual(sorted(expected), sorted(sources))
1833 def test_empty_exclude(self) -> None:
1834 path = THIS_DIR / "data" / "include_exclude_tests"
1835 report = black.Report()
1836 gitignore = PathSpec.from_lines("gitwildmatch", [])
1837 empty = re.compile(r"")
1838 sources: List[Path] = []
1840 Path(path / "b/dont_exclude/a.py"),
1841 Path(path / "b/dont_exclude/a.pyi"),
1842 Path(path / "b/exclude/a.py"),
1843 Path(path / "b/exclude/a.pyi"),
1844 Path(path / "b/.definitely_exclude/a.py"),
1845 Path(path / "b/.definitely_exclude/a.pyi"),
1847 this_abs = THIS_DIR.resolve()
1849 black.gen_python_files(
1852 re.compile(black.DEFAULT_INCLUDES),
1859 self.assertEqual(sorted(expected), sorted(sources))
1861 def test_invalid_include_exclude(self) -> None:
1862 for option in ["--include", "--exclude"]:
1863 self.invokeBlack(["-", option, "**()(!!*)"], exit_code=2)
1865 def test_preserves_line_endings(self) -> None:
1866 with TemporaryDirectory() as workspace:
1867 test_file = Path(workspace) / "test.py"
1868 for nl in ["\n", "\r\n"]:
1869 contents = nl.join(["def f( ):", " pass"])
1870 test_file.write_bytes(contents.encode())
1871 ff(test_file, write_back=black.WriteBack.YES)
1872 updated_contents: bytes = test_file.read_bytes()
1873 self.assertIn(nl.encode(), updated_contents)
1875 self.assertNotIn(b"\r\n", updated_contents)
1877 def test_preserves_line_endings_via_stdin(self) -> None:
1878 for nl in ["\n", "\r\n"]:
1879 contents = nl.join(["def f( ):", " pass"])
1880 runner = BlackRunner()
1881 result = runner.invoke(
1882 black.main, ["-", "--fast"], input=BytesIO(contents.encode("utf8"))
1884 self.assertEqual(result.exit_code, 0)
1885 output = runner.stdout_bytes
1886 self.assertIn(nl.encode("utf8"), output)
1888 self.assertNotIn(b"\r\n", output)
1890 def test_assert_equivalent_different_asts(self) -> None:
1891 with self.assertRaises(AssertionError):
1892 black.assert_equivalent("{}", "None")
1894 def test_symlink_out_of_root_directory(self) -> None:
1896 root = THIS_DIR.resolve()
1898 include = re.compile(black.DEFAULT_INCLUDES)
1899 exclude = re.compile(black.DEFAULT_EXCLUDES)
1900 report = black.Report()
1901 gitignore = PathSpec.from_lines("gitwildmatch", [])
1902 # `child` should behave like a symlink which resolved path is clearly
1903 # outside of the `root` directory.
1904 path.iterdir.return_value = [child]
1905 child.resolve.return_value = Path("/a/b/c")
1906 child.as_posix.return_value = "/a/b/c"
1907 child.is_symlink.return_value = True
1910 black.gen_python_files(
1911 path.iterdir(), root, include, exclude, None, report, gitignore
1914 except ValueError as ve:
1915 self.fail(f"`get_python_files_in_dir()` failed: {ve}")
1916 path.iterdir.assert_called_once()
1917 child.resolve.assert_called_once()
1918 child.is_symlink.assert_called_once()
1919 # `child` should behave like a strange file which resolved path is clearly
1920 # outside of the `root` directory.
1921 child.is_symlink.return_value = False
1922 with self.assertRaises(ValueError):
1924 black.gen_python_files(
1925 path.iterdir(), root, include, exclude, None, report, gitignore
1928 path.iterdir.assert_called()
1929 self.assertEqual(path.iterdir.call_count, 2)
1930 child.resolve.assert_called()
1931 self.assertEqual(child.resolve.call_count, 2)
1932 child.is_symlink.assert_called()
1933 self.assertEqual(child.is_symlink.call_count, 2)
1935 def test_shhh_click(self) -> None:
1937 from click import _unicodefun # type: ignore
1938 except ModuleNotFoundError:
1939 self.skipTest("Incompatible Click version")
1940 if not hasattr(_unicodefun, "_verify_python3_env"):
1941 self.skipTest("Incompatible Click version")
1942 # First, let's see if Click is crashing with a preferred ASCII charset.
1943 with patch("locale.getpreferredencoding") as gpe:
1944 gpe.return_value = "ASCII"
1945 with self.assertRaises(RuntimeError):
1946 _unicodefun._verify_python3_env()
1947 # Now, let's silence Click...
1949 # ...and confirm it's silent.
1950 with patch("locale.getpreferredencoding") as gpe:
1951 gpe.return_value = "ASCII"
1953 _unicodefun._verify_python3_env()
1954 except RuntimeError as re:
1955 self.fail(f"`patch_click()` failed, exception still raised: {re}")
1957 def test_root_logger_not_used_directly(self) -> None:
1958 def fail(*args: Any, **kwargs: Any) -> None:
1959 self.fail("Record created with root logger")
1961 with patch.multiple(
1972 @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
1973 def test_blackd_main(self) -> None:
1974 with patch("blackd.web.run_app"):
1975 result = CliRunner().invoke(blackd.main, [])
1976 if result.exception is not None:
1977 raise result.exception
1978 self.assertEqual(result.exit_code, 0)
1980 def test_invalid_config_return_code(self) -> None:
1981 tmp_file = Path(black.dump_to_file())
1983 tmp_config = Path(black.dump_to_file())
1985 args = ["--config", str(tmp_config), str(tmp_file)]
1986 self.invokeBlack(args, exit_code=2, ignore_config=False)
1990 def test_parse_pyproject_toml(self) -> None:
1991 test_toml_file = THIS_DIR / "test.toml"
1992 config = black.parse_pyproject_toml(str(test_toml_file))
1993 self.assertEqual(config["verbose"], 1)
1994 self.assertEqual(config["check"], "no")
1995 self.assertEqual(config["diff"], "y")
1996 self.assertEqual(config["color"], True)
1997 self.assertEqual(config["line_length"], 79)
1998 self.assertEqual(config["target_version"], ["py36", "py37", "py38"])
1999 self.assertEqual(config["exclude"], r"\.pyi?$")
2000 self.assertEqual(config["include"], r"\.py?$")
2002 def test_read_pyproject_toml(self) -> None:
2003 test_toml_file = THIS_DIR / "test.toml"
2004 fake_ctx = FakeContext()
2005 black.read_pyproject_toml(fake_ctx, FakeParameter(), str(test_toml_file))
2006 config = fake_ctx.default_map
2007 self.assertEqual(config["verbose"], "1")
2008 self.assertEqual(config["check"], "no")
2009 self.assertEqual(config["diff"], "y")
2010 self.assertEqual(config["color"], "True")
2011 self.assertEqual(config["line_length"], "79")
2012 self.assertEqual(config["target_version"], ["py36", "py37", "py38"])
2013 self.assertEqual(config["exclude"], r"\.pyi?$")
2014 self.assertEqual(config["include"], r"\.py?$")
2016 def test_find_project_root(self) -> None:
2017 with TemporaryDirectory() as workspace:
2018 root = Path(workspace)
2019 test_dir = root / "test"
2022 src_dir = root / "src"
2025 root_pyproject = root / "pyproject.toml"
2026 root_pyproject.touch()
2027 src_pyproject = src_dir / "pyproject.toml"
2028 src_pyproject.touch()
2029 src_python = src_dir / "foo.py"
2033 black.find_project_root((src_dir, test_dir)), root.resolve()
2035 self.assertEqual(black.find_project_root((src_dir,)), src_dir.resolve())
2036 self.assertEqual(black.find_project_root((src_python,)), src_dir.resolve())
2038 def test_bpo_33660_workaround(self) -> None:
2039 if system() == "Windows":
2042 # https://bugs.python.org/issue33660
2044 old_cwd = Path.cwd()
2048 path = Path("workspace") / "project"
2049 report = black.Report(verbose=True)
2050 normalized_path = black.normalize_path_maybe_ignore(path, root, report)
2051 self.assertEqual(normalized_path, "workspace/project")
2053 os.chdir(str(old_cwd))
2056 class BlackDTestCase(AioHTTPTestCase):
2057 async def get_application(self) -> web.Application:
2058 return blackd.make_app()
2060 # TODO: remove these decorators once the below is released
2061 # https://github.com/aio-libs/aiohttp/pull/3727
2062 @skip_if_exception("ClientOSError")
2063 @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
2065 async def test_blackd_request_needs_formatting(self) -> None:
2066 response = await self.client.post("/", data=b"print('hello world')")
2067 self.assertEqual(response.status, 200)
2068 self.assertEqual(response.charset, "utf8")
2069 self.assertEqual(await response.read(), b'print("hello world")\n')
2071 @skip_if_exception("ClientOSError")
2072 @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
2074 async def test_blackd_request_no_change(self) -> None:
2075 response = await self.client.post("/", data=b'print("hello world")\n')
2076 self.assertEqual(response.status, 204)
2077 self.assertEqual(await response.read(), b"")
2079 @skip_if_exception("ClientOSError")
2080 @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
2082 async def test_blackd_request_syntax_error(self) -> None:
2083 response = await self.client.post("/", data=b"what even ( is")
2084 self.assertEqual(response.status, 400)
2085 content = await response.text()
2087 content.startswith("Cannot parse"),
2088 msg=f"Expected error to start with 'Cannot parse', got {repr(content)}",
2091 @skip_if_exception("ClientOSError")
2092 @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
2094 async def test_blackd_unsupported_version(self) -> None:
2095 response = await self.client.post(
2096 "/", data=b"what", headers={blackd.PROTOCOL_VERSION_HEADER: "2"}
2098 self.assertEqual(response.status, 501)
2100 @skip_if_exception("ClientOSError")
2101 @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
2103 async def test_blackd_supported_version(self) -> None:
2104 response = await self.client.post(
2105 "/", data=b"what", headers={blackd.PROTOCOL_VERSION_HEADER: "1"}
2107 self.assertEqual(response.status, 200)
2109 @skip_if_exception("ClientOSError")
2110 @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
2112 async def test_blackd_invalid_python_variant(self) -> None:
2113 async def check(header_value: str, expected_status: int = 400) -> None:
2114 response = await self.client.post(
2115 "/", data=b"what", headers={blackd.PYTHON_VARIANT_HEADER: header_value}
2117 self.assertEqual(response.status, expected_status)
2120 await check("ruby3.5")
2121 await check("pyi3.6")
2122 await check("py1.5")
2124 await check("py2.8")
2126 await check("pypy3.0")
2127 await check("jython3.4")
2129 @skip_if_exception("ClientOSError")
2130 @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
2132 async def test_blackd_pyi(self) -> None:
2133 source, expected = read_data("stub.pyi")
2134 response = await self.client.post(
2135 "/", data=source, headers={blackd.PYTHON_VARIANT_HEADER: "pyi"}
2137 self.assertEqual(response.status, 200)
2138 self.assertEqual(await response.text(), expected)
2140 @skip_if_exception("ClientOSError")
2141 @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
2143 async def test_blackd_diff(self) -> None:
2144 diff_header = re.compile(
2145 r"(In|Out)\t\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d\.\d\d\d\d\d\d \+\d\d\d\d"
2148 source, _ = read_data("blackd_diff.py")
2149 expected, _ = read_data("blackd_diff.diff")
2151 response = await self.client.post(
2152 "/", data=source, headers={blackd.DIFF_HEADER: "true"}
2154 self.assertEqual(response.status, 200)
2156 actual = await response.text()
2157 actual = diff_header.sub(DETERMINISTIC_HEADER, actual)
2158 self.assertEqual(actual, expected)
2160 @skip_if_exception("ClientOSError")
2161 @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
2163 async def test_blackd_python_variant(self) -> None:
2166 " and_has_a_bunch_of,\n"
2167 " very_long_arguments_too,\n"
2168 " and_lots_of_them_as_well_lol,\n"
2169 " **and_very_long_keyword_arguments\n"
2174 async def check(header_value: str, expected_status: int) -> None:
2175 response = await self.client.post(
2176 "/", data=code, headers={blackd.PYTHON_VARIANT_HEADER: header_value}
2179 response.status, expected_status, msg=await response.text()
2182 await check("3.6", 200)
2183 await check("py3.6", 200)
2184 await check("3.6,3.7", 200)
2185 await check("3.6,py3.7", 200)
2186 await check("py36,py37", 200)
2187 await check("36", 200)
2188 await check("3.6.4", 200)
2190 await check("2", 204)
2191 await check("2.7", 204)
2192 await check("py2.7", 204)
2193 await check("3.4", 204)
2194 await check("py3.4", 204)
2195 await check("py34,py36", 204)
2196 await check("34", 204)
2198 @skip_if_exception("ClientOSError")
2199 @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
2201 async def test_blackd_line_length(self) -> None:
2202 response = await self.client.post(
2203 "/", data=b'print("hello")\n', headers={blackd.LINE_LENGTH_HEADER: "7"}
2205 self.assertEqual(response.status, 200)
2207 @skip_if_exception("ClientOSError")
2208 @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
2210 async def test_blackd_invalid_line_length(self) -> None:
2211 response = await self.client.post(
2212 "/", data=b'print("hello")\n', headers={blackd.LINE_LENGTH_HEADER: "NaN"}
2214 self.assertEqual(response.status, 400)
2216 @skip_if_exception("ClientOSError")
2217 @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
2219 async def test_blackd_response_black_version_header(self) -> None:
2220 response = await self.client.post("/")
2221 self.assertIsNotNone(response.headers.get(blackd.BLACK_VERSION_HEADER))
2224 with open(black.__file__, "r", encoding="utf-8") as _bf:
2225 black_source_lines = _bf.readlines()
2228 def tracefunc(frame: types.FrameType, event: str, arg: Any) -> Callable:
2229 """Show function calls `from black/__init__.py` as they happen.
2231 Register this with `sys.settrace()` in a test you're debugging.
2236 stack = len(inspect.stack()) - 19
2238 filename = frame.f_code.co_filename
2239 lineno = frame.f_lineno
2240 func_sig_lineno = lineno - 1
2241 funcname = black_source_lines[func_sig_lineno].strip()
2242 while funcname.startswith("@"):
2243 func_sig_lineno += 1
2244 funcname = black_source_lines[func_sig_lineno].strip()
2245 if "black/__init__.py" in filename:
2246 print(f"{' ' * stack}{lineno}:{funcname}")
2250 if __name__ == "__main__":
2251 unittest.main(module="test_black")