Skip to content

Commit 5249654

Browse files
authored
Test and fix trailing commas in many multiline string options in pyproject.toml (#18624)
Refs #18621 Closes #18623 With a lot more tests.
1 parent dd6df2e commit 5249654

File tree

4 files changed

+141
-2
lines changed

4 files changed

+141
-2
lines changed

mypy/config_parser.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,10 @@ def parse_version(v: str | float) -> tuple[int, int]:
5555
def try_split(v: str | Sequence[str], split_regex: str = "[,]") -> list[str]:
5656
"""Split and trim a str or list of str into a list of str"""
5757
if isinstance(v, str):
58-
return [p.strip() for p in re.split(split_regex, v)]
59-
58+
items = [p.strip() for p in re.split(split_regex, v)]
59+
if items and items[-1] == "":
60+
items.pop(-1)
61+
return items
6062
return [p.strip() for p in v]
6163

6264

test-data/unit/check-custom-plugin.test

+12
Original file line numberDiff line numberDiff line change
@@ -1098,3 +1098,15 @@ reveal_type(1) # N: Revealed type is "Literal[1]?"
10981098
[file mypy.ini]
10991099
\[mypy]
11001100
plugins=<ROOT>/test-data/unit/plugins/custom_errorcode.py
1101+
1102+
1103+
[case testPyprojectPluginsTrailingComma]
1104+
# flags: --config-file tmp/pyproject.toml
1105+
[file pyproject.toml]
1106+
# This test checks that trailing commas in string-based `plugins` are allowed.
1107+
\[tool.mypy]
1108+
plugins = """
1109+
<ROOT>/test-data/unit/plugins/function_sig_hook.py,
1110+
<ROOT>/test-data/unit/plugins/method_in_decorator.py,
1111+
"""
1112+
[out]

test-data/unit/cmdline.pyproject.test

+93
Original file line numberDiff line numberDiff line change
@@ -133,3 +133,96 @@ Neither is this!
133133
description = "Factory ⸻ A code generator 🏭"
134134
\[tool.mypy]
135135
[file x.py]
136+
137+
[case testPyprojectFilesTrailingComma]
138+
# cmd: mypy
139+
[file pyproject.toml]
140+
\[tool.mypy]
141+
# We combine multiple tests in a single one here, because these tests are slow.
142+
files = """
143+
a.py,
144+
b.py,
145+
"""
146+
always_true = """
147+
FLAG_A1,
148+
FLAG_B1,
149+
"""
150+
always_false = """
151+
FLAG_A2,
152+
FLAG_B2,
153+
"""
154+
[file a.py]
155+
x: str = 'x' # ok'
156+
157+
# --always-true
158+
FLAG_A1 = False
159+
FLAG_B1 = False
160+
if not FLAG_A1: # unreachable
161+
x: int = 'x'
162+
if not FLAG_B1: # unreachable
163+
y: int = 'y'
164+
165+
# --always-false
166+
FLAG_A2 = True
167+
FLAG_B2 = True
168+
if FLAG_A2: # unreachable
169+
x: int = 'x'
170+
if FLAG_B2: # unreachable
171+
y: int = 'y'
172+
[file b.py]
173+
y: int = 'y' # E: Incompatible types in assignment (expression has type "str", variable has type "int")
174+
[file c.py]
175+
# This should not trigger any errors, because it is not included:
176+
z: int = 'z'
177+
[out]
178+
179+
[case testPyprojectModulesTrailingComma]
180+
# cmd: mypy
181+
[file pyproject.toml]
182+
\[tool.mypy]
183+
# We combine multiple tests in a single one here, because these tests are slow.
184+
modules = """
185+
a,
186+
b,
187+
"""
188+
disable_error_code = """
189+
operator,
190+
import,
191+
"""
192+
enable_error_code = """
193+
redundant-expr,
194+
ignore-without-code,
195+
"""
196+
[file a.py]
197+
x: str = 'x' # ok
198+
199+
# --enable-error-code
200+
a: int = 'a' # type: ignore
201+
202+
# --disable-error-code
203+
'a' + 1
204+
[file b.py]
205+
y: int = 'y'
206+
[file c.py]
207+
# This should not trigger any errors, because it is not included:
208+
z: int = 'z'
209+
[out]
210+
b.py:1: error: Incompatible types in assignment (expression has type "str", variable has type "int")
211+
a.py:4: error: "type: ignore" comment without error code (consider "type: ignore[assignment]" instead)
212+
213+
[case testPyprojectPackagesTrailingComma]
214+
# cmd: mypy
215+
[file pyproject.toml]
216+
\[tool.mypy]
217+
packages = """
218+
a,
219+
b,
220+
"""
221+
[file a/__init__.py]
222+
x: str = 'x' # ok
223+
[file b/__init__.py]
224+
y: int = 'y' # E: Incompatible types in assignment (expression has type "str", variable has type "int")
225+
[file c/__init__.py]
226+
# This should not trigger any errors, because it is not included:
227+
z: int = 'z'
228+
[out]

test-data/unit/cmdline.test

+32
Original file line numberDiff line numberDiff line change
@@ -1342,6 +1342,38 @@ always_true =
13421342
MY_VAR,
13431343
[out]
13441344

1345+
[case testCmdlineCfgModulesTrailingComma]
1346+
# cmd: mypy
1347+
[file mypy.ini]
1348+
\[mypy]
1349+
modules =
1350+
a,
1351+
b,
1352+
[file a.py]
1353+
x: str = 'x' # ok
1354+
[file b.py]
1355+
y: int = 'y' # E: Incompatible types in assignment (expression has type "str", variable has type "int")
1356+
[file c.py]
1357+
# This should not trigger any errors, because it is not included:
1358+
z: int = 'z'
1359+
[out]
1360+
1361+
[case testCmdlineCfgPackagesTrailingComma]
1362+
# cmd: mypy
1363+
[file mypy.ini]
1364+
\[mypy]
1365+
packages =
1366+
a,
1367+
b,
1368+
[file a/__init__.py]
1369+
x: str = 'x' # ok
1370+
[file b/__init__.py]
1371+
y: int = 'y' # E: Incompatible types in assignment (expression has type "str", variable has type "int")
1372+
[file c/__init__.py]
1373+
# This should not trigger any errors, because it is not included:
1374+
z: int = 'z'
1375+
[out]
1376+
13451377
[case testTypeVarTupleUnpackEnabled]
13461378
# cmd: mypy --enable-incomplete-feature=TypeVarTuple --enable-incomplete-feature=Unpack a.py
13471379
[file a.py]

0 commit comments

Comments
 (0)