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.
4 from concurrent.futures import ThreadPoolExecutor
5 from contextlib import contextmanager
6 from functools import partial
7 from io import BytesIO, TextIOWrapper
9 from pathlib import Path
12 from tempfile import TemporaryDirectory
13 from typing import Any, BinaryIO, Dict, Generator, List, Tuple, Iterator, TypeVar
15 from unittest.mock import patch, MagicMock
18 from click import unstyle
19 from click.testing import CliRunner
22 from black import Feature, TargetVersion
26 from aiohttp.test_utils import AioHTTPTestCase, unittest_run_loop
27 from aiohttp import web
29 has_blackd_deps = False
31 has_blackd_deps = True
33 from pathspec import PathSpec
35 # Import other test classes
36 from .test_primer import PrimerCLITests # noqa: F401
39 ff = partial(black.format_file_in_place, mode=black.FileMode(), fast=True)
40 fs = partial(black.format_str, mode=black.FileMode())
41 THIS_FILE = Path(__file__)
42 THIS_DIR = THIS_FILE.parent
43 PROJECT_ROOT = THIS_DIR.parent
44 DETERMINISTIC_HEADER = "[Deterministic header]"
45 EMPTY_LINE = "# EMPTY LINE WITH WHITESPACE" + " (this comment will be removed)"
47 f"--target-version={version.name.lower()}" for version in black.PY36_VERSIONS
53 def dump_to_stderr(*output: str) -> str:
54 return "\n" + "\n".join(output) + "\n"
57 def read_data(name: str, data: bool = True) -> Tuple[str, str]:
58 """read_data('test_name') -> 'input', 'output'"""
59 if not name.endswith((".py", ".pyi", ".out", ".diff")):
61 _input: List[str] = []
62 _output: List[str] = []
63 base_dir = THIS_DIR / "data" if data else PROJECT_ROOT
64 with open(base_dir / name, "r", encoding="utf8") as test:
65 lines = test.readlines()
68 line = line.replace(EMPTY_LINE, "")
69 if line.rstrip() == "# output":
74 if _input and not _output:
75 # If there's no output marker, treat the entire file as already pre-formatted.
77 return "".join(_input).strip() + "\n", "".join(_output).strip() + "\n"
81 def cache_dir(exists: bool = True) -> Iterator[Path]:
82 with TemporaryDirectory() as workspace:
83 cache_dir = Path(workspace)
85 cache_dir = cache_dir / "new"
86 with patch("black.CACHE_DIR", cache_dir):
91 def event_loop() -> Iterator[None]:
92 policy = asyncio.get_event_loop_policy()
93 loop = policy.new_event_loop()
94 asyncio.set_event_loop(loop)
103 def skip_if_exception(e: str) -> Iterator[None]:
106 except Exception as exc:
107 if exc.__class__.__name__ == e:
108 unittest.skip(f"Encountered expected exception {exc}, skipping")
113 class FakeContext(click.Context):
114 """A fake click Context for when calling functions that need it."""
116 def __init__(self) -> None:
117 self.default_map: Dict[str, Any] = {}
120 class FakeParameter(click.Parameter):
121 """A fake click Parameter for when calling functions that need it."""
123 def __init__(self) -> None:
127 class BlackRunner(CliRunner):
128 """Modify CliRunner so that stderr is not merged with stdout.
130 This is a hack that can be removed once we depend on Click 7.x"""
132 def __init__(self) -> None:
133 self.stderrbuf = BytesIO()
134 self.stdoutbuf = BytesIO()
135 self.stdout_bytes = b""
136 self.stderr_bytes = b""
140 def isolation(self, *args: Any, **kwargs: Any) -> Generator[BinaryIO, None, None]:
141 with super().isolation(*args, **kwargs) as output:
143 hold_stderr = sys.stderr
144 sys.stderr = TextIOWrapper(self.stderrbuf, encoding=self.charset)
147 self.stdout_bytes = sys.stdout.buffer.getvalue() # type: ignore
148 self.stderr_bytes = sys.stderr.buffer.getvalue() # type: ignore
149 sys.stderr = hold_stderr
152 class BlackTestCase(unittest.TestCase):
155 def assertFormatEqual(self, expected: str, actual: str) -> None:
156 if actual != expected and not os.environ.get("SKIP_AST_PRINT"):
157 bdv: black.DebugVisitor[Any]
158 black.out("Expected tree:", fg="green")
160 exp_node = black.lib2to3_parse(expected)
161 bdv = black.DebugVisitor()
162 list(bdv.visit(exp_node))
163 except Exception as ve:
165 black.out("Actual tree:", fg="red")
167 exp_node = black.lib2to3_parse(actual)
168 bdv = black.DebugVisitor()
169 list(bdv.visit(exp_node))
170 except Exception as ve:
172 self.assertEqual(expected, actual)
175 self, args: List[str], exit_code: int = 0, ignore_config: bool = True
177 runner = BlackRunner()
179 args = ["--verbose", "--config", str(THIS_DIR / "empty.toml"), *args]
180 result = runner.invoke(black.main, args)
185 f"Failed with args: {args}\n"
186 f"stdout: {runner.stdout_bytes.decode()!r}\n"
187 f"stderr: {runner.stderr_bytes.decode()!r}\n"
188 f"exception: {result.exception}"
192 @patch("black.dump_to_file", dump_to_stderr)
193 def checkSourceFile(self, name: str) -> None:
194 path = THIS_DIR.parent / name
195 source, expected = read_data(str(path), data=False)
197 self.assertFormatEqual(expected, actual)
198 black.assert_equivalent(source, actual)
199 black.assert_stable(source, actual, black.FileMode())
200 self.assertFalse(ff(path))
202 @patch("black.dump_to_file", dump_to_stderr)
203 def test_empty(self) -> None:
204 source = expected = ""
206 self.assertFormatEqual(expected, actual)
207 black.assert_equivalent(source, actual)
208 black.assert_stable(source, actual, black.FileMode())
210 def test_empty_ff(self) -> None:
212 tmp_file = Path(black.dump_to_file())
214 self.assertFalse(ff(tmp_file, write_back=black.WriteBack.YES))
215 with open(tmp_file, encoding="utf8") as f:
219 self.assertFormatEqual(expected, actual)
221 def test_self(self) -> None:
222 self.checkSourceFile("tests/test_black.py")
224 def test_black(self) -> None:
225 self.checkSourceFile("src/black/__init__.py")
227 def test_pygram(self) -> None:
228 self.checkSourceFile("src/blib2to3/pygram.py")
230 def test_pytree(self) -> None:
231 self.checkSourceFile("src/blib2to3/pytree.py")
233 def test_conv(self) -> None:
234 self.checkSourceFile("src/blib2to3/pgen2/conv.py")
236 def test_driver(self) -> None:
237 self.checkSourceFile("src/blib2to3/pgen2/driver.py")
239 def test_grammar(self) -> None:
240 self.checkSourceFile("src/blib2to3/pgen2/grammar.py")
242 def test_literals(self) -> None:
243 self.checkSourceFile("src/blib2to3/pgen2/literals.py")
245 def test_parse(self) -> None:
246 self.checkSourceFile("src/blib2to3/pgen2/parse.py")
248 def test_pgen(self) -> None:
249 self.checkSourceFile("src/blib2to3/pgen2/pgen.py")
251 def test_tokenize(self) -> None:
252 self.checkSourceFile("src/blib2to3/pgen2/tokenize.py")
254 def test_token(self) -> None:
255 self.checkSourceFile("src/blib2to3/pgen2/token.py")
257 def test_setup(self) -> None:
258 self.checkSourceFile("setup.py")
260 def test_piping(self) -> None:
261 source, expected = read_data("src/black/__init__", data=False)
262 result = BlackRunner().invoke(
264 ["-", "--fast", f"--line-length={black.DEFAULT_LINE_LENGTH}"],
265 input=BytesIO(source.encode("utf8")),
267 self.assertEqual(result.exit_code, 0)
268 self.assertFormatEqual(expected, result.output)
269 black.assert_equivalent(source, result.output)
270 black.assert_stable(source, result.output, black.FileMode())
272 def test_piping_diff(self) -> None:
273 diff_header = re.compile(
274 r"(STDIN|STDOUT)\t\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d\.\d\d\d\d\d\d "
277 source, _ = read_data("expression.py")
278 expected, _ = read_data("expression.diff")
279 config = THIS_DIR / "data" / "empty_pyproject.toml"
283 f"--line-length={black.DEFAULT_LINE_LENGTH}",
285 f"--config={config}",
287 result = BlackRunner().invoke(
288 black.main, args, input=BytesIO(source.encode("utf8"))
290 self.assertEqual(result.exit_code, 0)
291 actual = diff_header.sub(DETERMINISTIC_HEADER, result.output)
292 actual = actual.rstrip() + "\n" # the diff output has a trailing space
293 self.assertEqual(expected, actual)
295 def test_piping_diff_with_color(self) -> None:
296 source, _ = read_data("expression.py")
297 config = THIS_DIR / "data" / "empty_pyproject.toml"
301 f"--line-length={black.DEFAULT_LINE_LENGTH}",
304 f"--config={config}",
306 result = BlackRunner().invoke(
307 black.main, args, input=BytesIO(source.encode("utf8"))
309 actual = result.output
310 # Again, the contents are checked in a different test, so only look for colors.
311 self.assertIn("\033[1;37m", actual)
312 self.assertIn("\033[36m", actual)
313 self.assertIn("\033[32m", actual)
314 self.assertIn("\033[31m", actual)
315 self.assertIn("\033[0m", actual)
317 @patch("black.dump_to_file", dump_to_stderr)
318 def test_function(self) -> None:
319 source, expected = read_data("function")
321 self.assertFormatEqual(expected, actual)
322 black.assert_equivalent(source, actual)
323 black.assert_stable(source, actual, black.FileMode())
325 @patch("black.dump_to_file", dump_to_stderr)
326 def test_function2(self) -> None:
327 source, expected = read_data("function2")
329 self.assertFormatEqual(expected, actual)
330 black.assert_equivalent(source, actual)
331 black.assert_stable(source, actual, black.FileMode())
333 @patch("black.dump_to_file", dump_to_stderr)
334 def test_function_trailing_comma(self) -> None:
335 source, expected = read_data("function_trailing_comma")
337 self.assertFormatEqual(expected, actual)
338 black.assert_equivalent(source, actual)
339 black.assert_stable(source, actual, black.FileMode())
341 @patch("black.dump_to_file", dump_to_stderr)
342 def test_expression(self) -> None:
343 source, expected = read_data("expression")
345 self.assertFormatEqual(expected, actual)
346 black.assert_equivalent(source, actual)
347 black.assert_stable(source, actual, black.FileMode())
349 @patch("black.dump_to_file", dump_to_stderr)
350 def test_pep_572(self) -> None:
351 source, expected = read_data("pep_572")
353 self.assertFormatEqual(expected, actual)
354 black.assert_stable(source, actual, black.FileMode())
355 if sys.version_info >= (3, 8):
356 black.assert_equivalent(source, actual)
358 def test_pep_572_version_detection(self) -> None:
359 source, _ = read_data("pep_572")
360 root = black.lib2to3_parse(source)
361 features = black.get_features_used(root)
362 self.assertIn(black.Feature.ASSIGNMENT_EXPRESSIONS, features)
363 versions = black.detect_target_versions(root)
364 self.assertIn(black.TargetVersion.PY38, versions)
366 def test_expression_ff(self) -> None:
367 source, expected = read_data("expression")
368 tmp_file = Path(black.dump_to_file(source))
370 self.assertTrue(ff(tmp_file, write_back=black.WriteBack.YES))
371 with open(tmp_file, encoding="utf8") as f:
375 self.assertFormatEqual(expected, actual)
376 with patch("black.dump_to_file", dump_to_stderr):
377 black.assert_equivalent(source, actual)
378 black.assert_stable(source, actual, black.FileMode())
380 def test_expression_diff(self) -> None:
381 source, _ = read_data("expression.py")
382 expected, _ = read_data("expression.diff")
383 tmp_file = Path(black.dump_to_file(source))
384 diff_header = re.compile(
385 rf"{re.escape(str(tmp_file))}\t\d\d\d\d-\d\d-\d\d "
386 r"\d\d:\d\d:\d\d\.\d\d\d\d\d\d \+\d\d\d\d"
389 result = BlackRunner().invoke(black.main, ["--diff", str(tmp_file)])
390 self.assertEqual(result.exit_code, 0)
393 actual = result.output
394 actual = diff_header.sub(DETERMINISTIC_HEADER, actual)
395 actual = actual.rstrip() + "\n" # the diff output has a trailing space
396 if expected != actual:
397 dump = black.dump_to_file(actual)
399 "Expected diff isn't equal to the actual. If you made changes to"
400 " expression.py and this is an anticipated difference, overwrite"
401 f" tests/data/expression.diff with {dump}"
403 self.assertEqual(expected, actual, msg)
405 def test_expression_diff_with_color(self) -> None:
406 source, _ = read_data("expression.py")
407 expected, _ = read_data("expression.diff")
408 tmp_file = Path(black.dump_to_file(source))
410 result = BlackRunner().invoke(
411 black.main, ["--diff", "--color", str(tmp_file)]
415 actual = result.output
416 # We check the contents of the diff in `test_expression_diff`. All
417 # we need to check here is that color codes exist in the result.
418 self.assertIn("\033[1;37m", actual)
419 self.assertIn("\033[36m", actual)
420 self.assertIn("\033[32m", actual)
421 self.assertIn("\033[31m", actual)
422 self.assertIn("\033[0m", actual)
424 @patch("black.dump_to_file", dump_to_stderr)
425 def test_fstring(self) -> None:
426 source, expected = read_data("fstring")
428 self.assertFormatEqual(expected, actual)
429 black.assert_equivalent(source, actual)
430 black.assert_stable(source, actual, black.FileMode())
432 @patch("black.dump_to_file", dump_to_stderr)
433 def test_pep_570(self) -> None:
434 source, expected = read_data("pep_570")
436 self.assertFormatEqual(expected, actual)
437 black.assert_stable(source, actual, black.FileMode())
438 if sys.version_info >= (3, 8):
439 black.assert_equivalent(source, actual)
441 def test_detect_pos_only_arguments(self) -> None:
442 source, _ = read_data("pep_570")
443 root = black.lib2to3_parse(source)
444 features = black.get_features_used(root)
445 self.assertIn(black.Feature.POS_ONLY_ARGUMENTS, features)
446 versions = black.detect_target_versions(root)
447 self.assertIn(black.TargetVersion.PY38, versions)
449 @patch("black.dump_to_file", dump_to_stderr)
450 def test_string_quotes(self) -> None:
451 source, expected = read_data("string_quotes")
453 self.assertFormatEqual(expected, actual)
454 black.assert_equivalent(source, actual)
455 black.assert_stable(source, actual, black.FileMode())
456 mode = black.FileMode(string_normalization=False)
457 not_normalized = fs(source, mode=mode)
458 self.assertFormatEqual(source.replace("\\\n", ""), not_normalized)
459 black.assert_equivalent(source, not_normalized)
460 black.assert_stable(source, not_normalized, mode=mode)
462 @patch("black.dump_to_file", dump_to_stderr)
463 def test_docstring(self) -> None:
464 source, expected = read_data("docstring")
466 self.assertFormatEqual(expected, actual)
467 black.assert_equivalent(source, actual)
468 black.assert_stable(source, actual, black.FileMode())
470 def test_long_strings(self) -> None:
471 """Tests for splitting long strings."""
472 source, expected = read_data("long_strings")
474 self.assertFormatEqual(expected, actual)
475 black.assert_equivalent(source, actual)
476 black.assert_stable(source, actual, black.FileMode())
478 @patch("black.dump_to_file", dump_to_stderr)
479 def test_long_strings__edge_case(self) -> None:
480 """Edge-case tests for splitting long strings."""
481 source, expected = read_data("long_strings__edge_case")
483 self.assertFormatEqual(expected, actual)
484 black.assert_equivalent(source, actual)
485 black.assert_stable(source, actual, black.FileMode())
487 @patch("black.dump_to_file", dump_to_stderr)
488 def test_long_strings__regression(self) -> None:
489 """Regression tests for splitting long strings."""
490 source, expected = read_data("long_strings__regression")
492 self.assertFormatEqual(expected, actual)
493 black.assert_equivalent(source, actual)
494 black.assert_stable(source, actual, black.FileMode())
496 @patch("black.dump_to_file", dump_to_stderr)
497 def test_slices(self) -> None:
498 source, expected = read_data("slices")
500 self.assertFormatEqual(expected, actual)
501 black.assert_equivalent(source, actual)
502 black.assert_stable(source, actual, black.FileMode())
504 @patch("black.dump_to_file", dump_to_stderr)
505 def test_percent_precedence(self) -> None:
506 source, expected = read_data("percent_precedence")
508 self.assertFormatEqual(expected, actual)
509 black.assert_equivalent(source, actual)
510 black.assert_stable(source, actual, black.FileMode())
512 @patch("black.dump_to_file", dump_to_stderr)
513 def test_comments(self) -> None:
514 source, expected = read_data("comments")
516 self.assertFormatEqual(expected, actual)
517 black.assert_equivalent(source, actual)
518 black.assert_stable(source, actual, black.FileMode())
520 @patch("black.dump_to_file", dump_to_stderr)
521 def test_comments2(self) -> None:
522 source, expected = read_data("comments2")
524 self.assertFormatEqual(expected, actual)
525 black.assert_equivalent(source, actual)
526 black.assert_stable(source, actual, black.FileMode())
528 @patch("black.dump_to_file", dump_to_stderr)
529 def test_comments3(self) -> None:
530 source, expected = read_data("comments3")
532 self.assertFormatEqual(expected, actual)
533 black.assert_equivalent(source, actual)
534 black.assert_stable(source, actual, black.FileMode())
536 @patch("black.dump_to_file", dump_to_stderr)
537 def test_comments4(self) -> None:
538 source, expected = read_data("comments4")
540 self.assertFormatEqual(expected, actual)
541 black.assert_equivalent(source, actual)
542 black.assert_stable(source, actual, black.FileMode())
544 @patch("black.dump_to_file", dump_to_stderr)
545 def test_comments5(self) -> None:
546 source, expected = read_data("comments5")
548 self.assertFormatEqual(expected, actual)
549 black.assert_equivalent(source, actual)
550 black.assert_stable(source, actual, black.FileMode())
552 @patch("black.dump_to_file", dump_to_stderr)
553 def test_comments6(self) -> None:
554 source, expected = read_data("comments6")
556 self.assertFormatEqual(expected, actual)
557 black.assert_equivalent(source, actual)
558 black.assert_stable(source, actual, black.FileMode())
560 @patch("black.dump_to_file", dump_to_stderr)
561 def test_comments7(self) -> None:
562 source, expected = read_data("comments7")
564 self.assertFormatEqual(expected, actual)
565 black.assert_equivalent(source, actual)
566 black.assert_stable(source, actual, black.FileMode())
568 @patch("black.dump_to_file", dump_to_stderr)
569 def test_comment_after_escaped_newline(self) -> None:
570 source, expected = read_data("comment_after_escaped_newline")
572 self.assertFormatEqual(expected, actual)
573 black.assert_equivalent(source, actual)
574 black.assert_stable(source, actual, black.FileMode())
576 @patch("black.dump_to_file", dump_to_stderr)
577 def test_cantfit(self) -> None:
578 source, expected = read_data("cantfit")
580 self.assertFormatEqual(expected, actual)
581 black.assert_equivalent(source, actual)
582 black.assert_stable(source, actual, black.FileMode())
584 @patch("black.dump_to_file", dump_to_stderr)
585 def test_import_spacing(self) -> None:
586 source, expected = read_data("import_spacing")
588 self.assertFormatEqual(expected, actual)
589 black.assert_equivalent(source, actual)
590 black.assert_stable(source, actual, black.FileMode())
592 @patch("black.dump_to_file", dump_to_stderr)
593 def test_composition(self) -> None:
594 source, expected = read_data("composition")
596 self.assertFormatEqual(expected, actual)
597 black.assert_equivalent(source, actual)
598 black.assert_stable(source, actual, black.FileMode())
600 @patch("black.dump_to_file", dump_to_stderr)
601 def test_empty_lines(self) -> None:
602 source, expected = read_data("empty_lines")
604 self.assertFormatEqual(expected, actual)
605 black.assert_equivalent(source, actual)
606 black.assert_stable(source, actual, black.FileMode())
608 @patch("black.dump_to_file", dump_to_stderr)
609 def test_remove_parens(self) -> None:
610 source, expected = read_data("remove_parens")
612 self.assertFormatEqual(expected, actual)
613 black.assert_equivalent(source, actual)
614 black.assert_stable(source, actual, black.FileMode())
616 @patch("black.dump_to_file", dump_to_stderr)
617 def test_string_prefixes(self) -> None:
618 source, expected = read_data("string_prefixes")
620 self.assertFormatEqual(expected, actual)
621 black.assert_equivalent(source, actual)
622 black.assert_stable(source, actual, black.FileMode())
624 @patch("black.dump_to_file", dump_to_stderr)
625 def test_numeric_literals(self) -> None:
626 source, expected = read_data("numeric_literals")
627 mode = black.FileMode(target_versions=black.PY36_VERSIONS)
628 actual = fs(source, mode=mode)
629 self.assertFormatEqual(expected, actual)
630 black.assert_equivalent(source, actual)
631 black.assert_stable(source, actual, mode)
633 @patch("black.dump_to_file", dump_to_stderr)
634 def test_numeric_literals_ignoring_underscores(self) -> None:
635 source, expected = read_data("numeric_literals_skip_underscores")
636 mode = black.FileMode(target_versions=black.PY36_VERSIONS)
637 actual = fs(source, mode=mode)
638 self.assertFormatEqual(expected, actual)
639 black.assert_equivalent(source, actual)
640 black.assert_stable(source, actual, mode)
642 @patch("black.dump_to_file", dump_to_stderr)
643 def test_numeric_literals_py2(self) -> None:
644 source, expected = read_data("numeric_literals_py2")
646 self.assertFormatEqual(expected, actual)
647 black.assert_stable(source, actual, black.FileMode())
649 @patch("black.dump_to_file", dump_to_stderr)
650 def test_python2(self) -> None:
651 source, expected = read_data("python2")
653 self.assertFormatEqual(expected, actual)
654 black.assert_equivalent(source, actual)
655 black.assert_stable(source, actual, black.FileMode())
657 @patch("black.dump_to_file", dump_to_stderr)
658 def test_python2_print_function(self) -> None:
659 source, expected = read_data("python2_print_function")
660 mode = black.FileMode(target_versions={TargetVersion.PY27})
661 actual = fs(source, mode=mode)
662 self.assertFormatEqual(expected, actual)
663 black.assert_equivalent(source, actual)
664 black.assert_stable(source, actual, mode)
666 @patch("black.dump_to_file", dump_to_stderr)
667 def test_python2_unicode_literals(self) -> None:
668 source, expected = read_data("python2_unicode_literals")
670 self.assertFormatEqual(expected, actual)
671 black.assert_equivalent(source, actual)
672 black.assert_stable(source, actual, black.FileMode())
674 @patch("black.dump_to_file", dump_to_stderr)
675 def test_stub(self) -> None:
676 mode = black.FileMode(is_pyi=True)
677 source, expected = read_data("stub.pyi")
678 actual = fs(source, mode=mode)
679 self.assertFormatEqual(expected, actual)
680 black.assert_stable(source, actual, mode)
682 @patch("black.dump_to_file", dump_to_stderr)
683 def test_async_as_identifier(self) -> None:
684 source_path = (THIS_DIR / "data" / "async_as_identifier.py").resolve()
685 source, expected = read_data("async_as_identifier")
687 self.assertFormatEqual(expected, actual)
688 major, minor = sys.version_info[:2]
689 if major < 3 or (major <= 3 and minor < 7):
690 black.assert_equivalent(source, actual)
691 black.assert_stable(source, actual, black.FileMode())
692 # ensure black can parse this when the target is 3.6
693 self.invokeBlack([str(source_path), "--target-version", "py36"])
694 # but not on 3.7, because async/await is no longer an identifier
695 self.invokeBlack([str(source_path), "--target-version", "py37"], exit_code=123)
697 @patch("black.dump_to_file", dump_to_stderr)
698 def test_python37(self) -> None:
699 source_path = (THIS_DIR / "data" / "python37.py").resolve()
700 source, expected = read_data("python37")
702 self.assertFormatEqual(expected, actual)
703 major, minor = sys.version_info[:2]
704 if major > 3 or (major == 3 and minor >= 7):
705 black.assert_equivalent(source, actual)
706 black.assert_stable(source, actual, black.FileMode())
707 # ensure black can parse this when the target is 3.7
708 self.invokeBlack([str(source_path), "--target-version", "py37"])
709 # but not on 3.6, because we use async as a reserved keyword
710 self.invokeBlack([str(source_path), "--target-version", "py36"], exit_code=123)
712 @patch("black.dump_to_file", dump_to_stderr)
713 def test_python38(self) -> None:
714 source, expected = read_data("python38")
716 self.assertFormatEqual(expected, actual)
717 major, minor = sys.version_info[:2]
718 if major > 3 or (major == 3 and minor >= 8):
719 black.assert_equivalent(source, actual)
720 black.assert_stable(source, actual, black.FileMode())
722 @patch("black.dump_to_file", dump_to_stderr)
723 def test_fmtonoff(self) -> None:
724 source, expected = read_data("fmtonoff")
726 self.assertFormatEqual(expected, actual)
727 black.assert_equivalent(source, actual)
728 black.assert_stable(source, actual, black.FileMode())
730 @patch("black.dump_to_file", dump_to_stderr)
731 def test_fmtonoff2(self) -> None:
732 source, expected = read_data("fmtonoff2")
734 self.assertFormatEqual(expected, actual)
735 black.assert_equivalent(source, actual)
736 black.assert_stable(source, actual, black.FileMode())
738 @patch("black.dump_to_file", dump_to_stderr)
739 def test_fmtonoff3(self) -> None:
740 source, expected = read_data("fmtonoff3")
742 self.assertFormatEqual(expected, actual)
743 black.assert_equivalent(source, actual)
744 black.assert_stable(source, actual, black.FileMode())
746 @patch("black.dump_to_file", dump_to_stderr)
747 def test_fmtonoff4(self) -> None:
748 source, expected = read_data("fmtonoff4")
750 self.assertFormatEqual(expected, actual)
751 black.assert_equivalent(source, actual)
752 black.assert_stable(source, actual, black.FileMode())
754 @patch("black.dump_to_file", dump_to_stderr)
755 def test_remove_empty_parentheses_after_class(self) -> None:
756 source, expected = read_data("class_blank_parentheses")
758 self.assertFormatEqual(expected, actual)
759 black.assert_equivalent(source, actual)
760 black.assert_stable(source, actual, black.FileMode())
762 @patch("black.dump_to_file", dump_to_stderr)
763 def test_new_line_between_class_and_code(self) -> None:
764 source, expected = read_data("class_methods_new_line")
766 self.assertFormatEqual(expected, actual)
767 black.assert_equivalent(source, actual)
768 black.assert_stable(source, actual, black.FileMode())
770 @patch("black.dump_to_file", dump_to_stderr)
771 def test_bracket_match(self) -> None:
772 source, expected = read_data("bracketmatch")
774 self.assertFormatEqual(expected, actual)
775 black.assert_equivalent(source, actual)
776 black.assert_stable(source, actual, black.FileMode())
778 @patch("black.dump_to_file", dump_to_stderr)
779 def test_tuple_assign(self) -> None:
780 source, expected = read_data("tupleassign")
782 self.assertFormatEqual(expected, actual)
783 black.assert_equivalent(source, actual)
784 black.assert_stable(source, actual, black.FileMode())
786 @patch("black.dump_to_file", dump_to_stderr)
787 def test_beginning_backslash(self) -> None:
788 source, expected = read_data("beginning_backslash")
790 self.assertFormatEqual(expected, actual)
791 black.assert_equivalent(source, actual)
792 black.assert_stable(source, actual, black.FileMode())
794 def test_tab_comment_indentation(self) -> None:
795 contents_tab = "if 1:\n\tif 2:\n\t\tpass\n\t# comment\n\tpass\n"
796 contents_spc = "if 1:\n if 2:\n pass\n # comment\n pass\n"
797 self.assertFormatEqual(contents_spc, fs(contents_spc))
798 self.assertFormatEqual(contents_spc, fs(contents_tab))
800 contents_tab = "if 1:\n\tif 2:\n\t\tpass\n\t\t# comment\n\tpass\n"
801 contents_spc = "if 1:\n if 2:\n pass\n # comment\n pass\n"
802 self.assertFormatEqual(contents_spc, fs(contents_spc))
803 self.assertFormatEqual(contents_spc, fs(contents_tab))
805 # mixed tabs and spaces (valid Python 2 code)
806 contents_tab = "if 1:\n if 2:\n\t\tpass\n\t# comment\n pass\n"
807 contents_spc = "if 1:\n if 2:\n pass\n # comment\n pass\n"
808 self.assertFormatEqual(contents_spc, fs(contents_spc))
809 self.assertFormatEqual(contents_spc, fs(contents_tab))
811 contents_tab = "if 1:\n if 2:\n\t\tpass\n\t\t# comment\n pass\n"
812 contents_spc = "if 1:\n if 2:\n pass\n # comment\n pass\n"
813 self.assertFormatEqual(contents_spc, fs(contents_spc))
814 self.assertFormatEqual(contents_spc, fs(contents_tab))
816 def test_report_verbose(self) -> None:
817 report = black.Report(verbose=True)
821 def out(msg: str, **kwargs: Any) -> None:
822 out_lines.append(msg)
824 def err(msg: str, **kwargs: Any) -> None:
825 err_lines.append(msg)
827 with patch("black.out", out), patch("black.err", err):
828 report.done(Path("f1"), black.Changed.NO)
829 self.assertEqual(len(out_lines), 1)
830 self.assertEqual(len(err_lines), 0)
831 self.assertEqual(out_lines[-1], "f1 already well formatted, good job.")
832 self.assertEqual(unstyle(str(report)), "1 file left unchanged.")
833 self.assertEqual(report.return_code, 0)
834 report.done(Path("f2"), black.Changed.YES)
835 self.assertEqual(len(out_lines), 2)
836 self.assertEqual(len(err_lines), 0)
837 self.assertEqual(out_lines[-1], "reformatted f2")
839 unstyle(str(report)), "1 file reformatted, 1 file left unchanged."
841 report.done(Path("f3"), black.Changed.CACHED)
842 self.assertEqual(len(out_lines), 3)
843 self.assertEqual(len(err_lines), 0)
845 out_lines[-1], "f3 wasn't modified on disk since last run."
848 unstyle(str(report)), "1 file reformatted, 2 files left unchanged."
850 self.assertEqual(report.return_code, 0)
852 self.assertEqual(report.return_code, 1)
854 report.failed(Path("e1"), "boom")
855 self.assertEqual(len(out_lines), 3)
856 self.assertEqual(len(err_lines), 1)
857 self.assertEqual(err_lines[-1], "error: cannot format e1: boom")
859 unstyle(str(report)),
860 "1 file reformatted, 2 files left unchanged, 1 file failed to"
863 self.assertEqual(report.return_code, 123)
864 report.done(Path("f3"), black.Changed.YES)
865 self.assertEqual(len(out_lines), 4)
866 self.assertEqual(len(err_lines), 1)
867 self.assertEqual(out_lines[-1], "reformatted f3")
869 unstyle(str(report)),
870 "2 files reformatted, 2 files left unchanged, 1 file failed to"
873 self.assertEqual(report.return_code, 123)
874 report.failed(Path("e2"), "boom")
875 self.assertEqual(len(out_lines), 4)
876 self.assertEqual(len(err_lines), 2)
877 self.assertEqual(err_lines[-1], "error: cannot format e2: boom")
879 unstyle(str(report)),
880 "2 files reformatted, 2 files left unchanged, 2 files failed to"
883 self.assertEqual(report.return_code, 123)
884 report.path_ignored(Path("wat"), "no match")
885 self.assertEqual(len(out_lines), 5)
886 self.assertEqual(len(err_lines), 2)
887 self.assertEqual(out_lines[-1], "wat ignored: no match")
889 unstyle(str(report)),
890 "2 files reformatted, 2 files left unchanged, 2 files failed to"
893 self.assertEqual(report.return_code, 123)
894 report.done(Path("f4"), black.Changed.NO)
895 self.assertEqual(len(out_lines), 6)
896 self.assertEqual(len(err_lines), 2)
897 self.assertEqual(out_lines[-1], "f4 already well formatted, good job.")
899 unstyle(str(report)),
900 "2 files reformatted, 3 files left unchanged, 2 files failed to"
903 self.assertEqual(report.return_code, 123)
906 unstyle(str(report)),
907 "2 files would be reformatted, 3 files would be left unchanged, 2 files"
908 " would fail to reformat.",
913 unstyle(str(report)),
914 "2 files would be reformatted, 3 files would be left unchanged, 2 files"
915 " would fail to reformat.",
918 def test_report_quiet(self) -> None:
919 report = black.Report(quiet=True)
923 def out(msg: str, **kwargs: Any) -> None:
924 out_lines.append(msg)
926 def err(msg: str, **kwargs: Any) -> None:
927 err_lines.append(msg)
929 with patch("black.out", out), patch("black.err", err):
930 report.done(Path("f1"), black.Changed.NO)
931 self.assertEqual(len(out_lines), 0)
932 self.assertEqual(len(err_lines), 0)
933 self.assertEqual(unstyle(str(report)), "1 file left unchanged.")
934 self.assertEqual(report.return_code, 0)
935 report.done(Path("f2"), black.Changed.YES)
936 self.assertEqual(len(out_lines), 0)
937 self.assertEqual(len(err_lines), 0)
939 unstyle(str(report)), "1 file reformatted, 1 file left unchanged."
941 report.done(Path("f3"), black.Changed.CACHED)
942 self.assertEqual(len(out_lines), 0)
943 self.assertEqual(len(err_lines), 0)
945 unstyle(str(report)), "1 file reformatted, 2 files left unchanged."
947 self.assertEqual(report.return_code, 0)
949 self.assertEqual(report.return_code, 1)
951 report.failed(Path("e1"), "boom")
952 self.assertEqual(len(out_lines), 0)
953 self.assertEqual(len(err_lines), 1)
954 self.assertEqual(err_lines[-1], "error: cannot format e1: boom")
956 unstyle(str(report)),
957 "1 file reformatted, 2 files left unchanged, 1 file failed to"
960 self.assertEqual(report.return_code, 123)
961 report.done(Path("f3"), black.Changed.YES)
962 self.assertEqual(len(out_lines), 0)
963 self.assertEqual(len(err_lines), 1)
965 unstyle(str(report)),
966 "2 files reformatted, 2 files left unchanged, 1 file failed to"
969 self.assertEqual(report.return_code, 123)
970 report.failed(Path("e2"), "boom")
971 self.assertEqual(len(out_lines), 0)
972 self.assertEqual(len(err_lines), 2)
973 self.assertEqual(err_lines[-1], "error: cannot format e2: boom")
975 unstyle(str(report)),
976 "2 files reformatted, 2 files left unchanged, 2 files failed to"
979 self.assertEqual(report.return_code, 123)
980 report.path_ignored(Path("wat"), "no match")
981 self.assertEqual(len(out_lines), 0)
982 self.assertEqual(len(err_lines), 2)
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), 0)
991 self.assertEqual(len(err_lines), 2)
993 unstyle(str(report)),
994 "2 files reformatted, 3 files left unchanged, 2 files failed to"
997 self.assertEqual(report.return_code, 123)
1000 unstyle(str(report)),
1001 "2 files would be reformatted, 3 files would be left unchanged, 2 files"
1002 " would fail to reformat.",
1004 report.check = False
1007 unstyle(str(report)),
1008 "2 files would be reformatted, 3 files would be left unchanged, 2 files"
1009 " would fail to reformat.",
1012 def test_report_normal(self) -> None:
1013 report = black.Report()
1017 def out(msg: str, **kwargs: Any) -> None:
1018 out_lines.append(msg)
1020 def err(msg: str, **kwargs: Any) -> None:
1021 err_lines.append(msg)
1023 with patch("black.out", out), patch("black.err", err):
1024 report.done(Path("f1"), black.Changed.NO)
1025 self.assertEqual(len(out_lines), 0)
1026 self.assertEqual(len(err_lines), 0)
1027 self.assertEqual(unstyle(str(report)), "1 file left unchanged.")
1028 self.assertEqual(report.return_code, 0)
1029 report.done(Path("f2"), black.Changed.YES)
1030 self.assertEqual(len(out_lines), 1)
1031 self.assertEqual(len(err_lines), 0)
1032 self.assertEqual(out_lines[-1], "reformatted f2")
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), 1)
1038 self.assertEqual(len(err_lines), 0)
1039 self.assertEqual(out_lines[-1], "reformatted f2")
1041 unstyle(str(report)), "1 file reformatted, 2 files left unchanged."
1043 self.assertEqual(report.return_code, 0)
1045 self.assertEqual(report.return_code, 1)
1046 report.check = False
1047 report.failed(Path("e1"), "boom")
1048 self.assertEqual(len(out_lines), 1)
1049 self.assertEqual(len(err_lines), 1)
1050 self.assertEqual(err_lines[-1], "error: cannot format e1: boom")
1052 unstyle(str(report)),
1053 "1 file reformatted, 2 files left unchanged, 1 file failed to"
1056 self.assertEqual(report.return_code, 123)
1057 report.done(Path("f3"), black.Changed.YES)
1058 self.assertEqual(len(out_lines), 2)
1059 self.assertEqual(len(err_lines), 1)
1060 self.assertEqual(out_lines[-1], "reformatted f3")
1062 unstyle(str(report)),
1063 "2 files reformatted, 2 files left unchanged, 1 file failed to"
1066 self.assertEqual(report.return_code, 123)
1067 report.failed(Path("e2"), "boom")
1068 self.assertEqual(len(out_lines), 2)
1069 self.assertEqual(len(err_lines), 2)
1070 self.assertEqual(err_lines[-1], "error: cannot format e2: boom")
1072 unstyle(str(report)),
1073 "2 files reformatted, 2 files left unchanged, 2 files failed to"
1076 self.assertEqual(report.return_code, 123)
1077 report.path_ignored(Path("wat"), "no match")
1078 self.assertEqual(len(out_lines), 2)
1079 self.assertEqual(len(err_lines), 2)
1081 unstyle(str(report)),
1082 "2 files reformatted, 2 files left unchanged, 2 files failed to"
1085 self.assertEqual(report.return_code, 123)
1086 report.done(Path("f4"), black.Changed.NO)
1087 self.assertEqual(len(out_lines), 2)
1088 self.assertEqual(len(err_lines), 2)
1090 unstyle(str(report)),
1091 "2 files reformatted, 3 files left unchanged, 2 files failed to"
1094 self.assertEqual(report.return_code, 123)
1097 unstyle(str(report)),
1098 "2 files would be reformatted, 3 files would be left unchanged, 2 files"
1099 " would fail to reformat.",
1101 report.check = False
1104 unstyle(str(report)),
1105 "2 files would be reformatted, 3 files would be left unchanged, 2 files"
1106 " would fail to reformat.",
1109 def test_lib2to3_parse(self) -> None:
1110 with self.assertRaises(black.InvalidInput):
1111 black.lib2to3_parse("invalid syntax")
1113 straddling = "x + y"
1114 black.lib2to3_parse(straddling)
1115 black.lib2to3_parse(straddling, {TargetVersion.PY27})
1116 black.lib2to3_parse(straddling, {TargetVersion.PY36})
1117 black.lib2to3_parse(straddling, {TargetVersion.PY27, TargetVersion.PY36})
1119 py2_only = "print x"
1120 black.lib2to3_parse(py2_only)
1121 black.lib2to3_parse(py2_only, {TargetVersion.PY27})
1122 with self.assertRaises(black.InvalidInput):
1123 black.lib2to3_parse(py2_only, {TargetVersion.PY36})
1124 with self.assertRaises(black.InvalidInput):
1125 black.lib2to3_parse(py2_only, {TargetVersion.PY27, TargetVersion.PY36})
1127 py3_only = "exec(x, end=y)"
1128 black.lib2to3_parse(py3_only)
1129 with self.assertRaises(black.InvalidInput):
1130 black.lib2to3_parse(py3_only, {TargetVersion.PY27})
1131 black.lib2to3_parse(py3_only, {TargetVersion.PY36})
1132 black.lib2to3_parse(py3_only, {TargetVersion.PY27, TargetVersion.PY36})
1134 def test_get_features_used(self) -> None:
1135 node = black.lib2to3_parse("def f(*, arg): ...\n")
1136 self.assertEqual(black.get_features_used(node), set())
1137 node = black.lib2to3_parse("def f(*, arg,): ...\n")
1138 self.assertEqual(black.get_features_used(node), {Feature.TRAILING_COMMA_IN_DEF})
1139 node = black.lib2to3_parse("f(*arg,)\n")
1141 black.get_features_used(node), {Feature.TRAILING_COMMA_IN_CALL}
1143 node = black.lib2to3_parse("def f(*, arg): f'string'\n")
1144 self.assertEqual(black.get_features_used(node), {Feature.F_STRINGS})
1145 node = black.lib2to3_parse("123_456\n")
1146 self.assertEqual(black.get_features_used(node), {Feature.NUMERIC_UNDERSCORES})
1147 node = black.lib2to3_parse("123456\n")
1148 self.assertEqual(black.get_features_used(node), set())
1149 source, expected = read_data("function")
1150 node = black.lib2to3_parse(source)
1151 expected_features = {
1152 Feature.TRAILING_COMMA_IN_CALL,
1153 Feature.TRAILING_COMMA_IN_DEF,
1156 self.assertEqual(black.get_features_used(node), expected_features)
1157 node = black.lib2to3_parse(expected)
1158 self.assertEqual(black.get_features_used(node), expected_features)
1159 source, expected = read_data("expression")
1160 node = black.lib2to3_parse(source)
1161 self.assertEqual(black.get_features_used(node), set())
1162 node = black.lib2to3_parse(expected)
1163 self.assertEqual(black.get_features_used(node), set())
1165 def test_get_future_imports(self) -> None:
1166 node = black.lib2to3_parse("\n")
1167 self.assertEqual(set(), black.get_future_imports(node))
1168 node = black.lib2to3_parse("from __future__ import black\n")
1169 self.assertEqual({"black"}, black.get_future_imports(node))
1170 node = black.lib2to3_parse("from __future__ import multiple, imports\n")
1171 self.assertEqual({"multiple", "imports"}, black.get_future_imports(node))
1172 node = black.lib2to3_parse("from __future__ import (parenthesized, imports)\n")
1173 self.assertEqual({"parenthesized", "imports"}, black.get_future_imports(node))
1174 node = black.lib2to3_parse(
1175 "from __future__ import multiple\nfrom __future__ import imports\n"
1177 self.assertEqual({"multiple", "imports"}, black.get_future_imports(node))
1178 node = black.lib2to3_parse("# comment\nfrom __future__ import black\n")
1179 self.assertEqual({"black"}, black.get_future_imports(node))
1180 node = black.lib2to3_parse('"""docstring"""\nfrom __future__ import black\n')
1181 self.assertEqual({"black"}, black.get_future_imports(node))
1182 node = black.lib2to3_parse("some(other, code)\nfrom __future__ import black\n")
1183 self.assertEqual(set(), black.get_future_imports(node))
1184 node = black.lib2to3_parse("from some.module import black\n")
1185 self.assertEqual(set(), black.get_future_imports(node))
1186 node = black.lib2to3_parse(
1187 "from __future__ import unicode_literals as _unicode_literals"
1189 self.assertEqual({"unicode_literals"}, black.get_future_imports(node))
1190 node = black.lib2to3_parse(
1191 "from __future__ import unicode_literals as _lol, print"
1193 self.assertEqual({"unicode_literals", "print"}, black.get_future_imports(node))
1195 def test_debug_visitor(self) -> None:
1196 source, _ = read_data("debug_visitor.py")
1197 expected, _ = read_data("debug_visitor.out")
1201 def out(msg: str, **kwargs: Any) -> None:
1202 out_lines.append(msg)
1204 def err(msg: str, **kwargs: Any) -> None:
1205 err_lines.append(msg)
1207 with patch("black.out", out), patch("black.err", err):
1208 black.DebugVisitor.show(source)
1209 actual = "\n".join(out_lines) + "\n"
1211 if expected != actual:
1212 log_name = black.dump_to_file(*out_lines)
1216 f"AST print out is different. Actual version dumped to {log_name}",
1219 def test_format_file_contents(self) -> None:
1221 mode = black.FileMode()
1222 with self.assertRaises(black.NothingChanged):
1223 black.format_file_contents(empty, mode=mode, fast=False)
1225 with self.assertRaises(black.NothingChanged):
1226 black.format_file_contents(just_nl, mode=mode, fast=False)
1227 same = "j = [1, 2, 3]\n"
1228 with self.assertRaises(black.NothingChanged):
1229 black.format_file_contents(same, mode=mode, fast=False)
1230 different = "j = [1,2,3]"
1232 actual = black.format_file_contents(different, mode=mode, fast=False)
1233 self.assertEqual(expected, actual)
1234 invalid = "return if you can"
1235 with self.assertRaises(black.InvalidInput) as e:
1236 black.format_file_contents(invalid, mode=mode, fast=False)
1237 self.assertEqual(str(e.exception), "Cannot parse: 1:7: return if you can")
1239 def test_endmarker(self) -> None:
1240 n = black.lib2to3_parse("\n")
1241 self.assertEqual(n.type, black.syms.file_input)
1242 self.assertEqual(len(n.children), 1)
1243 self.assertEqual(n.children[0].type, black.token.ENDMARKER)
1245 @unittest.skipIf(os.environ.get("SKIP_AST_PRINT"), "user set SKIP_AST_PRINT")
1246 def test_assertFormatEqual(self) -> None:
1250 def out(msg: str, **kwargs: Any) -> None:
1251 out_lines.append(msg)
1253 def err(msg: str, **kwargs: Any) -> None:
1254 err_lines.append(msg)
1256 with patch("black.out", out), patch("black.err", err):
1257 with self.assertRaises(AssertionError):
1258 self.assertFormatEqual("j = [1, 2, 3]", "j = [1, 2, 3,]")
1260 out_str = "".join(out_lines)
1261 self.assertTrue("Expected tree:" in out_str)
1262 self.assertTrue("Actual tree:" in out_str)
1263 self.assertEqual("".join(err_lines), "")
1265 def test_cache_broken_file(self) -> None:
1266 mode = black.FileMode()
1267 with cache_dir() as workspace:
1268 cache_file = black.get_cache_file(mode)
1269 with cache_file.open("w") as fobj:
1270 fobj.write("this is not a pickle")
1271 self.assertEqual(black.read_cache(mode), {})
1272 src = (workspace / "test.py").resolve()
1273 with src.open("w") as fobj:
1274 fobj.write("print('hello')")
1275 self.invokeBlack([str(src)])
1276 cache = black.read_cache(mode)
1277 self.assertIn(src, cache)
1279 def test_cache_single_file_already_cached(self) -> None:
1280 mode = black.FileMode()
1281 with cache_dir() as workspace:
1282 src = (workspace / "test.py").resolve()
1283 with src.open("w") as fobj:
1284 fobj.write("print('hello')")
1285 black.write_cache({}, [src], mode)
1286 self.invokeBlack([str(src)])
1287 with src.open("r") as fobj:
1288 self.assertEqual(fobj.read(), "print('hello')")
1291 def test_cache_multiple_files(self) -> None:
1292 mode = black.FileMode()
1293 with cache_dir() as workspace, patch(
1294 "black.ProcessPoolExecutor", new=ThreadPoolExecutor
1296 one = (workspace / "one.py").resolve()
1297 with one.open("w") as fobj:
1298 fobj.write("print('hello')")
1299 two = (workspace / "two.py").resolve()
1300 with two.open("w") as fobj:
1301 fobj.write("print('hello')")
1302 black.write_cache({}, [one], mode)
1303 self.invokeBlack([str(workspace)])
1304 with one.open("r") as fobj:
1305 self.assertEqual(fobj.read(), "print('hello')")
1306 with two.open("r") as fobj:
1307 self.assertEqual(fobj.read(), 'print("hello")\n')
1308 cache = black.read_cache(mode)
1309 self.assertIn(one, cache)
1310 self.assertIn(two, cache)
1312 def test_no_cache_when_writeback_diff(self) -> None:
1313 mode = black.FileMode()
1314 with cache_dir() as workspace:
1315 src = (workspace / "test.py").resolve()
1316 with src.open("w") as fobj:
1317 fobj.write("print('hello')")
1318 self.invokeBlack([str(src), "--diff"])
1319 cache_file = black.get_cache_file(mode)
1320 self.assertFalse(cache_file.exists())
1322 def test_no_cache_when_stdin(self) -> None:
1323 mode = black.FileMode()
1325 result = CliRunner().invoke(
1326 black.main, ["-"], input=BytesIO(b"print('hello')")
1328 self.assertEqual(result.exit_code, 0)
1329 cache_file = black.get_cache_file(mode)
1330 self.assertFalse(cache_file.exists())
1332 def test_read_cache_no_cachefile(self) -> None:
1333 mode = black.FileMode()
1335 self.assertEqual(black.read_cache(mode), {})
1337 def test_write_cache_read_cache(self) -> None:
1338 mode = black.FileMode()
1339 with cache_dir() as workspace:
1340 src = (workspace / "test.py").resolve()
1342 black.write_cache({}, [src], mode)
1343 cache = black.read_cache(mode)
1344 self.assertIn(src, cache)
1345 self.assertEqual(cache[src], black.get_cache_info(src))
1347 def test_filter_cached(self) -> None:
1348 with TemporaryDirectory() as workspace:
1349 path = Path(workspace)
1350 uncached = (path / "uncached").resolve()
1351 cached = (path / "cached").resolve()
1352 cached_but_changed = (path / "changed").resolve()
1355 cached_but_changed.touch()
1356 cache = {cached: black.get_cache_info(cached), cached_but_changed: (0.0, 0)}
1357 todo, done = black.filter_cached(
1358 cache, {uncached, cached, cached_but_changed}
1360 self.assertEqual(todo, {uncached, cached_but_changed})
1361 self.assertEqual(done, {cached})
1363 def test_write_cache_creates_directory_if_needed(self) -> None:
1364 mode = black.FileMode()
1365 with cache_dir(exists=False) as workspace:
1366 self.assertFalse(workspace.exists())
1367 black.write_cache({}, [], mode)
1368 self.assertTrue(workspace.exists())
1371 def test_failed_formatting_does_not_get_cached(self) -> None:
1372 mode = black.FileMode()
1373 with cache_dir() as workspace, patch(
1374 "black.ProcessPoolExecutor", new=ThreadPoolExecutor
1376 failing = (workspace / "failing.py").resolve()
1377 with failing.open("w") as fobj:
1378 fobj.write("not actually python")
1379 clean = (workspace / "clean.py").resolve()
1380 with clean.open("w") as fobj:
1381 fobj.write('print("hello")\n')
1382 self.invokeBlack([str(workspace)], exit_code=123)
1383 cache = black.read_cache(mode)
1384 self.assertNotIn(failing, cache)
1385 self.assertIn(clean, cache)
1387 def test_write_cache_write_fail(self) -> None:
1388 mode = black.FileMode()
1389 with cache_dir(), patch.object(Path, "open") as mock:
1390 mock.side_effect = OSError
1391 black.write_cache({}, [], mode)
1394 @patch("black.ProcessPoolExecutor", MagicMock(side_effect=OSError))
1395 def test_works_in_mono_process_only_environment(self) -> None:
1396 with cache_dir() as workspace:
1398 (workspace / "one.py").resolve(),
1399 (workspace / "two.py").resolve(),
1401 f.write_text('print("hello")\n')
1402 self.invokeBlack([str(workspace)])
1405 def test_check_diff_use_together(self) -> None:
1407 # Files which will be reformatted.
1408 src1 = (THIS_DIR / "data" / "string_quotes.py").resolve()
1409 self.invokeBlack([str(src1), "--diff", "--check"], exit_code=1)
1410 # Files which will not be reformatted.
1411 src2 = (THIS_DIR / "data" / "composition.py").resolve()
1412 self.invokeBlack([str(src2), "--diff", "--check"])
1413 # Multi file command.
1414 self.invokeBlack([str(src1), str(src2), "--diff", "--check"], exit_code=1)
1416 def test_no_files(self) -> None:
1418 # Without an argument, black exits with error code 0.
1419 self.invokeBlack([])
1421 def test_broken_symlink(self) -> None:
1422 with cache_dir() as workspace:
1423 symlink = workspace / "broken_link.py"
1425 symlink.symlink_to("nonexistent.py")
1426 except OSError as e:
1427 self.skipTest(f"Can't create symlinks: {e}")
1428 self.invokeBlack([str(workspace.resolve())])
1430 def test_read_cache_line_lengths(self) -> None:
1431 mode = black.FileMode()
1432 short_mode = black.FileMode(line_length=1)
1433 with cache_dir() as workspace:
1434 path = (workspace / "file.py").resolve()
1436 black.write_cache({}, [path], mode)
1437 one = black.read_cache(mode)
1438 self.assertIn(path, one)
1439 two = black.read_cache(short_mode)
1440 self.assertNotIn(path, two)
1442 def test_tricky_unicode_symbols(self) -> None:
1443 source, expected = read_data("tricky_unicode_symbols")
1445 self.assertFormatEqual(expected, actual)
1446 black.assert_equivalent(source, actual)
1447 black.assert_stable(source, actual, black.FileMode())
1449 def test_single_file_force_pyi(self) -> None:
1450 reg_mode = black.FileMode()
1451 pyi_mode = black.FileMode(is_pyi=True)
1452 contents, expected = read_data("force_pyi")
1453 with cache_dir() as workspace:
1454 path = (workspace / "file.py").resolve()
1455 with open(path, "w") as fh:
1457 self.invokeBlack([str(path), "--pyi"])
1458 with open(path, "r") as fh:
1460 # verify cache with --pyi is separate
1461 pyi_cache = black.read_cache(pyi_mode)
1462 self.assertIn(path, pyi_cache)
1463 normal_cache = black.read_cache(reg_mode)
1464 self.assertNotIn(path, normal_cache)
1465 self.assertEqual(actual, expected)
1468 def test_multi_file_force_pyi(self) -> None:
1469 reg_mode = black.FileMode()
1470 pyi_mode = black.FileMode(is_pyi=True)
1471 contents, expected = read_data("force_pyi")
1472 with cache_dir() as workspace:
1474 (workspace / "file1.py").resolve(),
1475 (workspace / "file2.py").resolve(),
1478 with open(path, "w") as fh:
1480 self.invokeBlack([str(p) for p in paths] + ["--pyi"])
1482 with open(path, "r") as fh:
1484 self.assertEqual(actual, expected)
1485 # verify cache with --pyi is separate
1486 pyi_cache = black.read_cache(pyi_mode)
1487 normal_cache = black.read_cache(reg_mode)
1489 self.assertIn(path, pyi_cache)
1490 self.assertNotIn(path, normal_cache)
1492 def test_pipe_force_pyi(self) -> None:
1493 source, expected = read_data("force_pyi")
1494 result = CliRunner().invoke(
1495 black.main, ["-", "-q", "--pyi"], input=BytesIO(source.encode("utf8"))
1497 self.assertEqual(result.exit_code, 0)
1498 actual = result.output
1499 self.assertFormatEqual(actual, expected)
1501 def test_single_file_force_py36(self) -> None:
1502 reg_mode = black.FileMode()
1503 py36_mode = black.FileMode(target_versions=black.PY36_VERSIONS)
1504 source, expected = read_data("force_py36")
1505 with cache_dir() as workspace:
1506 path = (workspace / "file.py").resolve()
1507 with open(path, "w") as fh:
1509 self.invokeBlack([str(path), *PY36_ARGS])
1510 with open(path, "r") as fh:
1512 # verify cache with --target-version is separate
1513 py36_cache = black.read_cache(py36_mode)
1514 self.assertIn(path, py36_cache)
1515 normal_cache = black.read_cache(reg_mode)
1516 self.assertNotIn(path, normal_cache)
1517 self.assertEqual(actual, expected)
1520 def test_multi_file_force_py36(self) -> None:
1521 reg_mode = black.FileMode()
1522 py36_mode = black.FileMode(target_versions=black.PY36_VERSIONS)
1523 source, expected = read_data("force_py36")
1524 with cache_dir() as workspace:
1526 (workspace / "file1.py").resolve(),
1527 (workspace / "file2.py").resolve(),
1530 with open(path, "w") as fh:
1532 self.invokeBlack([str(p) for p in paths] + PY36_ARGS)
1534 with open(path, "r") as fh:
1536 self.assertEqual(actual, expected)
1537 # verify cache with --target-version is separate
1538 pyi_cache = black.read_cache(py36_mode)
1539 normal_cache = black.read_cache(reg_mode)
1541 self.assertIn(path, pyi_cache)
1542 self.assertNotIn(path, normal_cache)
1544 def test_collections(self) -> None:
1545 source, expected = read_data("collections")
1547 self.assertFormatEqual(expected, actual)
1548 black.assert_equivalent(source, actual)
1549 black.assert_stable(source, actual, black.FileMode())
1551 def test_pipe_force_py36(self) -> None:
1552 source, expected = read_data("force_py36")
1553 result = CliRunner().invoke(
1555 ["-", "-q", "--target-version=py36"],
1556 input=BytesIO(source.encode("utf8")),
1558 self.assertEqual(result.exit_code, 0)
1559 actual = result.output
1560 self.assertFormatEqual(actual, expected)
1562 def test_include_exclude(self) -> None:
1563 path = THIS_DIR / "data" / "include_exclude_tests"
1564 include = re.compile(r"\.pyi?$")
1565 exclude = re.compile(r"/exclude/|/\.definitely_exclude/")
1566 report = black.Report()
1567 gitignore = PathSpec.from_lines("gitwildmatch", [])
1568 sources: List[Path] = []
1570 Path(path / "b/dont_exclude/a.py"),
1571 Path(path / "b/dont_exclude/a.pyi"),
1573 this_abs = THIS_DIR.resolve()
1575 black.gen_python_files(
1576 path.iterdir(), this_abs, include, exclude, None, report, gitignore
1579 self.assertEqual(sorted(expected), sorted(sources))
1581 @patch("black.find_project_root", lambda *args: THIS_DIR.resolve())
1582 def test_exclude_for_issue_1572(self) -> None:
1583 # Exclude shouldn't touch files that were explicitly given to Black through the
1584 # CLI. Exclude is supposed to only apply to the recursive discovery of files.
1585 # https://github.com/psf/black/issues/1572
1586 path = THIS_DIR / "data" / "include_exclude_tests"
1588 exclude = r"/exclude/|a\.py"
1589 src = str(path / "b/exclude/a.py")
1590 report = black.Report()
1591 expected = [Path(path / "b/exclude/a.py")]
1604 self.assertEqual(sorted(expected), sorted(sources))
1606 def test_gitignore_exclude(self) -> None:
1607 path = THIS_DIR / "data" / "include_exclude_tests"
1608 include = re.compile(r"\.pyi?$")
1609 exclude = re.compile(r"")
1610 report = black.Report()
1611 gitignore = PathSpec.from_lines(
1612 "gitwildmatch", ["exclude/", ".definitely_exclude"]
1614 sources: List[Path] = []
1616 Path(path / "b/dont_exclude/a.py"),
1617 Path(path / "b/dont_exclude/a.pyi"),
1619 this_abs = THIS_DIR.resolve()
1621 black.gen_python_files(
1622 path.iterdir(), this_abs, include, exclude, None, report, gitignore
1625 self.assertEqual(sorted(expected), sorted(sources))
1627 def test_empty_include(self) -> None:
1628 path = THIS_DIR / "data" / "include_exclude_tests"
1629 report = black.Report()
1630 gitignore = PathSpec.from_lines("gitwildmatch", [])
1631 empty = re.compile(r"")
1632 sources: List[Path] = []
1634 Path(path / "b/exclude/a.pie"),
1635 Path(path / "b/exclude/a.py"),
1636 Path(path / "b/exclude/a.pyi"),
1637 Path(path / "b/dont_exclude/a.pie"),
1638 Path(path / "b/dont_exclude/a.py"),
1639 Path(path / "b/dont_exclude/a.pyi"),
1640 Path(path / "b/.definitely_exclude/a.pie"),
1641 Path(path / "b/.definitely_exclude/a.py"),
1642 Path(path / "b/.definitely_exclude/a.pyi"),
1644 this_abs = THIS_DIR.resolve()
1646 black.gen_python_files(
1650 re.compile(black.DEFAULT_EXCLUDES),
1656 self.assertEqual(sorted(expected), sorted(sources))
1658 def test_empty_exclude(self) -> None:
1659 path = THIS_DIR / "data" / "include_exclude_tests"
1660 report = black.Report()
1661 gitignore = PathSpec.from_lines("gitwildmatch", [])
1662 empty = re.compile(r"")
1663 sources: List[Path] = []
1665 Path(path / "b/dont_exclude/a.py"),
1666 Path(path / "b/dont_exclude/a.pyi"),
1667 Path(path / "b/exclude/a.py"),
1668 Path(path / "b/exclude/a.pyi"),
1669 Path(path / "b/.definitely_exclude/a.py"),
1670 Path(path / "b/.definitely_exclude/a.pyi"),
1672 this_abs = THIS_DIR.resolve()
1674 black.gen_python_files(
1677 re.compile(black.DEFAULT_INCLUDES),
1684 self.assertEqual(sorted(expected), sorted(sources))
1686 def test_invalid_include_exclude(self) -> None:
1687 for option in ["--include", "--exclude"]:
1688 self.invokeBlack(["-", option, "**()(!!*)"], exit_code=2)
1690 def test_preserves_line_endings(self) -> None:
1691 with TemporaryDirectory() as workspace:
1692 test_file = Path(workspace) / "test.py"
1693 for nl in ["\n", "\r\n"]:
1694 contents = nl.join(["def f( ):", " pass"])
1695 test_file.write_bytes(contents.encode())
1696 ff(test_file, write_back=black.WriteBack.YES)
1697 updated_contents: bytes = test_file.read_bytes()
1698 self.assertIn(nl.encode(), updated_contents)
1700 self.assertNotIn(b"\r\n", updated_contents)
1702 def test_preserves_line_endings_via_stdin(self) -> None:
1703 for nl in ["\n", "\r\n"]:
1704 contents = nl.join(["def f( ):", " pass"])
1705 runner = BlackRunner()
1706 result = runner.invoke(
1707 black.main, ["-", "--fast"], input=BytesIO(contents.encode("utf8"))
1709 self.assertEqual(result.exit_code, 0)
1710 output = runner.stdout_bytes
1711 self.assertIn(nl.encode("utf8"), output)
1713 self.assertNotIn(b"\r\n", output)
1715 def test_assert_equivalent_different_asts(self) -> None:
1716 with self.assertRaises(AssertionError):
1717 black.assert_equivalent("{}", "None")
1719 def test_symlink_out_of_root_directory(self) -> None:
1721 root = THIS_DIR.resolve()
1723 include = re.compile(black.DEFAULT_INCLUDES)
1724 exclude = re.compile(black.DEFAULT_EXCLUDES)
1725 report = black.Report()
1726 gitignore = PathSpec.from_lines("gitwildmatch", [])
1727 # `child` should behave like a symlink which resolved path is clearly
1728 # outside of the `root` directory.
1729 path.iterdir.return_value = [child]
1730 child.resolve.return_value = Path("/a/b/c")
1731 child.as_posix.return_value = "/a/b/c"
1732 child.is_symlink.return_value = True
1735 black.gen_python_files(
1736 path.iterdir(), root, include, exclude, None, report, gitignore
1739 except ValueError as ve:
1740 self.fail(f"`get_python_files_in_dir()` failed: {ve}")
1741 path.iterdir.assert_called_once()
1742 child.resolve.assert_called_once()
1743 child.is_symlink.assert_called_once()
1744 # `child` should behave like a strange file which resolved path is clearly
1745 # outside of the `root` directory.
1746 child.is_symlink.return_value = False
1747 with self.assertRaises(ValueError):
1749 black.gen_python_files(
1750 path.iterdir(), root, include, exclude, None, report, gitignore
1753 path.iterdir.assert_called()
1754 self.assertEqual(path.iterdir.call_count, 2)
1755 child.resolve.assert_called()
1756 self.assertEqual(child.resolve.call_count, 2)
1757 child.is_symlink.assert_called()
1758 self.assertEqual(child.is_symlink.call_count, 2)
1760 def test_shhh_click(self) -> None:
1762 from click import _unicodefun # type: ignore
1763 except ModuleNotFoundError:
1764 self.skipTest("Incompatible Click version")
1765 if not hasattr(_unicodefun, "_verify_python3_env"):
1766 self.skipTest("Incompatible Click version")
1767 # First, let's see if Click is crashing with a preferred ASCII charset.
1768 with patch("locale.getpreferredencoding") as gpe:
1769 gpe.return_value = "ASCII"
1770 with self.assertRaises(RuntimeError):
1771 _unicodefun._verify_python3_env()
1772 # Now, let's silence Click...
1774 # ...and confirm it's silent.
1775 with patch("locale.getpreferredencoding") as gpe:
1776 gpe.return_value = "ASCII"
1778 _unicodefun._verify_python3_env()
1779 except RuntimeError as re:
1780 self.fail(f"`patch_click()` failed, exception still raised: {re}")
1782 def test_root_logger_not_used_directly(self) -> None:
1783 def fail(*args: Any, **kwargs: Any) -> None:
1784 self.fail("Record created with root logger")
1786 with patch.multiple(
1797 @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
1798 def test_blackd_main(self) -> None:
1799 with patch("blackd.web.run_app"):
1800 result = CliRunner().invoke(blackd.main, [])
1801 if result.exception is not None:
1802 raise result.exception
1803 self.assertEqual(result.exit_code, 0)
1805 def test_invalid_config_return_code(self) -> None:
1806 tmp_file = Path(black.dump_to_file())
1808 tmp_config = Path(black.dump_to_file())
1810 args = ["--config", str(tmp_config), str(tmp_file)]
1811 self.invokeBlack(args, exit_code=2, ignore_config=False)
1815 def test_parse_pyproject_toml(self) -> None:
1816 test_toml_file = THIS_DIR / "test.toml"
1817 config = black.parse_pyproject_toml(str(test_toml_file))
1818 self.assertEqual(config["verbose"], 1)
1819 self.assertEqual(config["check"], "no")
1820 self.assertEqual(config["diff"], "y")
1821 self.assertEqual(config["color"], True)
1822 self.assertEqual(config["line_length"], 79)
1823 self.assertEqual(config["target_version"], ["py36", "py37", "py38"])
1824 self.assertEqual(config["exclude"], r"\.pyi?$")
1825 self.assertEqual(config["include"], r"\.py?$")
1827 def test_read_pyproject_toml(self) -> None:
1828 test_toml_file = THIS_DIR / "test.toml"
1829 fake_ctx = FakeContext()
1830 black.read_pyproject_toml(
1831 fake_ctx, FakeParameter(), str(test_toml_file),
1833 config = fake_ctx.default_map
1834 self.assertEqual(config["verbose"], "1")
1835 self.assertEqual(config["check"], "no")
1836 self.assertEqual(config["diff"], "y")
1837 self.assertEqual(config["color"], "True")
1838 self.assertEqual(config["line_length"], "79")
1839 self.assertEqual(config["target_version"], ["py36", "py37", "py38"])
1840 self.assertEqual(config["exclude"], r"\.pyi?$")
1841 self.assertEqual(config["include"], r"\.py?$")
1843 def test_find_project_root(self) -> None:
1844 with TemporaryDirectory() as workspace:
1845 root = Path(workspace)
1846 test_dir = root / "test"
1849 src_dir = root / "src"
1852 root_pyproject = root / "pyproject.toml"
1853 root_pyproject.touch()
1854 src_pyproject = src_dir / "pyproject.toml"
1855 src_pyproject.touch()
1856 src_python = src_dir / "foo.py"
1860 black.find_project_root((src_dir, test_dir)), root.resolve()
1862 self.assertEqual(black.find_project_root((src_dir,)), src_dir.resolve())
1863 self.assertEqual(black.find_project_root((src_python,)), src_dir.resolve())
1866 class BlackDTestCase(AioHTTPTestCase):
1867 async def get_application(self) -> web.Application:
1868 return blackd.make_app()
1870 # TODO: remove these decorators once the below is released
1871 # https://github.com/aio-libs/aiohttp/pull/3727
1872 @skip_if_exception("ClientOSError")
1873 @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
1875 async def test_blackd_request_needs_formatting(self) -> None:
1876 response = await self.client.post("/", data=b"print('hello world')")
1877 self.assertEqual(response.status, 200)
1878 self.assertEqual(response.charset, "utf8")
1879 self.assertEqual(await response.read(), b'print("hello world")\n')
1881 @skip_if_exception("ClientOSError")
1882 @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
1884 async def test_blackd_request_no_change(self) -> None:
1885 response = await self.client.post("/", data=b'print("hello world")\n')
1886 self.assertEqual(response.status, 204)
1887 self.assertEqual(await response.read(), b"")
1889 @skip_if_exception("ClientOSError")
1890 @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
1892 async def test_blackd_request_syntax_error(self) -> None:
1893 response = await self.client.post("/", data=b"what even ( is")
1894 self.assertEqual(response.status, 400)
1895 content = await response.text()
1897 content.startswith("Cannot parse"),
1898 msg=f"Expected error to start with 'Cannot parse', got {repr(content)}",
1901 @skip_if_exception("ClientOSError")
1902 @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
1904 async def test_blackd_unsupported_version(self) -> None:
1905 response = await self.client.post(
1906 "/", data=b"what", headers={blackd.PROTOCOL_VERSION_HEADER: "2"}
1908 self.assertEqual(response.status, 501)
1910 @skip_if_exception("ClientOSError")
1911 @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
1913 async def test_blackd_supported_version(self) -> None:
1914 response = await self.client.post(
1915 "/", data=b"what", headers={blackd.PROTOCOL_VERSION_HEADER: "1"}
1917 self.assertEqual(response.status, 200)
1919 @skip_if_exception("ClientOSError")
1920 @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
1922 async def test_blackd_invalid_python_variant(self) -> None:
1923 async def check(header_value: str, expected_status: int = 400) -> None:
1924 response = await self.client.post(
1925 "/", data=b"what", headers={blackd.PYTHON_VARIANT_HEADER: header_value}
1927 self.assertEqual(response.status, expected_status)
1930 await check("ruby3.5")
1931 await check("pyi3.6")
1932 await check("py1.5")
1934 await check("py2.8")
1936 await check("pypy3.0")
1937 await check("jython3.4")
1939 @skip_if_exception("ClientOSError")
1940 @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
1942 async def test_blackd_pyi(self) -> None:
1943 source, expected = read_data("stub.pyi")
1944 response = await self.client.post(
1945 "/", data=source, headers={blackd.PYTHON_VARIANT_HEADER: "pyi"}
1947 self.assertEqual(response.status, 200)
1948 self.assertEqual(await response.text(), expected)
1950 @skip_if_exception("ClientOSError")
1951 @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
1953 async def test_blackd_diff(self) -> None:
1954 diff_header = re.compile(
1955 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"
1958 source, _ = read_data("blackd_diff.py")
1959 expected, _ = read_data("blackd_diff.diff")
1961 response = await self.client.post(
1962 "/", data=source, headers={blackd.DIFF_HEADER: "true"}
1964 self.assertEqual(response.status, 200)
1966 actual = await response.text()
1967 actual = diff_header.sub(DETERMINISTIC_HEADER, actual)
1968 self.assertEqual(actual, expected)
1970 @skip_if_exception("ClientOSError")
1971 @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
1973 async def test_blackd_python_variant(self) -> None:
1976 " and_has_a_bunch_of,\n"
1977 " very_long_arguments_too,\n"
1978 " and_lots_of_them_as_well_lol,\n"
1979 " **and_very_long_keyword_arguments\n"
1984 async def check(header_value: str, expected_status: int) -> None:
1985 response = await self.client.post(
1986 "/", data=code, headers={blackd.PYTHON_VARIANT_HEADER: header_value}
1989 response.status, expected_status, msg=await response.text()
1992 await check("3.6", 200)
1993 await check("py3.6", 200)
1994 await check("3.6,3.7", 200)
1995 await check("3.6,py3.7", 200)
1996 await check("py36,py37", 200)
1997 await check("36", 200)
1998 await check("3.6.4", 200)
2000 await check("2", 204)
2001 await check("2.7", 204)
2002 await check("py2.7", 204)
2003 await check("3.4", 204)
2004 await check("py3.4", 204)
2005 await check("py34,py36", 204)
2006 await check("34", 204)
2008 @skip_if_exception("ClientOSError")
2009 @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
2011 async def test_blackd_line_length(self) -> None:
2012 response = await self.client.post(
2013 "/", data=b'print("hello")\n', headers={blackd.LINE_LENGTH_HEADER: "7"}
2015 self.assertEqual(response.status, 200)
2017 @skip_if_exception("ClientOSError")
2018 @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
2020 async def test_blackd_invalid_line_length(self) -> None:
2021 response = await self.client.post(
2022 "/", data=b'print("hello")\n', headers={blackd.LINE_LENGTH_HEADER: "NaN"}
2024 self.assertEqual(response.status, 400)
2026 @skip_if_exception("ClientOSError")
2027 @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
2029 async def test_blackd_response_black_version_header(self) -> None:
2030 response = await self.client.post("/")
2031 self.assertIsNotNone(response.headers.get(blackd.BLACK_VERSION_HEADER))
2034 if __name__ == "__main__":
2035 unittest.main(module="test_black")