Repository: PyCQA/docformatter (python)

Issue:
Allow showing the diff and exit 1 if files were changed
## What is the current behavior?

Currently, you can either run:

- `docformatter file`, which will display the diff but always `exit 0` if nothing was changed
- `docformatter --check file`, which will only display the file name if there is a change and `exit 1`.

## What would you like?

I'd like to be able to both:

- show the diff
- exit 1if there is a change

in a single invocation, which would allow me to have an easier CI setup, while showing a nice diff to users.


I am happy to contribute the feature once/if there is an agreement that it is worth it and how we want to do it

Pull request description:
Allow showing the diff when using --in-place or --check with --diff
It can be useful to show the difference, e.g. in CI so that users know what is or should be changed.

This adds a `--diff` option, that, when combined with `--check` or `--in-place`, will, in addition, show the diff to stdout.

fix #125 

Patch:
diff --git a/src/docformatter/configuration.py b/src/docformatter/configuration.py
index be1be5d..c484bf7 100644
--- a/src/docformatter/configuration.py
+++ b/src/docformatter/configuration.py
@@ -102,6 +102,13 @@ class Configurater:
             action="store_true",
             help="only check and report incorrectly formatted files",
         )
+        self.parser.add_argument(
+            "-d",
+            "--diff",
+            action="store_true",
+            help="when used with `--check` or `--in-place`, also what changes "
+            "would be made",
+        )
         self.parser.add_argument(
             "-r",
             "--recursive",
diff --git a/src/docformatter/format.py b/src/docformatter/format.py
index 506c182..0f32942 100644
--- a/src/docformatter/format.py
+++ b/src/docformatter/format.py
@@ -155,8 +155,6 @@ class Formatter:
             try:
                 result = self._do_format_file(filename)
                 outcomes[result] += 1
-                if result == FormatResult.check_failed:
-                    print(unicode(filename), file=self.stderror)
             except IOError as exception:
                 outcomes[FormatResult.error] += 1
                 print(unicode(exception), file=self.stderror)
@@ -190,9 +188,13 @@ class Formatter:
             source = input_file.read()
             formatted_source = self._do_format_code(source)
 
+        ret = FormatResult.ok
+        show_diff = self.args.diff
+
         if source != formatted_source:
             if self.args.check:
-                return FormatResult.check_failed
+                print(unicode(filename), file=self.stderror)
+                ret = FormatResult.check_failed
             elif self.args.in_place:
                 with self.encodor.do_open_with_encoding(
                     filename,
@@ -200,6 +202,9 @@ class Formatter:
                 ) as output_file:
                     output_file.write(formatted_source)
             else:
+                show_diff = True
+
+            if show_diff:
                 # Standard Library Imports
                 import difflib
 
@@ -212,7 +217,7 @@ class Formatter:
                 )
                 self.stdout.write("\n".join(list(diff) + [""]))
 
-        return FormatResult.ok
+        return ret
 
     def _do_format_code(self, source):
         """Return source code with docstrings formatted.


Test patch:
diff --git a/tests/test_docformatter.py b/tests/test_docformatter.py
index 20007ee..f2b26ac 100644
--- a/tests/test_docformatter.py
+++ b/tests/test_docformatter.py
@@ -97,11 +97,18 @@ def foo():
 '''
         ],
     )
-    def test_in_place(self, temporary_file, contents):
+    @pytest.mark.parametrize(
+        "diff", [True, False], ids=["show-diff", "no-diff"]
+    )
+    def test_in_place(self, temporary_file, contents, diff):
         """Should make changes and save back to file."""
         output_file = io.StringIO()
+        args = ["my_fake_program", "--in-place", temporary_file]
+        if diff:
+            args.append("--diff")
+
         main._main(
-            argv=["my_fake_program", "--in-place", temporary_file],
+            argv=args,
             standard_out=output_file,
             standard_error=None,
             standard_in=None,
@@ -115,6 +122,11 @@ def foo():
                 == f.read()
             )
 
+        if diff:
+            assert "def foo" in output_file.getvalue()
+        else:
+            assert "def foo" not in output_file
+
     @pytest.mark.system
     @pytest.mark.parametrize(
         "contents",
@@ -167,12 +179,21 @@ def foo():
         "contents",
         ["""Totally fine docstring, do not report anything."""],
     )
-    def test_check_mode_correct_docstring(self, temporary_file, contents):
+    @pytest.mark.parametrize(
+        "diff", [True, False], ids=["show-diff", "no-diff"]
+    )
+    def test_check_mode_correct_docstring(
+        self, temporary_file, contents, diff
+    ):
         """"""
         stdout = io.StringIO()
         stderr = io.StringIO()
+        args = ["my_fake_program", "--check", temporary_file]
+        if diff:
+            args.append("--diff")
+
         ret_code = main._main(
-            argv=["my_fake_program", "--check", temporary_file],
+            argv=args,
             standard_out=stdout,
             standard_error=stderr,
             standard_in=None,
@@ -193,18 +214,30 @@ Print my path and return error code
 '''
         ],
     )
-    def test_check_mode_incorrect_docstring(self, temporary_file, contents):
+    @pytest.mark.parametrize(
+        "diff", [True, False], ids=["show-diff", "no-diff"]
+    )
+    def test_check_mode_incorrect_docstring(
+        self, temporary_file, contents, diff
+    ):
         """"""
         stdout = io.StringIO()
         stderr = io.StringIO()
+        args = ["my_fake_program", "--check", temporary_file]
+        if diff:
+            args.append("--diff")
+
         ret_code = main._main(
-            argv=["my_fake_program", "--check", temporary_file],
+            argv=args,
             standard_out=stdout,
             standard_error=stderr,
             standard_in=None,
         )
         assert ret_code == 3  # FormatResult.check_failed
-        assert stdout.getvalue() == ""
+        if diff:
+            assert "Print my path" in stdout.getvalue()
+        else:
+            assert stdout.getvalue() == ""
         assert stderr.getvalue().strip() == temporary_file