+ finally:
+ os.unlink(tmp_file)
+ actual = result.output
+ actual = diff_header.sub(DETERMINISTIC_HEADER, actual)
+ self.assertEqual(actual, expected)
+
+ @pytest.mark.python2
+ def test_docstring_reformat_for_py27(self) -> None:
+ """
+ Check that stripping trailing whitespace from Python 2 docstrings
+ doesn't trigger a "not equivalent to source" error
+ """
+ source = (
+ b'def foo():\r\n """Testing\r\n Testing """\r\n print "Foo"\r\n'
+ )
+ expected = 'def foo():\n """Testing\n Testing"""\n print "Foo"\n'
+
+ result = CliRunner().invoke(
+ black.main,
+ ["-", "-q", "--target-version=py27"],
+ input=BytesIO(source),
+ )
+
+ self.assertEqual(result.exit_code, 0)
+ actual = result.output
+ self.assertFormatEqual(actual, expected)
+
+ @staticmethod
+ def compare_results(
+ result: click.testing.Result, expected_value: str, expected_exit_code: int
+ ) -> None:
+ """Helper method to test the value and exit code of a click Result."""
+ assert (
+ result.output == expected_value
+ ), "The output did not match the expected value."
+ assert result.exit_code == expected_exit_code, "The exit code is incorrect."
+
+ def test_code_option(self) -> None:
+ """Test the code option with no changes."""
+ code = 'print("Hello world")\n'
+ args = ["--code", code]
+ result = CliRunner().invoke(black.main, args)
+
+ self.compare_results(result, code, 0)
+
+ def test_code_option_changed(self) -> None:
+ """Test the code option when changes are required."""
+ code = "print('hello world')"
+ formatted = black.format_str(code, mode=DEFAULT_MODE)
+
+ args = ["--code", code]
+ result = CliRunner().invoke(black.main, args)
+
+ self.compare_results(result, formatted, 0)
+
+ def test_code_option_check(self) -> None:
+ """Test the code option when check is passed."""
+ args = ["--check", "--code", 'print("Hello world")\n']
+ result = CliRunner().invoke(black.main, args)
+ self.compare_results(result, "", 0)
+
+ def test_code_option_check_changed(self) -> None:
+ """Test the code option when changes are required, and check is passed."""
+ args = ["--check", "--code", "print('hello world')"]
+ result = CliRunner().invoke(black.main, args)
+ self.compare_results(result, "", 1)
+
+ def test_code_option_diff(self) -> None:
+ """Test the code option when diff is passed."""
+ code = "print('hello world')"
+ formatted = black.format_str(code, mode=DEFAULT_MODE)
+ result_diff = diff(code, formatted, "STDIN", "STDOUT")
+
+ args = ["--diff", "--code", code]
+ result = CliRunner().invoke(black.main, args)
+
+ # Remove time from diff
+ output = DIFF_TIME.sub("", result.output)
+
+ assert output == result_diff, "The output did not match the expected value."
+ assert result.exit_code == 0, "The exit code is incorrect."
+
+ def test_code_option_color_diff(self) -> None:
+ """Test the code option when color and diff are passed."""
+ code = "print('hello world')"
+ formatted = black.format_str(code, mode=DEFAULT_MODE)
+
+ result_diff = diff(code, formatted, "STDIN", "STDOUT")
+ result_diff = color_diff(result_diff)
+
+ args = ["--diff", "--color", "--code", code]
+ result = CliRunner().invoke(black.main, args)
+
+ # Remove time from diff
+ output = DIFF_TIME.sub("", result.output)
+
+ assert output == result_diff, "The output did not match the expected value."
+ assert result.exit_code == 0, "The exit code is incorrect."
+
+ def test_code_option_safe(self) -> None:
+ """Test that the code option throws an error when the sanity checks fail."""
+ # Patch black.assert_equivalent to ensure the sanity checks fail
+ with patch.object(black, "assert_equivalent", side_effect=AssertionError):
+ code = 'print("Hello world")'
+ error_msg = f"{code}\nerror: cannot format <string>: \n"
+
+ args = ["--safe", "--code", code]
+ result = CliRunner().invoke(black.main, args)
+
+ self.compare_results(result, error_msg, 123)
+
+ def test_code_option_fast(self) -> None:
+ """Test that the code option ignores errors when the sanity checks fail."""
+ # Patch black.assert_equivalent to ensure the sanity checks fail
+ with patch.object(black, "assert_equivalent", side_effect=AssertionError):
+ code = 'print("Hello world")'
+ formatted = black.format_str(code, mode=DEFAULT_MODE)
+
+ args = ["--fast", "--code", code]
+ result = CliRunner().invoke(black.main, args)
+
+ self.compare_results(result, formatted, 0)
+
+ def test_code_option_config(self) -> None:
+ """
+ Test that the code option finds the pyproject.toml in the current directory.
+ """
+ with patch.object(black, "parse_pyproject_toml", return_value={}) as parse:
+ args = ["--code", "print"]
+ CliRunner().invoke(black.main, args)
+
+ pyproject_path = Path(Path().cwd(), "pyproject.toml").resolve()
+ assert (
+ len(parse.mock_calls) >= 1
+ ), "Expected config parse to be called with the current directory."
+
+ _, call_args, _ = parse.mock_calls[0]
+ assert (
+ call_args[0].lower() == str(pyproject_path).lower()
+ ), "Incorrect config loaded."
+
+ def test_code_option_parent_config(self) -> None:
+ """
+ Test that the code option finds the pyproject.toml in the parent directory.
+ """
+ with patch.object(black, "parse_pyproject_toml", return_value={}) as parse:
+ with change_directory(Path("tests")):
+ args = ["--code", "print"]
+ CliRunner().invoke(black.main, args)
+
+ pyproject_path = Path(Path().cwd().parent, "pyproject.toml").resolve()
+ assert (
+ len(parse.mock_calls) >= 1
+ ), "Expected config parse to be called with the current directory."
+
+ _, call_args, _ = parse.mock_calls[0]
+ assert (
+ call_args[0].lower() == str(pyproject_path).lower()
+ ), "Incorrect config loaded."
+
+
+with open(black.__file__, "r", encoding="utf-8") as _bf:
+ black_source_lines = _bf.readlines()
+
+
+def tracefunc(frame: types.FrameType, event: str, arg: Any) -> Callable:
+ """Show function calls `from black/__init__.py` as they happen.
+
+ Register this with `sys.settrace()` in a test you're debugging.
+ """
+ if event != "call":
+ return tracefunc
+
+ stack = len(inspect.stack()) - 19
+ stack *= 2
+ filename = frame.f_code.co_filename
+ lineno = frame.f_lineno
+ func_sig_lineno = lineno - 1
+ funcname = black_source_lines[func_sig_lineno].strip()
+ while funcname.startswith("@"):
+ func_sig_lineno += 1
+ funcname = black_source_lines[func_sig_lineno].strip()
+ if "black/__init__.py" in filename:
+ print(f"{' ' * stack}{lineno}:{funcname}")
+ return tracefunc