Repository: google/yapf
Pull request #1055 (2023-02-07 17:56:12)

Small fixes and optimizations
* Default LineEnding() to LF when no new lines in the file and when tied with another line ending. Currently defaults to CRLF on Python 3.6+ and random on older versions.

* Preserve line ending type when formatting a file that is a single new line. Currently always converts to LF. Also simplify logic.

* Don't compute diffs when print_diff == False.


Patch:
diff --git a/yapf/yapflib/file_resources.py b/yapf/yapflib/file_resources.py
index 6809ca9..da44b39 100644
--- a/yapf/yapflib/file_resources.py
+++ b/yapf/yapflib/file_resources.py
@@ -195,7 +195,7 @@ def LineEnding(lines):
       endings[CR] += 1
     elif line.endswith(LF):
       endings[LF] += 1
-  return (sorted(endings, key=endings.get, reverse=True) or [LF])[0]
+  return sorted((LF, CRLF, CR), key=endings.get, reverse=True)[0]
 
 
 def _FindPythonFiles(filenames, recursive, exclude):
diff --git a/yapf/yapflib/yapf_api.py b/yapf/yapflib/yapf_api.py
index ff87c7b..13b02a4 100644
--- a/yapf/yapflib/yapf_api.py
+++ b/yapf/yapflib/yapf_api.py
@@ -99,11 +99,10 @@ def FormatFile(filename,
       lines=lines,
       print_diff=print_diff,
       verify=verify)
-  if reformatted_source.rstrip('\n'):
-    lines = reformatted_source.rstrip('\n').split('\n')
-    reformatted_source = newline.join(iter(lines)) + newline
+  if newline != '\n':
+    reformatted_source = reformatted_source.replace('\n', newline)
   if in_place:
-    if original_source and original_source != reformatted_source:
+    if changed:
       file_resources.WriteReformattedCode(filename, reformatted_source,
                                           encoding, in_place)
     return None, encoding, changed
@@ -221,10 +220,9 @@ def FormatCode(unformatted_source,
   if unformatted_source == reformatted_source:
     return '' if print_diff else reformatted_source, False
 
-  code_diff = _GetUnifiedDiff(
-      unformatted_source, reformatted_source, filename=filename)
-
   if print_diff:
+    code_diff = _GetUnifiedDiff(
+        unformatted_source, reformatted_source, filename=filename)
     return code_diff, code_diff.strip() != ''  # pylint: disable=g-explicit-bool-comparison # noqa
 
   return reformatted_source, True


Test patch:
diff --git a/yapftests/file_resources_test.py b/yapftests/file_resources_test.py
index f54f393..bd21d08 100644
--- a/yapftests/file_resources_test.py
+++ b/yapftests/file_resources_test.py
@@ -560,6 +560,26 @@ class LineEndingTest(unittest.TestCase):
     actual = file_resources.LineEnding(lines)
     self.assertEqual(actual, '\n')
 
+  def test_line_ending_empty(self):
+    lines = []
+    actual = file_resources.LineEnding(lines)
+    self.assertEqual(actual, '\n')
+
+  def test_line_ending_no_newline(self):
+    lines = ['spam']
+    actual = file_resources.LineEnding(lines)
+    self.assertEqual(actual, '\n')
+
+  def test_line_ending_tie(self):
+    lines = [
+        'spam\n',
+        'spam\n',
+        'spam\r\n',
+        'spam\r\n',
+    ]
+    actual = file_resources.LineEnding(lines)
+    self.assertEqual(actual, '\n')
+
 
 if __name__ == '__main__':
   unittest.main()
diff --git a/yapftests/yapf_test.py b/yapftests/yapf_test.py
index 6749bf0..007d757 100644
--- a/yapftests/yapf_test.py
+++ b/yapftests/yapf_test.py
@@ -493,6 +493,31 @@ class CommandLineTest(unittest.TestCase):
         reformatted_code = fd.read()
     self.assertEqual(reformatted_code, expected_formatted_code)
 
+  def testInPlaceReformattingWindowsNewLine(self):
+    unformatted_code = u'\r\n\r\n'
+    expected_formatted_code = u'\r\n'
+    with utils.TempFileContents(
+        self.test_tmpdir, unformatted_code, suffix='.py') as filepath:
+      p = subprocess.Popen(YAPF_BINARY + ['--in-place', filepath])
+      p.wait()
+      with io.open(filepath, mode='r', encoding='utf-8', newline='') as fd:
+        reformatted_code = fd.read()
+    self.assertEqual(reformatted_code, expected_formatted_code)
+
+  def testInPlaceReformattingNoNewLine(self):
+    unformatted_code = textwrap.dedent(u"def foo(): x = 37")
+    expected_formatted_code = textwrap.dedent("""\
+        def foo():
+            x = 37
+        """)
+    with utils.TempFileContents(
+        self.test_tmpdir, unformatted_code, suffix='.py') as filepath:
+      p = subprocess.Popen(YAPF_BINARY + ['--in-place', filepath])
+      p.wait()
+      with io.open(filepath, mode='r', newline='') as fd:
+        reformatted_code = fd.read()
+    self.assertEqual(reformatted_code, expected_formatted_code)
+
   def testInPlaceReformattingEmpty(self):
     unformatted_code = u''
     expected_formatted_code = u''