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_docstring(self) -> None:
 
 395         source, expected = read_data("docstring")
 
 397         self.assertFormatEqual(expected, actual)
 
 398         black.assert_equivalent(source, actual)
 
 399         black.assert_stable(source, actual, black.FileMode())
 
 401     def test_long_strings(self) -> None:
 
 402         """Tests for splitting long strings."""
 
 403         source, expected = read_data("long_strings")
 
 405         self.assertFormatEqual(expected, actual)
 
 406         black.assert_equivalent(source, actual)
 
 407         black.assert_stable(source, actual, black.FileMode())
 
 409     @patch("black.dump_to_file", dump_to_stderr)
 
 410     def test_long_strings__edge_case(self) -> None:
 
 411         """Edge-case tests for splitting long strings."""
 
 412         source, expected = read_data("long_strings__edge_case")
 
 414         self.assertFormatEqual(expected, actual)
 
 415         black.assert_equivalent(source, actual)
 
 416         black.assert_stable(source, actual, black.FileMode())
 
 418     @patch("black.dump_to_file", dump_to_stderr)
 
 419     def test_long_strings__regression(self) -> None:
 
 420         """Regression tests for splitting long strings."""
 
 421         source, expected = read_data("long_strings__regression")
 
 423         self.assertFormatEqual(expected, actual)
 
 424         black.assert_equivalent(source, actual)
 
 425         black.assert_stable(source, actual, black.FileMode())
 
 427     @patch("black.dump_to_file", dump_to_stderr)
 
 428     def test_slices(self) -> None:
 
 429         source, expected = read_data("slices")
 
 431         self.assertFormatEqual(expected, actual)
 
 432         black.assert_equivalent(source, actual)
 
 433         black.assert_stable(source, actual, black.FileMode())
 
 435     @patch("black.dump_to_file", dump_to_stderr)
 
 436     def test_comments(self) -> None:
 
 437         source, expected = read_data("comments")
 
 439         self.assertFormatEqual(expected, actual)
 
 440         black.assert_equivalent(source, actual)
 
 441         black.assert_stable(source, actual, black.FileMode())
 
 443     @patch("black.dump_to_file", dump_to_stderr)
 
 444     def test_comments2(self) -> None:
 
 445         source, expected = read_data("comments2")
 
 447         self.assertFormatEqual(expected, actual)
 
 448         black.assert_equivalent(source, actual)
 
 449         black.assert_stable(source, actual, black.FileMode())
 
 451     @patch("black.dump_to_file", dump_to_stderr)
 
 452     def test_comments3(self) -> None:
 
 453         source, expected = read_data("comments3")
 
 455         self.assertFormatEqual(expected, actual)
 
 456         black.assert_equivalent(source, actual)
 
 457         black.assert_stable(source, actual, black.FileMode())
 
 459     @patch("black.dump_to_file", dump_to_stderr)
 
 460     def test_comments4(self) -> None:
 
 461         source, expected = read_data("comments4")
 
 463         self.assertFormatEqual(expected, actual)
 
 464         black.assert_equivalent(source, actual)
 
 465         black.assert_stable(source, actual, black.FileMode())
 
 467     @patch("black.dump_to_file", dump_to_stderr)
 
 468     def test_comments5(self) -> None:
 
 469         source, expected = read_data("comments5")
 
 471         self.assertFormatEqual(expected, actual)
 
 472         black.assert_equivalent(source, actual)
 
 473         black.assert_stable(source, actual, black.FileMode())
 
 475     @patch("black.dump_to_file", dump_to_stderr)
 
 476     def test_comments6(self) -> None:
 
 477         source, expected = read_data("comments6")
 
 479         self.assertFormatEqual(expected, actual)
 
 480         black.assert_equivalent(source, actual)
 
 481         black.assert_stable(source, actual, black.FileMode())
 
 483     @patch("black.dump_to_file", dump_to_stderr)
 
 484     def test_comments7(self) -> None:
 
 485         source, expected = read_data("comments7")
 
 487         self.assertFormatEqual(expected, actual)
 
 488         black.assert_equivalent(source, actual)
 
 489         black.assert_stable(source, actual, black.FileMode())
 
 491     @patch("black.dump_to_file", dump_to_stderr)
 
 492     def test_comment_after_escaped_newline(self) -> None:
 
 493         source, expected = read_data("comment_after_escaped_newline")
 
 495         self.assertFormatEqual(expected, actual)
 
 496         black.assert_equivalent(source, actual)
 
 497         black.assert_stable(source, actual, black.FileMode())
 
 499     @patch("black.dump_to_file", dump_to_stderr)
 
 500     def test_cantfit(self) -> None:
 
 501         source, expected = read_data("cantfit")
 
 503         self.assertFormatEqual(expected, actual)
 
 504         black.assert_equivalent(source, actual)
 
 505         black.assert_stable(source, actual, black.FileMode())
 
 507     @patch("black.dump_to_file", dump_to_stderr)
 
 508     def test_import_spacing(self) -> None:
 
 509         source, expected = read_data("import_spacing")
 
 511         self.assertFormatEqual(expected, actual)
 
 512         black.assert_equivalent(source, actual)
 
 513         black.assert_stable(source, actual, black.FileMode())
 
 515     @patch("black.dump_to_file", dump_to_stderr)
 
 516     def test_composition(self) -> None:
 
 517         source, expected = read_data("composition")
 
 519         self.assertFormatEqual(expected, actual)
 
 520         black.assert_equivalent(source, actual)
 
 521         black.assert_stable(source, actual, black.FileMode())
 
 523     @patch("black.dump_to_file", dump_to_stderr)
 
 524     def test_empty_lines(self) -> None:
 
 525         source, expected = read_data("empty_lines")
 
 527         self.assertFormatEqual(expected, actual)
 
 528         black.assert_equivalent(source, actual)
 
 529         black.assert_stable(source, actual, black.FileMode())
 
 531     @patch("black.dump_to_file", dump_to_stderr)
 
 532     def test_remove_parens(self) -> None:
 
 533         source, expected = read_data("remove_parens")
 
 535         self.assertFormatEqual(expected, actual)
 
 536         black.assert_equivalent(source, actual)
 
 537         black.assert_stable(source, actual, black.FileMode())
 
 539     @patch("black.dump_to_file", dump_to_stderr)
 
 540     def test_string_prefixes(self) -> None:
 
 541         source, expected = read_data("string_prefixes")
 
 543         self.assertFormatEqual(expected, actual)
 
 544         black.assert_equivalent(source, actual)
 
 545         black.assert_stable(source, actual, black.FileMode())
 
 547     @patch("black.dump_to_file", dump_to_stderr)
 
 548     def test_numeric_literals(self) -> None:
 
 549         source, expected = read_data("numeric_literals")
 
 550         mode = black.FileMode(target_versions=black.PY36_VERSIONS)
 
 551         actual = fs(source, mode=mode)
 
 552         self.assertFormatEqual(expected, actual)
 
 553         black.assert_equivalent(source, actual)
 
 554         black.assert_stable(source, actual, mode)
 
 556     @patch("black.dump_to_file", dump_to_stderr)
 
 557     def test_numeric_literals_ignoring_underscores(self) -> None:
 
 558         source, expected = read_data("numeric_literals_skip_underscores")
 
 559         mode = black.FileMode(target_versions=black.PY36_VERSIONS)
 
 560         actual = fs(source, mode=mode)
 
 561         self.assertFormatEqual(expected, actual)
 
 562         black.assert_equivalent(source, actual)
 
 563         black.assert_stable(source, actual, mode)
 
 565     @patch("black.dump_to_file", dump_to_stderr)
 
 566     def test_numeric_literals_py2(self) -> None:
 
 567         source, expected = read_data("numeric_literals_py2")
 
 569         self.assertFormatEqual(expected, actual)
 
 570         black.assert_stable(source, actual, black.FileMode())
 
 572     @patch("black.dump_to_file", dump_to_stderr)
 
 573     def test_python2(self) -> None:
 
 574         source, expected = read_data("python2")
 
 576         self.assertFormatEqual(expected, actual)
 
 577         black.assert_equivalent(source, actual)
 
 578         black.assert_stable(source, actual, black.FileMode())
 
 580     @patch("black.dump_to_file", dump_to_stderr)
 
 581     def test_python2_print_function(self) -> None:
 
 582         source, expected = read_data("python2_print_function")
 
 583         mode = black.FileMode(target_versions={TargetVersion.PY27})
 
 584         actual = fs(source, mode=mode)
 
 585         self.assertFormatEqual(expected, actual)
 
 586         black.assert_equivalent(source, actual)
 
 587         black.assert_stable(source, actual, mode)
 
 589     @patch("black.dump_to_file", dump_to_stderr)
 
 590     def test_python2_unicode_literals(self) -> None:
 
 591         source, expected = read_data("python2_unicode_literals")
 
 593         self.assertFormatEqual(expected, actual)
 
 594         black.assert_equivalent(source, actual)
 
 595         black.assert_stable(source, actual, black.FileMode())
 
 597     @patch("black.dump_to_file", dump_to_stderr)
 
 598     def test_stub(self) -> None:
 
 599         mode = black.FileMode(is_pyi=True)
 
 600         source, expected = read_data("stub.pyi")
 
 601         actual = fs(source, mode=mode)
 
 602         self.assertFormatEqual(expected, actual)
 
 603         black.assert_stable(source, actual, mode)
 
 605     @patch("black.dump_to_file", dump_to_stderr)
 
 606     def test_async_as_identifier(self) -> None:
 
 607         source_path = (THIS_DIR / "data" / "async_as_identifier.py").resolve()
 
 608         source, expected = read_data("async_as_identifier")
 
 610         self.assertFormatEqual(expected, actual)
 
 611         major, minor = sys.version_info[:2]
 
 612         if major < 3 or (major <= 3 and minor < 7):
 
 613             black.assert_equivalent(source, actual)
 
 614         black.assert_stable(source, actual, black.FileMode())
 
 615         # ensure black can parse this when the target is 3.6
 
 616         self.invokeBlack([str(source_path), "--target-version", "py36"])
 
 617         # but not on 3.7, because async/await is no longer an identifier
 
 618         self.invokeBlack([str(source_path), "--target-version", "py37"], exit_code=123)
 
 620     @patch("black.dump_to_file", dump_to_stderr)
 
 621     def test_python37(self) -> None:
 
 622         source_path = (THIS_DIR / "data" / "python37.py").resolve()
 
 623         source, expected = read_data("python37")
 
 625         self.assertFormatEqual(expected, actual)
 
 626         major, minor = sys.version_info[:2]
 
 627         if major > 3 or (major == 3 and minor >= 7):
 
 628             black.assert_equivalent(source, actual)
 
 629         black.assert_stable(source, actual, black.FileMode())
 
 630         # ensure black can parse this when the target is 3.7
 
 631         self.invokeBlack([str(source_path), "--target-version", "py37"])
 
 632         # but not on 3.6, because we use async as a reserved keyword
 
 633         self.invokeBlack([str(source_path), "--target-version", "py36"], exit_code=123)
 
 635     @patch("black.dump_to_file", dump_to_stderr)
 
 636     def test_python38(self) -> None:
 
 637         source, expected = read_data("python38")
 
 639         self.assertFormatEqual(expected, actual)
 
 640         major, minor = sys.version_info[:2]
 
 641         if major > 3 or (major == 3 and minor >= 8):
 
 642             black.assert_equivalent(source, actual)
 
 643         black.assert_stable(source, actual, black.FileMode())
 
 645     @patch("black.dump_to_file", dump_to_stderr)
 
 646     def test_fmtonoff(self) -> None:
 
 647         source, expected = read_data("fmtonoff")
 
 649         self.assertFormatEqual(expected, actual)
 
 650         black.assert_equivalent(source, actual)
 
 651         black.assert_stable(source, actual, black.FileMode())
 
 653     @patch("black.dump_to_file", dump_to_stderr)
 
 654     def test_fmtonoff2(self) -> None:
 
 655         source, expected = read_data("fmtonoff2")
 
 657         self.assertFormatEqual(expected, actual)
 
 658         black.assert_equivalent(source, actual)
 
 659         black.assert_stable(source, actual, black.FileMode())
 
 661     @patch("black.dump_to_file", dump_to_stderr)
 
 662     def test_fmtonoff3(self) -> None:
 
 663         source, expected = read_data("fmtonoff3")
 
 665         self.assertFormatEqual(expected, actual)
 
 666         black.assert_equivalent(source, actual)
 
 667         black.assert_stable(source, actual, black.FileMode())
 
 669     @patch("black.dump_to_file", dump_to_stderr)
 
 670     def test_fmtonoff4(self) -> None:
 
 671         source, expected = read_data("fmtonoff4")
 
 673         self.assertFormatEqual(expected, actual)
 
 674         black.assert_equivalent(source, actual)
 
 675         black.assert_stable(source, actual, black.FileMode())
 
 677     @patch("black.dump_to_file", dump_to_stderr)
 
 678     def test_remove_empty_parentheses_after_class(self) -> None:
 
 679         source, expected = read_data("class_blank_parentheses")
 
 681         self.assertFormatEqual(expected, actual)
 
 682         black.assert_equivalent(source, actual)
 
 683         black.assert_stable(source, actual, black.FileMode())
 
 685     @patch("black.dump_to_file", dump_to_stderr)
 
 686     def test_new_line_between_class_and_code(self) -> None:
 
 687         source, expected = read_data("class_methods_new_line")
 
 689         self.assertFormatEqual(expected, actual)
 
 690         black.assert_equivalent(source, actual)
 
 691         black.assert_stable(source, actual, black.FileMode())
 
 693     @patch("black.dump_to_file", dump_to_stderr)
 
 694     def test_bracket_match(self) -> None:
 
 695         source, expected = read_data("bracketmatch")
 
 697         self.assertFormatEqual(expected, actual)
 
 698         black.assert_equivalent(source, actual)
 
 699         black.assert_stable(source, actual, black.FileMode())
 
 701     @patch("black.dump_to_file", dump_to_stderr)
 
 702     def test_tuple_assign(self) -> None:
 
 703         source, expected = read_data("tupleassign")
 
 705         self.assertFormatEqual(expected, actual)
 
 706         black.assert_equivalent(source, actual)
 
 707         black.assert_stable(source, actual, black.FileMode())
 
 709     @patch("black.dump_to_file", dump_to_stderr)
 
 710     def test_beginning_backslash(self) -> None:
 
 711         source, expected = read_data("beginning_backslash")
 
 713         self.assertFormatEqual(expected, actual)
 
 714         black.assert_equivalent(source, actual)
 
 715         black.assert_stable(source, actual, black.FileMode())
 
 717     def test_tab_comment_indentation(self) -> None:
 
 718         contents_tab = "if 1:\n\tif 2:\n\t\tpass\n\t# comment\n\tpass\n"
 
 719         contents_spc = "if 1:\n    if 2:\n        pass\n    # comment\n    pass\n"
 
 720         self.assertFormatEqual(contents_spc, fs(contents_spc))
 
 721         self.assertFormatEqual(contents_spc, fs(contents_tab))
 
 723         contents_tab = "if 1:\n\tif 2:\n\t\tpass\n\t\t# comment\n\tpass\n"
 
 724         contents_spc = "if 1:\n    if 2:\n        pass\n        # comment\n    pass\n"
 
 725         self.assertFormatEqual(contents_spc, fs(contents_spc))
 
 726         self.assertFormatEqual(contents_spc, fs(contents_tab))
 
 728         # mixed tabs and spaces (valid Python 2 code)
 
 729         contents_tab = "if 1:\n        if 2:\n\t\tpass\n\t# comment\n        pass\n"
 
 730         contents_spc = "if 1:\n    if 2:\n        pass\n    # comment\n    pass\n"
 
 731         self.assertFormatEqual(contents_spc, fs(contents_spc))
 
 732         self.assertFormatEqual(contents_spc, fs(contents_tab))
 
 734         contents_tab = "if 1:\n        if 2:\n\t\tpass\n\t\t# comment\n        pass\n"
 
 735         contents_spc = "if 1:\n    if 2:\n        pass\n        # comment\n    pass\n"
 
 736         self.assertFormatEqual(contents_spc, fs(contents_spc))
 
 737         self.assertFormatEqual(contents_spc, fs(contents_tab))
 
 739     def test_report_verbose(self) -> None:
 
 740         report = black.Report(verbose=True)
 
 744         def out(msg: str, **kwargs: Any) -> None:
 
 745             out_lines.append(msg)
 
 747         def err(msg: str, **kwargs: Any) -> None:
 
 748             err_lines.append(msg)
 
 750         with patch("black.out", out), patch("black.err", err):
 
 751             report.done(Path("f1"), black.Changed.NO)
 
 752             self.assertEqual(len(out_lines), 1)
 
 753             self.assertEqual(len(err_lines), 0)
 
 754             self.assertEqual(out_lines[-1], "f1 already well formatted, good job.")
 
 755             self.assertEqual(unstyle(str(report)), "1 file left unchanged.")
 
 756             self.assertEqual(report.return_code, 0)
 
 757             report.done(Path("f2"), black.Changed.YES)
 
 758             self.assertEqual(len(out_lines), 2)
 
 759             self.assertEqual(len(err_lines), 0)
 
 760             self.assertEqual(out_lines[-1], "reformatted f2")
 
 762                 unstyle(str(report)), "1 file reformatted, 1 file left unchanged."
 
 764             report.done(Path("f3"), black.Changed.CACHED)
 
 765             self.assertEqual(len(out_lines), 3)
 
 766             self.assertEqual(len(err_lines), 0)
 
 768                 out_lines[-1], "f3 wasn't modified on disk since last run."
 
 771                 unstyle(str(report)), "1 file reformatted, 2 files left unchanged."
 
 773             self.assertEqual(report.return_code, 0)
 
 775             self.assertEqual(report.return_code, 1)
 
 777             report.failed(Path("e1"), "boom")
 
 778             self.assertEqual(len(out_lines), 3)
 
 779             self.assertEqual(len(err_lines), 1)
 
 780             self.assertEqual(err_lines[-1], "error: cannot format e1: boom")
 
 782                 unstyle(str(report)),
 
 783                 "1 file reformatted, 2 files left unchanged, 1 file failed to"
 
 786             self.assertEqual(report.return_code, 123)
 
 787             report.done(Path("f3"), black.Changed.YES)
 
 788             self.assertEqual(len(out_lines), 4)
 
 789             self.assertEqual(len(err_lines), 1)
 
 790             self.assertEqual(out_lines[-1], "reformatted f3")
 
 792                 unstyle(str(report)),
 
 793                 "2 files reformatted, 2 files left unchanged, 1 file failed to"
 
 796             self.assertEqual(report.return_code, 123)
 
 797             report.failed(Path("e2"), "boom")
 
 798             self.assertEqual(len(out_lines), 4)
 
 799             self.assertEqual(len(err_lines), 2)
 
 800             self.assertEqual(err_lines[-1], "error: cannot format e2: boom")
 
 802                 unstyle(str(report)),
 
 803                 "2 files reformatted, 2 files left unchanged, 2 files failed to"
 
 806             self.assertEqual(report.return_code, 123)
 
 807             report.path_ignored(Path("wat"), "no match")
 
 808             self.assertEqual(len(out_lines), 5)
 
 809             self.assertEqual(len(err_lines), 2)
 
 810             self.assertEqual(out_lines[-1], "wat ignored: no match")
 
 812                 unstyle(str(report)),
 
 813                 "2 files reformatted, 2 files left unchanged, 2 files failed to"
 
 816             self.assertEqual(report.return_code, 123)
 
 817             report.done(Path("f4"), black.Changed.NO)
 
 818             self.assertEqual(len(out_lines), 6)
 
 819             self.assertEqual(len(err_lines), 2)
 
 820             self.assertEqual(out_lines[-1], "f4 already well formatted, good job.")
 
 822                 unstyle(str(report)),
 
 823                 "2 files reformatted, 3 files left unchanged, 2 files failed to"
 
 826             self.assertEqual(report.return_code, 123)
 
 829                 unstyle(str(report)),
 
 830                 "2 files would be reformatted, 3 files would be left unchanged, 2 files"
 
 831                 " would fail to reformat.",
 
 836                 unstyle(str(report)),
 
 837                 "2 files would be reformatted, 3 files would be left unchanged, 2 files"
 
 838                 " would fail to reformat.",
 
 841     def test_report_quiet(self) -> None:
 
 842         report = black.Report(quiet=True)
 
 846         def out(msg: str, **kwargs: Any) -> None:
 
 847             out_lines.append(msg)
 
 849         def err(msg: str, **kwargs: Any) -> None:
 
 850             err_lines.append(msg)
 
 852         with patch("black.out", out), patch("black.err", err):
 
 853             report.done(Path("f1"), black.Changed.NO)
 
 854             self.assertEqual(len(out_lines), 0)
 
 855             self.assertEqual(len(err_lines), 0)
 
 856             self.assertEqual(unstyle(str(report)), "1 file left unchanged.")
 
 857             self.assertEqual(report.return_code, 0)
 
 858             report.done(Path("f2"), black.Changed.YES)
 
 859             self.assertEqual(len(out_lines), 0)
 
 860             self.assertEqual(len(err_lines), 0)
 
 862                 unstyle(str(report)), "1 file reformatted, 1 file left unchanged."
 
 864             report.done(Path("f3"), black.Changed.CACHED)
 
 865             self.assertEqual(len(out_lines), 0)
 
 866             self.assertEqual(len(err_lines), 0)
 
 868                 unstyle(str(report)), "1 file reformatted, 2 files left unchanged."
 
 870             self.assertEqual(report.return_code, 0)
 
 872             self.assertEqual(report.return_code, 1)
 
 874             report.failed(Path("e1"), "boom")
 
 875             self.assertEqual(len(out_lines), 0)
 
 876             self.assertEqual(len(err_lines), 1)
 
 877             self.assertEqual(err_lines[-1], "error: cannot format e1: boom")
 
 879                 unstyle(str(report)),
 
 880                 "1 file reformatted, 2 files left unchanged, 1 file failed to"
 
 883             self.assertEqual(report.return_code, 123)
 
 884             report.done(Path("f3"), black.Changed.YES)
 
 885             self.assertEqual(len(out_lines), 0)
 
 886             self.assertEqual(len(err_lines), 1)
 
 888                 unstyle(str(report)),
 
 889                 "2 files reformatted, 2 files left unchanged, 1 file failed to"
 
 892             self.assertEqual(report.return_code, 123)
 
 893             report.failed(Path("e2"), "boom")
 
 894             self.assertEqual(len(out_lines), 0)
 
 895             self.assertEqual(len(err_lines), 2)
 
 896             self.assertEqual(err_lines[-1], "error: cannot format e2: boom")
 
 898                 unstyle(str(report)),
 
 899                 "2 files reformatted, 2 files left unchanged, 2 files failed to"
 
 902             self.assertEqual(report.return_code, 123)
 
 903             report.path_ignored(Path("wat"), "no match")
 
 904             self.assertEqual(len(out_lines), 0)
 
 905             self.assertEqual(len(err_lines), 2)
 
 907                 unstyle(str(report)),
 
 908                 "2 files reformatted, 2 files left unchanged, 2 files failed to"
 
 911             self.assertEqual(report.return_code, 123)
 
 912             report.done(Path("f4"), black.Changed.NO)
 
 913             self.assertEqual(len(out_lines), 0)
 
 914             self.assertEqual(len(err_lines), 2)
 
 916                 unstyle(str(report)),
 
 917                 "2 files reformatted, 3 files left unchanged, 2 files failed to"
 
 920             self.assertEqual(report.return_code, 123)
 
 923                 unstyle(str(report)),
 
 924                 "2 files would be reformatted, 3 files would be left unchanged, 2 files"
 
 925                 " would fail to reformat.",
 
 930                 unstyle(str(report)),
 
 931                 "2 files would be reformatted, 3 files would be left unchanged, 2 files"
 
 932                 " would fail to reformat.",
 
 935     def test_report_normal(self) -> None:
 
 936         report = black.Report()
 
 940         def out(msg: str, **kwargs: Any) -> None:
 
 941             out_lines.append(msg)
 
 943         def err(msg: str, **kwargs: Any) -> None:
 
 944             err_lines.append(msg)
 
 946         with patch("black.out", out), patch("black.err", err):
 
 947             report.done(Path("f1"), black.Changed.NO)
 
 948             self.assertEqual(len(out_lines), 0)
 
 949             self.assertEqual(len(err_lines), 0)
 
 950             self.assertEqual(unstyle(str(report)), "1 file left unchanged.")
 
 951             self.assertEqual(report.return_code, 0)
 
 952             report.done(Path("f2"), black.Changed.YES)
 
 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, 1 file left unchanged."
 
 959             report.done(Path("f3"), black.Changed.CACHED)
 
 960             self.assertEqual(len(out_lines), 1)
 
 961             self.assertEqual(len(err_lines), 0)
 
 962             self.assertEqual(out_lines[-1], "reformatted f2")
 
 964                 unstyle(str(report)), "1 file reformatted, 2 files left unchanged."
 
 966             self.assertEqual(report.return_code, 0)
 
 968             self.assertEqual(report.return_code, 1)
 
 970             report.failed(Path("e1"), "boom")
 
 971             self.assertEqual(len(out_lines), 1)
 
 972             self.assertEqual(len(err_lines), 1)
 
 973             self.assertEqual(err_lines[-1], "error: cannot format e1: boom")
 
 975                 unstyle(str(report)),
 
 976                 "1 file reformatted, 2 files left unchanged, 1 file failed to"
 
 979             self.assertEqual(report.return_code, 123)
 
 980             report.done(Path("f3"), black.Changed.YES)
 
 981             self.assertEqual(len(out_lines), 2)
 
 982             self.assertEqual(len(err_lines), 1)
 
 983             self.assertEqual(out_lines[-1], "reformatted f3")
 
 985                 unstyle(str(report)),
 
 986                 "2 files reformatted, 2 files left unchanged, 1 file failed to"
 
 989             self.assertEqual(report.return_code, 123)
 
 990             report.failed(Path("e2"), "boom")
 
 991             self.assertEqual(len(out_lines), 2)
 
 992             self.assertEqual(len(err_lines), 2)
 
 993             self.assertEqual(err_lines[-1], "error: cannot format e2: boom")
 
 995                 unstyle(str(report)),
 
 996                 "2 files reformatted, 2 files left unchanged, 2 files failed to"
 
 999             self.assertEqual(report.return_code, 123)
 
