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 dataclasses import replace
 
   7 from functools import partial
 
   8 from io import BytesIO, TextIOWrapper
 
  10 from pathlib import Path
 
  13 from tempfile import TemporaryDirectory
 
  14 from typing import Any, BinaryIO, Dict, Generator, List, Tuple, Iterator, TypeVar
 
  16 from unittest.mock import patch, MagicMock
 
  19 from click import unstyle
 
  20 from click.testing import CliRunner
 
  23 from black import Feature, TargetVersion
 
  27     from aiohttp.test_utils import AioHTTPTestCase, unittest_run_loop
 
  28     from aiohttp import web
 
  30     has_blackd_deps = False
 
  32     has_blackd_deps = True
 
  34 from pathspec import PathSpec
 
  36 # Import other test classes
 
  37 from .test_primer import PrimerCLITests  # noqa: F401
 
  40 DEFAULT_MODE = black.FileMode(experimental_string_processing=True)
 
  41 ff = partial(black.format_file_in_place, mode=DEFAULT_MODE, fast=True)
 
  42 fs = partial(black.format_str, mode=DEFAULT_MODE)
 
  43 THIS_FILE = Path(__file__)
 
  44 THIS_DIR = THIS_FILE.parent
 
  45 PROJECT_ROOT = THIS_DIR.parent
 
  46 DETERMINISTIC_HEADER = "[Deterministic header]"
 
  47 EMPTY_LINE = "# EMPTY LINE WITH WHITESPACE" + " (this comment will be removed)"
 
  49     f"--target-version={version.name.lower()}" for version in black.PY36_VERSIONS
 
  55 def dump_to_stderr(*output: str) -> str:
 
  56     return "\n" + "\n".join(output) + "\n"
 
  59 def read_data(name: str, data: bool = True) -> Tuple[str, str]:
 
  60     """read_data('test_name') -> 'input', 'output'"""
 
  61     if not name.endswith((".py", ".pyi", ".out", ".diff")):
 
  63     _input: List[str] = []
 
  64     _output: List[str] = []
 
  65     base_dir = THIS_DIR / "data" if data else PROJECT_ROOT
 
  66     with open(base_dir / name, "r", encoding="utf8") as test:
 
  67         lines = test.readlines()
 
  70         line = line.replace(EMPTY_LINE, "")
 
  71         if line.rstrip() == "# output":
 
  76     if _input and not _output:
 
  77         # If there's no output marker, treat the entire file as already pre-formatted.
 
  79     return "".join(_input).strip() + "\n", "".join(_output).strip() + "\n"
 
  83 def cache_dir(exists: bool = True) -> Iterator[Path]:
 
  84     with TemporaryDirectory() as workspace:
 
  85         cache_dir = Path(workspace)
 
  87             cache_dir = cache_dir / "new"
 
  88         with patch("black.CACHE_DIR", cache_dir):
 
  93 def event_loop() -> Iterator[None]:
 
  94     policy = asyncio.get_event_loop_policy()
 
  95     loop = policy.new_event_loop()
 
  96     asyncio.set_event_loop(loop)
 
 105 def skip_if_exception(e: str) -> Iterator[None]:
 
 108     except Exception as exc:
 
 109         if exc.__class__.__name__ == e:
 
 110             unittest.skip(f"Encountered expected exception {exc}, skipping")
 
 115 class FakeContext(click.Context):
 
 116     """A fake click Context for when calling functions that need it."""
 
 118     def __init__(self) -> None:
 
 119         self.default_map: Dict[str, Any] = {}
 
 122 class FakeParameter(click.Parameter):
 
 123     """A fake click Parameter for when calling functions that need it."""
 
 125     def __init__(self) -> None:
 
 129 class BlackRunner(CliRunner):
 
 130     """Modify CliRunner so that stderr is not merged with stdout.
 
 132     This is a hack that can be removed once we depend on Click 7.x"""
 
 134     def __init__(self) -> None:
 
 135         self.stderrbuf = BytesIO()
 
 136         self.stdoutbuf = BytesIO()
 
 137         self.stdout_bytes = b""
 
 138         self.stderr_bytes = b""
 
 142     def isolation(self, *args: Any, **kwargs: Any) -> Generator[BinaryIO, None, None]:
 
 143         with super().isolation(*args, **kwargs) as output:
 
 145                 hold_stderr = sys.stderr
 
 146                 sys.stderr = TextIOWrapper(self.stderrbuf, encoding=self.charset)
 
 149                 self.stdout_bytes = sys.stdout.buffer.getvalue()  # type: ignore
 
 150                 self.stderr_bytes = sys.stderr.buffer.getvalue()  # type: ignore
 
 151                 sys.stderr = hold_stderr
 
 154 class BlackTestCase(unittest.TestCase):
 
 157     def assertFormatEqual(self, expected: str, actual: str) -> None:
 
 158         if actual != expected and not os.environ.get("SKIP_AST_PRINT"):
 
 159             bdv: black.DebugVisitor[Any]
 
 160             black.out("Expected tree:", fg="green")
 
 162                 exp_node = black.lib2to3_parse(expected)
 
 163                 bdv = black.DebugVisitor()
 
 164                 list(bdv.visit(exp_node))
 
 165             except Exception as ve:
 
 167             black.out("Actual tree:", fg="red")
 
 169                 exp_node = black.lib2to3_parse(actual)
 
 170                 bdv = black.DebugVisitor()
 
 171                 list(bdv.visit(exp_node))
 
 172             except Exception as ve:
 
 174         self.assertEqual(expected, actual)
 
 177         self, args: List[str], exit_code: int = 0, ignore_config: bool = True
 
 179         runner = BlackRunner()
 
 181             args = ["--verbose", "--config", str(THIS_DIR / "empty.toml"), *args]
 
 182         result = runner.invoke(black.main, args)
 
 187                 f"Failed with args: {args}\n"
 
 188                 f"stdout: {runner.stdout_bytes.decode()!r}\n"
 
 189                 f"stderr: {runner.stderr_bytes.decode()!r}\n"
 
 190                 f"exception: {result.exception}"
 
 194     @patch("black.dump_to_file", dump_to_stderr)
 
 195     def checkSourceFile(self, name: str, mode: black.FileMode = DEFAULT_MODE) -> None:
 
 196         path = THIS_DIR.parent / name
 
 197         source, expected = read_data(str(path), data=False)
 
 198         actual = fs(source, mode=mode)
 
 199         self.assertFormatEqual(expected, actual)
 
 200         black.assert_equivalent(source, actual)
 
 201         black.assert_stable(source, actual, mode)
 
 202         self.assertFalse(ff(path))
 
 204     @patch("black.dump_to_file", dump_to_stderr)
 
 205     def test_empty(self) -> None:
 
 206         source = expected = ""
 
 208         self.assertFormatEqual(expected, actual)
 
 209         black.assert_equivalent(source, actual)
 
 210         black.assert_stable(source, actual, DEFAULT_MODE)
 
 212     def test_empty_ff(self) -> None:
 
 214         tmp_file = Path(black.dump_to_file())
 
 216             self.assertFalse(ff(tmp_file, write_back=black.WriteBack.YES))
 
 217             with open(tmp_file, encoding="utf8") as f:
 
 221         self.assertFormatEqual(expected, actual)
 
 223     def test_self(self) -> None:
 
 224         self.checkSourceFile("tests/test_black.py")
 
 226     def test_black(self) -> None:
 
 227         self.checkSourceFile("src/black/__init__.py")
 
 229     def test_pygram(self) -> None:
 
 230         self.checkSourceFile("src/blib2to3/pygram.py")
 
 232     def test_pytree(self) -> None:
 
 233         self.checkSourceFile("src/blib2to3/pytree.py")
 
 235     def test_conv(self) -> None:
 
 236         self.checkSourceFile("src/blib2to3/pgen2/conv.py")
 
 238     def test_driver(self) -> None:
 
 239         self.checkSourceFile("src/blib2to3/pgen2/driver.py")
 
 241     def test_grammar(self) -> None:
 
 242         self.checkSourceFile("src/blib2to3/pgen2/grammar.py")
 
 244     def test_literals(self) -> None:
 
 245         self.checkSourceFile("src/blib2to3/pgen2/literals.py")
 
 247     def test_parse(self) -> None:
 
 248         self.checkSourceFile("src/blib2to3/pgen2/parse.py")
 
 250     def test_pgen(self) -> None:
 
 251         self.checkSourceFile("src/blib2to3/pgen2/pgen.py")
 
 253     def test_tokenize(self) -> None:
 
 254         self.checkSourceFile("src/blib2to3/pgen2/tokenize.py")
 
 256     def test_token(self) -> None:
 
 257         self.checkSourceFile("src/blib2to3/pgen2/token.py")
 
 259     def test_setup(self) -> None:
 
 260         self.checkSourceFile("setup.py")
 
 262     def test_piping(self) -> None:
 
 263         source, expected = read_data("src/black/__init__", data=False)
 
 264         result = BlackRunner().invoke(
 
 266             ["-", "--fast", f"--line-length={black.DEFAULT_LINE_LENGTH}"],
 
 267             input=BytesIO(source.encode("utf8")),
 
 269         self.assertEqual(result.exit_code, 0)
 
 270         self.assertFormatEqual(expected, result.output)
 
 271         black.assert_equivalent(source, result.output)
 
 272         black.assert_stable(source, result.output, DEFAULT_MODE)
 
 274     def test_piping_diff(self) -> None:
 
 275         diff_header = re.compile(
 
 276             r"(STDIN|STDOUT)\t\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d\.\d\d\d\d\d\d "
 
 279         source, _ = read_data("expression.py")
 
 280         expected, _ = read_data("expression.diff")
 
 281         config = THIS_DIR / "data" / "empty_pyproject.toml"
 
 285             f"--line-length={black.DEFAULT_LINE_LENGTH}",
 
 287             f"--config={config}",
 
 289         result = BlackRunner().invoke(
 
 290             black.main, args, input=BytesIO(source.encode("utf8"))
 
 292         self.assertEqual(result.exit_code, 0)
 
 293         actual = diff_header.sub(DETERMINISTIC_HEADER, result.output)
 
 294         actual = actual.rstrip() + "\n"  # the diff output has a trailing space
 
 295         self.assertEqual(expected, actual)
 
 297     def test_piping_diff_with_color(self) -> None:
 
 298         source, _ = read_data("expression.py")
 
 299         config = THIS_DIR / "data" / "empty_pyproject.toml"
 
 303             f"--line-length={black.DEFAULT_LINE_LENGTH}",
 
 306             f"--config={config}",
 
 308         result = BlackRunner().invoke(
 
 309             black.main, args, input=BytesIO(source.encode("utf8"))
 
 311         actual = result.output
 
 312         # Again, the contents are checked in a different test, so only look for colors.
 
 313         self.assertIn("\033[1;37m", actual)
 
 314         self.assertIn("\033[36m", actual)
 
 315         self.assertIn("\033[32m", actual)
 
 316         self.assertIn("\033[31m", actual)
 
 317         self.assertIn("\033[0m", actual)
 
 319     @patch("black.dump_to_file", dump_to_stderr)
 
 320     def test_function(self) -> None:
 
 321         source, expected = read_data("function")
 
 323         self.assertFormatEqual(expected, actual)
 
 324         black.assert_equivalent(source, actual)
 
 325         black.assert_stable(source, actual, DEFAULT_MODE)
 
 327     @patch("black.dump_to_file", dump_to_stderr)
 
 328     def test_function2(self) -> None:
 
 329         source, expected = read_data("function2")
 
 331         self.assertFormatEqual(expected, actual)
 
 332         black.assert_equivalent(source, actual)
 
 333         black.assert_stable(source, actual, DEFAULT_MODE)
 
 335     @patch("black.dump_to_file", dump_to_stderr)
 
 336     def test_function_trailing_comma(self) -> None:
 
 337         source, expected = read_data("function_trailing_comma")
 
 339         self.assertFormatEqual(expected, actual)
 
 340         black.assert_equivalent(source, actual)
 
 341         black.assert_stable(source, actual, DEFAULT_MODE)
 
 343     @patch("black.dump_to_file", dump_to_stderr)
 
 344     def test_expression(self) -> None:
 
 345         source, expected = read_data("expression")
 
 347         self.assertFormatEqual(expected, actual)
 
 348         black.assert_equivalent(source, actual)
 
 349         black.assert_stable(source, actual, DEFAULT_MODE)
 
 351     @patch("black.dump_to_file", dump_to_stderr)
 
 352     def test_pep_572(self) -> None:
 
 353         source, expected = read_data("pep_572")
 
 355         self.assertFormatEqual(expected, actual)
 
 356         black.assert_stable(source, actual, DEFAULT_MODE)
 
 357         if sys.version_info >= (3, 8):
 
 358             black.assert_equivalent(source, actual)
 
 360     def test_pep_572_version_detection(self) -> None:
 
 361         source, _ = read_data("pep_572")
 
 362         root = black.lib2to3_parse(source)
 
 363         features = black.get_features_used(root)
 
 364         self.assertIn(black.Feature.ASSIGNMENT_EXPRESSIONS, features)
 
 365         versions = black.detect_target_versions(root)
 
 366         self.assertIn(black.TargetVersion.PY38, versions)
 
 368     def test_expression_ff(self) -> None:
 
 369         source, expected = read_data("expression")
 
 370         tmp_file = Path(black.dump_to_file(source))
 
 372             self.assertTrue(ff(tmp_file, write_back=black.WriteBack.YES))
 
 373             with open(tmp_file, encoding="utf8") as f:
 
 377         self.assertFormatEqual(expected, actual)
 
 378         with patch("black.dump_to_file", dump_to_stderr):
 
 379             black.assert_equivalent(source, actual)
 
 380             black.assert_stable(source, actual, DEFAULT_MODE)
 
 382     def test_expression_diff(self) -> None:
 
 383         source, _ = read_data("expression.py")
 
 384         expected, _ = read_data("expression.diff")
 
 385         tmp_file = Path(black.dump_to_file(source))
 
 386         diff_header = re.compile(
 
 387             rf"{re.escape(str(tmp_file))}\t\d\d\d\d-\d\d-\d\d "
 
 388             r"\d\d:\d\d:\d\d\.\d\d\d\d\d\d \+\d\d\d\d"
 
 391             result = BlackRunner().invoke(black.main, ["--diff", str(tmp_file)])
 
 392             self.assertEqual(result.exit_code, 0)
 
 395         actual = result.output
 
 396         actual = diff_header.sub(DETERMINISTIC_HEADER, actual)
 
 397         actual = actual.rstrip() + "\n"  # the diff output has a trailing space
 
 398         if expected != actual:
 
 399             dump = black.dump_to_file(actual)
 
 401                 "Expected diff isn't equal to the actual. If you made changes to"
 
 402                 " expression.py and this is an anticipated difference, overwrite"
 
 403                 f" tests/data/expression.diff with {dump}"
 
 405             self.assertEqual(expected, actual, msg)
 
 407     def test_expression_diff_with_color(self) -> None:
 
 408         source, _ = read_data("expression.py")
 
 409         expected, _ = read_data("expression.diff")
 
 410         tmp_file = Path(black.dump_to_file(source))
 
 412             result = BlackRunner().invoke(
 
 413                 black.main, ["--diff", "--color", str(tmp_file)]
 
 417         actual = result.output
 
 418         # We check the contents of the diff in `test_expression_diff`. All
 
 419         # we need to check here is that color codes exist in the result.
 
 420         self.assertIn("\033[1;37m", actual)
 
 421         self.assertIn("\033[36m", actual)
 
 422         self.assertIn("\033[32m", actual)
 
 423         self.assertIn("\033[31m", actual)
 
 424         self.assertIn("\033[0m", actual)
 
 426     @patch("black.dump_to_file", dump_to_stderr)
 
 427     def test_fstring(self) -> None:
 
 428         source, expected = read_data("fstring")
 
 430         self.assertFormatEqual(expected, actual)
 
 431         black.assert_equivalent(source, actual)
 
 432         black.assert_stable(source, actual, DEFAULT_MODE)
 
 434     @patch("black.dump_to_file", dump_to_stderr)
 
 435     def test_pep_570(self) -> None:
 
 436         source, expected = read_data("pep_570")
 
 438         self.assertFormatEqual(expected, actual)
 
 439         black.assert_stable(source, actual, DEFAULT_MODE)
 
 440         if sys.version_info >= (3, 8):
 
 441             black.assert_equivalent(source, actual)
 
 443     def test_detect_pos_only_arguments(self) -> None:
 
 444         source, _ = read_data("pep_570")
 
 445         root = black.lib2to3_parse(source)
 
 446         features = black.get_features_used(root)
 
 447         self.assertIn(black.Feature.POS_ONLY_ARGUMENTS, features)
 
 448         versions = black.detect_target_versions(root)
 
 449         self.assertIn(black.TargetVersion.PY38, versions)
 
 451     @patch("black.dump_to_file", dump_to_stderr)
 
 452     def test_string_quotes(self) -> None:
 
 453         source, expected = read_data("string_quotes")
 
 455         self.assertFormatEqual(expected, actual)
 
 456         black.assert_equivalent(source, actual)
 
 457         black.assert_stable(source, actual, DEFAULT_MODE)
 
 458         mode = replace(DEFAULT_MODE, string_normalization=False)
 
 459         not_normalized = fs(source, mode=mode)
 
 460         self.assertFormatEqual(source.replace("\\\n", ""), not_normalized)
 
 461         black.assert_equivalent(source, not_normalized)
 
 462         black.assert_stable(source, not_normalized, mode=mode)
 
 464     @patch("black.dump_to_file", dump_to_stderr)
 
 465     def test_docstring(self) -> None:
 
 466         source, expected = read_data("docstring")
 
 468         self.assertFormatEqual(expected, actual)
 
 469         black.assert_equivalent(source, actual)
 
 470         black.assert_stable(source, actual, DEFAULT_MODE)
 
 472     def test_long_strings(self) -> None:
 
 473         """Tests for splitting long strings."""
 
 474         source, expected = read_data("long_strings")
 
 476         self.assertFormatEqual(expected, actual)
 
 477         black.assert_equivalent(source, actual)
 
 478         black.assert_stable(source, actual, DEFAULT_MODE)
 
 480     def test_long_strings_flag_disabled(self) -> None:
 
 481         """Tests for turning off the string processing logic."""
 
 482         source, expected = read_data("long_strings_flag_disabled")
 
 483         mode = replace(DEFAULT_MODE, experimental_string_processing=False)
 
 484         actual = fs(source, mode=mode)
 
 485         self.assertFormatEqual(expected, actual)
 
 486         black.assert_stable(expected, actual, mode)
 
 488     @patch("black.dump_to_file", dump_to_stderr)
 
 489     def test_long_strings__edge_case(self) -> None:
 
 490         """Edge-case tests for splitting long strings."""
 
 491         source, expected = read_data("long_strings__edge_case")
 
 493         self.assertFormatEqual(expected, actual)
 
 494         black.assert_equivalent(source, actual)
 
 495         black.assert_stable(source, actual, DEFAULT_MODE)
 
 497     @patch("black.dump_to_file", dump_to_stderr)
 
 498     def test_long_strings__regression(self) -> None:
 
 499         """Regression tests for splitting long strings."""
 
 500         source, expected = read_data("long_strings__regression")
 
 502         self.assertFormatEqual(expected, actual)
 
 503         black.assert_equivalent(source, actual)
 
 504         black.assert_stable(source, actual, DEFAULT_MODE)
 
 506     @patch("black.dump_to_file", dump_to_stderr)
 
 507     def test_slices(self) -> None:
 
 508         source, expected = read_data("slices")
 
 510         self.assertFormatEqual(expected, actual)
 
 511         black.assert_equivalent(source, actual)
 
 512         black.assert_stable(source, actual, DEFAULT_MODE)
 
 514     @patch("black.dump_to_file", dump_to_stderr)
 
 515     def test_percent_precedence(self) -> None:
 
 516         source, expected = read_data("percent_precedence")
 
 518         self.assertFormatEqual(expected, actual)
 
 519         black.assert_equivalent(source, actual)
 
 520         black.assert_stable(source, actual, DEFAULT_MODE)
 
 522     @patch("black.dump_to_file", dump_to_stderr)
 
 523     def test_comments(self) -> None:
 
 524         source, expected = read_data("comments")
 
 526         self.assertFormatEqual(expected, actual)
 
 527         black.assert_equivalent(source, actual)
 
 528         black.assert_stable(source, actual, DEFAULT_MODE)
 
 530     @patch("black.dump_to_file", dump_to_stderr)
 
 531     def test_comments2(self) -> None:
 
 532         source, expected = read_data("comments2")
 
 534         self.assertFormatEqual(expected, actual)
 
 535         black.assert_equivalent(source, actual)
 
 536         black.assert_stable(source, actual, DEFAULT_MODE)
 
 538     @patch("black.dump_to_file", dump_to_stderr)
 
 539     def test_comments3(self) -> None:
 
 540         source, expected = read_data("comments3")
 
 542         self.assertFormatEqual(expected, actual)
 
 543         black.assert_equivalent(source, actual)
 
 544         black.assert_stable(source, actual, DEFAULT_MODE)
 
 546     @patch("black.dump_to_file", dump_to_stderr)
 
 547     def test_comments4(self) -> None:
 
 548         source, expected = read_data("comments4")
 
 550         self.assertFormatEqual(expected, actual)
 
 551         black.assert_equivalent(source, actual)
 
 552         black.assert_stable(source, actual, DEFAULT_MODE)
 
 554     @patch("black.dump_to_file", dump_to_stderr)
 
 555     def test_comments5(self) -> None:
 
 556         source, expected = read_data("comments5")
 
 558         self.assertFormatEqual(expected, actual)
 
 559         black.assert_equivalent(source, actual)
 
 560         black.assert_stable(source, actual, DEFAULT_MODE)
 
 562     @patch("black.dump_to_file", dump_to_stderr)
 
 563     def test_comments6(self) -> None:
 
 564         source, expected = read_data("comments6")
 
 566         self.assertFormatEqual(expected, actual)
 
 567         black.assert_equivalent(source, actual)
 
 568         black.assert_stable(source, actual, DEFAULT_MODE)
 
 570     @patch("black.dump_to_file", dump_to_stderr)
 
 571     def test_comments7(self) -> None:
 
 572         source, expected = read_data("comments7")
 
 574         self.assertFormatEqual(expected, actual)
 
 575         black.assert_equivalent(source, actual)
 
 576         black.assert_stable(source, actual, DEFAULT_MODE)
 
 578     @patch("black.dump_to_file", dump_to_stderr)
 
 579     def test_comment_after_escaped_newline(self) -> None:
 
 580         source, expected = read_data("comment_after_escaped_newline")
 
 582         self.assertFormatEqual(expected, actual)
 
 583         black.assert_equivalent(source, actual)
 
 584         black.assert_stable(source, actual, DEFAULT_MODE)
 
 586     @patch("black.dump_to_file", dump_to_stderr)
 
 587     def test_cantfit(self) -> None:
 
 588         source, expected = read_data("cantfit")
 
 590         self.assertFormatEqual(expected, actual)
 
 591         black.assert_equivalent(source, actual)
 
 592         black.assert_stable(source, actual, DEFAULT_MODE)
 
 594     @patch("black.dump_to_file", dump_to_stderr)
 
 595     def test_import_spacing(self) -> None:
 
 596         source, expected = read_data("import_spacing")
 
 598         self.assertFormatEqual(expected, actual)
 
 599         black.assert_equivalent(source, actual)
 
 600         black.assert_stable(source, actual, DEFAULT_MODE)
 
 602     @patch("black.dump_to_file", dump_to_stderr)
 
 603     def test_composition(self) -> None:
 
 604         source, expected = read_data("composition")
 
 606         self.assertFormatEqual(expected, actual)
 
 607         black.assert_equivalent(source, actual)
 
 608         black.assert_stable(source, actual, DEFAULT_MODE)
 
 610     @patch("black.dump_to_file", dump_to_stderr)
 
 611     def test_empty_lines(self) -> None:
 
 612         source, expected = read_data("empty_lines")
 
 614         self.assertFormatEqual(expected, actual)
 
 615         black.assert_equivalent(source, actual)
 
 616         black.assert_stable(source, actual, DEFAULT_MODE)
 
 618     @patch("black.dump_to_file", dump_to_stderr)
 
 619     def test_remove_parens(self) -> None:
 
 620         source, expected = read_data("remove_parens")
 
 622         self.assertFormatEqual(expected, actual)
 
 623         black.assert_equivalent(source, actual)
 
 624         black.assert_stable(source, actual, DEFAULT_MODE)
 
 626     @patch("black.dump_to_file", dump_to_stderr)
 
 627     def test_string_prefixes(self) -> None:
 
 628         source, expected = read_data("string_prefixes")
 
 630         self.assertFormatEqual(expected, actual)
 
 631         black.assert_equivalent(source, actual)
 
 632         black.assert_stable(source, actual, DEFAULT_MODE)
 
 634     @patch("black.dump_to_file", dump_to_stderr)
 
 635     def test_numeric_literals(self) -> None:
 
 636         source, expected = read_data("numeric_literals")
 
 637         mode = replace(DEFAULT_MODE, target_versions=black.PY36_VERSIONS)
 
 638         actual = fs(source, mode=mode)
 
 639         self.assertFormatEqual(expected, actual)
 
 640         black.assert_equivalent(source, actual)
 
 641         black.assert_stable(source, actual, mode)
 
 643     @patch("black.dump_to_file", dump_to_stderr)
 
 644     def test_numeric_literals_ignoring_underscores(self) -> None:
 
 645         source, expected = read_data("numeric_literals_skip_underscores")
 
 646         mode = replace(DEFAULT_MODE, target_versions=black.PY36_VERSIONS)
 
 647         actual = fs(source, mode=mode)
 
 648         self.assertFormatEqual(expected, actual)
 
 649         black.assert_equivalent(source, actual)
 
 650         black.assert_stable(source, actual, mode)
 
 652     @patch("black.dump_to_file", dump_to_stderr)
 
 653     def test_numeric_literals_py2(self) -> None:
 
 654         source, expected = read_data("numeric_literals_py2")
 
 656         self.assertFormatEqual(expected, actual)
 
 657         black.assert_stable(source, actual, DEFAULT_MODE)
 
 659     @patch("black.dump_to_file", dump_to_stderr)
 
 660     def test_python2(self) -> None:
 
 661         source, expected = read_data("python2")
 
 663         self.assertFormatEqual(expected, actual)
 
 664         black.assert_equivalent(source, actual)
 
 665         black.assert_stable(source, actual, DEFAULT_MODE)
 
 667     @patch("black.dump_to_file", dump_to_stderr)
 
 668     def test_python2_print_function(self) -> None:
 
 669         source, expected = read_data("python2_print_function")
 
 670         mode = replace(DEFAULT_MODE, target_versions={TargetVersion.PY27})
 
 671         actual = fs(source, mode=mode)
 
 672         self.assertFormatEqual(expected, actual)
 
 673         black.assert_equivalent(source, actual)
 
 674         black.assert_stable(source, actual, mode)
 
 676     @patch("black.dump_to_file", dump_to_stderr)
 
 677     def test_python2_unicode_literals(self) -> None:
 
 678         source, expected = read_data("python2_unicode_literals")
 
 680         self.assertFormatEqual(expected, actual)
 
 681         black.assert_equivalent(source, actual)
 
 682         black.assert_stable(source, actual, DEFAULT_MODE)
 
 684     @patch("black.dump_to_file", dump_to_stderr)
 
 685     def test_stub(self) -> None:
 
 686         mode = replace(DEFAULT_MODE, is_pyi=True)
 
 687         source, expected = read_data("stub.pyi")
 
 688         actual = fs(source, mode=mode)
 
 689         self.assertFormatEqual(expected, actual)
 
 690         black.assert_stable(source, actual, mode)
 
 692     @patch("black.dump_to_file", dump_to_stderr)
 
 693     def test_async_as_identifier(self) -> None:
 
 694         source_path = (THIS_DIR / "data" / "async_as_identifier.py").resolve()
 
 695         source, expected = read_data("async_as_identifier")
 
 697         self.assertFormatEqual(expected, actual)
 
 698         major, minor = sys.version_info[:2]
 
 699         if major < 3 or (major <= 3 and minor < 7):
 
 700             black.assert_equivalent(source, actual)
 
 701         black.assert_stable(source, actual, DEFAULT_MODE)
 
 702         # ensure black can parse this when the target is 3.6
 
 703         self.invokeBlack([str(source_path), "--target-version", "py36"])
 
 704         # but not on 3.7, because async/await is no longer an identifier
 
 705         self.invokeBlack([str(source_path), "--target-version", "py37"], exit_code=123)
 
 707     @patch("black.dump_to_file", dump_to_stderr)
 
 708     def test_python37(self) -> None:
 
 709         source_path = (THIS_DIR / "data" / "python37.py").resolve()
 
 710         source, expected = read_data("python37")
 
 712         self.assertFormatEqual(expected, actual)
 
 713         major, minor = sys.version_info[:2]
 
 714         if major > 3 or (major == 3 and minor >= 7):
 
 715             black.assert_equivalent(source, actual)
 
 716         black.assert_stable(source, actual, DEFAULT_MODE)
 
 717         # ensure black can parse this when the target is 3.7
 
 718         self.invokeBlack([str(source_path), "--target-version", "py37"])
 
 719         # but not on 3.6, because we use async as a reserved keyword
 
 720         self.invokeBlack([str(source_path), "--target-version", "py36"], exit_code=123)
 
 722     @patch("black.dump_to_file", dump_to_stderr)
 
 723     def test_python38(self) -> None:
 
 724         source, expected = read_data("python38")
 
 726         self.assertFormatEqual(expected, actual)
 
 727         major, minor = sys.version_info[:2]
 
 728         if major > 3 or (major == 3 and minor >= 8):
 
 729             black.assert_equivalent(source, actual)
 
 730         black.assert_stable(source, actual, DEFAULT_MODE)
 
 732     @patch("black.dump_to_file", dump_to_stderr)
 
 733     def test_fmtonoff(self) -> None:
 
 734         source, expected = read_data("fmtonoff")
 
 736         self.assertFormatEqual(expected, actual)
 
 737         black.assert_equivalent(source, actual)
 
 738         black.assert_stable(source, actual, DEFAULT_MODE)
 
 740     @patch("black.dump_to_file", dump_to_stderr)
 
 741     def test_fmtonoff2(self) -> None:
 
 742         source, expected = read_data("fmtonoff2")
 
 744         self.assertFormatEqual(expected, actual)
 
 745         black.assert_equivalent(source, actual)
 
 746         black.assert_stable(source, actual, DEFAULT_MODE)
 
 748     @patch("black.dump_to_file", dump_to_stderr)
 
 749     def test_fmtonoff3(self) -> None:
 
 750         source, expected = read_data("fmtonoff3")
 
 752         self.assertFormatEqual(expected, actual)
 
 753         black.assert_equivalent(source, actual)
 
 754         black.assert_stable(source, actual, DEFAULT_MODE)
 
 756     @patch("black.dump_to_file", dump_to_stderr)
 
 757     def test_fmtonoff4(self) -> None:
 
 758         source, expected = read_data("fmtonoff4")
 
 760         self.assertFormatEqual(expected, actual)
 
 761         black.assert_equivalent(source, actual)
 
 762         black.assert_stable(source, actual, DEFAULT_MODE)
 
 764     @patch("black.dump_to_file", dump_to_stderr)
 
 765     def test_remove_empty_parentheses_after_class(self) -> None:
 
 766         source, expected = read_data("class_blank_parentheses")
 
 768         self.assertFormatEqual(expected, actual)
 
 769         black.assert_equivalent(source, actual)
 
 770         black.assert_stable(source, actual, DEFAULT_MODE)
 
 772     @patch("black.dump_to_file", dump_to_stderr)
 
 773     def test_new_line_between_class_and_code(self) -> None:
 
 774         source, expected = read_data("class_methods_new_line")
 
 776         self.assertFormatEqual(expected, actual)
 
 777         black.assert_equivalent(source, actual)
 
 778         black.assert_stable(source, actual, DEFAULT_MODE)
 
 780     @patch("black.dump_to_file", dump_to_stderr)
 
 781     def test_bracket_match(self) -> None:
 
 782         source, expected = read_data("bracketmatch")
 
 784         self.assertFormatEqual(expected, actual)
 
 785         black.assert_equivalent(source, actual)
 
 786         black.assert_stable(source, actual, DEFAULT_MODE)
 
 788     @patch("black.dump_to_file", dump_to_stderr)
 
 789     def test_tuple_assign(self) -> None:
 
 790         source, expected = read_data("tupleassign")
 
 792         self.assertFormatEqual(expected, actual)
 
 793         black.assert_equivalent(source, actual)
 
 794         black.assert_stable(source, actual, DEFAULT_MODE)
 
 796     @patch("black.dump_to_file", dump_to_stderr)
 
 797     def test_beginning_backslash(self) -> None:
 
 798         source, expected = read_data("beginning_backslash")
 
 800         self.assertFormatEqual(expected, actual)
 
 801         black.assert_equivalent(source, actual)
 
 802         black.assert_stable(source, actual, DEFAULT_MODE)
 
 804     def test_tab_comment_indentation(self) -> None:
 
 805         contents_tab = "if 1:\n\tif 2:\n\t\tpass\n\t# comment\n\tpass\n"
 
 806         contents_spc = "if 1:\n    if 2:\n        pass\n    # comment\n    pass\n"
 
 807         self.assertFormatEqual(contents_spc, fs(contents_spc))
 
 808         self.assertFormatEqual(contents_spc, fs(contents_tab))
 
 810         contents_tab = "if 1:\n\tif 2:\n\t\tpass\n\t\t# comment\n\tpass\n"
 
 811         contents_spc = "if 1:\n    if 2:\n        pass\n        # comment\n    pass\n"
 
 812         self.assertFormatEqual(contents_spc, fs(contents_spc))
 
 813         self.assertFormatEqual(contents_spc, fs(contents_tab))
 
 815         # mixed tabs and spaces (valid Python 2 code)
 
 816         contents_tab = "if 1:\n        if 2:\n\t\tpass\n\t# comment\n        pass\n"
 
 817         contents_spc = "if 1:\n    if 2:\n        pass\n    # comment\n    pass\n"
 
 818         self.assertFormatEqual(contents_spc, fs(contents_spc))
 
 819         self.assertFormatEqual(contents_spc, fs(contents_tab))
 
 821         contents_tab = "if 1:\n        if 2:\n\t\tpass\n\t\t# comment\n        pass\n"
 
 822         contents_spc = "if 1:\n    if 2:\n        pass\n        # comment\n    pass\n"
 
 823         self.assertFormatEqual(contents_spc, fs(contents_spc))
 
 824         self.assertFormatEqual(contents_spc, fs(contents_tab))
 
 826     def test_report_verbose(self) -> None:
 
 827         report = black.Report(verbose=True)
 
 831         def out(msg: str, **kwargs: Any) -> None:
 
 832             out_lines.append(msg)
 
 834         def err(msg: str, **kwargs: Any) -> None:
 
 835             err_lines.append(msg)
 
 837         with patch("black.out", out), patch("black.err", err):
 
 838             report.done(Path("f1"), black.Changed.NO)
 
 839             self.assertEqual(len(out_lines), 1)
 
 840             self.assertEqual(len(err_lines), 0)
 
 841             self.assertEqual(out_lines[-1], "f1 already well formatted, good job.")
 
 842             self.assertEqual(unstyle(str(report)), "1 file left unchanged.")
 
 843             self.assertEqual(report.return_code, 0)
 
 844             report.done(Path("f2"), black.Changed.YES)
 
 845             self.assertEqual(len(out_lines), 2)
 
 846             self.assertEqual(len(err_lines), 0)
 
 847             self.assertEqual(out_lines[-1], "reformatted f2")
 
 849                 unstyle(str(report)), "1 file reformatted, 1 file left unchanged."
 
 851             report.done(Path("f3"), black.Changed.CACHED)
 
 852             self.assertEqual(len(out_lines), 3)
 
 853             self.assertEqual(len(err_lines), 0)
 
 855                 out_lines[-1], "f3 wasn't modified on disk since last run."
 
 858                 unstyle(str(report)), "1 file reformatted, 2 files left unchanged."
 
 860             self.assertEqual(report.return_code, 0)
 
 862             self.assertEqual(report.return_code, 1)
 
 864             report.failed(Path("e1"), "boom")
 
 865             self.assertEqual(len(out_lines), 3)
 
 866             self.assertEqual(len(err_lines), 1)
 
 867             self.assertEqual(err_lines[-1], "error: cannot format e1: boom")
 
 869                 unstyle(str(report)),
 
 870                 "1 file reformatted, 2 files left unchanged, 1 file failed to"
 
 873             self.assertEqual(report.return_code, 123)
 
 874             report.done(Path("f3"), black.Changed.YES)
 
 875             self.assertEqual(len(out_lines), 4)
 
 876             self.assertEqual(len(err_lines), 1)
 
 877             self.assertEqual(out_lines[-1], "reformatted f3")
 
 879                 unstyle(str(report)),
 
 880                 "2 files reformatted, 2 files left unchanged, 1 file failed to"
 
 883             self.assertEqual(report.return_code, 123)
 
 884             report.failed(Path("e2"), "boom")
 
 885             self.assertEqual(len(out_lines), 4)
 
 886             self.assertEqual(len(err_lines), 2)
 
 887             self.assertEqual(err_lines[-1], "error: cannot format e2: boom")
 
 889                 unstyle(str(report)),
 
 890                 "2 files reformatted, 2 files left unchanged, 2 files failed to"
 
 893             self.assertEqual(report.return_code, 123)
 
 894             report.path_ignored(Path("wat"), "no match")
 
 895             self.assertEqual(len(out_lines), 5)
 
 896             self.assertEqual(len(err_lines), 2)
 
 897             self.assertEqual(out_lines[-1], "wat ignored: no match")
 
 899                 unstyle(str(report)),
 
 900                 "2 files reformatted, 2 files left unchanged, 2 files failed to"
 
 903             self.assertEqual(report.return_code, 123)
 
 904             report.done(Path("f4"), black.Changed.NO)
 
 905             self.assertEqual(len(out_lines), 6)
 
 906             self.assertEqual(len(err_lines), 2)
 
 907             self.assertEqual(out_lines[-1], "f4 already well formatted, good job.")
 
 909                 unstyle(str(report)),
 
 910                 "2 files reformatted, 3 files left unchanged, 2 files failed to"
 
 913             self.assertEqual(report.return_code, 123)
 
 916                 unstyle(str(report)),
 
 917                 "2 files would be reformatted, 3 files would be left unchanged, 2 files"
 
 918                 " would fail to reformat.",
 
 923                 unstyle(str(report)),
 
 924                 "2 files would be reformatted, 3 files would be left unchanged, 2 files"
 
 925                 " would fail to reformat.",
 
 928     def test_report_quiet(self) -> None:
 
 929         report = black.Report(quiet=True)
 
 933         def out(msg: str, **kwargs: Any) -> None:
 
 934             out_lines.append(msg)
 
 936         def err(msg: str, **kwargs: Any) -> None:
 
 937             err_lines.append(msg)
 
 939         with patch("black.out", out), patch("black.err", err):
 
 940             report.done(Path("f1"), black.Changed.NO)
 
 941             self.assertEqual(len(out_lines), 0)
 
 942             self.assertEqual(len(err_lines), 0)
 
 943             self.assertEqual(unstyle(str(report)), "1 file left unchanged.")
 
 944             self.assertEqual(report.return_code, 0)
 
 945             report.done(Path("f2"), black.Changed.YES)
 
 946             self.assertEqual(len(out_lines), 0)
 
 947             self.assertEqual(len(err_lines), 0)
 
 949                 unstyle(str(report)), "1 file reformatted, 1 file left unchanged."
 
 951             report.done(Path("f3"), black.Changed.CACHED)
 
 952             self.assertEqual(len(out_lines), 0)
 
 953             self.assertEqual(len(err_lines), 0)
 
 955                 unstyle(str(report)), "1 file reformatted, 2 files left unchanged."
 
 957             self.assertEqual(report.return_code, 0)
 
 959             self.assertEqual(report.return_code, 1)
 
 961             report.failed(Path("e1"), "boom")
 
 962             self.assertEqual(len(out_lines), 0)
 
 963             self.assertEqual(len(err_lines), 1)
 
 964             self.assertEqual(err_lines[-1], "error: cannot format e1: boom")
 
 966                 unstyle(str(report)),
 
 967                 "1 file reformatted, 2 files left unchanged, 1 file failed to"
 
 970             self.assertEqual(report.return_code, 123)
 
 971             report.done(Path("f3"), black.Changed.YES)
 
 972             self.assertEqual(len(out_lines), 0)
 
 973             self.assertEqual(len(err_lines), 1)
 
 975                 unstyle(str(report)),
 
 976                 "2 files reformatted, 2 files left unchanged, 1 file failed to"
 
 979             self.assertEqual(report.return_code, 123)
 
 980             report.failed(Path("e2"), "boom")
 
 981             self.assertEqual(len(out_lines), 0)
 
 982             self.assertEqual(len(err_lines), 2)
 
 983             self.assertEqual(err_lines[-1], "error: cannot format e2: boom")
 
 985                 unstyle(str(report)),
 
 986                 "2 files reformatted, 2 files left unchanged, 2 files failed to"
 
 989             self.assertEqual(report.return_code, 123)
 
 990             report.path_ignored(Path("wat"), "no match")
 
 991             self.assertEqual(len(out_lines), 0)
 
 992             self.assertEqual(len(err_lines), 2)
 
 994                 unstyle(str(report)),
 
 995                 "2 files reformatted, 2 files left unchanged, 2 files failed to"
 
 998             self.assertEqual(report.return_code, 123)
 
 999             report.done(Path("f4"), black.Changed.NO)
 
1000             self.assertEqual(len(out_lines), 0)
 
1001             self.assertEqual(len(err_lines), 2)
 
1003                 unstyle(str(report)),
 
1004                 "2 files reformatted, 3 files left unchanged, 2 files failed to"
 
1007             self.assertEqual(report.return_code, 123)
 
1010                 unstyle(str(report)),
 
1011                 "2 files would be reformatted, 3 files would be left unchanged, 2 files"
 
1012                 " would fail to reformat.",
 
1014             report.check = False
 
1017                 unstyle(str(report)),
 
1018                 "2 files would be reformatted, 3 files would be left unchanged, 2 files"
 
1019                 " would fail to reformat.",
 
1022     def test_report_normal(self) -> None:
 
1023         report = black.Report()
 
1027         def out(msg: str, **kwargs: Any) -> None:
 
1028             out_lines.append(msg)
 
1030         def err(msg: str, **kwargs: Any) -> None:
 
1031             err_lines.append(msg)
 
1033         with patch("black.out", out), patch("black.err", err):
 
1034             report.done(Path("f1"), black.Changed.NO)
 
1035             self.assertEqual(len(out_lines), 0)
 
1036             self.assertEqual(len(err_lines), 0)
 
1037             self.assertEqual(unstyle(str(report)), "1 file left unchanged.")
 
1038             self.assertEqual(report.return_code, 0)
 
1039             report.done(Path("f2"), black.Changed.YES)
 
1040             self.assertEqual(len(out_lines), 1)
 
1041             self.assertEqual(len(err_lines), 0)
 
1042             self.assertEqual(out_lines[-1], "reformatted f2")
 
1044                 unstyle(str(report)), "1 file reformatted, 1 file left unchanged."
 
1046             report.done(Path("f3"), black.Changed.CACHED)
 
1047             self.assertEqual(len(out_lines), 1)
 
1048             self.assertEqual(len(err_lines), 0)
 
1049             self.assertEqual(out_lines[-1], "reformatted f2")
 
1051                 unstyle(str(report)), "1 file reformatted, 2 files left unchanged."
 
1053             self.assertEqual(report.return_code, 0)
 
1055             self.assertEqual(report.return_code, 1)
 
1056             report.check = False
 
1057             report.failed(Path("e1"), "boom")
 
1058             self.assertEqual(len(out_lines), 1)
 
1059             self.assertEqual(len(err_lines), 1)
 
1060             self.assertEqual(err_lines[-1], "error: cannot format e1: boom")
 
1062                 unstyle(str(report)),
 
1063                 "1 file reformatted, 2 files left unchanged, 1 file failed to"
 
1066             self.assertEqual(report.return_code, 123)
 
1067             report.done(Path("f3"), black.Changed.YES)
 
1068             self.assertEqual(len(out_lines), 2)
 
1069             self.assertEqual(len(err_lines), 1)
 
1070             self.assertEqual(out_lines[-1], "reformatted f3")
 
1072                 unstyle(str(report)),
 
1073                 "2 files reformatted, 2 files left unchanged, 1 file failed to"
 
1076             self.assertEqual(report.return_code, 123)
 
1077             report.failed(Path("e2"), "boom")
 
1078             self.assertEqual(len(out_lines), 2)
 
1079             self.assertEqual(len(err_lines), 2)
 
1080             self.assertEqual(err_lines[-1], "error: cannot format e2: boom")
 
1082                 unstyle(str(report)),
 
1083                 "2 files reformatted, 2 files left unchanged, 2 files failed to"
 
1086             self.assertEqual(report.return_code, 123)
 
1087             report.path_ignored(Path("wat"), "no match")
 
1088             self.assertEqual(len(out_lines), 2)
 
1089             self.assertEqual(len(err_lines), 2)
 
1091                 unstyle(str(report)),
 
1092                 "2 files reformatted, 2 files left unchanged, 2 files failed to"
 
1095             self.assertEqual(report.return_code, 123)
 
1096             report.done(Path("f4"), black.Changed.NO)
 
1097             self.assertEqual(len(out_lines), 2)
 
1098             self.assertEqual(len(err_lines), 2)
 
1100                 unstyle(str(report)),
 
1101                 "2 files reformatted, 3 files left unchanged, 2 files failed to"
 
1104             self.assertEqual(report.return_code, 123)
 
1107                 unstyle(str(report)),
 
1108                 "2 files would be reformatted, 3 files would be left unchanged, 2 files"
 
1109                 " would fail to reformat.",
 
1111             report.check = False
 
1114                 unstyle(str(report)),
 
1115                 "2 files would be reformatted, 3 files would be left unchanged, 2 files"
 
1116                 " would fail to reformat.",
 
1119     def test_lib2to3_parse(self) -> None:
 
1120         with self.assertRaises(black.InvalidInput):
 
1121             black.lib2to3_parse("invalid syntax")
 
1123         straddling = "x + y"
 
1124         black.lib2to3_parse(straddling)
 
1125         black.lib2to3_parse(straddling, {TargetVersion.PY27})
 
1126         black.lib2to3_parse(straddling, {TargetVersion.PY36})
 
1127         black.lib2to3_parse(straddling, {TargetVersion.PY27, TargetVersion.PY36})
 
1129         py2_only = "print x"
 
1130         black.lib2to3_parse(py2_only)
 
1131         black.lib2to3_parse(py2_only, {TargetVersion.PY27})
 
1132         with self.assertRaises(black.InvalidInput):
 
1133             black.lib2to3_parse(py2_only, {TargetVersion.PY36})
 
1134         with self.assertRaises(black.InvalidInput):
 
1135             black.lib2to3_parse(py2_only, {TargetVersion.PY27, TargetVersion.PY36})
 
1137         py3_only = "exec(x, end=y)"
 
1138         black.lib2to3_parse(py3_only)
 
1139         with self.assertRaises(black.InvalidInput):
 
1140             black.lib2to3_parse(py3_only, {TargetVersion.PY27})
 
1141         black.lib2to3_parse(py3_only, {TargetVersion.PY36})
 
1142         black.lib2to3_parse(py3_only, {TargetVersion.PY27, TargetVersion.PY36})
 
1144     def test_get_features_used(self) -> None:
 
1145         node = black.lib2to3_parse("def f(*, arg): ...\n")
 
1146         self.assertEqual(black.get_features_used(node), set())
 
1147         node = black.lib2to3_parse("def f(*, arg,): ...\n")
 
1148         self.assertEqual(black.get_features_used(node), {Feature.TRAILING_COMMA_IN_DEF})
 
1149         node = black.lib2to3_parse("f(*arg,)\n")
 
1151             black.get_features_used(node), {Feature.TRAILING_COMMA_IN_CALL}
 
1153         node = black.lib2to3_parse("def f(*, arg): f'string'\n")
 
1154         self.assertEqual(black.get_features_used(node), {Feature.F_STRINGS})
 
1155         node = black.lib2to3_parse("123_456\n")
 
1156         self.assertEqual(black.get_features_used(node), {Feature.NUMERIC_UNDERSCORES})
 
1157         node = black.lib2to3_parse("123456\n")
 
1158         self.assertEqual(black.get_features_used(node), set())
 
1159         source, expected = read_data("function")
 
1160         node = black.lib2to3_parse(source)
 
1161         expected_features = {
 
1162             Feature.TRAILING_COMMA_IN_CALL,
 
1163             Feature.TRAILING_COMMA_IN_DEF,
 
1166         self.assertEqual(black.get_features_used(node), expected_features)
 
1167         node = black.lib2to3_parse(expected)
 
1168         self.assertEqual(black.get_features_used(node), expected_features)
 
1169         source, expected = read_data("expression")
 
1170         node = black.lib2to3_parse(source)
 
1171         self.assertEqual(black.get_features_used(node), set())
 
1172         node = black.lib2to3_parse(expected)
 
1173         self.assertEqual(black.get_features_used(node), set())
 
1175     def test_get_future_imports(self) -> None:
 
1176         node = black.lib2to3_parse("\n")
 
1177         self.assertEqual(set(), black.get_future_imports(node))
 
1178         node = black.lib2to3_parse("from __future__ import black\n")
 
1179         self.assertEqual({"black"}, black.get_future_imports(node))
 
1180         node = black.lib2to3_parse("from __future__ import multiple, imports\n")
 
1181         self.assertEqual({"multiple", "imports"}, black.get_future_imports(node))
 
1182         node = black.lib2to3_parse("from __future__ import (parenthesized, imports)\n")
 
1183         self.assertEqual({"parenthesized", "imports"}, black.get_future_imports(node))
 
1184         node = black.lib2to3_parse(
 
1185             "from __future__ import multiple\nfrom __future__ import imports\n"
 
1187         self.assertEqual({"multiple", "imports"}, black.get_future_imports(node))
 
1188         node = black.lib2to3_parse("# comment\nfrom __future__ import black\n")
 
1189         self.assertEqual({"black"}, black.get_future_imports(node))
 
1190         node = black.lib2to3_parse('"""docstring"""\nfrom __future__ import black\n')
 
1191         self.assertEqual({"black"}, black.get_future_imports(node))
 
1192         node = black.lib2to3_parse("some(other, code)\nfrom __future__ import black\n")
 
1193         self.assertEqual(set(), black.get_future_imports(node))
 
1194         node = black.lib2to3_parse("from some.module import black\n")
 
1195         self.assertEqual(set(), black.get_future_imports(node))
 
1196         node = black.lib2to3_parse(
 
1197             "from __future__ import unicode_literals as _unicode_literals"
 
1199         self.assertEqual({"unicode_literals"}, black.get_future_imports(node))
 
1200         node = black.lib2to3_parse(
 
1201             "from __future__ import unicode_literals as _lol, print"
 
1203         self.assertEqual({"unicode_literals", "print"}, black.get_future_imports(node))
 
1205     def test_debug_visitor(self) -> None:
 
1206         source, _ = read_data("debug_visitor.py")
 
1207         expected, _ = read_data("debug_visitor.out")
 
1211         def out(msg: str, **kwargs: Any) -> None:
 
1212             out_lines.append(msg)
 
1214         def err(msg: str, **kwargs: Any) -> None:
 
1215             err_lines.append(msg)
 
1217         with patch("black.out", out), patch("black.err", err):
 
1218             black.DebugVisitor.show(source)
 
1219         actual = "\n".join(out_lines) + "\n"
 
1221         if expected != actual:
 
1222             log_name = black.dump_to_file(*out_lines)
 
1226             f"AST print out is different. Actual version dumped to {log_name}",
 
1229     def test_format_file_contents(self) -> None:
 
1232         with self.assertRaises(black.NothingChanged):
 
1233             black.format_file_contents(empty, mode=mode, fast=False)
 
1235         with self.assertRaises(black.NothingChanged):
 
1236             black.format_file_contents(just_nl, mode=mode, fast=False)
 
1237         same = "j = [1, 2, 3]\n"
 
1238         with self.assertRaises(black.NothingChanged):
 
1239             black.format_file_contents(same, mode=mode, fast=False)
 
1240         different = "j = [1,2,3]"
 
1242         actual = black.format_file_contents(different, mode=mode, fast=False)
 
1243         self.assertEqual(expected, actual)
 
1244         invalid = "return if you can"
 
1245         with self.assertRaises(black.InvalidInput) as e:
 
1246             black.format_file_contents(invalid, mode=mode, fast=False)
 
1247         self.assertEqual(str(e.exception), "Cannot parse: 1:7: return if you can")
 
1249     def test_endmarker(self) -> None:
 
1250         n = black.lib2to3_parse("\n")
 
1251         self.assertEqual(n.type, black.syms.file_input)
 
1252         self.assertEqual(len(n.children), 1)
 
1253         self.assertEqual(n.children[0].type, black.token.ENDMARKER)
 
1255     @unittest.skipIf(os.environ.get("SKIP_AST_PRINT"), "user set SKIP_AST_PRINT")
 
1256     def test_assertFormatEqual(self) -> None:
 
1260         def out(msg: str, **kwargs: Any) -> None:
 
1261             out_lines.append(msg)
 
1263         def err(msg: str, **kwargs: Any) -> None:
 
1264             err_lines.append(msg)
 
1266         with patch("black.out", out), patch("black.err", err):
 
1267             with self.assertRaises(AssertionError):
 
1268                 self.assertFormatEqual("j = [1, 2, 3]", "j = [1, 2, 3,]")
 
1270         out_str = "".join(out_lines)
 
1271         self.assertTrue("Expected tree:" in out_str)
 
1272         self.assertTrue("Actual tree:" in out_str)
 
1273         self.assertEqual("".join(err_lines), "")
 
1275     def test_cache_broken_file(self) -> None:
 
1277         with cache_dir() as workspace:
 
1278             cache_file = black.get_cache_file(mode)
 
1279             with cache_file.open("w") as fobj:
 
1280                 fobj.write("this is not a pickle")
 
1281             self.assertEqual(black.read_cache(mode), {})
 
1282             src = (workspace / "test.py").resolve()
 
1283             with src.open("w") as fobj:
 
1284                 fobj.write("print('hello')")
 
1285             self.invokeBlack([str(src)])
 
1286             cache = black.read_cache(mode)
 
1287             self.assertIn(src, cache)
 
1289     def test_cache_single_file_already_cached(self) -> None:
 
1291         with cache_dir() as workspace:
 
1292             src = (workspace / "test.py").resolve()
 
1293             with src.open("w") as fobj:
 
1294                 fobj.write("print('hello')")
 
1295             black.write_cache({}, [src], mode)
 
1296             self.invokeBlack([str(src)])
 
1297             with src.open("r") as fobj:
 
1298                 self.assertEqual(fobj.read(), "print('hello')")
 
1301     def test_cache_multiple_files(self) -> None:
 
1303         with cache_dir() as workspace, patch(
 
1304             "black.ProcessPoolExecutor", new=ThreadPoolExecutor
 
1306             one = (workspace / "one.py").resolve()
 
1307             with one.open("w") as fobj:
 
1308                 fobj.write("print('hello')")
 
1309             two = (workspace / "two.py").resolve()
 
1310             with two.open("w") as fobj:
 
1311                 fobj.write("print('hello')")
 
1312             black.write_cache({}, [one], mode)
 
1313             self.invokeBlack([str(workspace)])
 
1314             with one.open("r") as fobj:
 
1315                 self.assertEqual(fobj.read(), "print('hello')")
 
1316             with two.open("r") as fobj:
 
1317                 self.assertEqual(fobj.read(), 'print("hello")\n')
 
1318             cache = black.read_cache(mode)
 
1319             self.assertIn(one, cache)
 
1320             self.assertIn(two, cache)
 
1322     def test_no_cache_when_writeback_diff(self) -> None:
 
1324         with cache_dir() as workspace:
 
1325             src = (workspace / "test.py").resolve()
 
1326             with src.open("w") as fobj:
 
1327                 fobj.write("print('hello')")
 
1328             self.invokeBlack([str(src), "--diff"])
 
1329             cache_file = black.get_cache_file(mode)
 
1330             self.assertFalse(cache_file.exists())
 
1332     def test_no_cache_when_stdin(self) -> None:
 
1335             result = CliRunner().invoke(
 
1336                 black.main, ["-"], input=BytesIO(b"print('hello')")
 
1338             self.assertEqual(result.exit_code, 0)
 
1339             cache_file = black.get_cache_file(mode)
 
1340             self.assertFalse(cache_file.exists())
 
1342     def test_read_cache_no_cachefile(self) -> None:
 
1345             self.assertEqual(black.read_cache(mode), {})
 
1347     def test_write_cache_read_cache(self) -> None:
 
1349         with cache_dir() as workspace:
 
1350             src = (workspace / "test.py").resolve()
 
1352             black.write_cache({}, [src], mode)
 
1353             cache = black.read_cache(mode)
 
1354             self.assertIn(src, cache)
 
1355             self.assertEqual(cache[src], black.get_cache_info(src))
 
1357     def test_filter_cached(self) -> None:
 
1358         with TemporaryDirectory() as workspace:
 
1359             path = Path(workspace)
 
1360             uncached = (path / "uncached").resolve()
 
1361             cached = (path / "cached").resolve()
 
1362             cached_but_changed = (path / "changed").resolve()
 
1365             cached_but_changed.touch()
 
1366             cache = {cached: black.get_cache_info(cached), cached_but_changed: (0.0, 0)}
 
1367             todo, done = black.filter_cached(
 
1368                 cache, {uncached, cached, cached_but_changed}
 
1370             self.assertEqual(todo, {uncached, cached_but_changed})
 
1371             self.assertEqual(done, {cached})
 
1373     def test_write_cache_creates_directory_if_needed(self) -> None:
 
1375         with cache_dir(exists=False) as workspace:
 
1376             self.assertFalse(workspace.exists())
 
1377             black.write_cache({}, [], mode)
 
1378             self.assertTrue(workspace.exists())
 
1381     def test_failed_formatting_does_not_get_cached(self) -> None:
 
1383         with cache_dir() as workspace, patch(
 
1384             "black.ProcessPoolExecutor", new=ThreadPoolExecutor
 
1386             failing = (workspace / "failing.py").resolve()
 
1387             with failing.open("w") as fobj:
 
1388                 fobj.write("not actually python")
 
1389             clean = (workspace / "clean.py").resolve()
 
1390             with clean.open("w") as fobj:
 
1391                 fobj.write('print("hello")\n')
 
1392             self.invokeBlack([str(workspace)], exit_code=123)
 
1393             cache = black.read_cache(mode)
 
1394             self.assertNotIn(failing, cache)
 
1395             self.assertIn(clean, cache)
 
1397     def test_write_cache_write_fail(self) -> None:
 
1399         with cache_dir(), patch.object(Path, "open") as mock:
 
1400             mock.side_effect = OSError
 
1401             black.write_cache({}, [], mode)
 
1404     @patch("black.ProcessPoolExecutor", MagicMock(side_effect=OSError))
 
1405     def test_works_in_mono_process_only_environment(self) -> None:
 
1406         with cache_dir() as workspace:
 
1408                 (workspace / "one.py").resolve(),
 
1409                 (workspace / "two.py").resolve(),
 
1411                 f.write_text('print("hello")\n')
 
1412             self.invokeBlack([str(workspace)])
 
1415     def test_check_diff_use_together(self) -> None:
 
1417             # Files which will be reformatted.
 
1418             src1 = (THIS_DIR / "data" / "string_quotes.py").resolve()
 
1419             self.invokeBlack([str(src1), "--diff", "--check"], exit_code=1)
 
1420             # Files which will not be reformatted.
 
1421             src2 = (THIS_DIR / "data" / "composition.py").resolve()
 
1422             self.invokeBlack([str(src2), "--diff", "--check"])
 
1423             # Multi file command.
 
1424             self.invokeBlack([str(src1), str(src2), "--diff", "--check"], exit_code=1)
 
1426     def test_no_files(self) -> None:
 
1428             # Without an argument, black exits with error code 0.
 
1429             self.invokeBlack([])
 
1431     def test_broken_symlink(self) -> None:
 
1432         with cache_dir() as workspace:
 
1433             symlink = workspace / "broken_link.py"
 
1435                 symlink.symlink_to("nonexistent.py")
 
1436             except OSError as e:
 
1437                 self.skipTest(f"Can't create symlinks: {e}")
 
1438             self.invokeBlack([str(workspace.resolve())])
 
1440     def test_read_cache_line_lengths(self) -> None:
 
1442         short_mode = replace(DEFAULT_MODE, line_length=1)
 
1443         with cache_dir() as workspace:
 
1444             path = (workspace / "file.py").resolve()
 
1446             black.write_cache({}, [path], mode)
 
1447             one = black.read_cache(mode)
 
1448             self.assertIn(path, one)
 
1449             two = black.read_cache(short_mode)
 
1450             self.assertNotIn(path, two)
 
1452     def test_tricky_unicode_symbols(self) -> None:
 
1453         source, expected = read_data("tricky_unicode_symbols")
 
1455         self.assertFormatEqual(expected, actual)
 
1456         black.assert_equivalent(source, actual)
 
1457         black.assert_stable(source, actual, DEFAULT_MODE)
 
1459     def test_single_file_force_pyi(self) -> None:
 
1460         reg_mode = DEFAULT_MODE
 
1461         pyi_mode = replace(DEFAULT_MODE, is_pyi=True)
 
1462         contents, expected = read_data("force_pyi")
 
1463         with cache_dir() as workspace:
 
1464             path = (workspace / "file.py").resolve()
 
1465             with open(path, "w") as fh:
 
1467             self.invokeBlack([str(path), "--pyi"])
 
1468             with open(path, "r") as fh:
 
1470             # verify cache with --pyi is separate
 
1471             pyi_cache = black.read_cache(pyi_mode)
 
1472             self.assertIn(path, pyi_cache)
 
1473             normal_cache = black.read_cache(reg_mode)
 
1474             self.assertNotIn(path, normal_cache)
 
1475         self.assertEqual(actual, expected)
 
1478     def test_multi_file_force_pyi(self) -> None:
 
1479         reg_mode = DEFAULT_MODE
 
1480         pyi_mode = replace(DEFAULT_MODE, is_pyi=True)
 
1481         contents, expected = read_data("force_pyi")
 
1482         with cache_dir() as workspace:
 
1484                 (workspace / "file1.py").resolve(),
 
1485                 (workspace / "file2.py").resolve(),
 
1488                 with open(path, "w") as fh:
 
1490             self.invokeBlack([str(p) for p in paths] + ["--pyi"])
 
1492                 with open(path, "r") as fh:
 
1494                 self.assertEqual(actual, expected)
 
1495             # verify cache with --pyi is separate
 
1496             pyi_cache = black.read_cache(pyi_mode)
 
1497             normal_cache = black.read_cache(reg_mode)
 
1499                 self.assertIn(path, pyi_cache)
 
1500                 self.assertNotIn(path, normal_cache)
 
1502     def test_pipe_force_pyi(self) -> None:
 
1503         source, expected = read_data("force_pyi")
 
1504         result = CliRunner().invoke(
 
1505             black.main, ["-", "-q", "--pyi"], input=BytesIO(source.encode("utf8"))
 
1507         self.assertEqual(result.exit_code, 0)
 
1508         actual = result.output
 
1509         self.assertFormatEqual(actual, expected)
 
1511     def test_single_file_force_py36(self) -> None:
 
1512         reg_mode = DEFAULT_MODE
 
1513         py36_mode = replace(DEFAULT_MODE, target_versions=black.PY36_VERSIONS)
 
1514         source, expected = read_data("force_py36")
 
1515         with cache_dir() as workspace:
 
1516             path = (workspace / "file.py").resolve()
 
1517             with open(path, "w") as fh:
 
1519             self.invokeBlack([str(path), *PY36_ARGS])
 
1520             with open(path, "r") as fh:
 
1522             # verify cache with --target-version is separate
 
1523             py36_cache = black.read_cache(py36_mode)
 
1524             self.assertIn(path, py36_cache)
 
1525             normal_cache = black.read_cache(reg_mode)
 
1526             self.assertNotIn(path, normal_cache)
 
1527         self.assertEqual(actual, expected)
 
1530     def test_multi_file_force_py36(self) -> None:
 
1531         reg_mode = DEFAULT_MODE
 
1532         py36_mode = replace(DEFAULT_MODE, target_versions=black.PY36_VERSIONS)
 
1533         source, expected = read_data("force_py36")
 
1534         with cache_dir() as workspace:
 
1536                 (workspace / "file1.py").resolve(),
 
1537                 (workspace / "file2.py").resolve(),
 
1540                 with open(path, "w") as fh:
 
1542             self.invokeBlack([str(p) for p in paths] + PY36_ARGS)
 
1544                 with open(path, "r") as fh:
 
1546                 self.assertEqual(actual, expected)
 
1547             # verify cache with --target-version is separate
 
1548             pyi_cache = black.read_cache(py36_mode)
 
1549             normal_cache = black.read_cache(reg_mode)
 
1551                 self.assertIn(path, pyi_cache)
 
1552                 self.assertNotIn(path, normal_cache)
 
1554     def test_collections(self) -> None:
 
1555         source, expected = read_data("collections")
 
1557         self.assertFormatEqual(expected, actual)
 
1558         black.assert_equivalent(source, actual)
 
1559         black.assert_stable(source, actual, DEFAULT_MODE)
 
1561     def test_pipe_force_py36(self) -> None:
 
1562         source, expected = read_data("force_py36")
 
1563         result = CliRunner().invoke(
 
1565             ["-", "-q", "--target-version=py36"],
 
1566             input=BytesIO(source.encode("utf8")),
 
1568         self.assertEqual(result.exit_code, 0)
 
1569         actual = result.output
 
1570         self.assertFormatEqual(actual, expected)
 
1572     def test_include_exclude(self) -> None:
 
1573         path = THIS_DIR / "data" / "include_exclude_tests"
 
1574         include = re.compile(r"\.pyi?$")
 
1575         exclude = re.compile(r"/exclude/|/\.definitely_exclude/")
 
1576         report = black.Report()
 
1577         gitignore = PathSpec.from_lines("gitwildmatch", [])
 
1578         sources: List[Path] = []
 
1580             Path(path / "b/dont_exclude/a.py"),
 
1581             Path(path / "b/dont_exclude/a.pyi"),
 
1583         this_abs = THIS_DIR.resolve()
 
1585             black.gen_python_files(
 
1586                 path.iterdir(), this_abs, include, exclude, None, report, gitignore
 
1589         self.assertEqual(sorted(expected), sorted(sources))
 
1591     @patch("black.find_project_root", lambda *args: THIS_DIR.resolve())
 
1592     def test_exclude_for_issue_1572(self) -> None:
 
1593         # Exclude shouldn't touch files that were explicitly given to Black through the
 
1594         # CLI. Exclude is supposed to only apply to the recursive discovery of files.
 
1595         # https://github.com/psf/black/issues/1572
 
1596         path = THIS_DIR / "data" / "include_exclude_tests"
 
1598         exclude = r"/exclude/|a\.py"
 
1599         src = str(path / "b/exclude/a.py")
 
1600         report = black.Report()
 
1601         expected = [Path(path / "b/exclude/a.py")]
 
1614         self.assertEqual(sorted(expected), sorted(sources))
 
1616     def test_gitignore_exclude(self) -> None:
 
1617         path = THIS_DIR / "data" / "include_exclude_tests"
 
1618         include = re.compile(r"\.pyi?$")
 
1619         exclude = re.compile(r"")
 
1620         report = black.Report()
 
1621         gitignore = PathSpec.from_lines(
 
1622             "gitwildmatch", ["exclude/", ".definitely_exclude"]
 
1624         sources: List[Path] = []
 
1626             Path(path / "b/dont_exclude/a.py"),
 
1627             Path(path / "b/dont_exclude/a.pyi"),
 
1629         this_abs = THIS_DIR.resolve()
 
1631             black.gen_python_files(
 
1632                 path.iterdir(), this_abs, include, exclude, None, report, gitignore
 
1635         self.assertEqual(sorted(expected), sorted(sources))
 
1637     def test_empty_include(self) -> None:
 
1638         path = THIS_DIR / "data" / "include_exclude_tests"
 
1639         report = black.Report()
 
1640         gitignore = PathSpec.from_lines("gitwildmatch", [])
 
1641         empty = re.compile(r"")
 
1642         sources: List[Path] = []
 
1644             Path(path / "b/exclude/a.pie"),
 
1645             Path(path / "b/exclude/a.py"),
 
1646             Path(path / "b/exclude/a.pyi"),
 
1647             Path(path / "b/dont_exclude/a.pie"),
 
1648             Path(path / "b/dont_exclude/a.py"),
 
1649             Path(path / "b/dont_exclude/a.pyi"),
 
1650             Path(path / "b/.definitely_exclude/a.pie"),
 
1651             Path(path / "b/.definitely_exclude/a.py"),
 
1652             Path(path / "b/.definitely_exclude/a.pyi"),
 
1654         this_abs = THIS_DIR.resolve()
 
1656             black.gen_python_files(
 
1660                 re.compile(black.DEFAULT_EXCLUDES),
 
1666         self.assertEqual(sorted(expected), sorted(sources))
 
1668     def test_empty_exclude(self) -> None:
 
1669         path = THIS_DIR / "data" / "include_exclude_tests"
 
1670         report = black.Report()
 
1671         gitignore = PathSpec.from_lines("gitwildmatch", [])
 
1672         empty = re.compile(r"")
 
1673         sources: List[Path] = []
 
1675             Path(path / "b/dont_exclude/a.py"),
 
1676             Path(path / "b/dont_exclude/a.pyi"),
 
1677             Path(path / "b/exclude/a.py"),
 
1678             Path(path / "b/exclude/a.pyi"),
 
1679             Path(path / "b/.definitely_exclude/a.py"),
 
1680             Path(path / "b/.definitely_exclude/a.pyi"),
 
1682         this_abs = THIS_DIR.resolve()
 
1684             black.gen_python_files(
 
1687                 re.compile(black.DEFAULT_INCLUDES),
 
1694         self.assertEqual(sorted(expected), sorted(sources))
 
1696     def test_invalid_include_exclude(self) -> None:
 
1697         for option in ["--include", "--exclude"]:
 
1698             self.invokeBlack(["-", option, "**()(!!*)"], exit_code=2)
 
1700     def test_preserves_line_endings(self) -> None:
 
1701         with TemporaryDirectory() as workspace:
 
1702             test_file = Path(workspace) / "test.py"
 
1703             for nl in ["\n", "\r\n"]:
 
1704                 contents = nl.join(["def f(  ):", "    pass"])
 
1705                 test_file.write_bytes(contents.encode())
 
1706                 ff(test_file, write_back=black.WriteBack.YES)
 
1707                 updated_contents: bytes = test_file.read_bytes()
 
1708                 self.assertIn(nl.encode(), updated_contents)
 
1710                     self.assertNotIn(b"\r\n", updated_contents)
 
1712     def test_preserves_line_endings_via_stdin(self) -> None:
 
1713         for nl in ["\n", "\r\n"]:
 
1714             contents = nl.join(["def f(  ):", "    pass"])
 
1715             runner = BlackRunner()
 
1716             result = runner.invoke(
 
1717                 black.main, ["-", "--fast"], input=BytesIO(contents.encode("utf8"))
 
1719             self.assertEqual(result.exit_code, 0)
 
1720             output = runner.stdout_bytes
 
1721             self.assertIn(nl.encode("utf8"), output)
 
1723                 self.assertNotIn(b"\r\n", output)
 
1725     def test_assert_equivalent_different_asts(self) -> None:
 
1726         with self.assertRaises(AssertionError):
 
1727             black.assert_equivalent("{}", "None")
 
1729     def test_symlink_out_of_root_directory(self) -> None:
 
1731         root = THIS_DIR.resolve()
 
1733         include = re.compile(black.DEFAULT_INCLUDES)
 
1734         exclude = re.compile(black.DEFAULT_EXCLUDES)
 
1735         report = black.Report()
 
1736         gitignore = PathSpec.from_lines("gitwildmatch", [])
 
1737         # `child` should behave like a symlink which resolved path is clearly
 
1738         # outside of the `root` directory.
 
1739         path.iterdir.return_value = [child]
 
1740         child.resolve.return_value = Path("/a/b/c")
 
1741         child.as_posix.return_value = "/a/b/c"
 
1742         child.is_symlink.return_value = True
 
1745                 black.gen_python_files(
 
1746                     path.iterdir(), root, include, exclude, None, report, gitignore
 
1749         except ValueError as ve:
 
1750             self.fail(f"`get_python_files_in_dir()` failed: {ve}")
 
1751         path.iterdir.assert_called_once()
 
1752         child.resolve.assert_called_once()
 
1753         child.is_symlink.assert_called_once()
 
1754         # `child` should behave like a strange file which resolved path is clearly
 
1755         # outside of the `root` directory.
 
1756         child.is_symlink.return_value = False
 
1757         with self.assertRaises(ValueError):
 
1759                 black.gen_python_files(
 
1760                     path.iterdir(), root, include, exclude, None, report, gitignore
 
1763         path.iterdir.assert_called()
 
1764         self.assertEqual(path.iterdir.call_count, 2)
 
1765         child.resolve.assert_called()
 
1766         self.assertEqual(child.resolve.call_count, 2)
 
1767         child.is_symlink.assert_called()
 
1768         self.assertEqual(child.is_symlink.call_count, 2)
 
1770     def test_shhh_click(self) -> None:
 
1772             from click import _unicodefun  # type: ignore
 
1773         except ModuleNotFoundError:
 
1774             self.skipTest("Incompatible Click version")
 
1775         if not hasattr(_unicodefun, "_verify_python3_env"):
 
1776             self.skipTest("Incompatible Click version")
 
1777         # First, let's see if Click is crashing with a preferred ASCII charset.
 
1778         with patch("locale.getpreferredencoding") as gpe:
 
1779             gpe.return_value = "ASCII"
 
1780             with self.assertRaises(RuntimeError):
 
1781                 _unicodefun._verify_python3_env()
 
1782         # Now, let's silence Click...
 
1784         # ...and confirm it's silent.
 
1785         with patch("locale.getpreferredencoding") as gpe:
 
1786             gpe.return_value = "ASCII"
 
1788                 _unicodefun._verify_python3_env()
 
1789             except RuntimeError as re:
 
1790                 self.fail(f"`patch_click()` failed, exception still raised: {re}")
 
1792     def test_root_logger_not_used_directly(self) -> None:
 
1793         def fail(*args: Any, **kwargs: Any) -> None:
 
1794             self.fail("Record created with root logger")
 
1796         with patch.multiple(
 
1807     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1808     def test_blackd_main(self) -> None:
 
1809         with patch("blackd.web.run_app"):
 
1810             result = CliRunner().invoke(blackd.main, [])
 
1811             if result.exception is not None:
 
1812                 raise result.exception
 
1813             self.assertEqual(result.exit_code, 0)
 
1815     def test_invalid_config_return_code(self) -> None:
 
1816         tmp_file = Path(black.dump_to_file())
 
1818             tmp_config = Path(black.dump_to_file())
 
1820             args = ["--config", str(tmp_config), str(tmp_file)]
 
1821             self.invokeBlack(args, exit_code=2, ignore_config=False)
 
1825     def test_parse_pyproject_toml(self) -> None:
 
1826         test_toml_file = THIS_DIR / "test.toml"
 
1827         config = black.parse_pyproject_toml(str(test_toml_file))
 
1828         self.assertEqual(config["verbose"], 1)
 
1829         self.assertEqual(config["check"], "no")
 
1830         self.assertEqual(config["diff"], "y")
 
1831         self.assertEqual(config["color"], True)
 
1832         self.assertEqual(config["line_length"], 79)
 
1833         self.assertEqual(config["target_version"], ["py36", "py37", "py38"])
 
1834         self.assertEqual(config["exclude"], r"\.pyi?$")
 
1835         self.assertEqual(config["include"], r"\.py?$")
 
1837     def test_read_pyproject_toml(self) -> None:
 
1838         test_toml_file = THIS_DIR / "test.toml"
 
1839         fake_ctx = FakeContext()
 
1840         black.read_pyproject_toml(fake_ctx, FakeParameter(), str(test_toml_file))
 
1841         config = fake_ctx.default_map
 
1842         self.assertEqual(config["verbose"], "1")
 
1843         self.assertEqual(config["check"], "no")
 
1844         self.assertEqual(config["diff"], "y")
 
1845         self.assertEqual(config["color"], "True")
 
1846         self.assertEqual(config["line_length"], "79")
 
1847         self.assertEqual(config["target_version"], ["py36", "py37", "py38"])
 
1848         self.assertEqual(config["exclude"], r"\.pyi?$")
 
1849         self.assertEqual(config["include"], r"\.py?$")
 
1851     def test_find_project_root(self) -> None:
 
1852         with TemporaryDirectory() as workspace:
 
1853             root = Path(workspace)
 
1854             test_dir = root / "test"
 
1857             src_dir = root / "src"
 
1860             root_pyproject = root / "pyproject.toml"
 
1861             root_pyproject.touch()
 
1862             src_pyproject = src_dir / "pyproject.toml"
 
1863             src_pyproject.touch()
 
1864             src_python = src_dir / "foo.py"
 
1868                 black.find_project_root((src_dir, test_dir)), root.resolve()
 
1870             self.assertEqual(black.find_project_root((src_dir,)), src_dir.resolve())
 
1871             self.assertEqual(black.find_project_root((src_python,)), src_dir.resolve())
 
1874 class BlackDTestCase(AioHTTPTestCase):
 
1875     async def get_application(self) -> web.Application:
 
1876         return blackd.make_app()
 
1878     # TODO: remove these decorators once the below is released
 
1879     # https://github.com/aio-libs/aiohttp/pull/3727
 
1880     @skip_if_exception("ClientOSError")
 
1881     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1883     async def test_blackd_request_needs_formatting(self) -> None:
 
1884         response = await self.client.post("/", data=b"print('hello world')")
 
1885         self.assertEqual(response.status, 200)
 
1886         self.assertEqual(response.charset, "utf8")
 
1887         self.assertEqual(await response.read(), b'print("hello world")\n')
 
1889     @skip_if_exception("ClientOSError")
 
1890     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1892     async def test_blackd_request_no_change(self) -> None:
 
1893         response = await self.client.post("/", data=b'print("hello world")\n')
 
1894         self.assertEqual(response.status, 204)
 
1895         self.assertEqual(await response.read(), b"")
 
1897     @skip_if_exception("ClientOSError")
 
1898     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1900     async def test_blackd_request_syntax_error(self) -> None:
 
1901         response = await self.client.post("/", data=b"what even ( is")
 
1902         self.assertEqual(response.status, 400)
 
1903         content = await response.text()
 
1905             content.startswith("Cannot parse"),
 
1906             msg=f"Expected error to start with 'Cannot parse', got {repr(content)}",
 
1909     @skip_if_exception("ClientOSError")
 
1910     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1912     async def test_blackd_unsupported_version(self) -> None:
 
1913         response = await self.client.post(
 
1914             "/", data=b"what", headers={blackd.PROTOCOL_VERSION_HEADER: "2"}
 
1916         self.assertEqual(response.status, 501)
 
1918     @skip_if_exception("ClientOSError")
 
1919     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1921     async def test_blackd_supported_version(self) -> None:
 
1922         response = await self.client.post(
 
1923             "/", data=b"what", headers={blackd.PROTOCOL_VERSION_HEADER: "1"}
 
1925         self.assertEqual(response.status, 200)
 
1927     @skip_if_exception("ClientOSError")
 
1928     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1930     async def test_blackd_invalid_python_variant(self) -> None:
 
1931         async def check(header_value: str, expected_status: int = 400) -> None:
 
1932             response = await self.client.post(
 
1933                 "/", data=b"what", headers={blackd.PYTHON_VARIANT_HEADER: header_value}
 
1935             self.assertEqual(response.status, expected_status)
 
1938         await check("ruby3.5")
 
1939         await check("pyi3.6")
 
1940         await check("py1.5")
 
1942         await check("py2.8")
 
1944         await check("pypy3.0")
 
1945         await check("jython3.4")
 
1947     @skip_if_exception("ClientOSError")
 
1948     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1950     async def test_blackd_pyi(self) -> None:
 
1951         source, expected = read_data("stub.pyi")
 
1952         response = await self.client.post(
 
1953             "/", data=source, headers={blackd.PYTHON_VARIANT_HEADER: "pyi"}
 
1955         self.assertEqual(response.status, 200)
 
1956         self.assertEqual(await response.text(), expected)
 
1958     @skip_if_exception("ClientOSError")
 
1959     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1961     async def test_blackd_diff(self) -> None:
 
1962         diff_header = re.compile(
 
1963             r"(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"
 
1966         source, _ = read_data("blackd_diff.py")
 
1967         expected, _ = read_data("blackd_diff.diff")
 
1969         response = await self.client.post(
 
1970             "/", data=source, headers={blackd.DIFF_HEADER: "true"}
 
1972         self.assertEqual(response.status, 200)
 
1974         actual = await response.text()
 
1975         actual = diff_header.sub(DETERMINISTIC_HEADER, actual)
 
1976         self.assertEqual(actual, expected)
 
1978     @skip_if_exception("ClientOSError")
 
1979     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
1981     async def test_blackd_python_variant(self) -> None:
 
1984             "    and_has_a_bunch_of,\n"
 
1985             "    very_long_arguments_too,\n"
 
1986             "    and_lots_of_them_as_well_lol,\n"
 
1987             "    **and_very_long_keyword_arguments\n"
 
1992         async def check(header_value: str, expected_status: int) -> None:
 
1993             response = await self.client.post(
 
1994                 "/", data=code, headers={blackd.PYTHON_VARIANT_HEADER: header_value}
 
1997                 response.status, expected_status, msg=await response.text()
 
2000         await check("3.6", 200)
 
2001         await check("py3.6", 200)
 
2002         await check("3.6,3.7", 200)
 
2003         await check("3.6,py3.7", 200)
 
2004         await check("py36,py37", 200)
 
2005         await check("36", 200)
 
2006         await check("3.6.4", 200)
 
2008         await check("2", 204)
 
2009         await check("2.7", 204)
 
2010         await check("py2.7", 204)
 
2011         await check("3.4", 204)
 
2012         await check("py3.4", 204)
 
2013         await check("py34,py36", 204)
 
2014         await check("34", 204)
 
2016     @skip_if_exception("ClientOSError")
 
2017     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
2019     async def test_blackd_line_length(self) -> None:
 
2020         response = await self.client.post(
 
2021             "/", data=b'print("hello")\n', headers={blackd.LINE_LENGTH_HEADER: "7"}
 
2023         self.assertEqual(response.status, 200)
 
2025     @skip_if_exception("ClientOSError")
 
2026     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
2028     async def test_blackd_invalid_line_length(self) -> None:
 
2029         response = await self.client.post(
 
2030             "/", data=b'print("hello")\n', headers={blackd.LINE_LENGTH_HEADER: "NaN"}
 
2032         self.assertEqual(response.status, 400)
 
2034     @skip_if_exception("ClientOSError")
 
2035     @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
 
2037     async def test_blackd_response_black_version_header(self) -> None:
 
2038         response = await self.client.post("/")
 
2039         self.assertIsNotNone(response.headers.get(blackd.BLACK_VERSION_HEADER))
 
2042 if __name__ == "__main__":
 
2043     unittest.main(module="test_black")