From ec969415a455e6b81c738180078eae6fe84615be Mon Sep 17 00:00:00 2001
From: SOUBHIK KUMAR MITRA <soubhikmitra98@gmail.com>
Date: Wed, 15 Jan 2025 15:02:13 +0530
Subject: [PATCH 1/3] Fix config parsing to allow trailing commas but prevent
 empty entries

- Modified `split_and_match_files_list` to handle trailing commas correctly.
- Ensured empty string entries result in an error to match CLI behavior.
- Updated unit tests to cover various edge cases.
- Improved test coverage for config parsing with different file list formats.
---
 mypy/config_parser.py         |  19 +++
 mypy/test/testconfigparser.py | 213 ++++++++++++++++++++++++++++++++++
 2 files changed, 232 insertions(+)
 create mode 100644 mypy/test/testconfigparser.py

diff --git a/mypy/config_parser.py b/mypy/config_parser.py
index 0e033471d2e9..0cb6fea2a167 100644
--- a/mypy/config_parser.py
+++ b/mypy/config_parser.py
@@ -109,6 +109,9 @@ def split_and_match_files_list(paths: Sequence[str]) -> list[str]:
     expanded_paths = []
 
     for path in paths:
+        if not path:
+            continue
+
         path = expand_path(path.strip())
         globbed_files = fileglob.glob(path, recursive=True)
         if globbed_files:
