Repository: google/yapf
Pull request #1054 (2023-02-07 17:11:46)

Add --print-modified to print modified files
Prints modified files when in place mode is used. This helps debugging formatting issues and makes developers more aware of the changes made by yapf.

bwendling: Please add a note in the `CHANGELOG` and some tests please.
eltoder: @bwendling PTAL
hartwork: > @bwendling PTAL

It's "Please Take A Look" in case you'd have to look it up like I did…
eltoder: @hartwork Good point. We can use f-strings now. Done.

Patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 6cc5028..23e9f8e 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -32,3 +32,11 @@ jobs:
         pip install pytest
         pip install pytest-cov
         pytest
+    - name: Lint with isort
+      run: |
+        pip install isort
+        isort . --check --diff
+    - name: Lint with yapf
+      run: |
+        pip install .
+        yapf . -vv --diff --recursive
diff --git a/.isort.cfg b/.isort.cfg
new file mode 100644
index 0000000..69720b9
--- /dev/null
+++ b/.isort.cfg
@@ -0,0 +1,5 @@
+[settings]
+force_single_line=true
+known_yapftests=yapftests
+
+sections=FUTURE,STDLIB,THIRDPARTY,FIRSTPARTY,LOCALFOLDER,YAPFTESTS
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 3bd65c2..049a1a2 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -2,6 +2,11 @@
 # to enable run `pip install pre-commit && pre-commit install`
 
 repos:
+  - repo: https://github.com/pycqa/isort
+    rev: 5.11.5
+    hooks:
+      - id: isort
+        name: isort (python)
   - repo: local
     hooks:
       - id: yapf
@@ -15,15 +20,12 @@ repos:
     hooks:
       - id: trailing-whitespace
       - id: check-docstring-first
-      - id: check-json
       - id: check-added-large-files
       - id: check-yaml
       - id: debug-statements
-      - id: requirements-txt-fixer
       - id: check-merge-conflict
       - id: double-quote-string-fixer
       - id: end-of-file-fixer
-      - id: sort-simple-yaml
   - repo: meta
     hooks:
       - id: check-hooks-apply
diff --git a/CHANGELOG b/CHANGELOG
index a76e19a..ec69b5c 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -5,6 +5,8 @@
 ## [0.40.0] UNRELEASED
 ### Added
 - Support for Python 3.11
+- Add the `--print-modified` flag to print out file names of modified files when
+  running in in-place mode.
 ### Removed
 - Support for Python <3.7
 
diff --git a/README.rst b/README.rst
index c4c56fb..8b6e781 100644
--- a/README.rst
+++ b/README.rst
@@ -116,6 +116,7 @@ Options::
       --no-local-style      don't search for local style definition
       -p, --parallel        run YAPF in parallel when formatting multiple
                             files. Requires concurrent.futures in Python 2.X
+      -m, --print-modified  print out file names of modified files
       -vv, --verbose        print out file names while processing
 
 
diff --git a/setup.py b/setup.py
index 0412f67..106d2b1 100644
--- a/setup.py
+++ b/setup.py
@@ -17,7 +17,9 @@ import codecs
 import sys
 import unittest
 
-from setuptools import find_packages, setup, Command
+from setuptools import Command
+from setuptools import find_packages
+from setuptools import setup
 
 import yapf
 
diff --git a/third_party/yapf_diff/yapf_diff.py b/third_party/yapf_diff/yapf_diff.py
index adc6fe4..a22abd9 100644
--- a/third_party/yapf_diff/yapf_diff.py
+++ b/third_party/yapf_diff/yapf_diff.py
@@ -30,7 +30,6 @@ import difflib
 import re
 import subprocess
 import sys
-
 from io import StringIO
 
 
diff --git a/yapf/__init__.py b/yapf/__init__.py
index 39b15c9..ce183e9 100644
--- a/yapf/__init__.py
+++ b/yapf/__init__.py
@@ -145,7 +145,8 @@ def main(argv):
       verify=args.verify,
       parallel=args.parallel,
       quiet=args.quiet,
