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.
3 from concurrent.futures import ThreadPoolExecutor
4 from contextlib import contextmanager
5 from functools import partial
6 from io import StringIO
8 from pathlib import Path
10 from tempfile import TemporaryDirectory
11 from typing import Any, List, Tuple, Iterator
13 from unittest.mock import patch
15 from click import unstyle
16 from click.testing import CliRunner
21 ff = partial(black.format_file_in_place, line_length=ll, fast=True)
22 fs = partial(black.format_str, line_length=ll)
23 THIS_FILE = Path(__file__)
24 THIS_DIR = THIS_FILE.parent
25 EMPTY_LINE = "# EMPTY LINE WITH WHITESPACE" + " (this comment will be removed)"
28 def dump_to_stderr(*output: str) -> str:
29 return "\n" + "\n".join(output) + "\n"
32 def read_data(name: str) -> Tuple[str, str]:
33 """read_data('test_name') -> 'input', 'output'"""
34 if not name.endswith((".py", ".pyi", ".out", ".diff")):
36 _input: List[str] = []
37 _output: List[str] = []
38 with open(THIS_DIR / name, "r", encoding="utf8") as test:
39 lines = test.readlines()
42 line = line.replace(EMPTY_LINE, "")
43 if line.rstrip() == "# output":
48 if _input and not _output:
49 # If there's no output marker, treat the entire file as already pre-formatted.
51 return "".join(_input).strip() + "\n", "".join(_output).strip() + "\n"
55 def cache_dir(exists: bool = True) -> Iterator[Path]:
56 with TemporaryDirectory() as workspace:
57 cache_dir = Path(workspace)
59 cache_dir = cache_dir / "new"
60 with patch("black.CACHE_DIR", cache_dir):
65 def event_loop(close: bool) -> Iterator[None]:
66 policy = asyncio.get_event_loop_policy()
67 old_loop = policy.get_event_loop()
68 loop = policy.new_event_loop()
69 asyncio.set_event_loop(loop)
74 policy.set_event_loop(old_loop)
79 class BlackTestCase(unittest.TestCase):
82 def assertFormatEqual(self, expected: str, actual: str) -> None:
83 if actual != expected and not os.environ.get("SKIP_AST_PRINT"):
84 bdv: black.DebugVisitor[Any]
85 black.out("Expected tree:", fg="green")
87 exp_node = black.lib2to3_parse(expected)
88 bdv = black.DebugVisitor()
89 list(bdv.visit(exp_node))
90 except Exception as ve:
92 black.out("Actual tree:", fg="red")
94 exp_node = black.lib2to3_parse(actual)
95 bdv = black.DebugVisitor()
96 list(bdv.visit(exp_node))
97 except Exception as ve:
99 self.assertEqual(expected, actual)
101 @patch("black.dump_to_file", dump_to_stderr)
102 def test_self(self) -> None:
103 source, expected = read_data("test_black")
105 self.assertFormatEqual(expected, actual)
106 black.assert_equivalent(source, actual)
107 black.assert_stable(source, actual, line_length=ll)
108 self.assertFalse(ff(THIS_FILE))
110 @patch("black.dump_to_file", dump_to_stderr)
111 def test_black(self) -> None:
112 source, expected = read_data("../black")
114 self.assertFormatEqual(expected, actual)
115 black.assert_equivalent(source, actual)
116 black.assert_stable(source, actual, line_length=ll)
117 self.assertFalse(ff(THIS_DIR / ".." / "black.py"))
119 def test_piping(self) -> None:
120 source, expected = read_data("../black")
121 hold_stdin, hold_stdout = sys.stdin, sys.stdout
123 sys.stdin, sys.stdout = StringIO(source), StringIO()
124 sys.stdin.name = "<stdin>"
125 black.format_stdin_to_stdout(
126 line_length=ll, fast=True, write_back=black.WriteBack.YES
129 actual = sys.stdout.read()
131 sys.stdin, sys.stdout = hold_stdin, hold_stdout
132 self.assertFormatEqual(expected, actual)
133 black.assert_equivalent(source, actual)
134 black.assert_stable(source, actual, line_length=ll)
136 def test_piping_diff(self) -> None:
137 source, _ = read_data("expression.py")
138 expected, _ = read_data("expression.diff")
139 hold_stdin, hold_stdout = sys.stdin, sys.stdout
141 sys.stdin, sys.stdout = StringIO(source), StringIO()
142 sys.stdin.name = "<stdin>"
143 black.format_stdin_to_stdout(
144 line_length=ll, fast=True, write_back=black.WriteBack.DIFF
147 actual = sys.stdout.read()
149 sys.stdin, sys.stdout = hold_stdin, hold_stdout
150 actual = actual.rstrip() + "\n" # the diff output has a trailing space
151 self.assertEqual(expected, actual)
153 @patch("black.dump_to_file", dump_to_stderr)
154 def test_setup(self) -> None:
155 source, expected = read_data("../setup")
157 self.assertFormatEqual(expected, actual)
158 black.assert_equivalent(source, actual)
159 black.assert_stable(source, actual, line_length=ll)
160 self.assertFalse(ff(THIS_DIR / ".." / "setup.py"))
162 @patch("black.dump_to_file", dump_to_stderr)
163 def test_function(self) -> None:
164 source, expected = read_data("function")
166 self.assertFormatEqual(expected, actual)
167 black.assert_equivalent(source, actual)
168 black.assert_stable(source, actual, line_length=ll)
170 @patch("black.dump_to_file", dump_to_stderr)
171 def test_function2(self) -> None:
172 source, expected = read_data("function2")
174 self.assertFormatEqual(expected, actual)
175 black.assert_equivalent(source, actual)
176 black.assert_stable(source, actual, line_length=ll)
178 @patch("black.dump_to_file", dump_to_stderr)
179 def test_expression(self) -> None:
180 source, expected = read_data("expression")
182 self.assertFormatEqual(expected, actual)
183 black.assert_equivalent(source, actual)
184 black.assert_stable(source, actual, line_length=ll)
186 def test_expression_ff(self) -> None:
187 source, expected = read_data("expression")
188 tmp_file = Path(black.dump_to_file(source))
190 self.assertTrue(ff(tmp_file, write_back=black.WriteBack.YES))
191 with open(tmp_file, encoding="utf8") as f:
195 self.assertFormatEqual(expected, actual)
196 with patch("black.dump_to_file", dump_to_stderr):
197 black.assert_equivalent(source, actual)
198 black.assert_stable(source, actual, line_length=ll)
200 def test_expression_diff(self) -> None:
201 source, _ = read_data("expression.py")
202 expected, _ = read_data("expression.diff")
203 tmp_file = Path(black.dump_to_file(source))
204 hold_stdout = sys.stdout
206 sys.stdout = StringIO()
207 self.assertTrue(ff(tmp_file, write_back=black.WriteBack.DIFF))
209 actual = sys.stdout.read()
210 actual = actual.replace(str(tmp_file), "<stdin>")
212 sys.stdout = hold_stdout
214 actual = actual.rstrip() + "\n" # the diff output has a trailing space
215 if expected != actual:
216 dump = black.dump_to_file(actual)
218 f"Expected diff isn't equal to the actual. If you made changes "
219 f"to expression.py and this is an anticipated difference, "
220 f"overwrite tests/expression.diff with {dump}"
222 self.assertEqual(expected, actual, msg)
224 @patch("black.dump_to_file", dump_to_stderr)
225 def test_fstring(self) -> None:
226 source, expected = read_data("fstring")
228 self.assertFormatEqual(expected, actual)
229 black.assert_equivalent(source, actual)
230 black.assert_stable(source, actual, line_length=ll)
232 @patch("black.dump_to_file", dump_to_stderr)
233 def test_string_quotes(self) -> None:
234 source, expected = read_data("string_quotes")
236 self.assertFormatEqual(expected, actual)
237 black.assert_equivalent(source, actual)
238 black.assert_stable(source, actual, line_length=ll)
240 @patch("black.dump_to_file", dump_to_stderr)
241 def test_slices(self) -> None:
242 source, expected = read_data("slices")
244 self.assertFormatEqual(expected, actual)
245 black.assert_equivalent(source, actual)
246 black.assert_stable(source, actual, line_length=ll)
248 @patch("black.dump_to_file", dump_to_stderr)
249 def test_comments(self) -> None:
250 source, expected = read_data("comments")
252 self.assertFormatEqual(expected, actual)
253 black.assert_equivalent(source, actual)
254 black.assert_stable(source, actual, line_length=ll)
256 @patch("black.dump_to_file", dump_to_stderr)
257 def test_comments2(self) -> None:
258 source, expected = read_data("comments2")
260 self.assertFormatEqual(expected, actual)
261 black.assert_equivalent(source, actual)
262 black.assert_stable(source, actual, line_length=ll)
264 @patch("black.dump_to_file", dump_to_stderr)
265 def test_comments3(self) -> None:
266 source, expected = read_data("comments3")
268 self.assertFormatEqual(expected, actual)
269 black.assert_equivalent(source, actual)
270 black.assert_stable(source, actual, line_length=ll)
272 @patch("black.dump_to_file", dump_to_stderr)
273 def test_comments4(self) -> None:
274 source, expected = read_data("comments4")
276 self.assertFormatEqual(expected, actual)
277 black.assert_equivalent(source, actual)
278 black.assert_stable(source, actual, line_length=ll)
280 @patch("black.dump_to_file", dump_to_stderr)
281 def test_comments5(self) -> None:
282 source, expected = read_data("comments5")
284 self.assertFormatEqual(expected, actual)
285 black.assert_equivalent(source, actual)
286 black.assert_stable(source, actual, line_length=ll)
288 @patch("black.dump_to_file", dump_to_stderr)
289 def test_cantfit(self) -> None:
290 source, expected = read_data("cantfit")
292 self.assertFormatEqual(expected, actual)
293 black.assert_equivalent(source, actual)
294 black.assert_stable(source, actual, line_length=ll)
296 @patch("black.dump_to_file", dump_to_stderr)
297 def test_import_spacing(self) -> None:
298 source, expected = read_data("import_spacing")
300 self.assertFormatEqual(expected, actual)
301 black.assert_equivalent(source, actual)
302 black.assert_stable(source, actual, line_length=ll)
304 @patch("black.dump_to_file", dump_to_stderr)
305 def test_composition(self) -> None:
306 source, expected = read_data("composition")
308 self.assertFormatEqual(expected, actual)
309 black.assert_equivalent(source, actual)
310 black.assert_stable(source, actual, line_length=ll)
312 @patch("black.dump_to_file", dump_to_stderr)
313 def test_empty_lines(self) -> None:
314 source, expected = read_data("empty_lines")
316 self.assertFormatEqual(expected, actual)
317 black.assert_equivalent(source, actual)
318 black.assert_stable(source, actual, line_length=ll)
320 @patch("black.dump_to_file", dump_to_stderr)
321 def test_string_prefixes(self) -> None:
322 source, expected = read_data("string_prefixes")
324 self.assertFormatEqual(expected, actual)
325 black.assert_equivalent(source, actual)
326 black.assert_stable(source, actual, line_length=ll)
328 @patch("black.dump_to_file", dump_to_stderr)
329 def test_python2(self) -> None:
330 source, expected = read_data("python2")
332 self.assertFormatEqual(expected, actual)
333 # black.assert_equivalent(source, actual)
334 black.assert_stable(source, actual, line_length=ll)
336 @patch("black.dump_to_file", dump_to_stderr)
337 def test_python2_unicode_literals(self) -> None:
338 source, expected = read_data("python2_unicode_literals")
340 self.assertFormatEqual(expected, actual)
341 black.assert_stable(source, actual, line_length=ll)
343 @patch("black.dump_to_file", dump_to_stderr)
344 def test_stub(self) -> None:
345 source, expected = read_data("stub.pyi")
346 actual = fs(source, is_pyi=True)
347 self.assertFormatEqual(expected, actual)
348 black.assert_stable(source, actual, line_length=ll, is_pyi=True)
350 @patch("black.dump_to_file", dump_to_stderr)
351 def test_fmtonoff(self) -> None:
352 source, expected = read_data("fmtonoff")
354 self.assertFormatEqual(expected, actual)
355 black.assert_equivalent(source, actual)
356 black.assert_stable(source, actual, line_length=ll)
358 @patch("black.dump_to_file", dump_to_stderr)
359 def test_remove_empty_parentheses_after_class(self) -> None:
360 source, expected = read_data("class_blank_parentheses")
362 self.assertFormatEqual(expected, actual)
363 black.assert_equivalent(source, actual)
364 black.assert_stable(source, actual, line_length=ll)
366 @patch("black.dump_to_file", dump_to_stderr)
367 def test_new_line_between_class_and_code(self) -> None:
368 source, expected = read_data("class_methods_new_line")
370 self.assertFormatEqual(expected, actual)
371 black.assert_equivalent(source, actual)
372 black.assert_stable(source, actual, line_length=ll)
374 def test_report(self) -> None:
375 report = black.Report()
379 def out(msg: str, **kwargs: Any) -> None:
380 out_lines.append(msg)
382 def err(msg: str, **kwargs: Any) -> None:
383 err_lines.append(msg)
385 with patch("black.out", out), patch("black.err", err):
386 report.done(Path("f1"), black.Changed.NO)
387 self.assertEqual(len(out_lines), 1)
388 self.assertEqual(len(err_lines), 0)
389 self.assertEqual(out_lines[-1], "f1 already well formatted, good job.")
390 self.assertEqual(unstyle(str(report)), "1 file left unchanged.")
391 self.assertEqual(report.return_code, 0)
392 report.done(Path("f2"), black.Changed.YES)
393 self.assertEqual(len(out_lines), 2)
394 self.assertEqual(len(err_lines), 0)
395 self.assertEqual(out_lines[-1], "reformatted f2")
397 unstyle(str(report)), "1 file reformatted, 1 file left unchanged."
399 report.done(Path("f3"), black.Changed.CACHED)
400 self.assertEqual(len(out_lines), 3)
401 self.assertEqual(len(err_lines), 0)
403 out_lines[-1], "f3 wasn't modified on disk since last run."
406 unstyle(str(report)), "1 file reformatted, 2 files left unchanged."
408 self.assertEqual(report.return_code, 0)
410 self.assertEqual(report.return_code, 1)
412 report.failed(Path("e1"), "boom")
413 self.assertEqual(len(out_lines), 3)
414 self.assertEqual(len(err_lines), 1)
415 self.assertEqual(err_lines[-1], "error: cannot format e1: boom")
417 unstyle(str(report)),
418 "1 file reformatted, 2 files left unchanged, "
419 "1 file failed to reformat.",
421 self.assertEqual(report.return_code, 123)
422 report.done(Path("f3"), black.Changed.YES)
423 self.assertEqual(len(out_lines), 4)
424 self.assertEqual(len(err_lines), 1)
425 self.assertEqual(out_lines[-1], "reformatted f3")
427 unstyle(str(report)),
428 "2 files reformatted, 2 files left unchanged, "
429 "1 file failed to reformat.",
431 self.assertEqual(report.return_code, 123)
432 report.failed(Path("e2"), "boom")
433 self.assertEqual(len(out_lines), 4)
434 self.assertEqual(len(err_lines), 2)
435 self.assertEqual(err_lines[-1], "error: cannot format e2: boom")
437 unstyle(str(report)),
438 "2 files reformatted, 2 files left unchanged, "
439 "2 files failed to reformat.",
441 self.assertEqual(report.return_code, 123)
442 report.done(Path("f4"), black.Changed.NO)
443 self.assertEqual(len(out_lines), 5)
444 self.assertEqual(len(err_lines), 2)
445 self.assertEqual(out_lines[-1], "f4 already well formatted, good job.")
447 unstyle(str(report)),
448 "2 files reformatted, 3 files left unchanged, "
449 "2 files failed to reformat.",
451 self.assertEqual(report.return_code, 123)
454 unstyle(str(report)),
455 "2 files would be reformatted, 3 files would be left unchanged, "
456 "2 files would fail to reformat.",
459 def test_is_python36(self) -> None:
460 node = black.lib2to3_parse("def f(*, arg): ...\n")
461 self.assertFalse(black.is_python36(node))
462 node = black.lib2to3_parse("def f(*, arg,): ...\n")
463 self.assertTrue(black.is_python36(node))
464 node = black.lib2to3_parse("def f(*, arg): f'string'\n")
465 self.assertTrue(black.is_python36(node))
466 source, expected = read_data("function")
467 node = black.lib2to3_parse(source)
468 self.assertTrue(black.is_python36(node))
469 node = black.lib2to3_parse(expected)
470 self.assertTrue(black.is_python36(node))
471 source, expected = read_data("expression")
472 node = black.lib2to3_parse(source)
473 self.assertFalse(black.is_python36(node))
474 node = black.lib2to3_parse(expected)
475 self.assertFalse(black.is_python36(node))
477 def test_get_future_imports(self) -> None:
478 node = black.lib2to3_parse("\n")
479 self.assertEqual(set(), black.get_future_imports(node))
480 node = black.lib2to3_parse("from __future__ import black\n")
481 self.assertEqual({"black"}, black.get_future_imports(node))
482 node = black.lib2to3_parse("from __future__ import multiple, imports\n")
483 self.assertEqual({"multiple", "imports"}, black.get_future_imports(node))
484 node = black.lib2to3_parse("from __future__ import (parenthesized, imports)\n")
485 self.assertEqual({"parenthesized", "imports"}, black.get_future_imports(node))
486 node = black.lib2to3_parse(
487 "from __future__ import multiple\nfrom __future__ import imports\n"
489 self.assertEqual({"multiple", "imports"}, black.get_future_imports(node))
490 node = black.lib2to3_parse("# comment\nfrom __future__ import black\n")
491 self.assertEqual({"black"}, black.get_future_imports(node))
492 node = black.lib2to3_parse('"""docstring"""\nfrom __future__ import black\n')
493 self.assertEqual({"black"}, black.get_future_imports(node))
494 node = black.lib2to3_parse("some(other, code)\nfrom __future__ import black\n")
495 self.assertEqual(set(), black.get_future_imports(node))
496 node = black.lib2to3_parse("from some.module import black\n")
497 self.assertEqual(set(), black.get_future_imports(node))
499 def test_debug_visitor(self) -> None:
500 source, _ = read_data("debug_visitor.py")
501 expected, _ = read_data("debug_visitor.out")
505 def out(msg: str, **kwargs: Any) -> None:
506 out_lines.append(msg)
508 def err(msg: str, **kwargs: Any) -> None:
509 err_lines.append(msg)
511 with patch("black.out", out), patch("black.err", err):
512 black.DebugVisitor.show(source)
513 actual = "\n".join(out_lines) + "\n"
515 if expected != actual:
516 log_name = black.dump_to_file(*out_lines)
520 f"AST print out is different. Actual version dumped to {log_name}",
523 def test_format_file_contents(self) -> None:
525 with self.assertRaises(black.NothingChanged):
526 black.format_file_contents(empty, line_length=ll, fast=False)
528 with self.assertRaises(black.NothingChanged):
529 black.format_file_contents(just_nl, line_length=ll, fast=False)
530 same = "l = [1, 2, 3]\n"
531 with self.assertRaises(black.NothingChanged):
532 black.format_file_contents(same, line_length=ll, fast=False)
533 different = "l = [1,2,3]"
535 actual = black.format_file_contents(different, line_length=ll, fast=False)
536 self.assertEqual(expected, actual)
537 invalid = "return if you can"
538 with self.assertRaises(ValueError) as e:
539 black.format_file_contents(invalid, line_length=ll, fast=False)
540 self.assertEqual(str(e.exception), "Cannot parse: 1:7: return if you can")
542 def test_endmarker(self) -> None:
543 n = black.lib2to3_parse("\n")
544 self.assertEqual(n.type, black.syms.file_input)
545 self.assertEqual(len(n.children), 1)
546 self.assertEqual(n.children[0].type, black.token.ENDMARKER)
548 @unittest.skipIf(os.environ.get("SKIP_AST_PRINT"), "user set SKIP_AST_PRINT")
549 def test_assertFormatEqual(self) -> None:
553 def out(msg: str, **kwargs: Any) -> None:
554 out_lines.append(msg)
556 def err(msg: str, **kwargs: Any) -> None:
557 err_lines.append(msg)
559 with patch("black.out", out), patch("black.err", err):
560 with self.assertRaises(AssertionError):
561 self.assertFormatEqual("l = [1, 2, 3]", "l = [1, 2, 3,]")
563 out_str = "".join(out_lines)
564 self.assertTrue("Expected tree:" in out_str)
565 self.assertTrue("Actual tree:" in out_str)
566 self.assertEqual("".join(err_lines), "")
568 def test_cache_broken_file(self) -> None:
569 with cache_dir() as workspace:
570 cache_file = black.get_cache_file(black.DEFAULT_LINE_LENGTH)
571 with cache_file.open("w") as fobj:
572 fobj.write("this is not a pickle")
573 self.assertEqual(black.read_cache(black.DEFAULT_LINE_LENGTH), {})
574 src = (workspace / "test.py").resolve()
575 with src.open("w") as fobj:
576 fobj.write("print('hello')")
577 result = CliRunner().invoke(black.main, [str(src)])
578 self.assertEqual(result.exit_code, 0)
579 cache = black.read_cache(black.DEFAULT_LINE_LENGTH)
580 self.assertIn(src, cache)
582 def test_cache_single_file_already_cached(self) -> None:
583 with cache_dir() as workspace:
584 src = (workspace / "test.py").resolve()
585 with src.open("w") as fobj:
586 fobj.write("print('hello')")
587 black.write_cache({}, [src], black.DEFAULT_LINE_LENGTH)
588 result = CliRunner().invoke(black.main, [str(src)])
589 self.assertEqual(result.exit_code, 0)
590 with src.open("r") as fobj:
591 self.assertEqual(fobj.read(), "print('hello')")
593 @event_loop(close=False)
594 def test_cache_multiple_files(self) -> None:
595 with cache_dir() as workspace, patch(
596 "black.ProcessPoolExecutor", new=ThreadPoolExecutor
598 one = (workspace / "one.py").resolve()
599 with one.open("w") as fobj:
600 fobj.write("print('hello')")
601 two = (workspace / "two.py").resolve()
602 with two.open("w") as fobj:
603 fobj.write("print('hello')")
604 black.write_cache({}, [one], black.DEFAULT_LINE_LENGTH)
605 result = CliRunner().invoke(black.main, [str(workspace)])
606 self.assertEqual(result.exit_code, 0)
607 with one.open("r") as fobj:
608 self.assertEqual(fobj.read(), "print('hello')")
609 with two.open("r") as fobj:
610 self.assertEqual(fobj.read(), 'print("hello")\n')
611 cache = black.read_cache(black.DEFAULT_LINE_LENGTH)
612 self.assertIn(one, cache)
613 self.assertIn(two, cache)
615 def test_no_cache_when_writeback_diff(self) -> None:
616 with cache_dir() as workspace:
617 src = (workspace / "test.py").resolve()
618 with src.open("w") as fobj:
619 fobj.write("print('hello')")
620 result = CliRunner().invoke(black.main, [str(src), "--diff"])
621 self.assertEqual(result.exit_code, 0)
622 cache_file = black.get_cache_file(black.DEFAULT_LINE_LENGTH)
623 self.assertFalse(cache_file.exists())
625 def test_no_cache_when_stdin(self) -> None:
627 result = CliRunner().invoke(black.main, ["-"], input="print('hello')")
628 self.assertEqual(result.exit_code, 0)
629 cache_file = black.get_cache_file(black.DEFAULT_LINE_LENGTH)
630 self.assertFalse(cache_file.exists())
632 def test_read_cache_no_cachefile(self) -> None:
634 self.assertEqual(black.read_cache(black.DEFAULT_LINE_LENGTH), {})
636 def test_write_cache_read_cache(self) -> None:
637 with cache_dir() as workspace:
638 src = (workspace / "test.py").resolve()
640 black.write_cache({}, [src], black.DEFAULT_LINE_LENGTH)
641 cache = black.read_cache(black.DEFAULT_LINE_LENGTH)
642 self.assertIn(src, cache)
643 self.assertEqual(cache[src], black.get_cache_info(src))
645 def test_filter_cached(self) -> None:
646 with TemporaryDirectory() as workspace:
647 path = Path(workspace)
648 uncached = (path / "uncached").resolve()
649 cached = (path / "cached").resolve()
650 cached_but_changed = (path / "changed").resolve()
653 cached_but_changed.touch()
654 cache = {cached: black.get_cache_info(cached), cached_but_changed: (0.0, 0)}
655 todo, done = black.filter_cached(
656 cache, [uncached, cached, cached_but_changed]
658 self.assertEqual(todo, [uncached, cached_but_changed])
659 self.assertEqual(done, [cached])
661 def test_write_cache_creates_directory_if_needed(self) -> None:
662 with cache_dir(exists=False) as workspace:
663 self.assertFalse(workspace.exists())
664 black.write_cache({}, [], black.DEFAULT_LINE_LENGTH)
665 self.assertTrue(workspace.exists())
667 @event_loop(close=False)
668 def test_failed_formatting_does_not_get_cached(self) -> None:
669 with cache_dir() as workspace, patch(
670 "black.ProcessPoolExecutor", new=ThreadPoolExecutor
672 failing = (workspace / "failing.py").resolve()
673 with failing.open("w") as fobj:
674 fobj.write("not actually python")
675 clean = (workspace / "clean.py").resolve()
676 with clean.open("w") as fobj:
677 fobj.write('print("hello")\n')
678 result = CliRunner().invoke(black.main, [str(workspace)])
679 self.assertEqual(result.exit_code, 123)
680 cache = black.read_cache(black.DEFAULT_LINE_LENGTH)
681 self.assertNotIn(failing, cache)
682 self.assertIn(clean, cache)
684 def test_write_cache_write_fail(self) -> None:
685 with cache_dir(), patch.object(Path, "open") as mock:
686 mock.side_effect = OSError
687 black.write_cache({}, [], black.DEFAULT_LINE_LENGTH)
689 @event_loop(close=False)
690 def test_check_diff_use_together(self) -> None:
692 # Files which will be reformatted.
693 src1 = (THIS_DIR / "string_quotes.py").resolve()
694 result = CliRunner().invoke(black.main, [str(src1), "--diff", "--check"])
695 self.assertEqual(result.exit_code, 1)
697 # Files which will not be reformatted.
698 src2 = (THIS_DIR / "composition.py").resolve()
699 result = CliRunner().invoke(black.main, [str(src2), "--diff", "--check"])
700 self.assertEqual(result.exit_code, 0)
702 # Multi file command.
703 result = CliRunner().invoke(
704 black.main, [str(src1), str(src2), "--diff", "--check"]
706 self.assertEqual(result.exit_code, 1, result.output)
708 def test_no_files(self) -> None:
710 # Without an argument, black exits with error code 0.
711 result = CliRunner().invoke(black.main, [])
712 self.assertEqual(result.exit_code, 0)
714 def test_broken_symlink(self) -> None:
715 with cache_dir() as workspace:
716 symlink = workspace / "broken_link.py"
717 symlink.symlink_to("nonexistent.py")
718 result = CliRunner().invoke(black.main, [str(workspace.resolve())])
719 self.assertEqual(result.exit_code, 0)
721 def test_read_cache_line_lengths(self) -> None:
722 with cache_dir() as workspace:
723 path = (workspace / "file.py").resolve()
725 black.write_cache({}, [path], 1)
726 one = black.read_cache(1)
727 self.assertIn(path, one)
728 two = black.read_cache(2)
729 self.assertNotIn(path, two)
731 def test_single_file_force_pyi(self) -> None:
732 contents, expected = read_data("force_pyi")
733 with cache_dir() as workspace:
734 path = (workspace / "file.py").resolve()
735 with open(path, "w") as fh:
737 result = CliRunner().invoke(black.main, [str(path), "--pyi"])
738 self.assertEqual(result.exit_code, 0)
739 with open(path, "r") as fh:
741 # verify cache with --pyi is separate
742 pyi_cache = black.read_cache(black.DEFAULT_LINE_LENGTH, pyi=True)
743 self.assertIn(path, pyi_cache)
744 normal_cache = black.read_cache(black.DEFAULT_LINE_LENGTH)
745 self.assertNotIn(path, normal_cache)
746 self.assertEqual(actual, expected)
748 @event_loop(close=False)
749 def test_multi_file_force_pyi(self) -> None:
750 contents, expected = read_data("force_pyi")
751 with cache_dir() as workspace:
753 (workspace / "file1.py").resolve(),
754 (workspace / "file2.py").resolve(),
757 with open(path, "w") as fh:
759 result = CliRunner().invoke(black.main, [str(p) for p in paths] + ["--pyi"])
760 self.assertEqual(result.exit_code, 0)
762 with open(path, "r") as fh:
764 self.assertEqual(actual, expected)
765 # verify cache with --pyi is separate
766 pyi_cache = black.read_cache(black.DEFAULT_LINE_LENGTH, pyi=True)
767 normal_cache = black.read_cache(black.DEFAULT_LINE_LENGTH)
769 self.assertIn(path, pyi_cache)
770 self.assertNotIn(path, normal_cache)
772 def test_pipe_force_pyi(self) -> None:
773 source, expected = read_data("force_pyi")
774 result = CliRunner().invoke(black.main, ["-", "-q", "--pyi"], input=source)
775 self.assertEqual(result.exit_code, 0)
776 actual = result.output
777 self.assertFormatEqual(actual, expected)
779 def test_single_file_force_py36(self) -> None:
780 source, expected = read_data("force_py36")
781 with cache_dir() as workspace:
782 path = (workspace / "file.py").resolve()
783 with open(path, "w") as fh:
785 result = CliRunner().invoke(black.main, [str(path), "--py36"])
786 self.assertEqual(result.exit_code, 0)
787 with open(path, "r") as fh:
789 # verify cache with --py36 is separate
790 py36_cache = black.read_cache(black.DEFAULT_LINE_LENGTH, py36=True)
791 self.assertIn(path, py36_cache)
792 normal_cache = black.read_cache(black.DEFAULT_LINE_LENGTH)
793 self.assertNotIn(path, normal_cache)
794 self.assertEqual(actual, expected)
796 @event_loop(close=False)
797 def test_multi_file_force_py36(self) -> None:
798 source, expected = read_data("force_py36")
799 with cache_dir() as workspace:
801 (workspace / "file1.py").resolve(),
802 (workspace / "file2.py").resolve(),
805 with open(path, "w") as fh:
807 result = CliRunner().invoke(
808 black.main, [str(p) for p in paths] + ["--py36"]
810 self.assertEqual(result.exit_code, 0)
812 with open(path, "r") as fh:
814 self.assertEqual(actual, expected)
815 # verify cache with --py36 is separate
816 pyi_cache = black.read_cache(black.DEFAULT_LINE_LENGTH, py36=True)
817 normal_cache = black.read_cache(black.DEFAULT_LINE_LENGTH)
819 self.assertIn(path, pyi_cache)
820 self.assertNotIn(path, normal_cache)
822 def test_pipe_force_py36(self) -> None:
823 source, expected = read_data("force_py36")
824 result = CliRunner().invoke(black.main, ["-", "-q", "--py36"], input=source)
825 self.assertEqual(result.exit_code, 0)
826 actual = result.output
827 self.assertFormatEqual(actual, expected)
830 if __name__ == "__main__":