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 def test_report(self) -> None:
367 report = black.Report()
371 def out(msg: str, **kwargs: Any) -> None:
372 out_lines.append(msg)
374 def err(msg: str, **kwargs: Any) -> None:
375 err_lines.append(msg)
377 with patch("black.out", out), patch("black.err", err):
378 report.done(Path("f1"), black.Changed.NO)
379 self.assertEqual(len(out_lines), 1)
380 self.assertEqual(len(err_lines), 0)
381 self.assertEqual(out_lines[-1], "f1 already well formatted, good job.")
382 self.assertEqual(unstyle(str(report)), "1 file left unchanged.")
383 self.assertEqual(report.return_code, 0)
384 report.done(Path("f2"), black.Changed.YES)
385 self.assertEqual(len(out_lines), 2)
386 self.assertEqual(len(err_lines), 0)
387 self.assertEqual(out_lines[-1], "reformatted f2")
389 unstyle(str(report)), "1 file reformatted, 1 file left unchanged."
391 report.done(Path("f3"), black.Changed.CACHED)
392 self.assertEqual(len(out_lines), 3)
393 self.assertEqual(len(err_lines), 0)
395 out_lines[-1], "f3 wasn't modified on disk since last run."
398 unstyle(str(report)), "1 file reformatted, 2 files left unchanged."
400 self.assertEqual(report.return_code, 0)
402 self.assertEqual(report.return_code, 1)
404 report.failed(Path("e1"), "boom")
405 self.assertEqual(len(out_lines), 3)
406 self.assertEqual(len(err_lines), 1)
407 self.assertEqual(err_lines[-1], "error: cannot format e1: boom")
409 unstyle(str(report)),
410 "1 file reformatted, 2 files left unchanged, "
411 "1 file failed to reformat.",
413 self.assertEqual(report.return_code, 123)
414 report.done(Path("f3"), black.Changed.YES)
415 self.assertEqual(len(out_lines), 4)
416 self.assertEqual(len(err_lines), 1)
417 self.assertEqual(out_lines[-1], "reformatted f3")
419 unstyle(str(report)),
420 "2 files reformatted, 2 files left unchanged, "
421 "1 file failed to reformat.",
423 self.assertEqual(report.return_code, 123)
424 report.failed(Path("e2"), "boom")
425 self.assertEqual(len(out_lines), 4)
426 self.assertEqual(len(err_lines), 2)
427 self.assertEqual(err_lines[-1], "error: cannot format e2: boom")
429 unstyle(str(report)),
430 "2 files reformatted, 2 files left unchanged, "
431 "2 files failed to reformat.",
433 self.assertEqual(report.return_code, 123)
434 report.done(Path("f4"), black.Changed.NO)
435 self.assertEqual(len(out_lines), 5)
436 self.assertEqual(len(err_lines), 2)
437 self.assertEqual(out_lines[-1], "f4 already well formatted, good job.")
439 unstyle(str(report)),
440 "2 files reformatted, 3 files left unchanged, "
441 "2 files failed to reformat.",
443 self.assertEqual(report.return_code, 123)
446 unstyle(str(report)),
447 "2 files would be reformatted, 3 files would be left unchanged, "
448 "2 files would fail to reformat.",
451 def test_is_python36(self) -> None:
452 node = black.lib2to3_parse("def f(*, arg): ...\n")
453 self.assertFalse(black.is_python36(node))
454 node = black.lib2to3_parse("def f(*, arg,): ...\n")
455 self.assertTrue(black.is_python36(node))
456 node = black.lib2to3_parse("def f(*, arg): f'string'\n")
457 self.assertTrue(black.is_python36(node))
458 source, expected = read_data("function")
459 node = black.lib2to3_parse(source)
460 self.assertTrue(black.is_python36(node))
461 node = black.lib2to3_parse(expected)
462 self.assertTrue(black.is_python36(node))
463 source, expected = read_data("expression")
464 node = black.lib2to3_parse(source)
465 self.assertFalse(black.is_python36(node))
466 node = black.lib2to3_parse(expected)
467 self.assertFalse(black.is_python36(node))
469 def test_get_future_imports(self) -> None:
470 node = black.lib2to3_parse("\n")
471 self.assertEqual(set(), black.get_future_imports(node))
472 node = black.lib2to3_parse("from __future__ import black\n")
473 self.assertEqual({"black"}, black.get_future_imports(node))
474 node = black.lib2to3_parse("from __future__ import multiple, imports\n")
475 self.assertEqual({"multiple", "imports"}, black.get_future_imports(node))
476 node = black.lib2to3_parse("from __future__ import (parenthesized, imports)\n")
477 self.assertEqual({"parenthesized", "imports"}, black.get_future_imports(node))
478 node = black.lib2to3_parse(
479 "from __future__ import multiple\nfrom __future__ import imports\n"
481 self.assertEqual({"multiple", "imports"}, black.get_future_imports(node))
482 node = black.lib2to3_parse("# comment\nfrom __future__ import black\n")
483 self.assertEqual({"black"}, black.get_future_imports(node))
484 node = black.lib2to3_parse('"""docstring"""\nfrom __future__ import black\n')
485 self.assertEqual({"black"}, black.get_future_imports(node))
486 node = black.lib2to3_parse("some(other, code)\nfrom __future__ import black\n")
487 self.assertEqual(set(), black.get_future_imports(node))
488 node = black.lib2to3_parse("from some.module import black\n")
489 self.assertEqual(set(), black.get_future_imports(node))
491 def test_debug_visitor(self) -> None:
492 source, _ = read_data("debug_visitor.py")
493 expected, _ = read_data("debug_visitor.out")
497 def out(msg: str, **kwargs: Any) -> None:
498 out_lines.append(msg)
500 def err(msg: str, **kwargs: Any) -> None:
501 err_lines.append(msg)
503 with patch("black.out", out), patch("black.err", err):
504 black.DebugVisitor.show(source)
505 actual = "\n".join(out_lines) + "\n"
507 if expected != actual:
508 log_name = black.dump_to_file(*out_lines)
512 f"AST print out is different. Actual version dumped to {log_name}",
515 def test_format_file_contents(self) -> None:
517 with self.assertRaises(black.NothingChanged):
518 black.format_file_contents(empty, line_length=ll, fast=False)
520 with self.assertRaises(black.NothingChanged):
521 black.format_file_contents(just_nl, line_length=ll, fast=False)
522 same = "l = [1, 2, 3]\n"
523 with self.assertRaises(black.NothingChanged):
524 black.format_file_contents(same, line_length=ll, fast=False)
525 different = "l = [1,2,3]"
527 actual = black.format_file_contents(different, line_length=ll, fast=False)
528 self.assertEqual(expected, actual)
529 invalid = "return if you can"
530 with self.assertRaises(ValueError) as e:
531 black.format_file_contents(invalid, line_length=ll, fast=False)
532 self.assertEqual(str(e.exception), "Cannot parse: 1:7: return if you can")
534 def test_endmarker(self) -> None:
535 n = black.lib2to3_parse("\n")
536 self.assertEqual(n.type, black.syms.file_input)
537 self.assertEqual(len(n.children), 1)
538 self.assertEqual(n.children[0].type, black.token.ENDMARKER)
540 @unittest.skipIf(os.environ.get("SKIP_AST_PRINT"), "user set SKIP_AST_PRINT")
541 def test_assertFormatEqual(self) -> None:
545 def out(msg: str, **kwargs: Any) -> None:
546 out_lines.append(msg)
548 def err(msg: str, **kwargs: Any) -> None:
549 err_lines.append(msg)
551 with patch("black.out", out), patch("black.err", err):
552 with self.assertRaises(AssertionError):
553 self.assertFormatEqual("l = [1, 2, 3]", "l = [1, 2, 3,]")
555 out_str = "".join(out_lines)
556 self.assertTrue("Expected tree:" in out_str)
557 self.assertTrue("Actual tree:" in out_str)
558 self.assertEqual("".join(err_lines), "")
560 def test_cache_broken_file(self) -> None:
561 with cache_dir() as workspace:
562 cache_file = black.get_cache_file(black.DEFAULT_LINE_LENGTH)
563 with cache_file.open("w") as fobj:
564 fobj.write("this is not a pickle")
565 self.assertEqual(black.read_cache(black.DEFAULT_LINE_LENGTH), {})
566 src = (workspace / "test.py").resolve()
567 with src.open("w") as fobj:
568 fobj.write("print('hello')")
569 result = CliRunner().invoke(black.main, [str(src)])
570 self.assertEqual(result.exit_code, 0)
571 cache = black.read_cache(black.DEFAULT_LINE_LENGTH)
572 self.assertIn(src, cache)
574 def test_cache_single_file_already_cached(self) -> None:
575 with cache_dir() as workspace:
576 src = (workspace / "test.py").resolve()
577 with src.open("w") as fobj:
578 fobj.write("print('hello')")
579 black.write_cache({}, [src], black.DEFAULT_LINE_LENGTH)
580 result = CliRunner().invoke(black.main, [str(src)])
581 self.assertEqual(result.exit_code, 0)
582 with src.open("r") as fobj:
583 self.assertEqual(fobj.read(), "print('hello')")
585 @event_loop(close=False)
586 def test_cache_multiple_files(self) -> None:
587 with cache_dir() as workspace, patch(
588 "black.ProcessPoolExecutor", new=ThreadPoolExecutor
590 one = (workspace / "one.py").resolve()
591 with one.open("w") as fobj:
592 fobj.write("print('hello')")
593 two = (workspace / "two.py").resolve()
594 with two.open("w") as fobj:
595 fobj.write("print('hello')")
596 black.write_cache({}, [one], black.DEFAULT_LINE_LENGTH)
597 result = CliRunner().invoke(black.main, [str(workspace)])
598 self.assertEqual(result.exit_code, 0)
599 with one.open("r") as fobj:
600 self.assertEqual(fobj.read(), "print('hello')")
601 with two.open("r") as fobj:
602 self.assertEqual(fobj.read(), 'print("hello")\n')
603 cache = black.read_cache(black.DEFAULT_LINE_LENGTH)
604 self.assertIn(one, cache)
605 self.assertIn(two, cache)
607 def test_no_cache_when_writeback_diff(self) -> None:
608 with cache_dir() as workspace:
609 src = (workspace / "test.py").resolve()
610 with src.open("w") as fobj:
611 fobj.write("print('hello')")
612 result = CliRunner().invoke(black.main, [str(src), "--diff"])
613 self.assertEqual(result.exit_code, 0)
614 cache_file = black.get_cache_file(black.DEFAULT_LINE_LENGTH)
615 self.assertFalse(cache_file.exists())
617 def test_no_cache_when_stdin(self) -> None:
619 result = CliRunner().invoke(black.main, ["-"], input="print('hello')")
620 self.assertEqual(result.exit_code, 0)
621 cache_file = black.get_cache_file(black.DEFAULT_LINE_LENGTH)
622 self.assertFalse(cache_file.exists())
624 def test_read_cache_no_cachefile(self) -> None:
626 self.assertEqual(black.read_cache(black.DEFAULT_LINE_LENGTH), {})
628 def test_write_cache_read_cache(self) -> None:
629 with cache_dir() as workspace:
630 src = (workspace / "test.py").resolve()
632 black.write_cache({}, [src], black.DEFAULT_LINE_LENGTH)
633 cache = black.read_cache(black.DEFAULT_LINE_LENGTH)
634 self.assertIn(src, cache)
635 self.assertEqual(cache[src], black.get_cache_info(src))
637 def test_filter_cached(self) -> None:
638 with TemporaryDirectory() as workspace:
639 path = Path(workspace)
640 uncached = (path / "uncached").resolve()
641 cached = (path / "cached").resolve()
642 cached_but_changed = (path / "changed").resolve()
645 cached_but_changed.touch()
646 cache = {cached: black.get_cache_info(cached), cached_but_changed: (0.0, 0)}
647 todo, done = black.filter_cached(
648 cache, [uncached, cached, cached_but_changed]
650 self.assertEqual(todo, [uncached, cached_but_changed])
651 self.assertEqual(done, [cached])
653 def test_write_cache_creates_directory_if_needed(self) -> None:
654 with cache_dir(exists=False) as workspace:
655 self.assertFalse(workspace.exists())
656 black.write_cache({}, [], black.DEFAULT_LINE_LENGTH)
657 self.assertTrue(workspace.exists())
659 @event_loop(close=False)
660 def test_failed_formatting_does_not_get_cached(self) -> None:
661 with cache_dir() as workspace, patch(
662 "black.ProcessPoolExecutor", new=ThreadPoolExecutor
664 failing = (workspace / "failing.py").resolve()
665 with failing.open("w") as fobj:
666 fobj.write("not actually python")
667 clean = (workspace / "clean.py").resolve()
668 with clean.open("w") as fobj:
669 fobj.write('print("hello")\n')
670 result = CliRunner().invoke(black.main, [str(workspace)])
671 self.assertEqual(result.exit_code, 123)
672 cache = black.read_cache(black.DEFAULT_LINE_LENGTH)
673 self.assertNotIn(failing, cache)
674 self.assertIn(clean, cache)
676 def test_write_cache_write_fail(self) -> None:
677 with cache_dir(), patch.object(Path, "open") as mock:
678 mock.side_effect = OSError
679 black.write_cache({}, [], black.DEFAULT_LINE_LENGTH)
681 @event_loop(close=False)
682 def test_check_diff_use_together(self) -> None:
684 # Files which will be reformatted.
685 src1 = (THIS_DIR / "string_quotes.py").resolve()
686 result = CliRunner().invoke(black.main, [str(src1), "--diff", "--check"])
687 self.assertEqual(result.exit_code, 1)
689 # Files which will not be reformatted.
690 src2 = (THIS_DIR / "composition.py").resolve()
691 result = CliRunner().invoke(black.main, [str(src2), "--diff", "--check"])
692 self.assertEqual(result.exit_code, 0)
694 # Multi file command.
695 result = CliRunner().invoke(
696 black.main, [str(src1), str(src2), "--diff", "--check"]
698 self.assertEqual(result.exit_code, 1, result.output)
700 def test_no_files(self) -> None:
702 # Without an argument, black exits with error code 0.
703 result = CliRunner().invoke(black.main, [])
704 self.assertEqual(result.exit_code, 0)
706 def test_broken_symlink(self) -> None:
707 with cache_dir() as workspace:
708 symlink = workspace / "broken_link.py"
709 symlink.symlink_to("nonexistent.py")
710 result = CliRunner().invoke(black.main, [str(workspace.resolve())])
711 self.assertEqual(result.exit_code, 0)
713 def test_read_cache_line_lengths(self) -> None:
714 with cache_dir() as workspace:
715 path = (workspace / "file.py").resolve()
717 black.write_cache({}, [path], 1)
718 one = black.read_cache(1)
719 self.assertIn(path, one)
720 two = black.read_cache(2)
721 self.assertNotIn(path, two)
723 def test_single_file_force_pyi(self) -> None:
724 contents, expected = read_data("force_pyi")
725 with cache_dir() as workspace:
726 path = (workspace / "file.py").resolve()
727 with open(path, "w") as fh:
729 result = CliRunner().invoke(black.main, [str(path), "--pyi"])
730 self.assertEqual(result.exit_code, 0)
731 with open(path, "r") as fh:
733 # verify cache with --pyi is separate
734 pyi_cache = black.read_cache(black.DEFAULT_LINE_LENGTH, pyi=True)
735 self.assertIn(path, pyi_cache)
736 normal_cache = black.read_cache(black.DEFAULT_LINE_LENGTH)
737 self.assertNotIn(path, normal_cache)
738 self.assertEqual(actual, expected)
740 @event_loop(close=False)
741 def test_multi_file_force_pyi(self) -> None:
742 contents, expected = read_data("force_pyi")
743 with cache_dir() as workspace:
745 (workspace / "file1.py").resolve(),
746 (workspace / "file2.py").resolve(),
749 with open(path, "w") as fh:
751 result = CliRunner().invoke(black.main, [str(p) for p in paths] + ["--pyi"])
752 self.assertEqual(result.exit_code, 0)
754 with open(path, "r") as fh:
756 self.assertEqual(actual, expected)
757 # verify cache with --pyi is separate
758 pyi_cache = black.read_cache(black.DEFAULT_LINE_LENGTH, pyi=True)
759 normal_cache = black.read_cache(black.DEFAULT_LINE_LENGTH)
761 self.assertIn(path, pyi_cache)
762 self.assertNotIn(path, normal_cache)
764 def test_pipe_force_pyi(self) -> None:
765 source, expected = read_data("force_pyi")
766 result = CliRunner().invoke(black.main, ["-", "-q", "--pyi"], input=source)
767 self.assertEqual(result.exit_code, 0)
768 actual = result.output
769 self.assertFormatEqual(actual, expected)
771 def test_single_file_force_py36(self) -> None:
772 source, expected = read_data("force_py36")
773 with cache_dir() as workspace:
774 path = (workspace / "file.py").resolve()
775 with open(path, "w") as fh:
777 result = CliRunner().invoke(black.main, [str(path), "--py36"])
778 self.assertEqual(result.exit_code, 0)
779 with open(path, "r") as fh:
781 # verify cache with --py36 is separate
782 py36_cache = black.read_cache(black.DEFAULT_LINE_LENGTH, py36=True)
783 self.assertIn(path, py36_cache)
784 normal_cache = black.read_cache(black.DEFAULT_LINE_LENGTH)
785 self.assertNotIn(path, normal_cache)
786 self.assertEqual(actual, expected)
788 @event_loop(close=False)
789 def test_multi_file_force_py36(self) -> None:
790 source, expected = read_data("force_py36")
791 with cache_dir() as workspace:
793 (workspace / "file1.py").resolve(),
794 (workspace / "file2.py").resolve(),
797 with open(path, "w") as fh:
799 result = CliRunner().invoke(
800 black.main, [str(p) for p in paths] + ["--py36"]
802 self.assertEqual(result.exit_code, 0)
804 with open(path, "r") as fh:
806 self.assertEqual(actual, expected)
807 # verify cache with --py36 is separate
808 pyi_cache = black.read_cache(black.DEFAULT_LINE_LENGTH, py36=True)
809 normal_cache = black.read_cache(black.DEFAULT_LINE_LENGTH)
811 self.assertIn(path, pyi_cache)
812 self.assertNotIn(path, normal_cache)
814 def test_pipe_force_py36(self) -> None:
815 source, expected = read_data("force_py36")
816 result = CliRunner().invoke(black.main, ["-", "-q", "--py36"], input=source)
817 self.assertEqual(result.exit_code, 0)
818 actual = result.output
819 self.assertFormatEqual(actual, expected)
822 if __name__ == "__main__":