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

Keep encoding and newlines intact. Fixes #115. #117

Merged
merged 1 commit into from
Apr 3, 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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -120,4 +120,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 @@ -51,4 +51,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