Skip to content

Commit

Permalink
Indicate that a final newline was added in --diff (#1897) (#1897)
Browse files Browse the repository at this point in the history
Fixes: #1662

Work-around for https://bugs.python.org/issue2142

The test has to slightly mess with its input data, because the utility
functions default to ensuring the test data has a final newline, which
defeats the point of the test.

Signed-off-by: Paul "TBBle" Hampson <Paul.Hampson@Pobox.com>
  • Loading branch information
TBBle committed Feb 22, 2021
1 parent 0cbe19c commit cd4295d
Show file tree
Hide file tree
Showing 6 changed files with 52 additions and 8 deletions.
2 changes: 2 additions & 0 deletions CHANGES.md
Expand Up @@ -28,6 +28,8 @@

- speed up caching by avoiding pathlib (#1950)

- `--diff` correctly indicates when a file doesn't end in a newline (#1662)

#### _Packaging_

- Self-contained native _Black_ binaries are now provided for releases via GitHub
Expand Down
1 change: 1 addition & 0 deletions docs/authors.md
Expand Up @@ -132,6 +132,7 @@ Multiple contributions by:
- [Pablo Galindo](mailto:Pablogsal@gmail.com)
- [Paul Ganssle](mailto:p.ganssle@gmail.com)
- [Paul Meinhardt](mailto:mnhrdt@gmail.com)
- [Paul "TBBle" Hampson](mailto:Paul.Hampson@Pobox.com)
- [Peter Bengtsson](mailto:mail@peterbe.com)
- [Peter Grayson](mailto:pete@jpgrayson.net)
- [Peter Stensmyr](mailto:peter.stensmyr@gmail.com)
Expand Down
23 changes: 16 additions & 7 deletions src/black/__init__.py
Expand Up @@ -6425,14 +6425,14 @@ def assert_stable(src: str, dst: str, mode: Mode) -> None:


@mypyc_attr(patchable=True)
def dump_to_file(*output: str) -> str:
def dump_to_file(*output: str, ensure_final_newline: bool = True) -> str:
"""Dump `output` to a temporary file. Return path to the file."""
with tempfile.NamedTemporaryFile(
mode="w", prefix="blk_", suffix=".log", delete=False, encoding="utf8"
) as f:
for lines in output:
f.write(lines)
if lines and lines[-1] != "\n":
if ensure_final_newline and lines and lines[-1] != "\n":
f.write("\n")
return f.name

Expand All @@ -6450,11 +6450,20 @@ def diff(a: str, b: str, a_name: str, b_name: str) -> str:
"""Return a unified diff string between strings `a` and `b`."""
import difflib

a_lines = [line + "\n" for line in a.splitlines()]
b_lines = [line + "\n" for line in b.splitlines()]
return "".join(
difflib.unified_diff(a_lines, b_lines, fromfile=a_name, tofile=b_name, n=5)
)
a_lines = [line for line in a.splitlines(keepends=True)]
b_lines = [line for line in b.splitlines(keepends=True)]
diff_lines = []
for line in difflib.unified_diff(
a_lines, b_lines, fromfile=a_name, tofile=b_name, n=5
):
# Work around https://bugs.python.org/issue2142
# See https://www.gnu.org/software/diffutils/manual/html_node/Incomplete-Lines.html
if line[-1] == "\n":
diff_lines.append(line)
else:
diff_lines.append(line + "\n")
diff_lines.append("\\ No newline at end of file\n")
return "".join(diff_lines)


def cancel(tasks: Iterable["asyncio.Task[Any]"]) -> None:
Expand Down
8 changes: 8 additions & 0 deletions tests/data/missing_final_newline.diff
@@ -0,0 +1,8 @@
--- [Deterministic header]
+++ [Deterministic header]
@@ -1,3 +1,3 @@
# A comment-only file, with no final EOL character
# This triggers https://bugs.python.org/issue2142
-# This is the line without the EOL character
\ No newline at end of file
+# This is the line without the EOL character
3 changes: 3 additions & 0 deletions tests/data/missing_final_newline.py
@@ -0,0 +1,3 @@
# A comment-only file, with no final EOL character
# This triggers https://bugs.python.org/issue2142
# This is the line without the EOL character
23 changes: 22 additions & 1 deletion tests/test_black.py
Expand Up @@ -300,7 +300,6 @@ def test_expression_diff(self) -> None:
os.unlink(tmp_file)
actual = result.output
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 = (
Expand Down Expand Up @@ -1798,6 +1797,28 @@ def test_newline_comment_interaction(self) -> None:
output = black.format_str(source, mode=DEFAULT_MODE)
black.assert_stable(source, output, mode=DEFAULT_MODE)

def test_bpo_2142_workaround(self) -> None:

# https://bugs.python.org/issue2142

source, _ = read_data("missing_final_newline.py")
# read_data adds a trailing newline
source = source.rstrip()
expected, _ = read_data("missing_final_newline.diff")
tmp_file = Path(black.dump_to_file(source, ensure_final_newline=False))
diff_header = re.compile(
rf"{re.escape(str(tmp_file))}\t\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)])
self.assertEqual(result.exit_code, 0)
finally:
os.unlink(tmp_file)
actual = result.output
actual = diff_header.sub(DETERMINISTIC_HEADER, actual)
self.assertEqual(actual, expected)


with open(black.__file__, "r", encoding="utf-8") as _bf:
black_source_lines = _bf.readlines()
Expand Down

0 comments on commit cd4295d

Please sign in to comment.