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 dataclasses import replace
7 from functools import partial
8 from io import BytesIO, TextIOWrapper
10 from pathlib import Path
13 from tempfile import TemporaryDirectory
14 from typing import Any, BinaryIO, Dict, Generator, List, Tuple, Iterator, TypeVar
16 from unittest.mock import patch, MagicMock
19 from click import unstyle
20 from click.testing import CliRunner
23 from black import Feature, TargetVersion
27 from aiohttp.test_utils import AioHTTPTestCase, unittest_run_loop
28 from aiohttp import web
30 has_blackd_deps = False
32 has_blackd_deps = True
34 from pathspec import PathSpec
36 # Import other test classes
37 from .test_primer import PrimerCLITests # noqa: F401
40 DEFAULT_MODE = black.FileMode(experimental_string_processing=True)
41 ff = partial(black.format_file_in_place, mode=DEFAULT_MODE, fast=True)
42 fs = partial(black.format_str, mode=DEFAULT_MODE)
43 THIS_FILE = Path(__file__)
44 THIS_DIR = THIS_FILE.parent
45 PROJECT_ROOT = THIS_DIR.parent
46 DETERMINISTIC_HEADER = "[Deterministic header]"
47 EMPTY_LINE = "# EMPTY LINE WITH WHITESPACE" + " (this comment will be removed)"
49 f"--target-version={version.name.lower()}" for version in black.PY36_VERSIONS
55 def dump_to_stderr(*output: str) -> str:
56 return "\n" + "\n".join(output) + "\n"
59 def read_data(name: str, data: bool = True) -> Tuple[str, str]:
60 """read_data('test_name') -> 'input', 'output'"""
61 if not name.endswith((".py", ".pyi", ".out", ".diff")):
63 _input: List[str] = []
64 _output: List[str] = []
65 base_dir = THIS_DIR / "data" if data else PROJECT_ROOT
66 with open(base_dir / name, "r", encoding="utf8") as test:
67 lines = test.readlines()
70 line = line.replace(EMPTY_LINE, "")
71 if line.rstrip() == "# output":
76 if _input and not _output:
77 # If there's no output marker, treat the entire file as already pre-formatted.
79 return "".join(_input).strip() + "\n", "".join(_output).strip() + "\n"
83 def cache_dir(exists: bool = True) -> Iterator[Path]:
84 with TemporaryDirectory() as workspace:
85 cache_dir = Path(workspace)
87 cache_dir = cache_dir / "new"
88 with patch("black.CACHE_DIR", cache_dir):
93 def event_loop() -> Iterator[None]:
94 policy = asyncio.get_event_loop_policy()
95 loop = policy.new_event_loop()
96 asyncio.set_event_loop(loop)
105 def skip_if_exception(e: str) -> Iterator[None]:
108 except Exception as exc:
109 if exc.__class__.__name__ == e:
110 unittest.skip(f"Encountered expected exception {exc}, skipping")
115 class FakeContext(click.Context):
116 """A fake click Context for when calling functions that need it."""
118 def __init__(self) -> None:
119 self.default_map: Dict[str, Any] = {}
122 class FakeParameter(click.Parameter):
123 """A fake click Parameter for when calling functions that need it."""
125 def __init__(self) -> None:
129 class BlackRunner(CliRunner):
130 """Modify CliRunner so that stderr is not merged with stdout.
132 This is a hack that can be removed once we depend on Click 7.x"""
134 def __init__(self) -> None:
135 self.stderrbuf = BytesIO()
136 self.stdoutbuf = BytesIO()
137 self.stdout_bytes = b""
138 self.stderr_bytes = b""
142 def isolation(self, *args: Any, **kwargs: Any) -> Generator[BinaryIO, None, None]:
143 with super().isolation(*args, **kwargs) as output:
145 hold_stderr = sys.stderr
146 sys.stderr = TextIOWrapper(self.stderrbuf, encoding=self.charset)
149 self.stdout_bytes = sys.stdout.buffer.getvalue() # type: ignore
150 self.stderr_bytes = sys.stderr.buffer.getvalue() # type: ignore
151 sys.stderr = hold_stderr
154 class BlackTestCase(unittest.TestCase):
157 def assertFormatEqual(self, expected: str, actual: str) -> None:
158 if actual != expected and not os.environ.get("SKIP_AST_PRINT"):
159 bdv: black.DebugVisitor[Any]
160 black.out("Expected tree:", fg="green")
162 exp_node = black.lib2to3_parse(expected)
163 bdv = black.DebugVisitor()
164 list(bdv.visit(exp_node))
165 except Exception as ve:
167 black.out("Actual tree:", fg="red")
169 exp_node = black.lib2to3_parse(actual)
170 bdv = black.DebugVisitor()
171 list(bdv.visit(exp_node))
172 except Exception as ve:
174 self.assertEqual(expected, actual)
177 self, args: List[str], exit_code: int = 0, ignore_config: bool = True
179 runner = BlackRunner()
181 args = ["--verbose", "--config", str(THIS_DIR / "empty.toml"), *args]
182 result = runner.invoke(black.main, args)
187 f"Failed with args: {args}\n"
188 f"stdout: {runner.stdout_bytes.decode()!r}\n"
189 f"stderr: {runner.stderr_bytes.decode()!r}\n"
190 f"exception: {result.exception}"
194 @patch("black.dump_to_file", dump_to_stderr)
195 def checkSourceFile(self, name: str, mode: black.FileMode = DEFAULT_MODE) -> None:
196 path = THIS_DIR.parent / name
197 source, expected = read_data(str(path), data=False)
198 actual = fs(source, mode=mode)
199 self.assertFormatEqual(expected, actual)
200 black.assert_equivalent(source, actual)
201 black.assert_stable(source, actual, mode)
202 self.assertFalse(ff(path))
204 @patch("black.dump_to_file", dump_to_stderr)
205 def test_empty(self) -> None:
206 source = expected = ""
208 self.assertFormatEqual(expected, actual)
209 black.assert_equivalent(source, actual)
210 black.assert_stable(source, actual, DEFAULT_MODE)
212 def test_empty_ff(self) -> None:
214 tmp_file = Path(black.dump_to_file())
216 self.assertFalse(ff(tmp_file, write_back=black.WriteBack.YES))
217 with open(tmp_file, encoding="utf8") as f:
221 self.assertFormatEqual(expected, actual)
223 def test_self(self) -> None:
224 self.checkSourceFile("tests/test_black.py")
226 def test_black(self) -> None:
227 self.checkSourceFile("src/black/__init__.py")
229 def test_pygram(self) -> None:
230 self.checkSourceFile("src/blib2to3/pygram.py")
232 def test_pytree(self) -> None:
233 self.checkSourceFile("src/blib2to3/pytree.py")
235 def test_conv(self) -> None:
236 self.checkSourceFile("src/blib2to3/pgen2/conv.py")
238 def test_driver(self) -> None:
239 self.checkSourceFile("src/blib2to3/pgen2/driver.py")
241 def test_grammar(self) -> None:
242 self.checkSourceFile("src/blib2to3/pgen2/grammar.py")
244 def test_literals(self) -> None:
245 self.checkSourceFile("src/blib2to3/pgen2/literals.py")
247 def test_parse(self) -> None:
248 self.checkSourceFile("src/blib2to3/pgen2/parse.py")
250 def test_pgen(self) -> None:
251 self.checkSourceFile("src/blib2to3/pgen2/pgen.py")
253 def test_tokenize(self) -> None:
254 self.checkSourceFile("src/blib2to3/pgen2/tokenize.py")
256 def test_token(self) -> None:
257 self.checkSourceFile("src/blib2to3/pgen2/token.py")
259 def test_setup(self) -> None:
260 self.checkSourceFile("setup.py")
262 def test_piping(self) -> None:
263 source, expected = read_data("src/black/__init__", data=False)
264 result = BlackRunner().invoke(
266 ["-", "--fast", f"--line-length={black.DEFAULT_LINE_LENGTH}"],
267 input=BytesIO(source.encode("utf8")),
269 self.assertEqual(result.exit_code, 0)
270 self.assertFormatEqual(expected, result.output)
271 black.assert_equivalent(source, result.output)
272 black.assert_stable(source, result.output, DEFAULT_MODE)
274 def test_piping_diff(self) -> None:
275 diff_header = re.compile(
276 r"(STDIN|STDOUT)\t\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d\.\d\d\d\d\d\d "
279 source, _ = read_data("expression.py")
280 expected, _ = read_data("expression.diff")
281 config = THIS_DIR / "data" / "empty_pyproject.toml"
285 f"--line-length={black.DEFAULT_LINE_LENGTH}",
287 f"--config={config}",
289 result = BlackRunner().invoke(
290 black.main, args, input=BytesIO(source.encode("utf8"))
292 self.assertEqual(result.exit_code, 0)
293 actual = diff_header.sub(DETERMINISTIC_HEADER, result.output)
294 actual = actual.rstrip() + "\n" # the diff output has a trailing space
295 self.assertEqual(expected, actual)
297 def test_piping_diff_with_color(self) -> None:
298 source, _ = read_data("expression.py")
299 config = THIS_DIR / "data" / "empty_pyproject.toml"
303 f"--line-length={black.DEFAULT_LINE_LENGTH}",
306 f"--config={config}",
308 result = BlackRunner().invoke(
309 black.main, args, input=BytesIO(source.encode("utf8"))
311 actual = result.output
312 # Again, the contents are checked in a different test, so only look for colors.
313 self.assertIn("\033[1;37m", actual)
314 self.assertIn("\033[36m", actual)
315 self.assertIn("\033[32m", actual)
316 self.assertIn("\033[31m", actual)
317 self.assertIn("\033[0m", actual)
319 @patch("black.dump_to_file", dump_to_stderr)
320 def test_function(self) -> None:
321 source, expected = read_data("function")
323 self.assertFormatEqual(expected, actual)
324 black.assert_equivalent(source, actual)
325 black.assert_stable(source, actual, DEFAULT_MODE)
327 @patch("black.dump_to_file", dump_to_stderr)
328 def test_function2(self) -> None:
329 source, expected = read_data("function2")
331 self.assertFormatEqual(expected, actual)
332 black.assert_equivalent(source, actual)
333 black.assert_stable(source, actual, DEFAULT_MODE)
335 @patch("black.dump_to_file", dump_to_stderr)
336 def test_function_trailing_comma(self) -> None:
337 source, expected = read_data("function_trailing_comma")
339 self.assertFormatEqual(expected, actual)
340 black.assert_equivalent(source, actual)
341 black.assert_stable(source, actual, DEFAULT_MODE)
343 @patch("black.dump_to_file", dump_to_stderr)
344 def test_expression(self) -> None:
345 source, expected = read_data("expression")
347 self.assertFormatEqual(expected, actual)
348 black.assert_equivalent(source, actual)
349 black.assert_stable(source, actual, DEFAULT_MODE)
351 @patch("black.dump_to_file", dump_to_stderr)
352 def test_pep_572(self) -> None:
353 source, expected = read_data("pep_572")
355 self.assertFormatEqual(expected, actual)
356 black.assert_stable(source, actual, DEFAULT_MODE)
357 if sys.version_info >= (3, 8):
358 black.assert_equivalent(source, actual)
360 def test_pep_572_version_detection(self) -> None:
361 source, _ = read_data("pep_572")
362 root = black.lib2to3_parse(source)
363 features = black.get_features_used(root)
364 self.assertIn(black.Feature.ASSIGNMENT_EXPRESSIONS, features)
365 versions = black.detect_target_versions(root)
366 self.assertIn(black.TargetVersion.PY38, versions)
368 def test_expression_ff(self) -> None:
369 source, expected = read_data("expression")
370 tmp_file = Path(black.dump_to_file(source))
372 self.assertTrue(ff(tmp_file, write_back=black.WriteBack.YES))
373 with open(tmp_file, encoding="utf8") as f:
377 self.assertFormatEqual(expected, actual)
378 with patch("black.dump_to_file", dump_to_stderr):
379 black.assert_equivalent(source, actual)
380 black.assert_stable(source, actual, DEFAULT_MODE)
382 def test_expression_diff(self) -> None:
383 source, _ = read_data("expression.py")
384 expected, _ = read_data("expression.diff")
385 tmp_file = Path(black.dump_to_file(source))
386 diff_header = re.compile(
387 rf"{re.escape(str(tmp_file))}\t\d\d\d\d-\d\d-\d\d "
388 r"\d\d:\d\d:\d\d\.\d\d\d\d\d\d \+\d\d\d\d"
391 result = BlackRunner().invoke(black.main, ["--diff", str(tmp_file)])
392 self.assertEqual(result.exit_code, 0)
395 actual = result.output
396 actual = diff_header.sub(DETERMINISTIC_HEADER, actual)
397 actual = actual.rstrip() + "\n" # the diff output has a trailing space
398 if expected != actual:
399 dump = black.dump_to_file(actual)
401 "Expected diff isn't equal to the actual. If you made changes to"
402 " expression.py and this is an anticipated difference, overwrite"
403 f" tests/data/expression.diff with {dump}"
405 self.assertEqual(expected, actual, msg)
407 def test_expression_diff_with_color(self) -> None:
408 source, _ = read_data("expression.py")
409 expected, _ = read_data("expression.diff")
410 tmp_file = Path(black.dump_to_file(source))
412 result = BlackRunner().invoke(
413 black.main, ["--diff", "--color", str(tmp_file)]
417 actual = result.output
418 # We check the contents of the diff in `test_expression_diff`. All
419 # we need to check here is that color codes exist in the result.
420 self.assertIn("\033[1;37m", actual)
421 self.assertIn("\033[36m", actual)
422 self.assertIn("\033[32m", actual)
423 self.assertIn("\033[31m", actual)
424 self.assertIn("\033[0m", actual)
426 @patch("black.dump_to_file", dump_to_stderr)
427 def test_fstring(self) -> None:
428 source, expected = read_data("fstring")
430 self.assertFormatEqual(expected, actual)
431 black.assert_equivalent(source, actual)
432 black.assert_stable(source, actual, DEFAULT_MODE)
434 @patch("black.dump_to_file", dump_to_stderr)
435 def test_pep_570(self) -> None:
436 source, expected = read_data("pep_570")
438 self.assertFormatEqual(expected, actual)
439 black.assert_stable(source, actual, DEFAULT_MODE)
440 if sys.version_info >= (3, 8):
441 black.assert_equivalent(source, actual)
443 def test_detect_pos_only_arguments(self) -> None:
444 source, _ = read_data("pep_570")
445 root = black.lib2to3_parse(source)
446 features = black.get_features_used(root)
447 self.assertIn(black.Feature.POS_ONLY_ARGUMENTS, features)
448 versions = black.detect_target_versions(root)
449 self.assertIn(black.TargetVersion.PY38, versions)
451 @patch("black.dump_to_file", dump_to_stderr)
452 def test_string_quotes(self) -> None:
453 source, expected = read_data("string_quotes")
455 self.assertFormatEqual(expected, actual)
456 black.assert_equivalent(source, actual)
457 black.assert_stable(source, actual, DEFAULT_MODE)
458 mode = replace(DEFAULT_MODE, string_normalization=False)
459 not_normalized = fs(source, mode=mode)
460 self.assertFormatEqual(source.replace("\\\n", ""), not_normalized)
461 black.assert_equivalent(source, not_normalized)
462 black.assert_stable(source, not_normalized, mode=mode)
464 @patch("black.dump_to_file", dump_to_stderr)
465 def test_docstring(self) -> None:
466 source, expected = read_data("docstring")
468 self.assertFormatEqual(expected, actual)
469 black.assert_equivalent(source, actual)
470 black.assert_stable(source, actual, DEFAULT_MODE)
472 def test_long_strings(self) -> None:
473 """Tests for splitting long strings."""
474 source, expected = read_data("long_strings")
476 self.assertFormatEqual(expected, actual)
477 black.assert_equivalent(source, actual)
478 black.assert_stable(source, actual, DEFAULT_MODE)
480 def test_long_strings_flag_disabled(self) -> None:
481 """Tests for turning off the string processing logic."""
482 source, expected = read_data("long_strings_flag_disabled")
483 mode = replace(DEFAULT_MODE, experimental_string_processing=False)
484 actual = fs(source, mode=mode)
485 self.assertFormatEqual(expected, actual)
486 black.assert_stable(expected, actual, mode)
488 @patch("black.dump_to_file", dump_to_stderr)
489 def test_long_strings__edge_case(self) -> None:
490 """Edge-case tests for splitting long strings."""
491 source, expected = read_data("long_strings__edge_case")
493 self.assertFormatEqual(expected, actual)
494 black.assert_equivalent(source, actual)
495 black.assert_stable(source, actual, DEFAULT_MODE)
497 @patch("black.dump_to_file", dump_to_stderr)
498 def test_long_strings__regression(self) -> None:
499 """Regression tests for splitting long strings."""
500 source, expected = read_data("long_strings__regression")
502 self.assertFormatEqual(expected, actual)
503 black.assert_equivalent(source, actual)
504 black.assert_stable(source, actual, DEFAULT_MODE)
506 @patch("black.dump_to_file", dump_to_stderr)
507 def test_slices(self) -> None:
508 source, expected = read_data("slices")
510 self.assertFormatEqual(expected, actual)
511 black.assert_equivalent(source, actual)
512 black.assert_stable(source, actual, DEFAULT_MODE)
514 @patch("black.dump_to_file", dump_to_stderr)
515 def test_percent_precedence(self) -> None:
516 source, expected = read_data("percent_precedence")
518 self.assertFormatEqual(expected, actual)
519 black.assert_equivalent(source, actual)
520 black.assert_stable(source, actual, DEFAULT_MODE)
522 @patch("black.dump_to_file", dump_to_stderr)
523 def test_comments(self) -> None:
524 source, expected = read_data("comments")
526 self.assertFormatEqual(expected, actual)
527 black.assert_equivalent(source, actual)
528 black.assert_stable(source, actual, DEFAULT_MODE)
530 @patch("black.dump_to_file", dump_to_stderr)
531 def test_comments2(self) -> None:
532 source, expected = read_data("comments2")
534 self.assertFormatEqual(expected, actual)
535 black.assert_equivalent(source, actual)
536 black.assert_stable(source, actual, DEFAULT_MODE)
538 @patch("black.dump_to_file", dump_to_stderr)
539 def test_comments3(self) -> None:
540 source, expected = read_data("comments3")
542 self.assertFormatEqual(expected, actual)
543 black.assert_equivalent(source, actual)
544 black.assert_stable(source, actual, DEFAULT_MODE)
546 @patch("black.dump_to_file", dump_to_stderr)
547 def test_comments4(self) -> None:
548 source, expected = read_data("comments4")
550 self.assertFormatEqual(expected, actual)
551 black.assert_equivalent(source, actual)
552 black.assert_stable(source, actual, DEFAULT_MODE)
554 @patch("black.dump_to_file", dump_to_stderr)
555 def test_comments5(self) -> None:
556 source, expected = read_data("comments5")
558 self.assertFormatEqual(expected, actual)
559 black.assert_equivalent(source, actual)
560 black.assert_stable(source, actual, DEFAULT_MODE)
562 @patch("black.dump_to_file", dump_to_stderr)
563 def test_comments6(self) -> None:
564 source, expected = read_data("comments6")
566 self.assertFormatEqual(expected, actual)
567 black.assert_equivalent(source, actual)
568 black.assert_stable(source, actual, DEFAULT_MODE)
570 @patch("black.dump_to_file", dump_to_stderr)
571 def test_comments7(self) -> None:
572 source, expected = read_data("comments7")
574 self.assertFormatEqual(expected, actual)
575 black.assert_equivalent(source, actual)
576 black.assert_stable(source, actual, DEFAULT_MODE)
578 @patch("black.dump_to_file", dump_to_stderr)
579 def test_comment_after_escaped_newline(self) -> None:
580 source, expected = read_data("comment_after_escaped_newline")
582 self.assertFormatEqual(expected, actual)
583 black.assert_equivalent(source, actual)
584 black.assert_stable(source, actual, DEFAULT_MODE)
586 @patch("black.dump_to_file", dump_to_stderr)
587 def test_cantfit(self) -> None:
588 source, expected = read_data("cantfit")
590 self.assertFormatEqual(expected, actual)
591 black.assert_equivalent(source, actual)
592 black.assert_stable(source, actual, DEFAULT_MODE)
594 @patch("black.dump_to_file", dump_to_stderr)
595 def test_import_spacing(self) -> None:
596 source, expected = read_data("import_spacing")
598 self.assertFormatEqual(expected, actual)
599 black.assert_equivalent(source, actual)
600 black.assert_stable(source, actual, DEFAULT_MODE)
602 @patch("black.dump_to_file", dump_to_stderr)
603 def test_composition(self) -> None:
604 source, expected = read_data("composition")
606 self.assertFormatEqual(expected, actual)
607 black.assert_equivalent(source, actual)
608 black.assert_stable(source, actual, DEFAULT_MODE)
610 @patch("black.dump_to_file", dump_to_stderr)
611 def test_empty_lines(self) -> None:
612 source, expected = read_data("empty_lines")
614 self.assertFormatEqual(expected, actual)
615 black.assert_equivalent(source, actual)
616 black.assert_stable(source, actual, DEFAULT_MODE)
618 @patch("black.dump_to_file", dump_to_stderr)
619 def test_remove_parens(self) -> None:
620 source, expected = read_data("remove_parens")
622 self.assertFormatEqual(expected, actual)
623 black.assert_equivalent(source, actual)
624 black.assert_stable(source, actual, DEFAULT_MODE)
626 @patch("black.dump_to_file", dump_to_stderr)
627 def test_string_prefixes(self) -> None:
628 source, expected = read_data("string_prefixes")
630 self.assertFormatEqual(expected, actual)
631 black.assert_equivalent(source, actual)
632 black.assert_stable(source, actual, DEFAULT_MODE)
634 @patch("black.dump_to_file", dump_to_stderr)
635 def test_numeric_literals(self) -> None:
636 source, expected = read_data("numeric_literals")
637 mode = replace(DEFAULT_MODE, target_versions=black.PY36_VERSIONS)
638 actual = fs(source, mode=mode)
639 self.assertFormatEqual(expected, actual)
640 black.assert_equivalent(source, actual)
641 black.assert_stable(source, actual, mode)
643 @patch("black.dump_to_file", dump_to_stderr)
644 def test_numeric_literals_ignoring_underscores(self) -> None:
645 source, expected = read_data("numeric_literals_skip_underscores")
646 mode = replace(DEFAULT_MODE, target_versions=black.PY36_VERSIONS)
647 actual = fs(source, mode=mode)
648 self.assertFormatEqual(expected, actual)
649 black.assert_equivalent(source, actual)
650 black.assert_stable(source, actual, mode)
652 @patch("black.dump_to_file", dump_to_stderr)
653 def test_numeric_literals_py2(self) -> None:
654 source, expected = read_data("numeric_literals_py2")
656 self.assertFormatEqual(expected, actual)
657 black.assert_stable(source, actual, DEFAULT_MODE)
659 @patch("black.dump_to_file", dump_to_stderr)
660 def test_python2(self) -> None:
661 source, expected = read_data("python2")
663 self.assertFormatEqual(expected, actual)
664 black.assert_equivalent(source, actual)
665 black.assert_stable(source, actual, DEFAULT_MODE)
667 @patch("black.dump_to_file", dump_to_stderr)
668 def test_python2_print_function(self) -> None:
669 source, expected = read_data("python2_print_function")
670 mode = replace(DEFAULT_MODE, target_versions={TargetVersion.PY27})
671 actual = fs(source, mode=mode)
672 self.assertFormatEqual(expected, actual)
673 black.assert_equivalent(source, actual)
674 black.assert_stable(source, actual, mode)
676 @patch("black.dump_to_file", dump_to_stderr)
677 def test_python2_unicode_literals(self) -> None:
678 source, expected = read_data("python2_unicode_literals")
680 self.assertFormatEqual(expected, actual)
681 black.assert_equivalent(source, actual)
682 black.assert_stable(source, actual, DEFAULT_MODE)
684 @patch("black.dump_to_file", dump_to_stderr)
685 def test_stub(self) -> None:
686 mode = replace(DEFAULT_MODE, is_pyi=True)
687 source, expected = read_data("stub.pyi")
688 actual = fs(source, mode=mode)
689 self.assertFormatEqual(expected, actual)
690 black.assert_stable(source, actual, mode)
692 @patch("black.dump_to_file", dump_to_stderr)
693 def test_async_as_identifier(self) -> None:
694 source_path = (THIS_DIR / "data" / "async_as_identifier.py").resolve()
695 source, expected = read_data("async_as_identifier")
697 self.assertFormatEqual(expected, actual)
698 major, minor = sys.version_info[:2]
699 if major < 3 or (major <= 3 and minor < 7):
700 black.assert_equivalent(source, actual)
701 black.assert_stable(source, actual, DEFAULT_MODE)
702 # ensure black can parse this when the target is 3.6
703 self.invokeBlack([str(source_path), "--target-version", "py36"])
704 # but not on 3.7, because async/await is no longer an identifier
705 self.invokeBlack([str(source_path), "--target-version", "py37"], exit_code=123)
707 @patch("black.dump_to_file", dump_to_stderr)
708 def test_python37(self) -> None:
709 source_path = (THIS_DIR / "data" / "python37.py").resolve()
710 source, expected = read_data("python37")
712 self.assertFormatEqual(expected, actual)
713 major, minor = sys.version_info[:2]
714 if major > 3 or (major == 3 and minor >= 7):
715 black.assert_equivalent(source, actual)
716 black.assert_stable(source, actual, DEFAULT_MODE)
717 # ensure black can parse this when the target is 3.7
718 self.invokeBlack([str(source_path), "--target-version", "py37"])
719 # but not on 3.6, because we use async as a reserved keyword
720 self.invokeBlack([str(source_path), "--target-version", "py36"], exit_code=123)
722 @patch("black.dump_to_file", dump_to_stderr)
723 def test_python38(self) -> None:
724 source, expected = read_data("python38")
726 self.assertFormatEqual(expected, actual)
727 major, minor = sys.version_info[:2]
728 if major > 3 or (major == 3 and minor >= 8):
729 black.assert_equivalent(source, actual)
730 black.assert_stable(source, actual, DEFAULT_MODE)
732 @patch("black.dump_to_file", dump_to_stderr)
733 def test_fmtonoff(self) -> None:
734 source, expected = read_data("fmtonoff")
736 self.assertFormatEqual(expected, actual)
737 black.assert_equivalent(source, actual)
738 black.assert_stable(source, actual, DEFAULT_MODE)
740 @patch("black.dump_to_file", dump_to_stderr)
741 def test_fmtonoff2(self) -> None:
742 source, expected = read_data("fmtonoff2")
744 self.assertFormatEqual(expected, actual)
745 black.assert_equivalent(source, actual)
746 black.assert_stable(source, actual, DEFAULT_MODE)
748 @patch("black.dump_to_file", dump_to_stderr)
749 def test_fmtonoff3(self) -> None:
750 source, expected = read_data("fmtonoff3")
752 self.assertFormatEqual(expected, actual)
753 black.assert_equivalent(source, actual)
754 black.assert_stable(source, actual, DEFAULT_MODE)
756 @patch("black.dump_to_file", dump_to_stderr)
757 def test_fmtonoff4(self) -> None:
758 source, expected = read_data("fmtonoff4")
760 self.assertFormatEqual(expected, actual)
761 black.assert_equivalent(source, actual)
762 black.assert_stable(source, actual, DEFAULT_MODE)
764 @patch("black.dump_to_file", dump_to_stderr)
765 def test_remove_empty_parentheses_after_class(self) -> None:
766 source, expected = read_data("class_blank_parentheses")
768 self.assertFormatEqual(expected, actual)
769 black.assert_equivalent(source, actual)
770 black.assert_stable(source, actual, DEFAULT_MODE)
772 @patch("black.dump_to_file", dump_to_stderr)
773 def test_new_line_between_class_and_code(self) -> None:
774 source, expected = read_data("class_methods_new_line")
776 self.assertFormatEqual(expected, actual)
777 black.assert_equivalent(source, actual)
778 black.assert_stable(source, actual, DEFAULT_MODE)
780 @patch("black.dump_to_file", dump_to_stderr)
781 def test_bracket_match(self) -> None:
782 source, expected = read_data("bracketmatch")
784 self.assertFormatEqual(expected, actual)
785 black.assert_equivalent(source, actual)
786 black.assert_stable(source, actual, DEFAULT_MODE)
788 @patch("black.dump_to_file", dump_to_stderr)
789 def test_tuple_assign(self) -> None:
790 source, expected = read_data("tupleassign")
792 self.assertFormatEqual(expected, actual)
793 black.assert_equivalent(source, actual)
794 black.assert_stable(source, actual, DEFAULT_MODE)
796 @patch("black.dump_to_file", dump_to_stderr)
797 def test_beginning_backslash(self) -> None:
798 source, expected = read_data("beginning_backslash")
800 self.assertFormatEqual(expected, actual)
801 black.assert_equivalent(source, actual)
802 black.assert_stable(source, actual, DEFAULT_MODE)
804 def test_tab_comment_indentation(self) -> None:
805 contents_tab = "if 1:\n\tif 2:\n\t\tpass\n\t# comment\n\tpass\n"
806 contents_spc = "if 1:\n if 2:\n pass\n # comment\n pass\n"
807 self.assertFormatEqual(contents_spc, fs(contents_spc))
808 self.assertFormatEqual(contents_spc, fs(contents_tab))
810 contents_tab = "if 1:\n\tif 2:\n\t\tpass\n\t\t# comment\n\tpass\n"
811 contents_spc = "if 1:\n if 2:\n pass\n # comment\n pass\n"
812 self.assertFormatEqual(contents_spc, fs(contents_spc))
813 self.assertFormatEqual(contents_spc, fs(contents_tab))
815 # mixed tabs and spaces (valid Python 2 code)
816 contents_tab = "if 1:\n if 2:\n\t\tpass\n\t# comment\n pass\n"
817 contents_spc = "if 1:\n if 2:\n pass\n # comment\n pass\n"
818 self.assertFormatEqual(contents_spc, fs(contents_spc))
819 self.assertFormatEqual(contents_spc, fs(contents_tab))
821 contents_tab = "if 1:\n if 2:\n\t\tpass\n\t\t# comment\n pass\n"
822 contents_spc = "if 1:\n if 2:\n pass\n # comment\n pass\n"
823 self.assertFormatEqual(contents_spc, fs(contents_spc))
824 self.assertFormatEqual(contents_spc, fs(contents_tab))
826 def test_report_verbose(self) -> None:
827 report = black.Report(verbose=True)
831 def out(msg: str, **kwargs: Any) -> None:
832 out_lines.append(msg)
834 def err(msg: str, **kwargs: Any) -> None:
835 err_lines.append(msg)
837 with patch("black.out", out), patch("black.err", err):
838 report.done(Path("f1"), black.Changed.NO)
839 self.assertEqual(len(out_lines), 1)
840 self.assertEqual(len(err_lines), 0)
841 self.assertEqual(out_lines[-1], "f1 already well formatted, good job.")
842 self.assertEqual(unstyle(str(report)), "1 file left unchanged.")
843 self.assertEqual(report.return_code, 0)
844 report.done(Path("f2"), black.Changed.YES)
845 self.assertEqual(len(out_lines), 2)
846 self.assertEqual(len(err_lines), 0)
847 self.assertEqual(out_lines[-1], "reformatted f2")
849 unstyle(str(report)), "1 file reformatted, 1 file left unchanged."
851 report.done(Path("f3"), black.Changed.CACHED)
852 self.assertEqual(len(out_lines), 3)
853 self.assertEqual(len(err_lines), 0)
855 out_lines[-1], "f3 wasn't modified on disk since last run."
858 unstyle(str(report)), "1 file reformatted, 2 files left unchanged."
860 self.assertEqual(report.return_code, 0)
862 self.assertEqual(report.return_code, 1)
864 report.failed(Path("e1"), "boom")
865 self.assertEqual(len(out_lines), 3)
866 self.assertEqual(len(err_lines), 1)
867 self.assertEqual(err_lines[-1], "error: cannot format e1: boom")
869 unstyle(str(report)),
870 "1 file reformatted, 2 files left unchanged, 1 file failed to"
873 self.assertEqual(report.return_code, 123)
874 report.done(Path("f3"), black.Changed.YES)
875 self.assertEqual(len(out_lines), 4)
876 self.assertEqual(len(err_lines), 1)
877 self.assertEqual(out_lines[-1], "reformatted f3")
879 unstyle(str(report)),
880 "2 files reformatted, 2 files left unchanged, 1 file failed to"
883 self.assertEqual(report.return_code, 123)
884 report.failed(Path("e2"), "boom")
885 self.assertEqual(len(out_lines), 4)
886 self.assertEqual(len(err_lines), 2)
887 self.assertEqual(err_lines[-1], "error: cannot format e2: boom")
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.path_ignored(Path("wat"), "no match")
895 self.assertEqual(len(out_lines), 5)
896 self.assertEqual(len(err_lines), 2)
897 self.assertEqual(out_lines[-1], "wat ignored: no match")
899 unstyle(str(report)),
900 "2 files reformatted, 2 files left unchanged, 2 files failed to"
903 self.assertEqual(report.return_code, 123)
904 report.done(Path("f4"), black.Changed.NO)
905 self.assertEqual(len(out_lines), 6)
906 self.assertEqual(len(err_lines), 2)
907 self.assertEqual(out_lines[-1], "f4 already well formatted, good job.")
909 unstyle(str(report)),
910 "2 files reformatted, 3 files left unchanged, 2 files failed to"
913 self.assertEqual(report.return_code, 123)
916 unstyle(str(report)),
917 "2 files would be reformatted, 3 files would be left unchanged, 2 files"
918 " would fail to reformat.",
923 unstyle(str(report)),
924 "2 files would be reformatted, 3 files would be left unchanged, 2 files"
925 " would fail to reformat.",
928 def test_report_quiet(self) -> None:
929 report = black.Report(quiet=True)
933 def out(msg: str, **kwargs: Any) -> None:
934 out_lines.append(msg)
936 def err(msg: str, **kwargs: Any) -> None:
937 err_lines.append(msg)
939 with patch("black.out", out), patch("black.err", err):
940 report.done(Path("f1"), black.Changed.NO)
941 self.assertEqual(len(out_lines), 0)
942 self.assertEqual(len(err_lines), 0)
943 self.assertEqual(unstyle(str(report)), "1 file left unchanged.")
944 self.assertEqual(report.return_code, 0)
945 report.done(Path("f2"), black.Changed.YES)
946 self.assertEqual(len(out_lines), 0)
947 self.assertEqual(len(err_lines), 0)
949 unstyle(str(report)), "1 file reformatted, 1 file left unchanged."
951 report.done(Path("f3"), black.Changed.CACHED)
952 self.assertEqual(len(out_lines), 0)
953 self.assertEqual(len(err_lines), 0)
955 unstyle(str(report)), "1 file reformatted, 2 files left unchanged."
957 self.assertEqual(report.return_code, 0)
959 self.assertEqual(report.return_code, 1)
961 report.failed(Path("e1"), "boom")
962 self.assertEqual(len(out_lines), 0)
963 self.assertEqual(len(err_lines), 1)
964 self.assertEqual(err_lines[-1], "error: cannot format e1: boom")
966 unstyle(str(report)),
967 "1 file reformatted, 2 files left unchanged, 1 file failed to"
970 self.assertEqual(report.return_code, 123)
971 report.done(Path("f3"), black.Changed.YES)
972 self.assertEqual(len(out_lines), 0)
973 self.assertEqual(len(err_lines), 1)
975 unstyle(str(report)),
976 "2 files reformatted, 2 files left unchanged, 1 file failed to"
979 self.assertEqual(report.return_code, 123)
980 report.failed(Path("e2"), "boom")
981 self.assertEqual(len(out_lines), 0)
982 self.assertEqual(len(err_lines), 2)
983 self.assertEqual(err_lines[-1], "error: cannot format e2: boom")
985 unstyle(str(report)),
986 "2 files reformatted, 2 files left unchanged, 2 files failed to"
989 self.assertEqual(report.return_code, 123)
990 report.path_ignored(Path("wat"), "no match")
991 self.assertEqual(len(out_lines), 0)
992 self.assertEqual(len(err_lines), 2)
994 unstyle(str(report)),
995 "2 files reformatted, 2 files left unchanged, 2 files failed to"
998 self.assertEqual(report.return_code, 123)
999 report.done(Path("f4"), black.Changed.NO)
1000 self.assertEqual(len(out_lines), 0)
1001 self.assertEqual(len(err_lines), 2)
1003 unstyle(str(report)),
1004 "2 files reformatted, 3 files left unchanged, 2 files failed to"
1007 self.assertEqual(report.return_code, 123)
1010 unstyle(str(report)),
1011 "2 files would be reformatted, 3 files would be left unchanged, 2 files"
1012 " would fail to reformat.",
1014 report.check = False
1017 unstyle(str(report)),
1018 "2 files would be reformatted, 3 files would be left unchanged, 2 files"
1019 " would fail to reformat.",
1022 def test_report_normal(self) -> None:
1023 report = black.Report()
1027 def out(msg: str, **kwargs: Any) -> None:
1028 out_lines.append(msg)
1030 def err(msg: str, **kwargs: Any) -> None:
1031 err_lines.append(msg)
1033 with patch("black.out", out), patch("black.err", err):
1034 report.done(Path("f1"), black.Changed.NO)
1035 self.assertEqual(len(out_lines), 0)
1036 self.assertEqual(len(err_lines), 0)
1037 self.assertEqual(unstyle(str(report)), "1 file left unchanged.")
1038 self.assertEqual(report.return_code, 0)
1039 report.done(Path("f2"), black.Changed.YES)
1040 self.assertEqual(len(out_lines), 1)
1041 self.assertEqual(len(err_lines), 0)
1042 self.assertEqual(out_lines[-1], "reformatted f2")
1044 unstyle(str(report)), "1 file reformatted, 1 file left unchanged."
1046 report.done(Path("f3"), black.Changed.CACHED)
1047 self.assertEqual(len(out_lines), 1)
1048 self.assertEqual(len(err_lines), 0)
1049 self.assertEqual(out_lines[-1], "reformatted f2")
1051 unstyle(str(report)), "1 file reformatted, 2 files left unchanged."
1053 self.assertEqual(report.return_code, 0)
1055 self.assertEqual(report.return_code, 1)
1056 report.check = False
1057 report.failed(Path("e1"), "boom")
1058 self.assertEqual(len(out_lines), 1)
1059 self.assertEqual(len(err_lines), 1)
1060 self.assertEqual(err_lines[-1], "error: cannot format e1: boom")
1062 unstyle(str(report)),
1063 "1 file reformatted, 2 files left unchanged, 1 file failed to"
1066 self.assertEqual(report.return_code, 123)
1067 report.done(Path("f3"), black.Changed.YES)
1068 self.assertEqual(len(out_lines), 2)
1069 self.assertEqual(len(err_lines), 1)
1070 self.assertEqual(out_lines[-1], "reformatted f3")
1072 unstyle(str(report)),
1073 "2 files reformatted, 2 files left unchanged, 1 file failed to"
1076 self.assertEqual(report.return_code, 123)
1077 report.failed(Path("e2"), "boom")
1078 self.assertEqual(len(out_lines), 2)
1079 self.assertEqual(len(err_lines), 2)
1080 self.assertEqual(err_lines[-1], "error: cannot format e2: boom")
1082 unstyle(str(report)),
1083 "2 files reformatted, 2 files left unchanged, 2 files failed to"
1086 self.assertEqual(report.return_code, 123)
1087 report.path_ignored(Path("wat"), "no match")
1088 self.assertEqual(len(out_lines), 2)
1089 self.assertEqual(len(err_lines), 2)
1091 unstyle(str(report)),
1092 "2 files reformatted, 2 files left unchanged, 2 files failed to"
1095 self.assertEqual(report.return_code, 123)
1096 report.done(Path("f4"), black.Changed.NO)
1097 self.assertEqual(len(out_lines), 2)
1098 self.assertEqual(len(err_lines), 2)
1100 unstyle(str(report)),
1101 "2 files reformatted, 3 files left unchanged, 2 files failed to"
1104 self.assertEqual(report.return_code, 123)
1107 unstyle(str(report)),
1108 "2 files would be reformatted, 3 files would be left unchanged, 2 files"
1109 " would fail to reformat.",
1111 report.check = False
1114 unstyle(str(report)),
1115 "2 files would be reformatted, 3 files would be left unchanged, 2 files"
1116 " would fail to reformat.",
1119 def test_lib2to3_parse(self) -> None:
1120 with self.assertRaises(black.InvalidInput):
1121 black.lib2to3_parse("invalid syntax")
1123 straddling = "x + y"
1124 black.lib2to3_parse(straddling)
1125 black.lib2to3_parse(straddling, {TargetVersion.PY27})
1126 black.lib2to3_parse(straddling, {TargetVersion.PY36})
1127 black.lib2to3_parse(straddling, {TargetVersion.PY27, TargetVersion.PY36})
1129 py2_only = "print x"
1130 black.lib2to3_parse(py2_only)
1131 black.lib2to3_parse(py2_only, {TargetVersion.PY27})
1132 with self.assertRaises(black.InvalidInput):
1133 black.lib2to3_parse(py2_only, {TargetVersion.PY36})
1134 with self.assertRaises(black.InvalidInput):
1135 black.lib2to3_parse(py2_only, {TargetVersion.PY27, TargetVersion.PY36})
1137 py3_only = "exec(x, end=y)"
1138 black.lib2to3_parse(py3_only)
1139 with self.assertRaises(black.InvalidInput):
1140 black.lib2to3_parse(py3_only, {TargetVersion.PY27})
1141 black.lib2to3_parse(py3_only, {TargetVersion.PY36})
1142 black.lib2to3_parse(py3_only, {TargetVersion.PY27, TargetVersion.PY36})
1144 def test_get_features_used(self) -> None:
1145 node = black.lib2to3_parse("def f(*, arg): ...\n")
1146 self.assertEqual(black.get_features_used(node), set())
1147 node = black.lib2to3_parse("def f(*, arg,): ...\n")
1148 self.assertEqual(black.get_features_used(node), {Feature.TRAILING_COMMA_IN_DEF})
1149 node = black.lib2to3_parse("f(*arg,)\n")
1151 black.get_features_used(node), {Feature.TRAILING_COMMA_IN_CALL}
1153 node = black.lib2to3_parse("def f(*, arg): f'string'\n")
1154 self.assertEqual(black.get_features_used(node), {Feature.F_STRINGS})
1155 node = black.lib2to3_parse("123_456\n")
1156 self.assertEqual(black.get_features_used(node), {Feature.NUMERIC_UNDERSCORES})
1157 node = black.lib2to3_parse("123456\n")
1158 self.assertEqual(black.get_features_used(node), set())
1159 source, expected = read_data("function")
1160 node = black.lib2to3_parse(source)
1161 expected_features = {
1162 Feature.TRAILING_COMMA_IN_CALL,
1163 Feature.TRAILING_COMMA_IN_DEF,
1166 self.assertEqual(black.get_features_used(node), expected_features)
1167 node = black.lib2to3_parse(expected)
1168 self.assertEqual(black.get_features_used(node), expected_features)
1169 source, expected = read_data("expression")
1170 node = black.lib2to3_parse(source)
1171 self.assertEqual(black.get_features_used(node), set())
1172 node = black.lib2to3_parse(expected)
1173 self.assertEqual(black.get_features_used(node), set())
1175 def test_get_future_imports(self) -> None:
1176 node = black.lib2to3_parse("\n")
1177 self.assertEqual(set(), black.get_future_imports(node))
1178 node = black.lib2to3_parse("from __future__ import black\n")
1179 self.assertEqual({"black"}, black.get_future_imports(node))
1180 node = black.lib2to3_parse("from __future__ import multiple, imports\n")
1181 self.assertEqual({"multiple", "imports"}, black.get_future_imports(node))
1182 node = black.lib2to3_parse("from __future__ import (parenthesized, imports)\n")
1183 self.assertEqual({"parenthesized", "imports"}, black.get_future_imports(node))
1184 node = black.lib2to3_parse(
1185 "from __future__ import multiple\nfrom __future__ import imports\n"
1187 self.assertEqual({"multiple", "imports"}, black.get_future_imports(node))
1188 node = black.lib2to3_parse("# comment\nfrom __future__ import black\n")
1189 self.assertEqual({"black"}, black.get_future_imports(node))
1190 node = black.lib2to3_parse('"""docstring"""\nfrom __future__ import black\n')
1191 self.assertEqual({"black"}, black.get_future_imports(node))
1192 node = black.lib2to3_parse("some(other, code)\nfrom __future__ import black\n")
1193 self.assertEqual(set(), black.get_future_imports(node))
1194 node = black.lib2to3_parse("from some.module import black\n")
1195 self.assertEqual(set(), black.get_future_imports(node))
1196 node = black.lib2to3_parse(
1197 "from __future__ import unicode_literals as _unicode_literals"
1199 self.assertEqual({"unicode_literals"}, black.get_future_imports(node))
1200 node = black.lib2to3_parse(
1201 "from __future__ import unicode_literals as _lol, print"
1203 self.assertEqual({"unicode_literals", "print"}, black.get_future_imports(node))
1205 def test_debug_visitor(self) -> None:
1206 source, _ = read_data("debug_visitor.py")
1207 expected, _ = read_data("debug_visitor.out")
1211 def out(msg: str, **kwargs: Any) -> None:
1212 out_lines.append(msg)
1214 def err(msg: str, **kwargs: Any) -> None:
1215 err_lines.append(msg)
1217 with patch("black.out", out), patch("black.err", err):
1218 black.DebugVisitor.show(source)
1219 actual = "\n".join(out_lines) + "\n"
1221 if expected != actual:
1222 log_name = black.dump_to_file(*out_lines)
1226 f"AST print out is different. Actual version dumped to {log_name}",
1229 def test_format_file_contents(self) -> None:
1232 with self.assertRaises(black.NothingChanged):
1233 black.format_file_contents(empty, mode=mode, fast=False)
1235 with self.assertRaises(black.NothingChanged):
1236 black.format_file_contents(just_nl, mode=mode, fast=False)
1237 same = "j = [1, 2, 3]\n"
1238 with self.assertRaises(black.NothingChanged):
1239 black.format_file_contents(same, mode=mode, fast=False)
1240 different = "j = [1,2,3]"
1242 actual = black.format_file_contents(different, mode=mode, fast=False)
1243 self.assertEqual(expected, actual)
1244 invalid = "return if you can"
1245 with self.assertRaises(black.InvalidInput) as e:
1246 black.format_file_contents(invalid, mode=mode, fast=False)
1247 self.assertEqual(str(e.exception), "Cannot parse: 1:7: return if you can")
1249 def test_endmarker(self) -> None:
1250 n = black.lib2to3_parse("\n")
1251 self.assertEqual(n.type, black.syms.file_input)
1252 self.assertEqual(len(n.children), 1)
1253 self.assertEqual(n.children[0].type, black.token.ENDMARKER)
1255 @unittest.skipIf(os.environ.get("SKIP_AST_PRINT"), "user set SKIP_AST_PRINT")
1256 def test_assertFormatEqual(self) -> None:
1260 def out(msg: str, **kwargs: Any) -> None:
1261 out_lines.append(msg)
1263 def err(msg: str, **kwargs: Any) -> None:
1264 err_lines.append(msg)
1266 with patch("black.out", out), patch("black.err", err):
1267 with self.assertRaises(AssertionError):
1268 self.assertFormatEqual("j = [1, 2, 3]", "j = [1, 2, 3,]")
1270 out_str = "".join(out_lines)
1271 self.assertTrue("Expected tree:" in out_str)
1272 self.assertTrue("Actual tree:" in out_str)
1273 self.assertEqual("".join(err_lines), "")
1275 def test_cache_broken_file(self) -> None:
1277 with cache_dir() as workspace:
1278 cache_file = black.get_cache_file(mode)
1279 with cache_file.open("w") as fobj:
1280 fobj.write("this is not a pickle")
1281 self.assertEqual(black.read_cache(mode), {})
1282 src = (workspace / "test.py").resolve()
1283 with src.open("w") as fobj:
1284 fobj.write("print('hello')")
1285 self.invokeBlack([str(src)])
1286 cache = black.read_cache(mode)
1287 self.assertIn(src, cache)
1289 def test_cache_single_file_already_cached(self) -> None:
1291 with cache_dir() as workspace:
1292 src = (workspace / "test.py").resolve()
1293 with src.open("w") as fobj:
1294 fobj.write("print('hello')")
1295 black.write_cache({}, [src], mode)
1296 self.invokeBlack([str(src)])
1297 with src.open("r") as fobj:
1298 self.assertEqual(fobj.read(), "print('hello')")
1301 def test_cache_multiple_files(self) -> None:
1303 with cache_dir() as workspace, patch(
1304 "black.ProcessPoolExecutor", new=ThreadPoolExecutor
1306 one = (workspace / "one.py").resolve()
1307 with one.open("w") as fobj:
1308 fobj.write("print('hello')")
1309 two = (workspace / "two.py").resolve()
1310 with two.open("w") as fobj:
1311 fobj.write("print('hello')")
1312 black.write_cache({}, [one], mode)
1313 self.invokeBlack([str(workspace)])
1314 with one.open("r") as fobj:
1315 self.assertEqual(fobj.read(), "print('hello')")
1316 with two.open("r") as fobj:
1317 self.assertEqual(fobj.read(), 'print("hello")\n')
1318 cache = black.read_cache(mode)
1319 self.assertIn(one, cache)
1320 self.assertIn(two, cache)
1322 def test_no_cache_when_writeback_diff(self) -> None:
1324 with cache_dir() as workspace:
1325 src = (workspace / "test.py").resolve()
1326 with src.open("w") as fobj:
1327 fobj.write("print('hello')")
1328 self.invokeBlack([str(src), "--diff"])
1329 cache_file = black.get_cache_file(mode)
1330 self.assertFalse(cache_file.exists())
1332 def test_no_cache_when_stdin(self) -> None:
1335 result = CliRunner().invoke(
1336 black.main, ["-"], input=BytesIO(b"print('hello')")
1338 self.assertEqual(result.exit_code, 0)
1339 cache_file = black.get_cache_file(mode)
1340 self.assertFalse(cache_file.exists())
1342 def test_read_cache_no_cachefile(self) -> None:
1345 self.assertEqual(black.read_cache(mode), {})
1347 def test_write_cache_read_cache(self) -> None:
1349 with cache_dir() as workspace:
1350 src = (workspace / "test.py").resolve()
1352 black.write_cache({}, [src], mode)
1353 cache = black.read_cache(mode)
1354 self.assertIn(src, cache)
1355 self.assertEqual(cache[src], black.get_cache_info(src))
1357 def test_filter_cached(self) -> None:
1358 with TemporaryDirectory() as workspace:
1359 path = Path(workspace)
1360 uncached = (path / "uncached").resolve()
1361 cached = (path / "cached").resolve()
1362 cached_but_changed = (path / "changed").resolve()
1365 cached_but_changed.touch()
1366 cache = {cached: black.get_cache_info(cached), cached_but_changed: (0.0, 0)}
1367 todo, done = black.filter_cached(
1368 cache, {uncached, cached, cached_but_changed}
1370 self.assertEqual(todo, {uncached, cached_but_changed})
1371 self.assertEqual(done, {cached})
1373 def test_write_cache_creates_directory_if_needed(self) -> None:
1375 with cache_dir(exists=False) as workspace:
1376 self.assertFalse(workspace.exists())
1377 black.write_cache({}, [], mode)
1378 self.assertTrue(workspace.exists())
1381 def test_failed_formatting_does_not_get_cached(self) -> None:
1383 with cache_dir() as workspace, patch(
1384 "black.ProcessPoolExecutor", new=ThreadPoolExecutor
1386 failing = (workspace / "failing.py").resolve()
1387 with failing.open("w") as fobj:
1388 fobj.write("not actually python")
1389 clean = (workspace / "clean.py").resolve()
1390 with clean.open("w") as fobj:
1391 fobj.write('print("hello")\n')
1392 self.invokeBlack([str(workspace)], exit_code=123)
1393 cache = black.read_cache(mode)
1394 self.assertNotIn(failing, cache)
1395 self.assertIn(clean, cache)
1397 def test_write_cache_write_fail(self) -> None:
1399 with cache_dir(), patch.object(Path, "open") as mock:
1400 mock.side_effect = OSError
1401 black.write_cache({}, [], mode)
1404 @patch("black.ProcessPoolExecutor", MagicMock(side_effect=OSError))
1405 def test_works_in_mono_process_only_environment(self) -> None:
1406 with cache_dir() as workspace:
1408 (workspace / "one.py").resolve(),
1409 (workspace / "two.py").resolve(),
1411 f.write_text('print("hello")\n')
1412 self.invokeBlack([str(workspace)])
1415 def test_check_diff_use_together(self) -> None:
1417 # Files which will be reformatted.
1418 src1 = (THIS_DIR / "data" / "string_quotes.py").resolve()
1419 self.invokeBlack([str(src1), "--diff", "--check"], exit_code=1)
1420 # Files which will not be reformatted.
1421 src2 = (THIS_DIR / "data" / "composition.py").resolve()
1422 self.invokeBlack([str(src2), "--diff", "--check"])
1423 # Multi file command.
1424 self.invokeBlack([str(src1), str(src2), "--diff", "--check"], exit_code=1)
1426 def test_no_files(self) -> None:
1428 # Without an argument, black exits with error code 0.
1429 self.invokeBlack([])
1431 def test_broken_symlink(self) -> None:
1432 with cache_dir() as workspace:
1433 symlink = workspace / "broken_link.py"
1435 symlink.symlink_to("nonexistent.py")
1436 except OSError as e:
1437 self.skipTest(f"Can't create symlinks: {e}")
1438 self.invokeBlack([str(workspace.resolve())])
1440 def test_read_cache_line_lengths(self) -> None:
1442 short_mode = replace(DEFAULT_MODE, line_length=1)
1443 with cache_dir() as workspace:
1444 path = (workspace / "file.py").resolve()
1446 black.write_cache({}, [path], mode)
1447 one = black.read_cache(mode)
1448 self.assertIn(path, one)
1449 two = black.read_cache(short_mode)
1450 self.assertNotIn(path, two)
1452 def test_tricky_unicode_symbols(self) -> None:
1453 source, expected = read_data("tricky_unicode_symbols")
1455 self.assertFormatEqual(expected, actual)
1456 black.assert_equivalent(source, actual)
1457 black.assert_stable(source, actual, DEFAULT_MODE)
1459 def test_single_file_force_pyi(self) -> None:
1460 reg_mode = DEFAULT_MODE
1461 pyi_mode = replace(DEFAULT_MODE, is_pyi=True)
1462 contents, expected = read_data("force_pyi")
1463 with cache_dir() as workspace:
1464 path = (workspace / "file.py").resolve()
1465 with open(path, "w") as fh:
1467 self.invokeBlack([str(path), "--pyi"])
1468 with open(path, "r") as fh:
1470 # verify cache with --pyi is separate
1471 pyi_cache = black.read_cache(pyi_mode)
1472 self.assertIn(path, pyi_cache)
1473 normal_cache = black.read_cache(reg_mode)
1474 self.assertNotIn(path, normal_cache)
1475 self.assertEqual(actual, expected)
1478 def test_multi_file_force_pyi(self) -> None:
1479 reg_mode = DEFAULT_MODE
1480 pyi_mode = replace(DEFAULT_MODE, is_pyi=True)
1481 contents, expected = read_data("force_pyi")
1482 with cache_dir() as workspace:
1484 (workspace / "file1.py").resolve(),
1485 (workspace / "file2.py").resolve(),
1488 with open(path, "w") as fh:
1490 self.invokeBlack([str(p) for p in paths] + ["--pyi"])
1492 with open(path, "r") as fh:
1494 self.assertEqual(actual, expected)
1495 # verify cache with --pyi is separate
1496 pyi_cache = black.read_cache(pyi_mode)
1497 normal_cache = black.read_cache(reg_mode)
1499 self.assertIn(path, pyi_cache)
1500 self.assertNotIn(path, normal_cache)
1502 def test_pipe_force_pyi(self) -> None:
1503 source, expected = read_data("force_pyi")
1504 result = CliRunner().invoke(
1505 black.main, ["-", "-q", "--pyi"], input=BytesIO(source.encode("utf8"))
1507 self.assertEqual(result.exit_code, 0)
1508 actual = result.output
1509 self.assertFormatEqual(actual, expected)
1511 def test_single_file_force_py36(self) -> None:
1512 reg_mode = DEFAULT_MODE
1513 py36_mode = replace(DEFAULT_MODE, target_versions=black.PY36_VERSIONS)
1514 source, expected = read_data("force_py36")
1515 with cache_dir() as workspace:
1516 path = (workspace / "file.py").resolve()
1517 with open(path, "w") as fh:
1519 self.invokeBlack([str(path), *PY36_ARGS])
1520 with open(path, "r") as fh:
1522 # verify cache with --target-version is separate
1523 py36_cache = black.read_cache(py36_mode)
1524 self.assertIn(path, py36_cache)
1525 normal_cache = black.read_cache(reg_mode)
1526 self.assertNotIn(path, normal_cache)
1527 self.assertEqual(actual, expected)
1530 def test_multi_file_force_py36(self) -> None:
1531 reg_mode = DEFAULT_MODE
1532 py36_mode = replace(DEFAULT_MODE, target_versions=black.PY36_VERSIONS)
1533 source, expected = read_data("force_py36")
1534 with cache_dir() as workspace:
1536 (workspace / "file1.py").resolve(),
1537 (workspace / "file2.py").resolve(),
1540 with open(path, "w") as fh:
1542 self.invokeBlack([str(p) for p in paths] + PY36_ARGS)
1544 with open(path, "r") as fh:
1546 self.assertEqual(actual, expected)
1547 # verify cache with --target-version is separate
1548 pyi_cache = black.read_cache(py36_mode)
1549 normal_cache = black.read_cache(reg_mode)
1551 self.assertIn(path, pyi_cache)
1552 self.assertNotIn(path, normal_cache)
1554 def test_collections(self) -> None:
1555 source, expected = read_data("collections")
1557 self.assertFormatEqual(expected, actual)
1558 black.assert_equivalent(source, actual)
1559 black.assert_stable(source, actual, DEFAULT_MODE)
1561 def test_pipe_force_py36(self) -> None:
1562 source, expected = read_data("force_py36")
1563 result = CliRunner().invoke(
1565 ["-", "-q", "--target-version=py36"],
1566 input=BytesIO(source.encode("utf8")),
1568 self.assertEqual(result.exit_code, 0)
1569 actual = result.output
1570 self.assertFormatEqual(actual, expected)
1572 def test_include_exclude(self) -> None:
1573 path = THIS_DIR / "data" / "include_exclude_tests"
1574 include = re.compile(r"\.pyi?$")
1575 exclude = re.compile(r"/exclude/|/\.definitely_exclude/")
1576 report = black.Report()
1577 gitignore = PathSpec.from_lines("gitwildmatch", [])
1578 sources: List[Path] = []
1580 Path(path / "b/dont_exclude/a.py"),
1581 Path(path / "b/dont_exclude/a.pyi"),
1583 this_abs = THIS_DIR.resolve()
1585 black.gen_python_files(
1586 path.iterdir(), this_abs, include, exclude, None, report, gitignore
1589 self.assertEqual(sorted(expected), sorted(sources))
1591 @patch("black.find_project_root", lambda *args: THIS_DIR.resolve())
1592 def test_exclude_for_issue_1572(self) -> None:
1593 # Exclude shouldn't touch files that were explicitly given to Black through the
1594 # CLI. Exclude is supposed to only apply to the recursive discovery of files.
1595 # https://github.com/psf/black/issues/1572
1596 path = THIS_DIR / "data" / "include_exclude_tests"
1598 exclude = r"/exclude/|a\.py"
1599 src = str(path / "b/exclude/a.py")
1600 report = black.Report()
1601 expected = [Path(path / "b/exclude/a.py")]
1614 self.assertEqual(sorted(expected), sorted(sources))
1616 def test_gitignore_exclude(self) -> None:
1617 path = THIS_DIR / "data" / "include_exclude_tests"
1618 include = re.compile(r"\.pyi?$")
1619 exclude = re.compile(r"")
1620 report = black.Report()
1621 gitignore = PathSpec.from_lines(
1622 "gitwildmatch", ["exclude/", ".definitely_exclude"]
1624 sources: List[Path] = []
1626 Path(path / "b/dont_exclude/a.py"),
1627 Path(path / "b/dont_exclude/a.pyi"),
1629 this_abs = THIS_DIR.resolve()
1631 black.gen_python_files(
1632 path.iterdir(), this_abs, include, exclude, None, report, gitignore
1635 self.assertEqual(sorted(expected), sorted(sources))
1637 def test_empty_include(self) -> None:
1638 path = THIS_DIR / "data" / "include_exclude_tests"
1639 report = black.Report()
1640 gitignore = PathSpec.from_lines("gitwildmatch", [])
1641 empty = re.compile(r"")
1642 sources: List[Path] = []
1644 Path(path / "b/exclude/a.pie"),
1645 Path(path / "b/exclude/a.py"),
1646 Path(path / "b/exclude/a.pyi"),
1647 Path(path / "b/dont_exclude/a.pie"),
1648 Path(path / "b/dont_exclude/a.py"),
1649 Path(path / "b/dont_exclude/a.pyi"),
1650 Path(path / "b/.definitely_exclude/a.pie"),
1651 Path(path / "b/.definitely_exclude/a.py"),
1652 Path(path / "b/.definitely_exclude/a.pyi"),
1654 this_abs = THIS_DIR.resolve()
1656 black.gen_python_files(
1660 re.compile(black.DEFAULT_EXCLUDES),
1666 self.assertEqual(sorted(expected), sorted(sources))
1668 def test_empty_exclude(self) -> None:
1669 path = THIS_DIR / "data" / "include_exclude_tests"
1670 report = black.Report()
1671 gitignore = PathSpec.from_lines("gitwildmatch", [])
1672 empty = re.compile(r"")
1673 sources: List[Path] = []
1675 Path(path / "b/dont_exclude/a.py"),
1676 Path(path / "b/dont_exclude/a.pyi"),
1677 Path(path / "b/exclude/a.py"),
1678 Path(path / "b/exclude/a.pyi"),
1679 Path(path / "b/.definitely_exclude/a.py"),
1680 Path(path / "b/.definitely_exclude/a.pyi"),
1682 this_abs = THIS_DIR.resolve()
1684 black.gen_python_files(
1687 re.compile(black.DEFAULT_INCLUDES),
1694 self.assertEqual(sorted(expected), sorted(sources))
1696 def test_invalid_include_exclude(self) -> None:
1697 for option in ["--include", "--exclude"]:
1698 self.invokeBlack(["-", option, "**()(!!*)"], exit_code=2)
1700 def test_preserves_line_endings(self) -> None:
1701 with TemporaryDirectory() as workspace:
1702 test_file = Path(workspace) / "test.py"
1703 for nl in ["\n", "\r\n"]:
1704 contents = nl.join(["def f( ):", " pass"])
1705 test_file.write_bytes(contents.encode())
1706 ff(test_file, write_back=black.WriteBack.YES)
1707 updated_contents: bytes = test_file.read_bytes()
1708 self.assertIn(nl.encode(), updated_contents)
1710 self.assertNotIn(b"\r\n", updated_contents)
1712 def test_preserves_line_endings_via_stdin(self) -> None:
1713 for nl in ["\n", "\r\n"]:
1714 contents = nl.join(["def f( ):", " pass"])
1715 runner = BlackRunner()
1716 result = runner.invoke(
1717 black.main, ["-", "--fast"], input=BytesIO(contents.encode("utf8"))
1719 self.assertEqual(result.exit_code, 0)
1720 output = runner.stdout_bytes
1721 self.assertIn(nl.encode("utf8"), output)
1723 self.assertNotIn(b"\r\n", output)
1725 def test_assert_equivalent_different_asts(self) -> None:
1726 with self.assertRaises(AssertionError):
1727 black.assert_equivalent("{}", "None")
1729 def test_symlink_out_of_root_directory(self) -> None:
1731 root = THIS_DIR.resolve()
1733 include = re.compile(black.DEFAULT_INCLUDES)
1734 exclude = re.compile(black.DEFAULT_EXCLUDES)
1735 report = black.Report()
1736 gitignore = PathSpec.from_lines("gitwildmatch", [])
1737 # `child` should behave like a symlink which resolved path is clearly
1738 # outside of the `root` directory.
1739 path.iterdir.return_value = [child]
1740 child.resolve.return_value = Path("/a/b/c")
1741 child.as_posix.return_value = "/a/b/c"
1742 child.is_symlink.return_value = True
1745 black.gen_python_files(
1746 path.iterdir(), root, include, exclude, None, report, gitignore
1749 except ValueError as ve:
1750 self.fail(f"`get_python_files_in_dir()` failed: {ve}")
1751 path.iterdir.assert_called_once()
1752 child.resolve.assert_called_once()
1753 child.is_symlink.assert_called_once()
1754 # `child` should behave like a strange file which resolved path is clearly
1755 # outside of the `root` directory.
1756 child.is_symlink.return_value = False
1757 with self.assertRaises(ValueError):
1759 black.gen_python_files(
1760 path.iterdir(), root, include, exclude, None, report, gitignore
1763 path.iterdir.assert_called()
1764 self.assertEqual(path.iterdir.call_count, 2)
1765 child.resolve.assert_called()
1766 self.assertEqual(child.resolve.call_count, 2)
1767 child.is_symlink.assert_called()
1768 self.assertEqual(child.is_symlink.call_count, 2)
1770 def test_shhh_click(self) -> None:
1772 from click import _unicodefun # type: ignore
1773 except ModuleNotFoundError:
1774 self.skipTest("Incompatible Click version")
1775 if not hasattr(_unicodefun, "_verify_python3_env"):
1776 self.skipTest("Incompatible Click version")
1777 # First, let's see if Click is crashing with a preferred ASCII charset.
1778 with patch("locale.getpreferredencoding") as gpe:
1779 gpe.return_value = "ASCII"
1780 with self.assertRaises(RuntimeError):
1781 _unicodefun._verify_python3_env()
1782 # Now, let's silence Click...
1784 # ...and confirm it's silent.
1785 with patch("locale.getpreferredencoding") as gpe:
1786 gpe.return_value = "ASCII"
1788 _unicodefun._verify_python3_env()
1789 except RuntimeError as re:
1790 self.fail(f"`patch_click()` failed, exception still raised: {re}")
1792 def test_root_logger_not_used_directly(self) -> None:
1793 def fail(*args: Any, **kwargs: Any) -> None:
1794 self.fail("Record created with root logger")
1796 with patch.multiple(
1807 @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
1808 def test_blackd_main(self) -> None:
1809 with patch("blackd.web.run_app"):
1810 result = CliRunner().invoke(blackd.main, [])
1811 if result.exception is not None:
1812 raise result.exception
1813 self.assertEqual(result.exit_code, 0)
1815 def test_invalid_config_return_code(self) -> None:
1816 tmp_file = Path(black.dump_to_file())
1818 tmp_config = Path(black.dump_to_file())
1820 args = ["--config", str(tmp_config), str(tmp_file)]
1821 self.invokeBlack(args, exit_code=2, ignore_config=False)
1825 def test_parse_pyproject_toml(self) -> None:
1826 test_toml_file = THIS_DIR / "test.toml"
1827 config = black.parse_pyproject_toml(str(test_toml_file))
1828 self.assertEqual(config["verbose"], 1)
1829 self.assertEqual(config["check"], "no")
1830 self.assertEqual(config["diff"], "y")
1831 self.assertEqual(config["color"], True)
1832 self.assertEqual(config["line_length"], 79)
1833 self.assertEqual(config["target_version"], ["py36", "py37", "py38"])
1834 self.assertEqual(config["exclude"], r"\.pyi?$")
1835 self.assertEqual(config["include"], r"\.py?$")
1837 def test_read_pyproject_toml(self) -> None:
1838 test_toml_file = THIS_DIR / "test.toml"
1839 fake_ctx = FakeContext()
1840 black.read_pyproject_toml(fake_ctx, FakeParameter(), str(test_toml_file))
1841 config = fake_ctx.default_map
1842 self.assertEqual(config["verbose"], "1")
1843 self.assertEqual(config["check"], "no")
1844 self.assertEqual(config["diff"], "y")
1845 self.assertEqual(config["color"], "True")
1846 self.assertEqual(config["line_length"], "79")
1847 self.assertEqual(config["target_version"], ["py36", "py37", "py38"])
1848 self.assertEqual(config["exclude"], r"\.pyi?$")
1849 self.assertEqual(config["include"], r"\.py?$")
1851 def test_find_project_root(self) -> None:
1852 with TemporaryDirectory() as workspace:
1853 root = Path(workspace)
1854 test_dir = root / "test"
1857 src_dir = root / "src"
1860 root_pyproject = root / "pyproject.toml"
1861 root_pyproject.touch()
1862 src_pyproject = src_dir / "pyproject.toml"
1863 src_pyproject.touch()
1864 src_python = src_dir / "foo.py"
1868 black.find_project_root((src_dir, test_dir)), root.resolve()
1870 self.assertEqual(black.find_project_root((src_dir,)), src_dir.resolve())
1871 self.assertEqual(black.find_project_root((src_python,)), src_dir.resolve())
1874 class BlackDTestCase(AioHTTPTestCase):
1875 async def get_application(self) -> web.Application:
1876 return blackd.make_app()
1878 # TODO: remove these decorators once the below is released
1879 # https://github.com/aio-libs/aiohttp/pull/3727
1880 @skip_if_exception("ClientOSError")
1881 @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
1883 async def test_blackd_request_needs_formatting(self) -> None:
1884 response = await self.client.post("/", data=b"print('hello world')")
1885 self.assertEqual(response.status, 200)
1886 self.assertEqual(response.charset, "utf8")
1887 self.assertEqual(await response.read(), b'print("hello world")\n')
1889 @skip_if_exception("ClientOSError")
1890 @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
1892 async def test_blackd_request_no_change(self) -> None:
1893 response = await self.client.post("/", data=b'print("hello world")\n')
1894 self.assertEqual(response.status, 204)
1895 self.assertEqual(await response.read(), b"")
1897 @skip_if_exception("ClientOSError")
1898 @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
1900 async def test_blackd_request_syntax_error(self) -> None:
1901 response = await self.client.post("/", data=b"what even ( is")
1902 self.assertEqual(response.status, 400)
1903 content = await response.text()
1905 content.startswith("Cannot parse"),
1906 msg=f"Expected error to start with 'Cannot parse', got {repr(content)}",
1909 @skip_if_exception("ClientOSError")
1910 @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
1912 async def test_blackd_unsupported_version(self) -> None:
1913 response = await self.client.post(
1914 "/", data=b"what", headers={blackd.PROTOCOL_VERSION_HEADER: "2"}
1916 self.assertEqual(response.status, 501)
1918 @skip_if_exception("ClientOSError")
1919 @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
1921 async def test_blackd_supported_version(self) -> None:
1922 response = await self.client.post(
1923 "/", data=b"what", headers={blackd.PROTOCOL_VERSION_HEADER: "1"}
1925 self.assertEqual(response.status, 200)
1927 @skip_if_exception("ClientOSError")
1928 @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
1930 async def test_blackd_invalid_python_variant(self) -> None:
1931 async def check(header_value: str, expected_status: int = 400) -> None:
1932 response = await self.client.post(
1933 "/", data=b"what", headers={blackd.PYTHON_VARIANT_HEADER: header_value}
1935 self.assertEqual(response.status, expected_status)
1938 await check("ruby3.5")
1939 await check("pyi3.6")
1940 await check("py1.5")
1942 await check("py2.8")
1944 await check("pypy3.0")
1945 await check("jython3.4")
1947 @skip_if_exception("ClientOSError")
1948 @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
1950 async def test_blackd_pyi(self) -> None:
1951 source, expected = read_data("stub.pyi")
1952 response = await self.client.post(
1953 "/", data=source, headers={blackd.PYTHON_VARIANT_HEADER: "pyi"}
1955 self.assertEqual(response.status, 200)
1956 self.assertEqual(await response.text(), expected)
1958 @skip_if_exception("ClientOSError")
1959 @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
1961 async def test_blackd_diff(self) -> None:
1962 diff_header = re.compile(
1963 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"
1966 source, _ = read_data("blackd_diff.py")
1967 expected, _ = read_data("blackd_diff.diff")
1969 response = await self.client.post(
1970 "/", data=source, headers={blackd.DIFF_HEADER: "true"}
1972 self.assertEqual(response.status, 200)
1974 actual = await response.text()
1975 actual = diff_header.sub(DETERMINISTIC_HEADER, actual)
1976 self.assertEqual(actual, expected)
1978 @skip_if_exception("ClientOSError")
1979 @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
1981 async def test_blackd_python_variant(self) -> None:
1984 " and_has_a_bunch_of,\n"
1985 " very_long_arguments_too,\n"
1986 " and_lots_of_them_as_well_lol,\n"
1987 " **and_very_long_keyword_arguments\n"
1992 async def check(header_value: str, expected_status: int) -> None:
1993 response = await self.client.post(
1994 "/", data=code, headers={blackd.PYTHON_VARIANT_HEADER: header_value}
1997 response.status, expected_status, msg=await response.text()
2000 await check("3.6", 200)
2001 await check("py3.6", 200)
2002 await check("3.6,3.7", 200)
2003 await check("3.6,py3.7", 200)
2004 await check("py36,py37", 200)
2005 await check("36", 200)
2006 await check("3.6.4", 200)
2008 await check("2", 204)
2009 await check("2.7", 204)
2010 await check("py2.7", 204)
2011 await check("3.4", 204)
2012 await check("py3.4", 204)
2013 await check("py34,py36", 204)
2014 await check("34", 204)
2016 @skip_if_exception("ClientOSError")
2017 @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
2019 async def test_blackd_line_length(self) -> None:
2020 response = await self.client.post(
2021 "/", data=b'print("hello")\n', headers={blackd.LINE_LENGTH_HEADER: "7"}
2023 self.assertEqual(response.status, 200)
2025 @skip_if_exception("ClientOSError")
2026 @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
2028 async def test_blackd_invalid_line_length(self) -> None:
2029 response = await self.client.post(
2030 "/", data=b'print("hello")\n', headers={blackd.LINE_LENGTH_HEADER: "NaN"}
2032 self.assertEqual(response.status, 400)
2034 @skip_if_exception("ClientOSError")
2035 @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
2037 async def test_blackd_response_black_version_header(self) -> None:
2038 response = await self.client.post("/")
2039 self.assertIsNotNone(response.headers.get(blackd.BLACK_VERSION_HEADER))
2042 if __name__ == "__main__":
2043 unittest.main(module="test_black")