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

Implement custom output handling in sort_file #1588

Merged
merged 3 commits into from Nov 1, 2020
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
110 changes: 69 additions & 41 deletions isort/api.py
Expand Up @@ -284,6 +284,7 @@ def sort_file(
ask_to_apply: bool = False,
show_diff: Union[bool, TextIO] = False,
write_to_stdout: bool = False,
output: Optional[TextIO] = None,
**config_kwargs,
) -> bool:
"""Sorts and formats any groups of imports imports within the provided file or Path.
Expand All @@ -298,6 +299,8 @@ def sort_file(
- **show_diff**: If `True` the changes that need to be done will be printed to stdout, if a
TextIO stream is provided results will be written to it, otherwise no diff will be computed.
- **write_to_stdout**: If `True`, write to stdout instead of the input file.
- **output**: If a TextIO is provided, results will be written there rather than replacing
the original file content.
- ****config_kwargs**: Any config modifications.
"""
with io.File.read(filename) as source_file:
Expand All @@ -315,49 +318,74 @@ def sort_file(
extension=extension,
)
else:
tmp_file = source_file.path.with_suffix(source_file.path.suffix + ".isorted")
try:
with tmp_file.open(
"w", encoding=source_file.encoding, newline=""
) as output_stream:
shutil.copymode(filename, tmp_file)
changed = sort_stream(
input_stream=source_file.stream,
output_stream=output_stream,
config=config,
file_path=actual_file_path,
disregard_skip=disregard_skip,
extension=extension,
)
if output is None:
tmp_file = source_file.path.with_suffix(source_file.path.suffix + ".isorted")
try:
with tmp_file.open(
"w", encoding=source_file.encoding, newline=""
) as output_stream:
shutil.copymode(filename, tmp_file)
changed = sort_stream(
input_stream=source_file.stream,
output_stream=output_stream,
config=config,
file_path=actual_file_path,
disregard_skip=disregard_skip,
extension=extension,
)
if changed:
if show_diff or ask_to_apply:
source_file.stream.seek(0)
with tmp_file.open(
encoding=source_file.encoding, newline=""
) as tmp_out:
show_unified_diff(
file_input=source_file.stream.read(),
file_output=tmp_out.read(),
file_path=actual_file_path,
output=None
if show_diff is True
else cast(TextIO, show_diff),
color_output=config.color_output,
)
if show_diff or (
ask_to_apply
and not ask_whether_to_apply_changes_to_file(
str(source_file.path)
)
):
return False
source_file.stream.close()
tmp_file.replace(source_file.path)
if not config.quiet:
print(f"Fixing {source_file.path}")
finally:
try: # Python 3.8+: use `missing_ok=True` instead of try except.
tmp_file.unlink()
except FileNotFoundError:
pass # pragma: no cover
else:
changed = sort_stream(
input_stream=source_file.stream,
output_stream=output,
config=config,
file_path=actual_file_path,
disregard_skip=disregard_skip,
extension=extension,
)
if changed:
if show_diff or ask_to_apply:
if show_diff:
source_file.stream.seek(0)
with tmp_file.open(
encoding=source_file.encoding, newline=""
) as tmp_out:
show_unified_diff(
file_input=source_file.stream.read(),
file_output=tmp_out.read(),
file_path=actual_file_path,
output=None if show_diff is True else cast(TextIO, show_diff),
color_output=config.color_output,
)
if show_diff or (
ask_to_apply
and not ask_whether_to_apply_changes_to_file(
str(source_file.path)
)
):
return False
source_file.stream.close()
tmp_file.replace(source_file.path)
if not config.quiet:
print(f"Fixing {source_file.path}")
finally:
try: # Python 3.8+: use `missing_ok=True` instead of try except.
tmp_file.unlink()
except FileNotFoundError:
pass # pragma: no cover
output.seek(0)
show_unified_diff(
file_input=source_file.stream.read(),
file_output=output.read(),
file_path=actual_file_path,
output=None if show_diff is True else cast(TextIO, show_diff),
color_output=config.color_output,
)
source_file.stream.close()

except ExistingSyntaxErrors:
warn(f"{actual_file_path} unable to sort due to existing syntax errors")
except IntroducedSyntaxErrors: # pragma: no cover
Expand Down
28 changes: 28 additions & 0 deletions tests/unit/test_ticketed_features.py
Expand Up @@ -848,3 +848,31 @@ def my_function():
pass
"""
)


def test_api_to_allow_custom_diff_and_output_stream_1583(capsys, tmpdir):
"""isort should provide a way from the Python API to process an existing
file and output to a stream the new version of that file, as well as a diff
to a different stream.
See: https://github.com/PyCQA/isort/issues/1583
"""

tmp_file = tmpdir.join("file.py")
tmp_file.write("import b\nimport a\n")

isort_diff = StringIO()
isort_output = StringIO()

isort.file(tmp_file, show_diff=isort_diff, output=isort_output)

_, error = capsys.readouterr()
assert not error

isort_diff.seek(0)
isort_diff_content = isort_diff.read()
assert "+import a" in isort_diff_content
assert " import b" in isort_diff_content
assert "-import a" in isort_diff_content

isort_output.seek(0)
assert isort_output.read().splitlines() == ["import a", "import b"]