- 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
- [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)
@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
"""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:
--- /dev/null
+--- [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
--- /dev/null
+# 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
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 = (
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()