1000             report.path_ignored(Path("wat"), "no match")
 
1001             self.assertEqual(len(out_lines), 2)
 
1002             self.assertEqual(len(err_lines), 2)
 
1004                 unstyle(str(report)),
 
1005                 "2 files reformatted, 2 files left unchanged, 2 files failed to"
 
1008             self.assertEqual(report.return_code, 123)
 
1009             report.done(Path("f4"), black.Changed.NO)
 
1010             self.assertEqual(len(out_lines), 2)
 
1011             self.assertEqual(len(err_lines), 2)
 
1013                 unstyle(str(report)),
 
1014                 "2 files reformatted, 3 files left unchanged, 2 files failed to"
 
1017             self.assertEqual(report.return_code, 123)
 
1020                 unstyle(str(report)),
 
1021                 "2 files would be reformatted, 3 files would be left unchanged, 2 files"
 
1022                 " would fail to reformat.",
 
1024             report.check = False
 
1027                 unstyle(str(report)),
 
1028                 "2 files would be reformatted, 3 files would be left unchanged, 2 files"
 
1029                 " would fail to reformat.",
 
1032     def test_lib2to3_parse(self) -> None:
 
1033         with self.assertRaises(black.InvalidInput):
 
1034             black.lib2to3_parse("invalid syntax")
 
