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 BlackRunner(CliRunner):
 
 114     """Modify CliRunner so that stderr is not merged with stdout.
 
 116     This is a hack that can be removed once we depend on Click 7.x"""
 
 118     def __init__(self) -> None:
 
 119         self.stderrbuf = BytesIO()
 
 120         self.stdoutbuf = BytesIO()
 
 121         self.stdout_bytes = b""
 
 122         self.stderr_bytes = b""
 
 126     def isolation(self, *args: Any, **kwargs: Any) -> Generator[BinaryIO, None, None]:
 
 127         with super().isolation(*args, **kwargs) as output:
 
 129                 hold_stderr = sys.stderr
 
 130                 sys.stderr = TextIOWrapper(self.stderrbuf, encoding=self.charset)
 
 133                 self.stdout_bytes = sys.stdout.buffer.getvalue()  # type: ignore
 
 134                 self.stderr_bytes = sys.stderr.buffer.getvalue()  # type: ignore
 
 135                 sys.stderr = hold_stderr
 
 138 class BlackTestCase(unittest.TestCase):
 
 141     def assertFormatEqual(self, expected: str, actual: str) -> None:
 
 142         if actual != expected and not os.environ.get("SKIP_AST_PRINT"):
 
 143             bdv: black.DebugVisitor[Any]
 
 144             black.out("Expected tree:", fg="green")
 
 146                 exp_node = black.lib2to3_parse(expected)
 
 147                 bdv = black.DebugVisitor()
 
 148                 list(bdv.visit(exp_node))
 
 149             except Exception as ve:
 
 151             black.out("Actual tree:", fg="red")
 
 153                 exp_node = black.lib2to3_parse(actual)
 
 154                 bdv = black.DebugVisitor()
 
 155                 list(bdv.visit(exp_node))
 
 156             except Exception as ve:
 
 158         self.assertEqual(expected, actual)
 
 161         self, args: List[str], exit_code: int = 0, ignore_config: bool = True
 
 163         runner = BlackRunner()
 
 165             args = ["--verbose", "--config", str(THIS_DIR / "empty.toml"), *args]
 
 166         result = runner.invoke(black.main, args)
 
 171                 f"Failed with args: {args}\n"
 
 172                 f"stdout: {runner.stdout_bytes.decode()!r}\n"
 
 173                 f"stderr: {runner.stderr_bytes.decode()!r}\n"
 
 174                 f"exception: {result.exception}"
 
 178     @patch("black.dump_to_file", dump_to_stderr)
 
 179     def checkSourceFile(self, name: str) -> None:
 
 180         path = THIS_DIR.parent / name
 
 181         source, expected = read_data(str(path), data=False)
 
 183         self.assertFormatEqual(expected, actual)
 
 184         black.assert_equivalent(source, actual)
 
 185         black.assert_stable(source, actual, black.FileMode())
 
 186         self.assertFalse(ff(path))
 
 188     @patch("black.dump_to_file", dump_to_stderr)
 
 189     def test_empty(self) -> None:
 
 190         source = expected = ""
 
 192         self.assertFormatEqual(expected, actual)
 
 193         black.assert_equivalent(source, actual)
 
 194         black.assert_stable(source, actual, black.FileMode())
 
 196     def test_empty_ff(self) -> None:
 
 198         tmp_file = Path(black.dump_to_file())
 
 200             self.assertFalse(ff(tmp_file, write_back=black.WriteBack.YES))
 
 201             with open(tmp_file, encoding="utf8") as f:
 
 205         self.assertFormatEqual(expected, actual)
 
 207     def test_self(self) -> None:
 
 208         self.checkSourceFile("tests/test_black.py")
 
 210     def test_black(self) -> None:
 
 211         self.checkSourceFile("src/black/__init__.py")
 
 213     def test_pygram(self) -> None:
 
 214         self.checkSourceFile("src/blib2to3/pygram.py")
 
 216     def test_pytree(self) -> None:
 
 217         self.checkSourceFile("src/blib2to3/pytree.py")
 
 219     def test_conv(self) -> None:
 
 220         self.checkSourceFile("src/blib2to3/pgen2/conv.py")
 
 222     def test_driver(self) -> None:
 
 223         self.checkSourceFile("src/blib2to3/pgen2/driver.py")
 
 225     def test_grammar(self) -> None:
 
 226         self.checkSourceFile("src/blib2to3/pgen2/grammar.py")
 
 228     def test_literals(self) -> None:
 
 229         self.checkSourceFile("src/blib2to3/pgen2/literals.py")
 
 231     def test_parse(self) -> None:
 
 232         self.checkSourceFile("src/blib2to3/pgen2/parse.py")
 
 234     def test_pgen(self) -> None:
 
 235         self.checkSourceFile("src/blib2to3/pgen2/pgen.py")
 
 237     def test_tokenize(self) -> None:
 
 238         self.checkSourceFile("src/blib2to3/pgen2/tokenize.py")
 
 240     def test_token(self) -> None:
 
 241         self.checkSourceFile("src/blib2to3/pgen2/token.py")
 
 243     def test_setup(self) -> None:
 
 244         self.checkSourceFile("setup.py")
 
 246     def test_piping(self) -> None:
 
 247         source, expected = read_data("src/black/__init__", data=False)
 
 248         result = BlackRunner().invoke(
 
 250             ["-", "--fast", f"--line-length={black.DEFAULT_LINE_LENGTH}"],
 
 251             input=BytesIO(source.encode("utf8")),
 
 253         self.assertEqual(result.exit_code, 0)
 
 254         self.assertFormatEqual(expected, result.output)
 
 255         black.assert_equivalent(source, result.output)
 
 256         black.assert_stable(source, result.output, black.FileMode())
 
 258     def test_piping_diff(self) -> None:
 
 259         diff_header = re.compile(
 
 260             r"(STDIN|STDOUT)\t\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d\.\d\d\d\d\d\d "
 
 263         source, _ = read_data("expression.py")
 
 264         expected, _ = read_data("expression.diff")
 
 265         config = THIS_DIR / "data" / "empty_pyproject.toml"
 
 269             f"--line-length={black.DEFAULT_LINE_LENGTH}",
 
 271             f"--config={config}",
 
 273         result = BlackRunner().invoke(
 
 274             black.main, args, input=BytesIO(source.encode("utf8"))
 
 276         self.assertEqual(result.exit_code, 0)
 
 277         actual = diff_header.sub(DETERMINISTIC_HEADER, result.output)
 
 278         actual = actual.rstrip() + "\n"  # the diff output has a trailing space
 
 279         self.assertEqual(expected, actual)
 
 281     def test_piping_diff_with_color(self) -> None:
 
 282         source, _ = read_data("expression.py")
 
 283         config = THIS_DIR / "data" / "empty_pyproject.toml"
 
 287             f"--line-length={black.DEFAULT_LINE_LENGTH}",
 
 290             f"--config={config}",
 
 292         result = BlackRunner().invoke(
 
 293             black.main, args, input=BytesIO(source.encode("utf8"))
 
 295         actual = result.output
 
 296         # Again, the contents are checked in a different test, so only look for colors.
 
 297         self.assertIn("\033[1;37m", actual)
 
 298         self.assertIn("\033[36m", actual)
 
 299         self.assertIn("\033[32m", actual)
 
 300         self.assertIn("\033[31m", actual)
 
 301         self.assertIn("\033[0m", actual)
 
 303     @patch("black.dump_to_file", dump_to_stderr)
 
 304     def test_function(self) -> None:
 
 305         source, expected = read_data("function")
 
 307         self.assertFormatEqual(expected, actual)
 
 308         black.assert_equivalent(source, actual)
 
 309         black.assert_stable(source, actual, black.FileMode())
 
 311     @patch("black.dump_to_file", dump_to_stderr)
 
 312     def test_function2(self) -> None:
 
 313         source, expected = read_data("function2")
 
 315         self.assertFormatEqual(expected, actual)
 
 316         black.assert_equivalent(source, actual)
 
 317         black.assert_stable(source, actual, black.FileMode())
 
 319     @patch("black.dump_to_file", dump_to_stderr)
 
 320     def test_function_trailing_comma(self) -> None:
 
 321         source, expected = read_data("function_trailing_comma")
 
 323         self.assertFormatEqual(expected, actual)
 
 324         black.assert_equivalent(source, actual)
 
 325         black.assert_stable(source, actual, black.FileMode())
 
 327     @patch("black.dump_to_file", dump_to_stderr)
 
 328     def test_expression(self) -> None:
 
 329         source, expected = read_data("expression")
 
 331         self.assertFormatEqual(expected, actual)
 
 332         black.assert_equivalent(source, actual)
 
 333         black.assert_stable(source, actual, black.FileMode())
 
 335     @patch("black.dump_to_file", dump_to_stderr)
 
 336     def test_pep_572(self) -> None:
 
 337         source, expected = read_data("pep_572")
 
 339         self.assertFormatEqual(expected, actual)
 
 340         black.assert_stable(source, actual, black.FileMode())
 
 341         if sys.version_info >= (3, 8):
 
 342             black.assert_equivalent(source, actual)
 
 344     def test_pep_572_version_detection(self) -> None:
 
 345         source, _ = read_data("pep_572")
 
 346         root = black.lib2to3_parse(source)
 
 347         features = black.get_features_used(root)
 
 348         self.assertIn(black.Feature.ASSIGNMENT_EXPRESSIONS, features)
 
 349         versions = black.detect_target_versions(root)
 
 350         self.assertIn(black.TargetVersion.PY38, versions)
 
 352     def test_expression_ff(self) -> None:
 
 353         source, expected = read_data("expression")
 
 354         tmp_file = Path(black.dump_to_file(source))
 
 356             self.assertTrue(ff(tmp_file, write_back=black.WriteBack.YES))
 
 357             with open(tmp_file, encoding="utf8") as f:
 
 361         self.assertFormatEqual(expected, actual)
 
 362         with patch("black.dump_to_file", dump_to_stderr):
 
 363             black.assert_equivalent(source, actual)
 
 364             black.assert_stable(source, actual, black.FileMode())
 
 366     def test_expression_diff(self) -> None:
 
 367         source, _ = read_data("expression.py")
 
 368         expected, _ = read_data("expression.diff")
 
 369         tmp_file = Path(black.dump_to_file(source))
 
 370         diff_header = re.compile(
 
 371             rf"{re.escape(str(tmp_file))}\t\d\d\d\d-\d\d-\d\d "
 
 372             r"\d\d:\d\d:\d\d\.\d\d\d\d\d\d \+\d\d\d\d"
 
 375             result = BlackRunner().invoke(black.main, ["--diff", str(tmp_file)])
 
 376             self.assertEqual(result.exit_code, 0)
 
 379         actual = result.output
 
 380         actual = diff_header.sub(DETERMINISTIC_HEADER, actual)
 
 381         actual = actual.rstrip() + "\n"  # the diff output has a trailing space
 
 382         if expected != actual:
 
 383             dump = black.dump_to_file(actual)
 
 385                 "Expected diff isn't equal to the actual. If you made changes to"
 
 386                 " expression.py and this is an anticipated difference, overwrite"
 
 387                 f" tests/data/expression.diff with {dump}"
 
 389             self.assertEqual(expected, actual, msg)
 
 391     def test_expression_diff_with_color(self) -> None:
 
 392         source, _ = read_data("expression.py")
 
 393         expected, _ = read_data("expression.diff")
 
 394         tmp_file = Path(black.dump_to_file(source))
 
 396             result = BlackRunner().invoke(
 
 397                 black.main, ["--diff", "--color", str(tmp_file)]
 
 401         actual = result.output
 
 402         # We check the contents of the diff in `test_expression_diff`. All
 
 403         # we need to check here is that color codes exist in the result.
 
 404         self.assertIn("\033[1;37m", actual)
 
 405         self.assertIn("\033[36m", actual)
 
 406         self.assertIn("\033[32m", actual)
 
 407         self.assertIn("\033[31m", actual)
 
 408         self.assertIn("\033[0m", actual)
 
 410     @patch("black.dump_to_file", dump_to_stderr)
 
 411     def test_fstring(self) -> None:
 
 412         source, expected = read_data("fstring")
 
 414         self.assertFormatEqual(expected, actual)
 
 415         black.assert_equivalent(source, actual)
 
 416         black.assert_stable(source, actual, black.FileMode())
 
 418     @patch("black.dump_to_file", dump_to_stderr)
 
 419     def test_pep_570(self) -> None:
 
 420         source, expected = read_data("pep_570")
 
 422         self.assertFormatEqual(expected, actual)
 
 423         black.assert_stable(source, actual, black.FileMode())
 
 424         if sys.version_info >= (3, 8):
 
 425             black.assert_equivalent(source, actual)
 
 427     def test_detect_pos_only_arguments(self) -> None:
 
 428         source, _ = read_data("pep_570")
 
 429         root = black.lib2to3_parse(source)
 
 430         features = black.get_features_used(root)
 
 431         self.assertIn(black.Feature.POS_ONLY_ARGUMENTS, features)
 
 432         versions = black.detect_target_versions(root)
 
 433         self.assertIn(black.TargetVersion.PY38, versions)
 
 435     @patch("black.dump_to_file", dump_to_stderr)
 
 436     def test_string_quotes(self) -> None:
 
 437         source, expected = read_data("string_quotes")
 
 439         self.assertFormatEqual(expected, actual)
 
 440         black.assert_equivalent(source, actual)
 
 441         black.assert_stable(source, actual, black.FileMode())
 
 442         mode = black.FileMode(string_normalization=False)
 
 443         not_normalized = fs(source, mode=mode)
 
 444         self.assertFormatEqual(source.replace("\\\n", ""), not_normalized)
 
 445         black.assert_equivalent(source, not_normalized)
 
 446         black.assert_stable(source, not_normalized, mode=mode)
 
 448     @patch("black.dump_to_file", dump_to_stderr)
 
 449     def test_docstring(self) -> None:
 
 450         source, expected = read_data("docstring")
 
 452         self.assertFormatEqual(expected, actual)
 
 453         black.assert_equivalent(source, actual)
 
 454         black.assert_stable(source, actual, black.FileMode())
 
 456     def test_long_strings(self) -> None:
 
 457         """Tests for splitting long strings."""
 
 458         source, expected = read_data("long_strings")
 
 460         self.assertFormatEqual(expected, actual)
 
 461         black.assert_equivalent(source, actual)
 
 462         black.assert_stable(source, actual, black.FileMode())
 
 464     @patch("black.dump_to_file", dump_to_stderr)
 
 465     def test_long_strings__edge_case(self) -> None:
 
 466         """Edge-case tests for splitting long strings."""
 
 467         source, expected = read_data("long_strings__edge_case")
 
 469         self.assertFormatEqual(expected, actual)
 
 470         black.assert_equivalent(source, actual)
 
 471         black.assert_stable(source, actual, black.FileMode())
 
 473     @patch("black.dump_to_file", dump_to_stderr)
 
 474     def test_long_strings__regression(self) -> None:
 
 475         """Regression tests for splitting long strings."""
 
 476         source, expected = read_data("long_strings__regression")
 
 478         self.assertFormatEqual(expected, actual)
 
 479         black.assert_equivalent(source, actual)
 
 480         black.assert_stable(source, actual, black.FileMode())
 
 482     @patch("black.dump_to_file", dump_to_stderr)
 
 483     def test_slices(self) -> None:
 
 484         source, expected = read_data("slices")
 
 486         self.assertFormatEqual(expected, actual)
 
 487         black.assert_equivalent(source, actual)
 
 488         black.assert_stable(source, actual, black.FileMode())
 
 490     @patch("black.dump_to_file", dump_to_stderr)
 
 491     def test_comments(self) -> None:
 
 492         source, expected = read_data("comments")
 
 494         self.assertFormatEqual(expected, actual)
 
 495         black.assert_equivalent(source, actual)
 
 496         black.assert_stable(source, actual, black.FileMode())
 
 498     @patch("black.dump_to_file", dump_to_stderr)
 
 499     def test_comments2(self) -> None:
 
 500         source, expected = read_data("comments2")
 
 502         self.assertFormatEqual(expected, actual)
 
 503         black.assert_equivalent(source, actual)
 
 504         black.assert_stable(source, actual, black.FileMode())
 
 506     @patch("black.dump_to_file", dump_to_stderr)
 
 507     def test_comments3(self) -> None:
 
 508         source, expected = read_data("comments3")
 
 510         self.assertFormatEqual(expected, actual)
 
 511         black.assert_equivalent(source, actual)
 
 512         black.assert_stable(source, actual, black.FileMode())
 
 514     @patch("black.dump_to_file", dump_to_stderr)
 
 515     def test_comments4(self) -> None:
 
 516         source, expected = read_data("comments4")
 
 518         self.assertFormatEqual(expected, actual)
 
 519         black.assert_equivalent(source, actual)
 
 520         black.assert_stable(source, actual, black.FileMode())
 
 522     @patch("black.dump_to_file", dump_to_stderr)
 
 523     def test_comments5(self) -> None:
 
 524         source, expected = read_data("comments5")
 
 526         self.assertFormatEqual(expected, actual)
 
 527         black.assert_equivalent(source, actual)
 
 528         black.assert_stable(source, actual, black.FileMode())
 
 530     @patch("black.dump_to_file", dump_to_stderr)
 
 531     def test_comments6(self) -> None:
 
 532         source, expected = read_data("comments6")
 
 534         self.assertFormatEqual(expected, actual)
 
 535         black.assert_equivalent(source, actual)
 
 536         black.assert_stable(source, actual, black.FileMode())
 
 538     @patch("black.dump_to_file", dump_to_stderr)
 
 539     def test_comments7(self) -> None:
 
 540         source, expected = read_data("comments7")
 
 542         self.assertFormatEqual(expected, actual)
 
 543         black.assert_equivalent(source, actual)
 
 544         black.assert_stable(source, actual, black.FileMode())
 
 546     @patch("black.dump_to_file", dump_to_stderr)
 
 547     def test_comment_after_escaped_newline(self) -> None:
 
 548         source, expected = read_data("comment_after_escaped_newline")
 
 550         self.assertFormatEqual(expected, actual)
 
 551         black.assert_equivalent(source, actual)
 
 552         black.assert_stable(source, actual, black.FileMode())
 
 554     @patch("black.dump_to_file", dump_to_stderr)
 
 555     def test_cantfit(self) -> None:
 
 556         source, expected = read_data("cantfit")
 
 558         self.assertFormatEqual(expected, actual)
 
 559         black.assert_equivalent(source, actual)
 
 560         black.assert_stable(source, actual, black.FileMode())
 
 562     @patch("black.dump_to_file", dump_to_stderr)
 
 563     def test_import_spacing(self) -> None:
 
 564         source, expected = read_data("import_spacing")
 
 566         self.assertFormatEqual(expected, actual)
 
 567         black.assert_equivalent(source, actual)
 
 568         black.assert_stable(source, actual, black.FileMode())
 
 570     @patch("black.dump_to_file", dump_to_stderr)
 
 571     def test_composition(self) -> None:
 
 572         source, expected = read_data("composition")
 
 574         self.assertFormatEqual(expected, actual)
 
 575         black.assert_equivalent(source, actual)
 
 576         black.assert_stable(source, actual, black.FileMode())
 
 578     @patch("black.dump_to_file", dump_to_stderr)
 
 579     def test_empty_lines(self) -> None:
 
 580         source, expected = read_data("empty_lines")
 
 582         self.assertFormatEqual(expected, actual)
 
 583         black.assert_equivalent(source, actual)
 
 584         black.assert_stable(source, actual, black.FileMode())
 
 586     @patch("black.dump_to_file", dump_to_stderr)
 
 587     def test_remove_parens(self) -> None:
 
 588         source, expected = read_data("remove_parens")
 
 590         self.assertFormatEqual(expected, actual)
 
 591         black.assert_equivalent(source, actual)
 
 592         black.assert_stable(source, actual, black.FileMode())
 
 594     @patch("black.dump_to_file", dump_to_stderr)
 
 595     def test_string_prefixes(self) -> None:
 
 596         source, expected = read_data("string_prefixes")
 
 598         self.assertFormatEqual(expected, actual)
 
 599         black.assert_equivalent(source, actual)
 
 600         black.assert_stable(source, actual, black.FileMode())
 
 602     @patch("black.dump_to_file", dump_to_stderr)
 
 603     def test_numeric_literals(self) -> None:
 
 604         source, expected = read_data("numeric_literals")
 
 605         mode = black.FileMode(target_versions=black.PY36_VERSIONS)
 
 606         actual = fs(source, mode=mode)
 
 607         self.assertFormatEqual(expected, actual)
 
 608         black.assert_equivalent(source, actual)
 
 609         black.assert_stable(source, actual, mode)
 
 611     @patch("black.dump_to_file", dump_to_stderr)
 
 612     def test_numeric_literals_ignoring_underscores(self) -> None:
 
 613         source, expected = read_data("numeric_literals_skip_underscores")
 
 614         mode = black.FileMode(target_versions=black.PY36_VERSIONS)
 
 615         actual = fs(source, mode=mode)
 
 616         self.assertFormatEqual(expected, actual)
 
 617         black.assert_equivalent(source, actual)
 
 618         black.assert_stable(source, actual, mode)
 
 620     @patch("black.dump_to_file", dump_to_stderr)
 
 621     def test_numeric_literals_py2(self) -> None:
 
 622         source, expected = read_data("numeric_literals_py2")
 
 624         self.assertFormatEqual(expected, actual)
 
 625         black.assert_stable(source, actual, black.FileMode())
 
 627     @patch("black.dump_to_file", dump_to_stderr)
 
 628     def test_python2(self) -> None:
 
 629         source, expected = read_data("python2")
 
 631         self.assertFormatEqual(expected, actual)
 
 632         black.assert_equivalent(source, actual)
 
 633         black.assert_stable(source, actual, black.FileMode())
 
 635     @patch("black.dump_to_file", dump_to_stderr)
 
 636     def test_python2_print_function(self) -> None:
 
 637         source, expected = read_data("python2_print_function")
 
 638         mode = black.FileMode(target_versions={TargetVersion.PY27})
 
 639         actual = fs(source, mode=mode)
 
 640         self.assertFormatEqual(expected, actual)
 
 641         black.assert_equivalent(source, actual)
 
 642         black.assert_stable(source, actual, mode)
 
 644     @patch("black.dump_to_file", dump_to_stderr)
 
 645     def test_python2_unicode_literals(self) -> None:
 
 646         source, expected = read_data("python2_unicode_literals")
 
 648         self.assertFormatEqual(expected, actual)
 
 649         black.assert_equivalent(source, actual)
 
 650         black.assert_stable(source, actual, black.FileMode())
 
 652     @patch("black.dump_to_file", dump_to_stderr)
 
 653     def test_stub(self) -> None:
 
 654         mode = black.FileMode(is_pyi=True)
 
 655         source, expected = read_data("stub.pyi")
 
 656         actual = fs(source, mode=mode)
 
 657         self.assertFormatEqual(expected, actual)
 
 658         black.assert_stable(source, actual, mode)
 
 660     @patch("black.dump_to_file", dump_to_stderr)
 
 661     def test_async_as_identifier(self) -> None:
 
 662         source_path = (THIS_DIR / "data" / "async_as_identifier.py").resolve()
 
 663         source, expected = read_data("async_as_identifier")
 
 665         self.assertFormatEqual(expected, actual)
 
 666         major, minor = sys.version_info[:2]
 
 667         if major < 3 or (major <= 3 and minor < 7):
 
 668             black.assert_equivalent(source, actual)
 
 669         black.assert_stable(source, actual, black.FileMode())
 
 670         # ensure black can parse this when the target is 3.6
 
 671         self.invokeBlack([str(source_path), "--target-version", "py36"])
 
 672         # but not on 3.7, because async/await is no longer an identifier
 
 673         self.invokeBlack([str(source_path), "--target-version", "py37"], exit_code=123)
 
 675     @patch("black.dump_to_file", dump_to_stderr)
 
 676     def test_python37(self) -> None:
 
 677         source_path = (THIS_DIR / "data" / "python37.py").resolve()
 
 678         source, expected = read_data("python37")
 
 680         self.assertFormatEqual(expected, actual)
 
 681         major, minor = sys.version_info[:2]
 
 682         if major > 3 or (major == 3 and minor >= 7):
 
 683             black.assert_equivalent(source, actual)
 
 684         black.assert_stable(source, actual, black.FileMode())
 
 685         # ensure black can parse this when the target is 3.7
 
 686         self.invokeBlack([str(source_path), "--target-version", "py37"])
 
 687         # but not on 3.6, because we use async as a reserved keyword
 
 688         self.invokeBlack([str(source_path), "--target-version", "py36"], exit_code=123)
 
 690     @patch("black.dump_to_file", dump_to_stderr)
 
 691     def test_python38(self) -> None:
 
 692         source, expected = read_data("python38")
 
 694         self.assertFormatEqual(expected, actual)
 
 695         major, minor = sys.version_info[:2]
 
 696         if major > 3 or (major == 3 and minor >= 8):
 
 697             black.assert_equivalent(source, actual)
 
 698         black.assert_stable(source, actual, black.FileMode())
 
 700     @patch("black.dump_to_file", dump_to_stderr)
 
 701     def test_fmtonoff(self) -> None:
 
 702         source, expected = read_data("fmtonoff")
 
 704         self.assertFormatEqual(expected, actual)
 
 705         black.assert_equivalent(source, actual)
 
 706         black.assert_stable(source, actual, black.FileMode())
 
 708     @patch("black.dump_to_file", dump_to_stderr)
 
 709     def test_fmtonoff2(self) -> None:
 
 710         source, expected = read_data("fmtonoff2")
 
 712         self.assertFormatEqual(expected, actual)
 
 713         black.assert_equivalent(source, actual)
 
 714         black.assert_stable(source, actual, black.FileMode())
 
 716     @patch("black.dump_to_file", dump_to_stderr)
 
 717     def test_fmtonoff3(self) -> None:
 
 718         source, expected = read_data("fmtonoff3")
 
 720         self.assertFormatEqual(expected, actual)
 
 721         black.assert_equivalent(source, actual)
 
 722         black.assert_stable(source, actual, black.FileMode())
 
 724     @patch("black.dump_to_file", dump_to_stderr)
 
 725     def test_fmtonoff4(self) -> None:
 
 726         source, expected = read_data("fmtonoff4")
 
 728         self.assertFormatEqual(expected, actual)
 
 729         black.assert_equivalent(source, actual)
 
 730         black.assert_stable(source, actual, black.FileMode())
 
 732     @patch("black.dump_to_file", dump_to_stderr)
 
 733     def test_remove_empty_parentheses_after_class(self) -> None:
 
 734         source, expected = read_data("class_blank_parentheses")
 
 736         self.assertFormatEqual(expected, actual)
 
 737         black.assert_equivalent(source, actual)
 
 738         black.assert_stable(source, actual, black.FileMode())
 
 740     @patch("black.dump_to_file", dump_to_stderr)
 
 741     def test_new_line_between_class_and_code(self) -> None:
 
 742         source, expected = read_data("class_methods_new_line")
 
 744         self.assertFormatEqual(expected, actual)
 
 745         black.assert_equivalent(source, actual)
 
 746         black.assert_stable(source, actual, black.FileMode())
 
 748     @patch("black.dump_to_file", dump_to_stderr)
 
 749     def test_bracket_match(self) -> None:
 
 750         source, expected = read_data("bracketmatch")
 
 752         self.assertFormatEqual(expected, actual)
 
 753         black.assert_equivalent(source, actual)
 
 754         black.assert_stable(source, actual, black.FileMode())
 
 756     @patch("black.dump_to_file", dump_to_stderr)
 
 757     def test_tuple_assign(self) -> None:
 
 758         source, expected = read_data("tupleassign")
 
 760         self.assertFormatEqual(expected, actual)
 
 761         black.assert_equivalent(source, actual)
 
 762         black.assert_stable(source, actual, black.FileMode())
 
 764     @patch("black.dump_to_file", dump_to_stderr)
 
 765     def test_beginning_backslash(self) -> None:
 
 766         source, expected = read_data("beginning_backslash")
 
 768         self.assertFormatEqual(expected, actual)
 
 769         black.assert_equivalent(source, actual)
 
 770         black.assert_stable(source, actual, black.FileMode())
 
 772     def test_tab_comment_indentation(self) -> None:
 
 773         contents_tab = "if 1:\n\tif 2:\n\t\tpass\n\t# comment\n\tpass\n"
 
 774         contents_spc = "if 1:\n    if 2:\n        pass\n    # comment\n    pass\n"
 
 775         self.assertFormatEqual(contents_spc, fs(contents_spc))
 
 776         self.assertFormatEqual(contents_spc, fs(contents_tab))
 
 778         contents_tab = "if 1:\n\tif 2:\n\t\tpass\n\t\t# comment\n\tpass\n"
 
 779         contents_spc = "if 1:\n    if 2:\n        pass\n        # comment\n    pass\n"
 
 780         self.assertFormatEqual(contents_spc, fs(contents_spc))
 
 781         self.assertFormatEqual(contents_spc, fs(contents_tab))
 
 783         # mixed tabs and spaces (valid Python 2 code)
 
 784         contents_tab = "if 1:\n        if 2:\n\t\tpass\n\t# comment\n        pass\n"
 
 785         contents_spc = "if 1:\n    if 2:\n        pass\n    # comment\n    pass\n"
 
 786         self.assertFormatEqual(contents_spc, fs(contents_spc))
 
 787         self.assertFormatEqual(contents_spc, fs(contents_tab))
 
 789         contents_tab = "if 1:\n        if 2:\n\t\tpass\n\t\t# comment\n        pass\n"
 
 790         contents_spc = "if 1:\n    if 2:\n        pass\n        # comment\n    pass\n"
 
 791         self.assertFormatEqual(contents_spc, fs(contents_spc))
 
 792         self.assertFormatEqual(contents_spc, fs(contents_tab))
 
 794     def test_report_verbose(self) -> None:
 
 795         report = black.Report(verbose=True)
 
 799         def out(msg: str, **kwargs: Any) -> None:
 
 800             out_lines.append(msg)
 
 802         def err(msg: str, **kwargs: Any) -> None:
 
 803             err_lines.append(msg)
 
 805         with patch("black.out", out), patch("black.err", err):
 
 806             report.done(Path("f1"), black.Changed.NO)
 
 807             self.assertEqual(len(out_lines), 1)
 
 808             self.assertEqual(len(err_lines), 0)
 
 809             self.assertEqual(out_lines[-1], "f1 already well formatted, good job.")
 
 810             self.assertEqual(unstyle(str(report)), "1 file left unchanged.")
 
 811             self.assertEqual(report.return_code, 0)
 
 812             report.done(Path("f2"), black.Changed.YES)
 
 813             self.assertEqual(len(out_lines), 2)
 
 814             self.assertEqual(len(err_lines), 0)
 
 815             self.assertEqual(out_lines[-1], "reformatted f2")
 
 817                 unstyle(str(report)), "1 file reformatted, 1 file left unchanged."
 
 819             report.done(Path("f3"), black.Changed.CACHED)
 
 820             self.assertEqual(len(out_lines), 3)
 
 821             self.assertEqual(len(err_lines), 0)
 
 823                 out_lines[-1], "f3 wasn't modified on disk since last run."
 
 826                 unstyle(str(report)), "1 file reformatted, 2 files left unchanged."
 
 828             self.assertEqual(report.return_code, 0)
 
 830             self.assertEqual(report.return_code, 1)
 
 832             report.failed(Path("e1"), "boom")
 
 833             self.assertEqual(len(out_lines), 3)
 
 834             self.assertEqual(len(err_lines), 1)
 
 835             self.assertEqual(err_lines[-1], "error: cannot format e1: boom")
 
 837                 unstyle(str(report)),
 
 838                 "1 file reformatted, 2 files left unchanged, 1 file failed to"
 
 841             self.assertEqual(report.return_code, 123)
 
 842             report.done(Path("f3"), black.Changed.YES)
 
 843             self.assertEqual(len(out_lines), 4)
 
 844             self.assertEqual(len(err_lines), 1)
 
 845             self.assertEqual(out_lines[-1], "reformatted f3")
 
 847                 unstyle(str(report)),
 
 848                 "2 files reformatted, 2 files left unchanged, 1 file failed to"
 
 851             self.assertEqual(report.return_code, 123)
 
 852             report.failed(Path("e2"), "boom")
 
 853             self.assertEqual(len(out_lines), 4)
 
 854             self.assertEqual(len(err_lines), 2)
 
 855             self.assertEqual(err_lines[-1], "error: cannot format e2: boom")
 
 857                 unstyle(str(report)),
 
 858                 "2 files reformatted, 2 files left unchanged, 2 files failed to"
 
 861             self.assertEqual(report.return_code, 123)
 
 862             report.path_ignored(Path("wat"), "no match")
 
 863             self.assertEqual(len(out_lines), 5)
 
 864             self.assertEqual(len(err_lines), 2)
 
 865             self.assertEqual(out_lines[-1], "wat ignored: no match")
 
 867                 unstyle(str(report)),
 
 868                 "2 files reformatted, 2 files left unchanged, 2 files failed to"
 
 871             self.assertEqual(report.return_code, 123)
 
 872             report.done(Path("f4"), black.Changed.NO)
 
 873             self.assertEqual(len(out_lines), 6)
 
 874             self.assertEqual(len(err_lines), 2)
 
 875             self.assertEqual(out_lines[-1], "f4 already well formatted, good job.")
 
 877                 unstyle(str(report)),
 
 878                 "2 files reformatted, 3 files left unchanged, 2 files failed to"
 
 881             self.assertEqual(report.return_code, 123)
 
 884                 unstyle(str(report)),
 
 885                 "2 files would be reformatted, 3 files would be left unchanged, 2 files"
 
 886                 " would fail to reformat.",
 
 891                 unstyle(str(report)),
 
 892                 "2 files would be reformatted, 3 files would be left unchanged, 2 files"
 
 893                 " would fail to reformat.",
 
 896     def test_report_quiet(self) -> None:
 
 897         report = black.Report(quiet=True)
 
 901         def out(msg: str, **kwargs: Any) -> None:
 
 902             out_lines.append(msg)
 
 904         def err(msg: str, **kwargs: Any) -> None:
 
 905             err_lines.append(msg)
 
 907         with patch("black.out", out), patch("black.err", err):
 
 908             report.done(Path("f1"), black.Changed.NO)
 
 909             self.assertEqual(len(out_lines), 0)
 
 910             self.assertEqual(len(err_lines), 0)
 
 911             self.assertEqual(unstyle(str(report)), "1 file left unchanged.")
 
 912             self.assertEqual(report.return_code, 0)
 
 913             report.done(Path("f2"), black.Changed.YES)
 
 914             self.assertEqual(len(out_lines), 0)
 
 915             self.assertEqual(len(err_lines), 0)
 
 917                 unstyle(str(report)), "1 file reformatted, 1 file left unchanged."
 
 919             report.done(Path("f3"), black.Changed.CACHED)
 
 920             self.assertEqual(len(out_lines), 0)
 
 921             self.assertEqual(len(err_lines), 0)
 
 923                 unstyle(str(report)), "1 file reformatted, 2 files left unchanged."
 
 925             self.assertEqual(report.return_code, 0)
 
 927             self.assertEqual(report.return_code, 1)
 
 929             report.failed(Path("e1"), "boom")
 
 930             self.assertEqual(len(out_lines), 0)
 
 931             self.assertEqual(len(err_lines), 1)
 
 932             self.assertEqual(err_lines[-1], "error: cannot format e1: boom")
 
 934                 unstyle(str(report)),
 
 935                 "1 file reformatted, 2 files left unchanged, 1 file failed to"
 
 938             self.assertEqual(report.return_code, 123)
 
 939             report.done(Path("f3"), black.Changed.YES)
 
 940             self.assertEqual(len(out_lines), 0)
 
 941             self.assertEqual(len(err_lines), 1)
 
 943                 unstyle(str(report)),
 
 944                 "2 files reformatted, 2 files left unchanged, 1 file failed to"
 
 947             self.assertEqual(report.return_code, 123)
 
 948             report.failed(Path("e2"), "boom")
 
 949             self.assertEqual(len(out_lines), 0)
 
 950             self.assertEqual(len(err_lines), 2)
 
 951             self.assertEqual(err_lines[-1], "error: cannot format e2: boom")
 
 953                 unstyle(str(report)),
 
 954                 "2 files reformatted, 2 files left unchanged, 2 files failed to"
 
 957             self.assertEqual(report.return_code, 123)
 
 958             report.path_ignored(Path("wat"), "no match")
 
 959             self.assertEqual(len(out_lines), 0)
 
 960             self.assertEqual(len(err_lines), 2)
 
 962                 unstyle(str(report)),
 
 963                 "2 files reformatted, 2 files left unchanged, 2 files failed to"
 
 966             self.assertEqual(report.return_code, 123)
 
 967             report.done(Path("f4"), black.Changed.NO)
 
 968             self.assertEqual(len(out_lines), 0)
 
 969             self.assertEqual(len(err_lines), 2)
 
 971                 unstyle(str(report)),
 
 972                 "2 files reformatted, 3 files left unchanged, 2 files failed to"
 
 975             self.assertEqual(report.return_code, 123)
 
 978                 unstyle(str(report)),
 
 979                 "2 files would be reformatted, 3 files would be left unchanged, 2 files"
 
 980                 " would fail to reformat.",
 
 985                 unstyle(str(report)),
 
 986                 "2 files would be reformatted, 3 files would be left unchanged, 2 files"
 
 987                 " would fail to reformat.",
 
 990     def test_report_normal(self) -> None:
 
 991         report = black.Report()
 
 995         def out(msg: str, **kwargs: Any) -> None:
 
 996             out_lines.append(msg)
 
 998         def err(msg: str, **kwargs: Any) -> None:
 
 999             err_lines.append(msg)
 
1001         with patch("black.out", out), patch("black.err", err):
 
1002             report.done(Path("f1"), black.Changed.NO)
 
1003             self.assertEqual(len(out_lines), 0)
 
1004             self.assertEqual(len(err_lines), 0)
 
1005             self.assertEqual(unstyle(str(report)), "1 file left unchanged.")
 
1006             self.assertEqual(report.return_code, 0)
 
1007             report.done(Path("f2"), black.Changed.YES)
 
1008             self.assertEqual(len(out_lines), 1)
 
1009             self.assertEqual(len(err_lines), 0)
 
1010             self.assertEqual(out_lines[-1], "reformatted f2")
 
1012                 unstyle(str(report)), "1 file reformatted, 1 file left unchanged."
 
1014             report.done(Path("f3"), black.Changed.CACHED)
 
1015             self.assertEqual(len(out_lines), 1)
 
1016             self.assertEqual(len(err_lines), 0)
 
1017             self.assertEqual(out_lines[-1], "reformatted f2")
 
1019                 unstyle(str(report)), "1 file reformatted, 2 files left unchanged."
 
1021             self.assertEqual(report.return_code, 0)
 
1023             self.assertEqual(report.return_code, 1)
 
1024             report.check = False
 
1025             report.failed(Path("e1"), "boom")
 
1026             self.assertEqual(len(out_lines), 1)
 
1027             self.assertEqual(len(err_lines), 1)
 
1028             self.assertEqual(err_lines[-1], "error: cannot format e1: boom")
 
1030                 unstyle(str(report)),
 
1031                 "1 file reformatted, 2 files left unchanged, 1 file failed to"
 
1034             self.assertEqual(report.return_code, 123)
 
1035             report.done(Path("f3"), black.Changed.YES)
 
1036             self.assertEqual(len(out_lines), 2)
 
1037             self.assertEqual(len(err_lines), 1)
 
1038             self.assertEqual(out_lines[-1], "reformatted f3")
 
1040                 unstyle(str(report)),
 
1041                 "2 files reformatted, 2 files left unchanged, 1 file failed to"
 
1044             self.assertEqual(report.return_code, 123)
 
1045             report.failed(Path("e2"), "boom")
 
1046             self.assertEqual(len(out_lines), 2)
 
1047             self.assertEqual(len(err_lines), 2)
 
1048             self.assertEqual(err_lines[-1], "error: cannot format e2: boom")
 
1050                 unstyle(str(report)),
 
1051                 "2 files reformatted, 2 files left unchanged, 2 files failed to"
 
1054             self.assertEqual(report.return_code, 123)
 
1055             report.path_ignored(Path("wat"), "no match")
 
1056             self.assertEqual(len(out_lines), 2)
 
1057             self.assertEqual(len(err_lines), 2)
 
1059                 unstyle(str(report)),
 
1060                 "2 files reformatted, 2 files left unchanged, 2 files failed to"
 
1063             self.assertEqual(report.return_code, 123)
 
1064             report.done(Path("f4"), black.Changed.NO)
 
1065             self.assertEqual(len(out_lines), 2)
 
1066             self.assertEqual(len(err_lines), 2)
 
1068                 unstyle(str(report)),
 
1069                 "2 files reformatted, 3 files left unchanged, 2 files failed to"
 
1072             self.assertEqual(report.return_code, 123)
 
1075                 unstyle(str(report)),
 
1076                 "2 files would be reformatted, 3 files would be left unchanged, 2 files"
 
1077                 " would fail to reformat.",
 
1079             report.check = False
 
1082                 unstyle(str(report)),
 
1083                 "2 files would be reformatted, 3 files would be left unchanged, 2 files"
 
1084                 " would fail to reformat.",
 
1087     def test_lib2to3_parse(self) -> None:
 
1088         with self.assertRaises(black.InvalidInput):
 
1089             black.lib2to3_parse("invalid syntax")
 
1091         straddling = "x + y"
 
1092         black.lib2to3_parse(straddling)
 
1093         black.lib2to3_parse(straddling, {TargetVersion.PY27})
 
1094         black.lib2to3_parse(straddling, {TargetVersion.PY36})
 
1095         black.lib2to3_parse(straddling, {TargetVersion.PY27, TargetVersion.PY36})
 
1097         py2_only = "print x"
 
1098         black.lib2to3_parse(py2_only)
 
1099         black.lib2to3_parse(py2_only, {TargetVersion.PY27})
 
1100         with self.assertRaises(black.InvalidInput):
 
1101             black.lib2to3_parse(py2_only, {TargetVersion.PY36})
 
1102         with self.assertRaises(black.InvalidInput):
 
1103             black.lib2to3_parse(py2_only, {TargetVersion.PY27, TargetVersion.PY36})
 
1105         py3_only = "exec(x, end=y)"
 
1106         black.lib2to3_parse(py3_only)
 
1107         with self.assertRaises(black.InvalidInput):
 
1108             black.lib2to3_parse(py3_only, {TargetVersion.PY27})
 
1109         black.lib2to3_parse(py3_only, {TargetVersion.PY36})
 
1110         black.lib2to3_parse(py3_only, {TargetVersion.PY27, TargetVersion.PY36})
 
1112     def test_get_features_used(self) -> None:
 
1113         node = black.lib2to3_parse("def f(*, arg): ...\n")
 
1114         self.assertEqual(black.get_features_used(node), set())
 
1115         node = black.lib2to3_parse("def f(*, arg,): ...\n")
 
1116         self.assertEqual(black.get_features_used(node), {Feature.TRAILING_COMMA_IN_DEF})
 
1117         node = black.lib2to3_parse("f(*arg,)\n")
 
1119             black.get_features_used(node), {Feature.TRAILING_COMMA_IN_CALL}
 
1121         node = black.lib2to3_parse("def f(*, arg): f'string'\n")
 
1122         self.assertEqual(black.get_features_used(node), {Feature.F_STRINGS})
 
1123         node = black.lib2to3_parse("123_456\n")
 
1124         self.assertEqual(black.get_features_used(node), {Feature.NUMERIC_UNDERSCORES})
 
1125         node = black.lib2to3_parse("123456\n")
 
1126         self.assertEqual(black.get_features_used(node), set())
 
1127         source, expected = read_data("function")
 
1128         node = black.lib2to3_parse(source)
 
1129         expected_features = {
 
1130             Feature.TRAILING_COMMA_IN_CALL,
 
1131             Feature.TRAILING_COMMA_IN_DEF,
 
1134         self.assertEqual(black.get_features_used(node), expected_features)
 
1135         node = black.lib2to3_parse(expected)
 
1136         self.assertEqual(black.get_features_used(node), expected_features)
 
1137         source, expected = read_data("expression")
 
1138         node = black.lib2to3_parse(source)
 
1139         self.assertEqual(black.get_features_used(node), set())
 
1140         node = black.lib2to3_parse(expected)
 
1141         self.assertEqual(black.get_features_used(node), set())
 
1143     def test_get_future_imports(self) -> None:
 
1144         node = black.lib2to3_parse("\n")
 
1145         self.assertEqual(set(), black.get_future_imports(node))
 
1146         node = black.lib2to3_parse("from __future__ import black\n")
 
1147         self.assertEqual({"black"}, black.get_future_imports(node))
 
1148         node = black.lib2to3_parse("from __future__ import multiple, imports\n")
 
1149         self.assertEqual({"multiple", "imports"}, black.get_future_imports(node))
 
1150         node = black.lib2to3_parse("from __future__ import (parenthesized, imports)\n")
 
1151         self.assertEqual({"parenthesized", "imports"}, black.get_future_imports(node))
 
1152         node = black.lib2to3_parse(
 
1153             "from __future__ import multiple\nfrom __future__ import imports\n"
 
1155         self.assertEqual({"multiple", "imports"}, black.get_future_imports(node))
 
1156         node = black.lib2to3_parse("# comment\nfrom __future__ import black\n")
 
1157         self.assertEqual({"black"}, black.get_future_imports(node))
 
1158         node = black.lib2to3_parse('"""docstring"""\nfrom __future__ import black\n')
 
1159         self.assertEqual({"black"}, black.get_future_imports(node))
 
1160         node = black.lib2to3_parse("some(other, code)\nfrom __future__ import black\n")
 
1161         self.assertEqual(set(), black.get_future_imports(node))
 
1162         node = black.lib2to3_parse("from some.module import black\n")
 
1163         self.assertEqual(set(), black.get_future_imports(node))
 
1164         node = black.lib2to3_parse(
 
1165             "from __future__ import unicode_literals as _unicode_literals"
 
1167         self.assertEqual({"unicode_literals"}, black.get_future_imports(node))
 
1168         node = black.lib2to3_parse(
 
1169             "from __future__ import unicode_literals as _lol, print"
 
1171         self.assertEqual({"unicode_literals", "print"}, black.get_future_imports(node))
 
1173     def test_debug_visitor(self) -> None:
 
1174         source, _ = read_data("debug_visitor.py")
 
1175         expected, _ = read_data("debug_visitor.out")
 
1179         def out(msg: str, **kwargs: Any) -> None:
 
1180             out_lines.append(msg)
 
1182         def err(msg: str, **kwargs: Any) -> None:
 
1183             err_lines.append(msg)
 
1185         with patch("black.out", out), patch("black.err", err):
 
1186             black.DebugVisitor.show(source)
 
1187         actual = "\n".join(out_lines) + "\n"
 
1189         if expected != actual:
 
1190             log_name = black.dump_to_file(*out_lines)
 
1194             f"AST print out is different. Actual version dumped to {log_name}",
 
1197     def test_format_file_contents(self) -> None:
 
1199         mode = black.FileMode()
 
1200         with self.assertRaises(black.NothingChanged):
 
1201             black.format_file_contents(empty, mode=mode, fast=False)
 
1203         with self.assertRaises(black.NothingChanged):
 
1204             black.format_file_contents(just_nl, mode=mode, fast=False)
 
1205         same = "j = [1, 2, 3]\n"
 
1206         with self.assertRaises(black.NothingChanged):
 
1207             black.format_file_contents(same, mode=mode, fast=False)
 
1208         different = "j = [1,2,3]"
 
1210         actual = black.format_file_contents(different, mode=mode, fast=False)
 
1211         self.assertEqual(expected, actual)
 
1212         invalid = "return if you can"
 
1213         with self.assertRaises(black.InvalidInput) as e:
 
1214             black.format_file_contents(invalid, mode=mode, fast=False)
 
1215         self.assertEqual(str(e.exception), "Cannot parse: 1:7: return if you can")
 
1217     def test_endmarker(self) -> None:
 
1218         n = black.lib2to3_parse("\n")
 
1219         self.assertEqual(n.type, black.syms.file_input)
 
1220         self.assertEqual(len(n.children), 1)
 
1221         self.assertEqual(n.children[0].type, black.token.ENDMARKER)
 
1223     @unittest.skipIf(os.environ.get("SKIP_AST_PRINT"), "user set SKIP_AST_PRINT")
 
1224     def test_assertFormatEqual(self) -> None:
 
1228         def out(msg: str, **kwargs: Any) -> None:
 
1229             out_lines.append(msg)
 
1231         def err(msg: str, **kwargs: Any) -> None:
 
1232             err_lines.append(msg)
 
1234         with patch("black.out", out), patch("black.err", err):
 
1235             with self.assertRaises(AssertionError):
 
1236                 self.assertFormatEqual("j = [1, 2, 3]", "j = [1, 2, 3,]")
 
1238         out_str = "".join(out_lines)
 
1239         self.assertTrue("Expected tree:" in out_str)
 
1240         self.assertTrue("Actual tree:" in out_str)
 
1241         self.assertEqual("".join(err_lines), "")
 
1243     def test_cache_broken_file(self) -> None:
 
1244         mode = black.FileMode()
 
1245         with cache_dir() as workspace:
 
1246             cache_file = black.get_cache_file(mode)
 
1247             with cache_file.open("w") as fobj:
 
1248                 fobj.write("this is not a pickle")
 
1249             self.assertEqual(black.read_cache(mode), {})
 
1250             src = (workspace / "test.py").resolve()
 
1251             with src.open("w") as fobj:
 
1252                 fobj.write("print('hello')")
 
1253             self.invokeBlack([str(src)])
 
1254             cache = black.read_cache(mode)
 
1255             self.assertIn(src, cache)
 
1257     def test_cache_single_file_already_cached(self) -> None:
 
1258         mode = black.FileMode()
 
1259         with cache_dir() as workspace:
 
1260             src = (workspace / "test.py").resolve()
 
1261             with src.open("w") as fobj:
 
1262                 fobj.write("print('hello')")
 
1263             black.write_cache({}, [src], mode)
 
1264             self.invokeBlack([str(src)])
 
1265             with src.open("r") as fobj:
 
1266                 self.assertEqual(fobj.read(), "print('hello')")
 
1269     def test_cache_multiple_files(self) -> None:
 
1270         mode = black.FileMode()
 
1271         with cache_dir() as workspace, patch(
 
1272             "black.ProcessPoolExecutor", new=ThreadPoolExecutor
 
1274             one = (workspace / "one.py").resolve()
 
1275             with one.open("w") as fobj:
 
1276                 fobj.write("print('hello')")
 
1277             two = (workspace / "two.py").resolve()
 
1278             with two.open("w") as fobj:
 
1279                 fobj.write("print('hello')")
 
1280             black.write_cache({}, [one], mode)
 
1281             self.invokeBlack([str(workspace)])
 
1282             with one.open("r") as fobj:
 
1283                 self.assertEqual(fobj.read(), "print('hello')")
 
1284             with two.open("r") as fobj:
 
1285                 self.assertEqual(fobj.read(), 'print("hello")\n')
 
1286             cache = black.read_cache(mode)
 
1287             self.assertIn(one, cache)
 
1288             self.assertIn(two, cache)
 
1290     def test_no_cache_when_writeback_diff(self) -> None:
 
1291         mode = black.FileMode()
 
1292         with cache_dir() as workspace:
 
1293             src = (workspace / "test.py").resolve()
 
1294             with src.open("w") as fobj:
 
1295                 fobj.write("print('hello')")
 
1296             self.invokeBlack([str(src), "--diff"])
 
1297             cache_file = black.get_cache_file(mode)
 
1298             self.assertFalse(cache_file.exists())
 
1300     def test_no_cache_when_stdin(self) -> None:
 
1301         mode = black.FileMode()
 
1303             result = CliRunner().invoke(
 
1304                 black.main, ["-"], input=BytesIO(b"print('hello')")
 
1306             self.assertEqual(result.exit_code, 0)
 
1307             cache_file = black.get_cache_file(mode)
 
1308             self.assertFalse(cache_file.exists())
 
1310     def test_read_cache_no_cachefile(self) -> None:
 
1311         mode = black.FileMode()
 
1313             self.assertEqual(black.read_cache(mode), {})
 
1315     def test_write_cache_read_cache(self) -> None:
 
1316         mode = black.FileMode()
 
1317         with cache_dir() as workspace:
 
1318             src = (workspace / "test.py").resolve()
 
1320             black.write_cache({}, [src], mode)
 
1321             cache = black.read_cache(mode)
 
1322             self.assertIn(src, cache)
 
1323             self.assertEqual(cache[src], black.get_cache_info(src))
 
1325     def test_filter_cached(self) -> None:
 
1326         with TemporaryDirectory() as workspace:
 
1327             path = Path(workspace)
 
1328             uncached = (path / "uncached").resolve()
 
1329             cached = (path / "cached").resolve()
 
1330             cached_but_changed = (path / "changed").resolve()
 
1333             cached_but_changed.touch()
 
1334             cache = {cached: black.get_cache_info(cached), cached_but_changed: (0.0, 0)}
 
1335             todo, done = black.filter_cached(
 
1336                 cache, {uncached, cached, cached_but_changed}
 
1338             self.assertEqual(todo, {uncached, cached_but_changed})
 
1339             self.assertEqual(done, {cached})
 
1341     def test_write_cache_creates_directory_if_needed(self) -> None:
 
1342         mode = black.FileMode()
 
1343         with cache_dir(exists=False) as workspace:
 
1344             self.assertFalse(workspace.exists())
 
1345             black.write_cache({}, [], mode)
 
1346             self.assertTrue(workspace.exists())
 
1349     def test_failed_formatting_does_not_get_cached(self) -> None:
 
1350         mode = black.FileMode()
 
1351         with cache_dir() as workspace, patch(
 
1352             "black.ProcessPoolExecutor", new=ThreadPoolExecutor
 
1354             failing = (workspace / "failing.py").resolve()
 
1355             with failing.open("w") as fobj:
 
1356                 fobj.write("not actually python")
 
1357             clean = (workspace / "clean.py").resolve()
 
1358             with clean.open("w") as fobj:
 
1359                 fobj.write('print("hello")\n')
 
1360             self.invokeBlack([str(workspace)], exit_code=123)
 
1361             cache = black.read_cache(mode)
 
1362             self.assertNotIn(failing, cache)
 
1363             self.assertIn(clean, cache)
 
1365     def test_write_cache_write_fail(self) -> None:
 
1366         mode = black.FileMode()
 
1367         with cache_dir(), patch.object(Path, "open") as mock:
 
1368             mock.side_effect = OSError
 
1369             black.write_cache({}, [], mode)
 
1372     @patch("black.ProcessPoolExecutor", MagicMock(side_effect=OSError))
 
1373     def test_works_in_mono_process_only_environment(self) -> None:
 
1374         with cache_dir() as workspace:
 
1376                 (workspace / "one.py").resolve(),
 
1377                 (workspace / "two.py").resolve(),
 
1379                 f.write_text('print("hello")\n')
 
1380             self.invokeBlack([str(workspace)])
 
1383     def test_check_diff_use_together(self) -> None:
 
1385             # Files which will be reformatted.
 
1386             src1 = (THIS_DIR / "data" / "string_quotes.py").resolve()
 
1387             self.invokeBlack([str(src1), "--diff", "--check"], exit_code=1)
 
1388             # Files which will not be reformatted.
 
1389             src2 = (THIS_DIR / "data" / "composition.py").resolve()
 
1390             self.invokeBlack([str(src2), "--diff", "--check"])
 
1391             # Multi file command.
 
1392             self.invokeBlack([str(src1), str(src2), "--diff", "--check"], exit_code=1)
 
1394     def test_no_files(self) -> None:
 
1396             # Without an argument, black exits with error code 0.
 
1397             self.invokeBlack([])
 
1399     def test_broken_symlink(self) -> None:
 
1400         with cache_dir() as workspace:
 
1401             symlink = workspace / "broken_link.py"
 
1403                 symlink.symlink_to("nonexistent.py")
 
1404             except OSError as e:
 
1405                 self.skipTest(f"Can't create symlinks: {e}")
 
1406             self.invokeBlack([str(workspace.resolve())])
 
1408     def test_read_cache_line_lengths(self) -> None:
 
1409         mode = black.FileMode()
 
1410         short_mode = black.FileMode(line_length=1)
 
1411         with cache_dir() as workspace:
 
1412             path = (workspace / "file.py").resolve()
 
1414             black.write_cache({}, [path], mode)
 
1415             one = black.read_cache(mode)
 
1416             self.assertIn(path, one)
 
1417             two = black.read_cache(short_mode)
 
1418             self.assertNotIn(path, two)
 
1420     def test_tricky_unicode_symbols(self) -> None:
 
1421         source, expected = read_data("tricky_unicode_symbols")
 
1423         self.assertFormatEqual(expected, actual)
 
1424         black.assert_equivalent(source, actual)
 
1425         black.assert_stable(source, actual, black.FileMode())
 
1427     def test_single_file_force_pyi(self) -> None:
 
1428         reg_mode = black.FileMode()
 
1429         pyi_mode = black.FileMode(is_pyi=True)
 
1430         contents, expected = read_data("force_pyi")
 
1431         with cache_dir() as workspace:
 
1432             path = (workspace / "file.py").resolve()
 
1433             with open(path, "w") as fh:
 
1435             self.invokeBlack([str(path), "--pyi"])
 
1436             with open(path, "r") as fh:
 
1438             # verify cache with --pyi is separate
 
1439             pyi_cache = black.read_cache(pyi_mode)
 
1440             self.assertIn(path, pyi_cache)
 
1441             normal_cache = black.read_cache(reg_mode)
 
1442             self.assertNotIn(path, normal_cache)
 
1443         self.assertEqual(actual, expected)
 
1446     def test_multi_file_force_pyi(self) -> None:
 
1447         reg_mode = black.FileMode()
 
1448         pyi_mode = black.FileMode(is_pyi=True)
 
1449         contents, expected = read_data("force_pyi")
 
1450         with cache_dir() as workspace:
 
1452                 (workspace / "file1.py").resolve(),
 
1453                 (workspace / "file2.py").resolve(),
 
1456                 with open(path, "w") as fh:
 
1458             self.invokeBlack([str(p) for p in paths] + ["--pyi"])
 
1460                 with open(path, "r") as fh:
 
1462                 self.assertEqual(actual, expected)
 
1463             # verify cache with --pyi is separate
 
1464             pyi_cache = black.read_cache(pyi_mode)
 
1465             normal_cache = black.read_cache(reg_mode)
 
1467                 self.assertIn(path, pyi_cache)
 
1468                 self.assertNotIn(path, normal_cache)
 
1470     def test_pipe_force_pyi(self) -> None:
 
1471         source, expected = read_data("force_pyi")
 
1472         result = CliRunner().invoke(
 
1473             black.main, ["-", "-q", "--pyi"], input=BytesIO(source.encode("utf8"))
 
1475         self.assertEqual(result.exit_code, 0)
 
1476         actual = result.output
 
1477         self.assertFormatEqual(actual, expected)
 
1479     def test_single_file_force_py36(self) -> None:
 
1480         reg_mode = black.FileMode()
 
1481         py36_mode = black.FileMode(target_versions=black.PY36_VERSIONS)
 
1482         source, expected = read_data("force_py36")
 
1483         with cache_dir() as workspace:
 
1484             path = (workspace / "file.py").resolve()
 
1485             with open(path, "w") as fh:
 
1487             self.invokeBlack([str(path), *PY36_ARGS])
 
1488             with open(path, "r") as fh:
 
1490             # verify cache with --target-version is separate
 
1491             py36_cache = black.read_cache(py36_mode)
 
1492             self.assertIn(path, py36_cache)
 
1493             normal_cache = black.read_cache(reg_mode)
 
1494             self.assertNotIn(path, normal_cache)
 
1495         self.assertEqual(actual, expected)
 
1498     def test_multi_file_force_py36(self) -> None:
 
1499         reg_mode = black.FileMode()
 
1500         py36_mode = black.FileMode(target_versions=black.PY36_VERSIONS)
 
1501         source, expected = read_data("force_py36")
 
1502         with cache_dir() as workspace:
 
1504                 (workspace / "file1.py").resolve(),
 
1505                 (workspace / "file2.py").resolve(),
 
1508                 with open(path, "w") as fh:
 
1510             self.invokeBlack([str(p) for p in paths] + PY36_ARGS)
 
1512                 with open(path, "r") as fh:
 
1514                 self.assertEqual(actual, expected)
 
1515             # verify cache with --target-version is separate
 
1516             pyi_cache = black.read_cache(py36_mode)
 
1517             normal_cache = black.read_cache(reg_mode)
 
1519                 self.assertIn(path, pyi_cache)
 
1520                 self.assertNotIn(path, normal_cache)
 
1522     def test_collections(self) -> None:
 
1523         source, expected = read_data("collections")
 
1525         self.assertFormatEqual(expected, actual)
 
1526         black.assert_equivalent(source, actual)
 
1527         black.assert_stable(source, actual, black.FileMode())
 
1529     def test_pipe_force_py36(self) -> None:
 
1530         source, expected = read_data("force_py36")
 
1531         result = CliRunner().invoke(
 
1533             ["-", "-q", "--target-version=py36"],
 
1534             input=BytesIO(source.encode("utf8")),
 
1536         self.assertEqual(result.exit_code, 0)
 
1537         actual = result.output
 
1538         self.assertFormatEqual(actual, expected)
 
1540     def test_include_exclude(self) -> None:
 
1541         path = THIS_DIR / "data" / "include_exclude_tests"
 
1542         include = re.compile(r"\.pyi?$")
 
1543         exclude = re.compile(r"/exclude/|/\.definitely_exclude/")
 
1544         report = black.Report()
 
1545         gitignore = PathSpec.from_lines("gitwildmatch", [])
 
1546         sources: List[Path] = []
 
1548             Path(path / "b/dont_exclude/a.py"),
 
1549             Path(path / "b/dont_exclude/a.pyi"),
 
1551         this_abs = THIS_DIR.resolve()
 
1553             black.gen_python_files(
 
1554                 path.iterdir(), this_abs, include, [exclude], report, gitignore
 
1557         self.assertEqual(sorted(expected), sorted(sources))
 
1559     def test_gitignore_exclude(self) -> None:
 
1560         path = THIS_DIR / "data" / "include_exclude_tests"
 
1561         include = re.compile(r"\.pyi?$")
 
1562         exclude = re.compile(r"")
 
1563         report = black.Report()
 
1564         gitignore = PathSpec.from_lines(
 
1565             "gitwildmatch", ["exclude/", ".definitely_exclude"]
 
1567         sources: List[Path] = []
 
1569             Path(path / "b/dont_exclude/a.py"),
 
1570             Path(path / "b/dont_exclude/a.pyi"),
 
1572         this_abs = THIS_DIR.resolve()
 
1574             black.gen_python_files(
 
1575                 path.iterdir(), this_abs, include, [exclude], report, gitignore
 
1578         self.assertEqual(sorted(expected), sorted(sources))
 
1580     def test_empty_include(self) -> None:
 
1581         path = THIS_DIR / "data" / "include_exclude_tests"
 
1582         report = black.Report()
 
1583         gitignore = PathSpec.from_lines("gitwildmatch", [])
 
1584         empty = re.compile(r"")
 
1585         sources: List[Path] = []
 
1587             Path(path / "b/exclude/a.pie"),
 
1588             Path(path / "b/exclude/a.py"),
 
1589             Path(path / "b/exclude/a.pyi"),
 
1590             Path(path / "b/dont_exclude/a.pie"),
 
1591             Path(path / "b/dont_exclude/a.py"),
 
1592             Path(path / "b/dont_exclude/a.pyi"),
 
1593             Path(path / "b/.definitely_exclude/a.pie"),
 
1594             Path(path / "b/.definitely_exclude/a.py"),
 
1595             Path(path / "b/.definitely_exclude/a.pyi"),
 
1597         this_abs = THIS_DIR.resolve()
 
1599             black.gen_python_files(
 
1603                 [re.compile(black.DEFAULT_EXCLUDES)],
 
1608         self.assertEqual(sorted(expected), sorted(sources))
 
1610     def test_empty_exclude(self) -> None:
 
1611         path = THIS_DIR / "data" / "include_exclude_tests"
 
1612         report = black.Report()
 
1613         gitignore = PathSpec.from_lines("gitwildmatch", [])
 
1614         empty = re.compile(r"")
 
1615         sources: List[Path] = []
 
1617             Path(path / "b/dont_exclude/a.py"),
 
1618             Path(path / "b/dont_exclude/a.pyi"),
 
1619             Path(path / "b/exclude/a.py"),
 
1620             Path(path / "b/exclude/a.pyi"),
 
1621             Path(path / "b/.definitely_exclude/a.py"),
 
1622             Path(path / "b/.definitely_exclude/a.pyi"),
 
1624         this_abs = THIS_DIR.resolve()
 
1626             black.gen_python_files(
 
1629                 re.compile(black.DEFAULT_INCLUDES),
 
1635         self.assertEqual(sorted(expected), sorted(sources))
 
1637     def test_invalid_include_exclude(self) -> None:
 
1638         for option in ["--include", "--exclude"]:
 
1639             self.invokeBlack(["-", option, "**()(!!*)"], exit_code=2)
 
1641     def test_preserves_line_endings(self) -> None:
 
1642         with TemporaryDirectory() as workspace:
 
1643             test_file = Path(workspace) / "test.py"
 
1644             for nl in ["\n", "\r\n"]:
 
1645                 contents = nl.join(["def f(  ):", "    pass"])
 
1646                 test_file.write_bytes(contents.encode())
 
1647                 ff(test_file, write_back=black.WriteBack.YES)
 
1648                 updated_contents: bytes = test_file.read_bytes()
 
1649                 self.assertIn(nl.encode(), updated_contents)
 
1651                     self.assertNotIn(b"\r\n", updated_contents)
 
1653     def test_preserves_line_endings_via_stdin(self) -> None:
 
1654         for nl in ["\n", "\r\n"]:
 
1655             contents = nl.join(["def f(  ):", "    pass"])
 
1656             runner = BlackRunner()
 
1657             result = runner.invoke(
 
1658                 black.main, ["-", "--fast"], input=BytesIO(contents.encode("utf8"))
 
1660             self.assertEqual(result.exit_code, 0)
 
1661             output = runner.stdout_bytes
 
1662             self.assertIn(nl.encode("utf8"), output)
 
1664                 self.assertNotIn(b"\r\n", output)
 
1666     def test_assert_equivalent_different_asts(self) -> None:
 
1667         with self.assertRaises(AssertionError):
 
1668             black.assert_equivalent("{}", "None")
 
1670     def test_symlink_out_of_root_directory(self) -> None:
 
1672         root = THIS_DIR.resolve()
 
1674         include = re.compile(black.DEFAULT_INCLUDES)
 
1675         exclude = re.compile(black.DEFAULT_EXCLUDES)
 
1676         report = black.Report()
 
1677         gitignore = PathSpec.from_lines("gitwildmatch", [])
 
1678         # `child` should behave like a symlink which resolved path is clearly
 
1679         # outside of the `root` directory.
 
1680         path.iterdir.return_value = [child]
 
1681         child.resolve.return_value = Path("/a/b/c")
 
1682         child.as_posix.return_value = "/a/b/c"
 
1683         child.is_symlink.return_value = True
 
1686                 black.gen_python_files(
 
1687                     path.iterdir(), root, include, exclude, report, gitignore
 
1690         except ValueError as ve:
 
1691             self.fail(f"`get_python_files_in_dir()` failed: {ve}")
 
1692         path.iterdir.assert_called_once()
 
1693         child.resolve.assert_called_once()
 
1694         child.is_symlink.assert_called_once()
 
1695         # `child` should behave like a strange file which resolved path is clearly
 
1696         # outside of the `root` directory.
 
1697         child.is_symlink.return_value = False
 
1698         with self.assertRaises(ValueError):
 
1700                 black.gen_python_files(
 
1701                     path.iterdir(), root, include, exclude, report, gitignore
 
1704         path.iterdir.assert_called()
 
1705         self.assertEqual(path.iterdir.call_count, 2)
 
1706         child.resolve.assert_called()
 
1707         self.assertEqual(child.resolve.call_count, 2)
 
1708         child.is_symlink.assert_called()
 
1709         self.assertEqual(child.is_symlink.call_count, 2)
 
1711     def test_shhh_click(self) -> None:
 
1713             from click import _unicodefun  # type: ignore
 
1714         except ModuleNotFoundError:
 
1715             self.skipTest("Incompatible Click version")
 
1716         if not hasattr(_unicodefun, "_verify_python3_env"):
 
1717             self.skipTest("Incompatible Click version")
 
1718         # First, let's see if Click is crashing with a preferred ASCII charset.
 
1719         with patch("locale.getpreferredencoding") as gpe:
 
1720             gpe.return_value = "ASCII"
 
1721             with self.assertRaises(RuntimeError):
 
1722                 _unicodefun._verify_python3_env()
 
1723         # Now, let's silence Click...
 
1725         # ...and confirm it's silent.
 
1726         with patch("locale.getpreferredencoding") as gpe:
 
1727             gpe.return_value = "ASCII"
 
1729                 _unicodefun._verify_python3_env()
 
1730             except RuntimeError as re:
 
1731                 self.fail(f"`patch_click()` failed, exception still raised: {re}")
 
1733     def test_root_logger_not_used_directly(self) -> None:
 
1734         def fail(*args: Any, **kwargs: Any) -> None:
 
1735             self.fail("Record created with root logger")
 
1737         with patch.multiple(
 
1748     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1749     def test_blackd_main(self) -> None:
 
1750         with patch("blackd.web.run_app"):
 
1751             result = CliRunner().invoke(blackd.main, [])
 
1752             if result.exception is not None:
 
1753                 raise result.exception
 
1754             self.assertEqual(result.exit_code, 0)
 
1756     def test_invalid_config_return_code(self) -> None:
 
1757         tmp_file = Path(black.dump_to_file())
 
1759             tmp_config = Path(black.dump_to_file())
 
1761             args = ["--config", str(tmp_config), str(tmp_file)]
 
1762             self.invokeBlack(args, exit_code=2, ignore_config=False)
 
1766     def test_parse_pyproject_toml(self) -> None:
 
1767         test_toml_file = THIS_DIR / "test.toml"
 
1768         config = black.parse_pyproject_toml(str(test_toml_file))
 
1769         self.assertEqual(config["verbose"], 1)
 
1770         self.assertEqual(config["check"], "no")
 
1771         self.assertEqual(config["diff"], "y")
 
1772         self.assertEqual(config["color"], True)
 
1773         self.assertEqual(config["line_length"], 79)
 
1774         self.assertEqual(config["target_version"], ["py36", "py37", "py38"])
 
1775         self.assertEqual(config["exclude"], r"\.pyi?$")
 
1776         self.assertEqual(config["include"], r"\.py?$")
 
1778     def test_read_pyproject_toml(self) -> None:
 
1779         test_toml_file = THIS_DIR / "test.toml"
 
1781         # Fake a click context and parameter so mypy stays happy
 
1782         class FakeContext(click.Context):
 
1783             def __init__(self) -> None:
 
1784                 self.default_map: Dict[str, Any] = {}
 
1786         class FakeParameter(click.Parameter):
 
1787             def __init__(self) -> None:
 
1790         fake_ctx = FakeContext()
 
1791         black.read_pyproject_toml(
 
1792             fake_ctx, FakeParameter(), str(test_toml_file),
 
1794         config = fake_ctx.default_map
 
1795         self.assertEqual(config["verbose"], "1")
 
1796         self.assertEqual(config["check"], "no")
 
1797         self.assertEqual(config["diff"], "y")
 
1798         self.assertEqual(config["color"], "True")
 
1799         self.assertEqual(config["line_length"], "79")
 
1800         self.assertEqual(config["target_version"], ["py36", "py37", "py38"])
 
1801         self.assertEqual(config["exclude"], r"\.pyi?$")
 
1802         self.assertEqual(config["include"], r"\.py?$")
 
1804     def test_find_project_root(self) -> None:
 
1805         with TemporaryDirectory() as workspace:
 
1806             root = Path(workspace)
 
1807             test_dir = root / "test"
 
1810             src_dir = root / "src"
 
1813             root_pyproject = root / "pyproject.toml"
 
1814             root_pyproject.touch()
 
1815             src_pyproject = src_dir / "pyproject.toml"
 
1816             src_pyproject.touch()
 
1817             src_python = src_dir / "foo.py"
 
1821                 black.find_project_root((src_dir, test_dir)), root.resolve()
 
1823             self.assertEqual(black.find_project_root((src_dir,)), src_dir.resolve())
 
1824             self.assertEqual(black.find_project_root((src_python,)), src_dir.resolve())
 
1827 class BlackDTestCase(AioHTTPTestCase):
 
1828     async def get_application(self) -> web.Application:
 
1829         return blackd.make_app()
 
1831     # TODO: remove these decorators once the below is released
 
1832     # https://github.com/aio-libs/aiohttp/pull/3727
 
1833     @skip_if_exception("ClientOSError")
 
1834     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1836     async def test_blackd_request_needs_formatting(self) -> None:
 
1837         response = await self.client.post("/", data=b"print('hello world')")
 
1838         self.assertEqual(response.status, 200)
 
1839         self.assertEqual(response.charset, "utf8")
 
1840         self.assertEqual(await response.read(), b'print("hello world")\n')
 
1842     @skip_if_exception("ClientOSError")
 
1843     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1845     async def test_blackd_request_no_change(self) -> None:
 
1846         response = await self.client.post("/", data=b'print("hello world")\n')
 
1847         self.assertEqual(response.status, 204)
 
1848         self.assertEqual(await response.read(), b"")
 
1850     @skip_if_exception("ClientOSError")
 
1851     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1853     async def test_blackd_request_syntax_error(self) -> None:
 
1854         response = await self.client.post("/", data=b"what even ( is")
 
1855         self.assertEqual(response.status, 400)
 
1856         content = await response.text()
 
1858             content.startswith("Cannot parse"),
 
1859             msg=f"Expected error to start with 'Cannot parse', got {repr(content)}",
 
1862     @skip_if_exception("ClientOSError")
 
1863     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1865     async def test_blackd_unsupported_version(self) -> None:
 
1866         response = await self.client.post(
 
1867             "/", data=b"what", headers={blackd.PROTOCOL_VERSION_HEADER: "2"}
 
1869         self.assertEqual(response.status, 501)
 
1871     @skip_if_exception("ClientOSError")
 
1872     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1874     async def test_blackd_supported_version(self) -> None:
 
1875         response = await self.client.post(
 
1876             "/", data=b"what", headers={blackd.PROTOCOL_VERSION_HEADER: "1"}
 
1878         self.assertEqual(response.status, 200)
 
1880     @skip_if_exception("ClientOSError")
 
1881     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1883     async def test_blackd_invalid_python_variant(self) -> None:
 
1884         async def check(header_value: str, expected_status: int = 400) -> None:
 
1885             response = await self.client.post(
 
1886                 "/", data=b"what", headers={blackd.PYTHON_VARIANT_HEADER: header_value}
 
1888             self.assertEqual(response.status, expected_status)
 
1891         await check("ruby3.5")
 
1892         await check("pyi3.6")
 
1893         await check("py1.5")
 
1895         await check("py2.8")
 
1897         await check("pypy3.0")
 
1898         await check("jython3.4")
 
1900     @skip_if_exception("ClientOSError")
 
1901     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1903     async def test_blackd_pyi(self) -> None:
 
1904         source, expected = read_data("stub.pyi")
 
1905         response = await self.client.post(
 
1906             "/", data=source, headers={blackd.PYTHON_VARIANT_HEADER: "pyi"}
 
1908         self.assertEqual(response.status, 200)
 
1909         self.assertEqual(await response.text(), expected)
 
1911     @skip_if_exception("ClientOSError")
 
1912     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1914     async def test_blackd_diff(self) -> None:
 
1915         diff_header = re.compile(
 
1916             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"
 
1919         source, _ = read_data("blackd_diff.py")
 
1920         expected, _ = read_data("blackd_diff.diff")
 
1922         response = await self.client.post(
 
1923             "/", data=source, headers={blackd.DIFF_HEADER: "true"}
 
1925         self.assertEqual(response.status, 200)
 
1927         actual = await response.text()
 
1928         actual = diff_header.sub(DETERMINISTIC_HEADER, actual)
 
1929         self.assertEqual(actual, expected)
 
1931     @skip_if_exception("ClientOSError")
 
1932     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1934     async def test_blackd_python_variant(self) -> None:
 
1937             "    and_has_a_bunch_of,\n"
 
1938             "    very_long_arguments_too,\n"
 
1939             "    and_lots_of_them_as_well_lol,\n"
 
1940             "    **and_very_long_keyword_arguments\n"
 
1945         async def check(header_value: str, expected_status: int) -> None:
 
1946             response = await self.client.post(
 
1947                 "/", data=code, headers={blackd.PYTHON_VARIANT_HEADER: header_value}
 
1950                 response.status, expected_status, msg=await response.text()
 
1953         await check("3.6", 200)
 
1954         await check("py3.6", 200)
 
1955         await check("3.6,3.7", 200)
 
1956         await check("3.6,py3.7", 200)
 
1957         await check("py36,py37", 200)
 
1958         await check("36", 200)
 
1959         await check("3.6.4", 200)
 
1961         await check("2", 204)
 
1962         await check("2.7", 204)
 
1963         await check("py2.7", 204)
 
1964         await check("3.4", 204)
 
1965         await check("py3.4", 204)
 
1966         await check("py34,py36", 204)
 
1967         await check("34", 204)
 
1969     @skip_if_exception("ClientOSError")
 
1970     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1972     async def test_blackd_line_length(self) -> None:
 
1973         response = await self.client.post(
 
1974             "/", data=b'print("hello")\n', headers={blackd.LINE_LENGTH_HEADER: "7"}
 
1976         self.assertEqual(response.status, 200)
 
1978     @skip_if_exception("ClientOSError")
 
1979     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1981     async def test_blackd_invalid_line_length(self) -> None:
 
1982         response = await self.client.post(
 
1983             "/", data=b'print("hello")\n', headers={blackd.LINE_LENGTH_HEADER: "NaN"}
 
1985         self.assertEqual(response.status, 400)
 
1987     @skip_if_exception("ClientOSError")
 
1988     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1990     async def test_blackd_response_black_version_header(self) -> None:
 
1991         response = await self.client.post("/")
 
1992         self.assertIsNotNone(response.headers.get(blackd.BLACK_VERSION_HEADER))
 
1995 if __name__ == "__main__":
 
1996     unittest.main(module="test_black")