1
1
import os
2
2
from typing import Any
3
+ from typing import Dict
3
4
from typing import Iterable
4
5
from typing import List
5
6
from typing import Optional
@@ -22,44 +23,84 @@ def exists(path, ignore=OSError):
22
23
return False
23
24
24
25
25
- def getcfg (args , config = None ):
26
+ def _parse_ini_config (path : py .path .local ) -> py .iniconfig .IniConfig :
27
+ """Parses the given generic '.ini' file using legacy IniConfig parser, returning
28
+ the parsed object.
29
+
30
+ Raises UsageError if the file cannot be parsed.
31
+ """
32
+ try :
33
+ return py .iniconfig .IniConfig (path )
34
+ except py .iniconfig .ParseError as exc :
35
+ raise UsageError (str (exc ))
36
+
37
+
38
+ def _get_ini_config_from_pytest_ini (path : py .path .local ) -> Optional [Dict [str , Any ]]:
39
+ """Parses and validates a 'pytest.ini' file.
40
+
41
+ If present, 'pytest.ini' files are always considered the source of truth of pytest
42
+ configuration, even if empty or without a "[pytest]" section.
43
+ """
44
+ iniconfig = _parse_ini_config (path )
45
+ if "pytest" in iniconfig :
46
+ return dict (iniconfig ["pytest" ].items ())
47
+ else :
48
+ return {}
49
+
50
+
51
+ def _get_ini_config_from_tox_ini (path : py .path .local ) -> Optional [Dict [str , Any ]]:
52
+ """Parses and validates a 'tox.ini' file for pytest configuration.
53
+
54
+ 'tox.ini' files are only considered for pytest configuration if they contain a "[pytest]"
55
+ section.
56
+ """
57
+ iniconfig = _parse_ini_config (path )
58
+ if "pytest" in iniconfig :
59
+ return dict (iniconfig ["pytest" ].items ())
60
+ else :
61
+ return None
62
+
63
+
64
+ def _get_ini_config_from_setup_cfg (path : py .path .local ) -> Optional [Dict [str , Any ]]:
65
+ """Parses and validates a 'setup.cfg' file for pytest configuration.
66
+
67
+ 'setup.cfg' files are only considered for pytest configuration if they contain a "[tool:pytest]"
68
+ section.
69
+
70
+ If a setup.cfg contains a "[pytest]" section, we raise a failure to indicate users that
71
+ plain "[pytest]" sections in setup.cfg files is no longer supported (#3086).
72
+ """
73
+ iniconfig = _parse_ini_config (path )
74
+
75
+ if "tool:pytest" in iniconfig .sections :
76
+ return dict (iniconfig ["tool:pytest" ].items ())
77
+ elif "pytest" in iniconfig .sections :
78
+ fail (CFG_PYTEST_SECTION .format (filename = "setup.cfg" ), pytrace = False )
79
+ return None
80
+
81
+
82
+ def getcfg (args ):
26
83
"""
27
84
Search the list of arguments for a valid ini-file for pytest,
28
85
and return a tuple of (rootdir, inifile, cfg-dict).
29
-
30
- note: config is optional and used only to issue warnings explicitly (#2891).
31
86
"""
32
- inibasenames = ["pytest.ini" , "tox.ini" , "setup.cfg" ]
87
+ ini_names_and_handlers = [
88
+ ("pytest.ini" , _get_ini_config_from_pytest_ini ),
89
+ ("tox.ini" , _get_ini_config_from_tox_ini ),
90
+ ("setup.cfg" , _get_ini_config_from_setup_cfg ),
91
+ ]
33
92
args = [x for x in args if not str (x ).startswith ("-" )]
34
93
if not args :
35
94
args = [py .path .local ()]
36
95
for arg in args :
37
96
arg = py .path .local (arg )
38
97
for base in arg .parts (reverse = True ):
39
- for inibasename in inibasenames :
98
+ for inibasename , handler in ini_names_and_handlers :
40
99
p = base .join (inibasename )
41
- if exists (p ):
42
- try :
43
- iniconfig = py .iniconfig .IniConfig (p )
44
- except py .iniconfig .ParseError as exc :
45
- raise UsageError (str (exc ))
46
-
47
- if (
48
- inibasename == "setup.cfg"
49
- and "tool:pytest" in iniconfig .sections
50
- ):
51
- return base , p , iniconfig ["tool:pytest" ]
52
- elif "pytest" in iniconfig .sections :
53
- if inibasename == "setup.cfg" and config is not None :
54
-
55
- fail (
56
- CFG_PYTEST_SECTION .format (filename = inibasename ),
57
- pytrace = False ,
58
- )
59
- return base , p , iniconfig ["pytest" ]
60
- elif inibasename == "pytest.ini" :
61
- # allowed to be empty
62
- return base , p , {}
100
+ if p .isfile ():
101
+ ini_config = handler (p )
102
+ if ini_config is not None :
103
+ return base , p , ini_config
63
104
return None , None , None
64
105
65
106
@@ -138,15 +179,15 @@ def determine_setup(
138
179
rootdir = get_common_ancestor (dirs )
139
180
else :
140
181
ancestor = get_common_ancestor (dirs )
141
- rootdir , inifile , inicfg = getcfg ([ancestor ], config = config )
182
+ rootdir , inifile , inicfg = getcfg ([ancestor ])
142
183
if rootdir is None and rootdir_cmd_arg is None :
143
184
for possible_rootdir in ancestor .parts (reverse = True ):
144
185
if possible_rootdir .join ("setup.py" ).exists ():
145
186
rootdir = possible_rootdir
146
187
break
147
188
else :
148
189
if dirs != [ancestor ]:
149
- rootdir , inifile , inicfg = getcfg (dirs , config = config )
190
+ rootdir , inifile , inicfg = getcfg (dirs )
150
191
if rootdir is None :
151
192
if config is not None :
152
193
cwd = config .invocation_dir
0 commit comments