@@ -318,6 +321,22 @@ def parse_config_file(
             print(f"{file_read}: No [mypy] section in config file", file=stderr)
     else:
         section = parser["mypy"]
+
+        if "files" in section:
+            raw_files = section["files"].strip()
+            files_split = [file.strip() for file in raw_files.split(",")]
+
+            # Remove trailing empty entry if present
+            if files_split and files_split[-1] == "":
+                files_split.pop()
+
+            # Raise an error if there are any remaining empty strings
+            if "" in files_split:
+                raise ValueError("Invalid config: Empty filenames are not allowed except for trailing commas.")
+
+            options.files = files_split
+
+
         prefix = f"{file_read}: [mypy]: "
         updates, report_dirs = parse_section(
             prefix, options, set_strict_flags, section, config_types, stderr
diff --git a/mypy/test/testconfigparser.py b/mypy/test/testconfigparser.py
new file mode 100644
index 000000000000..30155bca7047
--- /dev/null
+++ b/mypy/test/testconfigparser.py
@@ -0,0 +1,213 @@
+import os
+import tempfile
+from unittest import TestCase, main
+from mypy.options import Options
+from mypy.config_parser import parse_config_file
+
+class TestConfigParser(TestCase):
+    def test_parse_config_file_with_single_file(self):
+        """A single file should be correctly parsed."""
+        with tempfile.TemporaryDirectory() as tmpdirname:
+            config_path = os.path.join(tmpdirname, "test_config.ini")
+
+            with open(config_path, "w") as f:
+                f.write(
+                    """
+                    [mypy]
+                    files = file1.py
+                    """
+                )
+
+            options = Options()
+
+            parse_config_file(
+                options,
+                lambda: None,
+                config_path,
+                stdout=None,
+                stderr=None,
+            )
+
+            self.assertEqual(options.files, ["file1.py"])
+
+    def test_parse_config_file_with_no_spaces(self):
+        """Files listed without spaces should be correctly parsed."""
+        with tempfile.TemporaryDirectory() as tmpdirname:
+            config_path = os.path.join(tmpdirname, "test_config.ini")
+
+            with open(config_path, "w") as f:
+                f.write(
+                    """
+                    [mypy]
+                    files =file1.py,file2.py,file3.py
+                    """
+                )
+
+            options = Options()
+
+            parse_config_file(
+                options,
+                lambda: None,
+                config_path,
+                stdout=None,
+                stderr=None,
+            )
+
+            self.assertEqual(options.files, ["file1.py", "file2.py", "file3.py"])
+
+    def test_parse_config_file_with_extra_spaces(self):
+        """Files with extra spaces should be correctly parsed."""
+        with tempfile.TemporaryDirectory() as tmpdirname:
+            config_path = os.path.join(tmpdirname, "test_config.ini")
+
+            with open(config_path, "w") as f:
+                f.write(
+                    """
+                    [mypy]
+                    files =  file1.py ,   file2.py  ,   file3.py   
+                    """
+                )
+
+            options = Options()
+
+            parse_config_file(
+                options,
+                lambda: None,
+                config_path,
+                stdout=None,
+                stderr=None,
+            )
+
+            self.assertEqual(options.files, ["file1.py", "file2.py", "file3.py"])
+
+    def test_parse_config_file_with_empty_files_key(self):
+        """An empty files key should result in an empty list."""
+        with tempfile.TemporaryDirectory() as tmpdirname:
+            config_path = os.path.join(tmpdirname, "test_config.ini")
+
+            with open(config_path, "w") as f:
+                f.write(
+                    """
+                    [mypy]
+                    files = 
+                    """
+                )
+
+            options = Options()
+
+            parse_config_file(
+                options,
+                lambda: None,
+                config_path,
+                stdout=None,
+                stderr=None,
+            )
+
+            self.assertEqual(options.files, [])
+
+    def test_parse_config_file_with_only_comma(self):
+        """A files key with only a comma should raise an error."""
+        with tempfile.TemporaryDirectory() as tmpdirname:
+            config_path = os.path.join(tmpdirname, "test_config.ini")
+
+            with open(config_path, "w") as f:
+                f.write(
+                    """
+                    [mypy]
+                    files = ,
+                    """
+                )
+
+            options = Options()
+
+            with self.assertRaises(ValueError) as cm:
+                parse_config_file(
+                    options,
+                    lambda: None,
+                    config_path,
+                    stdout=None,
+                    stderr=None,
+                )
+
+            self.assertIn("Invalid config", str(cm.exception))
+
+    def test_parse_config_file_with_only_whitespace(self):
+        """A files key with only whitespace should result in an empty list."""
+        with tempfile.TemporaryDirectory() as tmpdirname:
+            config_path = os.path.join(tmpdirname, "test_config.ini")
+
+            with open(config_path, "w") as f:
+                f.write(
+                    """
+                    [mypy]
+                    files =    
+                    """
+                )
+
+            options = Options()
+
+            parse_config_file(
+                options,
+                lambda: None,
+                config_path,
+                stdout=None,
+                stderr=None,
+            )
+
+            self.assertEqual(options.files, [])
+
+    def test_parse_config_file_with_mixed_valid_and_invalid_entries(self):
+        """Mix of valid and invalid filenames should raise an error."""
+        with tempfile.TemporaryDirectory() as tmpdirname:
+            config_path = os.path.join(tmpdirname, "test_config.ini")
+
+            with open(config_path, "w") as f:
+                f.write(
+                    """
+                    [mypy]
+                    files = file1.py, , , file2.py
+                    """
+                )
+
+            options = Options()
+
+            with self.assertRaises(ValueError) as cm:
+                parse_config_file(
+                    options,
+                    lambda: None,
+                    config_path,
+                    stdout=None,
+                    stderr=None,
+                )
+
+            self.assertIn("Invalid config", str(cm.exception))
+
+    def test_parse_config_file_with_newlines_between_files(self):
+        """Newlines between file entries should be correctly handled."""
+        with tempfile.TemporaryDirectory() as tmpdirname:
+            config_path = os.path.join(tmpdirname, "test_config.ini")
+
+            with open(config_path, "w") as f:
+                f.write(
+                    """
+                    [mypy]
+                    files = file1.py,
+                            file2.py,
+                            file3.py
+                    """
+                )
+
+            options = Options()
+
+            parse_config_file(
+                options,
+                lambda: None,
+                config_path,
+                stdout=None,
+                stderr=None,
+            )
+
+            self.assertEqual(options.files, ["file1.py", "file2.py", "file3.py"])
+
+if __name__ == "__main__":
+    main()

From 082075271b648842f02c04e4b2ba5a754866fe34 Mon Sep 17 00:00:00 2001
From: SOUBHIK KUMAR MITRA <soubhikmitra98@gmail.com>
Date: Tue, 25 Feb 2025 02:13:49 +0530
Subject: [PATCH 2/3] fix the type check error in the pipeline checks

---
 mypy/test/testconfigparser.py | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/mypy/test/testconfigparser.py b/mypy/test/testconfigparser.py
index 30155bca7047..fe1ac03bec37 100644
--- a/mypy/test/testconfigparser.py
+++ b/mypy/test/testconfigparser.py
@@ -5,7 +5,7 @@
 from mypy.config_parser import parse_config_file
 
 class TestConfigParser(TestCase):
-    def test_parse_config_file_with_single_file(self):
+    def test_parse_config_file_with_single_file(self) -> None:
         """A single file should be correctly parsed."""
         with tempfile.TemporaryDirectory() as tmpdirname:
             config_path = os.path.join(tmpdirname, "test_config.ini")
@@ -30,7 +30,7 @@ def test_parse_config_file_with_single_file(self):
 
             self.assertEqual(options.files, ["file1.py"])
 
-    def test_parse_config_file_with_no_spaces(self):
+    def test_parse_config_file_with_no_spaces(self) -> None:
         """Files listed without spaces should be correctly parsed."""
         with tempfile.TemporaryDirectory() as tmpdirname:
             config_path = os.path.join(tmpdirname, "test_config.ini")
@@ -55,7 +55,7 @@ def test_parse_config_file_with_no_spaces(self):
 
             self.assertEqual(options.files, ["file1.py", "file2.py", "file3.py"])
 
-    def test_parse_config_file_with_extra_spaces(self):
+    def test_parse_config_file_with_extra_spaces(self) -> None:
         """Files with extra spaces should be correctly parsed."""
         with tempfile.TemporaryDirectory() as tmpdirname:
             config_path = os.path.join(tmpdirname, "test_config.ini")
@@ -80,7 +80,7 @@ def test_parse_config_file_with_extra_spaces(self):
 
             self.assertEqual(options.files, ["file1.py", "file2.py", "file3.py"])
 
-    def test_parse_config_file_with_empty_files_key(self):
+    def test_parse_config_file_with_empty_files_key(self) -> None:
         """An empty files key should result in an empty list."""
         with tempfile.TemporaryDirectory() as tmpdirname:
             config_path = os.path.join(tmpdirname, "test_config.ini")
@@ -105,7 +105,7 @@ def test_parse_config_file_with_empty_files_key(self):
 
             self.assertEqual(options.files, [])
 
-    def test_parse_config_file_with_only_comma(self):
+    def test_parse_config_file_with_only_comma(self) -> None:
         """A files key with only a comma should raise an error."""
         with tempfile.TemporaryDirectory() as tmpdirname:
             config_path = os.path.join(tmpdirname, "test_config.ini")
@@ -131,7 +131,7 @@ def test_parse_config_file_with_only_comma(self):
 
             self.assertIn("Invalid config", str(cm.exception))
 
-    def test_parse_config_file_with_only_whitespace(self):
+    def test_parse_config_file_with_only_whitespace(self) -> None:
         """A files key with only whitespace should result in an empty list."""
         with tempfile.TemporaryDirectory() as tmpdirname:
             config_path = os.path.join(tmpdirname, "test_config.ini")
@@ -156,7 +156,7 @@ def test_parse_config_file_with_only_whitespace(self):
 
             self.assertEqual(options.files, [])
 
-    def test_parse_config_file_with_mixed_valid_and_invalid_entries(self):
+    def test_parse_config_file_with_mixed_valid_and_invalid_entries(self) -> None:
         """Mix of valid and invalid filenames should raise an error."""
         with tempfile.TemporaryDirectory() as tmpdirname:
             config_path = os.path.join(tmpdirname, "test_config.ini")
@@ -182,7 +182,7 @@ def test_parse_config_file_with_mixed_valid_and_invalid_entries(self):
 
             self.assertIn("Invalid config", str(cm.exception))
 
-    def test_parse_config_file_with_newlines_between_files(self):
+    def test_parse_config_file_with_newlines_between_files(self) -> None:
         """Newlines between file entries should be correctly handled."""
         with tempfile.TemporaryDirectory() as tmpdirname:
             config_path = os.path.join(tmpdirname, "test_config.ini")

From e80c2fae7b8d55aac3bf68e552d02230be05e14a Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
 <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Mon, 24 Feb 2025 20:46:33 +0000
Subject: [PATCH 3/3] [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci
---
 mypy/config_parser.py         |  5 ++-
 mypy/test/testconfigparser.py | 75 +++++++----------------------------
 2 files changed, 18 insertions(+), 62 deletions(-)

diff --git a/mypy/config_parser.py b/mypy/config_parser.py
index 0cb6fea2a167..35720b8580d0 100644
--- a/mypy/config_parser.py
+++ b/mypy/config_parser.py
@@ -332,11 +332,12 @@ def parse_config_file(
 
             # Raise an error if there are any remaining empty strings
             if "" in files_split:
-                raise ValueError("Invalid config: Empty filenames are not allowed except for trailing commas.")
+                raise ValueError(
+                    "Invalid config: Empty filenames are not allowed except for trailing commas."
+                )
 
             options.files = files_split
 
-
         prefix = f"{file_read}: [mypy]: "
         updates, report_dirs = parse_section(
             prefix, options, set_strict_flags, section, config_types, stderr
diff --git a/mypy/test/testconfigparser.py b/mypy/test/testconfigparser.py
index fe1ac03bec37..ebb45fee377d 100644
--- a/mypy/test/testconfigparser.py
+++ b/mypy/test/testconfigparser.py
@@ -1,8 +1,10 @@
 import os
 import tempfile
 from unittest import TestCase, main
-from mypy.options import Options
+
 from mypy.config_parser import parse_config_file
+from mypy.options import Options
+
 
 class TestConfigParser(TestCase):
     def test_parse_config_file_with_single_file(self) -> None:
@@ -20,13 +22,7 @@ def test_parse_config_file_with_single_file(self) -> None:
 
             options = Options()
 
-            parse_config_file(
-                options,
-                lambda: None,
-                config_path,
-                stdout=None,
-                stderr=None,
-            )
+            parse_config_file(options, lambda: None, config_path, stdout=None, stderr=None)
 
             self.assertEqual(options.files, ["file1.py"])
 
@@ -45,13 +41,7 @@ def test_parse_config_file_with_no_spaces(self) -> None:
 
             options = Options()
 
-            parse_config_file(
-                options,
-                lambda: None,
-                config_path,
-                stdout=None,
-                stderr=None,
-            )
+            parse_config_file(options, lambda: None, config_path, stdout=None, stderr=None)
 
             self.assertEqual(options.files, ["file1.py", "file2.py", "file3.py"])
 
@@ -64,19 +54,13 @@ def test_parse_config_file_with_extra_spaces(self) -> None:
                 f.write(
                     """
                     [mypy]
-                    files =  file1.py ,   file2.py  ,   file3.py   
+                    files =  file1.py ,   file2.py  ,   file3.py
                     """
                 )
 
             options = Options()
 
-            parse_config_file(
-                options,
-                lambda: None,
-                config_path,
-                stdout=None,
-                stderr=None,
-            )
+            parse_config_file(options, lambda: None, config_path, stdout=None, stderr=None)
 
             self.assertEqual(options.files, ["file1.py", "file2.py", "file3.py"])
 
@@ -89,19 +73,13 @@ def test_parse_config_file_with_empty_files_key(self) -> None:
                 f.write(
                     """
                     [mypy]
-                    files = 
+                    files =
                     """
                 )
 
             options = Options()
 
-            parse_config_file(
-                options,
-                lambda: None,
-                config_path,
-                stdout=None,
-                stderr=None,
-            )
+            parse_config_file(options, lambda: None, config_path, stdout=None, stderr=None)
 
             self.assertEqual(options.files, [])
 
@@ -121,13 +99,7 @@ def test_parse_config_file_with_only_comma(self) -> None:
             options = Options()
 
             with self.assertRaises(ValueError) as cm:
-                parse_config_file(
-                    options,
-                    lambda: None,
-                    config_path,
-                    stdout=None,
-                    stderr=None,
-                )
+                parse_config_file(options, lambda: None, config_path, stdout=None, stderr=None)
 
             self.assertIn("Invalid config", str(cm.exception))
 
@@ -140,19 +112,13 @@ def test_parse_config_file_with_only_whitespace(self) -> None:
                 f.write(
                     """
                     [mypy]
-                    files =    
+                    files =
                     """
                 )
 
             options = Options()
 
-            parse_config_file(
-                options,
-                lambda: None,
-                config_path,
-                stdout=None,
-                stderr=None,
-            )
+            parse_config_file(options, lambda: None, config_path, stdout=None, stderr=None)
 
             self.assertEqual(options.files, [])
 
@@ -172,13 +138,7 @@ def test_parse_config_file_with_mixed_valid_and_invalid_entries(self) -> None:
             options = Options()
 
             with self.assertRaises(ValueError) as cm:
-                parse_config_file(
-                    options,
-                    lambda: None,
-                    config_path,
-                    stdout=None,
-                    stderr=None,
-                )
+                parse_config_file(options, lambda: None, config_path, stdout=None, stderr=None)
 
             self.assertIn("Invalid config", str(cm.exception))
 
@@ -199,15 +159,10 @@ def test_parse_config_file_with_newlines_between_files(self) -> None:
 
             options = Options()
 
-            parse_config_file(
-                options,
-                lambda: None,
-                config_path,
-                stdout=None,
-                stderr=None,
-            )
+            parse_config_file(options, lambda: None, config_path, stdout=None, stderr=None)
 
             self.assertEqual(options.files, ["file1.py", "file2.py", "file3.py"])
 
+
 if __name__ == "__main__":
     main()