Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Indicate that a final newline was added in --diff #1897

Merged
merged 1 commit into from Feb 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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 @@ -6423,14 +6423,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 @@ -6448,11 +6448,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)
TBBle marked this conversation as resolved.
Show resolved Hide resolved


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