-      verbose=args.verbose)
+      verbose=args.verbose,
+      print_modified=args.print_modified)
   return 1 if changed and (args.diff or args.quiet) else 0
 
 
@@ -175,7 +176,8 @@ def FormatFiles(filenames,
                 verify=False,
                 parallel=False,
                 quiet=False,
-                verbose=False):
+                verbose=False,
+                print_modified=False):
   """Format a list of files.
 
   Arguments:
@@ -194,27 +196,29 @@ def FormatFiles(filenames,
     parallel: (bool) True if should format multiple files in parallel.
     quiet: (bool) True if should output nothing.
     verbose: (bool) True if should print out filenames while processing.
+    print_modified: (bool) True if should print out filenames of modified files.
 
   Returns:
     True if the source code changed in any of the files being formatted.
   """
   changed = False
   if parallel:
-    import multiprocessing  # pylint: disable=g-import-not-at-top
     import concurrent.futures  # pylint: disable=g-import-not-at-top
+    import multiprocessing  # pylint: disable=g-import-not-at-top
     workers = min(multiprocessing.cpu_count(), len(filenames))
     with concurrent.futures.ProcessPoolExecutor(workers) as executor:
       future_formats = [
           executor.submit(_FormatFile, filename, lines, style_config,
                           no_local_style, in_place, print_diff, verify, quiet,
-                          verbose) for filename in filenames
+                          verbose, print_modified) for filename in filenames
       ]
       for future in concurrent.futures.as_completed(future_formats):
         changed |= future.result()
   else:
     for filename in filenames:
       changed |= _FormatFile(filename, lines, style_config, no_local_style,
-                             in_place, print_diff, verify, quiet, verbose)
+                             in_place, print_diff, verify, quiet, verbose,
+                             print_modified)
   return changed
 
 
@@ -226,10 +230,11 @@ def _FormatFile(filename,
                 print_diff=False,
                 verify=False,
                 quiet=False,
-                verbose=False):
+                verbose=False,
+                print_modified=False):
   """Format an individual file."""
   if verbose and not quiet:
