Skip to content

Commit

Permalink
Keep encoding and newlines intact. Fixes #115.
Browse files Browse the repository at this point in the history
  • Loading branch information
akaihola committed Apr 3, 2021
1 parent a2d1365 commit 51a791c
Show file tree
Hide file tree
Showing 9 changed files with 349 additions and 48 deletions.
8 changes: 5 additions & 3 deletions src/darker/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ def format_edited_parts(

for path_in_repo in sorted(changed_files):
src = git_root / path_in_repo
worktree_content = TextDocument.from_str(src.read_text())
worktree_content = TextDocument.from_file(src)

# 1. run isort
if enable_isort:
Expand Down Expand Up @@ -101,7 +101,9 @@ def format_edited_parts(

# 7. choose reformatted content
chosen = TextDocument.from_lines(
choose_lines(black_chunks, edited_linenums)
choose_lines(black_chunks, edited_linenums),
encoding=worktree_content.encoding,
newline=worktree_content.newline,
)

# 8. verify
Expand Down Expand Up @@ -145,7 +147,7 @@ def format_edited_parts(
def modify_file(path: Path, new_content: TextDocument) -> None:
"""Write new content to a file and inform the user by logging"""
logger.info("Writing %s bytes into %s", len(new_content.string), path)
path.write_text(new_content.string)
path.write_bytes(new_content.encoded_string)


def print_diff(path: Path, old: TextDocument, new: TextDocument) -> None:
Expand Down
6 changes: 5 additions & 1 deletion src/darker/black_diff.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,4 +118,8 @@ def run_black(
# Override defaults and pyproject.toml settings if they've been specified
# from the command line arguments
mode = Mode(**effective_args)
return TextDocument.from_str(format_str(src_contents.string, mode=mode))
return TextDocument.from_str(
format_str(src_contents.string, mode=mode),
encoding=src_contents.encoding,
override_newline=src_contents.newline,
)
4 changes: 3 additions & 1 deletion src/darker/import_sorting.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,6 @@ def apply_isort(
", ".join(f"{k}={v!r}" for k, v in isort_args.items())
)
)
return TextDocument.from_str(isort.code(code=content.string, **isort_args))
return TextDocument.from_str(
isort.code(code=content.string, **isort_args), encoding=content.encoding
)
18 changes: 15 additions & 3 deletions src/darker/tests/test_black_diff.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,21 @@ def test_black_config(tmpdir, config_path, config_lines, expect):
assert config == expect


def test_run_black(tmpdir):
src = TextDocument.from_lines(["print ( '42' )"])
@pytest.mark.parametrize("encoding", ["utf-8", "iso-8859-1"])
@pytest.mark.parametrize("newline", ["\n", "\r\n"])
def test_run_black(tmpdir, encoding, newline):
"""Running Black through its Python internal API gives correct results"""
src = TextDocument.from_lines(
[f"# coding: {encoding}", "print ( 'touché' )"],
encoding=encoding,
newline=newline,
)

result = run_black(Path(tmpdir / "src.py"), src, BlackArgs())

assert result.lines == ('print("42")',)
assert result.lines == (
f"# coding: {encoding}",
'print("touché")',
)
assert result.encoding == encoding
assert result.newline == newline
1 change: 1 addition & 0 deletions src/darker/tests/test_git.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ def test_git_get_content_at_revision(git_repo, revision, expect):
)
def test_git_get_content_at_revision_git_calls(revision, expect):
with patch("darker.git.check_output") as check_output:
check_output.return_value = "dummy output"

git_get_content_at_revision(Path("my.txt"), revision, Path("cwd"))

Expand Down
12 changes: 10 additions & 2 deletions src/darker/tests/test_import_sorting.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,18 @@
ISORTED_SOURCE = ("import os", "import sys")


def test_apply_isort():
result = apply_isort(TextDocument.from_lines(ORIGINAL_SOURCE), Path("test1.py"))
@pytest.mark.parametrize("encoding", ["utf-8", "iso-8859-1"])
@pytest.mark.parametrize("newline", ["\n", "\r\n"])
def test_apply_isort(encoding, newline):
"""Import sorting is applied correctly, with encoding and newline intact"""
result = apply_isort(
TextDocument.from_lines(ORIGINAL_SOURCE, encoding=encoding, newline=newline),
Path("test1.py"),
)

assert result.lines == ISORTED_SOURCE
assert result.encoding == encoding
assert result.newline == newline


@pytest.mark.parametrize(
Expand Down
85 changes: 67 additions & 18 deletions src/darker/tests/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ def run_isort(git_repo, monkeypatch, caplog, request):
darker.__main__,
run_black=Mock(return_value=TextDocument()),
verify_ast_unchanged=Mock(),
), patch("darker.import_sorting.isort.code"):
), patch("darker.import_sorting.isort.code") as isort_code:
isort_code.return_value = "dummy isort output"
darker.__main__.main(["--isort", "./test1.py", *args])
return SimpleNamespace(
isort_code=darker.import_sorting.isort.code, caplog=caplog
Expand Down Expand Up @@ -118,22 +119,25 @@ def test_format_edited_parts_empty():
(True, {}, A_PY_BLACK_ISORT),
(False, {'skip_string_normalization': True}, A_PY_BLACK_UNNORMALIZE),
],
ids=["black", "black_isort", "black_unnormalize"],
)
def test_format_edited_parts(git_repo, monkeypatch, enable_isort, black_args, expect):
monkeypatch.chdir(git_repo.root)
paths = git_repo.add({'a.py': '\n', 'b.py': '\n'}, commit='Initial commit')
paths['a.py'].write('\n'.join(A_PY))
paths['b.py'].write('print(42 )\n')
@pytest.mark.parametrize("newline", ["\n", "\r\n"], ids=["unix", "windows"])
def test_format_edited_parts(git_repo, enable_isort, black_args, newline, expect):
"""Correct reformatting and import sorting changes are produced"""
paths = git_repo.add({"a.py": newline, "b.py": newline}, commit="Initial commit")
paths["a.py"].write(newline.join(A_PY))
paths["b.py"].write(f"print(42 ){newline}")

