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
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 = [
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
) -> 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:
self.checkSourceFile("tests/test_black.py")
def test_black(self) -> None:
- self.checkSourceFile("black.py")
+ self.checkSourceFile("src/black/__init__.py")
def test_pygram(self) -> None:
- self.checkSourceFile("blib2to3/pygram.py")
+ self.checkSourceFile("src/blib2to3/pygram.py")
def test_pytree(self) -> None:
- self.checkSourceFile("blib2to3/pytree.py")
+ self.checkSourceFile("src/blib2to3/pytree.py")
def test_conv(self) -> None:
- self.checkSourceFile("blib2to3/pgen2/conv.py")
+ self.checkSourceFile("src/blib2to3/pgen2/conv.py")
def test_driver(self) -> None:
- self.checkSourceFile("blib2to3/pgen2/driver.py")
+ self.checkSourceFile("src/blib2to3/pgen2/driver.py")
def test_grammar(self) -> None:
- self.checkSourceFile("blib2to3/pgen2/grammar.py")
+ self.checkSourceFile("src/blib2to3/pgen2/grammar.py")
def test_literals(self) -> None:
- self.checkSourceFile("blib2to3/pgen2/literals.py")
+ self.checkSourceFile("src/blib2to3/pgen2/literals.py")
def test_parse(self) -> None:
- self.checkSourceFile("blib2to3/pgen2/parse.py")
+ self.checkSourceFile("src/blib2to3/pgen2/parse.py")
def test_pgen(self) -> None:
- self.checkSourceFile("blib2to3/pgen2/pgen.py")
+ self.checkSourceFile("src/blib2to3/pgen2/pgen.py")
def test_tokenize(self) -> None:
- self.checkSourceFile("blib2to3/pgen2/tokenize.py")
+ self.checkSourceFile("src/blib2to3/pgen2/tokenize.py")
def test_token(self) -> None:
- self.checkSourceFile("blib2to3/pgen2/token.py")
+ 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 \d\d:\d\d:\d\d\.\d\d\d\d\d\d "
- rf"\+\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")
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.
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)
]
this_abs = THIS_DIR.resolve()
sources.extend(
- black.gen_python_files_in_dir(
- path, this_abs, include, exclude, report, gitignore
+ black.gen_python_files(
+ path.iterdir(), this_abs, include, [exclude], report, gitignore
)
)
self.assertEqual(sorted(expected), sorted(sources))
]
this_abs = THIS_DIR.resolve()
sources.extend(
- black.gen_python_files_in_dir(
- path, this_abs, include, exclude, report, gitignore
+ black.gen_python_files(
+ path.iterdir(), this_abs, include, [exclude], report, gitignore
)
)
self.assertEqual(sorted(expected), sorted(sources))
]
this_abs = THIS_DIR.resolve()
sources.extend(
- black.gen_python_files_in_dir(
- path,
+ black.gen_python_files(
+ path.iterdir(),
this_abs,
empty,
- re.compile(black.DEFAULT_EXCLUDES),
+ [re.compile(black.DEFAULT_EXCLUDES)],
report,
gitignore,
)
]
this_abs = THIS_DIR.resolve()
sources.extend(
- black.gen_python_files_in_dir(
- path,
+ black.gen_python_files(
+ path.iterdir(),
this_abs,
re.compile(black.DEFAULT_INCLUDES),
- empty,
+ [empty],
report,
gitignore,
)
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)
child.is_symlink.return_value = True
try:
list(
- black.gen_python_files_in_dir(
- path, root, include, exclude, report, gitignore
+ black.gen_python_files(
+ path.iterdir(), root, include, exclude, report, gitignore
)
)
except ValueError as ve:
child.is_symlink.return_value = False
with self.assertRaises(ValueError):
list(
- black.gen_python_files_in_dir(
- path, root, include, exclude, report, gitignore
+ black.gen_python_files(
+ path.iterdir(), root, include, exclude, report, gitignore
)
)
path.iterdir.assert_called()
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 a click context and parameter so mypy stays happy
+ class FakeContext(click.Context):
+ def __init__(self) -> None:
+ self.default_map: Dict[str, Any] = {}
+
+ class FakeParameter(click.Parameter):
+ def __init__(self) -> None:
+ pass
+
+ 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_diff(self) -> None:
diff_header = re.compile(
- rf"(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"
+ 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")