-    print('Reformatting %s' % filename)
+    print(f'Reformatting {filename}')
 
   if style_config is None and not no_local_style:
     style_config = file_resources.GetDefaultStyleForDir(
@@ -252,6 +257,8 @@ def _FormatFile(filename,
   if not in_place and not quiet and reformatted_code:
     file_resources.WriteReformattedCode(filename, reformatted_code, encoding,
                                         in_place)
+  if print_modified and has_change and in_place and not quiet:
+    print(f'Formatted {filename}')
   return has_change
 
 
@@ -358,6 +365,11 @@ def _BuildParser():
       action='store_true',
       help=('run YAPF in parallel when formatting multiple files. Requires '
             'concurrent.futures in Python 2.X'))
+  parser.add_argument(
+      '-m',
+      '--print-modified',
+      action='store_true',
+      help='print out file names of modified files')
   parser.add_argument(
       '-vv',
       '--verbose',
diff --git a/yapf/pytree/blank_line_calculator.py b/yapf/pytree/blank_line_calculator.py
index d47f2b5..7bccb09 100644
--- a/yapf/pytree/blank_line_calculator.py
+++ b/yapf/pytree/blank_line_calculator.py
@@ -174,5 +174,4 @@ def _StartsInZerothColumn(node):
 
 
 def _AsyncFunction(node):
-  return (node.prev_sibling and
-          node.prev_sibling.type == grammar_token.ASYNC)
+  return (node.prev_sibling and node.prev_sibling.type == grammar_token.ASYNC)
diff --git a/yapf/pytree/pytree_utils.py b/yapf/pytree/pytree_utils.py
index 43ef876..8b3c406 100644
--- a/yapf/pytree/pytree_utils.py
+++ b/yapf/pytree/pytree_utils.py
@@ -26,7 +26,6 @@ the lib2to3 library.
 
 import ast
 import os
-
 from lib2to3 import pygram
 from lib2to3 import pytree
 from lib2to3.pgen2 import driver
diff --git a/yapf/pytree/pytree_visitor.py b/yapf/pytree/pytree_visitor.py
index 314431e..4f6c605 100644
--- a/yapf/pytree/pytree_visitor.py
+++ b/yapf/pytree/pytree_visitor.py
@@ -25,7 +25,6 @@ a pytree into a stream.
 """
 
 import sys
-
 from lib2to3 import pytree
 
 from yapf.pytree import pytree_utils
diff --git a/yapf/pytree/split_penalty.py b/yapf/pytree/split_penalty.py
index 81163e4..ccb3880 100644
--- a/yapf/pytree/split_penalty.py
+++ b/yapf/pytree/split_penalty.py
@@ -14,7 +14,6 @@
 """Computation of split penalties before/between tokens."""
 
 import re
-
 from lib2to3 import pytree
 from lib2to3.pgen2 import token as grammar_token
 
diff --git a/yapf/yapflib/format_token.py b/yapf/yapflib/format_token.py
index 41afd45..c572391 100644
--- a/yapf/yapflib/format_token.py
+++ b/yapf/yapflib/format_token.py
@@ -15,7 +15,6 @@
 
 import keyword
 import re
-
 from functools import lru_cache
 from lib2to3.pgen2 import token
 
diff --git a/yapf/yapflib/logical_line.py b/yapf/yapflib/logical_line.py
index 1528fc4..780db8a 100644
--- a/yapf/yapflib/logical_line.py
+++ b/yapf/yapflib/logical_line.py
@@ -19,14 +19,14 @@ line if there were no line length restrictions. It's then used by the parser to
 perform the wrapping required to comply with the style guide.
 """
 
+from lib2to3.fixer_util import syms as python_symbols
+
 from yapf.pytree import pytree_utils
 from yapf.pytree import split_penalty
 from yapf.yapflib import format_token
 from yapf.yapflib import style
 from yapf.yapflib import subtypes
 
-from lib2to3.fixer_util import syms as python_symbols
-
 
 class LogicalLine(object):
   """Represents a single logical line in the output.
diff --git a/yapf/yapflib/reformatter.py b/yapf/yapflib/reformatter.py
index f8c4bc4..b7c883e 100644
--- a/yapf/yapflib/reformatter.py
+++ b/yapf/yapflib/reformatter.py
@@ -22,7 +22,6 @@ can be merged together are. The best formatting is returned as a string.
 import collections
 import heapq
 import re
-
 from lib2to3 import pytree
 from lib2to3.pgen2 import token
 
diff --git a/yapf/yapflib/yapf_api.py b/yapf/yapflib/yapf_api.py
index 19e86f0..be5467a 100644
--- a/yapf/yapflib/yapf_api.py
+++ b/yapf/yapflib/yapf_api.py
@@ -38,12 +38,11 @@ import re
 import sys
 
 from yapf.pyparser import pyparser
-
-from yapf.pytree import pytree_unwrapper
-from yapf.pytree import pytree_utils
 from yapf.pytree import blank_line_calculator
 from yapf.pytree import comment_splicer
 from yapf.pytree import continuation_splicer
+from yapf.pytree import pytree_unwrapper
+from yapf.pytree import pytree_utils
 from yapf.pytree import split_penalty
 from yapf.pytree import subtype_assigner
 from yapf.yapflib import errors


Test patch:
diff --git a/yapftests/comment_splicer_test.py b/yapftests/comment_splicer_test.py
index 36c25b2..64cb708 100644
--- a/yapftests/comment_splicer_test.py
+++ b/yapftests/comment_splicer_test.py
@@ -16,8 +16,8 @@
 import textwrap
 import unittest
 
-from yapf.pytree import pytree_utils
 from yapf.pytree import comment_splicer
+from yapf.pytree import pytree_utils
 
 
 class CommentSplicerTest(unittest.TestCase):
diff --git a/yapftests/format_decision_state_test.py b/yapftests/format_decision_state_test.py
index 63961f3..3bff578 100644
--- a/yapftests/format_decision_state_test.py
+++ b/yapftests/format_decision_state_test.py
@@ -17,7 +17,6 @@ import textwrap
 import unittest
 
 from yapf.pytree import pytree_utils
-
 from yapf.yapflib import format_decision_state
 from yapf.yapflib import logical_line
 from yapf.yapflib import style
diff --git a/yapftests/format_token_test.py b/yapftests/format_token_test.py
index 18ebe48..5703e5a 100644
--- a/yapftests/format_token_test.py
+++ b/yapftests/format_token_test.py
@@ -14,7 +14,6 @@
 """Tests for yapf.format_token."""
 
 import unittest
-
 from lib2to3 import pytree
 from lib2to3.pgen2 import token
 
diff --git a/yapftests/logical_line_test.py b/yapftests/logical_line_test.py
index d18262a..9188b32 100644
--- a/yapftests/logical_line_test.py
+++ b/yapftests/logical_line_test.py
@@ -15,7 +15,6 @@
 
 import textwrap
 import unittest
-
 from lib2to3 import pytree
 from lib2to3.pgen2 import token
 
diff --git a/yapftests/main_test.py b/yapftests/main_test.py
index 7241a02..b5d9b92 100644
--- a/yapftests/main_test.py
+++ b/yapftests/main_test.py
@@ -14,10 +14,11 @@
 # limitations under the License.
 """Tests for yapf.__init__.main."""
 
-from contextlib import contextmanager
-from io import StringIO
 import sys
 import unittest
+from contextlib import contextmanager
+from io import StringIO
+
 import yapf
 
 from yapftests import yapf_test_helper
diff --git a/yapftests/pytree_utils_test.py b/yapftests/pytree_utils_test.py
index c55f668..b228fcb 100644
--- a/yapftests/pytree_utils_test.py
+++ b/yapftests/pytree_utils_test.py
@@ -14,7 +14,6 @@
 """Tests for yapf.pytree_utils."""
 
 import unittest
-
 from lib2to3 import pygram
 from lib2to3 import pytree
 from lib2to3.pgen2 import token
diff --git a/yapftests/split_penalty_test.py b/yapftests/split_penalty_test.py
index f7474a3..c08cf5a 100644
--- a/yapftests/split_penalty_test.py
+++ b/yapftests/split_penalty_test.py
@@ -16,7 +16,6 @@
 import sys
 import textwrap
 import unittest
-
 from lib2to3 import pytree
 
 from yapf.pytree import pytree_utils
diff --git a/yapftests/yapf_test.py b/yapftests/yapf_test.py
index 33cc143..7c241bd 100644
--- a/yapftests/yapf_test.py
+++ b/yapftests/yapf_test.py
@@ -23,7 +23,6 @@ import sys
 import tempfile
 import textwrap
 import unittest
-
 from io import StringIO
 from lib2to3.pgen2 import tokenize
 
@@ -285,8 +284,9 @@ class FormatFileTest(unittest.TestCase):
     with self.assertRaises(IOError) as context:
       yapf_api.FormatFile('not_a_file.py')
 
-    self.assertEqual(str(context.exception),
-                     "[Errno 2] No such file or directory: 'not_a_file.py'")
+    self.assertEqual(
+        str(context.exception),
+        "[Errno 2] No such file or directory: 'not_a_file.py'")
 
   def testCommentsUnformatted(self):
     code = textwrap.dedent("""\
@@ -505,6 +505,16 @@ class CommandLineTest(unittest.TestCase):
         reformatted_code = fd.read()
     self.assertEqual(reformatted_code, expected_formatted_code)
 
+  def testPrintModified(self):
+    for unformatted_code, has_change in [('1==2', True), ('1 == 2', False)]:
+      with utils.TempFileContents(
+          self.test_tmpdir, unformatted_code, suffix='.py') as filepath:
+        output = subprocess.check_output(
+            YAPF_BINARY + ['--in-place', '--print-modified', filepath],
+            text=True)
+        check = self.assertIn if has_change else self.assertNotIn
+        check(f'Formatted {filepath}', output)
+
   def testReadFromStdin(self):
     unformatted_code = textwrap.dedent("""\
         def foo():