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 "
 
 247             rf"\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             rf"\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                 f"Expected diff isn't equal to the actual. If you made changes "
 
 350                 f"to expression.py and this is an anticipated difference, "
 
 351                 f"overwrite 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, 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_slices(self) -> None:
 
 395         source, expected = read_data("slices")
 
 397         self.assertFormatEqual(expected, actual)
 
 398         black.assert_equivalent(source, actual)
 
 399         black.assert_stable(source, actual, black.FileMode())
 
 401     @patch("black.dump_to_file", dump_to_stderr)
 
 402     def test_comments(self) -> None:
 
 403         source, expected = read_data("comments")
 
 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_comments2(self) -> None:
 
 411         source, expected = read_data("comments2")
 
 413         self.assertFormatEqual(expected, actual)
 
 414         black.assert_equivalent(source, actual)
 
 415         black.assert_stable(source, actual, black.FileMode())
 
 417     @patch("black.dump_to_file", dump_to_stderr)
 
 418     def test_comments3(self) -> None:
 
 419         source, expected = read_data("comments3")
 
 421         self.assertFormatEqual(expected, actual)
 
 422         black.assert_equivalent(source, actual)
 
 423         black.assert_stable(source, actual, black.FileMode())
 
 425     @patch("black.dump_to_file", dump_to_stderr)
 
 426     def test_comments4(self) -> None:
 
 427         source, expected = read_data("comments4")
 
 429         self.assertFormatEqual(expected, actual)
 
 430         black.assert_equivalent(source, actual)
 
 431         black.assert_stable(source, actual, black.FileMode())
 
 433     @patch("black.dump_to_file", dump_to_stderr)
 
 434     def test_comments5(self) -> None:
 
 435         source, expected = read_data("comments5")
 
 437         self.assertFormatEqual(expected, actual)
 
 438         black.assert_equivalent(source, actual)
 
 439         black.assert_stable(source, actual, black.FileMode())
 
 441     @patch("black.dump_to_file", dump_to_stderr)
 
 442     def test_comments6(self) -> None:
 
 443         source, expected = read_data("comments6")
 
 445         self.assertFormatEqual(expected, actual)
 
 446         black.assert_equivalent(source, actual)
 
 447         black.assert_stable(source, actual, black.FileMode())
 
 449     @patch("black.dump_to_file", dump_to_stderr)
 
 450     def test_comments7(self) -> None:
 
 451         source, expected = read_data("comments7")
 
 453         self.assertFormatEqual(expected, actual)
 
 454         black.assert_equivalent(source, actual)
 
 455         black.assert_stable(source, actual, black.FileMode())
 
 457     @patch("black.dump_to_file", dump_to_stderr)
 
 458     def test_comment_after_escaped_newline(self) -> None:
 
 459         source, expected = read_data("comment_after_escaped_newline")
 
 461         self.assertFormatEqual(expected, actual)
 
 462         black.assert_equivalent(source, actual)
 
 463         black.assert_stable(source, actual, black.FileMode())
 
 465     @patch("black.dump_to_file", dump_to_stderr)
 
 466     def test_cantfit(self) -> None:
 
 467         source, expected = read_data("cantfit")
 
 469         self.assertFormatEqual(expected, actual)
 
 470         black.assert_equivalent(source, actual)
 
 471         black.assert_stable(source, actual, black.FileMode())
 
 473     @patch("black.dump_to_file", dump_to_stderr)
 
 474     def test_import_spacing(self) -> None:
 
 475         source, expected = read_data("import_spacing")
 
 477         self.assertFormatEqual(expected, actual)
 
 478         black.assert_equivalent(source, actual)
 
 479         black.assert_stable(source, actual, black.FileMode())
 
 481     @patch("black.dump_to_file", dump_to_stderr)
 
 482     def test_composition(self) -> None:
 
 483         source, expected = read_data("composition")
 
 485         self.assertFormatEqual(expected, actual)
 
 486         black.assert_equivalent(source, actual)
 
 487         black.assert_stable(source, actual, black.FileMode())
 
 489     @patch("black.dump_to_file", dump_to_stderr)
 
 490     def test_empty_lines(self) -> None:
 
 491         source, expected = read_data("empty_lines")
 
 493         self.assertFormatEqual(expected, actual)
 
 494         black.assert_equivalent(source, actual)
 
 495         black.assert_stable(source, actual, black.FileMode())
 
 497     @patch("black.dump_to_file", dump_to_stderr)
 
 498     def test_remove_parens(self) -> None:
 
 499         source, expected = read_data("remove_parens")
 
 501         self.assertFormatEqual(expected, actual)
 
 502         black.assert_equivalent(source, actual)
 
 503         black.assert_stable(source, actual, black.FileMode())
 
 505     @patch("black.dump_to_file", dump_to_stderr)
 
 506     def test_string_prefixes(self) -> None:
 
 507         source, expected = read_data("string_prefixes")
 
 509         self.assertFormatEqual(expected, actual)
 
 510         black.assert_equivalent(source, actual)
 
 511         black.assert_stable(source, actual, black.FileMode())
 
 513     @patch("black.dump_to_file", dump_to_stderr)
 
 514     def test_numeric_literals(self) -> None:
 
 515         source, expected = read_data("numeric_literals")
 
 516         mode = black.FileMode(target_versions=black.PY36_VERSIONS)
 
 517         actual = fs(source, mode=mode)
 
 518         self.assertFormatEqual(expected, actual)
 
 519         black.assert_equivalent(source, actual)
 
 520         black.assert_stable(source, actual, mode)
 
 522     @patch("black.dump_to_file", dump_to_stderr)
 
 523     def test_numeric_literals_ignoring_underscores(self) -> None:
 
 524         source, expected = read_data("numeric_literals_skip_underscores")
 
 525         mode = black.FileMode(target_versions=black.PY36_VERSIONS)
 
 526         actual = fs(source, mode=mode)
 
 527         self.assertFormatEqual(expected, actual)
 
 528         black.assert_equivalent(source, actual)
 
 529         black.assert_stable(source, actual, mode)
 
 531     @patch("black.dump_to_file", dump_to_stderr)
 
 532     def test_numeric_literals_py2(self) -> None:
 
 533         source, expected = read_data("numeric_literals_py2")
 
 535         self.assertFormatEqual(expected, actual)
 
 536         black.assert_stable(source, actual, black.FileMode())
 
 538     @patch("black.dump_to_file", dump_to_stderr)
 
 539     def test_python2(self) -> None:
 
 540         source, expected = read_data("python2")
 
 542         self.assertFormatEqual(expected, actual)
 
 543         black.assert_equivalent(source, actual)
 
 544         black.assert_stable(source, actual, black.FileMode())
 
 546     @patch("black.dump_to_file", dump_to_stderr)
 
 547     def test_python2_print_function(self) -> None:
 
 548         source, expected = read_data("python2_print_function")
 
 549         mode = black.FileMode(target_versions={TargetVersion.PY27})
 
 550         actual = fs(source, mode=mode)
 
 551         self.assertFormatEqual(expected, actual)
 
 552         black.assert_equivalent(source, actual)
 
 553         black.assert_stable(source, actual, mode)
 
 555     @patch("black.dump_to_file", dump_to_stderr)
 
 556     def test_python2_unicode_literals(self) -> None:
 
 557         source, expected = read_data("python2_unicode_literals")
 
 559         self.assertFormatEqual(expected, actual)
 
 560         black.assert_equivalent(source, actual)
 
 561         black.assert_stable(source, actual, black.FileMode())
 
 563     @patch("black.dump_to_file", dump_to_stderr)
 
 564     def test_stub(self) -> None:
 
 565         mode = black.FileMode(is_pyi=True)
 
 566         source, expected = read_data("stub.pyi")
 
 567         actual = fs(source, mode=mode)
 
 568         self.assertFormatEqual(expected, actual)
 
 569         black.assert_stable(source, actual, mode)
 
 571     @patch("black.dump_to_file", dump_to_stderr)
 
 572     def test_async_as_identifier(self) -> None:
 
 573         source_path = (THIS_DIR / "data" / "async_as_identifier.py").resolve()
 
 574         source, expected = read_data("async_as_identifier")
 
 576         self.assertFormatEqual(expected, actual)
 
 577         major, minor = sys.version_info[:2]
 
 578         if major < 3 or (major <= 3 and minor < 7):
 
 579             black.assert_equivalent(source, actual)
 
 580         black.assert_stable(source, actual, black.FileMode())
 
 581         # ensure black can parse this when the target is 3.6
 
 582         self.invokeBlack([str(source_path), "--target-version", "py36"])
 
 583         # but not on 3.7, because async/await is no longer an identifier
 
 584         self.invokeBlack([str(source_path), "--target-version", "py37"], exit_code=123)
 
 586     @patch("black.dump_to_file", dump_to_stderr)
 
 587     def test_python37(self) -> None:
 
 588         source_path = (THIS_DIR / "data" / "python37.py").resolve()
 
 589         source, expected = read_data("python37")
 
 591         self.assertFormatEqual(expected, actual)
 
 592         major, minor = sys.version_info[:2]
 
 593         if major > 3 or (major == 3 and minor >= 7):
 
 594             black.assert_equivalent(source, actual)
 
 595         black.assert_stable(source, actual, black.FileMode())
 
 596         # ensure black can parse this when the target is 3.7
 
 597         self.invokeBlack([str(source_path), "--target-version", "py37"])
 
 598         # but not on 3.6, because we use async as a reserved keyword
 
 599         self.invokeBlack([str(source_path), "--target-version", "py36"], exit_code=123)
 
 601     @patch("black.dump_to_file", dump_to_stderr)
 
 602     def test_fmtonoff(self) -> None:
 
 603         source, expected = read_data("fmtonoff")
 
 605         self.assertFormatEqual(expected, actual)
 
 606         black.assert_equivalent(source, actual)
 
 607         black.assert_stable(source, actual, black.FileMode())
 
 609     @patch("black.dump_to_file", dump_to_stderr)
 
 610     def test_fmtonoff2(self) -> None:
 
 611         source, expected = read_data("fmtonoff2")
 
 613         self.assertFormatEqual(expected, actual)
 
 614         black.assert_equivalent(source, actual)
 
 615         black.assert_stable(source, actual, black.FileMode())
 
 617     @patch("black.dump_to_file", dump_to_stderr)
 
 618     def test_fmtonoff3(self) -> None:
 
 619         source, expected = read_data("fmtonoff3")
 
 621         self.assertFormatEqual(expected, actual)
 
 622         black.assert_equivalent(source, actual)
 
 623         black.assert_stable(source, actual, black.FileMode())
 
 625     @patch("black.dump_to_file", dump_to_stderr)
 
 626     def test_remove_empty_parentheses_after_class(self) -> None:
 
 627         source, expected = read_data("class_blank_parentheses")
 
 629         self.assertFormatEqual(expected, actual)
 
 630         black.assert_equivalent(source, actual)
 
 631         black.assert_stable(source, actual, black.FileMode())
 
 633     @patch("black.dump_to_file", dump_to_stderr)
 
 634     def test_new_line_between_class_and_code(self) -> None:
 
 635         source, expected = read_data("class_methods_new_line")
 
 637         self.assertFormatEqual(expected, actual)
 
 638         black.assert_equivalent(source, actual)
 
 639         black.assert_stable(source, actual, black.FileMode())
 
 641     @patch("black.dump_to_file", dump_to_stderr)
 
 642     def test_bracket_match(self) -> None:
 
 643         source, expected = read_data("bracketmatch")
 
 645         self.assertFormatEqual(expected, actual)
 
 646         black.assert_equivalent(source, actual)
 
 647         black.assert_stable(source, actual, black.FileMode())
 
 649     @patch("black.dump_to_file", dump_to_stderr)
 
 650     def test_tuple_assign(self) -> None:
 
 651         source, expected = read_data("tupleassign")
 
 653         self.assertFormatEqual(expected, actual)
 
 654         black.assert_equivalent(source, actual)
 
 655         black.assert_stable(source, actual, black.FileMode())
 
 657     @patch("black.dump_to_file", dump_to_stderr)
 
 658     def test_beginning_backslash(self) -> None:
 
 659         source, expected = read_data("beginning_backslash")
 
 661         self.assertFormatEqual(expected, actual)
 
 662         black.assert_equivalent(source, actual)
 
 663         black.assert_stable(source, actual, black.FileMode())
 
 665     def test_tab_comment_indentation(self) -> None:
 
 666         contents_tab = "if 1:\n\tif 2:\n\t\tpass\n\t# comment\n\tpass\n"
 
 667         contents_spc = "if 1:\n    if 2:\n        pass\n    # comment\n    pass\n"
 
 668         self.assertFormatEqual(contents_spc, fs(contents_spc))
 
 669         self.assertFormatEqual(contents_spc, fs(contents_tab))
 
 671         contents_tab = "if 1:\n\tif 2:\n\t\tpass\n\t\t# comment\n\tpass\n"
 
 672         contents_spc = "if 1:\n    if 2:\n        pass\n        # comment\n    pass\n"
 
 673         self.assertFormatEqual(contents_spc, fs(contents_spc))
 
 674         self.assertFormatEqual(contents_spc, fs(contents_tab))
 
 676         # mixed tabs and spaces (valid Python 2 code)
 
 677         contents_tab = "if 1:\n        if 2:\n\t\tpass\n\t# comment\n        pass\n"
 
 678         contents_spc = "if 1:\n    if 2:\n        pass\n    # comment\n    pass\n"
 
 679         self.assertFormatEqual(contents_spc, fs(contents_spc))
 
 680         self.assertFormatEqual(contents_spc, fs(contents_tab))
 
 682         contents_tab = "if 1:\n        if 2:\n\t\tpass\n\t\t# comment\n        pass\n"
 
 683         contents_spc = "if 1:\n    if 2:\n        pass\n        # comment\n    pass\n"
 
 684         self.assertFormatEqual(contents_spc, fs(contents_spc))
 
 685         self.assertFormatEqual(contents_spc, fs(contents_tab))
 
 687     def test_report_verbose(self) -> None:
 
 688         report = black.Report(verbose=True)
 
 692         def out(msg: str, **kwargs: Any) -> None:
 
 693             out_lines.append(msg)
 
 695         def err(msg: str, **kwargs: Any) -> None:
 
 696             err_lines.append(msg)
 
 698         with patch("black.out", out), patch("black.err", err):
 
 699             report.done(Path("f1"), black.Changed.NO)
 
 700             self.assertEqual(len(out_lines), 1)
 
 701             self.assertEqual(len(err_lines), 0)
 
 702             self.assertEqual(out_lines[-1], "f1 already well formatted, good job.")
 
 703             self.assertEqual(unstyle(str(report)), "1 file left unchanged.")
 
 704             self.assertEqual(report.return_code, 0)
 
 705             report.done(Path("f2"), black.Changed.YES)
 
 706             self.assertEqual(len(out_lines), 2)
 
 707             self.assertEqual(len(err_lines), 0)
 
 708             self.assertEqual(out_lines[-1], "reformatted f2")
 
 710                 unstyle(str(report)), "1 file reformatted, 1 file left unchanged."
 
 712             report.done(Path("f3"), black.Changed.CACHED)
 
 713             self.assertEqual(len(out_lines), 3)
 
 714             self.assertEqual(len(err_lines), 0)
 
 716                 out_lines[-1], "f3 wasn't modified on disk since last run."
 
 719                 unstyle(str(report)), "1 file reformatted, 2 files left unchanged."
 
 721             self.assertEqual(report.return_code, 0)
 
 723             self.assertEqual(report.return_code, 1)
 
 725             report.failed(Path("e1"), "boom")
 
 726             self.assertEqual(len(out_lines), 3)
 
 727             self.assertEqual(len(err_lines), 1)
 
 728             self.assertEqual(err_lines[-1], "error: cannot format e1: boom")
 
 730                 unstyle(str(report)),
 
 731                 "1 file reformatted, 2 files left unchanged, "
 
 732                 "1 file failed to reformat.",
 
 734             self.assertEqual(report.return_code, 123)
 
 735             report.done(Path("f3"), black.Changed.YES)
 
 736             self.assertEqual(len(out_lines), 4)
 
 737             self.assertEqual(len(err_lines), 1)
 
 738             self.assertEqual(out_lines[-1], "reformatted f3")
 
 740                 unstyle(str(report)),
 
 741                 "2 files reformatted, 2 files left unchanged, "
 
 742                 "1 file failed to reformat.",
 
 744             self.assertEqual(report.return_code, 123)
 
 745             report.failed(Path("e2"), "boom")
 
 746             self.assertEqual(len(out_lines), 4)
 
 747             self.assertEqual(len(err_lines), 2)
 
 748             self.assertEqual(err_lines[-1], "error: cannot format e2: boom")
 
 750                 unstyle(str(report)),
 
 751                 "2 files reformatted, 2 files left unchanged, "
 
 752                 "2 files failed to reformat.",
 
 754             self.assertEqual(report.return_code, 123)
 
 755             report.path_ignored(Path("wat"), "no match")
 
 756             self.assertEqual(len(out_lines), 5)
 
 757             self.assertEqual(len(err_lines), 2)
 
 758             self.assertEqual(out_lines[-1], "wat ignored: no match")
 
 760                 unstyle(str(report)),
 
 761                 "2 files reformatted, 2 files left unchanged, "
 
 762                 "2 files failed to reformat.",
 
 764             self.assertEqual(report.return_code, 123)
 
 765             report.done(Path("f4"), black.Changed.NO)
 
 766             self.assertEqual(len(out_lines), 6)
 
 767             self.assertEqual(len(err_lines), 2)
 
 768             self.assertEqual(out_lines[-1], "f4 already well formatted, good job.")
 
 770                 unstyle(str(report)),
 
 771                 "2 files reformatted, 3 files left unchanged, "
 
 772                 "2 files failed to reformat.",
 
 774             self.assertEqual(report.return_code, 123)
 
 777                 unstyle(str(report)),
 
 778                 "2 files would be reformatted, 3 files would be left unchanged, "
 
 779                 "2 files would fail to reformat.",
 
 784                 unstyle(str(report)),
 
 785                 "2 files would be reformatted, 3 files would be left unchanged, "
 
 786                 "2 files would fail to reformat.",
 
 789     def test_report_quiet(self) -> None:
 
 790         report = black.Report(quiet=True)
 
 794         def out(msg: str, **kwargs: Any) -> None:
 
 795             out_lines.append(msg)
 
 797         def err(msg: str, **kwargs: Any) -> None:
 
 798             err_lines.append(msg)
 
 800         with patch("black.out", out), patch("black.err", err):
 
 801             report.done(Path("f1"), black.Changed.NO)
 
 802             self.assertEqual(len(out_lines), 0)
 
 803             self.assertEqual(len(err_lines), 0)
 
 804             self.assertEqual(unstyle(str(report)), "1 file left unchanged.")
 
 805             self.assertEqual(report.return_code, 0)
 
 806             report.done(Path("f2"), black.Changed.YES)
 
 807             self.assertEqual(len(out_lines), 0)
 
 808             self.assertEqual(len(err_lines), 0)
 
 810                 unstyle(str(report)), "1 file reformatted, 1 file left unchanged."
 
 812             report.done(Path("f3"), black.Changed.CACHED)
 
 813             self.assertEqual(len(out_lines), 0)
 
 814             self.assertEqual(len(err_lines), 0)
 
 816                 unstyle(str(report)), "1 file reformatted, 2 files left unchanged."
 
 818             self.assertEqual(report.return_code, 0)
 
 820             self.assertEqual(report.return_code, 1)
 
 822             report.failed(Path("e1"), "boom")
 
 823             self.assertEqual(len(out_lines), 0)
 
 824             self.assertEqual(len(err_lines), 1)
 
 825             self.assertEqual(err_lines[-1], "error: cannot format e1: boom")
 
 827                 unstyle(str(report)),
 
 828                 "1 file reformatted, 2 files left unchanged, "
 
 829                 "1 file failed to reformat.",
 
 831             self.assertEqual(report.return_code, 123)
 
 832             report.done(Path("f3"), black.Changed.YES)
 
 833             self.assertEqual(len(out_lines), 0)
 
 834             self.assertEqual(len(err_lines), 1)
 
 836                 unstyle(str(report)),
 
 837                 "2 files reformatted, 2 files left unchanged, "
 
 838                 "1 file failed to reformat.",
 
 840             self.assertEqual(report.return_code, 123)
 
 841             report.failed(Path("e2"), "boom")
 
 842             self.assertEqual(len(out_lines), 0)
 
 843             self.assertEqual(len(err_lines), 2)
 
 844             self.assertEqual(err_lines[-1], "error: cannot format e2: boom")
 
 846                 unstyle(str(report)),
 
 847                 "2 files reformatted, 2 files left unchanged, "
 
 848                 "2 files failed to reformat.",
 
 850             self.assertEqual(report.return_code, 123)
 
 851             report.path_ignored(Path("wat"), "no match")
 
 852             self.assertEqual(len(out_lines), 0)
 
 853             self.assertEqual(len(err_lines), 2)
 
 855                 unstyle(str(report)),
 
 856                 "2 files reformatted, 2 files left unchanged, "
 
 857                 "2 files failed to reformat.",
 
 859             self.assertEqual(report.return_code, 123)
 
 860             report.done(Path("f4"), black.Changed.NO)
 
 861             self.assertEqual(len(out_lines), 0)
 
 862             self.assertEqual(len(err_lines), 2)
 
 864                 unstyle(str(report)),
 
 865                 "2 files reformatted, 3 files left unchanged, "
 
 866                 "2 files failed to reformat.",
 
 868             self.assertEqual(report.return_code, 123)
 
 871                 unstyle(str(report)),
 
 872                 "2 files would be reformatted, 3 files would be left unchanged, "
 
 873                 "2 files would fail to reformat.",
 
 878                 unstyle(str(report)),
 
 879                 "2 files would be reformatted, 3 files would be left unchanged, "
 
 880                 "2 files would fail to reformat.",
 
 883     def test_report_normal(self) -> None:
 
 884         report = black.Report()
 
 888         def out(msg: str, **kwargs: Any) -> None:
 
 889             out_lines.append(msg)
 
 891         def err(msg: str, **kwargs: Any) -> None:
 
 892             err_lines.append(msg)
 
 894         with patch("black.out", out), patch("black.err", err):
 
 895             report.done(Path("f1"), black.Changed.NO)
 
 896             self.assertEqual(len(out_lines), 0)
 
 897             self.assertEqual(len(err_lines), 0)
 
 898             self.assertEqual(unstyle(str(report)), "1 file left unchanged.")
 
 899             self.assertEqual(report.return_code, 0)
 
 900             report.done(Path("f2"), black.Changed.YES)
 
 901             self.assertEqual(len(out_lines), 1)
 
 902             self.assertEqual(len(err_lines), 0)
 
 903             self.assertEqual(out_lines[-1], "reformatted f2")
 
 905                 unstyle(str(report)), "1 file reformatted, 1 file left unchanged."
 
 907             report.done(Path("f3"), black.Changed.CACHED)
 
 908             self.assertEqual(len(out_lines), 1)
 
 909             self.assertEqual(len(err_lines), 0)
 
 910             self.assertEqual(out_lines[-1], "reformatted f2")
 
 912                 unstyle(str(report)), "1 file reformatted, 2 files left unchanged."
 
 914             self.assertEqual(report.return_code, 0)
 
 916             self.assertEqual(report.return_code, 1)
 
 918             report.failed(Path("e1"), "boom")
 
 919             self.assertEqual(len(out_lines), 1)
 
 920             self.assertEqual(len(err_lines), 1)
 
 921             self.assertEqual(err_lines[-1], "error: cannot format e1: boom")
 
 923                 unstyle(str(report)),
 
 924                 "1 file reformatted, 2 files left unchanged, "
 
 925                 "1 file failed to reformat.",
 
 927             self.assertEqual(report.return_code, 123)
 
 928             report.done(Path("f3"), black.Changed.YES)
 
 929             self.assertEqual(len(out_lines), 2)
 
 930             self.assertEqual(len(err_lines), 1)
 
 931             self.assertEqual(out_lines[-1], "reformatted f3")
 
 933                 unstyle(str(report)),
 
 934                 "2 files reformatted, 2 files left unchanged, "
 
 935                 "1 file failed to reformat.",
 
 937             self.assertEqual(report.return_code, 123)
 
 938             report.failed(Path("e2"), "boom")
 
 939             self.assertEqual(len(out_lines), 2)
 
 940             self.assertEqual(len(err_lines), 2)
 
 941             self.assertEqual(err_lines[-1], "error: cannot format e2: boom")
 
 943                 unstyle(str(report)),
 
 944                 "2 files reformatted, 2 files left unchanged, "
 
 945                 "2 files failed to reformat.",
 
 947             self.assertEqual(report.return_code, 123)
 
 948             report.path_ignored(Path("wat"), "no match")
 
 949             self.assertEqual(len(out_lines), 2)
 
 950             self.assertEqual(len(err_lines), 2)
 
 952                 unstyle(str(report)),
 
 953                 "2 files reformatted, 2 files left unchanged, "
 
 954                 "2 files failed to reformat.",
 
 956             self.assertEqual(report.return_code, 123)
 
 957             report.done(Path("f4"), black.Changed.NO)
 
 958             self.assertEqual(len(out_lines), 2)
 
 959             self.assertEqual(len(err_lines), 2)
 
 961                 unstyle(str(report)),
 
 962                 "2 files reformatted, 3 files left unchanged, "
 
 963                 "2 files failed to reformat.",
 
 965             self.assertEqual(report.return_code, 123)
 
 968                 unstyle(str(report)),
 
 969                 "2 files would be reformatted, 3 files would be left unchanged, "
 
 970                 "2 files would fail to reformat.",
 
 975                 unstyle(str(report)),
 
 976                 "2 files would be reformatted, 3 files would be left unchanged, "
 
 977                 "2 files would fail to reformat.",
 
 980     def test_lib2to3_parse(self) -> None:
 
 981         with self.assertRaises(black.InvalidInput):
 
 982             black.lib2to3_parse("invalid syntax")
 
 985         black.lib2to3_parse(straddling)
 
 986         black.lib2to3_parse(straddling, {TargetVersion.PY27})
 
 987         black.lib2to3_parse(straddling, {TargetVersion.PY36})
 
 988         black.lib2to3_parse(straddling, {TargetVersion.PY27, TargetVersion.PY36})
 
 991         black.lib2to3_parse(py2_only)
 
 992         black.lib2to3_parse(py2_only, {TargetVersion.PY27})
 
 993         with self.assertRaises(black.InvalidInput):
 
 994             black.lib2to3_parse(py2_only, {TargetVersion.PY36})
 
 995         with self.assertRaises(black.InvalidInput):
 
 996             black.lib2to3_parse(py2_only, {TargetVersion.PY27, TargetVersion.PY36})
 
 998         py3_only = "exec(x, end=y)"
 
 999         black.lib2to3_parse(py3_only)
 