1036         straddling = "x + y"
 
1037         black.lib2to3_parse(straddling)
 
1038         black.lib2to3_parse(straddling, {TargetVersion.PY27})
 
1039         black.lib2to3_parse(straddling, {TargetVersion.PY36})
 
1040         black.lib2to3_parse(straddling, {TargetVersion.PY27, TargetVersion.PY36})
 
1042         py2_only = "print x"
 
1043         black.lib2to3_parse(py2_only)
 
1044         black.lib2to3_parse(py2_only, {TargetVersion.PY27})
 
1045         with self.assertRaises(black.InvalidInput):
 
1046             black.lib2to3_parse(py2_only, {TargetVersion.PY36})
 
1047         with self.assertRaises(black.InvalidInput):
 
1048             black.lib2to3_parse(py2_only, {TargetVersion.PY27, TargetVersion.PY36})
 
1050         py3_only = "exec(x, end=y)"
 
1051         black.lib2to3_parse(py3_only)
 
1052         with self.assertRaises(black.InvalidInput):
 
1053             black.lib2to3_parse(py3_only, {TargetVersion.PY27})
 
1054         black.lib2to3_parse(py3_only, {TargetVersion.PY36})
 
1055         black.lib2to3_parse(py3_only, {TargetVersion.PY27, TargetVersion.PY36})
 
