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, redirect_stderr
 
   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_cantfit(self) -> None:
 
 393         source, expected = read_data("cantfit")
 
 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_import_spacing(self) -> None:
 
 401         source, expected = read_data("import_spacing")
 
 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_composition(self) -> None:
 
 409         source, expected = read_data("composition")
 
 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_empty_lines(self) -> None:
 
 417         source, expected = read_data("empty_lines")
 
 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_string_prefixes(self) -> None:
 
 425         source, expected = read_data("string_prefixes")
 
 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_numeric_literals(self) -> None:
 
 433         source, expected = read_data("numeric_literals")
 
 434         mode = black.FileMode(target_versions=black.PY36_VERSIONS)
 
 435         actual = fs(source, mode=mode)
 
 436         self.assertFormatEqual(expected, actual)
 
 437         black.assert_equivalent(source, actual)
 
 438         black.assert_stable(source, actual, mode)
 
 440     @patch("black.dump_to_file", dump_to_stderr)
 
 441     def test_numeric_literals_ignoring_underscores(self) -> None:
 
 442         source, expected = read_data("numeric_literals_skip_underscores")
 
 443         mode = black.FileMode(target_versions=black.PY36_VERSIONS)
 
 444         actual = fs(source, mode=mode)
 
 445         self.assertFormatEqual(expected, actual)
 
 446         black.assert_equivalent(source, actual)
 
 447         black.assert_stable(source, actual, mode)
 
 449     @patch("black.dump_to_file", dump_to_stderr)
 
 450     def test_numeric_literals_py2(self) -> None:
 
 451         source, expected = read_data("numeric_literals_py2")
 
 453         self.assertFormatEqual(expected, actual)
 
 454         black.assert_stable(source, actual, black.FileMode())
 
 456     @patch("black.dump_to_file", dump_to_stderr)
 
 457     def test_python2(self) -> None:
 
 458         source, expected = read_data("python2")
 
 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_python2_print_function(self) -> None:
 
 466         source, expected = read_data("python2_print_function")
 
 467         mode = black.FileMode(target_versions={TargetVersion.PY27})
 
 468         actual = fs(source, mode=mode)
 
 469         self.assertFormatEqual(expected, actual)
 
 470         black.assert_stable(source, actual, mode)
 
 472     @patch("black.dump_to_file", dump_to_stderr)
 
 473     def test_python2_unicode_literals(self) -> None:
 
 474         source, expected = read_data("python2_unicode_literals")
 
 476         self.assertFormatEqual(expected, actual)
 
 477         black.assert_stable(source, actual, black.FileMode())
 
 479     @patch("black.dump_to_file", dump_to_stderr)
 
 480     def test_stub(self) -> None:
 
 481         mode = black.FileMode(is_pyi=True)
 
 482         source, expected = read_data("stub.pyi")
 
 483         actual = fs(source, mode=mode)
 
 484         self.assertFormatEqual(expected, actual)
 
 485         black.assert_stable(source, actual, mode)
 
 487     @patch("black.dump_to_file", dump_to_stderr)
 
 488     def test_python37(self) -> None:
 
 489         source, expected = read_data("python37")
 
 491         self.assertFormatEqual(expected, actual)
 
 492         major, minor = sys.version_info[:2]
 
 493         if major > 3 or (major == 3 and minor >= 7):
 
 494             black.assert_equivalent(source, actual)
 
 495         black.assert_stable(source, actual, black.FileMode())
 
 497     @patch("black.dump_to_file", dump_to_stderr)
 
 498     def test_fmtonoff(self) -> None:
 
 499         source, expected = read_data("fmtonoff")
 
 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_fmtonoff2(self) -> None:
 
 507         source, expected = read_data("fmtonoff2")
 
 509         self.assertFormatEqual(expected, actual)
 
 510         black.assert_equivalent(source, actual)
 
 511         black.assert_stable(source, actual, black.FileMode())
 
 513     @patch("black.dump_to_file", dump_to_stderr)
 
 514     def test_remove_empty_parentheses_after_class(self) -> None:
 
 515         source, expected = read_data("class_blank_parentheses")
 
 517         self.assertFormatEqual(expected, actual)
 
 518         black.assert_equivalent(source, actual)
 
 519         black.assert_stable(source, actual, black.FileMode())
 
 521     @patch("black.dump_to_file", dump_to_stderr)
 
 522     def test_new_line_between_class_and_code(self) -> None:
 
 523         source, expected = read_data("class_methods_new_line")
 
 525         self.assertFormatEqual(expected, actual)
 
 526         black.assert_equivalent(source, actual)
 
 527         black.assert_stable(source, actual, black.FileMode())
 
 529     @patch("black.dump_to_file", dump_to_stderr)
 
 530     def test_bracket_match(self) -> None:
 
 531         source, expected = read_data("bracketmatch")
 
 533         self.assertFormatEqual(expected, actual)
 
 534         black.assert_equivalent(source, actual)
 
 535         black.assert_stable(source, actual, black.FileMode())
 
 537     def test_tab_comment_indentation(self) -> None:
 
 538         contents_tab = "if 1:\n\tif 2:\n\t\tpass\n\t# comment\n\tpass\n"
 
 539         contents_spc = "if 1:\n    if 2:\n        pass\n    # comment\n    pass\n"
 
 540         self.assertFormatEqual(contents_spc, fs(contents_spc))
 
 541         self.assertFormatEqual(contents_spc, fs(contents_tab))
 
 543         contents_tab = "if 1:\n\tif 2:\n\t\tpass\n\t\t# comment\n\tpass\n"
 
 544         contents_spc = "if 1:\n    if 2:\n        pass\n        # comment\n    pass\n"
 
 545         self.assertFormatEqual(contents_spc, fs(contents_spc))
 
 546         self.assertFormatEqual(contents_spc, fs(contents_tab))
 
 548         # mixed tabs and spaces (valid Python 2 code)
 
 549         contents_tab = "if 1:\n        if 2:\n\t\tpass\n\t# comment\n        pass\n"
 
 550         contents_spc = "if 1:\n    if 2:\n        pass\n    # comment\n    pass\n"
 
 551         self.assertFormatEqual(contents_spc, fs(contents_spc))
 
 552         self.assertFormatEqual(contents_spc, fs(contents_tab))
 
 554         contents_tab = "if 1:\n        if 2:\n\t\tpass\n\t\t# comment\n        pass\n"
 
 555         contents_spc = "if 1:\n    if 2:\n        pass\n        # comment\n    pass\n"
 
 556         self.assertFormatEqual(contents_spc, fs(contents_spc))
 
 557         self.assertFormatEqual(contents_spc, fs(contents_tab))
 
 559     def test_report_verbose(self) -> None:
 
 560         report = black.Report(verbose=True)
 
 564         def out(msg: str, **kwargs: Any) -> None:
 
 565             out_lines.append(msg)
 
 567         def err(msg: str, **kwargs: Any) -> None:
 
 568             err_lines.append(msg)
 
 570         with patch("black.out", out), patch("black.err", err):
 
 571             report.done(Path("f1"), black.Changed.NO)
 
 572             self.assertEqual(len(out_lines), 1)
 
 573             self.assertEqual(len(err_lines), 0)
 
 574             self.assertEqual(out_lines[-1], "f1 already well formatted, good job.")
 
 575             self.assertEqual(unstyle(str(report)), "1 file left unchanged.")
 
 576             self.assertEqual(report.return_code, 0)
 
 577             report.done(Path("f2"), black.Changed.YES)
 
 578             self.assertEqual(len(out_lines), 2)
 
 579             self.assertEqual(len(err_lines), 0)
 
 580             self.assertEqual(out_lines[-1], "reformatted f2")
 
 582                 unstyle(str(report)), "1 file reformatted, 1 file left unchanged."
 
 584             report.done(Path("f3"), black.Changed.CACHED)
 
 585             self.assertEqual(len(out_lines), 3)
 
 586             self.assertEqual(len(err_lines), 0)
 
 588                 out_lines[-1], "f3 wasn't modified on disk since last run."
 
 591                 unstyle(str(report)), "1 file reformatted, 2 files left unchanged."
 
 593             self.assertEqual(report.return_code, 0)
 
 595             self.assertEqual(report.return_code, 1)
 
 597             report.failed(Path("e1"), "boom")
 
 598             self.assertEqual(len(out_lines), 3)
 
 599             self.assertEqual(len(err_lines), 1)
 
 600             self.assertEqual(err_lines[-1], "error: cannot format e1: boom")
 
 602                 unstyle(str(report)),
 
 603                 "1 file reformatted, 2 files left unchanged, "
 
 604                 "1 file failed to reformat.",
 
 606             self.assertEqual(report.return_code, 123)
 
 607             report.done(Path("f3"), black.Changed.YES)
 
 608             self.assertEqual(len(out_lines), 4)
 
 609             self.assertEqual(len(err_lines), 1)
 
 610             self.assertEqual(out_lines[-1], "reformatted f3")
 
 612                 unstyle(str(report)),
 
 613                 "2 files reformatted, 2 files left unchanged, "
 
 614                 "1 file failed to reformat.",
 
 616             self.assertEqual(report.return_code, 123)
 
 617             report.failed(Path("e2"), "boom")
 
 618             self.assertEqual(len(out_lines), 4)
 
 619             self.assertEqual(len(err_lines), 2)
 
 620             self.assertEqual(err_lines[-1], "error: cannot format e2: boom")
 
 622                 unstyle(str(report)),
 
 623                 "2 files reformatted, 2 files left unchanged, "
 
 624                 "2 files failed to reformat.",
 
 626             self.assertEqual(report.return_code, 123)
 
 627             report.path_ignored(Path("wat"), "no match")
 
 628             self.assertEqual(len(out_lines), 5)
 
 629             self.assertEqual(len(err_lines), 2)
 
 630             self.assertEqual(out_lines[-1], "wat ignored: no match")
 
 632                 unstyle(str(report)),
 
 633                 "2 files reformatted, 2 files left unchanged, "
 
 634                 "2 files failed to reformat.",
 
 636             self.assertEqual(report.return_code, 123)
 
 637             report.done(Path("f4"), black.Changed.NO)
 
 638             self.assertEqual(len(out_lines), 6)
 
 639             self.assertEqual(len(err_lines), 2)
 
 640             self.assertEqual(out_lines[-1], "f4 already well formatted, good job.")
 
 642                 unstyle(str(report)),
 
 643                 "2 files reformatted, 3 files left unchanged, "
 
 644                 "2 files failed to reformat.",
 
 646             self.assertEqual(report.return_code, 123)
 
 649                 unstyle(str(report)),
 
 650                 "2 files would be reformatted, 3 files would be left unchanged, "
 
 651                 "2 files would fail to reformat.",
 
 654     def test_report_quiet(self) -> None:
 
 655         report = black.Report(quiet=True)
 
 659         def out(msg: str, **kwargs: Any) -> None:
 
 660             out_lines.append(msg)
 
 662         def err(msg: str, **kwargs: Any) -> None:
 
 663             err_lines.append(msg)
 
 665         with patch("black.out", out), patch("black.err", err):
 
 666             report.done(Path("f1"), black.Changed.NO)
 
 667             self.assertEqual(len(out_lines), 0)
 
 668             self.assertEqual(len(err_lines), 0)
 
 669             self.assertEqual(unstyle(str(report)), "1 file left unchanged.")
 
 670             self.assertEqual(report.return_code, 0)
 
 671             report.done(Path("f2"), black.Changed.YES)
 
 672             self.assertEqual(len(out_lines), 0)
 
 673             self.assertEqual(len(err_lines), 0)
 
 675                 unstyle(str(report)), "1 file reformatted, 1 file left unchanged."
 
 677             report.done(Path("f3"), black.Changed.CACHED)
 
 678             self.assertEqual(len(out_lines), 0)
 
 679             self.assertEqual(len(err_lines), 0)
 
 681                 unstyle(str(report)), "1 file reformatted, 2 files left unchanged."
 
 683             self.assertEqual(report.return_code, 0)
 
 685             self.assertEqual(report.return_code, 1)
 
 687             report.failed(Path("e1"), "boom")
 
 688             self.assertEqual(len(out_lines), 0)
 
 689             self.assertEqual(len(err_lines), 1)
 
 690             self.assertEqual(err_lines[-1], "error: cannot format e1: boom")
 
 692                 unstyle(str(report)),
 
 693                 "1 file reformatted, 2 files left unchanged, "
 
 694                 "1 file failed to reformat.",
 
 696             self.assertEqual(report.return_code, 123)
 
 697             report.done(Path("f3"), black.Changed.YES)
 
 698             self.assertEqual(len(out_lines), 0)
 
 699             self.assertEqual(len(err_lines), 1)
 
 701                 unstyle(str(report)),
 
 702                 "2 files reformatted, 2 files left unchanged, "
 
 703                 "1 file failed to reformat.",
 
 705             self.assertEqual(report.return_code, 123)
 
 706             report.failed(Path("e2"), "boom")
 
 707             self.assertEqual(len(out_lines), 0)
 
 708             self.assertEqual(len(err_lines), 2)
 
 709             self.assertEqual(err_lines[-1], "error: cannot format e2: boom")
 
 711                 unstyle(str(report)),
 
 712                 "2 files reformatted, 2 files left unchanged, "
 
 713                 "2 files failed to reformat.",
 
 715             self.assertEqual(report.return_code, 123)
 
 716             report.path_ignored(Path("wat"), "no match")
 
 717             self.assertEqual(len(out_lines), 0)
 
 718             self.assertEqual(len(err_lines), 2)
 
 720                 unstyle(str(report)),
 
 721                 "2 files reformatted, 2 files left unchanged, "
 
 722                 "2 files failed to reformat.",
 
 724             self.assertEqual(report.return_code, 123)
 
 725             report.done(Path("f4"), black.Changed.NO)
 
 726             self.assertEqual(len(out_lines), 0)
 
 727             self.assertEqual(len(err_lines), 2)
 
 729                 unstyle(str(report)),
 
 730                 "2 files reformatted, 3 files left unchanged, "
 
 731                 "2 files failed to reformat.",
 
 733             self.assertEqual(report.return_code, 123)
 
 736                 unstyle(str(report)),
 
 737                 "2 files would be reformatted, 3 files would be left unchanged, "
 
 738                 "2 files would fail to reformat.",
 
 741     def test_report_normal(self) -> None:
 
 742         report = black.Report()
 
 746         def out(msg: str, **kwargs: Any) -> None:
 
 747             out_lines.append(msg)
 
 749         def err(msg: str, **kwargs: Any) -> None:
 
 750             err_lines.append(msg)
 
 752         with patch("black.out", out), patch("black.err", err):
 
 753             report.done(Path("f1"), black.Changed.NO)
 
 754             self.assertEqual(len(out_lines), 0)
 
 755             self.assertEqual(len(err_lines), 0)
 
 756             self.assertEqual(unstyle(str(report)), "1 file left unchanged.")
 
 757             self.assertEqual(report.return_code, 0)
 
 758             report.done(Path("f2"), black.Changed.YES)
 
 759             self.assertEqual(len(out_lines), 1)
 
 760             self.assertEqual(len(err_lines), 0)
 
 761             self.assertEqual(out_lines[-1], "reformatted f2")
 
 763                 unstyle(str(report)), "1 file reformatted, 1 file left unchanged."
 
 765             report.done(Path("f3"), black.Changed.CACHED)
 
 766             self.assertEqual(len(out_lines), 1)
 
 767             self.assertEqual(len(err_lines), 0)
 
 768             self.assertEqual(out_lines[-1], "reformatted f2")
 
 770                 unstyle(str(report)), "1 file reformatted, 2 files left unchanged."
 
 772             self.assertEqual(report.return_code, 0)
 
 774             self.assertEqual(report.return_code, 1)
 
 776             report.failed(Path("e1"), "boom")
 
 777             self.assertEqual(len(out_lines), 1)
 
 778             self.assertEqual(len(err_lines), 1)
 
 779             self.assertEqual(err_lines[-1], "error: cannot format e1: boom")
 
 781                 unstyle(str(report)),
 
 782                 "1 file reformatted, 2 files left unchanged, "
 
 783                 "1 file failed to reformat.",
 
 785             self.assertEqual(report.return_code, 123)
 
 786             report.done(Path("f3"), black.Changed.YES)
 
 787             self.assertEqual(len(out_lines), 2)
 
 788             self.assertEqual(len(err_lines), 1)
 
 789             self.assertEqual(out_lines[-1], "reformatted f3")
 
 791                 unstyle(str(report)),
 
 792                 "2 files reformatted, 2 files left unchanged, "
 
 793                 "1 file failed to reformat.",
 
 795             self.assertEqual(report.return_code, 123)
 
 796             report.failed(Path("e2"), "boom")
 
 797             self.assertEqual(len(out_lines), 2)
 
 798             self.assertEqual(len(err_lines), 2)
 
 799             self.assertEqual(err_lines[-1], "error: cannot format e2: boom")
 
 801                 unstyle(str(report)),
 
 802                 "2 files reformatted, 2 files left unchanged, "
 
 803                 "2 files failed to reformat.",
 
 805             self.assertEqual(report.return_code, 123)
 
 806             report.path_ignored(Path("wat"), "no match")
 
 807             self.assertEqual(len(out_lines), 2)
 
 808             self.assertEqual(len(err_lines), 2)
 
 810                 unstyle(str(report)),
 
 811                 "2 files reformatted, 2 files left unchanged, "
 
 812                 "2 files failed to reformat.",
 
 814             self.assertEqual(report.return_code, 123)
 
 815             report.done(Path("f4"), black.Changed.NO)
 
 816             self.assertEqual(len(out_lines), 2)
 
 817             self.assertEqual(len(err_lines), 2)
 
 819                 unstyle(str(report)),
 
 820                 "2 files reformatted, 3 files left unchanged, "
 
 821                 "2 files failed to reformat.",
 
 823             self.assertEqual(report.return_code, 123)
 
 826                 unstyle(str(report)),
 
 827                 "2 files would be reformatted, 3 files would be left unchanged, "
 
 828                 "2 files would fail to reformat.",
 
 831     def test_lib2to3_parse(self) -> None:
 
 832         with self.assertRaises(black.InvalidInput):
 
 833             black.lib2to3_parse("invalid syntax")
 
 836         black.lib2to3_parse(straddling)
 
 837         black.lib2to3_parse(straddling, {TargetVersion.PY27})
 
 838         black.lib2to3_parse(straddling, {TargetVersion.PY36})
 
 839         black.lib2to3_parse(straddling, {TargetVersion.PY27, TargetVersion.PY36})
 
 842         black.lib2to3_parse(py2_only)
 
 843         black.lib2to3_parse(py2_only, {TargetVersion.PY27})
 
 844         with self.assertRaises(black.InvalidInput):
 
 845             black.lib2to3_parse(py2_only, {TargetVersion.PY36})
 
 846         with self.assertRaises(black.InvalidInput):
 
 847             black.lib2to3_parse(py2_only, {TargetVersion.PY27, TargetVersion.PY36})
 
 849         py3_only = "exec(x, end=y)"
 
 850         black.lib2to3_parse(py3_only)
 
 851         with self.assertRaises(black.InvalidInput):
 
 852             black.lib2to3_parse(py3_only, {TargetVersion.PY27})
 
 853         black.lib2to3_parse(py3_only, {TargetVersion.PY36})
 
 854         black.lib2to3_parse(py3_only, {TargetVersion.PY27, TargetVersion.PY36})
 
 856     def test_get_features_used(self) -> None:
 
 857         node = black.lib2to3_parse("def f(*, arg): ...\n")
 
 858         self.assertEqual(black.get_features_used(node), set())
 
 859         node = black.lib2to3_parse("def f(*, arg,): ...\n")
 
 860         self.assertEqual(black.get_features_used(node), {Feature.TRAILING_COMMA_IN_DEF})
 
 861         node = black.lib2to3_parse("f(*arg,)\n")
 
 863             black.get_features_used(node), {Feature.TRAILING_COMMA_IN_CALL}
 
 865         node = black.lib2to3_parse("def f(*, arg): f'string'\n")
 
 866         self.assertEqual(black.get_features_used(node), {Feature.F_STRINGS})
 
 867         node = black.lib2to3_parse("123_456\n")
 
 868         self.assertEqual(black.get_features_used(node), {Feature.NUMERIC_UNDERSCORES})
 
 869         node = black.lib2to3_parse("123456\n")
 
 870         self.assertEqual(black.get_features_used(node), set())
 
 871         source, expected = read_data("function")
 
 872         node = black.lib2to3_parse(source)
 
 873         expected_features = {
 
 874             Feature.TRAILING_COMMA_IN_CALL,
 
 875             Feature.TRAILING_COMMA_IN_DEF,
 
 878         self.assertEqual(black.get_features_used(node), expected_features)
 
 879         node = black.lib2to3_parse(expected)
 
 880         self.assertEqual(black.get_features_used(node), expected_features)
 
 881         source, expected = read_data("expression")
 
 882         node = black.lib2to3_parse(source)
 
 883         self.assertEqual(black.get_features_used(node), set())
 
 884         node = black.lib2to3_parse(expected)
 
 885         self.assertEqual(black.get_features_used(node), set())
 
 887     def test_get_future_imports(self) -> None:
 
 888         node = black.lib2to3_parse("\n")
 
 889         self.assertEqual(set(), black.get_future_imports(node))
 
 890         node = black.lib2to3_parse("from __future__ import black\n")
 
 891         self.assertEqual({"black"}, black.get_future_imports(node))
 
 892         node = black.lib2to3_parse("from __future__ import multiple, imports\n")
 
 893         self.assertEqual({"multiple", "imports"}, black.get_future_imports(node))
 
 894         node = black.lib2to3_parse("from __future__ import (parenthesized, imports)\n")
 
 895         self.assertEqual({"parenthesized", "imports"}, black.get_future_imports(node))
 
 896         node = black.lib2to3_parse(
 
 897             "from __future__ import multiple\nfrom __future__ import imports\n"
 
 899         self.assertEqual({"multiple", "imports"}, black.get_future_imports(node))
 
 900         node = black.lib2to3_parse("# comment\nfrom __future__ import black\n")
 
 901         self.assertEqual({"black"}, black.get_future_imports(node))
 
 902         node = black.lib2to3_parse('"""docstring"""\nfrom __future__ import black\n')
 
 903         self.assertEqual({"black"}, black.get_future_imports(node))
 
 904         node = black.lib2to3_parse("some(other, code)\nfrom __future__ import black\n")
 
 905         self.assertEqual(set(), black.get_future_imports(node))
 
 906         node = black.lib2to3_parse("from some.module import black\n")
 
 907         self.assertEqual(set(), black.get_future_imports(node))
 
 908         node = black.lib2to3_parse(
 
 909             "from __future__ import unicode_literals as _unicode_literals"
 
 911         self.assertEqual({"unicode_literals"}, black.get_future_imports(node))
 
 912         node = black.lib2to3_parse(
 
 913             "from __future__ import unicode_literals as _lol, print"
 
 915         self.assertEqual({"unicode_literals", "print"}, black.get_future_imports(node))
 
 917     def test_debug_visitor(self) -> None:
 
 918         source, _ = read_data("debug_visitor.py")
 
 919         expected, _ = read_data("debug_visitor.out")
 
 923         def out(msg: str, **kwargs: Any) -> None:
 
 924             out_lines.append(msg)
 
 926         def err(msg: str, **kwargs: Any) -> None:
 
 927             err_lines.append(msg)
 
 929         with patch("black.out", out), patch("black.err", err):
 
 930             black.DebugVisitor.show(source)
 
 931         actual = "\n".join(out_lines) + "\n"
 
 933         if expected != actual:
 
 934             log_name = black.dump_to_file(*out_lines)
 
 938             f"AST print out is different. Actual version dumped to {log_name}",
 
 941     def test_format_file_contents(self) -> None:
 
 943         mode = black.FileMode()
 
 944         with self.assertRaises(black.NothingChanged):
 
 945             black.format_file_contents(empty, mode=mode, fast=False)
 
 947         with self.assertRaises(black.NothingChanged):
 
 948             black.format_file_contents(just_nl, mode=mode, fast=False)
 
 949         same = "l = [1, 2, 3]\n"
 
 950         with self.assertRaises(black.NothingChanged):
 
 951             black.format_file_contents(same, mode=mode, fast=False)
 
 952         different = "l = [1,2,3]"
 
 954         actual = black.format_file_contents(different, mode=mode, fast=False)
 
 955         self.assertEqual(expected, actual)
 
 956         invalid = "return if you can"
 
 957         with self.assertRaises(black.InvalidInput) as e:
 
 958             black.format_file_contents(invalid, mode=mode, fast=False)
 
 959         self.assertEqual(str(e.exception), "Cannot parse: 1:7: return if you can")
 
 961     def test_endmarker(self) -> None:
 
 962         n = black.lib2to3_parse("\n")
 
 963         self.assertEqual(n.type, black.syms.file_input)
 
 964         self.assertEqual(len(n.children), 1)
 
 965         self.assertEqual(n.children[0].type, black.token.ENDMARKER)
 
 967     @unittest.skipIf(os.environ.get("SKIP_AST_PRINT"), "user set SKIP_AST_PRINT")
 
 968     def test_assertFormatEqual(self) -> None:
 
 972         def out(msg: str, **kwargs: Any) -> None:
 
 973             out_lines.append(msg)
 
 975         def err(msg: str, **kwargs: Any) -> None:
 
 976             err_lines.append(msg)
 
 978         with patch("black.out", out), patch("black.err", err):
 
 979             with self.assertRaises(AssertionError):
 
 980                 self.assertFormatEqual("l = [1, 2, 3]", "l = [1, 2, 3,]")
 
 982         out_str = "".join(out_lines)
 
 983         self.assertTrue("Expected tree:" in out_str)
 
 984         self.assertTrue("Actual tree:" in out_str)
 
 985         self.assertEqual("".join(err_lines), "")
 
 987     def test_cache_broken_file(self) -> None:
 
 988         mode = black.FileMode()
 
 989         with cache_dir() as workspace:
 
 990             cache_file = black.get_cache_file(mode)
 
 991             with cache_file.open("w") as fobj:
 
 992                 fobj.write("this is not a pickle")
 
 993             self.assertEqual(black.read_cache(mode), {})
 
 994             src = (workspace / "test.py").resolve()
 
 995             with src.open("w") as fobj:
 
 996                 fobj.write("print('hello')")
 
 997             self.invokeBlack([str(src)])
 
 998             cache = black.read_cache(mode)
 
 999             self.assertIn(src, cache)
 
1001     def test_cache_single_file_already_cached(self) -> None:
 
1002         mode = black.FileMode()
 
1003         with cache_dir() as workspace:
 
1004             src = (workspace / "test.py").resolve()
 
1005             with src.open("w") as fobj:
 
1006                 fobj.write("print('hello')")
 
1007             black.write_cache({}, [src], mode)
 
1008             self.invokeBlack([str(src)])
 
1009             with src.open("r") as fobj:
 
1010                 self.assertEqual(fobj.read(), "print('hello')")
 
1012     @event_loop(close=False)
 
1013     def test_cache_multiple_files(self) -> None:
 
1014         mode = black.FileMode()
 
1015         with cache_dir() as workspace, patch(
 
1016             "black.ProcessPoolExecutor", new=ThreadPoolExecutor
 
1018             one = (workspace / "one.py").resolve()
 
1019             with one.open("w") as fobj:
 
1020                 fobj.write("print('hello')")
 
1021             two = (workspace / "two.py").resolve()
 
1022             with two.open("w") as fobj:
 
1023                 fobj.write("print('hello')")
 
1024             black.write_cache({}, [one], mode)
 
1025             self.invokeBlack([str(workspace)])
 
1026             with one.open("r") as fobj:
 
1027                 self.assertEqual(fobj.read(), "print('hello')")
 
1028             with two.open("r") as fobj:
 
1029                 self.assertEqual(fobj.read(), 'print("hello")\n')
 
1030             cache = black.read_cache(mode)
 
1031             self.assertIn(one, cache)
 
1032             self.assertIn(two, cache)
 
1034     def test_no_cache_when_writeback_diff(self) -> None:
 
1035         mode = black.FileMode()
 
1036         with cache_dir() as workspace:
 
1037             src = (workspace / "test.py").resolve()
 
1038             with src.open("w") as fobj:
 
1039                 fobj.write("print('hello')")
 
1040             self.invokeBlack([str(src), "--diff"])
 
1041             cache_file = black.get_cache_file(mode)
 
1042             self.assertFalse(cache_file.exists())
 
1044     def test_no_cache_when_stdin(self) -> None:
 
1045         mode = black.FileMode()
 
1047             result = CliRunner().invoke(
 
1048                 black.main, ["-"], input=BytesIO(b"print('hello')")
 
1050             self.assertEqual(result.exit_code, 0)
 
1051             cache_file = black.get_cache_file(mode)
 
1052             self.assertFalse(cache_file.exists())
 
1054     def test_read_cache_no_cachefile(self) -> None:
 
1055         mode = black.FileMode()
 
1057             self.assertEqual(black.read_cache(mode), {})
 
1059     def test_write_cache_read_cache(self) -> None:
 
1060         mode = black.FileMode()
 
1061         with cache_dir() as workspace:
 
1062             src = (workspace / "test.py").resolve()
 
1064             black.write_cache({}, [src], mode)
 
1065             cache = black.read_cache(mode)
 
1066             self.assertIn(src, cache)
 
1067             self.assertEqual(cache[src], black.get_cache_info(src))
 
1069     def test_filter_cached(self) -> None:
 
1070         with TemporaryDirectory() as workspace:
 
1071             path = Path(workspace)
 
1072             uncached = (path / "uncached").resolve()
 
1073             cached = (path / "cached").resolve()
 
1074             cached_but_changed = (path / "changed").resolve()
 
1077             cached_but_changed.touch()
 
1078             cache = {cached: black.get_cache_info(cached), cached_but_changed: (0.0, 0)}
 
1079             todo, done = black.filter_cached(
 
1080                 cache, {uncached, cached, cached_but_changed}
 
1082             self.assertEqual(todo, {uncached, cached_but_changed})
 
1083             self.assertEqual(done, {cached})
 
1085     def test_write_cache_creates_directory_if_needed(self) -> None:
 
1086         mode = black.FileMode()
 
1087         with cache_dir(exists=False) as workspace:
 
1088             self.assertFalse(workspace.exists())
 
1089             black.write_cache({}, [], mode)
 
1090             self.assertTrue(workspace.exists())
 
1092     @event_loop(close=False)
 
1093     def test_failed_formatting_does_not_get_cached(self) -> None:
 
1094         mode = black.FileMode()
 
1095         with cache_dir() as workspace, patch(
 
1096             "black.ProcessPoolExecutor", new=ThreadPoolExecutor
 
1098             failing = (workspace / "failing.py").resolve()
 
1099             with failing.open("w") as fobj:
 
1100                 fobj.write("not actually python")
 
1101             clean = (workspace / "clean.py").resolve()
 
1102             with clean.open("w") as fobj:
 
1103                 fobj.write('print("hello")\n')
 
1104             self.invokeBlack([str(workspace)], exit_code=123)
 
1105             cache = black.read_cache(mode)
 
1106             self.assertNotIn(failing, cache)
 
1107             self.assertIn(clean, cache)
 
1109     def test_write_cache_write_fail(self) -> None:
 
1110         mode = black.FileMode()
 
1111         with cache_dir(), patch.object(Path, "open") as mock:
 
1112             mock.side_effect = OSError
 
1113             black.write_cache({}, [], mode)
 
1115     @event_loop(close=False)
 
1116     def test_check_diff_use_together(self) -> None:
 
1118             # Files which will be reformatted.
 
1119             src1 = (THIS_DIR / "data" / "string_quotes.py").resolve()
 
1120             self.invokeBlack([str(src1), "--diff", "--check"], exit_code=1)
 
1121             # Files which will not be reformatted.
 
1122             src2 = (THIS_DIR / "data" / "composition.py").resolve()
 
1123             self.invokeBlack([str(src2), "--diff", "--check"])
 
1124             # Multi file command.
 
1125             self.invokeBlack([str(src1), str(src2), "--diff", "--check"], exit_code=1)
 
1127     def test_no_files(self) -> None:
 
1129             # Without an argument, black exits with error code 0.
 
1130             self.invokeBlack([])
 
1132     def test_broken_symlink(self) -> None:
 
1133         with cache_dir() as workspace:
 
1134             symlink = workspace / "broken_link.py"
 
1136                 symlink.symlink_to("nonexistent.py")
 
1137             except OSError as e:
 
1138                 self.skipTest(f"Can't create symlinks: {e}")
 
1139             self.invokeBlack([str(workspace.resolve())])
 
1141     def test_read_cache_line_lengths(self) -> None:
 
1142         mode = black.FileMode()
 
1143         short_mode = black.FileMode(line_length=1)
 
1144         with cache_dir() as workspace:
 
1145             path = (workspace / "file.py").resolve()
 
1147             black.write_cache({}, [path], mode)
 
1148             one = black.read_cache(mode)
 
1149             self.assertIn(path, one)
 
1150             two = black.read_cache(short_mode)
 
1151             self.assertNotIn(path, two)
 
1153     def test_single_file_force_pyi(self) -> None:
 
1154         reg_mode = black.FileMode()
 
1155         pyi_mode = black.FileMode(is_pyi=True)
 
1156         contents, expected = read_data("force_pyi")
 
1157         with cache_dir() as workspace:
 
1158             path = (workspace / "file.py").resolve()
 
1159             with open(path, "w") as fh:
 
1161             self.invokeBlack([str(path), "--pyi"])
 
1162             with open(path, "r") as fh:
 
1164             # verify cache with --pyi is separate
 
1165             pyi_cache = black.read_cache(pyi_mode)
 
1166             self.assertIn(path, pyi_cache)
 
1167             normal_cache = black.read_cache(reg_mode)
 
1168             self.assertNotIn(path, normal_cache)
 
1169         self.assertEqual(actual, expected)
 
1171     @event_loop(close=False)
 
1172     def test_multi_file_force_pyi(self) -> None:
 
1173         reg_mode = black.FileMode()
 
1174         pyi_mode = black.FileMode(is_pyi=True)
 
1175         contents, expected = read_data("force_pyi")
 
1176         with cache_dir() as workspace:
 
1178                 (workspace / "file1.py").resolve(),
 
1179                 (workspace / "file2.py").resolve(),
 
1182                 with open(path, "w") as fh:
 
1184             self.invokeBlack([str(p) for p in paths] + ["--pyi"])
 
1186                 with open(path, "r") as fh:
 
1188                 self.assertEqual(actual, expected)
 
1189             # verify cache with --pyi is separate
 
1190             pyi_cache = black.read_cache(pyi_mode)
 
1191             normal_cache = black.read_cache(reg_mode)
 
1193                 self.assertIn(path, pyi_cache)
 
1194                 self.assertNotIn(path, normal_cache)
 
1196     def test_pipe_force_pyi(self) -> None:
 
1197         source, expected = read_data("force_pyi")
 
1198         result = CliRunner().invoke(
 
1199             black.main, ["-", "-q", "--pyi"], input=BytesIO(source.encode("utf8"))
 
1201         self.assertEqual(result.exit_code, 0)
 
1202         actual = result.output
 
1203         self.assertFormatEqual(actual, expected)
 
1205     def test_single_file_force_py36(self) -> None:
 
1206         reg_mode = black.FileMode()
 
1207         py36_mode = black.FileMode(target_versions=black.PY36_VERSIONS)
 
1208         source, expected = read_data("force_py36")
 
1209         with cache_dir() as workspace:
 
1210             path = (workspace / "file.py").resolve()
 
1211             with open(path, "w") as fh:
 
1213             self.invokeBlack([str(path), *PY36_ARGS])
 
1214             with open(path, "r") as fh:
 
1216             # verify cache with --target-version is separate
 
1217             py36_cache = black.read_cache(py36_mode)
 
1218             self.assertIn(path, py36_cache)
 
1219             normal_cache = black.read_cache(reg_mode)
 
1220             self.assertNotIn(path, normal_cache)
 
1221         self.assertEqual(actual, expected)
 
1223     @event_loop(close=False)
 
1224     def test_multi_file_force_py36(self) -> None:
 
1225         reg_mode = black.FileMode()
 
1226         py36_mode = black.FileMode(target_versions=black.PY36_VERSIONS)
 
1227         source, expected = read_data("force_py36")
 
1228         with cache_dir() as workspace:
 
1230                 (workspace / "file1.py").resolve(),
 
1231                 (workspace / "file2.py").resolve(),
 
1234                 with open(path, "w") as fh:
 
1236             self.invokeBlack([str(p) for p in paths] + PY36_ARGS)
 
1238                 with open(path, "r") as fh:
 
1240                 self.assertEqual(actual, expected)
 
1241             # verify cache with --target-version is separate
 
1242             pyi_cache = black.read_cache(py36_mode)
 
1243             normal_cache = black.read_cache(reg_mode)
 
1245                 self.assertIn(path, pyi_cache)
 
1246                 self.assertNotIn(path, normal_cache)
 
1248     def test_pipe_force_py36(self) -> None:
 
1249         source, expected = read_data("force_py36")
 
1250         result = CliRunner().invoke(
 
1252             ["-", "-q", "--target-version=py36"],
 
1253             input=BytesIO(source.encode("utf8")),
 
1255         self.assertEqual(result.exit_code, 0)
 
1256         actual = result.output
 
1257         self.assertFormatEqual(actual, expected)
 
1259     def test_include_exclude(self) -> None:
 
1260         path = THIS_DIR / "data" / "include_exclude_tests"
 
1261         include = re.compile(r"\.pyi?$")
 
1262         exclude = re.compile(r"/exclude/|/\.definitely_exclude/")
 
1263         report = black.Report()
 
1264         sources: List[Path] = []
 
1266             Path(path / "b/dont_exclude/a.py"),
 
1267             Path(path / "b/dont_exclude/a.pyi"),
 
1269         this_abs = THIS_DIR.resolve()
 
1271             black.gen_python_files_in_dir(path, this_abs, include, exclude, report)
 
1273         self.assertEqual(sorted(expected), sorted(sources))
 
1275     def test_empty_include(self) -> None:
 
1276         path = THIS_DIR / "data" / "include_exclude_tests"
 
1277         report = black.Report()
 
1278         empty = re.compile(r"")
 
1279         sources: List[Path] = []
 
1281             Path(path / "b/exclude/a.pie"),
 
1282             Path(path / "b/exclude/a.py"),
 
1283             Path(path / "b/exclude/a.pyi"),
 
1284             Path(path / "b/dont_exclude/a.pie"),
 
1285             Path(path / "b/dont_exclude/a.py"),
 
1286             Path(path / "b/dont_exclude/a.pyi"),
 
1287             Path(path / "b/.definitely_exclude/a.pie"),
 
1288             Path(path / "b/.definitely_exclude/a.py"),
 
1289             Path(path / "b/.definitely_exclude/a.pyi"),
 
1291         this_abs = THIS_DIR.resolve()
 
1293             black.gen_python_files_in_dir(
 
1294                 path, this_abs, empty, re.compile(black.DEFAULT_EXCLUDES), report
 
1297         self.assertEqual(sorted(expected), sorted(sources))
 
1299     def test_empty_exclude(self) -> None:
 
1300         path = THIS_DIR / "data" / "include_exclude_tests"
 
1301         report = black.Report()
 
1302         empty = re.compile(r"")
 
1303         sources: List[Path] = []
 
1305             Path(path / "b/dont_exclude/a.py"),
 
1306             Path(path / "b/dont_exclude/a.pyi"),
 
1307             Path(path / "b/exclude/a.py"),
 
1308             Path(path / "b/exclude/a.pyi"),
 
1309             Path(path / "b/.definitely_exclude/a.py"),
 
1310             Path(path / "b/.definitely_exclude/a.pyi"),
 
1312         this_abs = THIS_DIR.resolve()
 
1314             black.gen_python_files_in_dir(
 
1315                 path, this_abs, re.compile(black.DEFAULT_INCLUDES), empty, report
 
1318         self.assertEqual(sorted(expected), sorted(sources))
 
1320     def test_invalid_include_exclude(self) -> None:
 
1321         for option in ["--include", "--exclude"]:
 
1322             self.invokeBlack(["-", option, "**()(!!*)"], exit_code=2)
 
1324     def test_preserves_line_endings(self) -> None:
 
1325         with TemporaryDirectory() as workspace:
 
1326             test_file = Path(workspace) / "test.py"
 
1327             for nl in ["\n", "\r\n"]:
 
1328                 contents = nl.join(["def f(  ):", "    pass"])
 
1329                 test_file.write_bytes(contents.encode())
 
1330                 ff(test_file, write_back=black.WriteBack.YES)
 
1331                 updated_contents: bytes = test_file.read_bytes()
 
1332                 self.assertIn(nl.encode(), updated_contents)
 
1334                     self.assertNotIn(b"\r\n", updated_contents)
 
1336     def test_preserves_line_endings_via_stdin(self) -> None:
 
1337         for nl in ["\n", "\r\n"]:
 
1338             contents = nl.join(["def f(  ):", "    pass"])
 
1339             runner = BlackRunner()
 
1340             result = runner.invoke(
 
1341                 black.main, ["-", "--fast"], input=BytesIO(contents.encode("utf8"))
 
1343             self.assertEqual(result.exit_code, 0)
 
1344             output = runner.stdout_bytes
 
1345             self.assertIn(nl.encode("utf8"), output)
 
1347                 self.assertNotIn(b"\r\n", output)
 
1349     def test_assert_equivalent_different_asts(self) -> None:
 
1350         with self.assertRaises(AssertionError):
 
1351             black.assert_equivalent("{}", "None")
 
1353     def test_symlink_out_of_root_directory(self) -> None:
 
1357         include = re.compile(black.DEFAULT_INCLUDES)
 
1358         exclude = re.compile(black.DEFAULT_EXCLUDES)
 
1359         report = black.Report()
 
1360         # `child` should behave like a symlink which resolved path is clearly
 
1361         # outside of the `root` directory.
 
1362         path.iterdir.return_value = [child]
 
1363         child.resolve.return_value = Path("/a/b/c")
 
1364         child.is_symlink.return_value = True
 
1366             list(black.gen_python_files_in_dir(path, root, include, exclude, report))
 
1367         except ValueError as ve:
 
1368             self.fail(f"`get_python_files_in_dir()` failed: {ve}")
 
1369         path.iterdir.assert_called_once()
 
1370         child.resolve.assert_called_once()
 
1371         child.is_symlink.assert_called_once()
 
1372         # `child` should behave like a strange file which resolved path is clearly
 
1373         # outside of the `root` directory.
 
1374         child.is_symlink.return_value = False
 
1375         with self.assertRaises(ValueError):
 
1376             list(black.gen_python_files_in_dir(path, root, include, exclude, report))
 
1377         path.iterdir.assert_called()
 
1378         self.assertEqual(path.iterdir.call_count, 2)
 
1379         child.resolve.assert_called()
 
1380         self.assertEqual(child.resolve.call_count, 2)
 
1381         child.is_symlink.assert_called()
 
1382         self.assertEqual(child.is_symlink.call_count, 2)
 
1384     def test_shhh_click(self) -> None:
 
1386             from click import _unicodefun  # type: ignore
 
1387         except ModuleNotFoundError:
 
1388             self.skipTest("Incompatible Click version")
 
1389         if not hasattr(_unicodefun, "_verify_python3_env"):
 
1390             self.skipTest("Incompatible Click version")
 
1391         # First, let's see if Click is crashing with a preferred ASCII charset.
 
1392         with patch("locale.getpreferredencoding") as gpe:
 
1393             gpe.return_value = "ASCII"
 
1394             with self.assertRaises(RuntimeError):
 
1395                 _unicodefun._verify_python3_env()
 
1396         # Now, let's silence Click...
 
1398         # ...and confirm it's silent.
 
1399         with patch("locale.getpreferredencoding") as gpe:
 
1400             gpe.return_value = "ASCII"
 
1402                 _unicodefun._verify_python3_env()
 
1403             except RuntimeError as re:
 
1404                 self.fail(f"`patch_click()` failed, exception still raised: {re}")
 
1406     def test_root_logger_not_used_directly(self) -> None:
 
1407         def fail(*args: Any, **kwargs: Any) -> None:
 
1408             self.fail("Record created with root logger")
 
1410         with patch.multiple(
 
1421     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1423     async def test_blackd_request_needs_formatting(self) -> None:
 
1424         app = blackd.make_app()
 
1425         async with TestClient(TestServer(app)) as client:
 
1426             response = await client.post("/", data=b"print('hello world')")
 
1427             self.assertEqual(response.status, 200)
 
1428             self.assertEqual(response.charset, "utf8")
 
1429             self.assertEqual(await response.read(), b'print("hello world")\n')
 
1431     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1433     async def test_blackd_request_no_change(self) -> None:
 
1434         app = blackd.make_app()
 
1435         async with TestClient(TestServer(app)) as client:
 
1436             response = await client.post("/", data=b'print("hello world")\n')
 
1437             self.assertEqual(response.status, 204)
 
1438             self.assertEqual(await response.read(), b"")
 
1440     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1442     async def test_blackd_request_syntax_error(self) -> None:
 
1443         app = blackd.make_app()
 
1444         async with TestClient(TestServer(app)) as client:
 
1445             response = await client.post("/", data=b"what even ( is")
 
1446             self.assertEqual(response.status, 400)
 
1447             content = await response.text()
 
1449                 content.startswith("Cannot parse"),
 
1450                 msg=f"Expected error to start with 'Cannot parse', got {repr(content)}",
 
1453     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1455     async def test_blackd_unsupported_version(self) -> None:
 
1456         app = blackd.make_app()
 
1457         async with TestClient(TestServer(app)) as client:
 
1458             response = await client.post(
 
1459                 "/", data=b"what", headers={blackd.VERSION_HEADER: "2"}
 
1461             self.assertEqual(response.status, 501)
 
1463     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1465     async def test_blackd_supported_version(self) -> None:
 
1466         app = blackd.make_app()
 
1467         async with TestClient(TestServer(app)) as client:
 
1468             response = await client.post(
 
1469                 "/", data=b"what", headers={blackd.VERSION_HEADER: "1"}
 
1471             self.assertEqual(response.status, 200)
 
1473     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1475     async def test_blackd_invalid_python_variant(self) -> None:
 
1476         app = blackd.make_app()
 
1477         async with TestClient(TestServer(app)) as client:
 
1479             async def check(header_value: str, expected_status: int = 400) -> None:
 
1480                 response = await client.post(
 
1483                     headers={blackd.PYTHON_VARIANT_HEADER: header_value},
 
1485                 self.assertEqual(response.status, expected_status)
 
1488             await check("ruby3.5")
 
1489             await check("pyi3.6")
 
1490             await check("py1.5")
 
1492             await check("py2.8")
 
1494             await check("pypy3.0")
 
1495             await check("jython3.4")
 
1497     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1499     async def test_blackd_pyi(self) -> None:
 
1500         app = blackd.make_app()
 
1501         async with TestClient(TestServer(app)) as client:
 
1502             source, expected = read_data("stub.pyi")
 
1503             response = await client.post(
 
1504                 "/", data=source, headers={blackd.PYTHON_VARIANT_HEADER: "pyi"}
 
1506             self.assertEqual(response.status, 200)
 
1507             self.assertEqual(await response.text(), expected)
 
1509     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1511     async def test_blackd_python_variant(self) -> None:
 
1512         app = blackd.make_app()
 
1515             "    and_has_a_bunch_of,\n"
 
1516             "    very_long_arguments_too,\n"
 
1517             "    and_lots_of_them_as_well_lol,\n"
 
1518             "    **and_very_long_keyword_arguments\n"
 
1522         async with TestClient(TestServer(app)) as client:
 
1524             async def check(header_value: str, expected_status: int) -> None:
 
1525                 response = await client.post(
 
1526                     "/", data=code, headers={blackd.PYTHON_VARIANT_HEADER: header_value}
 
1528                 self.assertEqual(response.status, expected_status)
 
1530             await check("3.6", 200)
 
1531             await check("py3.6", 200)
 
1532             await check("3.6,3.7", 200)
 
1533             await check("3.6,py3.7", 200)
 
1535             await check("2", 204)
 
1536             await check("2.7", 204)
 
1537             await check("py2.7", 204)
 
1538             await check("3.4", 204)
 
1539             await check("py3.4", 204)
 
1541     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1543     async def test_blackd_fast(self) -> None:
 
1544         with open(os.devnull, "w") as dn, redirect_stderr(dn):
 
1545             app = blackd.make_app()
 
1546             async with TestClient(TestServer(app)) as client:
 
1547                 response = await client.post("/", data=b"ur'hello'")
 
1548                 self.assertEqual(response.status, 500)
 
1549                 self.assertIn("failed to parse source file", await response.text())
 
1550                 response = await client.post(
 
1551                     "/", data=b"ur'hello'", headers={blackd.FAST_OR_SAFE_HEADER: "fast"}
 
1553                 self.assertEqual(response.status, 200)
 
1555     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1557     async def test_blackd_line_length(self) -> None:
 
1558         app = blackd.make_app()
 
1559         async with TestClient(TestServer(app)) as client:
 
1560             response = await client.post(
 
1561                 "/", data=b'print("hello")\n', headers={blackd.LINE_LENGTH_HEADER: "7"}
 
1563             self.assertEqual(response.status, 200)
 
1565     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1567     async def test_blackd_invalid_line_length(self) -> None:
 
1568         app = blackd.make_app()
 
1569         async with TestClient(TestServer(app)) as client:
 
1570             response = await client.post(
 
1572                 data=b'print("hello")\n',
 
1573                 headers={blackd.LINE_LENGTH_HEADER: "NaN"},
 
1575             self.assertEqual(response.status, 400)
 
1577     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1578     def test_blackd_main(self) -> None:
 
1579         with patch("blackd.web.run_app"):
 
1580             result = CliRunner().invoke(blackd.main, [])
 
1581             if result.exception is not None:
 
1582                 raise result.exception
 
1583             self.assertEqual(result.exit_code, 0)
 
1586 if __name__ == "__main__":
 
1587     unittest.main(module="test_black")