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.
   3 from concurrent.futures import ThreadPoolExecutor
 
   4 from contextlib import contextmanager
 
   5 from functools import partial, wraps
 
   6 from io import BytesIO, TextIOWrapper
 
   8 from pathlib import Path
 
  11 from tempfile import TemporaryDirectory
 
  24 from unittest.mock import patch, MagicMock
 
  26 from click import unstyle
 
  27 from click.testing import CliRunner
 
  33     from aiohttp.test_utils import TestClient, TestServer
 
  35     has_blackd_deps = False
 
  37     has_blackd_deps = True
 
  41 ff = partial(black.format_file_in_place, line_length=ll, fast=True)
 
  42 fs = partial(black.format_str, line_length=ll)
 
  43 THIS_FILE = Path(__file__)
 
  44 THIS_DIR = THIS_FILE.parent
 
  45 EMPTY_LINE = "# EMPTY LINE WITH WHITESPACE" + " (this comment will be removed)"
 
  50 def dump_to_stderr(*output: str) -> str:
 
  51     return "\n" + "\n".join(output) + "\n"
 
  54 def read_data(name: str, data: bool = True) -> Tuple[str, str]:
 
  55     """read_data('test_name') -> 'input', 'output'"""
 
  56     if not name.endswith((".py", ".pyi", ".out", ".diff")):
 
  58     _input: List[str] = []
 
  59     _output: List[str] = []
 
  60     base_dir = THIS_DIR / "data" if data else THIS_DIR
 
  61     with open(base_dir / name, "r", encoding="utf8") as test:
 
  62         lines = test.readlines()
 
  65         line = line.replace(EMPTY_LINE, "")
 
  66         if line.rstrip() == "# output":
 
  71     if _input and not _output:
 
  72         # If there's no output marker, treat the entire file as already pre-formatted.
 
  74     return "".join(_input).strip() + "\n", "".join(_output).strip() + "\n"
 
  78 def cache_dir(exists: bool = True) -> Iterator[Path]:
 
  79     with TemporaryDirectory() as workspace:
 
  80         cache_dir = Path(workspace)
 
  82             cache_dir = cache_dir / "new"
 
  83         with patch("black.CACHE_DIR", cache_dir):
 
  88 def event_loop(close: bool) -> Iterator[None]:
 
  89     policy = asyncio.get_event_loop_policy()
 
  90     old_loop = policy.get_event_loop()
 
  91     loop = policy.new_event_loop()
 
  92     asyncio.set_event_loop(loop)
 
  97         policy.set_event_loop(old_loop)
 
 102 def async_test(f: Callable[..., Coroutine[Any, None, R]]) -> Callable[..., None]:
 
 103     @event_loop(close=True)
 
 105     def wrapper(*args: Any, **kwargs: Any) -> None:
 
 106         asyncio.get_event_loop().run_until_complete(f(*args, **kwargs))
 
 111 class BlackRunner(CliRunner):
 
 112     """Modify CliRunner so that stderr is not merged with stdout.
 
 114     This is a hack that can be removed once we depend on Click 7.x"""
 
 116     def __init__(self) -> None:
 
 117         self.stderrbuf = BytesIO()
 
 118         self.stdoutbuf = BytesIO()
 
 119         self.stdout_bytes = b""
 
 120         self.stderr_bytes = b""
 
 124     def isolation(self, *args: Any, **kwargs: Any) -> Generator[BinaryIO, None, None]:
 
 125         with super().isolation(*args, **kwargs) as output:
 
 127                 hold_stderr = sys.stderr
 
 128                 sys.stderr = TextIOWrapper(self.stderrbuf, encoding=self.charset)
 
 131                 self.stdout_bytes = sys.stdout.buffer.getvalue()  # type: ignore
 
 132                 self.stderr_bytes = sys.stderr.buffer.getvalue()  # type: ignore
 
 133                 sys.stderr = hold_stderr
 
 136 class BlackTestCase(unittest.TestCase):
 
 139     def assertFormatEqual(self, expected: str, actual: str) -> None:
 
 140         if actual != expected and not os.environ.get("SKIP_AST_PRINT"):
 
 141             bdv: black.DebugVisitor[Any]
 
 142             black.out("Expected tree:", fg="green")
 
 144                 exp_node = black.lib2to3_parse(expected)
 
 145                 bdv = black.DebugVisitor()
 
 146                 list(bdv.visit(exp_node))
 
 147             except Exception as ve:
 
 149             black.out("Actual tree:", fg="red")
 
 151                 exp_node = black.lib2to3_parse(actual)
 
 152                 bdv = black.DebugVisitor()
 
 153                 list(bdv.visit(exp_node))
 
 154             except Exception as ve:
 
 156         self.assertEqual(expected, actual)
 
 158     @patch("black.dump_to_file", dump_to_stderr)
 
 159     def test_empty(self) -> None:
 
 160         source = expected = ""
 
 162         self.assertFormatEqual(expected, actual)
 
 163         black.assert_equivalent(source, actual)
 
 164         black.assert_stable(source, actual, line_length=ll)
 
 166     def test_empty_ff(self) -> None:
 
 168         tmp_file = Path(black.dump_to_file())
 
 170             self.assertFalse(ff(tmp_file, write_back=black.WriteBack.YES))
 
 171             with open(tmp_file, encoding="utf8") as f:
 
 175         self.assertFormatEqual(expected, actual)
 
 177     @patch("black.dump_to_file", dump_to_stderr)
 
 178     def test_self(self) -> None:
 
 179         source, expected = read_data("test_black", data=False)
 
 181         self.assertFormatEqual(expected, actual)
 
 182         black.assert_equivalent(source, actual)
 
 183         black.assert_stable(source, actual, line_length=ll)
 
 184         self.assertFalse(ff(THIS_FILE))
 
 186     @patch("black.dump_to_file", dump_to_stderr)
 
 187     def test_black(self) -> None:
 
 188         source, expected = read_data("../black", data=False)
 
 190         self.assertFormatEqual(expected, actual)
 
 191         black.assert_equivalent(source, actual)
 
 192         black.assert_stable(source, actual, line_length=ll)
 
 193         self.assertFalse(ff(THIS_DIR / ".." / "black.py"))
 
 195     def test_piping(self) -> None:
 
 196         source, expected = read_data("../black", data=False)
 
 197         result = BlackRunner().invoke(
 
 199             ["-", "--fast", f"--line-length={ll}"],
 
 200             input=BytesIO(source.encode("utf8")),
 
 202         self.assertEqual(result.exit_code, 0)
 
 203         self.assertFormatEqual(expected, result.output)
 
 204         black.assert_equivalent(source, result.output)
 
 205         black.assert_stable(source, result.output, line_length=ll)
 
 207     def test_piping_diff(self) -> None:
 
 208         diff_header = re.compile(
 
 209             rf"(STDIN|STDOUT)\t\d\d\d\d-\d\d-\d\d "
 
 210             rf"\d\d:\d\d:\d\d\.\d\d\d\d\d\d \+\d\d\d\d"
 
 212         source, _ = read_data("expression.py")
 
 213         expected, _ = read_data("expression.diff")
 
 214         config = THIS_DIR / "data" / "empty_pyproject.toml"
 
 215         args = ["-", "--fast", f"--line-length={ll}", "--diff", f"--config={config}"]
 
 216         result = BlackRunner().invoke(
 
 217             black.main, args, input=BytesIO(source.encode("utf8"))
 
 219         self.assertEqual(result.exit_code, 0)
 
 220         actual = diff_header.sub("[Deterministic header]", result.output)
 
 221         actual = actual.rstrip() + "\n"  # the diff output has a trailing space
 
 222         self.assertEqual(expected, actual)
 
 224     @patch("black.dump_to_file", dump_to_stderr)
 
 225     def test_setup(self) -> None:
 
 226         source, expected = read_data("../setup", data=False)
 
 228         self.assertFormatEqual(expected, actual)
 
 229         black.assert_equivalent(source, actual)
 
 230         black.assert_stable(source, actual, line_length=ll)
 
 231         self.assertFalse(ff(THIS_DIR / ".." / "setup.py"))
 
 233     @patch("black.dump_to_file", dump_to_stderr)
 
 234     def test_function(self) -> None:
 
 235         source, expected = read_data("function")
 
 237         self.assertFormatEqual(expected, actual)
 
 238         black.assert_equivalent(source, actual)
 
 239         black.assert_stable(source, actual, line_length=ll)
 
 241     @patch("black.dump_to_file", dump_to_stderr)
 
 242     def test_function2(self) -> None:
 
 243         source, expected = read_data("function2")
 
 245         self.assertFormatEqual(expected, actual)
 
 246         black.assert_equivalent(source, actual)
 
 247         black.assert_stable(source, actual, line_length=ll)
 
 249     @patch("black.dump_to_file", dump_to_stderr)
 
 250     def test_expression(self) -> None:
 
 251         source, expected = read_data("expression")
 
 253         self.assertFormatEqual(expected, actual)
 
 254         black.assert_equivalent(source, actual)
 
 255         black.assert_stable(source, actual, line_length=ll)
 
 257     def test_expression_ff(self) -> None:
 
 258         source, expected = read_data("expression")
 
 259         tmp_file = Path(black.dump_to_file(source))
 
 261             self.assertTrue(ff(tmp_file, write_back=black.WriteBack.YES))
 
 262             with open(tmp_file, encoding="utf8") as f:
 
 266         self.assertFormatEqual(expected, actual)
 
 267         with patch("black.dump_to_file", dump_to_stderr):
 
 268             black.assert_equivalent(source, actual)
 
 269             black.assert_stable(source, actual, line_length=ll)
 
 271     def test_expression_diff(self) -> None:
 
 272         source, _ = read_data("expression.py")
 
 273         expected, _ = read_data("expression.diff")
 
 274         tmp_file = Path(black.dump_to_file(source))
 
 275         diff_header = re.compile(
 
 276             rf"{re.escape(str(tmp_file))}\t\d\d\d\d-\d\d-\d\d "
 
 277             rf"\d\d:\d\d:\d\d\.\d\d\d\d\d\d \+\d\d\d\d"
 
 280             result = BlackRunner().invoke(black.main, ["--diff", str(tmp_file)])
 
 281             self.assertEqual(result.exit_code, 0)
 
 284         actual = result.output
 
 285         actual = diff_header.sub("[Deterministic header]", actual)
 
 286         actual = actual.rstrip() + "\n"  # the diff output has a trailing space
 
 287         if expected != actual:
 
 288             dump = black.dump_to_file(actual)
 
 290                 f"Expected diff isn't equal to the actual. If you made changes "
 
 291                 f"to expression.py and this is an anticipated difference, "
 
 292                 f"overwrite tests/expression.diff with {dump}"
 
 294             self.assertEqual(expected, actual, msg)
 
 296     @patch("black.dump_to_file", dump_to_stderr)
 
 297     def test_fstring(self) -> None:
 
 298         source, expected = read_data("fstring")
 
 300         self.assertFormatEqual(expected, actual)
 
 301         black.assert_equivalent(source, actual)
 
 302         black.assert_stable(source, actual, line_length=ll)
 
 304     @patch("black.dump_to_file", dump_to_stderr)
 
 305     def test_string_quotes(self) -> None:
 
 306         source, expected = read_data("string_quotes")
 
 308         self.assertFormatEqual(expected, actual)
 
 309         black.assert_equivalent(source, actual)
 
 310         black.assert_stable(source, actual, line_length=ll)
 
 311         mode = black.FileMode.NO_STRING_NORMALIZATION
 
 312         not_normalized = fs(source, mode=mode)
 
 313         self.assertFormatEqual(source, not_normalized)
 
 314         black.assert_equivalent(source, not_normalized)
 
 315         black.assert_stable(source, not_normalized, line_length=ll, mode=mode)
 
 317     @patch("black.dump_to_file", dump_to_stderr)
 
 318     def test_slices(self) -> None:
 
 319         source, expected = read_data("slices")
 
 321         self.assertFormatEqual(expected, actual)
 
 322         black.assert_equivalent(source, actual)
 
 323         black.assert_stable(source, actual, line_length=ll)
 
 325     @patch("black.dump_to_file", dump_to_stderr)
 
 326     def test_comments(self) -> None:
 
 327         source, expected = read_data("comments")
 
 329         self.assertFormatEqual(expected, actual)
 
 330         black.assert_equivalent(source, actual)
 
 331         black.assert_stable(source, actual, line_length=ll)
 
 333     @patch("black.dump_to_file", dump_to_stderr)
 
 334     def test_comments2(self) -> None:
 
 335         source, expected = read_data("comments2")
 
 337         self.assertFormatEqual(expected, actual)
 
 338         black.assert_equivalent(source, actual)
 
 339         black.assert_stable(source, actual, line_length=ll)
 
 341     @patch("black.dump_to_file", dump_to_stderr)
 
 342     def test_comments3(self) -> None:
 
 343         source, expected = read_data("comments3")
 
 345         self.assertFormatEqual(expected, actual)
 
 346         black.assert_equivalent(source, actual)
 
 347         black.assert_stable(source, actual, line_length=ll)
 
 349     @patch("black.dump_to_file", dump_to_stderr)
 
 350     def test_comments4(self) -> None:
 
 351         source, expected = read_data("comments4")
 
 353         self.assertFormatEqual(expected, actual)
 
 354         black.assert_equivalent(source, actual)
 
 355         black.assert_stable(source, actual, line_length=ll)
 
 357     @patch("black.dump_to_file", dump_to_stderr)
 
 358     def test_comments5(self) -> None:
 
 359         source, expected = read_data("comments5")
 
 361         self.assertFormatEqual(expected, actual)
 
 362         black.assert_equivalent(source, actual)
 
 363         black.assert_stable(source, actual, line_length=ll)
 
 365     @patch("black.dump_to_file", dump_to_stderr)
 
 366     def test_cantfit(self) -> None:
 
 367         source, expected = read_data("cantfit")
 
 369         self.assertFormatEqual(expected, actual)
 
 370         black.assert_equivalent(source, actual)
 
 371         black.assert_stable(source, actual, line_length=ll)
 
 373     @patch("black.dump_to_file", dump_to_stderr)
 
 374     def test_import_spacing(self) -> None:
 
 375         source, expected = read_data("import_spacing")
 
 377         self.assertFormatEqual(expected, actual)
 
 378         black.assert_equivalent(source, actual)
 
 379         black.assert_stable(source, actual, line_length=ll)
 
 381     @patch("black.dump_to_file", dump_to_stderr)
 
 382     def test_composition(self) -> None:
 
 383         source, expected = read_data("composition")
 
 385         self.assertFormatEqual(expected, actual)
 
 386         black.assert_equivalent(source, actual)
 
 387         black.assert_stable(source, actual, line_length=ll)
 
 389     @patch("black.dump_to_file", dump_to_stderr)
 
 390     def test_empty_lines(self) -> None:
 
 391         source, expected = read_data("empty_lines")
 
 393         self.assertFormatEqual(expected, actual)
 
 394         black.assert_equivalent(source, actual)
 
 395         black.assert_stable(source, actual, line_length=ll)
 
 397     @patch("black.dump_to_file", dump_to_stderr)
 
 398     def test_string_prefixes(self) -> None:
 
 399         source, expected = read_data("string_prefixes")
 
 401         self.assertFormatEqual(expected, actual)
 
 402         black.assert_equivalent(source, actual)
 
 403         black.assert_stable(source, actual, line_length=ll)
 
 405     @patch("black.dump_to_file", dump_to_stderr)
 
 406     def test_numeric_literals(self) -> None:
 
 407         source, expected = read_data("numeric_literals")
 
 408         actual = fs(source, mode=black.FileMode.PYTHON36)
 
 409         self.assertFormatEqual(expected, actual)
 
 410         black.assert_equivalent(source, actual)
 
 411         black.assert_stable(source, actual, line_length=ll)
 
 413     @patch("black.dump_to_file", dump_to_stderr)
 
 414     def test_numeric_literals_py2(self) -> None:
 
 415         source, expected = read_data("numeric_literals_py2")
 
 417         self.assertFormatEqual(expected, actual)
 
 418         black.assert_stable(source, actual, line_length=ll)
 
 420     @patch("black.dump_to_file", dump_to_stderr)
 
 421     def test_python2(self) -> None:
 
 422         source, expected = read_data("python2")
 
 424         self.assertFormatEqual(expected, actual)
 
 425         # black.assert_equivalent(source, actual)
 
 426         black.assert_stable(source, actual, line_length=ll)
 
 428     @patch("black.dump_to_file", dump_to_stderr)
 
 429     def test_python2_unicode_literals(self) -> None:
 
 430         source, expected = read_data("python2_unicode_literals")
 
 432         self.assertFormatEqual(expected, actual)
 
 433         black.assert_stable(source, actual, line_length=ll)
 
 435     @patch("black.dump_to_file", dump_to_stderr)
 
 436     def test_stub(self) -> None:
 
 437         mode = black.FileMode.PYI
 
 438         source, expected = read_data("stub.pyi")
 
 439         actual = fs(source, mode=mode)
 
 440         self.assertFormatEqual(expected, actual)
 
 441         black.assert_stable(source, actual, line_length=ll, mode=mode)
 
 443     @patch("black.dump_to_file", dump_to_stderr)
 
 444     def test_python37(self) -> None:
 
 445         source, expected = read_data("python37")
 
 447         self.assertFormatEqual(expected, actual)
 
 448         major, minor = sys.version_info[:2]
 
 449         if major > 3 or (major == 3 and minor >= 7):
 
 450             black.assert_equivalent(source, actual)
 
 451         black.assert_stable(source, actual, line_length=ll)
 
 453     @patch("black.dump_to_file", dump_to_stderr)
 
 454     def test_fmtonoff(self) -> None:
 
 455         source, expected = read_data("fmtonoff")
 
 457         self.assertFormatEqual(expected, actual)
 
 458         black.assert_equivalent(source, actual)
 
 459         black.assert_stable(source, actual, line_length=ll)
 
 461     @patch("black.dump_to_file", dump_to_stderr)
 
 462     def test_fmtonoff2(self) -> None:
 
 463         source, expected = read_data("fmtonoff2")
 
 465         self.assertFormatEqual(expected, actual)
 
 466         black.assert_equivalent(source, actual)
 
 467         black.assert_stable(source, actual, line_length=ll)
 
 469     @patch("black.dump_to_file", dump_to_stderr)
 
 470     def test_remove_empty_parentheses_after_class(self) -> None:
 
 471         source, expected = read_data("class_blank_parentheses")
 
 473         self.assertFormatEqual(expected, actual)
 
 474         black.assert_equivalent(source, actual)
 
 475         black.assert_stable(source, actual, line_length=ll)
 
 477     @patch("black.dump_to_file", dump_to_stderr)
 
 478     def test_new_line_between_class_and_code(self) -> None:
 
 479         source, expected = read_data("class_methods_new_line")
 
 481         self.assertFormatEqual(expected, actual)
 
 482         black.assert_equivalent(source, actual)
 
 483         black.assert_stable(source, actual, line_length=ll)
 
 485     @patch("black.dump_to_file", dump_to_stderr)
 
 486     def test_bracket_match(self) -> None:
 
 487         source, expected = read_data("bracketmatch")
 
 489         self.assertFormatEqual(expected, actual)
 
 490         black.assert_equivalent(source, actual)
 
 491         black.assert_stable(source, actual, line_length=ll)
 
 493     def test_report_verbose(self) -> None:
 
 494         report = black.Report(verbose=True)
 
 498         def out(msg: str, **kwargs: Any) -> None:
 
 499             out_lines.append(msg)
 
 501         def err(msg: str, **kwargs: Any) -> None:
 
 502             err_lines.append(msg)
 
 504         with patch("black.out", out), patch("black.err", err):
 
 505             report.done(Path("f1"), black.Changed.NO)
 
 506             self.assertEqual(len(out_lines), 1)
 
 507             self.assertEqual(len(err_lines), 0)
 
 508             self.assertEqual(out_lines[-1], "f1 already well formatted, good job.")
 
 509             self.assertEqual(unstyle(str(report)), "1 file left unchanged.")
 
 510             self.assertEqual(report.return_code, 0)
 
 511             report.done(Path("f2"), black.Changed.YES)
 
 512             self.assertEqual(len(out_lines), 2)
 
 513             self.assertEqual(len(err_lines), 0)
 
 514             self.assertEqual(out_lines[-1], "reformatted f2")
 
 516                 unstyle(str(report)), "1 file reformatted, 1 file left unchanged."
 
 518             report.done(Path("f3"), black.Changed.CACHED)
 
 519             self.assertEqual(len(out_lines), 3)
 
 520             self.assertEqual(len(err_lines), 0)
 
 522                 out_lines[-1], "f3 wasn't modified on disk since last run."
 
 525                 unstyle(str(report)), "1 file reformatted, 2 files left unchanged."
 
 527             self.assertEqual(report.return_code, 0)
 
 529             self.assertEqual(report.return_code, 1)
 
 531             report.failed(Path("e1"), "boom")
 
 532             self.assertEqual(len(out_lines), 3)
 
 533             self.assertEqual(len(err_lines), 1)
 
 534             self.assertEqual(err_lines[-1], "error: cannot format e1: boom")
 
 536                 unstyle(str(report)),
 
 537                 "1 file reformatted, 2 files left unchanged, "
 
 538                 "1 file failed to reformat.",
 
 540             self.assertEqual(report.return_code, 123)
 
 541             report.done(Path("f3"), black.Changed.YES)
 
 542             self.assertEqual(len(out_lines), 4)
 
 543             self.assertEqual(len(err_lines), 1)
 
 544             self.assertEqual(out_lines[-1], "reformatted f3")
 
 546                 unstyle(str(report)),
 
 547                 "2 files reformatted, 2 files left unchanged, "
 
 548                 "1 file failed to reformat.",
 
 550             self.assertEqual(report.return_code, 123)
 
 551             report.failed(Path("e2"), "boom")
 
 552             self.assertEqual(len(out_lines), 4)
 
 553             self.assertEqual(len(err_lines), 2)
 
 554             self.assertEqual(err_lines[-1], "error: cannot format e2: boom")
 
 556                 unstyle(str(report)),
 
 557                 "2 files reformatted, 2 files left unchanged, "
 
 558                 "2 files failed to reformat.",
 
 560             self.assertEqual(report.return_code, 123)
 
 561             report.path_ignored(Path("wat"), "no match")
 
 562             self.assertEqual(len(out_lines), 5)
 
 563             self.assertEqual(len(err_lines), 2)
 
 564             self.assertEqual(out_lines[-1], "wat ignored: no match")
 
 566                 unstyle(str(report)),
 
 567                 "2 files reformatted, 2 files left unchanged, "
 
 568                 "2 files failed to reformat.",
 
 570             self.assertEqual(report.return_code, 123)
 
 571             report.done(Path("f4"), black.Changed.NO)
 
 572             self.assertEqual(len(out_lines), 6)
 
 573             self.assertEqual(len(err_lines), 2)
 
 574             self.assertEqual(out_lines[-1], "f4 already well formatted, good job.")
 
 576                 unstyle(str(report)),
 
 577                 "2 files reformatted, 3 files left unchanged, "
 
 578                 "2 files failed to reformat.",
 
 580             self.assertEqual(report.return_code, 123)
 
 583                 unstyle(str(report)),
 
 584                 "2 files would be reformatted, 3 files would be left unchanged, "
 
 585                 "2 files would fail to reformat.",
 
 588     def test_report_quiet(self) -> None:
 
 589         report = black.Report(quiet=True)
 
 593         def out(msg: str, **kwargs: Any) -> None:
 
 594             out_lines.append(msg)
 
 596         def err(msg: str, **kwargs: Any) -> None:
 
 597             err_lines.append(msg)
 
 599         with patch("black.out", out), patch("black.err", err):
 
 600             report.done(Path("f1"), black.Changed.NO)
 
 601             self.assertEqual(len(out_lines), 0)
 
 602             self.assertEqual(len(err_lines), 0)
 
 603             self.assertEqual(unstyle(str(report)), "1 file left unchanged.")
 
 604             self.assertEqual(report.return_code, 0)
 
 605             report.done(Path("f2"), black.Changed.YES)
 
 606             self.assertEqual(len(out_lines), 0)
 
 607             self.assertEqual(len(err_lines), 0)
 
 609                 unstyle(str(report)), "1 file reformatted, 1 file left unchanged."
 
 611             report.done(Path("f3"), black.Changed.CACHED)
 
 612             self.assertEqual(len(out_lines), 0)
 
 613             self.assertEqual(len(err_lines), 0)
 
 615                 unstyle(str(report)), "1 file reformatted, 2 files left unchanged."
 
 617             self.assertEqual(report.return_code, 0)
 
 619             self.assertEqual(report.return_code, 1)
 
 621             report.failed(Path("e1"), "boom")
 
 622             self.assertEqual(len(out_lines), 0)
 
 623             self.assertEqual(len(err_lines), 1)
 
 624             self.assertEqual(err_lines[-1], "error: cannot format e1: boom")
 
 626                 unstyle(str(report)),
 
 627                 "1 file reformatted, 2 files left unchanged, "
 
 628                 "1 file failed to reformat.",
 
 630             self.assertEqual(report.return_code, 123)
 
 631             report.done(Path("f3"), black.Changed.YES)
 
 632             self.assertEqual(len(out_lines), 0)
 
 633             self.assertEqual(len(err_lines), 1)
 
 635                 unstyle(str(report)),
 
 636                 "2 files reformatted, 2 files left unchanged, "
 
 637                 "1 file failed to reformat.",
 
 639             self.assertEqual(report.return_code, 123)
 
 640             report.failed(Path("e2"), "boom")
 
 641             self.assertEqual(len(out_lines), 0)
 
 642             self.assertEqual(len(err_lines), 2)
 
 643             self.assertEqual(err_lines[-1], "error: cannot format e2: boom")
 
 645                 unstyle(str(report)),
 
 646                 "2 files reformatted, 2 files left unchanged, "
 
 647                 "2 files failed to reformat.",
 
 649             self.assertEqual(report.return_code, 123)
 
 650             report.path_ignored(Path("wat"), "no match")
 
 651             self.assertEqual(len(out_lines), 0)
 
 652             self.assertEqual(len(err_lines), 2)
 
 654                 unstyle(str(report)),
 
 655                 "2 files reformatted, 2 files left unchanged, "
 
 656                 "2 files failed to reformat.",
 
 658             self.assertEqual(report.return_code, 123)
 
 659             report.done(Path("f4"), black.Changed.NO)
 
 660             self.assertEqual(len(out_lines), 0)
 
 661             self.assertEqual(len(err_lines), 2)
 
 663                 unstyle(str(report)),
 
 664                 "2 files reformatted, 3 files left unchanged, "
 
 665                 "2 files failed to reformat.",
 
 667             self.assertEqual(report.return_code, 123)
 
 670                 unstyle(str(report)),
 
 671                 "2 files would be reformatted, 3 files would be left unchanged, "
 
 672                 "2 files would fail to reformat.",
 
 675     def test_report_normal(self) -> None:
 
 676         report = black.Report()
 
 680         def out(msg: str, **kwargs: Any) -> None:
 
 681             out_lines.append(msg)
 
 683         def err(msg: str, **kwargs: Any) -> None:
 
 684             err_lines.append(msg)
 
 686         with patch("black.out", out), patch("black.err", err):
 
 687             report.done(Path("f1"), black.Changed.NO)
 
 688             self.assertEqual(len(out_lines), 0)
 
 689             self.assertEqual(len(err_lines), 0)
 
 690             self.assertEqual(unstyle(str(report)), "1 file left unchanged.")
 
 691             self.assertEqual(report.return_code, 0)
 
 692             report.done(Path("f2"), black.Changed.YES)
 
 693             self.assertEqual(len(out_lines), 1)
 
 694             self.assertEqual(len(err_lines), 0)
 
 695             self.assertEqual(out_lines[-1], "reformatted f2")
 
 697                 unstyle(str(report)), "1 file reformatted, 1 file left unchanged."
 
 699             report.done(Path("f3"), black.Changed.CACHED)
 
 700             self.assertEqual(len(out_lines), 1)
 
 701             self.assertEqual(len(err_lines), 0)
 
 702             self.assertEqual(out_lines[-1], "reformatted f2")
 
 704                 unstyle(str(report)), "1 file reformatted, 2 files left unchanged."
 
 706             self.assertEqual(report.return_code, 0)
 
 708             self.assertEqual(report.return_code, 1)
 
 710             report.failed(Path("e1"), "boom")
 
 711             self.assertEqual(len(out_lines), 1)
 
 712             self.assertEqual(len(err_lines), 1)
 
 713             self.assertEqual(err_lines[-1], "error: cannot format e1: boom")
 
 715                 unstyle(str(report)),
 
 716                 "1 file reformatted, 2 files left unchanged, "
 
 717                 "1 file failed to reformat.",
 
 719             self.assertEqual(report.return_code, 123)
 
 720             report.done(Path("f3"), black.Changed.YES)
 
 721             self.assertEqual(len(out_lines), 2)
 
 722             self.assertEqual(len(err_lines), 1)
 
 723             self.assertEqual(out_lines[-1], "reformatted f3")
 
 725                 unstyle(str(report)),
 
 726                 "2 files reformatted, 2 files left unchanged, "
 
 727                 "1 file failed to reformat.",
 
 729             self.assertEqual(report.return_code, 123)
 
 730             report.failed(Path("e2"), "boom")
 
 731             self.assertEqual(len(out_lines), 2)
 
 732             self.assertEqual(len(err_lines), 2)
 
 733             self.assertEqual(err_lines[-1], "error: cannot format e2: boom")
 
 735                 unstyle(str(report)),
 
 736                 "2 files reformatted, 2 files left unchanged, "
 
 737                 "2 files failed to reformat.",
 
 739             self.assertEqual(report.return_code, 123)
 
 740             report.path_ignored(Path("wat"), "no match")
 
 741             self.assertEqual(len(out_lines), 2)
 
 742             self.assertEqual(len(err_lines), 2)
 
 744                 unstyle(str(report)),
 
 745                 "2 files reformatted, 2 files left unchanged, "
 
 746                 "2 files failed to reformat.",
 
 748             self.assertEqual(report.return_code, 123)
 
 749             report.done(Path("f4"), black.Changed.NO)
 
 750             self.assertEqual(len(out_lines), 2)
 
 751             self.assertEqual(len(err_lines), 2)
 
 753                 unstyle(str(report)),
 
 754                 "2 files reformatted, 3 files left unchanged, "
 
 755                 "2 files failed to reformat.",
 
 757             self.assertEqual(report.return_code, 123)
 
 760                 unstyle(str(report)),
 
 761                 "2 files would be reformatted, 3 files would be left unchanged, "
 
 762                 "2 files would fail to reformat.",
 
 765     def test_is_python36(self) -> None:
 
 766         node = black.lib2to3_parse("def f(*, arg): ...\n")
 
 767         self.assertFalse(black.is_python36(node))
 
 768         node = black.lib2to3_parse("def f(*, arg,): ...\n")
 
 769         self.assertTrue(black.is_python36(node))
 
 770         node = black.lib2to3_parse("def f(*, arg): f'string'\n")
 
 771         self.assertTrue(black.is_python36(node))
 
 772         node = black.lib2to3_parse("123_456\n")
 
 773         self.assertTrue(black.is_python36(node))
 
 774         node = black.lib2to3_parse("123456\n")
 
 775         self.assertFalse(black.is_python36(node))
 
 776         source, expected = read_data("function")
 
 777         node = black.lib2to3_parse(source)
 
 778         self.assertTrue(black.is_python36(node))
 
 779         node = black.lib2to3_parse(expected)
 
 780         self.assertTrue(black.is_python36(node))
 
 781         source, expected = read_data("expression")
 
 782         node = black.lib2to3_parse(source)
 
 783         self.assertFalse(black.is_python36(node))
 
 784         node = black.lib2to3_parse(expected)
 
 785         self.assertFalse(black.is_python36(node))
 
 787     def test_get_future_imports(self) -> None:
 
 788         node = black.lib2to3_parse("\n")
 
 789         self.assertEqual(set(), black.get_future_imports(node))
 
 790         node = black.lib2to3_parse("from __future__ import black\n")
 
 791         self.assertEqual({"black"}, black.get_future_imports(node))
 
 792         node = black.lib2to3_parse("from __future__ import multiple, imports\n")
 
 793         self.assertEqual({"multiple", "imports"}, black.get_future_imports(node))
 
 794         node = black.lib2to3_parse("from __future__ import (parenthesized, imports)\n")
 
 795         self.assertEqual({"parenthesized", "imports"}, black.get_future_imports(node))
 
 796         node = black.lib2to3_parse(
 
 797             "from __future__ import multiple\nfrom __future__ import imports\n"
 
 799         self.assertEqual({"multiple", "imports"}, black.get_future_imports(node))
 
 800         node = black.lib2to3_parse("# comment\nfrom __future__ import black\n")
 
 801         self.assertEqual({"black"}, black.get_future_imports(node))
 
 802         node = black.lib2to3_parse('"""docstring"""\nfrom __future__ import black\n')
 
 803         self.assertEqual({"black"}, black.get_future_imports(node))
 
 804         node = black.lib2to3_parse("some(other, code)\nfrom __future__ import black\n")
 
 805         self.assertEqual(set(), black.get_future_imports(node))
 
 806         node = black.lib2to3_parse("from some.module import black\n")
 
 807         self.assertEqual(set(), black.get_future_imports(node))
 
 808         node = black.lib2to3_parse(
 
 809             "from __future__ import unicode_literals as _unicode_literals"
 
 811         self.assertEqual({"unicode_literals"}, black.get_future_imports(node))
 
 812         node = black.lib2to3_parse(
 
 813             "from __future__ import unicode_literals as _lol, print"
 
 815         self.assertEqual({"unicode_literals", "print"}, black.get_future_imports(node))
 
 817     def test_debug_visitor(self) -> None:
 
 818         source, _ = read_data("debug_visitor.py")
 
 819         expected, _ = read_data("debug_visitor.out")
 
 823         def out(msg: str, **kwargs: Any) -> None:
 
 824             out_lines.append(msg)
 
 826         def err(msg: str, **kwargs: Any) -> None:
 
 827             err_lines.append(msg)
 
 829         with patch("black.out", out), patch("black.err", err):
 
 830             black.DebugVisitor.show(source)
 
 831         actual = "\n".join(out_lines) + "\n"
 
 833         if expected != actual:
 
 834             log_name = black.dump_to_file(*out_lines)
 
 838             f"AST print out is different. Actual version dumped to {log_name}",
 
 841     def test_format_file_contents(self) -> None:
 
 843         with self.assertRaises(black.NothingChanged):
 
 844             black.format_file_contents(empty, line_length=ll, fast=False)
 
 846         with self.assertRaises(black.NothingChanged):
 
 847             black.format_file_contents(just_nl, line_length=ll, fast=False)
 
 848         same = "l = [1, 2, 3]\n"
 
 849         with self.assertRaises(black.NothingChanged):
 
 850             black.format_file_contents(same, line_length=ll, fast=False)
 
 851         different = "l = [1,2,3]"
 
 853         actual = black.format_file_contents(different, line_length=ll, fast=False)
 
 854         self.assertEqual(expected, actual)
 
 855         invalid = "return if you can"
 
 856         with self.assertRaises(black.InvalidInput) as e:
 
 857             black.format_file_contents(invalid, line_length=ll, fast=False)
 
 858         self.assertEqual(str(e.exception), "Cannot parse: 1:7: return if you can")
 
 860     def test_endmarker(self) -> None:
 
 861         n = black.lib2to3_parse("\n")
 
 862         self.assertEqual(n.type, black.syms.file_input)
 
 863         self.assertEqual(len(n.children), 1)
 
 864         self.assertEqual(n.children[0].type, black.token.ENDMARKER)
 
 866     @unittest.skipIf(os.environ.get("SKIP_AST_PRINT"), "user set SKIP_AST_PRINT")
 
 867     def test_assertFormatEqual(self) -> None:
 
 871         def out(msg: str, **kwargs: Any) -> None:
 
 872             out_lines.append(msg)
 
 874         def err(msg: str, **kwargs: Any) -> None:
 
 875             err_lines.append(msg)
 
 877         with patch("black.out", out), patch("black.err", err):
 
 878             with self.assertRaises(AssertionError):
 
 879                 self.assertFormatEqual("l = [1, 2, 3]", "l = [1, 2, 3,]")
 
 881         out_str = "".join(out_lines)
 
 882         self.assertTrue("Expected tree:" in out_str)
 
 883         self.assertTrue("Actual tree:" in out_str)
 
 884         self.assertEqual("".join(err_lines), "")
 
 886     def test_cache_broken_file(self) -> None:
 
 887         mode = black.FileMode.AUTO_DETECT
 
 888         with cache_dir() as workspace:
 
 889             cache_file = black.get_cache_file(black.DEFAULT_LINE_LENGTH, mode)
 
 890             with cache_file.open("w") as fobj:
 
 891                 fobj.write("this is not a pickle")
 
 892             self.assertEqual(black.read_cache(black.DEFAULT_LINE_LENGTH, mode), {})
 
 893             src = (workspace / "test.py").resolve()
 
 894             with src.open("w") as fobj:
 
 895                 fobj.write("print('hello')")
 
 896             result = CliRunner().invoke(black.main, [str(src)])
 
 897             self.assertEqual(result.exit_code, 0)
 
 898             cache = black.read_cache(black.DEFAULT_LINE_LENGTH, mode)
 
 899             self.assertIn(src, cache)
 
 901     def test_cache_single_file_already_cached(self) -> None:
 
 902         mode = black.FileMode.AUTO_DETECT
 
 903         with cache_dir() as workspace:
 
 904             src = (workspace / "test.py").resolve()
 
 905             with src.open("w") as fobj:
 
 906                 fobj.write("print('hello')")
 
 907             black.write_cache({}, [src], black.DEFAULT_LINE_LENGTH, mode)
 
 908             result = CliRunner().invoke(black.main, [str(src)])
 
 909             self.assertEqual(result.exit_code, 0)
 
 910             with src.open("r") as fobj:
 
 911                 self.assertEqual(fobj.read(), "print('hello')")
 
 913     @event_loop(close=False)
 
 914     def test_cache_multiple_files(self) -> None:
 
 915         mode = black.FileMode.AUTO_DETECT
 
 916         with cache_dir() as workspace, patch(
 
 917             "black.ProcessPoolExecutor", new=ThreadPoolExecutor
 
 919             one = (workspace / "one.py").resolve()
 
 920             with one.open("w") as fobj:
 
 921                 fobj.write("print('hello')")
 
 922             two = (workspace / "two.py").resolve()
 
 923             with two.open("w") as fobj:
 
 924                 fobj.write("print('hello')")
 
 925             black.write_cache({}, [one], black.DEFAULT_LINE_LENGTH, mode)
 
 926             result = CliRunner().invoke(black.main, [str(workspace)])
 
 927             self.assertEqual(result.exit_code, 0)
 
 928             with one.open("r") as fobj:
 
 929                 self.assertEqual(fobj.read(), "print('hello')")
 
 930             with two.open("r") as fobj:
 
 931                 self.assertEqual(fobj.read(), 'print("hello")\n')
 
 932             cache = black.read_cache(black.DEFAULT_LINE_LENGTH, mode)
 
 933             self.assertIn(one, cache)
 
 934             self.assertIn(two, cache)
 
 936     def test_no_cache_when_writeback_diff(self) -> None:
 
 937         mode = black.FileMode.AUTO_DETECT
 
 938         with cache_dir() as workspace:
 
 939             src = (workspace / "test.py").resolve()
 
 940             with src.open("w") as fobj:
 
 941                 fobj.write("print('hello')")
 
 942             result = CliRunner().invoke(black.main, [str(src), "--diff"])
 
 943             self.assertEqual(result.exit_code, 0)
 
 944             cache_file = black.get_cache_file(black.DEFAULT_LINE_LENGTH, mode)
 
 945             self.assertFalse(cache_file.exists())
 
 947     def test_no_cache_when_stdin(self) -> None:
 
 948         mode = black.FileMode.AUTO_DETECT
 
 950             result = CliRunner().invoke(
 
 951                 black.main, ["-"], input=BytesIO(b"print('hello')")
 
 953             self.assertEqual(result.exit_code, 0)
 
 954             cache_file = black.get_cache_file(black.DEFAULT_LINE_LENGTH, mode)
 
 955             self.assertFalse(cache_file.exists())
 
 957     def test_read_cache_no_cachefile(self) -> None:
 
 958         mode = black.FileMode.AUTO_DETECT
 
 960             self.assertEqual(black.read_cache(black.DEFAULT_LINE_LENGTH, mode), {})
 
 962     def test_write_cache_read_cache(self) -> None:
 
 963         mode = black.FileMode.AUTO_DETECT
 
 964         with cache_dir() as workspace:
 
 965             src = (workspace / "test.py").resolve()
 
 967             black.write_cache({}, [src], black.DEFAULT_LINE_LENGTH, mode)
 
 968             cache = black.read_cache(black.DEFAULT_LINE_LENGTH, mode)
 
 969             self.assertIn(src, cache)
 
 970             self.assertEqual(cache[src], black.get_cache_info(src))
 
 972     def test_filter_cached(self) -> None:
 
 973         with TemporaryDirectory() as workspace:
 
 974             path = Path(workspace)
 
 975             uncached = (path / "uncached").resolve()
 
 976             cached = (path / "cached").resolve()
 
 977             cached_but_changed = (path / "changed").resolve()
 
 980             cached_but_changed.touch()
 
 981             cache = {cached: black.get_cache_info(cached), cached_but_changed: (0.0, 0)}
 
 982             todo, done = black.filter_cached(
 
 983                 cache, {uncached, cached, cached_but_changed}
 
 985             self.assertEqual(todo, {uncached, cached_but_changed})
 
 986             self.assertEqual(done, {cached})
 
 988     def test_write_cache_creates_directory_if_needed(self) -> None:
 
 989         mode = black.FileMode.AUTO_DETECT
 
 990         with cache_dir(exists=False) as workspace:
 
 991             self.assertFalse(workspace.exists())
 
 992             black.write_cache({}, [], black.DEFAULT_LINE_LENGTH, mode)
 
 993             self.assertTrue(workspace.exists())
 
 995     @event_loop(close=False)
 
 996     def test_failed_formatting_does_not_get_cached(self) -> None:
 
 997         mode = black.FileMode.AUTO_DETECT
 
 998         with cache_dir() as workspace, patch(
 
 999             "black.ProcessPoolExecutor", new=ThreadPoolExecutor
 
1001             failing = (workspace / "failing.py").resolve()
 
1002             with failing.open("w") as fobj:
 
1003                 fobj.write("not actually python")
 
1004             clean = (workspace / "clean.py").resolve()
 
1005             with clean.open("w") as fobj:
 
1006                 fobj.write('print("hello")\n')
 
1007             result = CliRunner().invoke(black.main, [str(workspace)])
 
1008             self.assertEqual(result.exit_code, 123)
 
1009             cache = black.read_cache(black.DEFAULT_LINE_LENGTH, mode)
 
1010             self.assertNotIn(failing, cache)
 
1011             self.assertIn(clean, cache)
 
1013     def test_write_cache_write_fail(self) -> None:
 
1014         mode = black.FileMode.AUTO_DETECT
 
1015         with cache_dir(), patch.object(Path, "open") as mock:
 
1016             mock.side_effect = OSError
 
1017             black.write_cache({}, [], black.DEFAULT_LINE_LENGTH, mode)
 
1019     @event_loop(close=False)
 
1020     def test_check_diff_use_together(self) -> None:
 
1022             # Files which will be reformatted.
 
1023             src1 = (THIS_DIR / "data" / "string_quotes.py").resolve()
 
1024             result = CliRunner().invoke(black.main, [str(src1), "--diff", "--check"])
 
1025             self.assertEqual(result.exit_code, 1, result.output)
 
1026             # Files which will not be reformatted.
 
1027             src2 = (THIS_DIR / "data" / "composition.py").resolve()
 
1028             result = CliRunner().invoke(black.main, [str(src2), "--diff", "--check"])
 
1029             self.assertEqual(result.exit_code, 0, result.output)
 
1030             # Multi file command.
 
1031             result = CliRunner().invoke(
 
1032                 black.main, [str(src1), str(src2), "--diff", "--check"]
 
1034             self.assertEqual(result.exit_code, 1, result.output)
 
1036     def test_no_files(self) -> None:
 
1038             # Without an argument, black exits with error code 0.
 
1039             result = CliRunner().invoke(black.main, [])
 
1040             self.assertEqual(result.exit_code, 0)
 
1042     def test_broken_symlink(self) -> None:
 
1043         with cache_dir() as workspace:
 
1044             symlink = workspace / "broken_link.py"
 
1046                 symlink.symlink_to("nonexistent.py")
 
1047             except OSError as e:
 
1048                 self.skipTest(f"Can't create symlinks: {e}")
 
1049             result = CliRunner().invoke(black.main, [str(workspace.resolve())])
 
1050             self.assertEqual(result.exit_code, 0)
 
1052     def test_read_cache_line_lengths(self) -> None:
 
1053         mode = black.FileMode.AUTO_DETECT
 
1054         with cache_dir() as workspace:
 
1055             path = (workspace / "file.py").resolve()
 
1057             black.write_cache({}, [path], 1, mode)
 
1058             one = black.read_cache(1, mode)
 
1059             self.assertIn(path, one)
 
1060             two = black.read_cache(2, mode)
 
1061             self.assertNotIn(path, two)
 
1063     def test_single_file_force_pyi(self) -> None:
 
1064         reg_mode = black.FileMode.AUTO_DETECT
 
1065         pyi_mode = black.FileMode.PYI
 
1066         contents, expected = read_data("force_pyi")
 
1067         with cache_dir() as workspace:
 
1068             path = (workspace / "file.py").resolve()
 
1069             with open(path, "w") as fh:
 
1071             result = CliRunner().invoke(black.main, [str(path), "--pyi"])
 
1072             self.assertEqual(result.exit_code, 0)
 
1073             with open(path, "r") as fh:
 
1075             # verify cache with --pyi is separate
 
1076             pyi_cache = black.read_cache(black.DEFAULT_LINE_LENGTH, pyi_mode)
 
1077             self.assertIn(path, pyi_cache)
 
1078             normal_cache = black.read_cache(black.DEFAULT_LINE_LENGTH, reg_mode)
 
1079             self.assertNotIn(path, normal_cache)
 
1080         self.assertEqual(actual, expected)
 
1082     @event_loop(close=False)
 
1083     def test_multi_file_force_pyi(self) -> None:
 
1084         reg_mode = black.FileMode.AUTO_DETECT
 
1085         pyi_mode = black.FileMode.PYI
 
1086         contents, expected = read_data("force_pyi")
 
1087         with cache_dir() as workspace:
 
1089                 (workspace / "file1.py").resolve(),
 
1090                 (workspace / "file2.py").resolve(),
 
1093                 with open(path, "w") as fh:
 
1095             result = CliRunner().invoke(black.main, [str(p) for p in paths] + ["--pyi"])
 
1096             self.assertEqual(result.exit_code, 0)
 
1098                 with open(path, "r") as fh:
 
1100                 self.assertEqual(actual, expected)
 
1101             # verify cache with --pyi is separate
 
1102             pyi_cache = black.read_cache(black.DEFAULT_LINE_LENGTH, pyi_mode)
 
1103             normal_cache = black.read_cache(black.DEFAULT_LINE_LENGTH, reg_mode)
 
1105                 self.assertIn(path, pyi_cache)
 
1106                 self.assertNotIn(path, normal_cache)
 
1108     def test_pipe_force_pyi(self) -> None:
 
1109         source, expected = read_data("force_pyi")
 
1110         result = CliRunner().invoke(
 
1111             black.main, ["-", "-q", "--pyi"], input=BytesIO(source.encode("utf8"))
 
1113         self.assertEqual(result.exit_code, 0)
 
1114         actual = result.output
 
1115         self.assertFormatEqual(actual, expected)
 
1117     def test_single_file_force_py36(self) -> None:
 
1118         reg_mode = black.FileMode.AUTO_DETECT
 
1119         py36_mode = black.FileMode.PYTHON36
 
1120         source, expected = read_data("force_py36")
 
1121         with cache_dir() as workspace:
 
1122             path = (workspace / "file.py").resolve()
 
1123             with open(path, "w") as fh:
 
1125             result = CliRunner().invoke(black.main, [str(path), "--py36"])
 
1126             self.assertEqual(result.exit_code, 0)
 
1127             with open(path, "r") as fh:
 
1129             # verify cache with --py36 is separate
 
1130             py36_cache = black.read_cache(black.DEFAULT_LINE_LENGTH, py36_mode)
 
1131             self.assertIn(path, py36_cache)
 
1132             normal_cache = black.read_cache(black.DEFAULT_LINE_LENGTH, reg_mode)
 
1133             self.assertNotIn(path, normal_cache)
 
1134         self.assertEqual(actual, expected)
 
1136     @event_loop(close=False)
 
1137     def test_multi_file_force_py36(self) -> None:
 
1138         reg_mode = black.FileMode.AUTO_DETECT
 
1139         py36_mode = black.FileMode.PYTHON36
 
1140         source, expected = read_data("force_py36")
 
1141         with cache_dir() as workspace:
 
1143                 (workspace / "file1.py").resolve(),
 
1144                 (workspace / "file2.py").resolve(),
 
1147                 with open(path, "w") as fh:
 
1149             result = CliRunner().invoke(
 
1150                 black.main, [str(p) for p in paths] + ["--py36"]
 
1152             self.assertEqual(result.exit_code, 0)
 
1154                 with open(path, "r") as fh:
 
1156                 self.assertEqual(actual, expected)
 
1157             # verify cache with --py36 is separate
 
1158             pyi_cache = black.read_cache(black.DEFAULT_LINE_LENGTH, py36_mode)
 
1159             normal_cache = black.read_cache(black.DEFAULT_LINE_LENGTH, reg_mode)
 
1161                 self.assertIn(path, pyi_cache)
 
1162                 self.assertNotIn(path, normal_cache)
 
1164     def test_pipe_force_py36(self) -> None:
 
1165         source, expected = read_data("force_py36")
 
1166         result = CliRunner().invoke(
 
1167             black.main, ["-", "-q", "--py36"], input=BytesIO(source.encode("utf8"))
 
1169         self.assertEqual(result.exit_code, 0)
 
1170         actual = result.output
 
1171         self.assertFormatEqual(actual, expected)
 
1173     def test_include_exclude(self) -> None:
 
1174         path = THIS_DIR / "data" / "include_exclude_tests"
 
1175         include = re.compile(r"\.pyi?$")
 
1176         exclude = re.compile(r"/exclude/|/\.definitely_exclude/")
 
1177         report = black.Report()
 
1178         sources: List[Path] = []
 
1180             Path(path / "b/dont_exclude/a.py"),
 
1181             Path(path / "b/dont_exclude/a.pyi"),
 
1183         this_abs = THIS_DIR.resolve()
 
1185             black.gen_python_files_in_dir(path, this_abs, include, exclude, report)
 
1187         self.assertEqual(sorted(expected), sorted(sources))
 
1189     def test_empty_include(self) -> None:
 
1190         path = THIS_DIR / "data" / "include_exclude_tests"
 
1191         report = black.Report()
 
1192         empty = re.compile(r"")
 
1193         sources: List[Path] = []
 
1195             Path(path / "b/exclude/a.pie"),
 
1196             Path(path / "b/exclude/a.py"),
 
1197             Path(path / "b/exclude/a.pyi"),
 
1198             Path(path / "b/dont_exclude/a.pie"),
 
1199             Path(path / "b/dont_exclude/a.py"),
 
1200             Path(path / "b/dont_exclude/a.pyi"),
 
1201             Path(path / "b/.definitely_exclude/a.pie"),
 
1202             Path(path / "b/.definitely_exclude/a.py"),
 
1203             Path(path / "b/.definitely_exclude/a.pyi"),
 
1205         this_abs = THIS_DIR.resolve()
 
1207             black.gen_python_files_in_dir(
 
1208                 path, this_abs, empty, re.compile(black.DEFAULT_EXCLUDES), report
 
1211         self.assertEqual(sorted(expected), sorted(sources))
 
1213     def test_empty_exclude(self) -> None:
 
1214         path = THIS_DIR / "data" / "include_exclude_tests"
 
1215         report = black.Report()
 
1216         empty = re.compile(r"")
 
1217         sources: List[Path] = []
 
1219             Path(path / "b/dont_exclude/a.py"),
 
1220             Path(path / "b/dont_exclude/a.pyi"),
 
1221             Path(path / "b/exclude/a.py"),
 
1222             Path(path / "b/exclude/a.pyi"),
 
1223             Path(path / "b/.definitely_exclude/a.py"),
 
1224             Path(path / "b/.definitely_exclude/a.pyi"),
 
1226         this_abs = THIS_DIR.resolve()
 
1228             black.gen_python_files_in_dir(
 
1229                 path, this_abs, re.compile(black.DEFAULT_INCLUDES), empty, report
 
1232         self.assertEqual(sorted(expected), sorted(sources))
 
1234     def test_invalid_include_exclude(self) -> None:
 
1235         for option in ["--include", "--exclude"]:
 
1236             result = CliRunner().invoke(black.main, ["-", option, "**()(!!*)"])
 
1237             self.assertEqual(result.exit_code, 2)
 
1239     def test_preserves_line_endings(self) -> None:
 
1240         with TemporaryDirectory() as workspace:
 
1241             test_file = Path(workspace) / "test.py"
 
1242             for nl in ["\n", "\r\n"]:
 
1243                 contents = nl.join(["def f(  ):", "    pass"])
 
1244                 test_file.write_bytes(contents.encode())
 
1245                 ff(test_file, write_back=black.WriteBack.YES)
 
1246                 updated_contents: bytes = test_file.read_bytes()
 
1247                 self.assertIn(nl.encode(), updated_contents)
 
1249                     self.assertNotIn(b"\r\n", updated_contents)
 
1251     def test_preserves_line_endings_via_stdin(self) -> None:
 
1252         for nl in ["\n", "\r\n"]:
 
1253             contents = nl.join(["def f(  ):", "    pass"])
 
1254             runner = BlackRunner()
 
1255             result = runner.invoke(
 
1256                 black.main, ["-", "--fast"], input=BytesIO(contents.encode("utf8"))
 
1258             self.assertEqual(result.exit_code, 0)
 
1259             output = runner.stdout_bytes
 
1260             self.assertIn(nl.encode("utf8"), output)
 
1262                 self.assertNotIn(b"\r\n", output)
 
1264     def test_assert_equivalent_different_asts(self) -> None:
 
1265         with self.assertRaises(AssertionError):
 
1266             black.assert_equivalent("{}", "None")
 
1268     def test_symlink_out_of_root_directory(self) -> None:
 
1272         include = re.compile(black.DEFAULT_INCLUDES)
 
1273         exclude = re.compile(black.DEFAULT_EXCLUDES)
 
1274         report = black.Report()
 
1275         # `child` should behave like a symlink which resolved path is clearly
 
1276         # outside of the `root` directory.
 
1277         path.iterdir.return_value = [child]
 
1278         child.resolve.return_value = Path("/a/b/c")
 
1279         child.is_symlink.return_value = True
 
1281             list(black.gen_python_files_in_dir(path, root, include, exclude, report))
 
1282         except ValueError as ve:
 
1283             self.fail("`get_python_files_in_dir()` failed: {ve}")
 
1284         path.iterdir.assert_called_once()
 
1285         child.resolve.assert_called_once()
 
1286         child.is_symlink.assert_called_once()
 
1287         # `child` should behave like a strange file which resolved path is clearly
 
1288         # outside of the `root` directory.
 
1289         child.is_symlink.return_value = False
 
1290         with self.assertRaises(ValueError):
 
1291             list(black.gen_python_files_in_dir(path, root, include, exclude, report))
 
1292         path.iterdir.assert_called()
 
1293         self.assertEqual(path.iterdir.call_count, 2)
 
1294         child.resolve.assert_called()
 
1295         self.assertEqual(child.resolve.call_count, 2)
 
1296         child.is_symlink.assert_called()
 
1297         self.assertEqual(child.is_symlink.call_count, 2)
 
1299     def test_shhh_click(self) -> None:
 
1301             from click import _unicodefun  # type: ignore
 
1302         except ModuleNotFoundError:
 
1303             self.skipTest("Incompatible Click version")
 
1304         if not hasattr(_unicodefun, "_verify_python3_env"):
 
1305             self.skipTest("Incompatible Click version")
 
1306         # First, let's see if Click is crashing with a preferred ASCII charset.
 
1307         with patch("locale.getpreferredencoding") as gpe:
 
1308             gpe.return_value = "ASCII"
 
1309             with self.assertRaises(RuntimeError):
 
1310                 _unicodefun._verify_python3_env()
 
1311         # Now, let's silence Click...
 
1313         # ...and confirm it's silent.
 
1314         with patch("locale.getpreferredencoding") as gpe:
 
1315             gpe.return_value = "ASCII"
 
1317                 _unicodefun._verify_python3_env()
 
1318             except RuntimeError as re:
 
1319                 self.fail(f"`patch_click()` failed, exception still raised: {re}")
 
1321     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1323     async def test_blackd_request_needs_formatting(self) -> None:
 
1324         app = blackd.make_app()
 
1325         async with TestClient(TestServer(app)) as client:
 
1326             response = await client.post("/", data=b"print('hello world')")
 
1327             self.assertEqual(response.status, 200)
 
1328             self.assertEqual(response.charset, "utf8")
 
1329             self.assertEqual(await response.read(), b'print("hello world")\n')
 
1331     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1333     async def test_blackd_request_no_change(self) -> None:
 
1334         app = blackd.make_app()
 
1335         async with TestClient(TestServer(app)) as client:
 
1336             response = await client.post("/", data=b'print("hello world")\n')
 
1337             self.assertEqual(response.status, 204)
 
1338             self.assertEqual(await response.read(), b"")
 
1340     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1342     async def test_blackd_request_syntax_error(self) -> None:
 
1343         app = blackd.make_app()
 
1344         async with TestClient(TestServer(app)) as client:
 
1345             response = await client.post("/", data=b"what even ( is")
 
1346             self.assertEqual(response.status, 400)
 
1347             content = await response.text()
 
1349                 content.startswith("Cannot parse"),
 
1350                 msg=f"Expected error to start with 'Cannot parse', got {repr(content)}",
 
1353     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1355     async def test_blackd_unsupported_version(self) -> None:
 
1356         app = blackd.make_app()
 
1357         async with TestClient(TestServer(app)) as client:
 
1358             response = await client.post(
 
1359                 "/", data=b"what", headers={blackd.VERSION_HEADER: "2"}
 
1361             self.assertEqual(response.status, 501)
 
1363     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1365     async def test_blackd_supported_version(self) -> None:
 
1366         app = blackd.make_app()
 
1367         async with TestClient(TestServer(app)) as client:
 
1368             response = await client.post(
 
1369                 "/", data=b"what", headers={blackd.VERSION_HEADER: "1"}
 
1371             self.assertEqual(response.status, 200)
 
1373     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1375     async def test_blackd_invalid_python_variant(self) -> None:
 
1376         app = blackd.make_app()
 
1377         async with TestClient(TestServer(app)) as client:
 
1378             response = await client.post(
 
1379                 "/", data=b"what", headers={blackd.PYTHON_VARIANT_HEADER: "lol"}
 
1381             self.assertEqual(response.status, 400)
 
1383     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1385     async def test_blackd_pyi(self) -> None:
 
1386         app = blackd.make_app()
 
1387         async with TestClient(TestServer(app)) as client:
 
1388             source, expected = read_data("stub.pyi")
 
1389             response = await client.post(
 
1390                 "/", data=source, headers={blackd.PYTHON_VARIANT_HEADER: "pyi"}
 
1392             self.assertEqual(response.status, 200)
 
1393             self.assertEqual(await response.text(), expected)
 
1395     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1397     async def test_blackd_py36(self) -> None:
 
1398         app = blackd.make_app()
 
1399         async with TestClient(TestServer(app)) as client:
 
1400             response = await client.post(
 
1404                     "    and_has_a_bunch_of,\n"
 
1405                     "    very_long_arguments_too,\n"
 
1406                     "    and_lots_of_them_as_well_lol,\n"
 
1407                     "    **and_very_long_keyword_arguments\n"
 
1411                 headers={blackd.PYTHON_VARIANT_HEADER: "3.6"},
 
1413             self.assertEqual(response.status, 200)
 
1414             response = await client.post(
 
1418                     "    and_has_a_bunch_of,\n"
 
1419                     "    very_long_arguments_too,\n"
 
1420                     "    and_lots_of_them_as_well_lol,\n"
 
1421                     "    **and_very_long_keyword_arguments\n"
 
1425                 headers={blackd.PYTHON_VARIANT_HEADER: "3.5"},
 
1427             self.assertEqual(response.status, 204)
 
1428             response = await client.post(
 
1432                     "    and_has_a_bunch_of,\n"
 
1433                     "    very_long_arguments_too,\n"
 
1434                     "    and_lots_of_them_as_well_lol,\n"
 
1435                     "    **and_very_long_keyword_arguments\n"
 
1439                 headers={blackd.PYTHON_VARIANT_HEADER: "2"},
 
1441             self.assertEqual(response.status, 204)
 
1443     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1445     async def test_blackd_fast(self) -> None:
 
1446         app = blackd.make_app()
 
1447         async with TestClient(TestServer(app)) as client:
 
1448             response = await client.post("/", data=b"ur'hello'")
 
1449             self.assertEqual(response.status, 500)
 
1450             self.assertIn("failed to parse source file", await response.text())
 
1451             response = await client.post(
 
1452                 "/", data=b"ur'hello'", headers={blackd.FAST_OR_SAFE_HEADER: "fast"}
 
1454             self.assertEqual(response.status, 200)
 
1456     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1458     async def test_blackd_line_length(self) -> None:
 
1459         app = blackd.make_app()
 
1460         async with TestClient(TestServer(app)) as client:
 
1461             response = await client.post(
 
1462                 "/", data=b'print("hello")\n', headers={blackd.LINE_LENGTH_HEADER: "7"}
 
1464             self.assertEqual(response.status, 200)
 
1466     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1468     async def test_blackd_invalid_line_length(self) -> None:
 
1469         app = blackd.make_app()
 
1470         async with TestClient(TestServer(app)) as client:
 
1471             response = await client.post(
 
1473                 data=b'print("hello")\n',
 
1474                 headers={blackd.LINE_LENGTH_HEADER: "NaN"},
 
1476             self.assertEqual(response.status, 400)
 
1478     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1479     def test_blackd_main(self) -> None:
 
1480         with patch("blackd.web.run_app"):
 
1481             result = CliRunner().invoke(blackd.main, [])
 
1482             if result.exception is not None:
 
1483                 raise result.exception
 
1484             self.assertEqual(result.exit_code, 0)
 
1487 if __name__ == "__main__":
 
1488     unittest.main(module="test_black")