Repository: google/yapf
Pull request #741 (2019-07-19 03:19:35)

Look for style file / setup config at root, add tests
  - We encountered a weird phenonemon when building containers with setup.cfg placed at the container root, and the yapf command fired on a subdirectory of root.
  - Appears as though the traversal to root breaks one step too early in the current implementation. Tiny fix; adds a test.
  - Also adds a few tests to test discovery of styles via setup.cfg
  - Thanks to sendittovignesh@gmail.com and matthewwillian@gmail.com for the discovery & diagnosis of the issue!


Patch:
diff --git a/CHANGELOG b/CHANGELOG
index a655589..4385ee3 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -10,6 +10,8 @@
 ### Fixed
 - Format subscript lists so that splits are essentially free after a comma.
 - Don't add a space between a string and its subscript.
+- Extend discovery of '.style.yapf' & 'setup.cfg' files to search the root
+  directory as well.
 
 ## [0.28.0] 2019-07-11
 ### Added
diff --git a/yapf/yapflib/file_resources.py b/yapf/yapflib/file_resources.py
index 3b1b3f0..59c3df5 100644
--- a/yapf/yapflib/file_resources.py
+++ b/yapf/yapflib/file_resources.py
@@ -97,10 +97,10 @@ def GetDefaultStyleForDir(dirname, default_style=style.DEFAULT_STYLE):
         if config.has_section('yapf'):
           return config_file
 
-    dirname = os.path.dirname(dirname)
     if (not dirname or not os.path.basename(dirname) or
         dirname == os.path.abspath(os.path.sep)):
       break
+    dirname = os.path.dirname(dirname)
 
   global_file = os.path.expanduser(style.GLOBAL_STYLE)
   if os.path.exists(global_file):


Test patch:
diff --git a/yapftests/file_resources_test.py b/yapftests/file_resources_test.py
index eaf4b8b..07e3134 100644
--- a/yapftests/file_resources_test.py
+++ b/yapftests/file_resources_test.py
@@ -36,6 +36,16 @@ def _restore_working_dir():
     os.chdir(curdir)
 
 
+@contextlib.contextmanager
+def _exists_mocked_in_module(module, mock_implementation):
+  unmocked_exists = getattr(module, 'exists')
+  setattr(module, 'exists', mock_implementation)
+  try:
+    yield
+  finally:
+    setattr(module, 'exists', unmocked_exists)
+
+
 class GetExcludePatternsForDir(unittest.TestCase):
 
   def setUp(self):  # pylint: disable=g-missing-super-call
@@ -92,6 +102,43 @@ class GetDefaultStyleForDirTest(unittest.TestCase):
     self.assertEqual(style_file,
                      file_resources.GetDefaultStyleForDir(test_filename))
 
+  def test_setup_config(self):
+    # An empty setup.cfg file should not be used
+    setup_config = os.path.join(self.test_tmpdir, 'setup.cfg')
+    open(setup_config, 'w').close()
+
+    test_dir = os.path.join(self.test_tmpdir, 'dir1')
+    style_name = file_resources.GetDefaultStyleForDir(test_dir)
+    self.assertEqual(style_name, 'pep8')
+
+    # One with a '[yapf]' section should be used
+    with open(setup_config, 'w') as f:
+      f.write('[yapf]\n')
+    self.assertEqual(setup_config,
+                     file_resources.GetDefaultStyleForDir(test_dir))
+
+  def test_local_style_at_root(self):
+    # Test behavior of files located on the root, and under root.
+    rootdir = os.path.abspath(os.path.sep)
+    test_dir_at_root = os.path.join(rootdir, 'dir1')
+    test_dir_under_root = os.path.join(rootdir, 'dir1', 'dir2')
+
+    # Fake placing only a style file at the root by mocking `os.path.exists`.
+    style_file = os.path.join(rootdir, '.style.yapf')
+
+    def mock_exists_implementation(path):
+      return path == style_file
+
+    with _exists_mocked_in_module(file_resources.os.path,
+                                  mock_exists_implementation):
+      # Both files should find the style file at the root.
+      default_style_at_root = file_resources.GetDefaultStyleForDir(
+          test_dir_at_root)
+      self.assertEqual(style_file, default_style_at_root)
+      default_style_under_root = file_resources.GetDefaultStyleForDir(
+          test_dir_under_root)
+      self.assertEqual(style_file, default_style_under_root)
+
 
 def _touch_files(filenames):
   for name in filenames: