from io import BytesIO, TextIOWrapper
import os
from pathlib import Path
-import re
+import regex as re
import sys
from tempfile import TemporaryDirectory
-from typing import Any, BinaryIO, Generator, List, Tuple, Iterator, TypeVar
+from typing import Any, BinaryIO, Dict, Generator, List, Tuple, Iterator, TypeVar
import unittest
from unittest.mock import patch, MagicMock
+import click
from click import unstyle
from click.testing import CliRunner
else:
has_blackd_deps = True
+from pathspec import PathSpec
+
+# Import other test classes
+from .test_primer import PrimerCLITests # noqa: F401
+
+
ff = partial(black.format_file_in_place, mode=black.FileMode(), fast=True)
fs = partial(black.format_str, mode=black.FileMode())
THIS_FILE = Path(__file__)
THIS_DIR = THIS_FILE.parent
+PROJECT_ROOT = THIS_DIR.parent
+DETERMINISTIC_HEADER = "[Deterministic header]"
EMPTY_LINE = "# EMPTY LINE WITH WHITESPACE" + " (this comment will be removed)"
PY36_ARGS = [
f"--target-version={version.name.lower()}" for version in black.PY36_VERSIONS
name += ".py"
_input: List[str] = []
_output: List[str] = []
- base_dir = THIS_DIR / "data" if data else THIS_DIR
+ base_dir = THIS_DIR / "data" if data else PROJECT_ROOT
with open(base_dir / name, "r", encoding="utf8") as test:
lines = test.readlines()
result = _input
@contextmanager
-def event_loop(close: bool) -> Iterator[None]:
+def event_loop() -> Iterator[None]:
policy = asyncio.get_event_loop_policy()
loop = policy.new_event_loop()
asyncio.set_event_loop(loop)
yield
finally:
- if close:
- loop.close()
+ loop.close()
@contextmanager
if exc.__class__.__name__ == e:
unittest.skip(f"Encountered expected exception {exc}, skipping")
else:
- raise exc
+ raise
+
+
+class FakeContext(click.Context):
+ """A fake click Context for when calling functions that need it."""
+
+ def __init__(self) -> None:
+ self.default_map: Dict[str, Any] = {}
+
+
+class FakeParameter(click.Parameter):
+ """A fake click Parameter for when calling functions that need it."""
+
+ def __init__(self) -> None:
+ pass
class BlackRunner(CliRunner):
) -> None:
runner = BlackRunner()
if ignore_config:
- args = ["--config", str(THIS_DIR / "empty.toml"), *args]
+ args = ["--verbose", "--config", str(THIS_DIR / "empty.toml"), *args]
result = runner.invoke(black.main, args)
- self.assertEqual(result.exit_code, exit_code, msg=runner.stderr_bytes.decode())
+ self.assertEqual(
+ result.exit_code,
+ exit_code,
+ msg=(
+ f"Failed with args: {args}\n"
+ f"stdout: {runner.stdout_bytes.decode()!r}\n"
+ f"stderr: {runner.stderr_bytes.decode()!r}\n"
+ f"exception: {result.exception}"
+ ),
+ )
+
+ @patch("black.dump_to_file", dump_to_stderr)
+ def checkSourceFile(self, name: str) -> None:
+ path = THIS_DIR.parent / name
+ source, expected = read_data(str(path), data=False)
+ actual = fs(source)
+ self.assertFormatEqual(expected, actual)
+ black.assert_equivalent(source, actual)
+ black.assert_stable(source, actual, black.FileMode())
+ self.assertFalse(ff(path))
@patch("black.dump_to_file", dump_to_stderr)
def test_empty(self) -> None:
os.unlink(tmp_file)
self.assertFormatEqual(expected, actual)
- @patch("black.dump_to_file", dump_to_stderr)
def test_self(self) -> None:
- source, expected = read_data("test_black", data=False)
- actual = fs(source)
- self.assertFormatEqual(expected, actual)
- black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
- self.assertFalse(ff(THIS_FILE))
+ self.checkSourceFile("tests/test_black.py")
- @patch("black.dump_to_file", dump_to_stderr)
def test_black(self) -> None:
- source, expected = read_data("../black", data=False)
- actual = fs(source)
- self.assertFormatEqual(expected, actual)
- black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
- self.assertFalse(ff(THIS_DIR / ".." / "black.py"))
+ self.checkSourceFile("src/black/__init__.py")
+
+ def test_pygram(self) -> None:
+ self.checkSourceFile("src/blib2to3/pygram.py")
+
+ def test_pytree(self) -> None:
+ self.checkSourceFile("src/blib2to3/pytree.py")
+
+ def test_conv(self) -> None:
+ self.checkSourceFile("src/blib2to3/pgen2/conv.py")
+
+ def test_driver(self) -> None:
+ self.checkSourceFile("src/blib2to3/pgen2/driver.py")
+
+ def test_grammar(self) -> None:
+ self.checkSourceFile("src/blib2to3/pgen2/grammar.py")
+
+ def test_literals(self) -> None:
+ self.checkSourceFile("src/blib2to3/pgen2/literals.py")
+
+ def test_parse(self) -> None:
+ self.checkSourceFile("src/blib2to3/pgen2/parse.py")
+
+ def test_pgen(self) -> None:
+ self.checkSourceFile("src/blib2to3/pgen2/pgen.py")
+
+ def test_tokenize(self) -> None:
+ self.checkSourceFile("src/blib2to3/pgen2/tokenize.py")
+
+ def test_token(self) -> None:
+ self.checkSourceFile("src/blib2to3/pgen2/token.py")
+
+ def test_setup(self) -> None:
+ self.checkSourceFile("setup.py")
def test_piping(self) -> None:
- source, expected = read_data("../black", data=False)
+ source, expected = read_data("src/black/__init__", data=False)
result = BlackRunner().invoke(
black.main,
["-", "--fast", f"--line-length={black.DEFAULT_LINE_LENGTH}"],
def test_piping_diff(self) -> None:
diff_header = re.compile(
- rf"(STDIN|STDOUT)\t\d\d\d\d-\d\d-\d\d "
- rf"\d\d:\d\d:\d\d\.\d\d\d\d\d\d \+\d\d\d\d"
+ r"(STDIN|STDOUT)\t\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d\.\d\d\d\d\d\d "
+ r"\+\d\d\d\d"
)
source, _ = read_data("expression.py")
expected, _ = read_data("expression.diff")
black.main, args, input=BytesIO(source.encode("utf8"))
)
self.assertEqual(result.exit_code, 0)
- actual = diff_header.sub("[Deterministic header]", result.output)
+ actual = diff_header.sub(DETERMINISTIC_HEADER, result.output)
actual = actual.rstrip() + "\n" # the diff output has a trailing space
self.assertEqual(expected, actual)
- @patch("black.dump_to_file", dump_to_stderr)
- def test_setup(self) -> None:
- source, expected = read_data("../setup", data=False)
- actual = fs(source)
- self.assertFormatEqual(expected, actual)
- black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
- self.assertFalse(ff(THIS_DIR / ".." / "setup.py"))
+ def test_piping_diff_with_color(self) -> None:
+ source, _ = read_data("expression.py")
+ config = THIS_DIR / "data" / "empty_pyproject.toml"
+ args = [
+ "-",
+ "--fast",
+ f"--line-length={black.DEFAULT_LINE_LENGTH}",
+ "--diff",
+ "--color",
+ f"--config={config}",
+ ]
+ result = BlackRunner().invoke(
+ black.main, args, input=BytesIO(source.encode("utf8"))
+ )
+ actual = result.output
+ # Again, the contents are checked in a different test, so only look for colors.
+ self.assertIn("\033[1;37m", actual)
+ self.assertIn("\033[36m", actual)
+ self.assertIn("\033[32m", actual)
+ self.assertIn("\033[31m", actual)
+ self.assertIn("\033[0m", actual)
@patch("black.dump_to_file", dump_to_stderr)
def test_function(self) -> None:
tmp_file = Path(black.dump_to_file(source))
diff_header = re.compile(
rf"{re.escape(str(tmp_file))}\t\d\d\d\d-\d\d-\d\d "
- rf"\d\d:\d\d:\d\d\.\d\d\d\d\d\d \+\d\d\d\d"
+ r"\d\d:\d\d:\d\d\.\d\d\d\d\d\d \+\d\d\d\d"
)
try:
result = BlackRunner().invoke(black.main, ["--diff", str(tmp_file)])
finally:
os.unlink(tmp_file)
actual = result.output
- actual = diff_header.sub("[Deterministic header]", actual)
+ actual = diff_header.sub(DETERMINISTIC_HEADER, actual)
actual = actual.rstrip() + "\n" # the diff output has a trailing space
if expected != actual:
dump = black.dump_to_file(actual)
msg = (
- f"Expected diff isn't equal to the actual. If you made changes "
- f"to expression.py and this is an anticipated difference, "
- f"overwrite tests/data/expression.diff with {dump}"
+ "Expected diff isn't equal to the actual. If you made changes to"
+ " expression.py and this is an anticipated difference, overwrite"
+ f" tests/data/expression.diff with {dump}"
)
self.assertEqual(expected, actual, msg)
+ def test_expression_diff_with_color(self) -> None:
+ source, _ = read_data("expression.py")
+ expected, _ = read_data("expression.diff")
+ tmp_file = Path(black.dump_to_file(source))
+ try:
+ result = BlackRunner().invoke(
+ black.main, ["--diff", "--color", str(tmp_file)]
+ )
+ finally:
+ os.unlink(tmp_file)
+ actual = result.output
+ # We check the contents of the diff in `test_expression_diff`. All
+ # we need to check here is that color codes exist in the result.
+ self.assertIn("\033[1;37m", actual)
+ self.assertIn("\033[36m", actual)
+ self.assertIn("\033[32m", actual)
+ self.assertIn("\033[31m", actual)
+ self.assertIn("\033[0m", actual)
+
@patch("black.dump_to_file", dump_to_stderr)
def test_fstring(self) -> None:
source, expected = read_data("fstring")
black.assert_stable(source, actual, black.FileMode())
mode = black.FileMode(string_normalization=False)
not_normalized = fs(source, mode=mode)
- self.assertFormatEqual(source, not_normalized)
+ self.assertFormatEqual(source.replace("\\\n", ""), not_normalized)
black.assert_equivalent(source, not_normalized)
black.assert_stable(source, not_normalized, mode=mode)
+ @patch("black.dump_to_file", dump_to_stderr)
+ def test_docstring(self) -> None:
+ source, expected = read_data("docstring")
+ actual = fs(source)
+ self.assertFormatEqual(expected, actual)
+ black.assert_equivalent(source, actual)
+ black.assert_stable(source, actual, black.FileMode())
+
+ def test_long_strings(self) -> None:
+ """Tests for splitting long strings."""
+ source, expected = read_data("long_strings")
+ actual = fs(source)
+ self.assertFormatEqual(expected, actual)
+ black.assert_equivalent(source, actual)
+ black.assert_stable(source, actual, black.FileMode())
+
+ @patch("black.dump_to_file", dump_to_stderr)
+ def test_long_strings__edge_case(self) -> None:
+ """Edge-case tests for splitting long strings."""
+ source, expected = read_data("long_strings__edge_case")
+ actual = fs(source)
+ self.assertFormatEqual(expected, actual)
+ black.assert_equivalent(source, actual)
+ black.assert_stable(source, actual, black.FileMode())
+
+ @patch("black.dump_to_file", dump_to_stderr)
+ def test_long_strings__regression(self) -> None:
+ """Regression tests for splitting long strings."""
+ source, expected = read_data("long_strings__regression")
+ actual = fs(source)
+ self.assertFormatEqual(expected, actual)
+ black.assert_equivalent(source, actual)
+ black.assert_stable(source, actual, black.FileMode())
+
@patch("black.dump_to_file", dump_to_stderr)
def test_slices(self) -> None:
source, expected = read_data("slices")
# but not on 3.6, because we use async as a reserved keyword
self.invokeBlack([str(source_path), "--target-version", "py36"], exit_code=123)
+ @patch("black.dump_to_file", dump_to_stderr)
+ def test_python38(self) -> None:
+ source, expected = read_data("python38")
+ actual = fs(source)
+ self.assertFormatEqual(expected, actual)
+ major, minor = sys.version_info[:2]
+ if major > 3 or (major == 3 and minor >= 8):
+ black.assert_equivalent(source, actual)
+ black.assert_stable(source, actual, black.FileMode())
+
@patch("black.dump_to_file", dump_to_stderr)
def test_fmtonoff(self) -> None:
source, expected = read_data("fmtonoff")
black.assert_equivalent(source, actual)
black.assert_stable(source, actual, black.FileMode())
+ @patch("black.dump_to_file", dump_to_stderr)
+ def test_fmtonoff3(self) -> None:
+ source, expected = read_data("fmtonoff3")
+ actual = fs(source)
+ self.assertFormatEqual(expected, actual)
+ black.assert_equivalent(source, actual)
+ black.assert_stable(source, actual, black.FileMode())
+
+ @patch("black.dump_to_file", dump_to_stderr)
+ def test_fmtonoff4(self) -> None:
+ source, expected = read_data("fmtonoff4")
+ actual = fs(source)
+ self.assertFormatEqual(expected, actual)
+ black.assert_equivalent(source, actual)
+ black.assert_stable(source, actual, black.FileMode())
+
@patch("black.dump_to_file", dump_to_stderr)
def test_remove_empty_parentheses_after_class(self) -> None:
source, expected = read_data("class_blank_parentheses")
self.assertEqual(err_lines[-1], "error: cannot format e1: boom")
self.assertEqual(
unstyle(str(report)),
- "1 file reformatted, 2 files left unchanged, "
- "1 file failed to reformat.",
+ "1 file reformatted, 2 files left unchanged, 1 file failed to"
+ " reformat.",
)
self.assertEqual(report.return_code, 123)
report.done(Path("f3"), black.Changed.YES)
self.assertEqual(out_lines[-1], "reformatted f3")
self.assertEqual(
unstyle(str(report)),
- "2 files reformatted, 2 files left unchanged, "
- "1 file failed to reformat.",
+ "2 files reformatted, 2 files left unchanged, 1 file failed to"
+ " reformat.",
)
self.assertEqual(report.return_code, 123)
report.failed(Path("e2"), "boom")
self.assertEqual(err_lines[-1], "error: cannot format e2: boom")
self.assertEqual(
unstyle(str(report)),
- "2 files reformatted, 2 files left unchanged, "
- "2 files failed to reformat.",
+ "2 files reformatted, 2 files left unchanged, 2 files failed to"
+ " reformat.",
)
self.assertEqual(report.return_code, 123)
report.path_ignored(Path("wat"), "no match")
self.assertEqual(out_lines[-1], "wat ignored: no match")
self.assertEqual(
unstyle(str(report)),
- "2 files reformatted, 2 files left unchanged, "
- "2 files failed to reformat.",
+ "2 files reformatted, 2 files left unchanged, 2 files failed to"
+ " reformat.",
)
self.assertEqual(report.return_code, 123)
report.done(Path("f4"), black.Changed.NO)
self.assertEqual(out_lines[-1], "f4 already well formatted, good job.")
self.assertEqual(
unstyle(str(report)),
- "2 files reformatted, 3 files left unchanged, "
- "2 files failed to reformat.",
+ "2 files reformatted, 3 files left unchanged, 2 files failed to"
+ " reformat.",
)
self.assertEqual(report.return_code, 123)
report.check = True
self.assertEqual(
unstyle(str(report)),
- "2 files would be reformatted, 3 files would be left unchanged, "
- "2 files would fail to reformat.",
+ "2 files would be reformatted, 3 files would be left unchanged, 2 files"
+ " would fail to reformat.",
+ )
+ report.check = False
+ report.diff = True
+ self.assertEqual(
+ unstyle(str(report)),
+ "2 files would be reformatted, 3 files would be left unchanged, 2 files"
+ " would fail to reformat.",
)
def test_report_quiet(self) -> None:
self.assertEqual(err_lines[-1], "error: cannot format e1: boom")
self.assertEqual(
unstyle(str(report)),
- "1 file reformatted, 2 files left unchanged, "
- "1 file failed to reformat.",
+ "1 file reformatted, 2 files left unchanged, 1 file failed to"
+ " reformat.",
)
self.assertEqual(report.return_code, 123)
report.done(Path("f3"), black.Changed.YES)
self.assertEqual(len(err_lines), 1)
self.assertEqual(
unstyle(str(report)),
- "2 files reformatted, 2 files left unchanged, "
- "1 file failed to reformat.",
+ "2 files reformatted, 2 files left unchanged, 1 file failed to"
+ " reformat.",
)
self.assertEqual(report.return_code, 123)
report.failed(Path("e2"), "boom")
self.assertEqual(err_lines[-1], "error: cannot format e2: boom")
self.assertEqual(
unstyle(str(report)),
- "2 files reformatted, 2 files left unchanged, "
- "2 files failed to reformat.",
+ "2 files reformatted, 2 files left unchanged, 2 files failed to"
+ " reformat.",
)
self.assertEqual(report.return_code, 123)
report.path_ignored(Path("wat"), "no match")
self.assertEqual(len(err_lines), 2)
self.assertEqual(
unstyle(str(report)),
- "2 files reformatted, 2 files left unchanged, "
- "2 files failed to reformat.",
+ "2 files reformatted, 2 files left unchanged, 2 files failed to"
+ " reformat.",
)
self.assertEqual(report.return_code, 123)
report.done(Path("f4"), black.Changed.NO)
self.assertEqual(len(err_lines), 2)
self.assertEqual(
unstyle(str(report)),
- "2 files reformatted, 3 files left unchanged, "
- "2 files failed to reformat.",
+ "2 files reformatted, 3 files left unchanged, 2 files failed to"
+ " reformat.",
)
self.assertEqual(report.return_code, 123)
report.check = True
self.assertEqual(
unstyle(str(report)),
- "2 files would be reformatted, 3 files would be left unchanged, "
- "2 files would fail to reformat.",
+ "2 files would be reformatted, 3 files would be left unchanged, 2 files"
+ " would fail to reformat.",
+ )
+ report.check = False
+ report.diff = True
+ self.assertEqual(
+ unstyle(str(report)),
+ "2 files would be reformatted, 3 files would be left unchanged, 2 files"
+ " would fail to reformat.",
)
def test_report_normal(self) -> None:
self.assertEqual(err_lines[-1], "error: cannot format e1: boom")
self.assertEqual(
unstyle(str(report)),
- "1 file reformatted, 2 files left unchanged, "
- "1 file failed to reformat.",
+ "1 file reformatted, 2 files left unchanged, 1 file failed to"
+ " reformat.",
)
self.assertEqual(report.return_code, 123)
report.done(Path("f3"), black.Changed.YES)
self.assertEqual(out_lines[-1], "reformatted f3")
self.assertEqual(
unstyle(str(report)),
- "2 files reformatted, 2 files left unchanged, "
- "1 file failed to reformat.",
+ "2 files reformatted, 2 files left unchanged, 1 file failed to"
+ " reformat.",
)
self.assertEqual(report.return_code, 123)
report.failed(Path("e2"), "boom")
self.assertEqual(err_lines[-1], "error: cannot format e2: boom")
self.assertEqual(
unstyle(str(report)),
- "2 files reformatted, 2 files left unchanged, "
- "2 files failed to reformat.",
+ "2 files reformatted, 2 files left unchanged, 2 files failed to"
+ " reformat.",
)
self.assertEqual(report.return_code, 123)
report.path_ignored(Path("wat"), "no match")
self.assertEqual(len(err_lines), 2)
self.assertEqual(
unstyle(str(report)),
- "2 files reformatted, 2 files left unchanged, "
- "2 files failed to reformat.",
+ "2 files reformatted, 2 files left unchanged, 2 files failed to"
+ " reformat.",
)
self.assertEqual(report.return_code, 123)
report.done(Path("f4"), black.Changed.NO)
self.assertEqual(len(err_lines), 2)
self.assertEqual(
unstyle(str(report)),
- "2 files reformatted, 3 files left unchanged, "
- "2 files failed to reformat.",
+ "2 files reformatted, 3 files left unchanged, 2 files failed to"
+ " reformat.",
)
self.assertEqual(report.return_code, 123)
report.check = True
self.assertEqual(
unstyle(str(report)),
- "2 files would be reformatted, 3 files would be left unchanged, "
- "2 files would fail to reformat.",
+ "2 files would be reformatted, 3 files would be left unchanged, 2 files"
+ " would fail to reformat.",
+ )
+ report.check = False
+ report.diff = True
+ self.assertEqual(
+ unstyle(str(report)),
+ "2 files would be reformatted, 3 files would be left unchanged, 2 files"
+ " would fail to reformat.",
)
def test_lib2to3_parse(self) -> None:
just_nl = "\n"
with self.assertRaises(black.NothingChanged):
black.format_file_contents(just_nl, mode=mode, fast=False)
- same = "l = [1, 2, 3]\n"
+ same = "j = [1, 2, 3]\n"
with self.assertRaises(black.NothingChanged):
black.format_file_contents(same, mode=mode, fast=False)
- different = "l = [1,2,3]"
+ different = "j = [1,2,3]"
expected = same
actual = black.format_file_contents(different, mode=mode, fast=False)
self.assertEqual(expected, actual)
with patch("black.out", out), patch("black.err", err):
with self.assertRaises(AssertionError):
- self.assertFormatEqual("l = [1, 2, 3]", "l = [1, 2, 3,]")
+ self.assertFormatEqual("j = [1, 2, 3]", "j = [1, 2, 3,]")
out_str = "".join(out_lines)
self.assertTrue("Expected tree:" in out_str)
with src.open("r") as fobj:
self.assertEqual(fobj.read(), "print('hello')")
- @event_loop(close=False)
+ @event_loop()
def test_cache_multiple_files(self) -> None:
mode = black.FileMode()
with cache_dir() as workspace, patch(
black.write_cache({}, [], mode)
self.assertTrue(workspace.exists())
- @event_loop(close=False)
+ @event_loop()
def test_failed_formatting_does_not_get_cached(self) -> None:
mode = black.FileMode()
with cache_dir() as workspace, patch(
mock.side_effect = OSError
black.write_cache({}, [], mode)
- @event_loop(close=False)
+ @event_loop()
+ @patch("black.ProcessPoolExecutor", MagicMock(side_effect=OSError))
+ def test_works_in_mono_process_only_environment(self) -> None:
+ with cache_dir() as workspace:
+ for f in [
+ (workspace / "one.py").resolve(),
+ (workspace / "two.py").resolve(),
+ ]:
+ f.write_text('print("hello")\n')
+ self.invokeBlack([str(workspace)])
+
+ @event_loop()
def test_check_diff_use_together(self) -> None:
with cache_dir():
# Files which will be reformatted.
two = black.read_cache(short_mode)
self.assertNotIn(path, two)
+ def test_tricky_unicode_symbols(self) -> None:
+ source, expected = read_data("tricky_unicode_symbols")
+ actual = fs(source)
+ self.assertFormatEqual(expected, actual)
+ black.assert_equivalent(source, actual)
+ black.assert_stable(source, actual, black.FileMode())
+
def test_single_file_force_pyi(self) -> None:
reg_mode = black.FileMode()
pyi_mode = black.FileMode(is_pyi=True)
self.assertNotIn(path, normal_cache)
self.assertEqual(actual, expected)
- @event_loop(close=False)
+ @event_loop()
def test_multi_file_force_pyi(self) -> None:
reg_mode = black.FileMode()
pyi_mode = black.FileMode(is_pyi=True)
self.assertNotIn(path, normal_cache)
self.assertEqual(actual, expected)
- @event_loop(close=False)
+ @event_loop()
def test_multi_file_force_py36(self) -> None:
reg_mode = black.FileMode()
py36_mode = black.FileMode(target_versions=black.PY36_VERSIONS)
self.assertIn(path, pyi_cache)
self.assertNotIn(path, normal_cache)
+ def test_collections(self) -> None:
+ source, expected = read_data("collections")
+ actual = fs(source)
+ self.assertFormatEqual(expected, actual)
+ black.assert_equivalent(source, actual)
+ black.assert_stable(source, actual, black.FileMode())
+
def test_pipe_force_py36(self) -> None:
source, expected = read_data("force_py36")
result = CliRunner().invoke(
include = re.compile(r"\.pyi?$")
exclude = re.compile(r"/exclude/|/\.definitely_exclude/")
report = black.Report()
+ gitignore = PathSpec.from_lines("gitwildmatch", [])
sources: List[Path] = []
expected = [
Path(path / "b/dont_exclude/a.py"),
]
this_abs = THIS_DIR.resolve()
sources.extend(
- black.gen_python_files_in_dir(path, this_abs, include, exclude, report)
+ black.gen_python_files(
+ path.iterdir(), this_abs, include, exclude, None, report, gitignore
+ )
+ )
+ self.assertEqual(sorted(expected), sorted(sources))
+
+ @patch("black.find_project_root", lambda *args: THIS_DIR.resolve())
+ def test_exclude_for_issue_1572(self) -> None:
+ # Exclude shouldn't touch files that were explicitly given to Black through the
+ # CLI. Exclude is supposed to only apply to the recursive discovery of files.
+ # https://github.com/psf/black/issues/1572
+ path = THIS_DIR / "data" / "include_exclude_tests"
+ include = ""
+ exclude = r"/exclude/|a\.py"
+ src = str(path / "b/exclude/a.py")
+ report = black.Report()
+ expected = [Path(path / "b/exclude/a.py")]
+ sources = list(
+ black.get_sources(
+ ctx=FakeContext(),
+ src=(src,),
+ quiet=True,
+ verbose=False,
+ include=include,
+ exclude=exclude,
+ force_exclude=None,
+ report=report,
+ )
+ )
+ self.assertEqual(sorted(expected), sorted(sources))
+
+ def test_gitignore_exclude(self) -> None:
+ path = THIS_DIR / "data" / "include_exclude_tests"
+ include = re.compile(r"\.pyi?$")
+ exclude = re.compile(r"")
+ report = black.Report()
+ gitignore = PathSpec.from_lines(
+ "gitwildmatch", ["exclude/", ".definitely_exclude"]
+ )
+ sources: List[Path] = []
+ expected = [
+ Path(path / "b/dont_exclude/a.py"),
+ Path(path / "b/dont_exclude/a.pyi"),
+ ]
+ this_abs = THIS_DIR.resolve()
+ sources.extend(
+ black.gen_python_files(
+ path.iterdir(), this_abs, include, exclude, None, report, gitignore
+ )
)
self.assertEqual(sorted(expected), sorted(sources))
def test_empty_include(self) -> None:
path = THIS_DIR / "data" / "include_exclude_tests"
report = black.Report()
+ gitignore = PathSpec.from_lines("gitwildmatch", [])
empty = re.compile(r"")
sources: List[Path] = []
expected = [
]
this_abs = THIS_DIR.resolve()
sources.extend(
- black.gen_python_files_in_dir(
- path, this_abs, empty, re.compile(black.DEFAULT_EXCLUDES), report
+ black.gen_python_files(
+ path.iterdir(),
+ this_abs,
+ empty,
+ re.compile(black.DEFAULT_EXCLUDES),
+ None,
+ report,
+ gitignore,
)
)
self.assertEqual(sorted(expected), sorted(sources))
def test_empty_exclude(self) -> None:
path = THIS_DIR / "data" / "include_exclude_tests"
report = black.Report()
+ gitignore = PathSpec.from_lines("gitwildmatch", [])
empty = re.compile(r"")
sources: List[Path] = []
expected = [
]
this_abs = THIS_DIR.resolve()
sources.extend(
- black.gen_python_files_in_dir(
- path, this_abs, re.compile(black.DEFAULT_INCLUDES), empty, report
+ black.gen_python_files(
+ path.iterdir(),
+ this_abs,
+ re.compile(black.DEFAULT_INCLUDES),
+ empty,
+ None,
+ report,
+ gitignore,
)
)
self.assertEqual(sorted(expected), sorted(sources))
def test_symlink_out_of_root_directory(self) -> None:
path = MagicMock()
- root = THIS_DIR
+ root = THIS_DIR.resolve()
child = MagicMock()
include = re.compile(black.DEFAULT_INCLUDES)
exclude = re.compile(black.DEFAULT_EXCLUDES)
report = black.Report()
+ gitignore = PathSpec.from_lines("gitwildmatch", [])
# `child` should behave like a symlink which resolved path is clearly
# outside of the `root` directory.
path.iterdir.return_value = [child]
child.resolve.return_value = Path("/a/b/c")
+ child.as_posix.return_value = "/a/b/c"
child.is_symlink.return_value = True
try:
- list(black.gen_python_files_in_dir(path, root, include, exclude, report))
+ list(
+ black.gen_python_files(
+ path.iterdir(), root, include, exclude, None, report, gitignore
+ )
+ )
except ValueError as ve:
self.fail(f"`get_python_files_in_dir()` failed: {ve}")
path.iterdir.assert_called_once()
# outside of the `root` directory.
child.is_symlink.return_value = False
with self.assertRaises(ValueError):
- list(black.gen_python_files_in_dir(path, root, include, exclude, report))
+ list(
+ black.gen_python_files(
+ path.iterdir(), root, include, exclude, None, report, gitignore
+ )
+ )
path.iterdir.assert_called()
self.assertEqual(path.iterdir.call_count, 2)
child.resolve.assert_called()
raise result.exception
self.assertEqual(result.exit_code, 0)
+ def test_invalid_config_return_code(self) -> None:
+ tmp_file = Path(black.dump_to_file())
+ try:
+ tmp_config = Path(black.dump_to_file())
+ tmp_config.unlink()
+ args = ["--config", str(tmp_config), str(tmp_file)]
+ self.invokeBlack(args, exit_code=2, ignore_config=False)
+ finally:
+ tmp_file.unlink()
+
+ def test_parse_pyproject_toml(self) -> None:
+ test_toml_file = THIS_DIR / "test.toml"
+ config = black.parse_pyproject_toml(str(test_toml_file))
+ self.assertEqual(config["verbose"], 1)
+ self.assertEqual(config["check"], "no")
+ self.assertEqual(config["diff"], "y")
+ self.assertEqual(config["color"], True)
+ self.assertEqual(config["line_length"], 79)
+ self.assertEqual(config["target_version"], ["py36", "py37", "py38"])
+ self.assertEqual(config["exclude"], r"\.pyi?$")
+ self.assertEqual(config["include"], r"\.py?$")
+
+ def test_read_pyproject_toml(self) -> None:
+ test_toml_file = THIS_DIR / "test.toml"
+ fake_ctx = FakeContext()
+ black.read_pyproject_toml(
+ fake_ctx, FakeParameter(), str(test_toml_file),
+ )
+ config = fake_ctx.default_map
+ self.assertEqual(config["verbose"], "1")
+ self.assertEqual(config["check"], "no")
+ self.assertEqual(config["diff"], "y")
+ self.assertEqual(config["color"], "True")
+ self.assertEqual(config["line_length"], "79")
+ self.assertEqual(config["target_version"], ["py36", "py37", "py38"])
+ self.assertEqual(config["exclude"], r"\.pyi?$")
+ self.assertEqual(config["include"], r"\.py?$")
+
+ def test_find_project_root(self) -> None:
+ with TemporaryDirectory() as workspace:
+ root = Path(workspace)
+ test_dir = root / "test"
+ test_dir.mkdir()
+
+ src_dir = root / "src"
+ src_dir.mkdir()
+
+ root_pyproject = root / "pyproject.toml"
+ root_pyproject.touch()
+ src_pyproject = src_dir / "pyproject.toml"
+ src_pyproject.touch()
+ src_python = src_dir / "foo.py"
+ src_python.touch()
+
+ self.assertEqual(
+ black.find_project_root((src_dir, test_dir)), root.resolve()
+ )
+ self.assertEqual(black.find_project_root((src_dir,)), src_dir.resolve())
+ self.assertEqual(black.find_project_root((src_python,)), src_dir.resolve())
+
class BlackDTestCase(AioHTTPTestCase):
async def get_application(self) -> web.Application:
@unittest_run_loop
async def test_blackd_unsupported_version(self) -> None:
response = await self.client.post(
- "/", data=b"what", headers={blackd.VERSION_HEADER: "2"}
+ "/", data=b"what", headers={blackd.PROTOCOL_VERSION_HEADER: "2"}
)
self.assertEqual(response.status, 501)
@unittest_run_loop
async def test_blackd_supported_version(self) -> None:
response = await self.client.post(
- "/", data=b"what", headers={blackd.VERSION_HEADER: "1"}
+ "/", data=b"what", headers={blackd.PROTOCOL_VERSION_HEADER: "1"}
)
self.assertEqual(response.status, 200)
self.assertEqual(response.status, 200)
self.assertEqual(await response.text(), expected)
+ @skip_if_exception("ClientOSError")
+ @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
+ @unittest_run_loop
+ async def test_blackd_diff(self) -> None:
+ diff_header = re.compile(
+ 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"
+ )
+
+ source, _ = read_data("blackd_diff.py")
+ expected, _ = read_data("blackd_diff.diff")
+
+ response = await self.client.post(
+ "/", data=source, headers={blackd.DIFF_HEADER: "true"}
+ )
+ self.assertEqual(response.status, 200)
+
+ actual = await response.text()
+ actual = diff_header.sub(DETERMINISTIC_HEADER, actual)
+ self.assertEqual(actual, expected)
+
@skip_if_exception("ClientOSError")
@unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
@unittest_run_loop
response = await self.client.post(
"/", data=code, headers={blackd.PYTHON_VARIANT_HEADER: header_value}
)
- self.assertEqual(response.status, expected_status)
+ self.assertEqual(
+ response.status, expected_status, msg=await response.text()
+ )
await check("3.6", 200)
await check("py3.6", 200)
await check("3.6,3.7", 200)
await check("3.6,py3.7", 200)
+ await check("py36,py37", 200)
+ await check("36", 200)
+ await check("3.6.4", 200)
await check("2", 204)
await check("2.7", 204)
await check("py2.7", 204)
await check("3.4", 204)
await check("py3.4", 204)
+ await check("py34,py36", 204)
+ await check("34", 204)
@skip_if_exception("ClientOSError")
@unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
)
self.assertEqual(response.status, 400)
+ @skip_if_exception("ClientOSError")
+ @unittest.skipUnless(has_blackd_deps, "blackd's dependencies are not installed")
+ @unittest_run_loop
+ async def test_blackd_response_black_version_header(self) -> None:
+ response = await self.client.post("/")
+ self.assertIsNotNone(response.headers.get(blackd.BLACK_VERSION_HEADER))
+
if __name__ == "__main__":
unittest.main(module="test_black")