From 9550b55b8fa4458ff1b5663687e151ccf390aca2 Mon Sep 17 00:00:00 2001 From: Blank Spruce <32396809+BlankSpruce@users.noreply.github.com> Date: Fri, 14 Feb 2025 16:43:04 +0100 Subject: [PATCH] Make --diff usable with --check This follows black's behaviour. --- .gersemirc.example | 2 +- CHANGELOG.md | 4 ++++ README.md | 9 ++++---- gersemi/__main__.py | 6 ++++- gersemi/__version__.py | 2 +- gersemi/mode.py | 3 +++ gersemi/runner.py | 34 +++++++++++++++++++--------- gersemi/tasks/check_and_show_diff.py | 12 ++++++++++ gersemi/tasks/show_diff.py | 21 ++++++----------- tests/test_executable.py | 27 +++++++++++++++++++++- 10 files changed, 87 insertions(+), 33 deletions(-) create mode 100644 gersemi/tasks/check_and_show_diff.py diff --git a/.gersemirc.example b/.gersemirc.example index d7759d5..00ef0c1 100644 --- a/.gersemirc.example +++ b/.gersemirc.example @@ -1,4 +1,4 @@ -# yaml-language-server: $schema=https://raw.githubusercontent.com/BlankSpruce/gersemi/0.19.0/gersemi/configuration.schema.json +# yaml-language-server: $schema=https://raw.githubusercontent.com/BlankSpruce/gersemi/0.19.1/gersemi/configuration.schema.json definitions: [] disable_formatting: false diff --git a/CHANGELOG.md b/CHANGELOG.md index 924645a..50cf972 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,8 @@ # Changelog +## [0.19.1] 2025-02-14 +### Fixed +- Make `--diff` usable with `--check`. (#58) + ## [0.19.0] 2025-02-10 ### Added - Add `--warnings-as-errors`. (#57) diff --git a/README.md b/README.md index 03a8c7e..c5dc448 100644 --- a/README.md +++ b/README.md @@ -36,9 +36,10 @@ positional arguments: modes: -c, --check Check if files require reformatting. Return 0 when there's nothing to reformat. Return 1 when some files would be - reformatted. + reformatted. It can be used together with --diff. -i, --in-place Format files in-place. - --diff Show diff on stdout for each formatted file instead. + --diff Show diff on stdout for each formatted file instead. It can be + used together with --check. --print-config {minimal,verbose,default} Print configuration for files. With "minimal" prints source of outcome configuration (configuration file or defaults) and the @@ -134,7 +135,7 @@ You can use gersemi with a pre-commit hook by adding the following to `.pre-comm ```yaml repos: - repo: https://github.com/BlankSpruce/gersemi - rev: 0.19.0 + rev: 0.19.1 hooks: - id: gersemi ``` @@ -150,7 +151,7 @@ If you want to use extensions with pre-commit list them with [`additional_depend ```yaml repos: - repo: https://github.com/BlankSpruce/gersemi - rev: 0.19.0 + rev: 0.19.1 hooks: - id: gersemi additional_dependencies: diff --git a/gersemi/__main__.py b/gersemi/__main__.py index 1040dd3..a5e64db 100644 --- a/gersemi/__main__.py +++ b/gersemi/__main__.py @@ -68,6 +68,7 @@ def create_argparser(): Check if files require reformatting. Return {SUCCESS} when there's nothing to reformat. Return {FAIL} when some files would be reformatted. + It can be used together with --diff. """, ) modes_group.add_argument( @@ -81,7 +82,10 @@ def create_argparser(): "--diff", dest="show_diff", action="store_true", - help="Show diff on stdout for each formatted file instead.", + help=""" + Show diff on stdout for each formatted file instead. + It can be used together with --check. + """, ) modes_group.add_argument( "--print-config", diff --git a/gersemi/__version__.py b/gersemi/__version__.py index 13e8e9a..6c96e78 100644 --- a/gersemi/__version__.py +++ b/gersemi/__version__.py @@ -4,4 +4,4 @@ __license__ = "MPL 2.0" __title__ = "gersemi" __url__ = "https://github.com/BlankSpruce/gersemi" -__version__ = "0.19.0" +__version__ = "0.19.1" diff --git a/gersemi/mode.py b/gersemi/mode.py index c9469ff..1ab07c5 100644 --- a/gersemi/mode.py +++ b/gersemi/mode.py @@ -7,9 +7,12 @@ class Mode(Enum): CheckFormatting = 2 ShowDiff = 3 PrintConfig = 4 + CheckFormattingAndShowDiff = 5 def get_mode(args) -> Mode: + if args.check_formatting and args.show_diff: + return Mode.CheckFormattingAndShowDiff if args.show_diff: return Mode.ShowDiff if args.check_formatting: diff --git a/gersemi/runner.py b/gersemi/runner.py index ac97b54..3aaf6fb 100644 --- a/gersemi/runner.py +++ b/gersemi/runner.py @@ -37,11 +37,12 @@ from gersemi.return_codes import FAIL, INTERNAL_ERROR, SUCCESS from gersemi.task_result import TaskResult from gersemi.tasks.check_formatting import check_formatting +from gersemi.tasks.check_and_show_diff import check_and_show_diff from gersemi.tasks.do_nothing import do_nothing from gersemi.tasks.forward_to_stdout import forward_to_stdout from gersemi.tasks.format_file import format_file from gersemi.tasks.rewrite_in_place import rewrite_in_place -from gersemi.tasks.show_diff import show_colorized_diff, show_diff +from gersemi.tasks.show_diff import show_diff from gersemi.utils import fromfile, smart_open from gersemi.keywords import Keywords from gersemi.warnings import UnknownCommandWarning @@ -58,11 +59,16 @@ class WarningSink: def __init__(self, quiet): self.quiet = quiet self.at_least_one_warning_issued = False + self.records = [] - def __call__(self, *args, **kwargs): + def __call__(self, s: str): self.at_least_one_warning_issued = True if not self.quiet: - print_to_stderr(*args, **kwargs) + self.records.append(s) + + def flush(self): + for s in self.records: + print_to_stderr(s) class StatusCode: @@ -162,13 +168,14 @@ def find_all_custom_command_definitions( def select_task(mode: Mode, configuration: Configuration): return { - Mode.ForwardToStdout: lambda _: forward_to_stdout, - Mode.RewriteInPlace: lambda _: rewrite_in_place, - Mode.CheckFormatting: lambda _: check_formatting, - Mode.ShowDiff: lambda config: ( - show_colorized_diff if config.control.color else show_diff + Mode.ForwardToStdout: forward_to_stdout, + Mode.RewriteInPlace: rewrite_in_place, + Mode.CheckFormatting: check_formatting, + Mode.ShowDiff: partial(show_diff, configuration.control.color), + Mode.CheckFormattingAndShowDiff: partial( + check_and_show_diff, configuration.control.color ), - }[mode](configuration) + }[mode] def run_task( @@ -257,7 +264,11 @@ def split_files_by_formatting_state( def store_files_in_cache( mode: Mode, cache, configuration_summary: str, files: Iterable[Path] ) -> None: - if mode in [Mode.CheckFormatting, Mode.RewriteInPlace]: + if mode in [ + Mode.CheckFormatting, + Mode.CheckFormattingAndShowDiff, + Mode.RewriteInPlace, + ]: cache.store_files(configuration_summary, files) @@ -335,7 +346,7 @@ def __init__( self.warning_sink = warning_sink def _warn(self, item: NotSupportedKeys, text: str): - self.warning_sink(f"{item.path}:", text) + self.warning_sink(f"{item.path}: {text}") def _inform_about_not_supported_keys(self, item: NotSupportedKeys): if item.path in self.processed_configuration_files: @@ -442,4 +453,5 @@ def run(args: argparse.Namespace): if (control.warnings_as_errors and warning_sink.at_least_one_warning_issued) else SUCCESS ) + warning_sink.flush() return status_code.value diff --git a/gersemi/tasks/check_and_show_diff.py b/gersemi/tasks/check_and_show_diff.py new file mode 100644 index 0000000..b7e3c39 --- /dev/null +++ b/gersemi/tasks/check_and_show_diff.py @@ -0,0 +1,12 @@ +from gersemi.formatted_file import FormattedFile +from gersemi.task_result import TaskResult +from gersemi.tasks.check_formatting import check_formatting +from gersemi.tasks.show_diff import get_diff + + +def check_and_show_diff( + should_colorize: bool, formatted_file: FormattedFile +) -> TaskResult: + result = check_formatting(formatted_file) + result.to_stdout = get_diff(should_colorize, formatted_file) + return result diff --git a/gersemi/tasks/show_diff.py b/gersemi/tasks/show_diff.py index b62204a..e6784ee 100644 --- a/gersemi/tasks/show_diff.py +++ b/gersemi/tasks/show_diff.py @@ -1,5 +1,4 @@ from difflib import unified_diff -from typing import Iterator from gersemi.formatted_file import FormattedFile from gersemi.return_codes import SUCCESS from gersemi.task_result import TaskResult @@ -29,29 +28,23 @@ def colorize(diff): yield from diff -def get_diff(formatted_file: FormattedFile) -> Iterator[str]: - return unified_diff( +def get_diff(should_colorize: bool, formatted_file: FormattedFile) -> str: + result = unified_diff( a=f"{formatted_file.before}\n".splitlines(keepends=True), b=f"{formatted_file.after}\n".splitlines(keepends=True), fromfile=fromfile(formatted_file.path), tofile=tofile(formatted_file.path), n=5, ) + if should_colorize: + result = colorize(result) + return "".join(result) -def show_diff(formatted_file: FormattedFile) -> TaskResult: +def show_diff(should_colorize: bool, formatted_file: FormattedFile) -> TaskResult: return TaskResult( path=formatted_file.path, return_code=SUCCESS, - to_stdout="".join(get_diff(formatted_file)), - warnings=formatted_file.warnings, - ) - - -def show_colorized_diff(formatted_file: FormattedFile) -> TaskResult: - return TaskResult( - path=formatted_file.path, - return_code=SUCCESS, - to_stdout="".join(colorize(get_diff(formatted_file))), + to_stdout=get_diff(should_colorize, formatted_file), warnings=formatted_file.warnings, ) diff --git a/tests/test_executable.py b/tests/test_executable.py index 55e0e31..0befe52 100644 --- a/tests/test_executable.py +++ b/tests/test_executable.py @@ -81,7 +81,32 @@ def test_check_on_formatted_file_should_return_zero(app, testfiles): def test_check_on_not_formatted_file_should_return_one(app, testfiles): - assert app("--check", testfiles / "not_formatted_file.cmake") == fail() + target = (testfiles / "not_formatted_file.cmake").resolve() + assert app("--check", target) == fail( + stdout="", + stderr=f"""{target} would be reformatted +""", + ) + + +def test_diff_on_not_formatted_files_should_return_zero(app, testfiles): + target = testfiles / "directory_with_not_formatted_files" + assert app("--diff", target) == success(stdout=match_not(""), stderr="") + + +def test_check_with_diff_on_not_formatted_files_should_return_one(app, testfiles): + target = testfiles / "directory_with_not_formatted_files" + file1 = (target / "file1.cmake").resolve() + file2 = (target / "file2.cmake").resolve() + file3 = (target / "file3.cmake").resolve() + + assert app("--check", "--diff", target) == fail( + stdout=match_not(""), + stderr=f"""{file1} would be reformatted +{file2} would be reformatted +{file3} would be reformatted +""", + ) def test_format_file_in_place(app, testfiles):