Skip to content

Commit

Permalink
feat: highlight suggested changes with color
Browse files Browse the repository at this point in the history
  • Loading branch information
andreoliwa committed Jun 20, 2019
1 parent 61f467c commit f49f456
Show file tree
Hide file tree
Showing 12 changed files with 136 additions and 100 deletions.
4 changes: 2 additions & 2 deletions nitpick/files/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ def check_exists(self) -> YieldFlake8Error:
if missing_message:
phrases.append(missing_message)
if suggestion:
phrases.append("Create it with this content:\n{}".format(suggestion))
yield self.flake8_error(1, ". ".join(phrases))
phrases.append("Create it with this content:")
yield self.flake8_error(1, ". ".join(phrases), suggestion)
elif not should_exist and file_exists:
yield self.flake8_error(2, " should be deleted")
elif file_exists:
Expand Down
5 changes: 1 addition & 4 deletions nitpick/files/pre_commit.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,10 +119,7 @@ def check_repo_block(self, expected_repo_block: OrderedDict) -> YieldFlake8Error
for unique_key, hook in expected_hooks.items():
if unique_key not in self.actual_hooks:
yield self.flake8_error(
2,
": hook {!r} not found. Use this:\n{}".format(
hook.hook_id, Yaml(data=hook.yaml.as_data).reformatted
),
2, ": hook {!r} not found. Use this:".format(hook.hook_id), Yaml(data=hook.yaml.as_data).reformatted
)
continue

Expand Down
14 changes: 6 additions & 8 deletions nitpick/files/setup_cfg.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def check_rules(self) -> YieldFlake8Error:
actual_sections = set(setup_cfg.sections())
missing = self.get_missing_output(actual_sections)
if missing:
yield self.flake8_error(1, " has some missing sections. Use this:\n{}".format(missing))
yield self.flake8_error(1, " has some missing sections. Use this:", missing)

