1515else :
1616 import tomli as tomllib
1717
18- from collections .abc import Iterable , Mapping , MutableMapping , Sequence
18+ from collections .abc import Mapping , MutableMapping , Sequence
1919from typing import Any , Callable , Final , TextIO , Union
2020from typing_extensions import TypeAlias as _TypeAlias
2121
@@ -217,6 +217,72 @@ def split_commas(value: str) -> list[str]:
217217)
218218
219219
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+
220286def parse_config_file (
221287 options : Options ,
222288 set_strict_flags : Callable [[], None ],
@@ -233,47 +299,20 @@ def parse_config_file(
233299 stdout = stdout or sys .stdout
234300 stderr = stderr or sys .stderr
235301
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 :
271308 return
309+ parser , config_types , file_read = ret
272310
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 ))
274313
275314 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 :
277316 print (f"{ file_read } : No [mypy] section in config file" , file = stderr )
278317 else :
279318 section = parser ["mypy" ]
0 commit comments