1000         with self.assertRaises(black.InvalidInput):
 
1001             black.lib2to3_parse(py3_only, {TargetVersion.PY27})
 
1002         black.lib2to3_parse(py3_only, {TargetVersion.PY36})
 
1003         black.lib2to3_parse(py3_only, {TargetVersion.PY27, TargetVersion.PY36})
 
1005     def test_get_features_used(self) -> None:
 
1006         node = black.lib2to3_parse("def f(*, arg): ...\n")
 
1007         self.assertEqual(black.get_features_used(node), set())
 
1008         node = black.lib2to3_parse("def f(*, arg,): ...\n")
 
1009         self.assertEqual(black.get_features_used(node), {Feature.TRAILING_COMMA_IN_DEF})
 
1010         node = black.lib2to3_parse("f(*arg,)\n")
 
1012             black.get_features_used(node), {Feature.TRAILING_COMMA_IN_CALL}
 
1014         node = black.lib2to3_parse("def f(*, arg): f'string'\n")
 
1015         self.assertEqual(black.get_features_used(node), {Feature.F_STRINGS})
 
1016         node = black.lib2to3_parse("123_456\n")
 
1017         self.assertEqual(black.get_features_used(node), {Feature.NUMERIC_UNDERSCORES})
 
1018         node = black.lib2to3_parse("123456\n")
 
