Skip to content

Commit ec96941

Browse files
committed
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.
1 parent 66dde14 commit ec96941

File tree

2 files changed

+232
-0
lines changed

2 files changed

+232
-0
lines changed

mypy/config_parser.py

+19
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,9 @@ def split_and_match_files_list(paths: Sequence[str]) -> list[str]:
109109
expanded_paths = []
110110

111111
for path in paths:
112+
if not path:
113+
continue
114+
112115
path = expand_path(path.strip())
113116
globbed_files = fileglob.glob(path, recursive=True)
114117
if globbed_files:
@@ -318,6 +321,22 @@ def parse_config_file(
318321
print(f"{file_read}: No [mypy] section in config file", file=stderr)
319322
else:
320323
section = parser["mypy"]
324+
325+
if "files" in section:
326+
raw_files = section["files"].strip()
327+
files_split = [file.strip() for file in raw_files.split(",")]
328+
329+
# Remove trailing empty entry if present
330+
if files_split and files_split[-1] == "":
331+
files_split.pop()
332+
333+
# Raise an error if there are any remaining empty strings
334+
if "" in files_split:
335+
raise ValueError("Invalid config: Empty filenames are not allowed except for trailing commas.")
336+
337+
options.files = files_split
338+
339+
321340
prefix = f"{file_read}: [mypy]: "
322341
updates, report_dirs = parse_section(
323342
prefix, options, set_strict_flags, section, config_types, stderr

mypy/test/testconfigparser.py

+213
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
import os
2+
import tempfile
3+
from unittest import TestCase, main
4+
from mypy.options import Options
5+
from mypy.config_parser import parse_config_file
6+
7+
class TestConfigParser(TestCase):
8+
def test_parse_config_file_with_single_file(self):
9+
"""A single file should be correctly parsed."""
10+
with tempfile.TemporaryDirectory() as tmpdirname:
11+
config_path = os.path.join(tmpdirname, "test_config.ini")
12+
13+
with open(config_path, "w") as f:
14+
f.write(
15+
"""
16+
[mypy]
17+
files = file1.py
18+
"""
19+
)
20+
21+
options = Options()
22+
23+
parse_config_file(
24+
options,
25+
lambda: None,
26+
config_path,
27+
stdout=None,
28+
stderr=None,
29+
)
30+
31+
self.assertEqual(options.files, ["file1.py"])
32+
33+
def test_parse_config_file_with_no_spaces(self):
34+
"""Files listed without spaces should be correctly parsed."""
35+
with tempfile.TemporaryDirectory() as tmpdirname:
36+
config_path = os.path.join(tmpdirname, "test_config.ini")
37+
38+
with open(config_path, "w") as f:
39+
f.write(
40+
"""
41+
[mypy]
42+
files =file1.py,file2.py,file3.py
43+
"""
44+
)
45+
46+
options = Options()
47+
48+
parse_config_file(
49+
options,
50+
lambda: None,
51+
config_path,
52+
stdout=None,
53+
stderr=None,
54+
)
55+
56+
self.assertEqual(options.files, ["file1.py", "file2.py", "file3.py"])
57+
58+
def test_parse_config_file_with_extra_spaces(self):
59+
"""Files with extra spaces should be correctly parsed."""
60+
with tempfile.TemporaryDirectory() as tmpdirname:
61+
config_path = os.path.join(tmpdirname, "test_config.ini")
62+
63+
with open(config_path, "w") as f:
64+
f.write(
65+
"""
66+
[mypy]
67+
files = file1.py , file2.py , file3.py
68+
"""
69+
)
70+
71+
options = Options()
72+
73+
parse_config_file(
74+
options,
75+
lambda: None,
76+
config_path,
77+
stdout=None,
78+
stderr=None,
79+
)
80+
81+
self.assertEqual(options.files, ["file1.py", "file2.py", "file3.py"])
82+
83+
def test_parse_config_file_with_empty_files_key(self):
84+
"""An empty files key should result in an empty list."""
85+
with tempfile.TemporaryDirectory() as tmpdirname:
86+
config_path = os.path.join(tmpdirname, "test_config.ini")
87+
88+
with open(config_path, "w") as f:
89+
f.write(
90+
"""
91+
[mypy]
92+
files =
93+
"""
94+
)
95+
96+
options = Options()
97+
98+
parse_config_file(
99+
options,
100+
lambda: None,
101+
config_path,
102+
stdout=None,
103+
stderr=None,
104+
)
105+
106+
self.assertEqual(options.files, [])
107+
108+
def test_parse_config_file_with_only_comma(self):
109+
"""A files key with only a comma should raise an error."""
110+
with tempfile.TemporaryDirectory() as tmpdirname:
111+
config_path = os.path.join(tmpdirname, "test_config.ini")
112+
113+
with open(config_path, "w") as f:
114+
f.write(
115+
"""
116+
[mypy]
117+
files = ,
118+
"""
119+
)
120+
121+
options = Options()
122+
123+
with self.assertRaises(ValueError) as cm:
124+
parse_config_file(
125+
options,
126+
lambda: None,
127+
config_path,
128+
stdout=None,
129+
stderr=None,
130+
)
131+
132+
self.assertIn("Invalid config", str(cm.exception))
133+
134+
def test_parse_config_file_with_only_whitespace(self):
135+
"""A files key with only whitespace should result in an empty list."""
136+
with tempfile.TemporaryDirectory() as tmpdirname:
137+
config_path = os.path.join(tmpdirname, "test_config.ini")
138+
139+
with open(config_path, "w") as f:
140+
f.write(
141+
"""
142+
[mypy]
143+
files =
144+
"""
145+
)
146+
147+
options = Options()
148+
149+
parse_config_file(
150+
options,
151+
lambda: None,
152+
config_path,
153+
stdout=None,
154+
stderr=None,
155+
)
156+
157+
self.assertEqual(options.files, [])
158+
159+
def test_parse_config_file_with_mixed_valid_and_invalid_entries(self):
160+
"""Mix of valid and invalid filenames should raise an error."""
161+
with tempfile.TemporaryDirectory() as tmpdirname:
162+
config_path = os.path.join(tmpdirname, "test_config.ini")
163+
164+
with open(config_path, "w") as f:
165+
f.write(
166+
"""
167+
[mypy]
168+
files = file1.py, , , file2.py
169+
"""
170+
)
171+
172+
options = Options()
173+
174+
with self.assertRaises(ValueError) as cm:
175+
parse_config_file(
176+
options,
177+
lambda: None,
178+
config_path,
179+
stdout=None,
180+
stderr=None,
181+
)
182+
183+
self.assertIn("Invalid config", str(cm.exception))
184+
185+
def test_parse_config_file_with_newlines_between_files(self):
186+
"""Newlines between file entries should be correctly handled."""
187+
with tempfile.TemporaryDirectory() as tmpdirname:
188+
config_path = os.path.join(tmpdirname, "test_config.ini")
189+
190+
with open(config_path, "w") as f:
191+
f.write(
192+
"""
193+
[mypy]
194+
files = file1.py,
195+
file2.py,
196+
file3.py
197+
"""
198+
)
199+
200+
options = Options()
201+
202+
parse_config_file(
203+
options,
204+
lambda: None,
205+
config_path,
206+
stdout=None,
207+
stderr=None,
208+
)
209+
210+
self.assertEqual(options.files, ["file1.py", "file2.py", "file3.py"])
211+
212+
if __name__ == "__main__":
213+
main()

0 commit comments

Comments
 (0)