Skip to content

Commit

Permalink
Adds colors to --diff output.
Browse files Browse the repository at this point in the history
Resolves #1405
  • Loading branch information
sztamas committed Aug 25, 2020
1 parent cf44d67 commit 1371f04
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 8 deletions.
3 changes: 3 additions & 0 deletions isort/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ def sort_stream(
file_output=_output_stream.read(),
file_path=file_path,
output=output_stream if show_diff is True else cast(TextIO, show_diff),
color_output=config.color_output,
)
return changed

Expand Down Expand Up @@ -233,6 +234,7 @@ def check_stream(
file_output=output_stream.read(),
file_path=file_path,
output=None if show_diff is True else cast(TextIO, show_diff),
color_output=config.color_output,
)
return False

Expand Down Expand Up @@ -335,6 +337,7 @@ def sort_file(
file_output=tmp_out.read(),
file_path=file_path or source_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
Expand Down
48 changes: 40 additions & 8 deletions isort/format.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import re
import sys
from datetime import datetime
from difflib import unified_diff
Expand All @@ -13,6 +14,10 @@
colorama.init()


ADDED_LINE_PATTERN = re.compile(r"\+[^+]")
REMOVED_LINE_PATTERN = re.compile(r"-[^-]")


def format_simplified(import_line: str) -> str:
import_line = import_line.strip()
if import_line.startswith("from "):
Expand All @@ -37,16 +42,22 @@ def format_natural(import_line: str) -> str:


def show_unified_diff(
*, file_input: str, file_output: str, file_path: Optional[Path], output: Optional[TextIO] = None
*,
file_input: str,
file_output: str,
file_path: Optional[Path],
output: Optional[TextIO] = None,
color_output: bool = False,
):
"""Shows a unified_diff for the provided input and output against the provided file path.
- **file_input**: A string that represents the contents of a file before changes.
- **file_output**: A string that represents the contents of a file after changes.
- **file_path**: A Path object that represents the file path of the file being changed.
- **output**: A stream to output the diff to. If non is provided uses sys.stdout.
- **color_output**: Use color in output if True.
"""
output = sys.stdout if output is None else output
printer = create_terminal_printer(color_output, output)
file_name = "" if file_path is None else str(file_path)
file_mtime = str(
datetime.now() if file_path is None else datetime.fromtimestamp(file_path.stat().st_mtime)
Expand All @@ -60,7 +71,7 @@ def show_unified_diff(
tofiledate=str(datetime.now()),
)
for line in unified_diff_lines:
output.write(line)
printer.diff_line(line)


def ask_whether_to_apply_changes_to_file(file_path: str) -> bool:
Expand All @@ -84,28 +95,49 @@ class BasicPrinter:
ERROR = "ERROR"
SUCCESS = "SUCCESS"

def __init__(self, output: Optional[TextIO] = None):
self.output = output or sys.stdout

def success(self, message: str) -> None:
print(f"{self.SUCCESS}: {message}")
print(f"{self.SUCCESS}: {message}", file=self.output)

def error(self, message: str) -> None:
print(
f"{self.ERROR}: {message}",
file=self.output,
# TODO this should print to stderr, but don't want to make it backward incompatible now
# file=sys.stderr
)

def diff_line(self, line: str) -> None:
self.output.write(line)


class ColoramaPrinter(BasicPrinter):
def __init__(self):
ADDED_LINE = colorama.Fore.GREEN
REMOVED_LINE = colorama.Fore.RED

def __init__(self, output: Optional[TextIO] = None):
self.output = output or sys.stdout
self.ERROR = self.style_text("ERROR", colorama.Fore.RED)
self.SUCCESS = self.style_text("SUCCESS", colorama.Fore.GREEN)

@staticmethod
def style_text(text: str, style: str) -> str:
def style_text(text: str, style: Optional[str] = None) -> str:
if style is None:
return text
return style + text + colorama.Style.RESET_ALL

def diff_line(self, line: str) -> None:
style = None
if re.match(ADDED_LINE_PATTERN, line):
style = self.ADDED_LINE
elif re.match(REMOVED_LINE_PATTERN, line):
style = self.REMOVED_LINE
self.output.write(self.style_text(line, style))


def create_terminal_printer(color: bool):
def create_terminal_printer(color: bool, output: Optional[TextIO] = None):
if color and colorama_unavailable:
no_colorama_message = (
"\n"
Expand All @@ -118,4 +150,4 @@ def create_terminal_printer(color: bool):
print(no_colorama_message, file=sys.stderr)
sys.exit(1)

return ColoramaPrinter() if color else BasicPrinter()
return ColoramaPrinter(output) if color else BasicPrinter(output)
42 changes: 42 additions & 0 deletions tests/unit/test_format.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from io import StringIO
from unittest.mock import MagicMock, patch

import colorama
Expand Down Expand Up @@ -29,6 +30,15 @@ def test_basic_printer(capsys):
assert out == "ERROR: Some error\n"


def test_basic_printer_diff(capsys):
printer = isort.format.create_terminal_printer(color=False)
printer.diff_line("+ added line\n")
printer.diff_line("- removed line\n")

out, _ = capsys.readouterr()
assert out == "+ added line\n- removed line\n"


def test_colored_printer_success(capsys):
printer = isort.format.create_terminal_printer(color=True)
printer.success("All good!")
Expand All @@ -47,6 +57,38 @@ def test_colored_printer_error(capsys):
assert colorama.Fore.RED in out


def test_colored_printer_diff(capsys):
printer = isort.format.create_terminal_printer(color=True)
printer.diff_line("+++ file1\n")
printer.diff_line("--- file2\n")
printer.diff_line("+ added line\n")
printer.diff_line("normal line\n")
printer.diff_line("- removed line\n")
printer.diff_line("normal line\n")

out, _ = capsys.readouterr()
# No color added to lines with multiple + and -'s
assert out.startswith("+++ file1\n--- file2\n")
# Added lines are green
assert colorama.Fore.GREEN + "+ added line" in out
# Removed lines are red
assert colorama.Fore.RED + "- removed line" in out
# Normal lines are resetted back
assert colorama.Style.RESET_ALL + "normal line" in out


def test_colored_printer_diff_output(capsys):
output = StringIO()
printer = isort.format.create_terminal_printer(color=True, output=output)
printer.diff_line("a line\n")

out, _ = capsys.readouterr()
assert out == ""

output.seek(0)
assert output.read().startswith("a line\n")


@patch("isort.format.colorama_unavailable", True)
def test_colorama_not_available_handled_gracefully(capsys):
with pytest.raises(SystemExit) as system_exit:
Expand Down

0 comments on commit 1371f04

Please sign in to comment.