1019         self.assertEqual(black.get_features_used(node), set())
 
1020         source, expected = read_data("function")
 
1021         node = black.lib2to3_parse(source)
 
1022         expected_features = {
 
1023             Feature.TRAILING_COMMA_IN_CALL,
 
1024             Feature.TRAILING_COMMA_IN_DEF,
 
1027         self.assertEqual(black.get_features_used(node), expected_features)
 
1028         node = black.lib2to3_parse(expected)
 
1029         self.assertEqual(black.get_features_used(node), expected_features)
 
1030         source, expected = read_data("expression")
 
1031         node = black.lib2to3_parse(source)
 
1032         self.assertEqual(black.get_features_used(node), set())
 
1033         node = black.lib2to3_parse(expected)
 
1034         self.assertEqual(black.get_features_used(node), set())
 
1036     def test_get_future_imports(self) -> None:
 
1037         node = black.lib2to3_parse("\n")
 
1038         self.assertEqual(set(), black.get_future_imports(node))
 
1039         node = black.lib2to3_parse("from __future__ import black\n")
 
1040         self.assertEqual({"black"}, black.get_future_imports(node))
 
1041         node = black.lib2to3_parse("from __future__ import multiple, imports\n")
 
1042         self.assertEqual({"multiple", "imports"}, black.get_future_imports(node))
 
1043         node = black.lib2to3_parse("from __future__ import (parenthesized, imports)\n")
 
1044         self.assertEqual({"parenthesized", "imports"}, black.get_future_imports(node))
 
1045         node = black.lib2to3_parse(
 
1046             "from __future__ import multiple\nfrom __future__ import imports\n"
 
1048         self.assertEqual({"multiple", "imports"}, black.get_future_imports(node))
 
1049         node = black.lib2to3_parse("# comment\nfrom __future__ import black\n")
 
1050         self.assertEqual({"black"}, black.get_future_imports(node))
 
1051         node = black.lib2to3_parse('"""docstring"""\nfrom __future__ import black\n')
 
1052         self.assertEqual({"black"}, black.get_future_imports(node))
 
1053         node = black.lib2to3_parse("some(other, code)\nfrom __future__ import black\n")
 
1054         self.assertEqual(set(), black.get_future_imports(node))
 
1055         node = black.lib2to3_parse("from some.module import black\n")
 
1056         self.assertEqual(set(), black.get_future_imports(node))
 
1057         node = black.lib2to3_parse(
 
1058             "from __future__ import unicode_literals as _unicode_literals"
 
1060         self.assertEqual({"unicode_literals"}, black.get_future_imports(node))
 
1061         node = black.lib2to3_parse(
 
1062             "from __future__ import unicode_literals as _lol, print"
 
1064         self.assertEqual({"unicode_literals", "print"}, black.get_future_imports(node))
 
1066     def test_debug_visitor(self) -> None:
 
1067         source, _ = read_data("debug_visitor.py")
 
1068         expected, _ = read_data("debug_visitor.out")
 
1072         def out(msg: str, **kwargs: Any) -> None:
 
1073             out_lines.append(msg)
 
1075         def err(msg: str, **kwargs: Any) -> None:
 
1076             err_lines.append(msg)
 
1078         with patch("black.out", out), patch("black.err", err):
 
1079             black.DebugVisitor.show(source)
 
1080         actual = "\n".join(out_lines) + "\n"
 
1082         if expected != actual:
 
1083             log_name = black.dump_to_file(*out_lines)
 
1087             f"AST print out is different. Actual version dumped to {log_name}",
 
1090     def test_format_file_contents(self) -> None:
 
1092         mode = black.FileMode()
 
1093         with self.assertRaises(black.NothingChanged):
 
1094             black.format_file_contents(empty, mode=mode, fast=False)
 
1096         with self.assertRaises(black.NothingChanged):
 
1097             black.format_file_contents(just_nl, mode=mode, fast=False)
 
1098         same = "j = [1, 2, 3]\n"
 
1099         with self.assertRaises(black.NothingChanged):
 
1100             black.format_file_contents(same, mode=mode, fast=False)
 
1101         different = "j = [1,2,3]"
 
1103         actual = black.format_file_contents(different, mode=mode, fast=False)
 
1104         self.assertEqual(expected, actual)
 
1105         invalid = "return if you can"
 
1106         with self.assertRaises(black.InvalidInput) as e:
 
1107             black.format_file_contents(invalid, mode=mode, fast=False)
 
1108         self.assertEqual(str(e.exception), "Cannot parse: 1:7: return if you can")
 
1110     def test_endmarker(self) -> None:
 
1111         n = black.lib2to3_parse("\n")
 
1112         self.assertEqual(n.type, black.syms.file_input)
 
1113         self.assertEqual(len(n.children), 1)
 
1114         self.assertEqual(n.children[0].type, black.token.ENDMARKER)
 
1116     @unittest.skipIf(os.environ.get("SKIP_AST_PRINT"), "user set SKIP_AST_PRINT")
 
1117     def test_assertFormatEqual(self) -> None:
 
1121         def out(msg: str, **kwargs: Any) -> None:
 
1122             out_lines.append(msg)
 
1124         def err(msg: str, **kwargs: Any) -> None:
 
1125             err_lines.append(msg)
 
1127         with patch("black.out", out), patch("black.err", err):
 
1128             with self.assertRaises(AssertionError):
 
1129                 self.assertFormatEqual("j = [1, 2, 3]", "j = [1, 2, 3,]")
 
1131         out_str = "".join(out_lines)
 
1132         self.assertTrue("Expected tree:" in out_str)
 
1133         self.assertTrue("Actual tree:" in out_str)
 
1134         self.assertEqual("".join(err_lines), "")
 
1136     def test_cache_broken_file(self) -> None:
 
1137         mode = black.FileMode()
 
1138         with cache_dir() as workspace:
 
1139             cache_file = black.get_cache_file(mode)
 
1140             with cache_file.open("w") as fobj:
 
1141                 fobj.write("this is not a pickle")
 
1142             self.assertEqual(black.read_cache(mode), {})
 
1143             src = (workspace / "test.py").resolve()
 
1144             with src.open("w") as fobj:
 
1145                 fobj.write("print('hello')")
 
1146             self.invokeBlack([str(src)])
 
1147             cache = black.read_cache(mode)
 
1148             self.assertIn(src, cache)
 
1150     def test_cache_single_file_already_cached(self) -> None:
 
1151         mode = black.FileMode()
 
1152         with cache_dir() as workspace:
 
1153             src = (workspace / "test.py").resolve()
 
1154             with src.open("w") as fobj:
 
1155                 fobj.write("print('hello')")
 
1156             black.write_cache({}, [src], mode)
 
1157             self.invokeBlack([str(src)])
 
1158             with src.open("r") as fobj:
 
1159                 self.assertEqual(fobj.read(), "print('hello')")
 
1161     @event_loop(close=False)
 
1162     def test_cache_multiple_files(self) -> None:
 
1163         mode = black.FileMode()
 
1164         with cache_dir() as workspace, patch(
 
1165             "black.ProcessPoolExecutor", new=ThreadPoolExecutor
 
1167             one = (workspace / "one.py").resolve()
 
1168             with one.open("w") as fobj:
 
1169                 fobj.write("print('hello')")
 
1170             two = (workspace / "two.py").resolve()
 
1171             with two.open("w") as fobj:
 
1172                 fobj.write("print('hello')")
 
1173             black.write_cache({}, [one], mode)
 
1174             self.invokeBlack([str(workspace)])
 
1175             with one.open("r") as fobj:
 
1176                 self.assertEqual(fobj.read(), "print('hello')")
 
1177             with two.open("r") as fobj:
 
1178                 self.assertEqual(fobj.read(), 'print("hello")\n')
 
1179             cache = black.read_cache(mode)
 
1180             self.assertIn(one, cache)
 
1181             self.assertIn(two, cache)
 
1183     def test_no_cache_when_writeback_diff(self) -> None:
 
1184         mode = black.FileMode()
 
1185         with cache_dir() as workspace:
 
1186             src = (workspace / "test.py").resolve()
 
1187             with src.open("w") as fobj:
 
1188                 fobj.write("print('hello')")
 
1189             self.invokeBlack([str(src), "--diff"])
 
1190             cache_file = black.get_cache_file(mode)
 
1191             self.assertFalse(cache_file.exists())
 
1193     def test_no_cache_when_stdin(self) -> None:
 
1194         mode = black.FileMode()
 
1196             result = CliRunner().invoke(
 
1197                 black.main, ["-"], input=BytesIO(b"print('hello')")
 
1199             self.assertEqual(result.exit_code, 0)
 
1200             cache_file = black.get_cache_file(mode)
 
1201             self.assertFalse(cache_file.exists())
 
1203     def test_read_cache_no_cachefile(self) -> None:
 
1204         mode = black.FileMode()
 
1206             self.assertEqual(black.read_cache(mode), {})
 
1208     def test_write_cache_read_cache(self) -> None:
 
1209         mode = black.FileMode()
 
1210         with cache_dir() as workspace:
 
1211             src = (workspace / "test.py").resolve()
 
1213             black.write_cache({}, [src], mode)
 
1214             cache = black.read_cache(mode)
 
1215             self.assertIn(src, cache)
 
1216             self.assertEqual(cache[src], black.get_cache_info(src))
 
1218     def test_filter_cached(self) -> None:
 
1219         with TemporaryDirectory() as workspace:
 
1220             path = Path(workspace)
 
1221             uncached = (path / "uncached").resolve()
 
1222             cached = (path / "cached").resolve()
 
1223             cached_but_changed = (path / "changed").resolve()
 
1226             cached_but_changed.touch()
 
1227             cache = {cached: black.get_cache_info(cached), cached_but_changed: (0.0, 0)}
 
1228             todo, done = black.filter_cached(
 
1229                 cache, {uncached, cached, cached_but_changed}
 
1231             self.assertEqual(todo, {uncached, cached_but_changed})
 
1232             self.assertEqual(done, {cached})
 
1234     def test_write_cache_creates_directory_if_needed(self) -> None:
 
1235         mode = black.FileMode()
 
1236         with cache_dir(exists=False) as workspace:
 
1237             self.assertFalse(workspace.exists())
 
1238             black.write_cache({}, [], mode)
 
1239             self.assertTrue(workspace.exists())
 
1241     @event_loop(close=False)
 
1242     def test_failed_formatting_does_not_get_cached(self) -> None:
 
1243         mode = black.FileMode()
 
1244         with cache_dir() as workspace, patch(
 
1245             "black.ProcessPoolExecutor", new=ThreadPoolExecutor
 
1247             failing = (workspace / "failing.py").resolve()
 
1248             with failing.open("w") as fobj:
 
1249                 fobj.write("not actually python")
 
1250             clean = (workspace / "clean.py").resolve()
 
1251             with clean.open("w") as fobj:
 
1252                 fobj.write('print("hello")\n')
 
1253             self.invokeBlack([str(workspace)], exit_code=123)
 
1254             cache = black.read_cache(mode)
 
1255             self.assertNotIn(failing, cache)
 
1256             self.assertIn(clean, cache)
 
1258     def test_write_cache_write_fail(self) -> None:
 
1259         mode = black.FileMode()
 
1260         with cache_dir(), patch.object(Path, "open") as mock:
 
1261             mock.side_effect = OSError
 
1262             black.write_cache({}, [], mode)
 
1264     @event_loop(close=False)
 
1265     def test_check_diff_use_together(self) -> None:
 
1267             # Files which will be reformatted.
 
1268             src1 = (THIS_DIR / "data" / "string_quotes.py").resolve()
 
1269             self.invokeBlack([str(src1), "--diff", "--check"], exit_code=1)
 
1270             # Files which will not be reformatted.
 
1271             src2 = (THIS_DIR / "data" / "composition.py").resolve()
 
1272             self.invokeBlack([str(src2), "--diff", "--check"])
 
1273             # Multi file command.
 
1274             self.invokeBlack([str(src1), str(src2), "--diff", "--check"], exit_code=1)
 
1276     def test_no_files(self) -> None:
 
1278             # Without an argument, black exits with error code 0.
 
1279             self.invokeBlack([])
 
1281     def test_broken_symlink(self) -> None:
 
1282         with cache_dir() as workspace:
 
1283             symlink = workspace / "broken_link.py"
 
1285                 symlink.symlink_to("nonexistent.py")
 
1286             except OSError as e:
 
1287                 self.skipTest(f"Can't create symlinks: {e}")
 
1288             self.invokeBlack([str(workspace.resolve())])
 
1290     def test_read_cache_line_lengths(self) -> None:
 
1291         mode = black.FileMode()
 
1292         short_mode = black.FileMode(line_length=1)
 
1293         with cache_dir() as workspace:
 
1294             path = (workspace / "file.py").resolve()
 
1296             black.write_cache({}, [path], mode)
 
1297             one = black.read_cache(mode)
 
1298             self.assertIn(path, one)
 
1299             two = black.read_cache(short_mode)
 
1300             self.assertNotIn(path, two)
 
1302     def test_tricky_unicode_symbols(self) -> None:
 
1303         source, expected = read_data("tricky_unicode_symbols")
 
1305         self.assertFormatEqual(expected, actual)
 
1306         black.assert_equivalent(source, actual)
 
1307         black.assert_stable(source, actual, black.FileMode())
 
1309     def test_single_file_force_pyi(self) -> None:
 
1310         reg_mode = black.FileMode()
 
1311         pyi_mode = black.FileMode(is_pyi=True)
 
1312         contents, expected = read_data("force_pyi")
 
1313         with cache_dir() as workspace:
 
1314             path = (workspace / "file.py").resolve()
 
1315             with open(path, "w") as fh:
 
1317             self.invokeBlack([str(path), "--pyi"])
 
1318             with open(path, "r") as fh:
 
1320             # verify cache with --pyi is separate
 
1321             pyi_cache = black.read_cache(pyi_mode)
 
1322             self.assertIn(path, pyi_cache)
 
1323             normal_cache = black.read_cache(reg_mode)
 
1324             self.assertNotIn(path, normal_cache)
 
1325         self.assertEqual(actual, expected)
 
1327     @event_loop(close=False)
 
1328     def test_multi_file_force_pyi(self) -> None:
 
1329         reg_mode = black.FileMode()
 
1330         pyi_mode = black.FileMode(is_pyi=True)
 
1331         contents, expected = read_data("force_pyi")
 
1332         with cache_dir() as workspace:
 
1334                 (workspace / "file1.py").resolve(),
 
1335                 (workspace / "file2.py").resolve(),
 
1338                 with open(path, "w") as fh:
 
1340             self.invokeBlack([str(p) for p in paths] + ["--pyi"])
 
1342                 with open(path, "r") as fh:
 
1344                 self.assertEqual(actual, expected)
 
1345             # verify cache with --pyi is separate
 
1346             pyi_cache = black.read_cache(pyi_mode)
 
1347             normal_cache = black.read_cache(reg_mode)
 
1349                 self.assertIn(path, pyi_cache)
 
1350                 self.assertNotIn(path, normal_cache)
 
1352     def test_pipe_force_pyi(self) -> None:
 
1353         source, expected = read_data("force_pyi")
 
1354         result = CliRunner().invoke(
 
1355             black.main, ["-", "-q", "--pyi"], input=BytesIO(source.encode("utf8"))
 
1357         self.assertEqual(result.exit_code, 0)
 
1358         actual = result.output
 
1359         self.assertFormatEqual(actual, expected)
 
1361     def test_single_file_force_py36(self) -> None:
 
1362         reg_mode = black.FileMode()
 
1363         py36_mode = black.FileMode(target_versions=black.PY36_VERSIONS)
 
1364         source, expected = read_data("force_py36")
 
1365         with cache_dir() as workspace:
 
1366             path = (workspace / "file.py").resolve()
 
1367             with open(path, "w") as fh:
 
1369             self.invokeBlack([str(path), *PY36_ARGS])
 
1370             with open(path, "r") as fh:
 
1372             # verify cache with --target-version is separate
 
1373             py36_cache = black.read_cache(py36_mode)
 
1374             self.assertIn(path, py36_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_py36(self) -> None:
 
1381         reg_mode = black.FileMode()
 
1382         py36_mode = black.FileMode(target_versions=black.PY36_VERSIONS)
 
1383         source, expected = read_data("force_py36")
 
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] + PY36_ARGS)
 
1394                 with open(path, "r") as fh:
 
1396                 self.assertEqual(actual, expected)
 
1397             # verify cache with --target-version is separate
 
1398             pyi_cache = black.read_cache(py36_mode)
 
1399             normal_cache = black.read_cache(reg_mode)
 
1401                 self.assertIn(path, pyi_cache)
 
1402                 self.assertNotIn(path, normal_cache)
 
1404     def test_collections(self) -> None:
 
1405         source, expected = read_data("collections")
 
1407         self.assertFormatEqual(expected, actual)
 
1408         black.assert_equivalent(source, actual)
 
1409         black.assert_stable(source, actual, black.FileMode())
 
1411     def test_pipe_force_py36(self) -> None:
 
1412         source, expected = read_data("force_py36")
 
1413         result = CliRunner().invoke(
 
1415             ["-", "-q", "--target-version=py36"],
 
1416             input=BytesIO(source.encode("utf8")),
 
1418         self.assertEqual(result.exit_code, 0)
 
1419         actual = result.output
 
1420         self.assertFormatEqual(actual, expected)
 
1422     def test_include_exclude(self) -> None:
 
1423         path = THIS_DIR / "data" / "include_exclude_tests"
 
1424         include = re.compile(r"\.pyi?$")
 
1425         exclude = re.compile(r"/exclude/|/\.definitely_exclude/")
 
1426         report = black.Report()
 
1427         gitignore = PathSpec.from_lines("gitwildmatch", [])
 
1428         sources: List[Path] = []
 
1430             Path(path / "b/dont_exclude/a.py"),
 
1431             Path(path / "b/dont_exclude/a.pyi"),
 
1433         this_abs = THIS_DIR.resolve()
 
1435             black.gen_python_files_in_dir(
 
1436                 path, this_abs, include, exclude, report, gitignore
 
1439         self.assertEqual(sorted(expected), sorted(sources))
 
1441     def test_gitignore_exclude(self) -> None:
 
1442         path = THIS_DIR / "data" / "include_exclude_tests"
 
1443         include = re.compile(r"\.pyi?$")
 
1444         exclude = re.compile(r"")
 
1445         report = black.Report()
 
1446         gitignore = PathSpec.from_lines(
 
1447             "gitwildmatch", ["exclude/", ".definitely_exclude"]
 
1449         sources: List[Path] = []
 
1451             Path(path / "b/dont_exclude/a.py"),
 
1452             Path(path / "b/dont_exclude/a.pyi"),
 
1454         this_abs = THIS_DIR.resolve()
 
1456             black.gen_python_files_in_dir(
 
1457                 path, this_abs, include, exclude, report, gitignore
 
1460         self.assertEqual(sorted(expected), sorted(sources))
 
1462     def test_empty_include(self) -> None:
 
1463         path = THIS_DIR / "data" / "include_exclude_tests"
 
1464         report = black.Report()
 
1465         gitignore = PathSpec.from_lines("gitwildmatch", [])
 
1466         empty = re.compile(r"")
 
1467         sources: List[Path] = []
 
1469             Path(path / "b/exclude/a.pie"),
 
1470             Path(path / "b/exclude/a.py"),
 
1471             Path(path / "b/exclude/a.pyi"),
 
1472             Path(path / "b/dont_exclude/a.pie"),
 
1473             Path(path / "b/dont_exclude/a.py"),
 
1474             Path(path / "b/dont_exclude/a.pyi"),
 
1475             Path(path / "b/.definitely_exclude/a.pie"),
 
1476             Path(path / "b/.definitely_exclude/a.py"),
 
1477             Path(path / "b/.definitely_exclude/a.pyi"),
 
1479         this_abs = THIS_DIR.resolve()
 
1481             black.gen_python_files_in_dir(
 
1485                 re.compile(black.DEFAULT_EXCLUDES),
 
1490         self.assertEqual(sorted(expected), sorted(sources))
 
1492     def test_empty_exclude(self) -> None:
 
1493         path = THIS_DIR / "data" / "include_exclude_tests"
 
1494         report = black.Report()
 
1495         gitignore = PathSpec.from_lines("gitwildmatch", [])
 
1496         empty = re.compile(r"")
 
1497         sources: List[Path] = []
 
1499             Path(path / "b/dont_exclude/a.py"),
 
1500             Path(path / "b/dont_exclude/a.pyi"),
 
1501             Path(path / "b/exclude/a.py"),
 
1502             Path(path / "b/exclude/a.pyi"),
 
1503             Path(path / "b/.definitely_exclude/a.py"),
 
1504             Path(path / "b/.definitely_exclude/a.pyi"),
 
1506         this_abs = THIS_DIR.resolve()
 
1508             black.gen_python_files_in_dir(
 
1511                 re.compile(black.DEFAULT_INCLUDES),
 
1517         self.assertEqual(sorted(expected), sorted(sources))
 
1519     def test_invalid_include_exclude(self) -> None:
 
1520         for option in ["--include", "--exclude"]:
 
1521             self.invokeBlack(["-", option, "**()(!!*)"], exit_code=2)
 
1523     def test_preserves_line_endings(self) -> None:
 
1524         with TemporaryDirectory() as workspace:
 
1525             test_file = Path(workspace) / "test.py"
 
1526             for nl in ["\n", "\r\n"]:
 
1527                 contents = nl.join(["def f(  ):", "    pass"])
 
1528                 test_file.write_bytes(contents.encode())
 
1529                 ff(test_file, write_back=black.WriteBack.YES)
 
1530                 updated_contents: bytes = test_file.read_bytes()
 
1531                 self.assertIn(nl.encode(), updated_contents)
 
1533                     self.assertNotIn(b"\r\n", updated_contents)
 
1535     def test_preserves_line_endings_via_stdin(self) -> None:
 
1536         for nl in ["\n", "\r\n"]:
 
1537             contents = nl.join(["def f(  ):", "    pass"])
 
1538             runner = BlackRunner()
 
1539             result = runner.invoke(
 
1540                 black.main, ["-", "--fast"], input=BytesIO(contents.encode("utf8"))
 
1542             self.assertEqual(result.exit_code, 0)
 
1543             output = runner.stdout_bytes
 
1544             self.assertIn(nl.encode("utf8"), output)
 
1546                 self.assertNotIn(b"\r\n", output)
 
1548     def test_assert_equivalent_different_asts(self) -> None:
 
1549         with self.assertRaises(AssertionError):
 
1550             black.assert_equivalent("{}", "None")
 
1552     def test_symlink_out_of_root_directory(self) -> None:
 
1556         include = re.compile(black.DEFAULT_INCLUDES)
 
1557         exclude = re.compile(black.DEFAULT_EXCLUDES)
 
1558         report = black.Report()
 
1559         gitignore = PathSpec.from_lines("gitwildmatch", [])
 
1560         # `child` should behave like a symlink which resolved path is clearly
 
1561         # outside of the `root` directory.
 
1562         path.iterdir.return_value = [child]
 
1563         child.resolve.return_value = Path("/a/b/c")
 
1564         child.as_posix.return_value = "/a/b/c"
 
1565         child.is_symlink.return_value = True
 
1568                 black.gen_python_files_in_dir(
 
1569                     path, root, include, exclude, report, gitignore
 
1572         except ValueError as ve:
 
1573             self.fail(f"`get_python_files_in_dir()` failed: {ve}")
 
1574         path.iterdir.assert_called_once()
 
1575         child.resolve.assert_called_once()
 
1576         child.is_symlink.assert_called_once()
 
1577         # `child` should behave like a strange file which resolved path is clearly
 
1578         # outside of the `root` directory.
 
1579         child.is_symlink.return_value = False
 
1580         with self.assertRaises(ValueError):
 
1582                 black.gen_python_files_in_dir(
 
1583                     path, root, include, exclude, report, gitignore
 
1586         path.iterdir.assert_called()
 
1587         self.assertEqual(path.iterdir.call_count, 2)
 
1588         child.resolve.assert_called()
 
1589         self.assertEqual(child.resolve.call_count, 2)
 
1590         child.is_symlink.assert_called()
 
1591         self.assertEqual(child.is_symlink.call_count, 2)
 
1593     def test_shhh_click(self) -> None:
 
1595             from click import _unicodefun  # type: ignore
 
1596         except ModuleNotFoundError:
 
1597             self.skipTest("Incompatible Click version")
 
1598         if not hasattr(_unicodefun, "_verify_python3_env"):
 
1599             self.skipTest("Incompatible Click version")
 
1600         # First, let's see if Click is crashing with a preferred ASCII charset.
 
1601         with patch("locale.getpreferredencoding") as gpe:
 
1602             gpe.return_value = "ASCII"
 
1603             with self.assertRaises(RuntimeError):
 
1604                 _unicodefun._verify_python3_env()
 
1605         # Now, let's silence Click...
 
1607         # ...and confirm it's silent.
 
1608         with patch("locale.getpreferredencoding") as gpe:
 
1609             gpe.return_value = "ASCII"
 
1611                 _unicodefun._verify_python3_env()
 
1612             except RuntimeError as re:
 
1613                 self.fail(f"`patch_click()` failed, exception still raised: {re}")
 
1615     def test_root_logger_not_used_directly(self) -> None:
 
1616         def fail(*args: Any, **kwargs: Any) -> None:
 
1617             self.fail("Record created with root logger")
 
1619         with patch.multiple(
 
1630     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1631     def test_blackd_main(self) -> None:
 
1632         with patch("blackd.web.run_app"):
 
1633             result = CliRunner().invoke(blackd.main, [])
 
1634             if result.exception is not None:
 
1635                 raise result.exception
 
1636             self.assertEqual(result.exit_code, 0)
 
1639 class BlackDTestCase(AioHTTPTestCase):
 
1640     async def get_application(self) -> web.Application:
 
1641         return blackd.make_app()
 
1643     # TODO: remove these decorators once the below is released
 
1644     # https://github.com/aio-libs/aiohttp/pull/3727
 
1645     @skip_if_exception("ClientOSError")
 
1646     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1648     async def test_blackd_request_needs_formatting(self) -> None:
 
1649         response = await self.client.post("/", data=b"print('hello world')")
 
1650         self.assertEqual(response.status, 200)
 
1651         self.assertEqual(response.charset, "utf8")
 
1652         self.assertEqual(await response.read(), b'print("hello world")\n')
 
1654     @skip_if_exception("ClientOSError")
 
1655     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1657     async def test_blackd_request_no_change(self) -> None:
 
1658         response = await self.client.post("/", data=b'print("hello world")\n')
 
1659         self.assertEqual(response.status, 204)
 
1660         self.assertEqual(await response.read(), b"")
 
1662     @skip_if_exception("ClientOSError")
 
1663     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1665     async def test_blackd_request_syntax_error(self) -> None:
 
1666         response = await self.client.post("/", data=b"what even ( is")
 
1667         self.assertEqual(response.status, 400)
 
1668         content = await response.text()
 
1670             content.startswith("Cannot parse"),
 
1671             msg=f"Expected error to start with 'Cannot parse', got {repr(content)}",
 
1674     @skip_if_exception("ClientOSError")
 
1675     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1677     async def test_blackd_unsupported_version(self) -> None:
 
1678         response = await self.client.post(
 
1679             "/", data=b"what", headers={blackd.PROTOCOL_VERSION_HEADER: "2"}
 
1681         self.assertEqual(response.status, 501)
 
1683     @skip_if_exception("ClientOSError")
 
1684     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1686     async def test_blackd_supported_version(self) -> None:
 
1687         response = await self.client.post(
 
1688             "/", data=b"what", headers={blackd.PROTOCOL_VERSION_HEADER: "1"}
 
1690         self.assertEqual(response.status, 200)
 
1692     @skip_if_exception("ClientOSError")
 
1693     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1695     async def test_blackd_invalid_python_variant(self) -> None:
 
1696         async def check(header_value: str, expected_status: int = 400) -> None:
 
1697             response = await self.client.post(
 
1698                 "/", data=b"what", headers={blackd.PYTHON_VARIANT_HEADER: header_value}
 
1700             self.assertEqual(response.status, expected_status)
 
1703         await check("ruby3.5")
 
1704         await check("pyi3.6")
 
1705         await check("py1.5")
 
1707         await check("py2.8")
 
1709         await check("pypy3.0")
 
1710         await check("jython3.4")
 
1712     @skip_if_exception("ClientOSError")
 
1713     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1715     async def test_blackd_pyi(self) -> None:
 
1716         source, expected = read_data("stub.pyi")
 
1717         response = await self.client.post(
 
1718             "/", data=source, headers={blackd.PYTHON_VARIANT_HEADER: "pyi"}
 
1720         self.assertEqual(response.status, 200)
 
1721         self.assertEqual(await response.text(), expected)
 
1723     @skip_if_exception("ClientOSError")
 
1724     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1726     async def test_blackd_diff(self) -> None:
 
1727         diff_header = re.compile(
 
1728             rf"(In|Out)\t\d\d\d\d-\d\d-\d\d "
 
1729             rf"\d\d:\d\d:\d\d\.\d\d\d\d\d\d \+\d\d\d\d"
 
1732         source, _ = read_data("blackd_diff.py")
 
1733         expected, _ = read_data("blackd_diff.diff")
 
1735         response = await self.client.post(
 
1736             "/", data=source, headers={blackd.DIFF_HEADER: "true"}
 
1738         self.assertEqual(response.status, 200)
 
1740         actual = await response.text()
 
1741         actual = diff_header.sub(DETERMINISTIC_HEADER, actual)
 
1742         self.assertEqual(actual, expected)
 
1744     @skip_if_exception("ClientOSError")
 
1745     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1747     async def test_blackd_python_variant(self) -> None:
 
1750             "    and_has_a_bunch_of,\n"
 
1751             "    very_long_arguments_too,\n"
 
1752             "    and_lots_of_them_as_well_lol,\n"
 
1753             "    **and_very_long_keyword_arguments\n"
 
1758         async def check(header_value: str, expected_status: int) -> None:
 
1759             response = await self.client.post(
 
1760                 "/", data=code, headers={blackd.PYTHON_VARIANT_HEADER: header_value}
 
1763                 response.status, expected_status, msg=await response.text()
 
1766         await check("3.6", 200)
 
1767         await check("py3.6", 200)
 
1768         await check("3.6,3.7", 200)
 
1769         await check("3.6,py3.7", 200)
 
1770         await check("py36,py37", 200)
 
1771         await check("36", 200)
 
1772         await check("3.6.4", 200)
 
1774         await check("2", 204)
 
1775         await check("2.7", 204)
 
1776         await check("py2.7", 204)
 
1777         await check("3.4", 204)
 
1778         await check("py3.4", 204)
 
1779         await check("py34,py36", 204)
 
1780         await check("34", 204)
 
1782     @skip_if_exception("ClientOSError")
 
1783     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1785     async def test_blackd_line_length(self) -> None:
 
1786         response = await self.client.post(
 
1787             "/", data=b'print("hello")\n', headers={blackd.LINE_LENGTH_HEADER: "7"}
 
1789         self.assertEqual(response.status, 200)
 
1791     @skip_if_exception("ClientOSError")
 
1792     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1794     async def test_blackd_invalid_line_length(self) -> None:
 
1795         response = await self.client.post(
 
1796             "/", data=b'print("hello")\n', headers={blackd.LINE_LENGTH_HEADER: "NaN"}
 
1798         self.assertEqual(response.status, 400)
 
1800     @skip_if_exception("ClientOSError")
 
1801     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1803     async def test_blackd_response_black_version_header(self) -> None:
 
1804         response = await self.client.post("/")
 
1805         self.assertIsNotNone(response.headers.get(blackd.BLACK_VERSION_HEADER))
 
1808 if __name__ == "__main__":
 
1809     unittest.main(module="test_black")