#!/usr/bin/env python3
+import multiprocessing
import asyncio
import logging
from concurrent.futures import ThreadPoolExecutor
from contextlib import contextmanager
+from dataclasses import replace
from functools import partial
+import inspect
from io import BytesIO, TextIOWrapper
import os
from pathlib import Path
+from platform import system
import regex as re
import sys
from tempfile import TemporaryDirectory
-from typing import Any, BinaryIO, Dict, Generator, List, Tuple, Iterator, TypeVar
+import types
+from typing import (
+ Any,
+ BinaryIO,
+ Callable,
+ Dict,
+ Generator,
+ List,
+ Tuple,
+ Iterator,
+ TypeVar,
+)
import unittest
from unittest.mock import patch, MagicMock
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())
+DEFAULT_MODE = black.FileMode(experimental_string_processing=True)
+ff = partial(black.format_file_in_place, mode=DEFAULT_MODE, fast=True)
+fs = partial(black.format_str, mode=DEFAULT_MODE)
THIS_FILE = Path(__file__)
THIS_DIR = THIS_FILE.parent
PROJECT_ROOT = THIS_DIR.parent
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):
"""Modify CliRunner so that stderr is not merged with stdout.
class BlackTestCase(unittest.TestCase):
maxDiff = None
+ _diffThreshold = 2 ** 20
def assertFormatEqual(self, expected: str, actual: str) -> None:
if actual != expected and not os.environ.get("SKIP_AST_PRINT"):
list(bdv.visit(exp_node))
except Exception as ve:
black.err(str(ve))
- self.assertEqual(expected, actual)
+ self.assertMultiLineEqual(expected, actual)
def invokeBlack(
self, args: List[str], exit_code: int = 0, ignore_config: bool = True
)
@patch("black.dump_to_file", dump_to_stderr)
- def checkSourceFile(self, name: str) -> None:
+ def checkSourceFile(self, name: str, mode: black.FileMode = DEFAULT_MODE) -> None:
path = THIS_DIR.parent / name
source, expected = read_data(str(path), data=False)
- actual = fs(source)
+ actual = fs(source, mode=mode)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, mode)
self.assertFalse(ff(path))
@patch("black.dump_to_file", dump_to_stderr)
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
def test_empty_ff(self) -> None:
expected = ""
self.assertEqual(result.exit_code, 0)
self.assertFormatEqual(expected, result.output)
black.assert_equivalent(source, result.output)
- black.assert_stable(source, result.output, black.FileMode())
+ black.assert_stable(source, result.output, DEFAULT_MODE)
def test_piping_diff(self) -> None:
diff_header = re.compile(
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
@patch("black.dump_to_file", dump_to_stderr)
def test_function2(self) -> None:
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
+ black.assert_stable(source, actual, DEFAULT_MODE)
+
+ @patch("black.dump_to_file", dump_to_stderr)
+ def _test_wip(self) -> None:
+ source, expected = read_data("wip")
+ sys.settrace(tracefunc)
+ mode = replace(
+ DEFAULT_MODE,
+ experimental_string_processing=False,
+ target_versions={black.TargetVersion.PY38},
+ )
+ actual = fs(source, mode=mode)
+ sys.settrace(None)
+ self.assertFormatEqual(expected, actual)
+ black.assert_equivalent(source, actual)
black.assert_stable(source, actual, black.FileMode())
@patch("black.dump_to_file", dump_to_stderr)
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
+
+ @unittest.expectedFailure
+ @patch("black.dump_to_file", dump_to_stderr)
+ def test_trailing_comma_optional_parens_stability1(self) -> None:
+ source, _expected = read_data("trailing_comma_optional_parens1")
+ actual = fs(source)
+ black.assert_stable(source, actual, DEFAULT_MODE)
+
+ @unittest.expectedFailure
+ @patch("black.dump_to_file", dump_to_stderr)
+ def test_trailing_comma_optional_parens_stability2(self) -> None:
+ source, _expected = read_data("trailing_comma_optional_parens2")
+ actual = fs(source)
+ black.assert_stable(source, actual, DEFAULT_MODE)
+
+ @unittest.expectedFailure
+ @patch("black.dump_to_file", dump_to_stderr)
+ def test_trailing_comma_optional_parens_stability3(self) -> None:
+ source, _expected = read_data("trailing_comma_optional_parens3")
+ actual = fs(source)
+ black.assert_stable(source, actual, DEFAULT_MODE)
@patch("black.dump_to_file", dump_to_stderr)
def test_expression(self) -> None:
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
@patch("black.dump_to_file", dump_to_stderr)
def test_pep_572(self) -> None:
source, expected = read_data("pep_572")
actual = fs(source)
self.assertFormatEqual(expected, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
if sys.version_info >= (3, 8):
black.assert_equivalent(source, actual)
self.assertFormatEqual(expected, actual)
with patch("black.dump_to_file", dump_to_stderr):
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
def test_expression_diff(self) -> None:
source, _ = read_data("expression.py")
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
@patch("black.dump_to_file", dump_to_stderr)
def test_pep_570(self) -> None:
source, expected = read_data("pep_570")
actual = fs(source)
self.assertFormatEqual(expected, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
if sys.version_info >= (3, 8):
black.assert_equivalent(source, actual)
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
- mode = black.FileMode(string_normalization=False)
+ black.assert_stable(source, actual, DEFAULT_MODE)
+ mode = replace(DEFAULT_MODE, string_normalization=False)
not_normalized = fs(source, mode=mode)
self.assertFormatEqual(source.replace("\\\n", ""), not_normalized)
black.assert_equivalent(source, not_normalized)
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
+
+ @patch("black.dump_to_file", dump_to_stderr)
+ def test_docstring_no_string_normalization(self) -> None:
+ """Like test_docstring but with string normalization off."""
+ source, expected = read_data("docstring_no_string_normalization")
+ mode = replace(DEFAULT_MODE, string_normalization=False)
+ actual = fs(source, mode=mode)
+ self.assertFormatEqual(expected, actual)
+ black.assert_equivalent(source, actual)
+ black.assert_stable(source, actual, mode)
def test_long_strings(self) -> None:
"""Tests for splitting long strings."""
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
+
+ def test_long_strings_flag_disabled(self) -> None:
+ """Tests for turning off the string processing logic."""
+ source, expected = read_data("long_strings_flag_disabled")
+ mode = replace(DEFAULT_MODE, experimental_string_processing=False)
+ actual = fs(source, mode=mode)
+ self.assertFormatEqual(expected, actual)
+ black.assert_stable(expected, actual, mode)
@patch("black.dump_to_file", dump_to_stderr)
def test_long_strings__edge_case(self) -> None:
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
@patch("black.dump_to_file", dump_to_stderr)
def test_long_strings__regression(self) -> None:
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
@patch("black.dump_to_file", dump_to_stderr)
def test_slices(self) -> None:
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
+
+ @patch("black.dump_to_file", dump_to_stderr)
+ def test_percent_precedence(self) -> None:
+ source, expected = read_data("percent_precedence")
+ actual = fs(source)
+ self.assertFormatEqual(expected, actual)
+ black.assert_equivalent(source, actual)
+ black.assert_stable(source, actual, DEFAULT_MODE)
@patch("black.dump_to_file", dump_to_stderr)
def test_comments(self) -> None:
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
@patch("black.dump_to_file", dump_to_stderr)
def test_comments2(self) -> None:
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
@patch("black.dump_to_file", dump_to_stderr)
def test_comments3(self) -> None:
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
@patch("black.dump_to_file", dump_to_stderr)
def test_comments4(self) -> None:
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
@patch("black.dump_to_file", dump_to_stderr)
def test_comments5(self) -> None:
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
@patch("black.dump_to_file", dump_to_stderr)
def test_comments6(self) -> None:
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
@patch("black.dump_to_file", dump_to_stderr)
def test_comments7(self) -> None:
source, expected = read_data("comments7")
- actual = fs(source)
+ mode = replace(DEFAULT_MODE, target_versions={black.TargetVersion.PY38})
+ actual = fs(source, mode=mode)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
@patch("black.dump_to_file", dump_to_stderr)
def test_comment_after_escaped_newline(self) -> None:
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
@patch("black.dump_to_file", dump_to_stderr)
def test_cantfit(self) -> None:
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
@patch("black.dump_to_file", dump_to_stderr)
def test_import_spacing(self) -> None:
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
@patch("black.dump_to_file", dump_to_stderr)
def test_composition(self) -> None:
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
+
+ @patch("black.dump_to_file", dump_to_stderr)
+ def test_composition_no_trailing_comma(self) -> None:
+ source, expected = read_data("composition_no_trailing_comma")
+ mode = replace(DEFAULT_MODE, target_versions={black.TargetVersion.PY38})
+ actual = fs(source, mode=mode)
+ self.assertFormatEqual(expected, actual)
+ black.assert_equivalent(source, actual)
+ black.assert_stable(source, actual, DEFAULT_MODE)
@patch("black.dump_to_file", dump_to_stderr)
def test_empty_lines(self) -> None:
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
@patch("black.dump_to_file", dump_to_stderr)
def test_remove_parens(self) -> None:
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
@patch("black.dump_to_file", dump_to_stderr)
def test_string_prefixes(self) -> None:
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
@patch("black.dump_to_file", dump_to_stderr)
def test_numeric_literals(self) -> None:
source, expected = read_data("numeric_literals")
- mode = black.FileMode(target_versions=black.PY36_VERSIONS)
+ mode = replace(DEFAULT_MODE, target_versions=black.PY36_VERSIONS)
actual = fs(source, mode=mode)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
@patch("black.dump_to_file", dump_to_stderr)
def test_numeric_literals_ignoring_underscores(self) -> None:
source, expected = read_data("numeric_literals_skip_underscores")
- mode = black.FileMode(target_versions=black.PY36_VERSIONS)
+ mode = replace(DEFAULT_MODE, target_versions=black.PY36_VERSIONS)
actual = fs(source, mode=mode)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
source, expected = read_data("numeric_literals_py2")
actual = fs(source)
self.assertFormatEqual(expected, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
@patch("black.dump_to_file", dump_to_stderr)
def test_python2(self) -> None:
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
@patch("black.dump_to_file", dump_to_stderr)
def test_python2_print_function(self) -> None:
source, expected = read_data("python2_print_function")
- mode = black.FileMode(target_versions={TargetVersion.PY27})
+ mode = replace(DEFAULT_MODE, target_versions={TargetVersion.PY27})
actual = fs(source, mode=mode)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
@patch("black.dump_to_file", dump_to_stderr)
def test_stub(self) -> None:
- mode = black.FileMode(is_pyi=True)
+ mode = replace(DEFAULT_MODE, is_pyi=True)
source, expected = read_data("stub.pyi")
actual = fs(source, mode=mode)
self.assertFormatEqual(expected, actual)
major, minor = sys.version_info[:2]
if major < 3 or (major <= 3 and minor < 7):
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
# ensure black can parse this when the target is 3.6
self.invokeBlack([str(source_path), "--target-version", "py36"])
# but not on 3.7, because async/await is no longer an identifier
major, minor = sys.version_info[:2]
if major > 3 or (major == 3 and minor >= 7):
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
# ensure black can parse this when the target is 3.7
self.invokeBlack([str(source_path), "--target-version", "py37"])
# but not on 3.6, because we use async as a reserved keyword
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())
+ black.assert_stable(source, actual, DEFAULT_MODE)
@patch("black.dump_to_file", dump_to_stderr)
def test_fmtonoff(self) -> None:
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
@patch("black.dump_to_file", dump_to_stderr)
def test_fmtonoff2(self) -> None:
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
@patch("black.dump_to_file", dump_to_stderr)
def test_fmtonoff3(self) -> None:
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
@patch("black.dump_to_file", dump_to_stderr)
def test_fmtonoff4(self) -> None:
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
@patch("black.dump_to_file", dump_to_stderr)
def test_remove_empty_parentheses_after_class(self) -> None:
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
@patch("black.dump_to_file", dump_to_stderr)
def test_new_line_between_class_and_code(self) -> None:
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
@patch("black.dump_to_file", dump_to_stderr)
def test_bracket_match(self) -> None:
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
@patch("black.dump_to_file", dump_to_stderr)
def test_tuple_assign(self) -> None:
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
@patch("black.dump_to_file", dump_to_stderr)
def test_beginning_backslash(self) -> None:
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
def test_tab_comment_indentation(self) -> None:
contents_tab = "if 1:\n\tif 2:\n\t\tpass\n\t# comment\n\tpass\n"
def test_format_file_contents(self) -> None:
empty = ""
- mode = black.FileMode()
+ mode = DEFAULT_MODE
with self.assertRaises(black.NothingChanged):
black.format_file_contents(empty, mode=mode, fast=False)
just_nl = "\n"
self.assertEqual("".join(err_lines), "")
def test_cache_broken_file(self) -> None:
- mode = black.FileMode()
+ mode = DEFAULT_MODE
with cache_dir() as workspace:
cache_file = black.get_cache_file(mode)
with cache_file.open("w") as fobj:
self.assertIn(src, cache)
def test_cache_single_file_already_cached(self) -> None:
- mode = black.FileMode()
+ mode = DEFAULT_MODE
with cache_dir() as workspace:
src = (workspace / "test.py").resolve()
with src.open("w") as fobj:
@event_loop()
def test_cache_multiple_files(self) -> None:
- mode = black.FileMode()
+ mode = DEFAULT_MODE
with cache_dir() as workspace, patch(
"black.ProcessPoolExecutor", new=ThreadPoolExecutor
):
self.assertIn(two, cache)
def test_no_cache_when_writeback_diff(self) -> None:
- mode = black.FileMode()
+ mode = DEFAULT_MODE
with cache_dir() as workspace:
src = (workspace / "test.py").resolve()
with src.open("w") as fobj:
fobj.write("print('hello')")
- self.invokeBlack([str(src), "--diff"])
- cache_file = black.get_cache_file(mode)
- self.assertFalse(cache_file.exists())
+ with patch("black.read_cache") as read_cache, patch(
+ "black.write_cache"
+ ) as write_cache:
+ self.invokeBlack([str(src), "--diff"])
+ cache_file = black.get_cache_file(mode)
+ self.assertFalse(cache_file.exists())
+ write_cache.assert_not_called()
+ read_cache.assert_not_called()
+
+ def test_no_cache_when_writeback_color_diff(self) -> None:
+ mode = DEFAULT_MODE
+ with cache_dir() as workspace:
+ src = (workspace / "test.py").resolve()
+ with src.open("w") as fobj:
+ fobj.write("print('hello')")
+ with patch("black.read_cache") as read_cache, patch(
+ "black.write_cache"
+ ) as write_cache:
+ self.invokeBlack([str(src), "--diff", "--color"])
+ cache_file = black.get_cache_file(mode)
+ self.assertFalse(cache_file.exists())
+ write_cache.assert_not_called()
+ read_cache.assert_not_called()
+
+ @event_loop()
+ def test_output_locking_when_writeback_diff(self) -> None:
+ with cache_dir() as workspace:
+ for tag in range(0, 4):
+ src = (workspace / f"test{tag}.py").resolve()
+ with src.open("w") as fobj:
+ fobj.write("print('hello')")
+ with patch("black.Manager", wraps=multiprocessing.Manager) as mgr:
+ self.invokeBlack(["--diff", str(workspace)], exit_code=0)
+ # this isn't quite doing what we want, but if it _isn't_
+ # called then we cannot be using the lock it provides
+ mgr.assert_called()
+
+ @event_loop()
+ def test_output_locking_when_writeback_color_diff(self) -> None:
+ with cache_dir() as workspace:
+ for tag in range(0, 4):
+ src = (workspace / f"test{tag}.py").resolve()
+ with src.open("w") as fobj:
+ fobj.write("print('hello')")
+ with patch("black.Manager", wraps=multiprocessing.Manager) as mgr:
+ self.invokeBlack(["--diff", "--color", str(workspace)], exit_code=0)
+ # this isn't quite doing what we want, but if it _isn't_
+ # called then we cannot be using the lock it provides
+ mgr.assert_called()
def test_no_cache_when_stdin(self) -> None:
- mode = black.FileMode()
+ mode = DEFAULT_MODE
with cache_dir():
result = CliRunner().invoke(
black.main, ["-"], input=BytesIO(b"print('hello')")
self.assertFalse(cache_file.exists())
def test_read_cache_no_cachefile(self) -> None:
- mode = black.FileMode()
+ mode = DEFAULT_MODE
with cache_dir():
self.assertEqual(black.read_cache(mode), {})
def test_write_cache_read_cache(self) -> None:
- mode = black.FileMode()
+ mode = DEFAULT_MODE
with cache_dir() as workspace:
src = (workspace / "test.py").resolve()
src.touch()
self.assertEqual(done, {cached})
def test_write_cache_creates_directory_if_needed(self) -> None:
- mode = black.FileMode()
+ mode = DEFAULT_MODE
with cache_dir(exists=False) as workspace:
self.assertFalse(workspace.exists())
black.write_cache({}, [], mode)
@event_loop()
def test_failed_formatting_does_not_get_cached(self) -> None:
- mode = black.FileMode()
+ mode = DEFAULT_MODE
with cache_dir() as workspace, patch(
"black.ProcessPoolExecutor", new=ThreadPoolExecutor
):
self.assertIn(clean, cache)
def test_write_cache_write_fail(self) -> None:
- mode = black.FileMode()
+ mode = DEFAULT_MODE
with cache_dir(), patch.object(Path, "open") as mock:
mock.side_effect = OSError
black.write_cache({}, [], mode)
self.invokeBlack([str(workspace.resolve())])
def test_read_cache_line_lengths(self) -> None:
- mode = black.FileMode()
- short_mode = black.FileMode(line_length=1)
+ mode = DEFAULT_MODE
+ short_mode = replace(DEFAULT_MODE, line_length=1)
with cache_dir() as workspace:
path = (workspace / "file.py").resolve()
path.touch()
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
def test_single_file_force_pyi(self) -> None:
- reg_mode = black.FileMode()
- pyi_mode = black.FileMode(is_pyi=True)
+ pyi_mode = replace(DEFAULT_MODE, is_pyi=True)
contents, expected = read_data("force_pyi")
with cache_dir() as workspace:
path = (workspace / "file.py").resolve()
# verify cache with --pyi is separate
pyi_cache = black.read_cache(pyi_mode)
self.assertIn(path, pyi_cache)
- normal_cache = black.read_cache(reg_mode)
+ normal_cache = black.read_cache(DEFAULT_MODE)
self.assertNotIn(path, normal_cache)
- self.assertEqual(actual, expected)
+ self.assertFormatEqual(expected, actual)
+ black.assert_equivalent(contents, actual)
+ black.assert_stable(contents, actual, pyi_mode)
@event_loop()
def test_multi_file_force_pyi(self) -> None:
- reg_mode = black.FileMode()
- pyi_mode = black.FileMode(is_pyi=True)
+ reg_mode = DEFAULT_MODE
+ pyi_mode = replace(DEFAULT_MODE, is_pyi=True)
contents, expected = read_data("force_pyi")
with cache_dir() as workspace:
paths = [
self.assertFormatEqual(actual, expected)
def test_single_file_force_py36(self) -> None:
- reg_mode = black.FileMode()
- py36_mode = black.FileMode(target_versions=black.PY36_VERSIONS)
+ reg_mode = DEFAULT_MODE
+ py36_mode = replace(DEFAULT_MODE, target_versions=black.PY36_VERSIONS)
source, expected = read_data("force_py36")
with cache_dir() as workspace:
path = (workspace / "file.py").resolve()
@event_loop()
def test_multi_file_force_py36(self) -> None:
- reg_mode = black.FileMode()
- py36_mode = black.FileMode(target_versions=black.PY36_VERSIONS)
+ reg_mode = DEFAULT_MODE
+ py36_mode = replace(DEFAULT_MODE, target_versions=black.PY36_VERSIONS)
source, expected = read_data("force_py36")
with cache_dir() as workspace:
paths = [
actual = fs(source)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
- black.assert_stable(source, actual, black.FileMode())
+ black.assert_stable(source, actual, DEFAULT_MODE)
def test_pipe_force_py36(self) -> None:
source, expected = read_data("force_py36")
this_abs = THIS_DIR.resolve()
sources.extend(
black.gen_python_files(
- path.iterdir(), this_abs, include, [exclude], report, gitignore
+ 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))
this_abs = THIS_DIR.resolve()
sources.extend(
black.gen_python_files(
- path.iterdir(), this_abs, include, [exclude], report, gitignore
+ path.iterdir(), this_abs, include, exclude, None, report, gitignore
)
)
self.assertEqual(sorted(expected), sorted(sources))
path.iterdir(),
this_abs,
empty,
- [re.compile(black.DEFAULT_EXCLUDES)],
+ re.compile(black.DEFAULT_EXCLUDES),
+ None,
report,
gitignore,
)
path.iterdir(),
this_abs,
re.compile(black.DEFAULT_INCLUDES),
- [empty],
+ empty,
+ None,
report,
gitignore,
)
try:
list(
black.gen_python_files(
- path.iterdir(), root, include, exclude, report, gitignore
+ path.iterdir(), root, include, exclude, None, report, gitignore
)
)
except ValueError as ve:
with self.assertRaises(ValueError):
list(
black.gen_python_files(
- path.iterdir(), root, include, exclude, report, gitignore
+ path.iterdir(), root, include, exclude, None, report, gitignore
)
)
path.iterdir.assert_called()
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),
- )
+ 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["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())
+
+ def test_bpo_33660_workaround(self) -> None:
+ if system() == "Windows":
+ return
+
+ # https://bugs.python.org/issue33660
+
+ old_cwd = Path.cwd()
+ try:
+ root = Path("/")
+ os.chdir(str(root))
+ path = Path("workspace") / "project"
+ report = black.Report(verbose=True)
+ normalized_path = black.normalize_path_maybe_ignore(path, root, report)
+ self.assertEqual(normalized_path, "workspace/project")
+ finally:
+ os.chdir(str(old_cwd))
+
class BlackDTestCase(AioHTTPTestCase):
async def get_application(self) -> web.Application:
self.assertIsNotNone(response.headers.get(blackd.BLACK_VERSION_HEADER))
+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
+
+
if __name__ == "__main__":
unittest.main(module="test_black")