15
15
else :
16
16
import tomli as tomllib
17
17
18
- from collections .abc import Iterable , Mapping , MutableMapping , Sequence
18
+ from collections .abc import Mapping , MutableMapping , Sequence
19
19
from typing import Any , Callable , Final , TextIO , Union
20
20
from typing_extensions import TypeAlias as _TypeAlias
21
21
@@ -217,6 +217,72 @@ def split_commas(value: str) -> list[str]:
217
217
)
218
218
219
219
220
+ def _parse_individual_file (
221
+ config_file : str , stderr : TextIO | None = None
222
+ ) -> tuple [MutableMapping [str , Any ], dict [str , _INI_PARSER_CALLABLE ], str ] | None :
223
+
224
+ if not os .path .exists (config_file ):
225
+ return None
226
+
227
+ parser : MutableMapping [str , Any ]
228
+ try :
229
+ if is_toml (config_file ):
230
+ with open (config_file , "rb" ) as f :
231
+ toml_data = tomllib .load (f )
232
+ # Filter down to just mypy relevant toml keys
233
+ toml_data = toml_data .get ("tool" , {})
234
+ if "mypy" not in toml_data :
235
+ return None
236
+ toml_data = {"mypy" : toml_data ["mypy" ]}
237
+ parser = destructure_overrides (toml_data )
238
+ config_types = toml_config_types
239
+ else :
240
+ parser = configparser .RawConfigParser ()
241
+ parser .read (config_file )
242
+ config_types = ini_config_types
243
+
244
+ except (tomllib .TOMLDecodeError , configparser .Error , ConfigTOMLValueError ) as err :
245
+ print (f"{ config_file } : { err } " , file = stderr )
246
+ return None
247
+
248
+ if os .path .basename (config_file ) in defaults .SHARED_CONFIG_NAMES and "mypy" not in parser :
249
+ return None
250
+
251
+ return parser , config_types , config_file
252
+
253
+
254
+ def _find_config_file (
255
+ stderr : TextIO | None = None ,
256
+ ) -> tuple [MutableMapping [str , Any ], dict [str , _INI_PARSER_CALLABLE ], str ] | None :
257
+
258
+ current_dir = os .path .abspath (os .getcwd ())
259
+
260
+ while True :
261
+ for name in defaults .CONFIG_NAMES + defaults .SHARED_CONFIG_NAMES :
262
+ config_file = os .path .relpath (os .path .join (current_dir , name ))
263
+ ret = _parse_individual_file (config_file , stderr )
264
+ if ret is None :
265
+ continue
266
+ return ret
267
+
268
+ if any (
269
+ os .path .exists (os .path .join (current_dir , cvs_root )) for cvs_root in (".git" , ".hg" )
270
+ ):
271
+ break
272
+ parent_dir = os .path .dirname (current_dir )
273
+ if parent_dir == current_dir :
274
+ break
275
+ current_dir = parent_dir
276
+
277
+ for config_file in defaults .USER_CONFIG_FILES :
278
+ ret = _parse_individual_file (config_file , stderr )
279
+ if ret is None :
280
+ continue
281
+ return ret
282
+
283
+ return None
284
+
285
+
220
286
def parse_config_file (
221
287
options : Options ,
222
288
set_strict_flags : Callable [[], None ],
@@ -233,47 +299,20 @@ def parse_config_file(
233
299
stdout = stdout or sys .stdout
234
300
stderr = stderr or sys .stderr
235
301
236
- if filename is not None :
237
- config_files : tuple [str , ...] = (filename ,)
238
- else :
239
- config_files_iter : Iterable [str ] = map (os .path .expanduser , defaults .CONFIG_FILES )
240
- config_files = tuple (config_files_iter )
241
-
242
- config_parser = configparser .RawConfigParser ()
243
-
244
- for config_file in config_files :
245
- if not os .path .exists (config_file ):
246
- continue
247
- try :
248
- if is_toml (config_file ):
249
- with open (config_file , "rb" ) as f :
250
- toml_data = tomllib .load (f )
251
- # Filter down to just mypy relevant toml keys
252
- toml_data = toml_data .get ("tool" , {})
253
- if "mypy" not in toml_data :
254
- continue
255
- toml_data = {"mypy" : toml_data ["mypy" ]}
256
- parser : MutableMapping [str , Any ] = destructure_overrides (toml_data )
257
- config_types = toml_config_types
258
- else :
259
- config_parser .read (config_file )
260
- parser = config_parser
261
- config_types = ini_config_types
262
- except (tomllib .TOMLDecodeError , configparser .Error , ConfigTOMLValueError ) as err :
263
- print (f"{ config_file } : { err } " , file = stderr )
264
- else :
265
- if config_file in defaults .SHARED_CONFIG_FILES and "mypy" not in parser :
266
- continue
267
- file_read = config_file
268
- options .config_file = file_read
269
- break
270
- else :
302
+ ret = (
303
+ _parse_individual_file (filename , stderr )
304
+ if filename is not None
305
+ else _find_config_file (stderr )
306
+ )
307
+ if ret is None :
271
308
return
309
+ parser , config_types , file_read = ret
272
310
273
- os .environ ["MYPY_CONFIG_FILE_DIR" ] = os .path .dirname (os .path .abspath (config_file ))
311
+ options .config_file = file_read
312
+ os .environ ["MYPY_CONFIG_FILE_DIR" ] = os .path .dirname (os .path .abspath (file_read ))
274
313
275
314
if "mypy" not in parser :
276
- if filename or file_read not in defaults .SHARED_CONFIG_FILES :
315
+ if filename or os . path . basename ( file_read ) not in defaults .SHARED_CONFIG_NAMES :
277
316
print (f"{ file_read } : No [mypy] section in config file" , file = stderr )
278
317
else :
279
318
section = parser ["mypy" ]
0 commit comments