for section in self.expected_sections - self.missing_sections:
expected_dict = self.file_dict[section]
Expand All @@ -72,8 +72,8 @@ def compare_different_keys(self, section, key, raw_actual: Any, raw_expected: An
if missing:
yield self.flake8_error(
2,
" has missing values in the {!r} key.".format(key)
+ " Include those values:\n[{}]\n{} = (...),{}".format(section, key, ",".join(sorted(missing))),
" has missing values in the {!r} key. Include those values:".format(key),
"[{}]\n{} = (...),{}".format(section, key, ",".join(sorted(missing))),
)
return

Expand All @@ -87,18 +87,16 @@ def compare_different_keys(self, section, key, raw_actual: Any, raw_expected: An
if actual != expected:
yield self.flake8_error(
3,
": [{}]{} is {} but it should be like this:".format(section, key, raw_actual)
+ "\n[{}]\n{} = {}".format(section, key, raw_expected),
": [{}]{} is {} but it should be like this:".format(section, key, raw_actual),
"[{}]\n{} = {}".format(section, key, raw_expected),
)

def show_missing_keys(self, section, key, values: List[Tuple[str, Any]]) -> YieldFlake8Error:
"""Show the keys that are not present in a section."""
missing_cfg = ConfigParser()
missing_cfg[section] = dict(values)
output = self.get_example_cfg(missing_cfg)
yield self.flake8_error(
4, ": section [{}] has some missing key/value pairs. Use this:\n{}".format(section, output)
)
yield self.flake8_error(4, ": section [{}] has some missing key/value pairs. Use this:".format(section), output)

@staticmethod
def get_example_cfg(config_parser: ConfigParser) -> str:
Expand Down
17 changes: 12 additions & 5 deletions nitpick/mixin.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
"""Mixin to raise flake8 errors."""
import click

from nitpick.constants import ERROR_PREFIX
from nitpick.formats import Comparison
from nitpick.typedefs import Flake8Error
Expand All @@ -10,26 +12,31 @@ class NitpickMixin:
error_base_number = 0 # type: int
error_prefix = "" # type: str

def flake8_error(self, error_number: int, error_message: str) -> Flake8Error:
def flake8_error(self, number: int, message: str, suggestion: str = None) -> Flake8Error:
"""Return a flake8 error as a tuple."""
final_number = self.error_base_number + error_number
joined_number = self.error_base_number + number
suggestion_with_newline = (
click.style("\n{}".format(suggestion.rstrip()), fg="bright_green") if suggestion else ""
)

from nitpick.plugin import NitpickChecker

return (
1,
0,
"{}{} {}{}".format(ERROR_PREFIX, final_number, self.error_prefix, error_message.rstrip()),
"{}{} {}{}{}".format(
ERROR_PREFIX, joined_number, self.error_prefix, message.rstrip(), suggestion_with_newline
),
NitpickChecker,
)

def warn_missing_different(self, comparison: Comparison, prefix_message: str = ""):
"""Warn about missing and different keys."""
if comparison.missing_format:
yield self.flake8_error(
8, "{} has missing values:\n{}".format(prefix_message, comparison.missing_format.reformatted)
8, "{} has missing values:".format(prefix_message), comparison.missing_format.reformatted
)
if comparison.diff_format:
yield self.flake8_error(
9, "{} has different values. Use this:\n{}".format(prefix_message, comparison.diff_format.reformatted)
9, "{} has different values. Use this:".format(prefix_message), comparison.diff_format.reformatted
)
12 changes: 10 additions & 2 deletions nitpick/style.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,15 @@ def merge_toml_dict(self) -> JsonDict:
return {}
merged_dict = self._all_styles.merge()
merged_style_path = self.config.cache_dir / MERGED_STYLE_TOML # type: Path
self.config.cache_dir.mkdir(parents=True, exist_ok=True)
toml = Toml(data=merged_dict)
merged_style_path.write_text(toml.reformatted)

attempt = 1
while attempt < 5:
try:
self.config.cache_dir.mkdir(parents=True, exist_ok=True)
merged_style_path.write_text(toml.reformatted)
break
except OSError:
attempt += 1

return merged_dict
13 changes: 11 additions & 2 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ dictdiffer = "*"
python-slugify = "*"
jmespath = "*"
sortedcontainers = "*"
click = "*"

[tool.poetry.dev-dependencies]
ipython = "*"
Expand Down
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ include_trailing_comma = True
force_grid_wrap = 0
combine_as_imports = True
known_first_party = tests
known_third_party = _pytest,attr,dictdiffer,jmespath,pytest,requests,responses,ruamel,slugify,sortedcontainers,testfixtures,toml
known_third_party = _pytest,attr,click,dictdiffer,jmespath,pytest,requests,responses,ruamel,slugify,sortedcontainers,testfixtures,toml

[mypy]
ignore_missing_imports = True
Expand Down
7 changes: 4 additions & 3 deletions tests/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,14 +177,15 @@ def assert_errors_contain_unordered(self, raw_error: str, expected_count: int =
TODO Once there is a way to force some sorting on the YAML output, this method can be removed,
and ``assert_errors_contain()`` can be used again.
"""
expected_error = dedent(raw_error).strip()
original_expected_error = dedent(raw_error).strip()
expected_error = original_expected_error.replace("\x1b[0m", "")
expected_error_lines = set(expected_error.split("\n"))
for actual_error in self._errors:
if set(actual_error.split("\n")) == expected_error_lines:
if set(actual_error.replace("\x1b[0m", "").split("\n")) == expected_error_lines:
self.assert_error_count(expected_count)
return self

self.show_errors(expected_error)
self.show_errors(original_expected_error)
return self

def assert_single_error(self, raw_error: str) -> "ProjectMock":
Expand Down
48 changes: 24 additions & 24 deletions tests/test_pre_commit.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def test_suggest_initial_contents(request):
"""
).lint().assert_errors_contain(
"""
NIP331 File .pre-commit-config.yaml was not found. Create it with this content:
NIP331 File .pre-commit-config.yaml was not found. Create it with this content:\x1b[92m
repos:
- repo: https://github.com/asottile/seed-isort-config
rev: v1.9.1
Expand All @@ -42,7 +42,7 @@ def test_suggest_initial_contents(request):
rev: v1.0.0-1
hooks:
- id: blacken-docs
additional_dependencies: [black==19.3b0]
additional_dependencies: [black==19.3b0]\x1b[0m
"""
)

Expand All @@ -58,10 +58,10 @@ def test_root_values_on_missing_file(request):
"""
).lint().assert_errors_contain_unordered(
"""
NIP331 File .pre-commit-config.yaml was not found. Create it with this content:
NIP331 File .pre-commit-config.yaml was not found. Create it with this content:\x1b[92m
bla_bla: oh yeah
fail_fast: true
whatever: '1'
whatever: '1'\x1b[0m
"""
)

Expand All @@ -86,15 +86,15 @@ def test_root_values_on_existing_file(request):
"""
).lint().assert_errors_contain_unordered(
"""
NIP338 File .pre-commit-config.yaml has missing values:
NIP338 File .pre-commit-config.yaml has missing values:\x1b[92m
blabla: what
fail_fast: true
fail_fast: true\x1b[0m
"""
).assert_errors_contain(
"""
NIP339 File .pre-commit-config.yaml has different values. Use this:
NIP339 File .pre-commit-config.yaml has different values. Use this:\x1b[92m
another_thing: yep
something: true
something: true\x1b[0m
"""
)

Expand Down Expand Up @@ -368,51 +368,51 @@ def test_missing_different_values(request):
"""
).lint().assert_errors_contain(
"""
NIP332 File .pre-commit-config.yaml: hook 'mypy' not found. Use this:
NIP332 File .pre-commit-config.yaml: hook 'mypy' not found. Use this:\x1b[92m
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v0.701
hooks:
- id: mypy
- id: mypy\x1b[0m
"""
).assert_errors_contain(
"""
NIP332 File .pre-commit-config.yaml: hook 'python-check-mock-methods' not found. Use this:
NIP332 File .pre-commit-config.yaml: hook 'python-check-mock-methods' not found. Use this:\x1b[92m
- repo: https://github.com/pre-commit/pygrep-hooks
rev: v1.4.0
hooks:
- id: python-check-mock-methods
- id: python-check-mock-methods\x1b[0m
"""
).assert_errors_contain(
"""
NIP339 File .pre-commit-config.yaml: hook 'bashate' has different values. Use this:
rev: 0.6.0
NIP339 File .pre-commit-config.yaml: hook 'bashate' has different values. Use this:\x1b[92m
rev: 0.6.0\x1b[0m
"""
).assert_errors_contain(
"""
NIP339 File .pre-commit-config.yaml: hook 'python-check-blanket-noqa' has different values. Use this:
rev: v1.4.0
NIP339 File .pre-commit-config.yaml: hook 'python-check-blanket-noqa' has different values. Use this:\x1b[92m
rev: v1.4.0\x1b[0m
"""
).assert_errors_contain(
"""
NIP339 File .pre-commit-config.yaml: hook 'python-no-eval' has different values. Use this:
rev: v1.4.0
NIP339 File .pre-commit-config.yaml: hook 'python-no-eval' has different values. Use this:\x1b[92m
rev: v1.4.0\x1b[0m
"""
).assert_errors_contain(
"""
NIP339 File .pre-commit-config.yaml: hook 'python-no-log-warn' has different values. Use this:
rev: v1.4.0
NIP339 File .pre-commit-config.yaml: hook 'python-no-log-warn' has different values. Use this:\x1b[92m
rev: v1.4.0\x1b[0m
"""
).assert_errors_contain(
"""
NIP339 File .pre-commit-config.yaml: hook 'my-hook' has different values. Use this:
NIP339 File .pre-commit-config.yaml: hook 'my-hook' has different values. Use this:\x1b[92m
args:
- --expected
- arguments
- arguments\x1b[0m
"""
).assert_errors_contain(
"""
NIP339 File .pre-commit-config.yaml: hook 'rst-backticks' has different values. Use this:
rev: v1.4.0
NIP339 File .pre-commit-config.yaml: hook 'rst-backticks' has different values. Use this:\x1b[92m
rev: v1.4.0\x1b[0m
""",
8,
)
20 changes: 10 additions & 10 deletions tests/test_setup_cfg.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ def test_comma_separated_keys_on_style_file(request):
)
project.assert_single_error(
"""
NIP322 File setup.cfg has missing values in the 'eat' key. Include those values:
NIP322 File setup.cfg has missing values in the 'eat' key. Include those values:\x1b[92m
[food]
eat = (...),ham,salt
eat = (...),ham,salt\x1b[0m
"""
)

Expand All @@ -55,15 +55,15 @@ def test_suggest_initial_contents(request):
"""
).lint().assert_single_error(
"""
NIP321 File setup.cfg was not found. Do something here. Create it with this content:
NIP321 File setup.cfg was not found. Do something here. Create it with this content:\x1b[92m
[flake8]
max-line-length = 120
[isort]
line_length = 120
[mypy]
ignore_missing_imports = True
ignore_missing_imports = True\x1b[0m
"""
)

Expand All @@ -88,12 +88,12 @@ def test_missing_sections(request):
"""
).lint().assert_single_error(
"""
NIP321 File setup.cfg has some missing sections. Use this:
NIP321 File setup.cfg has some missing sections. Use this:\x1b[92m
[flake8]
max-line-length = 120
[isort]
line_length = 120
line_length = 120\x1b[0m
"""
)

Expand Down Expand Up @@ -122,14 +122,14 @@ def test_different_missing_keys(request):
"""
).lint().assert_errors_contain(
"""
NIP323 File setup.cfg: [isort]line_length is 30 but it should be like this:
NIP323 File setup.cfg: [isort]line_length is 30 but it should be like this:\x1b[92m
[isort]
line_length = 110
line_length = 110\x1b[0m
"""
).assert_errors_contain(
"""
NIP324 File setup.cfg: section [flake8] has some missing key/value pairs. Use this:
NIP324 File setup.cfg: section [flake8] has some missing key/value pairs. Use this:\x1b[92m
[flake8]
max-line-length = 112
max-line-length = 112\x1b[0m
"""
)
Loading

0 comments on commit f49f456

Please sign in to comment.