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, wraps
 
   7 from io import BytesIO, TextIOWrapper
 
   9 from pathlib import Path
 
  12 from tempfile import TemporaryDirectory
 
  25 from unittest.mock import patch, MagicMock
 
  27 from click import unstyle
 
  28 from click.testing import CliRunner
 
  31 from black import Feature, TargetVersion
 
  35     from aiohttp.test_utils import TestClient, TestServer
 
  37     has_blackd_deps = False
 
  39     has_blackd_deps = True
 
  41 ff = partial(black.format_file_in_place, mode=black.FileMode(), fast=True)
 
  42 fs = partial(black.format_str, mode=black.FileMode())
 
  43 THIS_FILE = Path(__file__)
 
  44 THIS_DIR = THIS_FILE.parent
 
  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 THIS_DIR
 
  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(close: bool) -> Iterator[None]:
 
  92     policy = asyncio.get_event_loop_policy()
 
  93     old_loop = policy.get_event_loop()
 
  94     loop = policy.new_event_loop()
 
  95     asyncio.set_event_loop(loop)
 
 100         policy.set_event_loop(old_loop)
 
 105 def async_test(f: Callable[..., Coroutine[Any, None, R]]) -> Callable[..., None]:
 
 106     @event_loop(close=True)
 
 108     def wrapper(*args: Any, **kwargs: Any) -> None:
 
 109         asyncio.get_event_loop().run_until_complete(f(*args, **kwargs))
 
 114 class BlackRunner(CliRunner):
 
 115     """Modify CliRunner so that stderr is not merged with stdout.
 
 117     This is a hack that can be removed once we depend on Click 7.x"""
 
 119     def __init__(self) -> None:
 
 120         self.stderrbuf = BytesIO()
 
 121         self.stdoutbuf = BytesIO()
 
 122         self.stdout_bytes = b""
 
 123         self.stderr_bytes = b""
 
 127     def isolation(self, *args: Any, **kwargs: Any) -> Generator[BinaryIO, None, None]:
 
 128         with super().isolation(*args, **kwargs) as output:
 
 130                 hold_stderr = sys.stderr
 
 131                 sys.stderr = TextIOWrapper(self.stderrbuf, encoding=self.charset)
 
 134                 self.stdout_bytes = sys.stdout.buffer.getvalue()  # type: ignore
 
 135                 self.stderr_bytes = sys.stderr.buffer.getvalue()  # type: ignore
 
 136                 sys.stderr = hold_stderr
 
 139 class BlackTestCase(unittest.TestCase):
 
 142     def assertFormatEqual(self, expected: str, actual: str) -> None:
 
 143         if actual != expected and not os.environ.get("SKIP_AST_PRINT"):
 
 144             bdv: black.DebugVisitor[Any]
 
 145             black.out("Expected tree:", fg="green")
 
 147                 exp_node = black.lib2to3_parse(expected)
 
 148                 bdv = black.DebugVisitor()
 
 149                 list(bdv.visit(exp_node))
 
 150             except Exception as ve:
 
 152             black.out("Actual tree:", fg="red")
 
 154                 exp_node = black.lib2to3_parse(actual)
 
 155                 bdv = black.DebugVisitor()
 
 156                 list(bdv.visit(exp_node))
 
 157             except Exception as ve:
 
 159         self.assertEqual(expected, actual)
 
 162         self, args: List[str], exit_code: int = 0, ignore_config: bool = True
 
 164         runner = BlackRunner()
 
 166             args = ["--config", str(THIS_DIR / "empty.toml"), *args]
 
 167         result = runner.invoke(black.main, args)
 
 168         self.assertEqual(result.exit_code, exit_code, msg=runner.stderr_bytes.decode())
 
 170     @patch("black.dump_to_file", dump_to_stderr)
 
 171     def test_empty(self) -> None:
 
 172         source = expected = ""
 
 174         self.assertFormatEqual(expected, actual)
 
 175         black.assert_equivalent(source, actual)
 
 176         black.assert_stable(source, actual, black.FileMode())
 
 178     def test_empty_ff(self) -> None:
 
 180         tmp_file = Path(black.dump_to_file())
 
 182             self.assertFalse(ff(tmp_file, write_back=black.WriteBack.YES))
 
 183             with open(tmp_file, encoding="utf8") as f:
 
 187         self.assertFormatEqual(expected, actual)
 
 189     @patch("black.dump_to_file", dump_to_stderr)
 
 190     def test_self(self) -> None:
 
 191         source, expected = read_data("test_black", data=False)
 
 193         self.assertFormatEqual(expected, actual)
 
 194         black.assert_equivalent(source, actual)
 
 195         black.assert_stable(source, actual, black.FileMode())
 
 196         self.assertFalse(ff(THIS_FILE))
 
 198     @patch("black.dump_to_file", dump_to_stderr)
 
 199     def test_black(self) -> None:
 
 200         source, expected = read_data("../black", data=False)
 
 202         self.assertFormatEqual(expected, actual)
 
 203         black.assert_equivalent(source, actual)
 
 204         black.assert_stable(source, actual, black.FileMode())
 
 205         self.assertFalse(ff(THIS_DIR / ".." / "black.py"))
 
 207     def test_piping(self) -> None:
 
 208         source, expected = read_data("../black", data=False)
 
 209         result = BlackRunner().invoke(
 
 211             ["-", "--fast", f"--line-length={black.DEFAULT_LINE_LENGTH}"],
 
 212             input=BytesIO(source.encode("utf8")),
 
 214         self.assertEqual(result.exit_code, 0)
 
 215         self.assertFormatEqual(expected, result.output)
 
 216         black.assert_equivalent(source, result.output)
 
 217         black.assert_stable(source, result.output, black.FileMode())
 
 219     def test_piping_diff(self) -> None:
 
 220         diff_header = re.compile(
 
 221             rf"(STDIN|STDOUT)\t\d\d\d\d-\d\d-\d\d "
 
 222             rf"\d\d:\d\d:\d\d\.\d\d\d\d\d\d \+\d\d\d\d"
 
 224         source, _ = read_data("expression.py")
 
 225         expected, _ = read_data("expression.diff")
 
 226         config = THIS_DIR / "data" / "empty_pyproject.toml"
 
 230             f"--line-length={black.DEFAULT_LINE_LENGTH}",
 
 232             f"--config={config}",
 
 234         result = BlackRunner().invoke(
 
 235             black.main, args, input=BytesIO(source.encode("utf8"))
 
 237         self.assertEqual(result.exit_code, 0)
 
 238         actual = diff_header.sub("[Deterministic header]", result.output)
 
 239         actual = actual.rstrip() + "\n"  # the diff output has a trailing space
 
 240         self.assertEqual(expected, actual)
 
 242     @patch("black.dump_to_file", dump_to_stderr)
 
 243     def test_setup(self) -> None:
 
 244         source, expected = read_data("../setup", data=False)
 
 246         self.assertFormatEqual(expected, actual)
 
 247         black.assert_equivalent(source, actual)
 
 248         black.assert_stable(source, actual, black.FileMode())
 
 249         self.assertFalse(ff(THIS_DIR / ".." / "setup.py"))
 
 251     @patch("black.dump_to_file", dump_to_stderr)
 
 252     def test_function(self) -> None:
 
 253         source, expected = read_data("function")
 
 255         self.assertFormatEqual(expected, actual)
 
 256         black.assert_equivalent(source, actual)
 
 257         black.assert_stable(source, actual, black.FileMode())
 
 259     @patch("black.dump_to_file", dump_to_stderr)
 
 260     def test_function2(self) -> None:
 
 261         source, expected = read_data("function2")
 
 263         self.assertFormatEqual(expected, actual)
 
 264         black.assert_equivalent(source, actual)
 
 265         black.assert_stable(source, actual, black.FileMode())
 
 267     @patch("black.dump_to_file", dump_to_stderr)
 
 268     def test_expression(self) -> None:
 
 269         source, expected = read_data("expression")
 
 271         self.assertFormatEqual(expected, actual)
 
 272         black.assert_equivalent(source, actual)
 
 273         black.assert_stable(source, actual, black.FileMode())
 
 275     def test_expression_ff(self) -> None:
 
 276         source, expected = read_data("expression")
 
 277         tmp_file = Path(black.dump_to_file(source))
 
 279             self.assertTrue(ff(tmp_file, write_back=black.WriteBack.YES))
 
 280             with open(tmp_file, encoding="utf8") as f:
 
 284         self.assertFormatEqual(expected, actual)
 
 285         with patch("black.dump_to_file", dump_to_stderr):
 
 286             black.assert_equivalent(source, actual)
 
 287             black.assert_stable(source, actual, black.FileMode())
 
 289     def test_expression_diff(self) -> None:
 
 290         source, _ = read_data("expression.py")
 
 291         expected, _ = read_data("expression.diff")
 
 292         tmp_file = Path(black.dump_to_file(source))
 
 293         diff_header = re.compile(
 
 294             rf"{re.escape(str(tmp_file))}\t\d\d\d\d-\d\d-\d\d "
 
 295             rf"\d\d:\d\d:\d\d\.\d\d\d\d\d\d \+\d\d\d\d"
 
 298             result = BlackRunner().invoke(black.main, ["--diff", str(tmp_file)])
 
 299             self.assertEqual(result.exit_code, 0)
 
 302         actual = result.output
 
 303         actual = diff_header.sub("[Deterministic header]", actual)
 
 304         actual = actual.rstrip() + "\n"  # the diff output has a trailing space
 
 305         if expected != actual:
 
 306             dump = black.dump_to_file(actual)
 
 308                 f"Expected diff isn't equal to the actual. If you made changes "
 
 309                 f"to expression.py and this is an anticipated difference, "
 
 310                 f"overwrite tests/data/expression.diff with {dump}"
 
 312             self.assertEqual(expected, actual, msg)
 
 314     @patch("black.dump_to_file", dump_to_stderr)
 
 315     def test_fstring(self) -> None:
 
 316         source, expected = read_data("fstring")
 
 318         self.assertFormatEqual(expected, actual)
 
 319         black.assert_equivalent(source, actual)
 
 320         black.assert_stable(source, actual, black.FileMode())
 
 322     @patch("black.dump_to_file", dump_to_stderr)
 
 323     def test_string_quotes(self) -> None:
 
 324         source, expected = read_data("string_quotes")
 
 326         self.assertFormatEqual(expected, actual)
 
 327         black.assert_equivalent(source, actual)
 
 328         black.assert_stable(source, actual, black.FileMode())
 
 329         mode = black.FileMode(string_normalization=False)
 
 330         not_normalized = fs(source, mode=mode)
 
 331         self.assertFormatEqual(source, not_normalized)
 
 332         black.assert_equivalent(source, not_normalized)
 
 333         black.assert_stable(source, not_normalized, mode=mode)
 
 335     @patch("black.dump_to_file", dump_to_stderr)
 
 336     def test_slices(self) -> None:
 
 337         source, expected = read_data("slices")
 
 339         self.assertFormatEqual(expected, actual)
 
 340         black.assert_equivalent(source, actual)
 
 341         black.assert_stable(source, actual, black.FileMode())
 
 343     @patch("black.dump_to_file", dump_to_stderr)
 
 344     def test_comments(self) -> None:
 
 345         source, expected = read_data("comments")
 
 347         self.assertFormatEqual(expected, actual)
 
 348         black.assert_equivalent(source, actual)
 
 349         black.assert_stable(source, actual, black.FileMode())
 
 351     @patch("black.dump_to_file", dump_to_stderr)
 
 352     def test_comments2(self) -> None:
 
 353         source, expected = read_data("comments2")
 
 355         self.assertFormatEqual(expected, actual)
 
 356         black.assert_equivalent(source, actual)
 
 357         black.assert_stable(source, actual, black.FileMode())
 
 359     @patch("black.dump_to_file", dump_to_stderr)
 
 360     def test_comments3(self) -> None:
 
 361         source, expected = read_data("comments3")
 
 363         self.assertFormatEqual(expected, actual)
 
 364         black.assert_equivalent(source, actual)
 
 365         black.assert_stable(source, actual, black.FileMode())
 
 367     @patch("black.dump_to_file", dump_to_stderr)
 
 368     def test_comments4(self) -> None:
 
 369         source, expected = read_data("comments4")
 
 371         self.assertFormatEqual(expected, actual)
 
 372         black.assert_equivalent(source, actual)
 
 373         black.assert_stable(source, actual, black.FileMode())
 
 375     @patch("black.dump_to_file", dump_to_stderr)
 
 376     def test_comments5(self) -> None:
 
 377         source, expected = read_data("comments5")
 
 379         self.assertFormatEqual(expected, actual)
 
 380         black.assert_equivalent(source, actual)
 
 381         black.assert_stable(source, actual, black.FileMode())
 
 383     @patch("black.dump_to_file", dump_to_stderr)
 
 384     def test_comments6(self) -> None:
 
 385         source, expected = read_data("comments6")
 
 387         self.assertFormatEqual(expected, actual)
 
 388         black.assert_equivalent(source, actual)
 
 389         black.assert_stable(source, actual, black.FileMode())
 
 391     @patch("black.dump_to_file", dump_to_stderr)
 
 392     def test_comments7(self) -> None:
 
 393         source, expected = read_data("comments7")
 
 395         self.assertFormatEqual(expected, actual)
 
 396         black.assert_equivalent(source, actual)
 
 397         black.assert_stable(source, actual, black.FileMode())
 
 399     @patch("black.dump_to_file", dump_to_stderr)
 
 400     def test_comment_after_escaped_newline(self) -> None:
 
 401         source, expected = read_data("comment_after_escaped_newline")
 
 403         self.assertFormatEqual(expected, actual)
 
 404         black.assert_equivalent(source, actual)
 
 405         black.assert_stable(source, actual, black.FileMode())
 
 407     @patch("black.dump_to_file", dump_to_stderr)
 
 408     def test_cantfit(self) -> None:
 
 409         source, expected = read_data("cantfit")
 
 411         self.assertFormatEqual(expected, actual)
 
 412         black.assert_equivalent(source, actual)
 
 413         black.assert_stable(source, actual, black.FileMode())
 
 415     @patch("black.dump_to_file", dump_to_stderr)
 
 416     def test_import_spacing(self) -> None:
 
 417         source, expected = read_data("import_spacing")
 
 419         self.assertFormatEqual(expected, actual)
 
 420         black.assert_equivalent(source, actual)
 
 421         black.assert_stable(source, actual, black.FileMode())
 
 423     @patch("black.dump_to_file", dump_to_stderr)
 
 424     def test_composition(self) -> None:
 
 425         source, expected = read_data("composition")
 
 427         self.assertFormatEqual(expected, actual)
 
 428         black.assert_equivalent(source, actual)
 
 429         black.assert_stable(source, actual, black.FileMode())
 
 431     @patch("black.dump_to_file", dump_to_stderr)
 
 432     def test_empty_lines(self) -> None:
 
 433         source, expected = read_data("empty_lines")
 
 435         self.assertFormatEqual(expected, actual)
 
 436         black.assert_equivalent(source, actual)
 
 437         black.assert_stable(source, actual, black.FileMode())
 
 439     @patch("black.dump_to_file", dump_to_stderr)
 
 440     def test_remove_parens(self) -> None:
 
 441         source, expected = read_data("remove_parens")
 
 443         self.assertFormatEqual(expected, actual)
 
 444         black.assert_equivalent(source, actual)
 
 445         black.assert_stable(source, actual, black.FileMode())
 
 447     @patch("black.dump_to_file", dump_to_stderr)
 
 448     def test_string_prefixes(self) -> None:
 
 449         source, expected = read_data("string_prefixes")
 
 451         self.assertFormatEqual(expected, actual)
 
 452         black.assert_equivalent(source, actual)
 
 453         black.assert_stable(source, actual, black.FileMode())
 
 455     @patch("black.dump_to_file", dump_to_stderr)
 
 456     def test_numeric_literals(self) -> None:
 
 457         source, expected = read_data("numeric_literals")
 
 458         mode = black.FileMode(target_versions=black.PY36_VERSIONS)
 
 459         actual = fs(source, mode=mode)
 
 460         self.assertFormatEqual(expected, actual)
 
 461         black.assert_equivalent(source, actual)
 
 462         black.assert_stable(source, actual, mode)
 
 464     @patch("black.dump_to_file", dump_to_stderr)
 
 465     def test_numeric_literals_ignoring_underscores(self) -> None:
 
 466         source, expected = read_data("numeric_literals_skip_underscores")
 
 467         mode = black.FileMode(target_versions=black.PY36_VERSIONS)
 
 468         actual = fs(source, mode=mode)
 
 469         self.assertFormatEqual(expected, actual)
 
 470         black.assert_equivalent(source, actual)
 
 471         black.assert_stable(source, actual, mode)
 
 473     @patch("black.dump_to_file", dump_to_stderr)
 
 474     def test_numeric_literals_py2(self) -> None:
 
 475         source, expected = read_data("numeric_literals_py2")
 
 477         self.assertFormatEqual(expected, actual)
 
 478         black.assert_stable(source, actual, black.FileMode())
 
 480     @patch("black.dump_to_file", dump_to_stderr)
 
 481     def test_python2(self) -> None:
 
 482         source, expected = read_data("python2")
 
 484         self.assertFormatEqual(expected, actual)
 
 485         black.assert_equivalent(source, actual)
 
 486         black.assert_stable(source, actual, black.FileMode())
 
 488     @patch("black.dump_to_file", dump_to_stderr)
 
 489     def test_python2_print_function(self) -> None:
 
 490         source, expected = read_data("python2_print_function")
 
 491         mode = black.FileMode(target_versions={TargetVersion.PY27})
 
 492         actual = fs(source, mode=mode)
 
 493         self.assertFormatEqual(expected, actual)
 
 494         black.assert_equivalent(source, actual)
 
 495         black.assert_stable(source, actual, mode)
 
 497     @patch("black.dump_to_file", dump_to_stderr)
 
 498     def test_python2_unicode_literals(self) -> None:
 
 499         source, expected = read_data("python2_unicode_literals")
 
 501         self.assertFormatEqual(expected, actual)
 
 502         black.assert_equivalent(source, actual)
 
 503         black.assert_stable(source, actual, black.FileMode())
 
 505     @patch("black.dump_to_file", dump_to_stderr)
 
 506     def test_stub(self) -> None:
 
 507         mode = black.FileMode(is_pyi=True)
 
 508         source, expected = read_data("stub.pyi")
 
 509         actual = fs(source, mode=mode)
 
 510         self.assertFormatEqual(expected, actual)
 
 511         black.assert_stable(source, actual, mode)
 
 513     @patch("black.dump_to_file", dump_to_stderr)
 
 514     def test_async_as_identifier(self) -> None:
 
 515         source_path = (THIS_DIR / "data" / "async_as_identifier.py").resolve()
 
 516         source, expected = read_data("async_as_identifier")
 
 518         self.assertFormatEqual(expected, actual)
 
 519         major, minor = sys.version_info[:2]
 
 520         if major < 3 or (major <= 3 and minor < 7):
 
 521             black.assert_equivalent(source, actual)
 
 522         black.assert_stable(source, actual, black.FileMode())
 
 523         # ensure black can parse this when the target is 3.6
 
 524         self.invokeBlack([str(source_path), "--target-version", "py36"])
 
 525         # but not on 3.7, because async/await is no longer an identifier
 
 526         self.invokeBlack([str(source_path), "--target-version", "py37"], exit_code=123)
 
 528     @patch("black.dump_to_file", dump_to_stderr)
 
 529     def test_python37(self) -> None:
 
 530         source_path = (THIS_DIR / "data" / "python37.py").resolve()
 
 531         source, expected = read_data("python37")
 
 533         self.assertFormatEqual(expected, actual)
 
 534         major, minor = sys.version_info[:2]
 
 535         if major > 3 or (major == 3 and minor >= 7):
 
 536             black.assert_equivalent(source, actual)
 
 537         black.assert_stable(source, actual, black.FileMode())
 
 538         # ensure black can parse this when the target is 3.7
 
 539         self.invokeBlack([str(source_path), "--target-version", "py37"])
 
 540         # but not on 3.6, because we use async as a reserved keyword
 
 541         self.invokeBlack([str(source_path), "--target-version", "py36"], exit_code=123)
 
 543     @patch("black.dump_to_file", dump_to_stderr)
 
 544     def test_fmtonoff(self) -> None:
 
 545         source, expected = read_data("fmtonoff")
 
 547         self.assertFormatEqual(expected, actual)
 
 548         black.assert_equivalent(source, actual)
 
 549         black.assert_stable(source, actual, black.FileMode())
 
 551     @patch("black.dump_to_file", dump_to_stderr)
 
 552     def test_fmtonoff2(self) -> None:
 
 553         source, expected = read_data("fmtonoff2")
 
 555         self.assertFormatEqual(expected, actual)
 
 556         black.assert_equivalent(source, actual)
 
 557         black.assert_stable(source, actual, black.FileMode())
 
 559     @patch("black.dump_to_file", dump_to_stderr)
 
 560     def test_remove_empty_parentheses_after_class(self) -> None:
 
 561         source, expected = read_data("class_blank_parentheses")
 
 563         self.assertFormatEqual(expected, actual)
 
 564         black.assert_equivalent(source, actual)
 
 565         black.assert_stable(source, actual, black.FileMode())
 
 567     @patch("black.dump_to_file", dump_to_stderr)
 
 568     def test_new_line_between_class_and_code(self) -> None:
 
 569         source, expected = read_data("class_methods_new_line")
 
 571         self.assertFormatEqual(expected, actual)
 
 572         black.assert_equivalent(source, actual)
 
 573         black.assert_stable(source, actual, black.FileMode())
 
 575     @patch("black.dump_to_file", dump_to_stderr)
 
 576     def test_bracket_match(self) -> None:
 
 577         source, expected = read_data("bracketmatch")
 
 579         self.assertFormatEqual(expected, actual)
 
 580         black.assert_equivalent(source, actual)
 
 581         black.assert_stable(source, actual, black.FileMode())
 
 583     @patch("black.dump_to_file", dump_to_stderr)
 
 584     def test_tuple_assign(self) -> None:
 
 585         source, expected = read_data("tupleassign")
 
 587         self.assertFormatEqual(expected, actual)
 
 588         black.assert_equivalent(source, actual)
 
 589         black.assert_stable(source, actual, black.FileMode())
 
 591     def test_tab_comment_indentation(self) -> None:
 
 592         contents_tab = "if 1:\n\tif 2:\n\t\tpass\n\t# comment\n\tpass\n"
 
 593         contents_spc = "if 1:\n    if 2:\n        pass\n    # comment\n    pass\n"
 
 594         self.assertFormatEqual(contents_spc, fs(contents_spc))
 
 595         self.assertFormatEqual(contents_spc, fs(contents_tab))
 
 597         contents_tab = "if 1:\n\tif 2:\n\t\tpass\n\t\t# comment\n\tpass\n"
 
 598         contents_spc = "if 1:\n    if 2:\n        pass\n        # comment\n    pass\n"
 
 599         self.assertFormatEqual(contents_spc, fs(contents_spc))
 
 600         self.assertFormatEqual(contents_spc, fs(contents_tab))
 
 602         # mixed tabs and spaces (valid Python 2 code)
 
 603         contents_tab = "if 1:\n        if 2:\n\t\tpass\n\t# comment\n        pass\n"
 
 604         contents_spc = "if 1:\n    if 2:\n        pass\n    # comment\n    pass\n"
 
 605         self.assertFormatEqual(contents_spc, fs(contents_spc))
 
 606         self.assertFormatEqual(contents_spc, fs(contents_tab))
 
 608         contents_tab = "if 1:\n        if 2:\n\t\tpass\n\t\t# comment\n        pass\n"
 
 609         contents_spc = "if 1:\n    if 2:\n        pass\n        # comment\n    pass\n"
 
 610         self.assertFormatEqual(contents_spc, fs(contents_spc))
 
 611         self.assertFormatEqual(contents_spc, fs(contents_tab))
 
 613     def test_report_verbose(self) -> None:
 
 614         report = black.Report(verbose=True)
 
 618         def out(msg: str, **kwargs: Any) -> None:
 
 619             out_lines.append(msg)
 
 621         def err(msg: str, **kwargs: Any) -> None:
 
 622             err_lines.append(msg)
 
 624         with patch("black.out", out), patch("black.err", err):
 
 625             report.done(Path("f1"), black.Changed.NO)
 
 626             self.assertEqual(len(out_lines), 1)
 
 627             self.assertEqual(len(err_lines), 0)
 
 628             self.assertEqual(out_lines[-1], "f1 already well formatted, good job.")
 
 629             self.assertEqual(unstyle(str(report)), "1 file left unchanged.")
 
 630             self.assertEqual(report.return_code, 0)
 
 631             report.done(Path("f2"), black.Changed.YES)
 
 632             self.assertEqual(len(out_lines), 2)
 
 633             self.assertEqual(len(err_lines), 0)
 
 634             self.assertEqual(out_lines[-1], "reformatted f2")
 
 636                 unstyle(str(report)), "1 file reformatted, 1 file left unchanged."
 
 638             report.done(Path("f3"), black.Changed.CACHED)
 
 639             self.assertEqual(len(out_lines), 3)
 
 640             self.assertEqual(len(err_lines), 0)
 
 642                 out_lines[-1], "f3 wasn't modified on disk since last run."
 
 645                 unstyle(str(report)), "1 file reformatted, 2 files left unchanged."
 
 647             self.assertEqual(report.return_code, 0)
 
 649             self.assertEqual(report.return_code, 1)
 
 651             report.failed(Path("e1"), "boom")
 
 652             self.assertEqual(len(out_lines), 3)
 
 653             self.assertEqual(len(err_lines), 1)
 
 654             self.assertEqual(err_lines[-1], "error: cannot format e1: boom")
 
 656                 unstyle(str(report)),
 
 657                 "1 file reformatted, 2 files left unchanged, "
 
 658                 "1 file failed to reformat.",
 
 660             self.assertEqual(report.return_code, 123)
 
 661             report.done(Path("f3"), black.Changed.YES)
 
 662             self.assertEqual(len(out_lines), 4)
 
 663             self.assertEqual(len(err_lines), 1)
 
 664             self.assertEqual(out_lines[-1], "reformatted f3")
 
 666                 unstyle(str(report)),
 
 667                 "2 files reformatted, 2 files left unchanged, "
 
 668                 "1 file failed to reformat.",
 
 670             self.assertEqual(report.return_code, 123)
 
 671             report.failed(Path("e2"), "boom")
 
 672             self.assertEqual(len(out_lines), 4)
 
 673             self.assertEqual(len(err_lines), 2)
 
 674             self.assertEqual(err_lines[-1], "error: cannot format e2: boom")
 
 676                 unstyle(str(report)),
 
 677                 "2 files reformatted, 2 files left unchanged, "
 
 678                 "2 files failed to reformat.",
 
 680             self.assertEqual(report.return_code, 123)
 
 681             report.path_ignored(Path("wat"), "no match")
 
 682             self.assertEqual(len(out_lines), 5)
 
 683             self.assertEqual(len(err_lines), 2)
 
 684             self.assertEqual(out_lines[-1], "wat ignored: no match")
 
 686                 unstyle(str(report)),
 
 687                 "2 files reformatted, 2 files left unchanged, "
 
 688                 "2 files failed to reformat.",
 
 690             self.assertEqual(report.return_code, 123)
 
 691             report.done(Path("f4"), black.Changed.NO)
 
 692             self.assertEqual(len(out_lines), 6)
 
 693             self.assertEqual(len(err_lines), 2)
 
 694             self.assertEqual(out_lines[-1], "f4 already well formatted, good job.")
 
 696                 unstyle(str(report)),
 
 697                 "2 files reformatted, 3 files left unchanged, "
 
 698                 "2 files failed to reformat.",
 
 700             self.assertEqual(report.return_code, 123)
 
 703                 unstyle(str(report)),
 
 704                 "2 files would be reformatted, 3 files would be left unchanged, "
 
 705                 "2 files would fail to reformat.",
 
 708     def test_report_quiet(self) -> None:
 
 709         report = black.Report(quiet=True)
 
 713         def out(msg: str, **kwargs: Any) -> None:
 
 714             out_lines.append(msg)
 
 716         def err(msg: str, **kwargs: Any) -> None:
 
 717             err_lines.append(msg)
 
 719         with patch("black.out", out), patch("black.err", err):
 
 720             report.done(Path("f1"), black.Changed.NO)
 
 721             self.assertEqual(len(out_lines), 0)
 
 722             self.assertEqual(len(err_lines), 0)
 
 723             self.assertEqual(unstyle(str(report)), "1 file left unchanged.")
 
 724             self.assertEqual(report.return_code, 0)
 
 725             report.done(Path("f2"), black.Changed.YES)
 
 726             self.assertEqual(len(out_lines), 0)
 
 727             self.assertEqual(len(err_lines), 0)
 
 729                 unstyle(str(report)), "1 file reformatted, 1 file left unchanged."
 
 731             report.done(Path("f3"), black.Changed.CACHED)
 
 732             self.assertEqual(len(out_lines), 0)
 
 733             self.assertEqual(len(err_lines), 0)
 
 735                 unstyle(str(report)), "1 file reformatted, 2 files left unchanged."
 
 737             self.assertEqual(report.return_code, 0)
 
 739             self.assertEqual(report.return_code, 1)
 
 741             report.failed(Path("e1"), "boom")
 
 742             self.assertEqual(len(out_lines), 0)
 
 743             self.assertEqual(len(err_lines), 1)
 
 744             self.assertEqual(err_lines[-1], "error: cannot format e1: boom")
 
 746                 unstyle(str(report)),
 
 747                 "1 file reformatted, 2 files left unchanged, "
 
 748                 "1 file failed to reformat.",
 
 750             self.assertEqual(report.return_code, 123)
 
 751             report.done(Path("f3"), black.Changed.YES)
 
 752             self.assertEqual(len(out_lines), 0)
 
 753             self.assertEqual(len(err_lines), 1)
 
 755                 unstyle(str(report)),
 
 756                 "2 files reformatted, 2 files left unchanged, "
 
 757                 "1 file failed to reformat.",
 
 759             self.assertEqual(report.return_code, 123)
 
 760             report.failed(Path("e2"), "boom")
 
 761             self.assertEqual(len(out_lines), 0)
 
 762             self.assertEqual(len(err_lines), 2)
 
 763             self.assertEqual(err_lines[-1], "error: cannot format e2: boom")
 
 765                 unstyle(str(report)),
 
 766                 "2 files reformatted, 2 files left unchanged, "
 
 767                 "2 files failed to reformat.",
 
 769             self.assertEqual(report.return_code, 123)
 
 770             report.path_ignored(Path("wat"), "no match")
 
 771             self.assertEqual(len(out_lines), 0)
 
 772             self.assertEqual(len(err_lines), 2)
 
 774                 unstyle(str(report)),
 
 775                 "2 files reformatted, 2 files left unchanged, "
 
 776                 "2 files failed to reformat.",
 
 778             self.assertEqual(report.return_code, 123)
 
 779             report.done(Path("f4"), black.Changed.NO)
 
 780             self.assertEqual(len(out_lines), 0)
 
 781             self.assertEqual(len(err_lines), 2)
 
 783                 unstyle(str(report)),
 
 784                 "2 files reformatted, 3 files left unchanged, "
 
 785                 "2 files failed to reformat.",
 
 787             self.assertEqual(report.return_code, 123)
 
 790                 unstyle(str(report)),
 
 791                 "2 files would be reformatted, 3 files would be left unchanged, "
 
 792                 "2 files would fail to reformat.",
 
 795     def test_report_normal(self) -> None:
 
 796         report = black.Report()
 
 800         def out(msg: str, **kwargs: Any) -> None:
 
 801             out_lines.append(msg)
 
 803         def err(msg: str, **kwargs: Any) -> None:
 
 804             err_lines.append(msg)
 
 806         with patch("black.out", out), patch("black.err", err):
 
 807             report.done(Path("f1"), black.Changed.NO)
 
 808             self.assertEqual(len(out_lines), 0)
 
 809             self.assertEqual(len(err_lines), 0)
 
 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), 1)
 
 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), 1)
 
 821             self.assertEqual(len(err_lines), 0)
 
 822             self.assertEqual(out_lines[-1], "reformatted f2")
 
 824                 unstyle(str(report)), "1 file reformatted, 2 files left unchanged."
 
 826             self.assertEqual(report.return_code, 0)
 
 828             self.assertEqual(report.return_code, 1)
 
 830             report.failed(Path("e1"), "boom")
 
 831             self.assertEqual(len(out_lines), 1)
 
 832             self.assertEqual(len(err_lines), 1)
 
 833             self.assertEqual(err_lines[-1], "error: cannot format e1: boom")
 
 835                 unstyle(str(report)),
 
 836                 "1 file reformatted, 2 files left unchanged, "
 
 837                 "1 file failed to reformat.",
 
 839             self.assertEqual(report.return_code, 123)
 
 840             report.done(Path("f3"), black.Changed.YES)
 
 841             self.assertEqual(len(out_lines), 2)
 
 842             self.assertEqual(len(err_lines), 1)
 
 843             self.assertEqual(out_lines[-1], "reformatted f3")
 
 845                 unstyle(str(report)),
 
 846                 "2 files reformatted, 2 files left unchanged, "
 
 847                 "1 file failed to reformat.",
 
 849             self.assertEqual(report.return_code, 123)
 
 850             report.failed(Path("e2"), "boom")
 
 851             self.assertEqual(len(out_lines), 2)
 
 852             self.assertEqual(len(err_lines), 2)
 
 853             self.assertEqual(err_lines[-1], "error: cannot format e2: boom")
 
 855                 unstyle(str(report)),
 
 856                 "2 files reformatted, 2 files left unchanged, "
 
 857                 "2 files failed to reformat.",
 
 859             self.assertEqual(report.return_code, 123)
 
 860             report.path_ignored(Path("wat"), "no match")
 
 861             self.assertEqual(len(out_lines), 2)
 
 862             self.assertEqual(len(err_lines), 2)
 
 864                 unstyle(str(report)),
 
 865                 "2 files reformatted, 2 files left unchanged, "
 
 866                 "2 files failed to reformat.",
 
 868             self.assertEqual(report.return_code, 123)
 
 869             report.done(Path("f4"), black.Changed.NO)
 
 870             self.assertEqual(len(out_lines), 2)
 
 871             self.assertEqual(len(err_lines), 2)
 
 873                 unstyle(str(report)),
 
 874                 "2 files reformatted, 3 files left unchanged, "
 
 875                 "2 files failed to reformat.",
 
 877             self.assertEqual(report.return_code, 123)
 
 880                 unstyle(str(report)),
 
 881                 "2 files would be reformatted, 3 files would be left unchanged, "
 
 882                 "2 files would fail to reformat.",
 
 885     def test_lib2to3_parse(self) -> None:
 
 886         with self.assertRaises(black.InvalidInput):
 
 887             black.lib2to3_parse("invalid syntax")
 
 890         black.lib2to3_parse(straddling)
 
 891         black.lib2to3_parse(straddling, {TargetVersion.PY27})
 
 892         black.lib2to3_parse(straddling, {TargetVersion.PY36})
 
 893         black.lib2to3_parse(straddling, {TargetVersion.PY27, TargetVersion.PY36})
 
 896         black.lib2to3_parse(py2_only)
 
 897         black.lib2to3_parse(py2_only, {TargetVersion.PY27})
 
 898         with self.assertRaises(black.InvalidInput):
 
 899             black.lib2to3_parse(py2_only, {TargetVersion.PY36})
 
 900         with self.assertRaises(black.InvalidInput):
 
 901             black.lib2to3_parse(py2_only, {TargetVersion.PY27, TargetVersion.PY36})
 
 903         py3_only = "exec(x, end=y)"
 
 904         black.lib2to3_parse(py3_only)
 
 905         with self.assertRaises(black.InvalidInput):
 
 906             black.lib2to3_parse(py3_only, {TargetVersion.PY27})
 
 907         black.lib2to3_parse(py3_only, {TargetVersion.PY36})
 
 908         black.lib2to3_parse(py3_only, {TargetVersion.PY27, TargetVersion.PY36})
 
 910     def test_get_features_used(self) -> None:
 
 911         node = black.lib2to3_parse("def f(*, arg): ...\n")
 
 912         self.assertEqual(black.get_features_used(node), set())
 
 913         node = black.lib2to3_parse("def f(*, arg,): ...\n")
 
 914         self.assertEqual(black.get_features_used(node), {Feature.TRAILING_COMMA_IN_DEF})
 
 915         node = black.lib2to3_parse("f(*arg,)\n")
 
 917             black.get_features_used(node), {Feature.TRAILING_COMMA_IN_CALL}
 
 919         node = black.lib2to3_parse("def f(*, arg): f'string'\n")
 
 920         self.assertEqual(black.get_features_used(node), {Feature.F_STRINGS})
 
 921         node = black.lib2to3_parse("123_456\n")
 
 922         self.assertEqual(black.get_features_used(node), {Feature.NUMERIC_UNDERSCORES})
 
 923         node = black.lib2to3_parse("123456\n")
 
 924         self.assertEqual(black.get_features_used(node), set())
 
 925         source, expected = read_data("function")
 
 926         node = black.lib2to3_parse(source)
 
 927         expected_features = {
 
 928             Feature.TRAILING_COMMA_IN_CALL,
 
 929             Feature.TRAILING_COMMA_IN_DEF,
 
 932         self.assertEqual(black.get_features_used(node), expected_features)
 
 933         node = black.lib2to3_parse(expected)
 
 934         self.assertEqual(black.get_features_used(node), expected_features)
 
 935         source, expected = read_data("expression")
 
 936         node = black.lib2to3_parse(source)
 
 937         self.assertEqual(black.get_features_used(node), set())
 
 938         node = black.lib2to3_parse(expected)
 
 939         self.assertEqual(black.get_features_used(node), set())
 
 941     def test_get_future_imports(self) -> None:
 
 942         node = black.lib2to3_parse("\n")
 
 943         self.assertEqual(set(), black.get_future_imports(node))
 
 944         node = black.lib2to3_parse("from __future__ import black\n")
 
 945         self.assertEqual({"black"}, black.get_future_imports(node))
 
 946         node = black.lib2to3_parse("from __future__ import multiple, imports\n")
 
 947         self.assertEqual({"multiple", "imports"}, black.get_future_imports(node))
 
 948         node = black.lib2to3_parse("from __future__ import (parenthesized, imports)\n")
 
 949         self.assertEqual({"parenthesized", "imports"}, black.get_future_imports(node))
 
 950         node = black.lib2to3_parse(
 
 951             "from __future__ import multiple\nfrom __future__ import imports\n"
 
 953         self.assertEqual({"multiple", "imports"}, black.get_future_imports(node))
 
 954         node = black.lib2to3_parse("# comment\nfrom __future__ import black\n")
 
 955         self.assertEqual({"black"}, black.get_future_imports(node))
 
 956         node = black.lib2to3_parse('"""docstring"""\nfrom __future__ import black\n')
 
 957         self.assertEqual({"black"}, black.get_future_imports(node))
 
 958         node = black.lib2to3_parse("some(other, code)\nfrom __future__ import black\n")
 
 959         self.assertEqual(set(), black.get_future_imports(node))
 
 960         node = black.lib2to3_parse("from some.module import black\n")
 
 961         self.assertEqual(set(), black.get_future_imports(node))
 
 962         node = black.lib2to3_parse(
 
 963             "from __future__ import unicode_literals as _unicode_literals"
 
 965         self.assertEqual({"unicode_literals"}, black.get_future_imports(node))
 
 966         node = black.lib2to3_parse(
 
 967             "from __future__ import unicode_literals as _lol, print"
 
 969         self.assertEqual({"unicode_literals", "print"}, black.get_future_imports(node))
 
 971     def test_debug_visitor(self) -> None:
 
 972         source, _ = read_data("debug_visitor.py")
 
 973         expected, _ = read_data("debug_visitor.out")
 
 977         def out(msg: str, **kwargs: Any) -> None:
 
 978             out_lines.append(msg)
 
 980         def err(msg: str, **kwargs: Any) -> None:
 
 981             err_lines.append(msg)
 
 983         with patch("black.out", out), patch("black.err", err):
 
 984             black.DebugVisitor.show(source)
 
 985         actual = "\n".join(out_lines) + "\n"
 
 987         if expected != actual:
 
 988             log_name = black.dump_to_file(*out_lines)
 
 992             f"AST print out is different. Actual version dumped to {log_name}",
 
 995     def test_format_file_contents(self) -> None:
 
 997         mode = black.FileMode()
 
 998         with self.assertRaises(black.NothingChanged):
 
 999             black.format_file_contents(empty, mode=mode, fast=False)
 
1001         with self.assertRaises(black.NothingChanged):
 
1002             black.format_file_contents(just_nl, mode=mode, fast=False)
 
1003         same = "l = [1, 2, 3]\n"
 
1004         with self.assertRaises(black.NothingChanged):
 
1005             black.format_file_contents(same, mode=mode, fast=False)
 
1006         different = "l = [1,2,3]"
 
1008         actual = black.format_file_contents(different, mode=mode, fast=False)
 
1009         self.assertEqual(expected, actual)
 
1010         invalid = "return if you can"
 
1011         with self.assertRaises(black.InvalidInput) as e:
 
1012             black.format_file_contents(invalid, mode=mode, fast=False)
 
1013         self.assertEqual(str(e.exception), "Cannot parse: 1:7: return if you can")
 
1015     def test_endmarker(self) -> None:
 
1016         n = black.lib2to3_parse("\n")
 
1017         self.assertEqual(n.type, black.syms.file_input)
 
1018         self.assertEqual(len(n.children), 1)
 
1019         self.assertEqual(n.children[0].type, black.token.ENDMARKER)
 
1021     @unittest.skipIf(os.environ.get("SKIP_AST_PRINT"), "user set SKIP_AST_PRINT")
 
1022     def test_assertFormatEqual(self) -> None:
 
1026         def out(msg: str, **kwargs: Any) -> None:
 
1027             out_lines.append(msg)
 
1029         def err(msg: str, **kwargs: Any) -> None:
 
1030             err_lines.append(msg)
 
1032         with patch("black.out", out), patch("black.err", err):
 
1033             with self.assertRaises(AssertionError):
 
1034                 self.assertFormatEqual("l = [1, 2, 3]", "l = [1, 2, 3,]")
 
1036         out_str = "".join(out_lines)
 
1037         self.assertTrue("Expected tree:" in out_str)
 
1038         self.assertTrue("Actual tree:" in out_str)
 
1039         self.assertEqual("".join(err_lines), "")
 
1041     def test_cache_broken_file(self) -> None:
 
1042         mode = black.FileMode()
 
1043         with cache_dir() as workspace:
 
1044             cache_file = black.get_cache_file(mode)
 
1045             with cache_file.open("w") as fobj:
 
1046                 fobj.write("this is not a pickle")
 
1047             self.assertEqual(black.read_cache(mode), {})
 
1048             src = (workspace / "test.py").resolve()
 
1049             with src.open("w") as fobj:
 
1050                 fobj.write("print('hello')")
 
1051             self.invokeBlack([str(src)])
 
1052             cache = black.read_cache(mode)
 
1053             self.assertIn(src, cache)
 
1055     def test_cache_single_file_already_cached(self) -> None:
 
1056         mode = black.FileMode()
 
1057         with cache_dir() as workspace:
 
1058             src = (workspace / "test.py").resolve()
 
1059             with src.open("w") as fobj:
 
1060                 fobj.write("print('hello')")
 
1061             black.write_cache({}, [src], mode)
 
1062             self.invokeBlack([str(src)])
 
1063             with src.open("r") as fobj:
 
1064                 self.assertEqual(fobj.read(), "print('hello')")
 
1066     @event_loop(close=False)
 
1067     def test_cache_multiple_files(self) -> None:
 
1068         mode = black.FileMode()
 
1069         with cache_dir() as workspace, patch(
 
1070             "black.ProcessPoolExecutor", new=ThreadPoolExecutor
 
1072             one = (workspace / "one.py").resolve()
 
1073             with one.open("w") as fobj:
 
1074                 fobj.write("print('hello')")
 
1075             two = (workspace / "two.py").resolve()
 
1076             with two.open("w") as fobj:
 
1077                 fobj.write("print('hello')")
 
1078             black.write_cache({}, [one], mode)
 
1079             self.invokeBlack([str(workspace)])
 
1080             with one.open("r") as fobj:
 
1081                 self.assertEqual(fobj.read(), "print('hello')")
 
1082             with two.open("r") as fobj:
 
1083                 self.assertEqual(fobj.read(), 'print("hello")\n')
 
1084             cache = black.read_cache(mode)
 
1085             self.assertIn(one, cache)
 
1086             self.assertIn(two, cache)
 
1088     def test_no_cache_when_writeback_diff(self) -> None:
 
1089         mode = black.FileMode()
 
1090         with cache_dir() as workspace:
 
1091             src = (workspace / "test.py").resolve()
 
1092             with src.open("w") as fobj:
 
1093                 fobj.write("print('hello')")
 
1094             self.invokeBlack([str(src), "--diff"])
 
1095             cache_file = black.get_cache_file(mode)
 
1096             self.assertFalse(cache_file.exists())
 
1098     def test_no_cache_when_stdin(self) -> None:
 
1099         mode = black.FileMode()
 
1101             result = CliRunner().invoke(
 
1102                 black.main, ["-"], input=BytesIO(b"print('hello')")
 
1104             self.assertEqual(result.exit_code, 0)
 
1105             cache_file = black.get_cache_file(mode)
 
1106             self.assertFalse(cache_file.exists())
 
1108     def test_read_cache_no_cachefile(self) -> None:
 
1109         mode = black.FileMode()
 
1111             self.assertEqual(black.read_cache(mode), {})
 
1113     def test_write_cache_read_cache(self) -> None:
 
1114         mode = black.FileMode()
 
1115         with cache_dir() as workspace:
 
1116             src = (workspace / "test.py").resolve()
 
1118             black.write_cache({}, [src], mode)
 
1119             cache = black.read_cache(mode)
 
1120             self.assertIn(src, cache)
 
1121             self.assertEqual(cache[src], black.get_cache_info(src))
 
1123     def test_filter_cached(self) -> None:
 
1124         with TemporaryDirectory() as workspace:
 
1125             path = Path(workspace)
 
1126             uncached = (path / "uncached").resolve()
 
1127             cached = (path / "cached").resolve()
 
1128             cached_but_changed = (path / "changed").resolve()
 
1131             cached_but_changed.touch()
 
1132             cache = {cached: black.get_cache_info(cached), cached_but_changed: (0.0, 0)}
 
1133             todo, done = black.filter_cached(
 
1134                 cache, {uncached, cached, cached_but_changed}
 
1136             self.assertEqual(todo, {uncached, cached_but_changed})
 
1137             self.assertEqual(done, {cached})
 
1139     def test_write_cache_creates_directory_if_needed(self) -> None:
 
1140         mode = black.FileMode()
 
1141         with cache_dir(exists=False) as workspace:
 
1142             self.assertFalse(workspace.exists())
 
1143             black.write_cache({}, [], mode)
 
1144             self.assertTrue(workspace.exists())
 
1146     @event_loop(close=False)
 
1147     def test_failed_formatting_does_not_get_cached(self) -> None:
 
1148         mode = black.FileMode()
 
1149         with cache_dir() as workspace, patch(
 
1150             "black.ProcessPoolExecutor", new=ThreadPoolExecutor
 
1152             failing = (workspace / "failing.py").resolve()
 
1153             with failing.open("w") as fobj:
 
1154                 fobj.write("not actually python")
 
1155             clean = (workspace / "clean.py").resolve()
 
1156             with clean.open("w") as fobj:
 
1157                 fobj.write('print("hello")\n')
 
1158             self.invokeBlack([str(workspace)], exit_code=123)
 
1159             cache = black.read_cache(mode)
 
1160             self.assertNotIn(failing, cache)
 
1161             self.assertIn(clean, cache)
 
1163     def test_write_cache_write_fail(self) -> None:
 
1164         mode = black.FileMode()
 
1165         with cache_dir(), patch.object(Path, "open") as mock:
 
1166             mock.side_effect = OSError
 
1167             black.write_cache({}, [], mode)
 
1169     @event_loop(close=False)
 
1170     def test_check_diff_use_together(self) -> None:
 
1172             # Files which will be reformatted.
 
1173             src1 = (THIS_DIR / "data" / "string_quotes.py").resolve()
 
1174             self.invokeBlack([str(src1), "--diff", "--check"], exit_code=1)
 
1175             # Files which will not be reformatted.
 
1176             src2 = (THIS_DIR / "data" / "composition.py").resolve()
 
1177             self.invokeBlack([str(src2), "--diff", "--check"])
 
1178             # Multi file command.
 
1179             self.invokeBlack([str(src1), str(src2), "--diff", "--check"], exit_code=1)
 
1181     def test_no_files(self) -> None:
 
1183             # Without an argument, black exits with error code 0.
 
1184             self.invokeBlack([])
 
1186     def test_broken_symlink(self) -> None:
 
1187         with cache_dir() as workspace:
 
1188             symlink = workspace / "broken_link.py"
 
1190                 symlink.symlink_to("nonexistent.py")
 
1191             except OSError as e:
 
1192                 self.skipTest(f"Can't create symlinks: {e}")
 
1193             self.invokeBlack([str(workspace.resolve())])
 
1195     def test_read_cache_line_lengths(self) -> None:
 
1196         mode = black.FileMode()
 
1197         short_mode = black.FileMode(line_length=1)
 
1198         with cache_dir() as workspace:
 
1199             path = (workspace / "file.py").resolve()
 
1201             black.write_cache({}, [path], mode)
 
1202             one = black.read_cache(mode)
 
1203             self.assertIn(path, one)
 
1204             two = black.read_cache(short_mode)
 
1205             self.assertNotIn(path, two)
 
1207     def test_single_file_force_pyi(self) -> None:
 
1208         reg_mode = black.FileMode()
 
1209         pyi_mode = black.FileMode(is_pyi=True)
 
1210         contents, expected = read_data("force_pyi")
 
1211         with cache_dir() as workspace:
 
1212             path = (workspace / "file.py").resolve()
 
1213             with open(path, "w") as fh:
 
1215             self.invokeBlack([str(path), "--pyi"])
 
1216             with open(path, "r") as fh:
 
1218             # verify cache with --pyi is separate
 
1219             pyi_cache = black.read_cache(pyi_mode)
 
1220             self.assertIn(path, pyi_cache)
 
1221             normal_cache = black.read_cache(reg_mode)
 
1222             self.assertNotIn(path, normal_cache)
 
1223         self.assertEqual(actual, expected)
 
1225     @event_loop(close=False)
 
1226     def test_multi_file_force_pyi(self) -> None:
 
1227         reg_mode = black.FileMode()
 
1228         pyi_mode = black.FileMode(is_pyi=True)
 
1229         contents, expected = read_data("force_pyi")
 
1230         with cache_dir() as workspace:
 
1232                 (workspace / "file1.py").resolve(),
 
1233                 (workspace / "file2.py").resolve(),
 
1236                 with open(path, "w") as fh:
 
1238             self.invokeBlack([str(p) for p in paths] + ["--pyi"])
 
1240                 with open(path, "r") as fh:
 
1242                 self.assertEqual(actual, expected)
 
1243             # verify cache with --pyi is separate
 
1244             pyi_cache = black.read_cache(pyi_mode)
 
1245             normal_cache = black.read_cache(reg_mode)
 
1247                 self.assertIn(path, pyi_cache)
 
1248                 self.assertNotIn(path, normal_cache)
 
1250     def test_pipe_force_pyi(self) -> None:
 
1251         source, expected = read_data("force_pyi")
 
1252         result = CliRunner().invoke(
 
1253             black.main, ["-", "-q", "--pyi"], input=BytesIO(source.encode("utf8"))
 
1255         self.assertEqual(result.exit_code, 0)
 
1256         actual = result.output
 
1257         self.assertFormatEqual(actual, expected)
 
1259     def test_single_file_force_py36(self) -> None:
 
1260         reg_mode = black.FileMode()
 
1261         py36_mode = black.FileMode(target_versions=black.PY36_VERSIONS)
 
1262         source, expected = read_data("force_py36")
 
1263         with cache_dir() as workspace:
 
1264             path = (workspace / "file.py").resolve()
 
1265             with open(path, "w") as fh:
 
1267             self.invokeBlack([str(path), *PY36_ARGS])
 
1268             with open(path, "r") as fh:
 
1270             # verify cache with --target-version is separate
 
1271             py36_cache = black.read_cache(py36_mode)
 
1272             self.assertIn(path, py36_cache)
 
1273             normal_cache = black.read_cache(reg_mode)
 
1274             self.assertNotIn(path, normal_cache)
 
1275         self.assertEqual(actual, expected)
 
1277     @event_loop(close=False)
 
1278     def test_multi_file_force_py36(self) -> None:
 
1279         reg_mode = black.FileMode()
 
1280         py36_mode = black.FileMode(target_versions=black.PY36_VERSIONS)
 
1281         source, expected = read_data("force_py36")
 
1282         with cache_dir() as workspace:
 
1284                 (workspace / "file1.py").resolve(),
 
1285                 (workspace / "file2.py").resolve(),
 
1288                 with open(path, "w") as fh:
 
1290             self.invokeBlack([str(p) for p in paths] + PY36_ARGS)
 
1292                 with open(path, "r") as fh:
 
1294                 self.assertEqual(actual, expected)
 
1295             # verify cache with --target-version is separate
 
1296             pyi_cache = black.read_cache(py36_mode)
 
1297             normal_cache = black.read_cache(reg_mode)
 
1299                 self.assertIn(path, pyi_cache)
 
1300                 self.assertNotIn(path, normal_cache)
 
1302     def test_pipe_force_py36(self) -> None:
 
1303         source, expected = read_data("force_py36")
 
1304         result = CliRunner().invoke(
 
1306             ["-", "-q", "--target-version=py36"],
 
1307             input=BytesIO(source.encode("utf8")),
 
1309         self.assertEqual(result.exit_code, 0)
 
1310         actual = result.output
 
1311         self.assertFormatEqual(actual, expected)
 
1313     def test_include_exclude(self) -> None:
 
1314         path = THIS_DIR / "data" / "include_exclude_tests"
 
1315         include = re.compile(r"\.pyi?$")
 
1316         exclude = re.compile(r"/exclude/|/\.definitely_exclude/")
 
1317         report = black.Report()
 
1318         sources: List[Path] = []
 
1320             Path(path / "b/dont_exclude/a.py"),
 
1321             Path(path / "b/dont_exclude/a.pyi"),
 
1323         this_abs = THIS_DIR.resolve()
 
1325             black.gen_python_files_in_dir(path, this_abs, include, exclude, report)
 
1327         self.assertEqual(sorted(expected), sorted(sources))
 
1329     def test_empty_include(self) -> None:
 
1330         path = THIS_DIR / "data" / "include_exclude_tests"
 
1331         report = black.Report()
 
1332         empty = re.compile(r"")
 
1333         sources: List[Path] = []
 
1335             Path(path / "b/exclude/a.pie"),
 
1336             Path(path / "b/exclude/a.py"),
 
1337             Path(path / "b/exclude/a.pyi"),
 
1338             Path(path / "b/dont_exclude/a.pie"),
 
1339             Path(path / "b/dont_exclude/a.py"),
 
1340             Path(path / "b/dont_exclude/a.pyi"),
 
1341             Path(path / "b/.definitely_exclude/a.pie"),
 
1342             Path(path / "b/.definitely_exclude/a.py"),
 
1343             Path(path / "b/.definitely_exclude/a.pyi"),
 
1345         this_abs = THIS_DIR.resolve()
 
1347             black.gen_python_files_in_dir(
 
1348                 path, this_abs, empty, re.compile(black.DEFAULT_EXCLUDES), report
 
1351         self.assertEqual(sorted(expected), sorted(sources))
 
1353     def test_empty_exclude(self) -> None:
 
1354         path = THIS_DIR / "data" / "include_exclude_tests"
 
1355         report = black.Report()
 
1356         empty = re.compile(r"")
 
1357         sources: List[Path] = []
 
1359             Path(path / "b/dont_exclude/a.py"),
 
1360             Path(path / "b/dont_exclude/a.pyi"),
 
1361             Path(path / "b/exclude/a.py"),
 
1362             Path(path / "b/exclude/a.pyi"),
 
1363             Path(path / "b/.definitely_exclude/a.py"),
 
1364             Path(path / "b/.definitely_exclude/a.pyi"),
 
1366         this_abs = THIS_DIR.resolve()
 
1368             black.gen_python_files_in_dir(
 
1369                 path, this_abs, re.compile(black.DEFAULT_INCLUDES), empty, report
 
1372         self.assertEqual(sorted(expected), sorted(sources))
 
1374     def test_invalid_include_exclude(self) -> None:
 
1375         for option in ["--include", "--exclude"]:
 
1376             self.invokeBlack(["-", option, "**()(!!*)"], exit_code=2)
 
1378     def test_preserves_line_endings(self) -> None:
 
1379         with TemporaryDirectory() as workspace:
 
1380             test_file = Path(workspace) / "test.py"
 
1381             for nl in ["\n", "\r\n"]:
 
1382                 contents = nl.join(["def f(  ):", "    pass"])
 
1383                 test_file.write_bytes(contents.encode())
 
1384                 ff(test_file, write_back=black.WriteBack.YES)
 
1385                 updated_contents: bytes = test_file.read_bytes()
 
1386                 self.assertIn(nl.encode(), updated_contents)
 
1388                     self.assertNotIn(b"\r\n", updated_contents)
 
1390     def test_preserves_line_endings_via_stdin(self) -> None:
 
1391         for nl in ["\n", "\r\n"]:
 
1392             contents = nl.join(["def f(  ):", "    pass"])
 
1393             runner = BlackRunner()
 
1394             result = runner.invoke(
 
1395                 black.main, ["-", "--fast"], input=BytesIO(contents.encode("utf8"))
 
1397             self.assertEqual(result.exit_code, 0)
 
1398             output = runner.stdout_bytes
 
1399             self.assertIn(nl.encode("utf8"), output)
 
1401                 self.assertNotIn(b"\r\n", output)
 
1403     def test_assert_equivalent_different_asts(self) -> None:
 
1404         with self.assertRaises(AssertionError):
 
1405             black.assert_equivalent("{}", "None")
 
1407     def test_symlink_out_of_root_directory(self) -> None:
 
1411         include = re.compile(black.DEFAULT_INCLUDES)
 
1412         exclude = re.compile(black.DEFAULT_EXCLUDES)
 
1413         report = black.Report()
 
1414         # `child` should behave like a symlink which resolved path is clearly
 
1415         # outside of the `root` directory.
 
1416         path.iterdir.return_value = [child]
 
1417         child.resolve.return_value = Path("/a/b/c")
 
1418         child.is_symlink.return_value = True
 
1420             list(black.gen_python_files_in_dir(path, root, include, exclude, report))
 
1421         except ValueError as ve:
 
1422             self.fail(f"`get_python_files_in_dir()` failed: {ve}")
 
1423         path.iterdir.assert_called_once()
 
1424         child.resolve.assert_called_once()
 
1425         child.is_symlink.assert_called_once()
 
1426         # `child` should behave like a strange file which resolved path is clearly
 
1427         # outside of the `root` directory.
 
1428         child.is_symlink.return_value = False
 
1429         with self.assertRaises(ValueError):
 
1430             list(black.gen_python_files_in_dir(path, root, include, exclude, report))
 
1431         path.iterdir.assert_called()
 
1432         self.assertEqual(path.iterdir.call_count, 2)
 
1433         child.resolve.assert_called()
 
1434         self.assertEqual(child.resolve.call_count, 2)
 
1435         child.is_symlink.assert_called()
 
1436         self.assertEqual(child.is_symlink.call_count, 2)
 
1438     def test_shhh_click(self) -> None:
 
1440             from click import _unicodefun  # type: ignore
 
1441         except ModuleNotFoundError:
 
1442             self.skipTest("Incompatible Click version")
 
1443         if not hasattr(_unicodefun, "_verify_python3_env"):
 
1444             self.skipTest("Incompatible Click version")
 
1445         # First, let's see if Click is crashing with a preferred ASCII charset.
 
1446         with patch("locale.getpreferredencoding") as gpe:
 
1447             gpe.return_value = "ASCII"
 
1448             with self.assertRaises(RuntimeError):
 
1449                 _unicodefun._verify_python3_env()
 
1450         # Now, let's silence Click...
 
1452         # ...and confirm it's silent.
 
1453         with patch("locale.getpreferredencoding") as gpe:
 
1454             gpe.return_value = "ASCII"
 
1456                 _unicodefun._verify_python3_env()
 
1457             except RuntimeError as re:
 
1458                 self.fail(f"`patch_click()` failed, exception still raised: {re}")
 
1460     def test_root_logger_not_used_directly(self) -> None:
 
1461         def fail(*args: Any, **kwargs: Any) -> None:
 
1462             self.fail("Record created with root logger")
 
1464         with patch.multiple(
 
1475     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1477     async def test_blackd_request_needs_formatting(self) -> None:
 
1478         app = blackd.make_app()
 
1479         async with TestClient(TestServer(app)) as client:
 
1480             response = await client.post("/", data=b"print('hello world')")
 
1481             self.assertEqual(response.status, 200)
 
1482             self.assertEqual(response.charset, "utf8")
 
1483             self.assertEqual(await response.read(), b'print("hello world")\n')
 
1485     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1487     async def test_blackd_request_no_change(self) -> None:
 
1488         app = blackd.make_app()
 
1489         async with TestClient(TestServer(app)) as client:
 
1490             response = await client.post("/", data=b'print("hello world")\n')
 
1491             self.assertEqual(response.status, 204)
 
1492             self.assertEqual(await response.read(), b"")
 
1494     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1496     async def test_blackd_request_syntax_error(self) -> None:
 
1497         app = blackd.make_app()
 
1498         async with TestClient(TestServer(app)) as client:
 
1499             response = await client.post("/", data=b"what even ( is")
 
1500             self.assertEqual(response.status, 400)
 
1501             content = await response.text()
 
1503                 content.startswith("Cannot parse"),
 
1504                 msg=f"Expected error to start with 'Cannot parse', got {repr(content)}",
 
1507     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1509     async def test_blackd_unsupported_version(self) -> None:
 
1510         app = blackd.make_app()
 
1511         async with TestClient(TestServer(app)) as client:
 
1512             response = await client.post(
 
1513                 "/", data=b"what", headers={blackd.VERSION_HEADER: "2"}
 
1515             self.assertEqual(response.status, 501)
 
1517     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1519     async def test_blackd_supported_version(self) -> None:
 
1520         app = blackd.make_app()
 
1521         async with TestClient(TestServer(app)) as client:
 
1522             response = await client.post(
 
1523                 "/", data=b"what", headers={blackd.VERSION_HEADER: "1"}
 
1525             self.assertEqual(response.status, 200)
 
1527     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1529     async def test_blackd_invalid_python_variant(self) -> None:
 
1530         app = blackd.make_app()
 
1531         async with TestClient(TestServer(app)) as client:
 
1533             async def check(header_value: str, expected_status: int = 400) -> None:
 
1534                 response = await client.post(
 
1537                     headers={blackd.PYTHON_VARIANT_HEADER: header_value},
 
1539                 self.assertEqual(response.status, expected_status)
 
1542             await check("ruby3.5")
 
1543             await check("pyi3.6")
 
1544             await check("py1.5")
 
1546             await check("py2.8")
 
1548             await check("pypy3.0")
 
1549             await check("jython3.4")
 
1551     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1553     async def test_blackd_pyi(self) -> None:
 
1554         app = blackd.make_app()
 
1555         async with TestClient(TestServer(app)) as client:
 
1556             source, expected = read_data("stub.pyi")
 
1557             response = await client.post(
 
1558                 "/", data=source, headers={blackd.PYTHON_VARIANT_HEADER: "pyi"}
 
1560             self.assertEqual(response.status, 200)
 
1561             self.assertEqual(await response.text(), expected)
 
1563     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1565     async def test_blackd_python_variant(self) -> None:
 
1566         app = blackd.make_app()
 
1569             "    and_has_a_bunch_of,\n"
 
1570             "    very_long_arguments_too,\n"
 
1571             "    and_lots_of_them_as_well_lol,\n"
 
1572             "    **and_very_long_keyword_arguments\n"
 
1576         async with TestClient(TestServer(app)) as client:
 
1578             async def check(header_value: str, expected_status: int) -> None:
 
1579                 response = await client.post(
 
1580                     "/", data=code, headers={blackd.PYTHON_VARIANT_HEADER: header_value}
 
1582                 self.assertEqual(response.status, expected_status)
 
1584             await check("3.6", 200)
 
1585             await check("py3.6", 200)
 
1586             await check("3.6,3.7", 200)
 
1587             await check("3.6,py3.7", 200)
 
1589             await check("2", 204)
 
1590             await check("2.7", 204)
 
1591             await check("py2.7", 204)
 
1592             await check("3.4", 204)
 
1593             await check("py3.4", 204)
 
1595     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1597     async def test_blackd_line_length(self) -> None:
 
1598         app = blackd.make_app()
 
1599         async with TestClient(TestServer(app)) as client:
 
1600             response = await client.post(
 
1601                 "/", data=b'print("hello")\n', headers={blackd.LINE_LENGTH_HEADER: "7"}
 
1603             self.assertEqual(response.status, 200)
 
1605     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1607     async def test_blackd_invalid_line_length(self) -> None:
 
1608         app = blackd.make_app()
 
1609         async with TestClient(TestServer(app)) as client:
 
1610             response = await client.post(
 
1612                 data=b'print("hello")\n',
 
1613                 headers={blackd.LINE_LENGTH_HEADER: "NaN"},
 
1615             self.assertEqual(response.status, 400)
 
1617     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1618     def test_blackd_main(self) -> None:
 
1619         with patch("blackd.web.run_app"):
 
1620             result = CliRunner().invoke(blackd.main, [])
 
1621             if result.exception is not None:
 
1622                 raise result.exception
 
1623             self.assertEqual(result.exit_code, 0)
 
1626 if __name__ == "__main__":
 
1627     unittest.main(module="test_black")