Skip to content

Commit

Permalink
Move whitespace-only behavior to preview style
Browse files Browse the repository at this point in the history
The new criteria to reformat empty and whitespace-only files should go
into the preview style

Signed-off-by: Antonio Ossa Guerra <aaossa@uc.cl>
  • Loading branch information
aaossa committed Oct 26, 2022
1 parent a2150e9 commit e7e755d
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 18 deletions.
4 changes: 2 additions & 2 deletions CHANGES.md
Expand Up @@ -31,8 +31,6 @@
- Parsing support has been added for walruses inside generator expression that are
passed as function args (for example,
`any(match := my_re.match(text) for text in texts)`) (#3327).
- Reformat empty and whitespace-only files as either an empty file (if no newline is
present) or as a single newline character (if a newline is present) (#3348)

### Performance

Expand Down Expand Up @@ -71,6 +69,8 @@

- Fix a crash when formatting some dicts with parenthesis-wrapped long string keys
(#3262)
- Reformat empty and whitespace-only files as either an empty file (if no newline is
present) or as a single newline character (if a newline is present) (#3348)

### Configuration

Expand Down
7 changes: 5 additions & 2 deletions src/black/__init__.py
Expand Up @@ -914,6 +914,9 @@ def format_file_contents(src_contents: str, *, fast: bool, mode: Mode) -> FileCo
valid by calling :func:`assert_equivalent` and :func:`assert_stable` on it.
`mode` is passed to :func:`format_str`.
"""
if not mode.preview and not src_contents.strip():
raise NothingChanged

if mode.is_ipynb:
dst_contents = format_ipynb_string(src_contents, fast=fast, mode=mode)
else:
Expand Down Expand Up @@ -1008,7 +1011,7 @@ def format_ipynb_string(src_contents: str, *, fast: bool, mode: Mode) -> FileCon
Operate cell-by-cell, only on code cells, only for Python notebooks.
If the ``.ipynb`` originally had a trailing newline, it'll be preserved.
"""
if not src_contents:
if mode.preview and not src_contents:
raise NothingChanged

trailing_newline = src_contents[-1] == "\n"
Expand Down Expand Up @@ -1103,7 +1106,7 @@ def _format_str_once(src_contents: str, *, mode: Mode) -> str:
dst_contents = []
for block in dst_blocks:
dst_contents.extend(block.all_lines())
if not dst_contents:
if mode.preview and not dst_contents:
normalized_content, _, newline = decode_bytes(src_contents.encode("utf-8"))
if "\n" in normalized_content:
return newline
Expand Down
File renamed without changes.
50 changes: 36 additions & 14 deletions tests/test_black.py
Expand Up @@ -156,14 +156,16 @@ def test_empty_ff(self) -> None:

@patch("black.dump_to_file", dump_to_stderr)
def test_one_empty_line(self) -> None:
mode = black.Mode(preview=True)
for nl in ["\n", "\r\n"]:
source = expected = nl
actual = fs(source)
actual = fs(source, mode=mode)
self.assertFormatEqual(expected, actual)
black.assert_equivalent(source, actual)
black.assert_stable(source, actual, DEFAULT_MODE)
black.assert_stable(source, actual, mode)

def test_one_empty_line_ff(self) -> None:
mode = black.Mode(preview=True)
for nl in ["\n", "\r\n"]:
expected = nl
tmp_file = Path(black.dump_to_file(nl))
Expand All @@ -174,7 +176,9 @@ def test_one_empty_line_ff(self) -> None:
with open(tmp_file, "wb") as f:
f.write(nl.encode("utf-8"))
try:
self.assertFalse(ff(tmp_file, write_back=black.WriteBack.YES))
self.assertFalse(
ff(tmp_file, mode=mode, write_back=black.WriteBack.YES)
)
with open(tmp_file, "rb") as f:
actual = f.read().decode("utf8")
finally:
Expand Down Expand Up @@ -957,22 +961,13 @@ def err(msg: str, **kwargs: Any) -> None:
)

def test_format_file_contents(self) -> None:
empty = ""
mode = DEFAULT_MODE
empty = ""
with self.assertRaises(black.NothingChanged):
black.format_file_contents(empty, mode=mode, fast=False)
just_nl = "\n"
with self.assertRaises(black.NothingChanged):
black.format_file_contents(just_nl, mode=mode, fast=False)
just_crlf = "\r\n"
with self.assertRaises(black.NothingChanged):
black.format_file_contents(just_crlf, mode=mode, fast=False)
just_whitespace_nl = "\n\t\n \n\t \n \t\n\n"
actual = black.format_file_contents(just_whitespace_nl, mode=mode, fast=False)
self.assertEqual("\n", actual)
just_whitespace_crlf = "\r\n\t\r\n \r\n\t \r\n \t\r\n\r\n"
actual = black.format_file_contents(just_whitespace_crlf, mode=mode, fast=False)
self.assertEqual("\r\n", actual)
same = "j = [1, 2, 3]\n"
with self.assertRaises(black.NothingChanged):
black.format_file_contents(same, mode=mode, fast=False)
Expand All @@ -985,6 +980,17 @@ def test_format_file_contents(self) -> None:
black.format_file_contents(invalid, mode=mode, fast=False)
self.assertEqual(str(e.exception), "Cannot parse: 1:7: return if you can")

mode = black.Mode(preview=True)
just_crlf = "\r\n"
with self.assertRaises(black.NothingChanged):
black.format_file_contents(just_crlf, mode=mode, fast=False)
just_whitespace_nl = "\n\t\n \n\t \n \t\n\n"
actual = black.format_file_contents(just_whitespace_nl, mode=mode, fast=False)
self.assertEqual("\n", actual)
just_whitespace_crlf = "\r\n\t\r\n \r\n\t \r\n \t\r\n\r\n"
actual = black.format_file_contents(just_whitespace_crlf, mode=mode, fast=False)
self.assertEqual("\r\n", actual)

def test_endmarker(self) -> None:
n = black.lib2to3_parse("\n")
self.assertEqual(n.type, black.syms.file_input)
Expand Down Expand Up @@ -1300,6 +1306,7 @@ def get_output(*args: Any, **kwargs: Any) -> io.TextIOWrapper:

return get_output

mode = black.Mode(preview=True)
for content, expected in cases:
output = io.StringIO()
io_TextIOWrapper = io.TextIOWrapper
Expand All @@ -1310,12 +1317,27 @@ def get_output(*args: Any, **kwargs: Any) -> io.TextIOWrapper:
fast=True,
content=content,
write_back=black.WriteBack.YES,
mode=DEFAULT_MODE,
mode=mode,
)
except io.UnsupportedOperation:
pass # StringIO does not support detach
assert output.getvalue() == expected

# An empty string is the only test case for `preview=False`
output = io.StringIO()
io_TextIOWrapper = io.TextIOWrapper
with patch("io.TextIOWrapper", _new_wrapper(output, io_TextIOWrapper)):
try:
black.format_stdin_to_stdout(
fast=True,
content="",
write_back=black.WriteBack.YES,
mode=DEFAULT_MODE,
)
except io.UnsupportedOperation:
pass # StringIO does not support detach
assert output.getvalue() == ""

def test_invalid_cli_regex(self) -> None:
for option in ["--include", "--exclude", "--extend-exclude", "--force-exclude"]:
self.invokeBlack(["-", option, "**()(!!*)"], exit_code=2)
Expand Down

0 comments on commit e7e755d

Please sign in to comment.