import click
import pytest
-import regex as re
+import re
from click import unstyle
from click.testing import CliRunner
from pathspec import PathSpec
DATA_DIR,
DEFAULT_MODE,
DETERMINISTIC_HEADER,
+ PROJECT_ROOT,
PY36_VERSIONS,
THIS_DIR,
BlackBaseTestCase,
R = TypeVar("R")
# Match the time output in a diff, but nothing else
-DIFF_TIME = re.compile(r"\t[\d-:+\. ]+")
+DIFF_TIME = re.compile(r"\t[\d\-:+\. ]+")
@contextmanager
runner = BlackRunner()
if ignore_config:
args = ["--verbose", "--config", str(THIS_DIR / "empty.toml"), *args]
- result = runner.invoke(black.main, args)
+ result = runner.invoke(black.main, args, catch_exceptions=False)
assert result.stdout_bytes is not None
assert result.stderr_bytes is not None
msg = (
node = black.lib2to3_parse("def fn(a, /, b): ...")
self.assertEqual(black.get_features_used(node), {Feature.POS_ONLY_ARGUMENTS})
+ def test_get_features_used_for_future_flags(self) -> None:
+ for src, features in [
+ ("from __future__ import annotations", {Feature.FUTURE_ANNOTATIONS}),
+ (
+ "from __future__ import (other, annotations)",
+ {Feature.FUTURE_ANNOTATIONS},
+ ),
+ ("a = 1 + 2\nfrom something import annotations", set()),
+ ("from __future__ import x, y", set()),
+ ]:
+ with self.subTest(src=src, features=features):
+ node = black.lib2to3_parse(src)
+ future_imports = black.get_future_imports(node)
+ self.assertEqual(
+ black.get_features_used(node, future_imports=future_imports),
+ features,
+ )
+
def test_get_future_imports(self) -> None:
node = black.lib2to3_parse("\n")
self.assertEqual(set(), black.get_future_imports(node))
)
self.assertEqual({"unicode_literals", "print"}, black.get_future_imports(node))
+ @pytest.mark.incompatible_with_mypyc
def test_debug_visitor(self) -> None:
source, _ = read_data("debug_visitor.py")
expected, _ = read_data("debug_visitor.out")
self.assertEqual(len(n.children), 1)
self.assertEqual(n.children[0].type, black.token.ENDMARKER)
+ @pytest.mark.incompatible_with_mypyc
@unittest.skipIf(os.environ.get("SKIP_AST_PRINT"), "user set SKIP_AST_PRINT")
def test_assertFormatEqual(self) -> None:
out_lines = []
symlink = workspace / "broken_link.py"
try:
symlink.symlink_to("nonexistent.py")
- except OSError as e:
+ except (OSError, NotImplementedError) as e:
self.skipTest(f"Can't create symlinks: {e}")
self.invokeBlack([str(workspace.resolve())])
actual = result.output
self.assertFormatEqual(actual, expected)
+ @pytest.mark.incompatible_with_mypyc
def test_reformat_one_with_stdin(self) -> None:
with patch(
"black.format_stdin_to_stdout",
fsts.assert_called_once()
report.done.assert_called_with(path, black.Changed.YES)
+ @pytest.mark.incompatible_with_mypyc
def test_reformat_one_with_stdin_filename(self) -> None:
with patch(
"black.format_stdin_to_stdout",
# __BLACK_STDIN_FILENAME__ should have been stripped
report.done.assert_called_with(expected, black.Changed.YES)
+ @pytest.mark.incompatible_with_mypyc
def test_reformat_one_with_stdin_filename_pyi(self) -> None:
with patch(
"black.format_stdin_to_stdout",
# __BLACK_STDIN_FILENAME__ should have been stripped
report.done.assert_called_with(expected, black.Changed.YES)
+ @pytest.mark.incompatible_with_mypyc
def test_reformat_one_with_stdin_filename_ipynb(self) -> None:
with patch(
"black.format_stdin_to_stdout",
# __BLACK_STDIN_FILENAME__ should have been stripped
report.done.assert_called_with(expected, black.Changed.YES)
+ @pytest.mark.incompatible_with_mypyc
def test_reformat_one_with_stdin_and_existing_path(self) -> None:
with patch(
"black.format_stdin_to_stdout",
self.assertEqual(config["exclude"], r"\.pyi?$")
self.assertEqual(config["include"], r"\.py?$")
+ @pytest.mark.incompatible_with_mypyc
def test_find_project_root(self) -> None:
with TemporaryDirectory() as workspace:
root = Path(workspace)
)
expected = 'def foo():\n """Testing\n Testing"""\n print "Foo"\n'
- result = CliRunner().invoke(
+ result = BlackRunner().invoke(
black.main,
["-", "-q", "--target-version=py27"],
input=BytesIO(source),
)
self.assertEqual(result.exit_code, 0)
- actual = result.output
+ actual = result.stdout
self.assertFormatEqual(actual, expected)
@staticmethod
assert output == result_diff, "The output did not match the expected value."
assert result.exit_code == 0, "The exit code is incorrect."
+ @pytest.mark.incompatible_with_mypyc
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
self.compare_results(result, formatted, 0)
+ @pytest.mark.incompatible_with_mypyc
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)
+ # This is the only directory known to contain a pyproject.toml
+ with change_directory(PROJECT_ROOT):
+ CliRunner().invoke(black.main, args)
+ pyproject_path = Path(Path.cwd(), "pyproject.toml").resolve()
- 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[0].lower() == str(pyproject_path).lower()
), "Incorrect config loaded."
+ @pytest.mark.incompatible_with_mypyc
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")):
+ with change_directory(THIS_DIR):
args = ["--code", "print"]
CliRunner().invoke(black.main, args)
call_args[0].lower() == str(pyproject_path).lower()
), "Incorrect config loaded."
+ def test_for_handled_unexpected_eof_error(self) -> None:
+ """
+ Test that an unexpected EOF SyntaxError is nicely presented.
+ """
+ with pytest.raises(black.parsing.InvalidInput) as exc_info:
+ black.lib2to3_parse("print(", {})
+
+ exc_info.match("Cannot parse: 2:0: EOF in multi-line statement")
+
class TestCaching:
def test_cache_broken_file(self) -> None:
report=black.Report(),
stdin_filename=stdin_filename,
)
- assert sorted(list(collected)) == sorted(gs_expected)
+ assert sorted(collected) == sorted(gs_expected)
class TestFileCollection:
src, expected, exclude=r"\.pyi$", extend_exclude=r"\.definitely_exclude"
)
+ @pytest.mark.incompatible_with_mypyc
def test_symlink_out_of_root_directory(self) -> None:
path = MagicMock()
root = THIS_DIR.resolve()
)
-with open(black.__file__, "r", encoding="utf-8") as _bf:
- black_source_lines = _bf.readlines()
+@pytest.mark.python2
+@pytest.mark.parametrize("explicit", [True, False], ids=["explicit", "autodetection"])
+def test_python_2_deprecation_with_target_version(explicit: bool) -> None:
+ args = [
+ "--config",
+ str(THIS_DIR / "empty.toml"),
+ str(DATA_DIR / "python2.py"),
+ "--check",
+ ]
+ if explicit:
+ args.append("--target-version=py27")
+ with cache_dir():
+ result = BlackRunner().invoke(black.main, args)
+ assert "DEPRECATION: Python 2 support will be removed" in result.stderr
+
+
+@pytest.mark.python2
+def test_python_2_deprecation_autodetection_extended() -> None:
+ # this test has a similar construction to test_get_features_used_decorator
+ python2, non_python2 = read_data("python2_detection")
+ for python2_case in python2.split("###"):
+ node = black.lib2to3_parse(python2_case)
+ assert black.detect_target_versions(node) == {TargetVersion.PY27}, python2_case
+ for non_python2_case in non_python2.split("###"):
+ node = black.lib2to3_parse(non_python2_case)
+ assert black.detect_target_versions(node) != {
+ TargetVersion.PY27
+ }, non_python2_case
+
+
+try:
+ with open(black.__file__, "r", encoding="utf-8") as _bf:
+ black_source_lines = _bf.readlines()
+except UnicodeDecodeError:
+ if not black.COMPILED:
+ raise
def tracefunc(