result = darker.__main__.format_edited_parts(
[Path("a.py")], RevisionRange("HEAD"), enable_isort, [], black_args
)

changes = [
(path, worktree_content.string, chosen.string, chosen.lines)
for path, worktree_content, chosen in darker.__main__.format_edited_parts(
[Path("a.py")], RevisionRange("HEAD"), enable_isort, [], black_args
)
for path, worktree_content, chosen in result
]

expect_changes = [
(paths["a.py"], "\n".join(A_PY), "\n".join(expect), tuple(expect[:-1]))
(paths["a.py"], newline.join(A_PY), newline.join(expect), tuple(expect[:-1]))
]
assert changes == expect_changes

Expand Down Expand Up @@ -173,24 +177,50 @@ def test_format_edited_parts_all_unchanged(git_repo, monkeypatch):
(['--check', '--diff', '--isort'], A_PY_DIFF_BLACK_ISORT, A_PY, 1),
],
)
@pytest.mark.parametrize("newline", ["\n", "\r\n"], ids=["unix", "windows"])
def test_main(
git_repo, monkeypatch, capsys, arguments, expect_stdout, expect_a_py, expect_retval
git_repo,
monkeypatch,
capsys,
arguments,
newline,
expect_stdout,
expect_a_py,
expect_retval,
): # pylint: disable=too-many-arguments
"""Main function outputs diffs and modifies files correctly"""
monkeypatch.chdir(git_repo.root)
paths = git_repo.add({'a.py': '\n', 'b.py': '\n'}, commit='Initial commit')
paths['a.py'].write('\n'.join(A_PY))
paths['b.py'].write('print(42 )\n')
paths = git_repo.add({"a.py": newline, "b.py": newline}, commit="Initial commit")
paths["a.py"].write(newline.join(A_PY))
paths["b.py"].write("print(42 ){newline}")

retval = darker.__main__.main(arguments + ['a.py'])

stdout = capsys.readouterr().out.replace(str(git_repo.root), '')
assert stdout.split('\n') == expect_stdout
assert paths['a.py'].readlines(cr=False) == expect_a_py
assert paths['b.py'].readlines(cr=False) == ['print(42 )', '']
assert stdout.split("\n") == expect_stdout
assert paths["a.py"].read("br").decode("ascii") == newline.join(expect_a_py)
assert paths["b.py"].read("br").decode("ascii") == "print(42 ){newline}"
assert retval == expect_retval


@pytest.mark.parametrize(
"encoding, text", [(b"utf-8", b"touch\xc3\xa9"), (b"iso-8859-1", b"touch\xe9")]
)
@pytest.mark.parametrize("newline", [b"\n", b"\r\n"])
def test_main_encoding(git_repo, encoding, text, newline):
"""Encoding and newline of the file is kept unchanged after reformatting"""
paths = git_repo.add({"a.py": newline}, commit="Initial commit")
edited = [b"# coding: ", encoding, newline, b's="', text, b'"', newline]
expect = [b"# coding: ", encoding, newline, b's = "', text, b'"', newline]
paths["a.py"].write(b"".join(edited), "wb")

retval = darker.__main__.main(["a.py"])

result = paths["a.py"].read("br")
assert retval == 0
assert result == b"".join(expect)


def test_output_diff(capsys):
"""output_diff() prints Black-style diff output"""
darker.__main__.print_diff(
Expand Down Expand Up @@ -220,3 +250,22 @@ def test_output_diff(capsys):
'-changed',
'+Changed',
]


@pytest.mark.parametrize(
"new_content, expect",
[
(TextDocument(), b""),
(TextDocument(lines=["touché"]), b"touch\xc3\xa9\n"),
(TextDocument(lines=["touché"], newline="\r\n"), b"touch\xc3\xa9\r\n"),
(TextDocument(lines=["touché"], encoding="iso-8859-1"), b"touch\xe9\n"),
],
)
def test_modify_file(tmp_path, new_content, expect):
"""Encoding and newline are respected when writing a text file on disk"""
path = tmp_path / "test.py"

darker.__main__.modify_file(path, new_content)

result = path.read_bytes()
assert result == expect
Loading

0 comments on commit 51a791c

Please sign in to comment.