All patches and comments are welcome. Please squash your changes to logical
commits before using git-format-patch and git-send-email to
patches@git.madduck.net.
If you'd read over the Git project's submission guidelines and adhered to them,
I'd be especially grateful.
   4 from concurrent.futures import ThreadPoolExecutor
 
   5 from contextlib import contextmanager
 
   6 from functools import partial
 
   7 from io import BytesIO, TextIOWrapper
 
   9 from pathlib import Path
 
  12 from tempfile import TemporaryDirectory
 
  13 from typing import Any, BinaryIO, Generator, List, Tuple, Iterator, TypeVar
 
  15 from unittest.mock import patch, MagicMock
 
  17 from click import unstyle
 
  18 from click.testing import CliRunner
 
  21 from black import Feature, TargetVersion
 
  25     from aiohttp.test_utils import AioHTTPTestCase, unittest_run_loop
 
  26     from aiohttp import web
 
  28     has_blackd_deps = False
 
  30     has_blackd_deps = True
 
  32 from pathspec import PathSpec
 
  34 ff = partial(black.format_file_in_place, mode=black.FileMode(), fast=True)
 
  35 fs = partial(black.format_str, mode=black.FileMode())
 
  36 THIS_FILE = Path(__file__)
 
  37 THIS_DIR = THIS_FILE.parent
 
  38 DETERMINISTIC_HEADER = "[Deterministic header]"
 
  39 EMPTY_LINE = "# EMPTY LINE WITH WHITESPACE" + " (this comment will be removed)"
 
  41     f"--target-version={version.name.lower()}" for version in black.PY36_VERSIONS
 
  47 def dump_to_stderr(*output: str) -> str:
 
  48     return "\n" + "\n".join(output) + "\n"
 
  51 def read_data(name: str, data: bool = True) -> Tuple[str, str]:
 
  52     """read_data('test_name') -> 'input', 'output'"""
 
  53     if not name.endswith((".py", ".pyi", ".out", ".diff")):
 
  55     _input: List[str] = []
 
  56     _output: List[str] = []
 
  57     base_dir = THIS_DIR / "data" if data else THIS_DIR
 
  58     with open(base_dir / name, "r", encoding="utf8") as test:
 
  59         lines = test.readlines()
 
  62         line = line.replace(EMPTY_LINE, "")
 
  63         if line.rstrip() == "# output":
 
  68     if _input and not _output:
 
  69         # If there's no output marker, treat the entire file as already pre-formatted.
 
  71     return "".join(_input).strip() + "\n", "".join(_output).strip() + "\n"
 
  75 def cache_dir(exists: bool = True) -> Iterator[Path]:
 
  76     with TemporaryDirectory() as workspace:
 
  77         cache_dir = Path(workspace)
 
  79             cache_dir = cache_dir / "new"
 
  80         with patch("black.CACHE_DIR", cache_dir):
 
  85 def event_loop(close: bool) -> Iterator[None]:
 
  86     policy = asyncio.get_event_loop_policy()
 
  87     loop = policy.new_event_loop()
 
  88     asyncio.set_event_loop(loop)
 
  98 def skip_if_exception(e: str) -> Iterator[None]:
 
 101     except Exception as exc:
 
 102         if exc.__class__.__name__ == e:
 
 103             unittest.skip(f"Encountered expected exception {exc}, skipping")
 
 108 class BlackRunner(CliRunner):
 
 109     """Modify CliRunner so that stderr is not merged with stdout.
 
 111     This is a hack that can be removed once we depend on Click 7.x"""
 
 113     def __init__(self) -> None:
 
 114         self.stderrbuf = BytesIO()
 
 115         self.stdoutbuf = BytesIO()
 
 116         self.stdout_bytes = b""
 
 117         self.stderr_bytes = b""
 
 121     def isolation(self, *args: Any, **kwargs: Any) -> Generator[BinaryIO, None, None]:
 
 122         with super().isolation(*args, **kwargs) as output:
 
 124                 hold_stderr = sys.stderr
 
 125                 sys.stderr = TextIOWrapper(self.stderrbuf, encoding=self.charset)
 
 128                 self.stdout_bytes = sys.stdout.buffer.getvalue()  # type: ignore
 
 129                 self.stderr_bytes = sys.stderr.buffer.getvalue()  # type: ignore
 
 130                 sys.stderr = hold_stderr
 
 133 class BlackTestCase(unittest.TestCase):
 
 136     def assertFormatEqual(self, expected: str, actual: str) -> None:
 
 137         if actual != expected and not os.environ.get("SKIP_AST_PRINT"):
 
 138             bdv: black.DebugVisitor[Any]
 
 139             black.out("Expected tree:", fg="green")
 
 141                 exp_node = black.lib2to3_parse(expected)
 
 142                 bdv = black.DebugVisitor()
 
 143                 list(bdv.visit(exp_node))
 
 144             except Exception as ve:
 
 146             black.out("Actual tree:", fg="red")
 
 148                 exp_node = black.lib2to3_parse(actual)
 
 149                 bdv = black.DebugVisitor()
 
 150                 list(bdv.visit(exp_node))
 
 151             except Exception as ve:
 
 153         self.assertEqual(expected, actual)
 
 156         self, args: List[str], exit_code: int = 0, ignore_config: bool = True
 
 158         runner = BlackRunner()
 
 160             args = ["--config", str(THIS_DIR / "empty.toml"), *args]
 
 161         result = runner.invoke(black.main, args)
 
 162         self.assertEqual(result.exit_code, exit_code, msg=runner.stderr_bytes.decode())
 
 164     @patch("black.dump_to_file", dump_to_stderr)
 
 165     def checkSourceFile(self, name: str) -> None:
 
 166         path = THIS_DIR.parent / name
 
 167         source, expected = read_data(str(path), data=False)
 
 169         self.assertFormatEqual(expected, actual)
 
 170         black.assert_equivalent(source, actual)
 
 171         black.assert_stable(source, actual, black.FileMode())
 
 172         self.assertFalse(ff(path))
 
 174     @patch("black.dump_to_file", dump_to_stderr)
 
 175     def test_empty(self) -> None:
 
 176         source = expected = ""
 
 178         self.assertFormatEqual(expected, actual)
 
 179         black.assert_equivalent(source, actual)
 
 180         black.assert_stable(source, actual, black.FileMode())
 
 182     def test_empty_ff(self) -> None:
 
 184         tmp_file = Path(black.dump_to_file())
 
 186             self.assertFalse(ff(tmp_file, write_back=black.WriteBack.YES))
 
 187             with open(tmp_file, encoding="utf8") as f:
 
 191         self.assertFormatEqual(expected, actual)
 
 193     def test_self(self) -> None:
 
 194         self.checkSourceFile("tests/test_black.py")
 
 196     def test_black(self) -> None:
 
 197         self.checkSourceFile("black.py")
 
 199     def test_pygram(self) -> None:
 
 200         self.checkSourceFile("blib2to3/pygram.py")
 
 202     def test_pytree(self) -> None:
 
 203         self.checkSourceFile("blib2to3/pytree.py")
 
 205     def test_conv(self) -> None:
 
 206         self.checkSourceFile("blib2to3/pgen2/conv.py")
 
 208     def test_driver(self) -> None:
 
 209         self.checkSourceFile("blib2to3/pgen2/driver.py")
 
 211     def test_grammar(self) -> None:
 
 212         self.checkSourceFile("blib2to3/pgen2/grammar.py")
 
 214     def test_literals(self) -> None:
 
 215         self.checkSourceFile("blib2to3/pgen2/literals.py")
 
 217     def test_parse(self) -> None:
 
 218         self.checkSourceFile("blib2to3/pgen2/parse.py")
 
 220     def test_pgen(self) -> None:
 
 221         self.checkSourceFile("blib2to3/pgen2/pgen.py")
 
 223     def test_tokenize(self) -> None:
 
 224         self.checkSourceFile("blib2to3/pgen2/tokenize.py")
 
 226     def test_token(self) -> None:
 
 227         self.checkSourceFile("blib2to3/pgen2/token.py")
 
 229     def test_setup(self) -> None:
 
 230         self.checkSourceFile("setup.py")
 
 232     def test_piping(self) -> None:
 
 233         source, expected = read_data("../black", data=False)
 
 234         result = BlackRunner().invoke(
 
 236             ["-", "--fast", f"--line-length={black.DEFAULT_LINE_LENGTH}"],
 
 237             input=BytesIO(source.encode("utf8")),
 
 239         self.assertEqual(result.exit_code, 0)
 
 240         self.assertFormatEqual(expected, result.output)
 
 241         black.assert_equivalent(source, result.output)
 
 242         black.assert_stable(source, result.output, black.FileMode())
 
 244     def test_piping_diff(self) -> None:
 
 245         diff_header = re.compile(
 
 246             rf"(STDIN|STDOUT)\t\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d\.\d\d\d\d\d\d "
 
 249         source, _ = read_data("expression.py")
 
 250         expected, _ = read_data("expression.diff")
 
 251         config = THIS_DIR / "data" / "empty_pyproject.toml"
 
 255             f"--line-length={black.DEFAULT_LINE_LENGTH}",
 
 257             f"--config={config}",
 
 259         result = BlackRunner().invoke(
 
 260             black.main, args, input=BytesIO(source.encode("utf8"))
 
 262         self.assertEqual(result.exit_code, 0)
 
 263         actual = diff_header.sub(DETERMINISTIC_HEADER, result.output)
 
 264         actual = actual.rstrip() + "\n"  # the diff output has a trailing space
 
 265         self.assertEqual(expected, actual)
 
 267     @patch("black.dump_to_file", dump_to_stderr)
 
 268     def test_function(self) -> None:
 
 269         source, expected = read_data("function")
 
 271         self.assertFormatEqual(expected, actual)
 
 272         black.assert_equivalent(source, actual)
 
 273         black.assert_stable(source, actual, black.FileMode())
 
 275     @patch("black.dump_to_file", dump_to_stderr)
 
 276     def test_function2(self) -> None:
 
 277         source, expected = read_data("function2")
 
 279         self.assertFormatEqual(expected, actual)
 
 280         black.assert_equivalent(source, actual)
 
 281         black.assert_stable(source, actual, black.FileMode())
 
 283     @patch("black.dump_to_file", dump_to_stderr)
 
 284     def test_function_trailing_comma(self) -> None:
 
 285         source, expected = read_data("function_trailing_comma")
 
 287         self.assertFormatEqual(expected, actual)
 
 288         black.assert_equivalent(source, actual)
 
 289         black.assert_stable(source, actual, black.FileMode())
 
 291     @patch("black.dump_to_file", dump_to_stderr)
 
 292     def test_expression(self) -> None:
 
 293         source, expected = read_data("expression")
 
 295         self.assertFormatEqual(expected, actual)
 
 296         black.assert_equivalent(source, actual)
 
 297         black.assert_stable(source, actual, black.FileMode())
 
 299     @patch("black.dump_to_file", dump_to_stderr)
 
 300     def test_pep_572(self) -> None:
 
 301         source, expected = read_data("pep_572")
 
 303         self.assertFormatEqual(expected, actual)
 
 304         black.assert_stable(source, actual, black.FileMode())
 
 305         if sys.version_info >= (3, 8):
 
 306             black.assert_equivalent(source, actual)
 
 308     def test_pep_572_version_detection(self) -> None:
 
 309         source, _ = read_data("pep_572")
 
 310         root = black.lib2to3_parse(source)
 
 311         features = black.get_features_used(root)
 
 312         self.assertIn(black.Feature.ASSIGNMENT_EXPRESSIONS, features)
 
 313         versions = black.detect_target_versions(root)
 
 314         self.assertIn(black.TargetVersion.PY38, versions)
 
 316     def test_expression_ff(self) -> None:
 
 317         source, expected = read_data("expression")
 
 318         tmp_file = Path(black.dump_to_file(source))
 
 320             self.assertTrue(ff(tmp_file, write_back=black.WriteBack.YES))
 
 321             with open(tmp_file, encoding="utf8") as f:
 
 325         self.assertFormatEqual(expected, actual)
 
 326         with patch("black.dump_to_file", dump_to_stderr):
 
 327             black.assert_equivalent(source, actual)
 
 328             black.assert_stable(source, actual, black.FileMode())
 
 330     def test_expression_diff(self) -> None:
 
 331         source, _ = read_data("expression.py")
 
 332         expected, _ = read_data("expression.diff")
 
 333         tmp_file = Path(black.dump_to_file(source))
 
 334         diff_header = re.compile(
 
 335             rf"{re.escape(str(tmp_file))}\t\d\d\d\d-\d\d-\d\d "
 
 336             r"\d\d:\d\d:\d\d\.\d\d\d\d\d\d \+\d\d\d\d"
 
 339             result = BlackRunner().invoke(black.main, ["--diff", str(tmp_file)])
 
 340             self.assertEqual(result.exit_code, 0)
 
 343         actual = result.output
 
 344         actual = diff_header.sub(DETERMINISTIC_HEADER, actual)
 
 345         actual = actual.rstrip() + "\n"  # the diff output has a trailing space
 
 346         if expected != actual:
 
 347             dump = black.dump_to_file(actual)
 
 349                 "Expected diff isn't equal to the actual. If you made changes to"
 
 350                 " expression.py and this is an anticipated difference, overwrite"
 
 351                 f" tests/data/expression.diff with {dump}"
 
 353             self.assertEqual(expected, actual, msg)
 
 355     @patch("black.dump_to_file", dump_to_stderr)
 
 356     def test_fstring(self) -> None:
 
 357         source, expected = read_data("fstring")
 
 359         self.assertFormatEqual(expected, actual)
 
 360         black.assert_equivalent(source, actual)
 
 361         black.assert_stable(source, actual, black.FileMode())
 
 363     @patch("black.dump_to_file", dump_to_stderr)
 
 364     def test_pep_570(self) -> None:
 
 365         source, expected = read_data("pep_570")
 
 367         self.assertFormatEqual(expected, actual)
 
 368         black.assert_stable(source, actual, black.FileMode())
 
 369         if sys.version_info >= (3, 8):
 
 370             black.assert_equivalent(source, actual)
 
 372     def test_detect_pos_only_arguments(self) -> None:
 
 373         source, _ = read_data("pep_570")
 
 374         root = black.lib2to3_parse(source)
 
 375         features = black.get_features_used(root)
 
 376         self.assertIn(black.Feature.POS_ONLY_ARGUMENTS, features)
 
 377         versions = black.detect_target_versions(root)
 
 378         self.assertIn(black.TargetVersion.PY38, versions)
 
 380     @patch("black.dump_to_file", dump_to_stderr)
 
 381     def test_string_quotes(self) -> None:
 
 382         source, expected = read_data("string_quotes")
 
 384         self.assertFormatEqual(expected, actual)
 
 385         black.assert_equivalent(source, actual)
 
 386         black.assert_stable(source, actual, black.FileMode())
 
 387         mode = black.FileMode(string_normalization=False)
 
 388         not_normalized = fs(source, mode=mode)
 
 389         self.assertFormatEqual(source.replace("\\\n", ""), not_normalized)
 
 390         black.assert_equivalent(source, not_normalized)
 
 391         black.assert_stable(source, not_normalized, mode=mode)
 
 393     @patch("black.dump_to_file", dump_to_stderr)
 
 394     def test_long_strings(self) -> None:
 
 395         """Tests for splitting long strings."""
 
 396         source, expected = read_data("long_strings")
 
 398         self.assertFormatEqual(expected, actual)
 
 399         black.assert_equivalent(source, actual)
 
 400         black.assert_stable(source, actual, black.FileMode())
 
 402     @patch("black.dump_to_file", dump_to_stderr)
 
 403     def test_long_strings__edge_case(self) -> None:
 
 404         """Edge-case tests for splitting long strings."""
 
 405         source, expected = read_data("long_strings__edge_case")
 
 407         self.assertFormatEqual(expected, actual)
 
 408         black.assert_equivalent(source, actual)
 
 409         black.assert_stable(source, actual, black.FileMode())
 
 411     @patch("black.dump_to_file", dump_to_stderr)
 
 412     def test_long_strings__regression(self) -> None:
 
 413         """Regression tests for splitting long strings."""
 
 414         source, expected = read_data("long_strings__regression")
 
 416         self.assertFormatEqual(expected, actual)
 
 417         black.assert_equivalent(source, actual)
 
 418         black.assert_stable(source, actual, black.FileMode())
 
 420     @patch("black.dump_to_file", dump_to_stderr)
 
 421     def test_slices(self) -> None:
 
 422         source, expected = read_data("slices")
 
 424         self.assertFormatEqual(expected, actual)
 
 425         black.assert_equivalent(source, actual)
 
 426         black.assert_stable(source, actual, black.FileMode())
 
 428     @patch("black.dump_to_file", dump_to_stderr)
 
 429     def test_comments(self) -> None:
 
 430         source, expected = read_data("comments")
 
 432         self.assertFormatEqual(expected, actual)
 
 433         black.assert_equivalent(source, actual)
 
 434         black.assert_stable(source, actual, black.FileMode())
 
 436     @patch("black.dump_to_file", dump_to_stderr)
 
 437     def test_comments2(self) -> None:
 
 438         source, expected = read_data("comments2")
 
 440         self.assertFormatEqual(expected, actual)
 
 441         black.assert_equivalent(source, actual)
 
 442         black.assert_stable(source, actual, black.FileMode())
 
 444     @patch("black.dump_to_file", dump_to_stderr)
 
 445     def test_comments3(self) -> None:
 
 446         source, expected = read_data("comments3")
 
 448         self.assertFormatEqual(expected, actual)
 
 449         black.assert_equivalent(source, actual)
 
 450         black.assert_stable(source, actual, black.FileMode())
 
 452     @patch("black.dump_to_file", dump_to_stderr)
 
 453     def test_comments4(self) -> None:
 
 454         source, expected = read_data("comments4")
 
 456         self.assertFormatEqual(expected, actual)
 
 457         black.assert_equivalent(source, actual)
 
 458         black.assert_stable(source, actual, black.FileMode())
 
 460     @patch("black.dump_to_file", dump_to_stderr)
 
 461     def test_comments5(self) -> None:
 
 462         source, expected = read_data("comments5")
 
 464         self.assertFormatEqual(expected, actual)
 
 465         black.assert_equivalent(source, actual)
 
 466         black.assert_stable(source, actual, black.FileMode())
 
 468     @patch("black.dump_to_file", dump_to_stderr)
 
 469     def test_comments6(self) -> None:
 
 470         source, expected = read_data("comments6")
 
 472         self.assertFormatEqual(expected, actual)
 
 473         black.assert_equivalent(source, actual)
 
 474         black.assert_stable(source, actual, black.FileMode())
 
 476     @patch("black.dump_to_file", dump_to_stderr)
 
 477     def test_comments7(self) -> None:
 
 478         source, expected = read_data("comments7")
 
 480         self.assertFormatEqual(expected, actual)
 
 481         black.assert_equivalent(source, actual)
 
 482         black.assert_stable(source, actual, black.FileMode())
 
 484     @patch("black.dump_to_file", dump_to_stderr)
 
 485     def test_comment_after_escaped_newline(self) -> None:
 
 486         source, expected = read_data("comment_after_escaped_newline")
 
 488         self.assertFormatEqual(expected, actual)
 
 489         black.assert_equivalent(source, actual)
 
 490         black.assert_stable(source, actual, black.FileMode())
 
 492     @patch("black.dump_to_file", dump_to_stderr)
 
 493     def test_cantfit(self) -> None:
 
 494         source, expected = read_data("cantfit")
 
 496         self.assertFormatEqual(expected, actual)
 
 497         black.assert_equivalent(source, actual)
 
 498         black.assert_stable(source, actual, black.FileMode())
 
 500     @patch("black.dump_to_file", dump_to_stderr)
 
 501     def test_import_spacing(self) -> None:
 
 502         source, expected = read_data("import_spacing")
 
 504         self.assertFormatEqual(expected, actual)
 
 505         black.assert_equivalent(source, actual)
 
 506         black.assert_stable(source, actual, black.FileMode())
 
 508     @patch("black.dump_to_file", dump_to_stderr)
 
 509     def test_composition(self) -> None:
 
 510         source, expected = read_data("composition")
 
 512         self.assertFormatEqual(expected, actual)
 
 513         black.assert_equivalent(source, actual)
 
 514         black.assert_stable(source, actual, black.FileMode())
 
 516     @patch("black.dump_to_file", dump_to_stderr)
 
 517     def test_empty_lines(self) -> None:
 
 518         source, expected = read_data("empty_lines")
 
 520         self.assertFormatEqual(expected, actual)
 
 521         black.assert_equivalent(source, actual)
 
 522         black.assert_stable(source, actual, black.FileMode())
 
 524     @patch("black.dump_to_file", dump_to_stderr)
 
 525     def test_remove_parens(self) -> None:
 
 526         source, expected = read_data("remove_parens")
 
 528         self.assertFormatEqual(expected, actual)
 
 529         black.assert_equivalent(source, actual)
 
 530         black.assert_stable(source, actual, black.FileMode())
 
 532     @patch("black.dump_to_file", dump_to_stderr)
 
 533     def test_string_prefixes(self) -> None:
 
 534         source, expected = read_data("string_prefixes")
 
 536         self.assertFormatEqual(expected, actual)
 
 537         black.assert_equivalent(source, actual)
 
 538         black.assert_stable(source, actual, black.FileMode())
 
 540     @patch("black.dump_to_file", dump_to_stderr)
 
 541     def test_numeric_literals(self) -> None:
 
 542         source, expected = read_data("numeric_literals")
 
 543         mode = black.FileMode(target_versions=black.PY36_VERSIONS)
 
 544         actual = fs(source, mode=mode)
 
 545         self.assertFormatEqual(expected, actual)
 
 546         black.assert_equivalent(source, actual)
 
 547         black.assert_stable(source, actual, mode)
 
 549     @patch("black.dump_to_file", dump_to_stderr)
 
 550     def test_numeric_literals_ignoring_underscores(self) -> None:
 
 551         source, expected = read_data("numeric_literals_skip_underscores")
 
 552         mode = black.FileMode(target_versions=black.PY36_VERSIONS)
 
 553         actual = fs(source, mode=mode)
 
 554         self.assertFormatEqual(expected, actual)
 
 555         black.assert_equivalent(source, actual)
 
 556         black.assert_stable(source, actual, mode)
 
 558     @patch("black.dump_to_file", dump_to_stderr)
 
 559     def test_numeric_literals_py2(self) -> None:
 
 560         source, expected = read_data("numeric_literals_py2")
 
 562         self.assertFormatEqual(expected, actual)
 
 563         black.assert_stable(source, actual, black.FileMode())
 
 565     @patch("black.dump_to_file", dump_to_stderr)
 
 566     def test_python2(self) -> None:
 
 567         source, expected = read_data("python2")
 
 569         self.assertFormatEqual(expected, actual)
 
 570         black.assert_equivalent(source, actual)
 
 571         black.assert_stable(source, actual, black.FileMode())
 
 573     @patch("black.dump_to_file", dump_to_stderr)
 
 574     def test_python2_print_function(self) -> None:
 
 575         source, expected = read_data("python2_print_function")
 
 576         mode = black.FileMode(target_versions={TargetVersion.PY27})
 
 577         actual = fs(source, mode=mode)
 
 578         self.assertFormatEqual(expected, actual)
 
 579         black.assert_equivalent(source, actual)
 
 580         black.assert_stable(source, actual, mode)
 
 582     @patch("black.dump_to_file", dump_to_stderr)
 
 583     def test_python2_unicode_literals(self) -> None:
 
 584         source, expected = read_data("python2_unicode_literals")
 
 586         self.assertFormatEqual(expected, actual)
 
 587         black.assert_equivalent(source, actual)
 
 588         black.assert_stable(source, actual, black.FileMode())
 
 590     @patch("black.dump_to_file", dump_to_stderr)
 
 591     def test_stub(self) -> None:
 
 592         mode = black.FileMode(is_pyi=True)
 
 593         source, expected = read_data("stub.pyi")
 
 594         actual = fs(source, mode=mode)
 
 595         self.assertFormatEqual(expected, actual)
 
 596         black.assert_stable(source, actual, mode)
 
 598     @patch("black.dump_to_file", dump_to_stderr)
 
 599     def test_async_as_identifier(self) -> None:
 
 600         source_path = (THIS_DIR / "data" / "async_as_identifier.py").resolve()
 
 601         source, expected = read_data("async_as_identifier")
 
 603         self.assertFormatEqual(expected, actual)
 
 604         major, minor = sys.version_info[:2]
 
 605         if major < 3 or (major <= 3 and minor < 7):
 
 606             black.assert_equivalent(source, actual)
 
 607         black.assert_stable(source, actual, black.FileMode())
 
 608         # ensure black can parse this when the target is 3.6
 
 609         self.invokeBlack([str(source_path), "--target-version", "py36"])
 
 610         # but not on 3.7, because async/await is no longer an identifier
 
 611         self.invokeBlack([str(source_path), "--target-version", "py37"], exit_code=123)
 
 613     @patch("black.dump_to_file", dump_to_stderr)
 
 614     def test_python37(self) -> None:
 
 615         source_path = (THIS_DIR / "data" / "python37.py").resolve()
 
 616         source, expected = read_data("python37")
 
 618         self.assertFormatEqual(expected, actual)
 
 619         major, minor = sys.version_info[:2]
 
 620         if major > 3 or (major == 3 and minor >= 7):
 
 621             black.assert_equivalent(source, actual)
 
 622         black.assert_stable(source, actual, black.FileMode())
 
 623         # ensure black can parse this when the target is 3.7
 
 624         self.invokeBlack([str(source_path), "--target-version", "py37"])
 
 625         # but not on 3.6, because we use async as a reserved keyword
 
 626         self.invokeBlack([str(source_path), "--target-version", "py36"], exit_code=123)
 
 628     @patch("black.dump_to_file", dump_to_stderr)
 
 629     def test_python38(self) -> None:
 
 630         source, expected = read_data("python38")
 
 632         self.assertFormatEqual(expected, actual)
 
 633         major, minor = sys.version_info[:2]
 
 634         if major > 3 or (major == 3 and minor >= 8):
 
 635             black.assert_equivalent(source, actual)
 
 636         black.assert_stable(source, actual, black.FileMode())
 
 638     @patch("black.dump_to_file", dump_to_stderr)
 
 639     def test_fmtonoff(self) -> None:
 
 640         source, expected = read_data("fmtonoff")
 
 642         self.assertFormatEqual(expected, actual)
 
 643         black.assert_equivalent(source, actual)
 
 644         black.assert_stable(source, actual, black.FileMode())
 
 646     @patch("black.dump_to_file", dump_to_stderr)
 
 647     def test_fmtonoff2(self) -> None:
 
 648         source, expected = read_data("fmtonoff2")
 
 650         self.assertFormatEqual(expected, actual)
 
 651         black.assert_equivalent(source, actual)
 
 652         black.assert_stable(source, actual, black.FileMode())
 
 654     @patch("black.dump_to_file", dump_to_stderr)
 
 655     def test_fmtonoff3(self) -> None:
 
 656         source, expected = read_data("fmtonoff3")
 
 658         self.assertFormatEqual(expected, actual)
 
 659         black.assert_equivalent(source, actual)
 
 660         black.assert_stable(source, actual, black.FileMode())
 
 662     @patch("black.dump_to_file", dump_to_stderr)
 
 663     def test_fmtonoff4(self) -> None:
 
 664         source, expected = read_data("fmtonoff4")
 
 666         self.assertFormatEqual(expected, actual)
 
 667         black.assert_equivalent(source, actual)
 
 668         black.assert_stable(source, actual, black.FileMode())
 
 670     @patch("black.dump_to_file", dump_to_stderr)
 
 671     def test_remove_empty_parentheses_after_class(self) -> None:
 
 672         source, expected = read_data("class_blank_parentheses")
 
 674         self.assertFormatEqual(expected, actual)
 
 675         black.assert_equivalent(source, actual)
 
 676         black.assert_stable(source, actual, black.FileMode())
 
 678     @patch("black.dump_to_file", dump_to_stderr)
 
 679     def test_new_line_between_class_and_code(self) -> None:
 
 680         source, expected = read_data("class_methods_new_line")
 
 682         self.assertFormatEqual(expected, actual)
 
 683         black.assert_equivalent(source, actual)
 
 684         black.assert_stable(source, actual, black.FileMode())
 
 686     @patch("black.dump_to_file", dump_to_stderr)
 
 687     def test_bracket_match(self) -> None:
 
 688         source, expected = read_data("bracketmatch")
 
 690         self.assertFormatEqual(expected, actual)
 
 691         black.assert_equivalent(source, actual)
 
 692         black.assert_stable(source, actual, black.FileMode())
 
 694     @patch("black.dump_to_file", dump_to_stderr)
 
 695     def test_tuple_assign(self) -> None:
 
 696         source, expected = read_data("tupleassign")
 
 698         self.assertFormatEqual(expected, actual)
 
 699         black.assert_equivalent(source, actual)
 
 700         black.assert_stable(source, actual, black.FileMode())
 
 702     @patch("black.dump_to_file", dump_to_stderr)
 
 703     def test_beginning_backslash(self) -> None:
 
 704         source, expected = read_data("beginning_backslash")
 
 706         self.assertFormatEqual(expected, actual)
 
 707         black.assert_equivalent(source, actual)
 
 708         black.assert_stable(source, actual, black.FileMode())
 
 710     def test_tab_comment_indentation(self) -> None:
 
 711         contents_tab = "if 1:\n\tif 2:\n\t\tpass\n\t# comment\n\tpass\n"
 
 712         contents_spc = "if 1:\n    if 2:\n        pass\n    # comment\n    pass\n"
 
 713         self.assertFormatEqual(contents_spc, fs(contents_spc))
 
 714         self.assertFormatEqual(contents_spc, fs(contents_tab))
 
 716         contents_tab = "if 1:\n\tif 2:\n\t\tpass\n\t\t# comment\n\tpass\n"
 
 717         contents_spc = "if 1:\n    if 2:\n        pass\n        # comment\n    pass\n"
 
 718         self.assertFormatEqual(contents_spc, fs(contents_spc))
 
 719         self.assertFormatEqual(contents_spc, fs(contents_tab))
 
 721         # mixed tabs and spaces (valid Python 2 code)
 
 722         contents_tab = "if 1:\n        if 2:\n\t\tpass\n\t# comment\n        pass\n"
 
 723         contents_spc = "if 1:\n    if 2:\n        pass\n    # comment\n    pass\n"
 
 724         self.assertFormatEqual(contents_spc, fs(contents_spc))
 
 725         self.assertFormatEqual(contents_spc, fs(contents_tab))
 
 727         contents_tab = "if 1:\n        if 2:\n\t\tpass\n\t\t# comment\n        pass\n"
 
 728         contents_spc = "if 1:\n    if 2:\n        pass\n        # comment\n    pass\n"
 
 729         self.assertFormatEqual(contents_spc, fs(contents_spc))
 
 730         self.assertFormatEqual(contents_spc, fs(contents_tab))
 
 732     def test_report_verbose(self) -> None:
 
 733         report = black.Report(verbose=True)
 
 737         def out(msg: str, **kwargs: Any) -> None:
 
 738             out_lines.append(msg)
 
 740         def err(msg: str, **kwargs: Any) -> None:
 
 741             err_lines.append(msg)
 
 743         with patch("black.out", out), patch("black.err", err):
 
 744             report.done(Path("f1"), black.Changed.NO)
 
 745             self.assertEqual(len(out_lines), 1)
 
 746             self.assertEqual(len(err_lines), 0)
 
 747             self.assertEqual(out_lines[-1], "f1 already well formatted, good job.")
 
 748             self.assertEqual(unstyle(str(report)), "1 file left unchanged.")
 
 749             self.assertEqual(report.return_code, 0)
 
 750             report.done(Path("f2"), black.Changed.YES)
 
 751             self.assertEqual(len(out_lines), 2)
 
 752             self.assertEqual(len(err_lines), 0)
 
 753             self.assertEqual(out_lines[-1], "reformatted f2")
 
 755                 unstyle(str(report)), "1 file reformatted, 1 file left unchanged."
 
 757             report.done(Path("f3"), black.Changed.CACHED)
 
 758             self.assertEqual(len(out_lines), 3)
 
 759             self.assertEqual(len(err_lines), 0)
 
 761                 out_lines[-1], "f3 wasn't modified on disk since last run."
 
 764                 unstyle(str(report)), "1 file reformatted, 2 files left unchanged."
 
 766             self.assertEqual(report.return_code, 0)
 
 768             self.assertEqual(report.return_code, 1)
 
 770             report.failed(Path("e1"), "boom")
 
 771             self.assertEqual(len(out_lines), 3)
 
 772             self.assertEqual(len(err_lines), 1)
 
 773             self.assertEqual(err_lines[-1], "error: cannot format e1: boom")
 
 775                 unstyle(str(report)),
 
 776                 "1 file reformatted, 2 files left unchanged, 1 file failed to"
 
 779             self.assertEqual(report.return_code, 123)
 
 780             report.done(Path("f3"), black.Changed.YES)
 
 781             self.assertEqual(len(out_lines), 4)
 
 782             self.assertEqual(len(err_lines), 1)
 
 783             self.assertEqual(out_lines[-1], "reformatted f3")
 
 785                 unstyle(str(report)),
 
 786                 "2 files reformatted, 2 files left unchanged, 1 file failed to"
 
 789             self.assertEqual(report.return_code, 123)
 
 790             report.failed(Path("e2"), "boom")
 
 791             self.assertEqual(len(out_lines), 4)
 
 792             self.assertEqual(len(err_lines), 2)
 
 793             self.assertEqual(err_lines[-1], "error: cannot format e2: boom")
 
 795                 unstyle(str(report)),
 
 796                 "2 files reformatted, 2 files left unchanged, 2 files failed to"
 
 799             self.assertEqual(report.return_code, 123)
 
 800             report.path_ignored(Path("wat"), "no match")
 
 801             self.assertEqual(len(out_lines), 5)
 
 802             self.assertEqual(len(err_lines), 2)
 
 803             self.assertEqual(out_lines[-1], "wat ignored: no match")
 
 805                 unstyle(str(report)),
 
 806                 "2 files reformatted, 2 files left unchanged, 2 files failed to"
 
 809             self.assertEqual(report.return_code, 123)
 
 810             report.done(Path("f4"), black.Changed.NO)
 
 811             self.assertEqual(len(out_lines), 6)
 
 812             self.assertEqual(len(err_lines), 2)
 
 813             self.assertEqual(out_lines[-1], "f4 already well formatted, good job.")
 
 815                 unstyle(str(report)),
 
 816                 "2 files reformatted, 3 files left unchanged, 2 files failed to"
 
 819             self.assertEqual(report.return_code, 123)
 
 822                 unstyle(str(report)),
 
 823                 "2 files would be reformatted, 3 files would be left unchanged, 2 files"
 
 824                 " would fail to reformat.",
 
 829                 unstyle(str(report)),
 
 830                 "2 files would be reformatted, 3 files would be left unchanged, 2 files"
 
 831                 " would fail to reformat.",
 
 834     def test_report_quiet(self) -> None:
 
 835         report = black.Report(quiet=True)
 
 839         def out(msg: str, **kwargs: Any) -> None:
 
 840             out_lines.append(msg)
 
 842         def err(msg: str, **kwargs: Any) -> None:
 
 843             err_lines.append(msg)
 
 845         with patch("black.out", out), patch("black.err", err):
 
 846             report.done(Path("f1"), black.Changed.NO)
 
 847             self.assertEqual(len(out_lines), 0)
 
 848             self.assertEqual(len(err_lines), 0)
 
 849             self.assertEqual(unstyle(str(report)), "1 file left unchanged.")
 
 850             self.assertEqual(report.return_code, 0)
 
 851             report.done(Path("f2"), black.Changed.YES)
 
 852             self.assertEqual(len(out_lines), 0)
 
 853             self.assertEqual(len(err_lines), 0)
 
 855                 unstyle(str(report)), "1 file reformatted, 1 file left unchanged."
 
 857             report.done(Path("f3"), black.Changed.CACHED)
 
 858             self.assertEqual(len(out_lines), 0)
 
 859             self.assertEqual(len(err_lines), 0)
 
 861                 unstyle(str(report)), "1 file reformatted, 2 files left unchanged."
 
 863             self.assertEqual(report.return_code, 0)
 
 865             self.assertEqual(report.return_code, 1)
 
 867             report.failed(Path("e1"), "boom")
 
 868             self.assertEqual(len(out_lines), 0)
 
 869             self.assertEqual(len(err_lines), 1)
 
 870             self.assertEqual(err_lines[-1], "error: cannot format e1: boom")
 
 872                 unstyle(str(report)),
 
 873                 "1 file reformatted, 2 files left unchanged, 1 file failed to"
 
 876             self.assertEqual(report.return_code, 123)
 
 877             report.done(Path("f3"), black.Changed.YES)
 
 878             self.assertEqual(len(out_lines), 0)
 
 879             self.assertEqual(len(err_lines), 1)
 
 881                 unstyle(str(report)),
 
 882                 "2 files reformatted, 2 files left unchanged, 1 file failed to"
 
 885             self.assertEqual(report.return_code, 123)
 
 886             report.failed(Path("e2"), "boom")
 
 887             self.assertEqual(len(out_lines), 0)
 
 888             self.assertEqual(len(err_lines), 2)
 
 889             self.assertEqual(err_lines[-1], "error: cannot format e2: boom")
 
 891                 unstyle(str(report)),
 
 892                 "2 files reformatted, 2 files left unchanged, 2 files failed to"
 
 895             self.assertEqual(report.return_code, 123)
 
 896             report.path_ignored(Path("wat"), "no match")
 
 897             self.assertEqual(len(out_lines), 0)
 
 898             self.assertEqual(len(err_lines), 2)
 
 900                 unstyle(str(report)),
 
 901                 "2 files reformatted, 2 files left unchanged, 2 files failed to"
 
 904             self.assertEqual(report.return_code, 123)
 
 905             report.done(Path("f4"), black.Changed.NO)
 
 906             self.assertEqual(len(out_lines), 0)
 
 907             self.assertEqual(len(err_lines), 2)
 
 909                 unstyle(str(report)),
 
 910                 "2 files reformatted, 3 files left unchanged, 2 files failed to"
 
 913             self.assertEqual(report.return_code, 123)
 
 916                 unstyle(str(report)),
 
 917                 "2 files would be reformatted, 3 files would be left unchanged, 2 files"
 
 918                 " would fail to reformat.",
 
 923                 unstyle(str(report)),
 
 924                 "2 files would be reformatted, 3 files would be left unchanged, 2 files"
 
 925                 " would fail to reformat.",
 
 928     def test_report_normal(self) -> None:
 
 929         report = black.Report()
 
 933         def out(msg: str, **kwargs: Any) -> None:
 
 934             out_lines.append(msg)
 
 936         def err(msg: str, **kwargs: Any) -> None:
 
 937             err_lines.append(msg)
 
 939         with patch("black.out", out), patch("black.err", err):
 
 940             report.done(Path("f1"), black.Changed.NO)
 
 941             self.assertEqual(len(out_lines), 0)
 
 942             self.assertEqual(len(err_lines), 0)
 
 943             self.assertEqual(unstyle(str(report)), "1 file left unchanged.")
 
 944             self.assertEqual(report.return_code, 0)
 
 945             report.done(Path("f2"), black.Changed.YES)
 
 946             self.assertEqual(len(out_lines), 1)
 
 947             self.assertEqual(len(err_lines), 0)
 
 948             self.assertEqual(out_lines[-1], "reformatted f2")
 
 950                 unstyle(str(report)), "1 file reformatted, 1 file left unchanged."
 
 952             report.done(Path("f3"), black.Changed.CACHED)
 
 953             self.assertEqual(len(out_lines), 1)
 
 954             self.assertEqual(len(err_lines), 0)
 
 955             self.assertEqual(out_lines[-1], "reformatted f2")
 
 957                 unstyle(str(report)), "1 file reformatted, 2 files left unchanged."
 
 959             self.assertEqual(report.return_code, 0)
 
 961             self.assertEqual(report.return_code, 1)
 
 963             report.failed(Path("e1"), "boom")
 
 964             self.assertEqual(len(out_lines), 1)
 
 965             self.assertEqual(len(err_lines), 1)
 
 966             self.assertEqual(err_lines[-1], "error: cannot format e1: boom")
 
 968                 unstyle(str(report)),
 
 969                 "1 file reformatted, 2 files left unchanged, 1 file failed to"
 
 972             self.assertEqual(report.return_code, 123)
 
 973             report.done(Path("f3"), black.Changed.YES)
 
 974             self.assertEqual(len(out_lines), 2)
 
 975             self.assertEqual(len(err_lines), 1)
 
 976             self.assertEqual(out_lines[-1], "reformatted f3")
 
 978                 unstyle(str(report)),
 
 979                 "2 files reformatted, 2 files left unchanged, 1 file failed to"
 
 982             self.assertEqual(report.return_code, 123)
 
 983             report.failed(Path("e2"), "boom")
 
 984             self.assertEqual(len(out_lines), 2)
 
 985             self.assertEqual(len(err_lines), 2)
 
 986             self.assertEqual(err_lines[-1], "error: cannot format e2: boom")
 
 988                 unstyle(str(report)),
 
 989                 "2 files reformatted, 2 files left unchanged, 2 files failed to"
 
 992             self.assertEqual(report.return_code, 123)
 
 993             report.path_ignored(Path("wat"), "no match")
 
 994             self.assertEqual(len(out_lines), 2)
 
 995             self.assertEqual(len(err_lines), 2)
 
 997                 unstyle(str(report)),
 
 998                 "2 files reformatted, 2 files left unchanged, 2 files failed to"
 
1001             self.assertEqual(report.return_code, 123)
 
1002             report.done(Path("f4"), black.Changed.NO)
 
1003             self.assertEqual(len(out_lines), 2)
 
1004             self.assertEqual(len(err_lines), 2)
 
1006                 unstyle(str(report)),
 
1007                 "2 files reformatted, 3 files left unchanged, 2 files failed to"
 
1010             self.assertEqual(report.return_code, 123)
 
1013                 unstyle(str(report)),
 
1014                 "2 files would be reformatted, 3 files would be left unchanged, 2 files"
 
1015                 " would fail to reformat.",
 
1017             report.check = False
 
1020                 unstyle(str(report)),
 
1021                 "2 files would be reformatted, 3 files would be left unchanged, 2 files"
 
1022                 " would fail to reformat.",
 
1025     def test_lib2to3_parse(self) -> None:
 
1026         with self.assertRaises(black.InvalidInput):
 
1027             black.lib2to3_parse("invalid syntax")
 
1029         straddling = "x + y"
 
1030         black.lib2to3_parse(straddling)
 
1031         black.lib2to3_parse(straddling, {TargetVersion.PY27})
 
1032         black.lib2to3_parse(straddling, {TargetVersion.PY36})
 
1033         black.lib2to3_parse(straddling, {TargetVersion.PY27, TargetVersion.PY36})
 
1035         py2_only = "print x"
 
1036         black.lib2to3_parse(py2_only)
 
1037         black.lib2to3_parse(py2_only, {TargetVersion.PY27})
 
1038         with self.assertRaises(black.InvalidInput):
 
1039             black.lib2to3_parse(py2_only, {TargetVersion.PY36})
 
1040         with self.assertRaises(black.InvalidInput):
 
1041             black.lib2to3_parse(py2_only, {TargetVersion.PY27, TargetVersion.PY36})
 
1043         py3_only = "exec(x, end=y)"
 
1044         black.lib2to3_parse(py3_only)
 
1045         with self.assertRaises(black.InvalidInput):
 
1046             black.lib2to3_parse(py3_only, {TargetVersion.PY27})
 
1047         black.lib2to3_parse(py3_only, {TargetVersion.PY36})
 
1048         black.lib2to3_parse(py3_only, {TargetVersion.PY27, TargetVersion.PY36})
 
1050     def test_get_features_used(self) -> None:
 
1051         node = black.lib2to3_parse("def f(*, arg): ...\n")
 
1052         self.assertEqual(black.get_features_used(node), set())
 
1053         node = black.lib2to3_parse("def f(*, arg,): ...\n")
 
1054         self.assertEqual(black.get_features_used(node), {Feature.TRAILING_COMMA_IN_DEF})
 
1055         node = black.lib2to3_parse("f(*arg,)\n")
 
1057             black.get_features_used(node), {Feature.TRAILING_COMMA_IN_CALL}
 
1059         node = black.lib2to3_parse("def f(*, arg): f'string'\n")
 
1060         self.assertEqual(black.get_features_used(node), {Feature.F_STRINGS})
 
1061         node = black.lib2to3_parse("123_456\n")
 
1062         self.assertEqual(black.get_features_used(node), {Feature.NUMERIC_UNDERSCORES})
 
1063         node = black.lib2to3_parse("123456\n")
 
1064         self.assertEqual(black.get_features_used(node), set())
 
1065         source, expected = read_data("function")
 
1066         node = black.lib2to3_parse(source)
 
1067         expected_features = {
 
1068             Feature.TRAILING_COMMA_IN_CALL,
 
1069             Feature.TRAILING_COMMA_IN_DEF,
 
1072         self.assertEqual(black.get_features_used(node), expected_features)
 
1073         node = black.lib2to3_parse(expected)
 
1074         self.assertEqual(black.get_features_used(node), expected_features)
 
1075         source, expected = read_data("expression")
 
1076         node = black.lib2to3_parse(source)
 
1077         self.assertEqual(black.get_features_used(node), set())
 
1078         node = black.lib2to3_parse(expected)
 
1079         self.assertEqual(black.get_features_used(node), set())
 
1081     def test_get_future_imports(self) -> None:
 
1082         node = black.lib2to3_parse("\n")
 
1083         self.assertEqual(set(), black.get_future_imports(node))
 
1084         node = black.lib2to3_parse("from __future__ import black\n")
 
1085         self.assertEqual({"black"}, black.get_future_imports(node))
 
1086         node = black.lib2to3_parse("from __future__ import multiple, imports\n")
 
1087         self.assertEqual({"multiple", "imports"}, black.get_future_imports(node))
 
1088         node = black.lib2to3_parse("from __future__ import (parenthesized, imports)\n")
 
1089         self.assertEqual({"parenthesized", "imports"}, black.get_future_imports(node))
 
1090         node = black.lib2to3_parse(
 
1091             "from __future__ import multiple\nfrom __future__ import imports\n"
 
1093         self.assertEqual({"multiple", "imports"}, black.get_future_imports(node))
 
1094         node = black.lib2to3_parse("# comment\nfrom __future__ import black\n")
 
1095         self.assertEqual({"black"}, black.get_future_imports(node))
 
1096         node = black.lib2to3_parse('"""docstring"""\nfrom __future__ import black\n')
 
1097         self.assertEqual({"black"}, black.get_future_imports(node))
 
1098         node = black.lib2to3_parse("some(other, code)\nfrom __future__ import black\n")
 
1099         self.assertEqual(set(), black.get_future_imports(node))
 
1100         node = black.lib2to3_parse("from some.module import black\n")
 
1101         self.assertEqual(set(), black.get_future_imports(node))
 
1102         node = black.lib2to3_parse(
 
1103             "from __future__ import unicode_literals as _unicode_literals"
 
1105         self.assertEqual({"unicode_literals"}, black.get_future_imports(node))
 
1106         node = black.lib2to3_parse(
 
1107             "from __future__ import unicode_literals as _lol, print"
 
1109         self.assertEqual({"unicode_literals", "print"}, black.get_future_imports(node))
 
1111     def test_debug_visitor(self) -> None:
 
1112         source, _ = read_data("debug_visitor.py")
 
1113         expected, _ = read_data("debug_visitor.out")
 
1117         def out(msg: str, **kwargs: Any) -> None:
 
1118             out_lines.append(msg)
 
1120         def err(msg: str, **kwargs: Any) -> None:
 
1121             err_lines.append(msg)
 
1123         with patch("black.out", out), patch("black.err", err):
 
1124             black.DebugVisitor.show(source)
 
1125         actual = "\n".join(out_lines) + "\n"
 
1127         if expected != actual:
 
1128             log_name = black.dump_to_file(*out_lines)
 
1132             f"AST print out is different. Actual version dumped to {log_name}",
 
1135     def test_format_file_contents(self) -> None:
 
1137         mode = black.FileMode()
 
1138         with self.assertRaises(black.NothingChanged):
 
1139             black.format_file_contents(empty, mode=mode, fast=False)
 
1141         with self.assertRaises(black.NothingChanged):
 
1142             black.format_file_contents(just_nl, mode=mode, fast=False)
 
1143         same = "j = [1, 2, 3]\n"
 
1144         with self.assertRaises(black.NothingChanged):
 
1145             black.format_file_contents(same, mode=mode, fast=False)
 
1146         different = "j = [1,2,3]"
 
1148         actual = black.format_file_contents(different, mode=mode, fast=False)
 
1149         self.assertEqual(expected, actual)
 
1150         invalid = "return if you can"
 
1151         with self.assertRaises(black.InvalidInput) as e:
 
1152             black.format_file_contents(invalid, mode=mode, fast=False)
 
1153         self.assertEqual(str(e.exception), "Cannot parse: 1:7: return if you can")
 
1155     def test_endmarker(self) -> None:
 
1156         n = black.lib2to3_parse("\n")
 
1157         self.assertEqual(n.type, black.syms.file_input)
 
1158         self.assertEqual(len(n.children), 1)
 
1159         self.assertEqual(n.children[0].type, black.token.ENDMARKER)
 
1161     @unittest.skipIf(os.environ.get("SKIP_AST_PRINT"), "user set SKIP_AST_PRINT")
 
1162     def test_assertFormatEqual(self) -> None:
 
1166         def out(msg: str, **kwargs: Any) -> None:
 
1167             out_lines.append(msg)
 
1169         def err(msg: str, **kwargs: Any) -> None:
 
1170             err_lines.append(msg)
 
1172         with patch("black.out", out), patch("black.err", err):
 
1173             with self.assertRaises(AssertionError):
 
1174                 self.assertFormatEqual("j = [1, 2, 3]", "j = [1, 2, 3,]")
 
1176         out_str = "".join(out_lines)
 
1177         self.assertTrue("Expected tree:" in out_str)
 
1178         self.assertTrue("Actual tree:" in out_str)
 
1179         self.assertEqual("".join(err_lines), "")
 
1181     def test_cache_broken_file(self) -> None:
 
1182         mode = black.FileMode()
 
1183         with cache_dir() as workspace:
 
1184             cache_file = black.get_cache_file(mode)
 
1185             with cache_file.open("w") as fobj:
 
1186                 fobj.write("this is not a pickle")
 
1187             self.assertEqual(black.read_cache(mode), {})
 
1188             src = (workspace / "test.py").resolve()
 
1189             with src.open("w") as fobj:
 
1190                 fobj.write("print('hello')")
 
1191             self.invokeBlack([str(src)])
 
1192             cache = black.read_cache(mode)
 
1193             self.assertIn(src, cache)
 
1195     def test_cache_single_file_already_cached(self) -> None:
 
1196         mode = black.FileMode()
 
1197         with cache_dir() as workspace:
 
1198             src = (workspace / "test.py").resolve()
 
1199             with src.open("w") as fobj:
 
1200                 fobj.write("print('hello')")
 
1201             black.write_cache({}, [src], mode)
 
1202             self.invokeBlack([str(src)])
 
1203             with src.open("r") as fobj:
 
1204                 self.assertEqual(fobj.read(), "print('hello')")
 
1206     @event_loop(close=False)
 
1207     def test_cache_multiple_files(self) -> None:
 
1208         mode = black.FileMode()
 
1209         with cache_dir() as workspace, patch(
 
1210             "black.ProcessPoolExecutor", new=ThreadPoolExecutor
 
1212             one = (workspace / "one.py").resolve()
 
1213             with one.open("w") as fobj:
 
1214                 fobj.write("print('hello')")
 
1215             two = (workspace / "two.py").resolve()
 
1216             with two.open("w") as fobj:
 
1217                 fobj.write("print('hello')")
 
1218             black.write_cache({}, [one], mode)
 
1219             self.invokeBlack([str(workspace)])
 
1220             with one.open("r") as fobj:
 
1221                 self.assertEqual(fobj.read(), "print('hello')")
 
1222             with two.open("r") as fobj:
 
1223                 self.assertEqual(fobj.read(), 'print("hello")\n')
 
1224             cache = black.read_cache(mode)
 
1225             self.assertIn(one, cache)
 
1226             self.assertIn(two, cache)
 
1228     def test_no_cache_when_writeback_diff(self) -> None:
 
1229         mode = black.FileMode()
 
1230         with cache_dir() as workspace:
 
1231             src = (workspace / "test.py").resolve()
 
1232             with src.open("w") as fobj:
 
1233                 fobj.write("print('hello')")
 
1234             self.invokeBlack([str(src), "--diff"])
 
1235             cache_file = black.get_cache_file(mode)
 
1236             self.assertFalse(cache_file.exists())
 
1238     def test_no_cache_when_stdin(self) -> None:
 
1239         mode = black.FileMode()
 
1241             result = CliRunner().invoke(
 
1242                 black.main, ["-"], input=BytesIO(b"print('hello')")
 
1244             self.assertEqual(result.exit_code, 0)
 
1245             cache_file = black.get_cache_file(mode)
 
1246             self.assertFalse(cache_file.exists())
 
1248     def test_read_cache_no_cachefile(self) -> None:
 
1249         mode = black.FileMode()
 
1251             self.assertEqual(black.read_cache(mode), {})
 
1253     def test_write_cache_read_cache(self) -> None:
 
1254         mode = black.FileMode()
 
1255         with cache_dir() as workspace:
 
1256             src = (workspace / "test.py").resolve()
 
1258             black.write_cache({}, [src], mode)
 
1259             cache = black.read_cache(mode)
 
1260             self.assertIn(src, cache)
 
1261             self.assertEqual(cache[src], black.get_cache_info(src))
 
1263     def test_filter_cached(self) -> None:
 
1264         with TemporaryDirectory() as workspace:
 
1265             path = Path(workspace)
 
1266             uncached = (path / "uncached").resolve()
 
1267             cached = (path / "cached").resolve()
 
1268             cached_but_changed = (path / "changed").resolve()
 
1271             cached_but_changed.touch()
 
1272             cache = {cached: black.get_cache_info(cached), cached_but_changed: (0.0, 0)}
 
1273             todo, done = black.filter_cached(
 
1274                 cache, {uncached, cached, cached_but_changed}
 
1276             self.assertEqual(todo, {uncached, cached_but_changed})
 
1277             self.assertEqual(done, {cached})
 
1279     def test_write_cache_creates_directory_if_needed(self) -> None:
 
1280         mode = black.FileMode()
 
1281         with cache_dir(exists=False) as workspace:
 
1282             self.assertFalse(workspace.exists())
 
1283             black.write_cache({}, [], mode)
 
1284             self.assertTrue(workspace.exists())
 
1286     @event_loop(close=False)
 
1287     def test_failed_formatting_does_not_get_cached(self) -> None:
 
1288         mode = black.FileMode()
 
1289         with cache_dir() as workspace, patch(
 
1290             "black.ProcessPoolExecutor", new=ThreadPoolExecutor
 
1292             failing = (workspace / "failing.py").resolve()
 
1293             with failing.open("w") as fobj:
 
1294                 fobj.write("not actually python")
 
1295             clean = (workspace / "clean.py").resolve()
 
1296             with clean.open("w") as fobj:
 
1297                 fobj.write('print("hello")\n')
 
1298             self.invokeBlack([str(workspace)], exit_code=123)
 
1299             cache = black.read_cache(mode)
 
1300             self.assertNotIn(failing, cache)
 
1301             self.assertIn(clean, cache)
 
1303     def test_write_cache_write_fail(self) -> None:
 
1304         mode = black.FileMode()
 
1305         with cache_dir(), patch.object(Path, "open") as mock:
 
1306             mock.side_effect = OSError
 
1307             black.write_cache({}, [], mode)
 
1309     @event_loop(close=False)
 
1310     def test_check_diff_use_together(self) -> None:
 
1312             # Files which will be reformatted.
 
1313             src1 = (THIS_DIR / "data" / "string_quotes.py").resolve()
 
1314             self.invokeBlack([str(src1), "--diff", "--check"], exit_code=1)
 
1315             # Files which will not be reformatted.
 
1316             src2 = (THIS_DIR / "data" / "composition.py").resolve()
 
1317             self.invokeBlack([str(src2), "--diff", "--check"])
 
1318             # Multi file command.
 
1319             self.invokeBlack([str(src1), str(src2), "--diff", "--check"], exit_code=1)
 
1321     def test_no_files(self) -> None:
 
1323             # Without an argument, black exits with error code 0.
 
1324             self.invokeBlack([])
 
1326     def test_broken_symlink(self) -> None:
 
1327         with cache_dir() as workspace:
 
1328             symlink = workspace / "broken_link.py"
 
1330                 symlink.symlink_to("nonexistent.py")
 
1331             except OSError as e:
 
1332                 self.skipTest(f"Can't create symlinks: {e}")
 
1333             self.invokeBlack([str(workspace.resolve())])
 
1335     def test_read_cache_line_lengths(self) -> None:
 
1336         mode = black.FileMode()
 
1337         short_mode = black.FileMode(line_length=1)
 
1338         with cache_dir() as workspace:
 
1339             path = (workspace / "file.py").resolve()
 
1341             black.write_cache({}, [path], mode)
 
1342             one = black.read_cache(mode)
 
1343             self.assertIn(path, one)
 
1344             two = black.read_cache(short_mode)
 
1345             self.assertNotIn(path, two)
 
1347     def test_tricky_unicode_symbols(self) -> None:
 
1348         source, expected = read_data("tricky_unicode_symbols")
 
1350         self.assertFormatEqual(expected, actual)
 
1351         black.assert_equivalent(source, actual)
 
1352         black.assert_stable(source, actual, black.FileMode())
 
1354     def test_single_file_force_pyi(self) -> None:
 
1355         reg_mode = black.FileMode()
 
1356         pyi_mode = black.FileMode(is_pyi=True)
 
1357         contents, expected = read_data("force_pyi")
 
1358         with cache_dir() as workspace:
 
1359             path = (workspace / "file.py").resolve()
 
1360             with open(path, "w") as fh:
 
1362             self.invokeBlack([str(path), "--pyi"])
 
1363             with open(path, "r") as fh:
 
1365             # verify cache with --pyi is separate
 
1366             pyi_cache = black.read_cache(pyi_mode)
 
1367             self.assertIn(path, pyi_cache)
 
1368             normal_cache = black.read_cache(reg_mode)
 
1369             self.assertNotIn(path, normal_cache)
 
1370         self.assertEqual(actual, expected)
 
1372     @event_loop(close=False)
 
1373     def test_multi_file_force_pyi(self) -> None:
 
1374         reg_mode = black.FileMode()
 
1375         pyi_mode = black.FileMode(is_pyi=True)
 
1376         contents, expected = read_data("force_pyi")
 
1377         with cache_dir() as workspace:
 
1379                 (workspace / "file1.py").resolve(),
 
1380                 (workspace / "file2.py").resolve(),
 
1383                 with open(path, "w") as fh:
 
1385             self.invokeBlack([str(p) for p in paths] + ["--pyi"])
 
1387                 with open(path, "r") as fh:
 
1389                 self.assertEqual(actual, expected)
 
1390             # verify cache with --pyi is separate
 
1391             pyi_cache = black.read_cache(pyi_mode)
 
1392             normal_cache = black.read_cache(reg_mode)
 
1394                 self.assertIn(path, pyi_cache)
 
1395                 self.assertNotIn(path, normal_cache)
 
1397     def test_pipe_force_pyi(self) -> None:
 
1398         source, expected = read_data("force_pyi")
 
1399         result = CliRunner().invoke(
 
1400             black.main, ["-", "-q", "--pyi"], input=BytesIO(source.encode("utf8"))
 
1402         self.assertEqual(result.exit_code, 0)
 
1403         actual = result.output
 
1404         self.assertFormatEqual(actual, expected)
 
1406     def test_single_file_force_py36(self) -> None:
 
1407         reg_mode = black.FileMode()
 
1408         py36_mode = black.FileMode(target_versions=black.PY36_VERSIONS)
 
1409         source, expected = read_data("force_py36")
 
1410         with cache_dir() as workspace:
 
1411             path = (workspace / "file.py").resolve()
 
1412             with open(path, "w") as fh:
 
1414             self.invokeBlack([str(path), *PY36_ARGS])
 
1415             with open(path, "r") as fh:
 
1417             # verify cache with --target-version is separate
 
1418             py36_cache = black.read_cache(py36_mode)
 
1419             self.assertIn(path, py36_cache)
 
1420             normal_cache = black.read_cache(reg_mode)
 
1421             self.assertNotIn(path, normal_cache)
 
1422         self.assertEqual(actual, expected)
 
1424     @event_loop(close=False)
 
1425     def test_multi_file_force_py36(self) -> None:
 
1426         reg_mode = black.FileMode()
 
1427         py36_mode = black.FileMode(target_versions=black.PY36_VERSIONS)
 
1428         source, expected = read_data("force_py36")
 
1429         with cache_dir() as workspace:
 
1431                 (workspace / "file1.py").resolve(),
 
1432                 (workspace / "file2.py").resolve(),
 
1435                 with open(path, "w") as fh:
 
1437             self.invokeBlack([str(p) for p in paths] + PY36_ARGS)
 
1439                 with open(path, "r") as fh:
 
1441                 self.assertEqual(actual, expected)
 
1442             # verify cache with --target-version is separate
 
1443             pyi_cache = black.read_cache(py36_mode)
 
1444             normal_cache = black.read_cache(reg_mode)
 
1446                 self.assertIn(path, pyi_cache)
 
1447                 self.assertNotIn(path, normal_cache)
 
1449     def test_collections(self) -> None:
 
1450         source, expected = read_data("collections")
 
1452         self.assertFormatEqual(expected, actual)
 
1453         black.assert_equivalent(source, actual)
 
1454         black.assert_stable(source, actual, black.FileMode())
 
1456     def test_pipe_force_py36(self) -> None:
 
1457         source, expected = read_data("force_py36")
 
1458         result = CliRunner().invoke(
 
1460             ["-", "-q", "--target-version=py36"],
 
1461             input=BytesIO(source.encode("utf8")),
 
1463         self.assertEqual(result.exit_code, 0)
 
1464         actual = result.output
 
1465         self.assertFormatEqual(actual, expected)
 
1467     def test_include_exclude(self) -> None:
 
1468         path = THIS_DIR / "data" / "include_exclude_tests"
 
1469         include = re.compile(r"\.pyi?$")
 
1470         exclude = re.compile(r"/exclude/|/\.definitely_exclude/")
 
1471         report = black.Report()
 
1472         gitignore = PathSpec.from_lines("gitwildmatch", [])
 
1473         sources: List[Path] = []
 
1475             Path(path / "b/dont_exclude/a.py"),
 
1476             Path(path / "b/dont_exclude/a.pyi"),
 
1478         this_abs = THIS_DIR.resolve()
 
1480             black.gen_python_files_in_dir(
 
1481                 path, this_abs, include, exclude, report, gitignore
 
1484         self.assertEqual(sorted(expected), sorted(sources))
 
1486     def test_gitignore_exclude(self) -> None:
 
1487         path = THIS_DIR / "data" / "include_exclude_tests"
 
1488         include = re.compile(r"\.pyi?$")
 
1489         exclude = re.compile(r"")
 
1490         report = black.Report()
 
1491         gitignore = PathSpec.from_lines(
 
1492             "gitwildmatch", ["exclude/", ".definitely_exclude"]
 
1494         sources: List[Path] = []
 
1496             Path(path / "b/dont_exclude/a.py"),
 
1497             Path(path / "b/dont_exclude/a.pyi"),
 
1499         this_abs = THIS_DIR.resolve()
 
1501             black.gen_python_files_in_dir(
 
1502                 path, this_abs, include, exclude, report, gitignore
 
1505         self.assertEqual(sorted(expected), sorted(sources))
 
1507     def test_empty_include(self) -> None:
 
1508         path = THIS_DIR / "data" / "include_exclude_tests"
 
1509         report = black.Report()
 
1510         gitignore = PathSpec.from_lines("gitwildmatch", [])
 
1511         empty = re.compile(r"")
 
1512         sources: List[Path] = []
 
1514             Path(path / "b/exclude/a.pie"),
 
1515             Path(path / "b/exclude/a.py"),
 
1516             Path(path / "b/exclude/a.pyi"),
 
1517             Path(path / "b/dont_exclude/a.pie"),
 
1518             Path(path / "b/dont_exclude/a.py"),
 
1519             Path(path / "b/dont_exclude/a.pyi"),
 
1520             Path(path / "b/.definitely_exclude/a.pie"),
 
1521             Path(path / "b/.definitely_exclude/a.py"),
 
1522             Path(path / "b/.definitely_exclude/a.pyi"),
 
1524         this_abs = THIS_DIR.resolve()
 
1526             black.gen_python_files_in_dir(
 
1530                 re.compile(black.DEFAULT_EXCLUDES),
 
1535         self.assertEqual(sorted(expected), sorted(sources))
 
1537     def test_empty_exclude(self) -> None:
 
1538         path = THIS_DIR / "data" / "include_exclude_tests"
 
1539         report = black.Report()
 
1540         gitignore = PathSpec.from_lines("gitwildmatch", [])
 
1541         empty = re.compile(r"")
 
1542         sources: List[Path] = []
 
1544             Path(path / "b/dont_exclude/a.py"),
 
1545             Path(path / "b/dont_exclude/a.pyi"),
 
1546             Path(path / "b/exclude/a.py"),
 
1547             Path(path / "b/exclude/a.pyi"),
 
1548             Path(path / "b/.definitely_exclude/a.py"),
 
1549             Path(path / "b/.definitely_exclude/a.pyi"),
 
1551         this_abs = THIS_DIR.resolve()
 
1553             black.gen_python_files_in_dir(
 
1556                 re.compile(black.DEFAULT_INCLUDES),
 
1562         self.assertEqual(sorted(expected), sorted(sources))
 
1564     def test_invalid_include_exclude(self) -> None:
 
1565         for option in ["--include", "--exclude"]:
 
1566             self.invokeBlack(["-", option, "**()(!!*)"], exit_code=2)
 
1568     def test_preserves_line_endings(self) -> None:
 
1569         with TemporaryDirectory() as workspace:
 
1570             test_file = Path(workspace) / "test.py"
 
1571             for nl in ["\n", "\r\n"]:
 
1572                 contents = nl.join(["def f(  ):", "    pass"])
 
1573                 test_file.write_bytes(contents.encode())
 
1574                 ff(test_file, write_back=black.WriteBack.YES)
 
1575                 updated_contents: bytes = test_file.read_bytes()
 
1576                 self.assertIn(nl.encode(), updated_contents)
 
1578                     self.assertNotIn(b"\r\n", updated_contents)
 
1580     def test_preserves_line_endings_via_stdin(self) -> None:
 
1581         for nl in ["\n", "\r\n"]:
 
1582             contents = nl.join(["def f(  ):", "    pass"])
 
1583             runner = BlackRunner()
 
1584             result = runner.invoke(
 
1585                 black.main, ["-", "--fast"], input=BytesIO(contents.encode("utf8"))
 
1587             self.assertEqual(result.exit_code, 0)
 
1588             output = runner.stdout_bytes
 
1589             self.assertIn(nl.encode("utf8"), output)
 
1591                 self.assertNotIn(b"\r\n", output)
 
1593     def test_assert_equivalent_different_asts(self) -> None:
 
1594         with self.assertRaises(AssertionError):
 
1595             black.assert_equivalent("{}", "None")
 
1597     def test_symlink_out_of_root_directory(self) -> None:
 
1601         include = re.compile(black.DEFAULT_INCLUDES)
 
1602         exclude = re.compile(black.DEFAULT_EXCLUDES)
 
1603         report = black.Report()
 
1604         gitignore = PathSpec.from_lines("gitwildmatch", [])
 
1605         # `child` should behave like a symlink which resolved path is clearly
 
1606         # outside of the `root` directory.
 
1607         path.iterdir.return_value = [child]
 
1608         child.resolve.return_value = Path("/a/b/c")
 
1609         child.as_posix.return_value = "/a/b/c"
 
1610         child.is_symlink.return_value = True
 
1613                 black.gen_python_files_in_dir(
 
1614                     path, root, include, exclude, report, gitignore
 
1617         except ValueError as ve:
 
1618             self.fail(f"`get_python_files_in_dir()` failed: {ve}")
 
1619         path.iterdir.assert_called_once()
 
1620         child.resolve.assert_called_once()
 
1621         child.is_symlink.assert_called_once()
 
1622         # `child` should behave like a strange file which resolved path is clearly
 
1623         # outside of the `root` directory.
 
1624         child.is_symlink.return_value = False
 
1625         with self.assertRaises(ValueError):
 
1627                 black.gen_python_files_in_dir(
 
1628                     path, root, include, exclude, report, gitignore
 
1631         path.iterdir.assert_called()
 
1632         self.assertEqual(path.iterdir.call_count, 2)
 
1633         child.resolve.assert_called()
 
1634         self.assertEqual(child.resolve.call_count, 2)
 
1635         child.is_symlink.assert_called()
 
1636         self.assertEqual(child.is_symlink.call_count, 2)
 
1638     def test_shhh_click(self) -> None:
 
1640             from click import _unicodefun  # type: ignore
 
1641         except ModuleNotFoundError:
 
1642             self.skipTest("Incompatible Click version")
 
1643         if not hasattr(_unicodefun, "_verify_python3_env"):
 
1644             self.skipTest("Incompatible Click version")
 
1645         # First, let's see if Click is crashing with a preferred ASCII charset.
 
1646         with patch("locale.getpreferredencoding") as gpe:
 
1647             gpe.return_value = "ASCII"
 
1648             with self.assertRaises(RuntimeError):
 
1649                 _unicodefun._verify_python3_env()
 
1650         # Now, let's silence Click...
 
1652         # ...and confirm it's silent.
 
1653         with patch("locale.getpreferredencoding") as gpe:
 
1654             gpe.return_value = "ASCII"
 
1656                 _unicodefun._verify_python3_env()
 
1657             except RuntimeError as re:
 
1658                 self.fail(f"`patch_click()` failed, exception still raised: {re}")
 
1660     def test_root_logger_not_used_directly(self) -> None:
 
1661         def fail(*args: Any, **kwargs: Any) -> None:
 
1662             self.fail("Record created with root logger")
 
1664         with patch.multiple(
 
1675     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1676     def test_blackd_main(self) -> None:
 
1677         with patch("blackd.web.run_app"):
 
1678             result = CliRunner().invoke(blackd.main, [])
 
1679             if result.exception is not None:
 
1680                 raise result.exception
 
1681             self.assertEqual(result.exit_code, 0)
 
1683     def test_invalid_config_return_code(self) -> None:
 
1684         tmp_file = Path(black.dump_to_file())
 
1686             tmp_config = Path(black.dump_to_file())
 
1688             args = ["--config", str(tmp_config), str(tmp_file)]
 
1689             self.invokeBlack(args, exit_code=2, ignore_config=False)
 
1694 class BlackDTestCase(AioHTTPTestCase):
 
1695     async def get_application(self) -> web.Application:
 
1696         return blackd.make_app()
 
1698     # TODO: remove these decorators once the below is released
 
1699     # https://github.com/aio-libs/aiohttp/pull/3727
 
1700     @skip_if_exception("ClientOSError")
 
1701     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1703     async def test_blackd_request_needs_formatting(self) -> None:
 
1704         response = await self.client.post("/", data=b"print('hello world')")
 
1705         self.assertEqual(response.status, 200)
 
1706         self.assertEqual(response.charset, "utf8")
 
1707         self.assertEqual(await response.read(), b'print("hello world")\n')
 
1709     @skip_if_exception("ClientOSError")
 
1710     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1712     async def test_blackd_request_no_change(self) -> None:
 
1713         response = await self.client.post("/", data=b'print("hello world")\n')
 
1714         self.assertEqual(response.status, 204)
 
1715         self.assertEqual(await response.read(), b"")
 
1717     @skip_if_exception("ClientOSError")
 
1718     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1720     async def test_blackd_request_syntax_error(self) -> None:
 
1721         response = await self.client.post("/", data=b"what even ( is")
 
1722         self.assertEqual(response.status, 400)
 
1723         content = await response.text()
 
1725             content.startswith("Cannot parse"),
 
1726             msg=f"Expected error to start with 'Cannot parse', got {repr(content)}",
 
1729     @skip_if_exception("ClientOSError")
 
1730     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1732     async def test_blackd_unsupported_version(self) -> None:
 
1733         response = await self.client.post(
 
1734             "/", data=b"what", headers={blackd.PROTOCOL_VERSION_HEADER: "2"}
 
1736         self.assertEqual(response.status, 501)
 
1738     @skip_if_exception("ClientOSError")
 
1739     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1741     async def test_blackd_supported_version(self) -> None:
 
1742         response = await self.client.post(
 
1743             "/", data=b"what", headers={blackd.PROTOCOL_VERSION_HEADER: "1"}
 
1745         self.assertEqual(response.status, 200)
 
1747     @skip_if_exception("ClientOSError")
 
1748     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1750     async def test_blackd_invalid_python_variant(self) -> None:
 
1751         async def check(header_value: str, expected_status: int = 400) -> None:
 
1752             response = await self.client.post(
 
1753                 "/", data=b"what", headers={blackd.PYTHON_VARIANT_HEADER: header_value}
 
1755             self.assertEqual(response.status, expected_status)
 
1758         await check("ruby3.5")
 
1759         await check("pyi3.6")
 
1760         await check("py1.5")
 
1762         await check("py2.8")
 
1764         await check("pypy3.0")
 
1765         await check("jython3.4")
 
1767     @skip_if_exception("ClientOSError")
 
1768     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1770     async def test_blackd_pyi(self) -> None:
 
1771         source, expected = read_data("stub.pyi")
 
1772         response = await self.client.post(
 
1773             "/", data=source, headers={blackd.PYTHON_VARIANT_HEADER: "pyi"}
 
1775         self.assertEqual(response.status, 200)
 
1776         self.assertEqual(await response.text(), expected)
 
1778     @skip_if_exception("ClientOSError")
 
1779     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1781     async def test_blackd_diff(self) -> None:
 
1782         diff_header = re.compile(
 
1783             rf"(In|Out)\t\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d\.\d\d\d\d\d\d \+\d\d\d\d"
 
1786         source, _ = read_data("blackd_diff.py")
 
1787         expected, _ = read_data("blackd_diff.diff")
 
1789         response = await self.client.post(
 
1790             "/", data=source, headers={blackd.DIFF_HEADER: "true"}
 
1792         self.assertEqual(response.status, 200)
 
1794         actual = await response.text()
 
1795         actual = diff_header.sub(DETERMINISTIC_HEADER, actual)
 
1796         self.assertEqual(actual, expected)
 
1798     @skip_if_exception("ClientOSError")
 
1799     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1801     async def test_blackd_python_variant(self) -> None:
 
1804             "    and_has_a_bunch_of,\n"
 
1805             "    very_long_arguments_too,\n"
 
1806             "    and_lots_of_them_as_well_lol,\n"
 
1807             "    **and_very_long_keyword_arguments\n"
 
1812         async def check(header_value: str, expected_status: int) -> None:
 
1813             response = await self.client.post(
 
1814                 "/", data=code, headers={blackd.PYTHON_VARIANT_HEADER: header_value}
 
1817                 response.status, expected_status, msg=await response.text()
 
1820         await check("3.6", 200)
 
1821         await check("py3.6", 200)
 
1822         await check("3.6,3.7", 200)
 
1823         await check("3.6,py3.7", 200)
 
1824         await check("py36,py37", 200)
 
1825         await check("36", 200)
 
1826         await check("3.6.4", 200)
 
1828         await check("2", 204)
 
1829         await check("2.7", 204)
 
1830         await check("py2.7", 204)
 
1831         await check("3.4", 204)
 
1832         await check("py3.4", 204)
 
1833         await check("py34,py36", 204)
 
1834         await check("34", 204)
 
1836     @skip_if_exception("ClientOSError")
 
1837     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1839     async def test_blackd_line_length(self) -> None:
 
1840         response = await self.client.post(
 
1841             "/", data=b'print("hello")\n', headers={blackd.LINE_LENGTH_HEADER: "7"}
 
1843         self.assertEqual(response.status, 200)
 
1845     @skip_if_exception("ClientOSError")
 
1846     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1848     async def test_blackd_invalid_line_length(self) -> None:
 
1849         response = await self.client.post(
 
1850             "/", data=b'print("hello")\n', headers={blackd.LINE_LENGTH_HEADER: "NaN"}
 
1852         self.assertEqual(response.status, 400)
 
1854     @skip_if_exception("ClientOSError")
 
1855     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1857     async def test_blackd_response_black_version_header(self) -> None:
 
1858         response = await self.client.post("/")
 
1859         self.assertIsNotNone(response.headers.get(blackd.BLACK_VERSION_HEADER))
 
1862 if __name__ == "__main__":
 
1863     unittest.main(module="test_black")