1057     def test_get_features_used(self) -> None:
 
1058         node = black.lib2to3_parse("def f(*, arg): ...\n")
 
1059         self.assertEqual(black.get_features_used(node), set())
 
1060         node = black.lib2to3_parse("def f(*, arg,): ...\n")
 
1061         self.assertEqual(black.get_features_used(node), {Feature.TRAILING_COMMA_IN_DEF})
 
1062         node = black.lib2to3_parse("f(*arg,)\n")
 
1064             black.get_features_used(node), {Feature.TRAILING_COMMA_IN_CALL}
 
1066         node = black.lib2to3_parse("def f(*, arg): f'string'\n")
 
1067         self.assertEqual(black.get_features_used(node), {Feature.F_STRINGS})
 
1068         node = black.lib2to3_parse("123_456\n")
 
1069         self.assertEqual(black.get_features_used(node), {Feature.NUMERIC_UNDERSCORES})
 
1070         node = black.lib2to3_parse("123456\n")
 
1071         self.assertEqual(black.get_features_used(node), set())
 
1072         source, expected = read_data("function")
 
1073         node = black.lib2to3_parse(source)
 
1074         expected_features = {
 
1075             Feature.TRAILING_COMMA_IN_CALL,
 
1076             Feature.TRAILING_COMMA_IN_DEF,
 
1079         self.assertEqual(black.get_features_used(node), expected_features)
 
1080         node = black.lib2to3_parse(expected)
 
1081         self.assertEqual(black.get_features_used(node), expected_features)
 
1082         source, expected = read_data("expression")
 
1083         node = black.lib2to3_parse(source)
 
1084         self.assertEqual(black.get_features_used(node), set())
 
1085         node = black.lib2to3_parse(expected)
 
1086         self.assertEqual(black.get_features_used(node), set())
 
1088     def test_get_future_imports(self) -> None:
 
1089         node = black.lib2to3_parse("\n")
 
1090         self.assertEqual(set(), black.get_future_imports(node))
 
1091         node = black.lib2to3_parse("from __future__ import black\n")
 
1092         self.assertEqual({"black"}, black.get_future_imports(node))
 
1093         node = black.lib2to3_parse("from __future__ import multiple, imports\n")
 
1094         self.assertEqual({"multiple", "imports"}, black.get_future_imports(node))
 
1095         node = black.lib2to3_parse("from __future__ import (parenthesized, imports)\n")
 
1096         self.assertEqual({"parenthesized", "imports"}, black.get_future_imports(node))
 
1097         node = black.lib2to3_parse(
 
1098             "from __future__ import multiple\nfrom __future__ import imports\n"
 
1100         self.assertEqual({"multiple", "imports"}, black.get_future_imports(node))
 
1101         node = black.lib2to3_parse("# comment\nfrom __future__ import black\n")
 
1102         self.assertEqual({"black"}, black.get_future_imports(node))
 
1103         node = black.lib2to3_parse('"""docstring"""\nfrom __future__ import black\n')
 
1104         self.assertEqual({"black"}, black.get_future_imports(node))
 
1105         node = black.lib2to3_parse("some(other, code)\nfrom __future__ import black\n")
 
1106         self.assertEqual(set(), black.get_future_imports(node))
 
1107         node = black.lib2to3_parse("from some.module import black\n")
 
1108         self.assertEqual(set(), black.get_future_imports(node))
 
1109         node = black.lib2to3_parse(
 
1110             "from __future__ import unicode_literals as _unicode_literals"
 
1112         self.assertEqual({"unicode_literals"}, black.get_future_imports(node))
 
1113         node = black.lib2to3_parse(
 
1114             "from __future__ import unicode_literals as _lol, print"
 
1116         self.assertEqual({"unicode_literals", "print"}, black.get_future_imports(node))
 
1118     def test_debug_visitor(self) -> None:
 
1119         source, _ = read_data("debug_visitor.py")
 
1120         expected, _ = read_data("debug_visitor.out")
 
1124         def out(msg: str, **kwargs: Any) -> None:
 
1125             out_lines.append(msg)
 
1127         def err(msg: str, **kwargs: Any) -> None:
 
1128             err_lines.append(msg)
 
1130         with patch("black.out", out), patch("black.err", err):
 
1131             black.DebugVisitor.show(source)
 
1132         actual = "\n".join(out_lines) + "\n"
 
1134         if expected != actual:
 
1135             log_name = black.dump_to_file(*out_lines)
 
1139             f"AST print out is different. Actual version dumped to {log_name}",
 
1142     def test_format_file_contents(self) -> None:
 
1144         mode = black.FileMode()
 
1145         with self.assertRaises(black.NothingChanged):
 
1146             black.format_file_contents(empty, mode=mode, fast=False)
 
1148         with self.assertRaises(black.NothingChanged):
 
1149             black.format_file_contents(just_nl, mode=mode, fast=False)
 
1150         same = "j = [1, 2, 3]\n"
 
1151         with self.assertRaises(black.NothingChanged):
 
1152             black.format_file_contents(same, mode=mode, fast=False)
 
1153         different = "j = [1,2,3]"
 
1155         actual = black.format_file_contents(different, mode=mode, fast=False)
 
1156         self.assertEqual(expected, actual)
 
1157         invalid = "return if you can"
 
1158         with self.assertRaises(black.InvalidInput) as e:
 
1159             black.format_file_contents(invalid, mode=mode, fast=False)
 
1160         self.assertEqual(str(e.exception), "Cannot parse: 1:7: return if you can")
 
1162     def test_endmarker(self) -> None:
 
1163         n = black.lib2to3_parse("\n")
 
1164         self.assertEqual(n.type, black.syms.file_input)
 
1165         self.assertEqual(len(n.children), 1)
 
1166         self.assertEqual(n.children[0].type, black.token.ENDMARKER)
 
1168     @unittest.skipIf(os.environ.get("SKIP_AST_PRINT"), "user set SKIP_AST_PRINT")
 
1169     def test_assertFormatEqual(self) -> None:
 
1173         def out(msg: str, **kwargs: Any) -> None:
 
1174             out_lines.append(msg)
 
1176         def err(msg: str, **kwargs: Any) -> None:
 
1177             err_lines.append(msg)
 
1179         with patch("black.out", out), patch("black.err", err):
 
1180             with self.assertRaises(AssertionError):
 
1181                 self.assertFormatEqual("j = [1, 2, 3]", "j = [1, 2, 3,]")
 
1183         out_str = "".join(out_lines)
 
1184         self.assertTrue("Expected tree:" in out_str)
 
1185         self.assertTrue("Actual tree:" in out_str)
 
1186         self.assertEqual("".join(err_lines), "")
 
1188     def test_cache_broken_file(self) -> None:
 
1189         mode = black.FileMode()
 
1190         with cache_dir() as workspace:
 
1191             cache_file = black.get_cache_file(mode)
 
1192             with cache_file.open("w") as fobj:
 
1193                 fobj.write("this is not a pickle")
 
1194             self.assertEqual(black.read_cache(mode), {})
 
1195             src = (workspace / "test.py").resolve()
 
1196             with src.open("w") as fobj:
 
1197                 fobj.write("print('hello')")
 
1198             self.invokeBlack([str(src)])
 
1199             cache = black.read_cache(mode)
 
1200             self.assertIn(src, cache)
 
1202     def test_cache_single_file_already_cached(self) -> None:
 
1203         mode = black.FileMode()
 
1204         with cache_dir() as workspace:
 
1205             src = (workspace / "test.py").resolve()
 
1206             with src.open("w") as fobj:
 
1207                 fobj.write("print('hello')")
 
1208             black.write_cache({}, [src], mode)
 
1209             self.invokeBlack([str(src)])
 
1210             with src.open("r") as fobj:
 
1211                 self.assertEqual(fobj.read(), "print('hello')")
 
1213     @event_loop(close=False)
 
1214     def test_cache_multiple_files(self) -> None:
 
1215         mode = black.FileMode()
 
1216         with cache_dir() as workspace, patch(
 
1217             "black.ProcessPoolExecutor", new=ThreadPoolExecutor
 
1219             one = (workspace / "one.py").resolve()
 
1220             with one.open("w") as fobj:
 
1221                 fobj.write("print('hello')")
 
1222             two = (workspace / "two.py").resolve()
 
1223             with two.open("w") as fobj:
 
1224                 fobj.write("print('hello')")
 
1225             black.write_cache({}, [one], mode)
 
1226             self.invokeBlack([str(workspace)])
 
1227             with one.open("r") as fobj:
 
1228                 self.assertEqual(fobj.read(), "print('hello')")
 
1229             with two.open("r") as fobj:
 
1230                 self.assertEqual(fobj.read(), 'print("hello")\n')
 
1231             cache = black.read_cache(mode)
 
1232             self.assertIn(one, cache)
 
1233             self.assertIn(two, cache)
 
1235     def test_no_cache_when_writeback_diff(self) -> None:
 
1236         mode = black.FileMode()
 
1237         with cache_dir() as workspace:
 
1238             src = (workspace / "test.py").resolve()
 
1239             with src.open("w") as fobj:
 
1240                 fobj.write("print('hello')")
 
1241             self.invokeBlack([str(src), "--diff"])
 
1242             cache_file = black.get_cache_file(mode)
 
1243             self.assertFalse(cache_file.exists())
 
1245     def test_no_cache_when_stdin(self) -> None:
 
1246         mode = black.FileMode()
 
1248             result = CliRunner().invoke(
 
1249                 black.main, ["-"], input=BytesIO(b"print('hello')")
 
1251             self.assertEqual(result.exit_code, 0)
 
1252             cache_file = black.get_cache_file(mode)
 
1253             self.assertFalse(cache_file.exists())
 
1255     def test_read_cache_no_cachefile(self) -> None:
 
1256         mode = black.FileMode()
 
1258             self.assertEqual(black.read_cache(mode), {})
 
1260     def test_write_cache_read_cache(self) -> None:
 
1261         mode = black.FileMode()
 
1262         with cache_dir() as workspace:
 
1263             src = (workspace / "test.py").resolve()
 
1265             black.write_cache({}, [src], mode)
 
1266             cache = black.read_cache(mode)
 
1267             self.assertIn(src, cache)
 
1268             self.assertEqual(cache[src], black.get_cache_info(src))
 
1270     def test_filter_cached(self) -> None:
 
1271         with TemporaryDirectory() as workspace:
 
1272             path = Path(workspace)
 
1273             uncached = (path / "uncached").resolve()
 
1274             cached = (path / "cached").resolve()
 
1275             cached_but_changed = (path / "changed").resolve()
 
1278             cached_but_changed.touch()
 
1279             cache = {cached: black.get_cache_info(cached), cached_but_changed: (0.0, 0)}
 
1280             todo, done = black.filter_cached(
 
1281                 cache, {uncached, cached, cached_but_changed}
 
1283             self.assertEqual(todo, {uncached, cached_but_changed})
 
1284             self.assertEqual(done, {cached})
 
1286     def test_write_cache_creates_directory_if_needed(self) -> None:
 
1287         mode = black.FileMode()
 
1288         with cache_dir(exists=False) as workspace:
 
1289             self.assertFalse(workspace.exists())
 
1290             black.write_cache({}, [], mode)
 
1291             self.assertTrue(workspace.exists())
 
1293     @event_loop(close=False)
 
1294     def test_failed_formatting_does_not_get_cached(self) -> None:
 
1295         mode = black.FileMode()
 
1296         with cache_dir() as workspace, patch(
 
1297             "black.ProcessPoolExecutor", new=ThreadPoolExecutor
 
1299             failing = (workspace / "failing.py").resolve()
 
1300             with failing.open("w") as fobj:
 
1301                 fobj.write("not actually python")
 
1302             clean = (workspace / "clean.py").resolve()
 
1303             with clean.open("w") as fobj:
 
1304                 fobj.write('print("hello")\n')
 
1305             self.invokeBlack([str(workspace)], exit_code=123)
 
1306             cache = black.read_cache(mode)
 
1307             self.assertNotIn(failing, cache)
 
1308             self.assertIn(clean, cache)
 
1310     def test_write_cache_write_fail(self) -> None:
 
1311         mode = black.FileMode()
 
1312         with cache_dir(), patch.object(Path, "open") as mock:
 
1313             mock.side_effect = OSError
 
1314             black.write_cache({}, [], mode)
 
1316     @event_loop(close=False)
 
1317     def test_check_diff_use_together(self) -> None:
 
1319             # Files which will be reformatted.
 
1320             src1 = (THIS_DIR / "data" / "string_quotes.py").resolve()
 
1321             self.invokeBlack([str(src1), "--diff", "--check"], exit_code=1)
 
1322             # Files which will not be reformatted.
 
1323             src2 = (THIS_DIR / "data" / "composition.py").resolve()
 
1324             self.invokeBlack([str(src2), "--diff", "--check"])
 
1325             # Multi file command.
 
1326             self.invokeBlack([str(src1), str(src2), "--diff", "--check"], exit_code=1)
 
1328     def test_no_files(self) -> None:
 
1330             # Without an argument, black exits with error code 0.
 
1331             self.invokeBlack([])
 
1333     def test_broken_symlink(self) -> None:
 
1334         with cache_dir() as workspace:
 
1335             symlink = workspace / "broken_link.py"
 
1337                 symlink.symlink_to("nonexistent.py")
 
1338             except OSError as e:
 
1339                 self.skipTest(f"Can't create symlinks: {e}")
 
1340             self.invokeBlack([str(workspace.resolve())])
 
1342     def test_read_cache_line_lengths(self) -> None:
 
1343         mode = black.FileMode()
 
1344         short_mode = black.FileMode(line_length=1)
 
1345         with cache_dir() as workspace:
 
1346             path = (workspace / "file.py").resolve()
 
1348             black.write_cache({}, [path], mode)
 
1349             one = black.read_cache(mode)
 
1350             self.assertIn(path, one)
 
1351             two = black.read_cache(short_mode)
 
1352             self.assertNotIn(path, two)
 
1354     def test_tricky_unicode_symbols(self) -> None:
 
1355         source, expected = read_data("tricky_unicode_symbols")
 
1357         self.assertFormatEqual(expected, actual)
 
1358         black.assert_equivalent(source, actual)
 
1359         black.assert_stable(source, actual, black.FileMode())
 
1361     def test_single_file_force_pyi(self) -> None:
 
1362         reg_mode = black.FileMode()
 
1363         pyi_mode = black.FileMode(is_pyi=True)
 
1364         contents, expected = read_data("force_pyi")
 
1365         with cache_dir() as workspace:
 
1366             path = (workspace / "file.py").resolve()
 
1367             with open(path, "w") as fh:
 
1369             self.invokeBlack([str(path), "--pyi"])
 
1370             with open(path, "r") as fh:
 
1372             # verify cache with --pyi is separate
 
1373             pyi_cache = black.read_cache(pyi_mode)
 
1374             self.assertIn(path, pyi_cache)
 
1375             normal_cache = black.read_cache(reg_mode)
 
1376             self.assertNotIn(path, normal_cache)
 
1377         self.assertEqual(actual, expected)
 
1379     @event_loop(close=False)
 
1380     def test_multi_file_force_pyi(self) -> None:
 
1381         reg_mode = black.FileMode()
 
1382         pyi_mode = black.FileMode(is_pyi=True)
 
1383         contents, expected = read_data("force_pyi")
 
1384         with cache_dir() as workspace:
 
1386                 (workspace / "file1.py").resolve(),
 
1387                 (workspace / "file2.py").resolve(),
 
1390                 with open(path, "w") as fh:
 
1392             self.invokeBlack([str(p) for p in paths] + ["--pyi"])
 
1394                 with open(path, "r") as fh:
 
1396                 self.assertEqual(actual, expected)
 
1397             # verify cache with --pyi is separate
 
1398             pyi_cache = black.read_cache(pyi_mode)
 
1399             normal_cache = black.read_cache(reg_mode)
 
1401                 self.assertIn(path, pyi_cache)
 
1402                 self.assertNotIn(path, normal_cache)
 
1404     def test_pipe_force_pyi(self) -> None:
 
1405         source, expected = read_data("force_pyi")
 
1406         result = CliRunner().invoke(
 
1407             black.main, ["-", "-q", "--pyi"], input=BytesIO(source.encode("utf8"))
 
1409         self.assertEqual(result.exit_code, 0)
 
1410         actual = result.output
 
1411         self.assertFormatEqual(actual, expected)
 
1413     def test_single_file_force_py36(self) -> None:
 
1414         reg_mode = black.FileMode()
 
1415         py36_mode = black.FileMode(target_versions=black.PY36_VERSIONS)
 
1416         source, expected = read_data("force_py36")
 
1417         with cache_dir() as workspace:
 
1418             path = (workspace / "file.py").resolve()
 
1419             with open(path, "w") as fh:
 
1421             self.invokeBlack([str(path), *PY36_ARGS])
 
1422             with open(path, "r") as fh:
 
1424             # verify cache with --target-version is separate
 
1425             py36_cache = black.read_cache(py36_mode)
 
1426             self.assertIn(path, py36_cache)
 
1427             normal_cache = black.read_cache(reg_mode)
 
1428             self.assertNotIn(path, normal_cache)
 
1429         self.assertEqual(actual, expected)
 
1431     @event_loop(close=False)
 
1432     def test_multi_file_force_py36(self) -> None:
 
1433         reg_mode = black.FileMode()
 
1434         py36_mode = black.FileMode(target_versions=black.PY36_VERSIONS)
 
1435         source, expected = read_data("force_py36")
 
1436         with cache_dir() as workspace:
 
1438                 (workspace / "file1.py").resolve(),
 
1439                 (workspace / "file2.py").resolve(),
 
1442                 with open(path, "w") as fh:
 
1444             self.invokeBlack([str(p) for p in paths] + PY36_ARGS)
 
1446                 with open(path, "r") as fh:
 
1448                 self.assertEqual(actual, expected)
 
1449             # verify cache with --target-version is separate
 
1450             pyi_cache = black.read_cache(py36_mode)
 
1451             normal_cache = black.read_cache(reg_mode)
 
1453                 self.assertIn(path, pyi_cache)
 
1454                 self.assertNotIn(path, normal_cache)
 
1456     def test_collections(self) -> None:
 
1457         source, expected = read_data("collections")
 
1459         self.assertFormatEqual(expected, actual)
 
1460         black.assert_equivalent(source, actual)
 
1461         black.assert_stable(source, actual, black.FileMode())
 
1463     def test_pipe_force_py36(self) -> None:
 
1464         source, expected = read_data("force_py36")
 
1465         result = CliRunner().invoke(
 
1467             ["-", "-q", "--target-version=py36"],
 
1468             input=BytesIO(source.encode("utf8")),
 
1470         self.assertEqual(result.exit_code, 0)
 
1471         actual = result.output
 
1472         self.assertFormatEqual(actual, expected)
 
1474     def test_include_exclude(self) -> None:
 
1475         path = THIS_DIR / "data" / "include_exclude_tests"
 
1476         include = re.compile(r"\.pyi?$")
 
1477         exclude = re.compile(r"/exclude/|/\.definitely_exclude/")
 
1478         report = black.Report()
 
1479         gitignore = PathSpec.from_lines("gitwildmatch", [])
 
1480         sources: List[Path] = []
 
1482             Path(path / "b/dont_exclude/a.py"),
 
1483             Path(path / "b/dont_exclude/a.pyi"),
 
1485         this_abs = THIS_DIR.resolve()
 
1487             black.gen_python_files_in_dir(
 
1488                 path, this_abs, include, exclude, report, gitignore
 
1491         self.assertEqual(sorted(expected), sorted(sources))
 
1493     def test_gitignore_exclude(self) -> None:
 
1494         path = THIS_DIR / "data" / "include_exclude_tests"
 
1495         include = re.compile(r"\.pyi?$")
 
1496         exclude = re.compile(r"")
 
1497         report = black.Report()
 
1498         gitignore = PathSpec.from_lines(
 
1499             "gitwildmatch", ["exclude/", ".definitely_exclude"]
 
1501         sources: List[Path] = []
 
1503             Path(path / "b/dont_exclude/a.py"),
 
1504             Path(path / "b/dont_exclude/a.pyi"),
 
1506         this_abs = THIS_DIR.resolve()
 
1508             black.gen_python_files_in_dir(
 
1509                 path, this_abs, include, exclude, report, gitignore
 
1512         self.assertEqual(sorted(expected), sorted(sources))
 
1514     def test_empty_include(self) -> None:
 
1515         path = THIS_DIR / "data" / "include_exclude_tests"
 
1516         report = black.Report()
 
1517         gitignore = PathSpec.from_lines("gitwildmatch", [])
 
1518         empty = re.compile(r"")
 
1519         sources: List[Path] = []
 
1521             Path(path / "b/exclude/a.pie"),
 
1522             Path(path / "b/exclude/a.py"),
 
1523             Path(path / "b/exclude/a.pyi"),
 
1524             Path(path / "b/dont_exclude/a.pie"),
 
1525             Path(path / "b/dont_exclude/a.py"),
 
1526             Path(path / "b/dont_exclude/a.pyi"),
 
1527             Path(path / "b/.definitely_exclude/a.pie"),
 
1528             Path(path / "b/.definitely_exclude/a.py"),
 
1529             Path(path / "b/.definitely_exclude/a.pyi"),
 
1531         this_abs = THIS_DIR.resolve()
 
1533             black.gen_python_files_in_dir(
 
1537                 re.compile(black.DEFAULT_EXCLUDES),
 
1542         self.assertEqual(sorted(expected), sorted(sources))
 
1544     def test_empty_exclude(self) -> None:
 
1545         path = THIS_DIR / "data" / "include_exclude_tests"
 
1546         report = black.Report()
 
1547         gitignore = PathSpec.from_lines("gitwildmatch", [])
 
1548         empty = re.compile(r"")
 
1549         sources: List[Path] = []
 
1551             Path(path / "b/dont_exclude/a.py"),
 
1552             Path(path / "b/dont_exclude/a.pyi"),
 
1553             Path(path / "b/exclude/a.py"),
 
1554             Path(path / "b/exclude/a.pyi"),
 
1555             Path(path / "b/.definitely_exclude/a.py"),
 
1556             Path(path / "b/.definitely_exclude/a.pyi"),
 
1558         this_abs = THIS_DIR.resolve()
 
1560             black.gen_python_files_in_dir(
 
1563                 re.compile(black.DEFAULT_INCLUDES),
 
1569         self.assertEqual(sorted(expected), sorted(sources))
 
1571     def test_invalid_include_exclude(self) -> None:
 
1572         for option in ["--include", "--exclude"]:
 
1573             self.invokeBlack(["-", option, "**()(!!*)"], exit_code=2)
 
1575     def test_preserves_line_endings(self) -> None:
 
1576         with TemporaryDirectory() as workspace:
 
1577             test_file = Path(workspace) / "test.py"
 
1578             for nl in ["\n", "\r\n"]:
 
1579                 contents = nl.join(["def f(  ):", "    pass"])
 
1580                 test_file.write_bytes(contents.encode())
 
1581                 ff(test_file, write_back=black.WriteBack.YES)
 
1582                 updated_contents: bytes = test_file.read_bytes()
 
1583                 self.assertIn(nl.encode(), updated_contents)
 
1585                     self.assertNotIn(b"\r\n", updated_contents)
 
1587     def test_preserves_line_endings_via_stdin(self) -> None:
 
1588         for nl in ["\n", "\r\n"]:
 
1589             contents = nl.join(["def f(  ):", "    pass"])
 
1590             runner = BlackRunner()
 
1591             result = runner.invoke(
 
1592                 black.main, ["-", "--fast"], input=BytesIO(contents.encode("utf8"))
 
1594             self.assertEqual(result.exit_code, 0)
 
1595             output = runner.stdout_bytes
 
1596             self.assertIn(nl.encode("utf8"), output)
 
1598                 self.assertNotIn(b"\r\n", output)
 
1600     def test_assert_equivalent_different_asts(self) -> None:
 
1601         with self.assertRaises(AssertionError):
 
1602             black.assert_equivalent("{}", "None")
 
1604     def test_symlink_out_of_root_directory(self) -> None:
 
1608         include = re.compile(black.DEFAULT_INCLUDES)
 
1609         exclude = re.compile(black.DEFAULT_EXCLUDES)
 
1610         report = black.Report()
 
1611         gitignore = PathSpec.from_lines("gitwildmatch", [])
 
1612         # `child` should behave like a symlink which resolved path is clearly
 
1613         # outside of the `root` directory.
 
1614         path.iterdir.return_value = [child]
 
1615         child.resolve.return_value = Path("/a/b/c")
 
1616         child.as_posix.return_value = "/a/b/c"
 
1617         child.is_symlink.return_value = True
 
1620                 black.gen_python_files_in_dir(
 
1621                     path, root, include, exclude, report, gitignore
 
1624         except ValueError as ve:
 
1625             self.fail(f"`get_python_files_in_dir()` failed: {ve}")
 
1626         path.iterdir.assert_called_once()
 
1627         child.resolve.assert_called_once()
 
1628         child.is_symlink.assert_called_once()
 
1629         # `child` should behave like a strange file which resolved path is clearly
 
1630         # outside of the `root` directory.
 
1631         child.is_symlink.return_value = False
 
1632         with self.assertRaises(ValueError):
 
1634                 black.gen_python_files_in_dir(
 
1635                     path, root, include, exclude, report, gitignore
 
1638         path.iterdir.assert_called()
 
1639         self.assertEqual(path.iterdir.call_count, 2)
 
1640         child.resolve.assert_called()
 
1641         self.assertEqual(child.resolve.call_count, 2)
 
1642         child.is_symlink.assert_called()
 
1643         self.assertEqual(child.is_symlink.call_count, 2)
 
1645     def test_shhh_click(self) -> None:
 
1647             from click import _unicodefun  # type: ignore
 
1648         except ModuleNotFoundError:
 
1649             self.skipTest("Incompatible Click version")
 
1650         if not hasattr(_unicodefun, "_verify_python3_env"):
 
1651             self.skipTest("Incompatible Click version")
 
1652         # First, let's see if Click is crashing with a preferred ASCII charset.
 
1653         with patch("locale.getpreferredencoding") as gpe:
 
1654             gpe.return_value = "ASCII"
 
1655             with self.assertRaises(RuntimeError):
 
1656                 _unicodefun._verify_python3_env()
 
1657         # Now, let's silence Click...
 
1659         # ...and confirm it's silent.
 
1660         with patch("locale.getpreferredencoding") as gpe:
 
1661             gpe.return_value = "ASCII"
 
1663                 _unicodefun._verify_python3_env()
 
1664             except RuntimeError as re:
 
1665                 self.fail(f"`patch_click()` failed, exception still raised: {re}")
 
1667     def test_root_logger_not_used_directly(self) -> None:
 
1668         def fail(*args: Any, **kwargs: Any) -> None:
 
1669             self.fail("Record created with root logger")
 
1671         with patch.multiple(
 
1682     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1683     def test_blackd_main(self) -> None:
 
1684         with patch("blackd.web.run_app"):
 
1685             result = CliRunner().invoke(blackd.main, [])
 
1686             if result.exception is not None:
 
1687                 raise result.exception
 
1688             self.assertEqual(result.exit_code, 0)
 
1690     def test_invalid_config_return_code(self) -> None:
 
1691         tmp_file = Path(black.dump_to_file())
 
1693             tmp_config = Path(black.dump_to_file())
 
1695             args = ["--config", str(tmp_config), str(tmp_file)]
 
1696             self.invokeBlack(args, exit_code=2, ignore_config=False)
 
1701 class BlackDTestCase(AioHTTPTestCase):
 
1702     async def get_application(self) -> web.Application:
 
1703         return blackd.make_app()
 
1705     # TODO: remove these decorators once the below is released
 
1706     # https://github.com/aio-libs/aiohttp/pull/3727
 
1707     @skip_if_exception("ClientOSError")
 
1708     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1710     async def test_blackd_request_needs_formatting(self) -> None:
 
1711         response = await self.client.post("/", data=b"print('hello world')")
 
1712         self.assertEqual(response.status, 200)
 
1713         self.assertEqual(response.charset, "utf8")
 
1714         self.assertEqual(await response.read(), b'print("hello world")\n')
 
1716     @skip_if_exception("ClientOSError")
 
1717     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1719     async def test_blackd_request_no_change(self) -> None:
 
1720         response = await self.client.post("/", data=b'print("hello world")\n')
 
1721         self.assertEqual(response.status, 204)
 
1722         self.assertEqual(await response.read(), b"")
 
1724     @skip_if_exception("ClientOSError")
 
1725     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1727     async def test_blackd_request_syntax_error(self) -> None:
 
1728         response = await self.client.post("/", data=b"what even ( is")
 
1729         self.assertEqual(response.status, 400)
 
1730         content = await response.text()
 
1732             content.startswith("Cannot parse"),
 
1733             msg=f"Expected error to start with 'Cannot parse', got {repr(content)}",
 
1736     @skip_if_exception("ClientOSError")
 
1737     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1739     async def test_blackd_unsupported_version(self) -> None:
 
1740         response = await self.client.post(
 
1741             "/", data=b"what", headers={blackd.PROTOCOL_VERSION_HEADER: "2"}
 
1743         self.assertEqual(response.status, 501)
 
1745     @skip_if_exception("ClientOSError")
 
1746     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1748     async def test_blackd_supported_version(self) -> None:
 
1749         response = await self.client.post(
 
1750             "/", data=b"what", headers={blackd.PROTOCOL_VERSION_HEADER: "1"}
 
1752         self.assertEqual(response.status, 200)
 
1754     @skip_if_exception("ClientOSError")
 
1755     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1757     async def test_blackd_invalid_python_variant(self) -> None:
 
1758         async def check(header_value: str, expected_status: int = 400) -> None:
 
1759             response = await self.client.post(
 
1760                 "/", data=b"what", headers={blackd.PYTHON_VARIANT_HEADER: header_value}
 
1762             self.assertEqual(response.status, expected_status)
 
1765         await check("ruby3.5")
 
1766         await check("pyi3.6")
 
1767         await check("py1.5")
 
1769         await check("py2.8")
 
1771         await check("pypy3.0")
 
1772         await check("jython3.4")
 
1774     @skip_if_exception("ClientOSError")
 
1775     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1777     async def test_blackd_pyi(self) -> None:
 
1778         source, expected = read_data("stub.pyi")
 
1779         response = await self.client.post(
 
1780             "/", data=source, headers={blackd.PYTHON_VARIANT_HEADER: "pyi"}
 
1782         self.assertEqual(response.status, 200)
 
1783         self.assertEqual(await response.text(), expected)
 
1785     @skip_if_exception("ClientOSError")
 
1786     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1788     async def test_blackd_diff(self) -> None:
 
1789         diff_header = re.compile(
 
1790             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"
 
1793         source, _ = read_data("blackd_diff.py")
 
1794         expected, _ = read_data("blackd_diff.diff")
 
1796         response = await self.client.post(
 
1797             "/", data=source, headers={blackd.DIFF_HEADER: "true"}
 
1799         self.assertEqual(response.status, 200)
 
1801         actual = await response.text()
 
1802         actual = diff_header.sub(DETERMINISTIC_HEADER, actual)
 
1803         self.assertEqual(actual, expected)
 
1805     @skip_if_exception("ClientOSError")
 
1806     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1808     async def test_blackd_python_variant(self) -> None:
 
1811             "    and_has_a_bunch_of,\n"
 
1812             "    very_long_arguments_too,\n"
 
1813             "    and_lots_of_them_as_well_lol,\n"
 
1814             "    **and_very_long_keyword_arguments\n"
 
1819         async def check(header_value: str, expected_status: int) -> None:
 
1820             response = await self.client.post(
 
1821                 "/", data=code, headers={blackd.PYTHON_VARIANT_HEADER: header_value}
 
1824                 response.status, expected_status, msg=await response.text()
 
1827         await check("3.6", 200)
 
1828         await check("py3.6", 200)
 
1829         await check("3.6,3.7", 200)
 
1830         await check("3.6,py3.7", 200)
 
1831         await check("py36,py37", 200)
 
1832         await check("36", 200)
 
1833         await check("3.6.4", 200)
 
1835         await check("2", 204)
 
1836         await check("2.7", 204)
 
1837         await check("py2.7", 204)
 
1838         await check("3.4", 204)
 
1839         await check("py3.4", 204)
 
1840         await check("py34,py36", 204)
 
1841         await check("34", 204)
 
1843     @skip_if_exception("ClientOSError")
 
1844     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1846     async def test_blackd_line_length(self) -> None:
 
1847         response = await self.client.post(
 
1848             "/", data=b'print("hello")\n', headers={blackd.LINE_LENGTH_HEADER: "7"}
 
1850         self.assertEqual(response.status, 200)
 
1852     @skip_if_exception("ClientOSError")
 
1853     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1855     async def test_blackd_invalid_line_length(self) -> None:
 
1856         response = await self.client.post(
 
1857             "/", data=b'print("hello")\n', headers={blackd.LINE_LENGTH_HEADER: "NaN"}
 
1859         self.assertEqual(response.status, 400)
 
1861     @skip_if_exception("ClientOSError")
 
1862     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1864     async def test_blackd_response_black_version_header(self) -> None:
 
1865         response = await self.client.post("/")
 
1866         self.assertIsNotNone(response.headers.get(blackd.BLACK_VERSION_HEADER))
 
1869 if __name__ == "__main__":
 
1870     unittest.main(module="test_black")