@@ -34,9 +34,10 @@ class ConfigurationParser(object):
34
34
35
35
Run Configurations:
36
36
------------------
37
- Responsible for deciding things that are related to the user interface,
38
- e.g. verbosity, debug options, etc.
39
- All run configurations default to `False` and are decided only by CLI.
37
+ Responsible for deciding things that are related to the user interface and
38
+ configuration discovery, e.g. verbosity, debug options, etc.
39
+ All run configurations default to `False` or `None` and are decided only
40
+ by CLI.
40
41
41
42
Check Configurations:
42
43
--------------------
@@ -175,6 +176,48 @@ def _get_ignore_decorators(config):
175
176
176
177
# --------------------------- Private Methods -----------------------------
177
178
179
+ def _get_config_by_discovery (self , node ):
180
+ """Get a configuration for checking `node` by config discovery.
181
+
182
+ Config discovery happens when no explicit config file is specified. The
183
+ file system is searched for config files starting from the directory
184
+ containing the file being checked, and up until the root directory of
185
+ the project.
186
+
187
+ See `_get_config` for further details.
188
+
189
+ """
190
+ path = self ._get_node_dir (node )
191
+
192
+ if path in self ._cache :
193
+ return self ._cache [path ]
194
+
195
+ config_file = self ._get_config_file_in_folder (path )
196
+
197
+ if config_file is None :
198
+ parent_dir , tail = os .path .split (path )
199
+ if tail :
200
+ # No configuration file, simply take the parent's.
201
+ config = self ._get_config (parent_dir )
202
+ else :
203
+ # There's no configuration file and no parent directory.
204
+ # Use the default configuration or the one given in the CLI.
205
+ config = self ._create_check_config (self ._options )
206
+ else :
207
+ # There's a config file! Read it and merge if necessary.
208
+ options , inherit = self ._read_configuration_file (config_file )
209
+
210
+ parent_dir , tail = os .path .split (path )
211
+ if tail and inherit :
212
+ # There is a parent dir and we should try to merge.
213
+ parent_config = self ._get_config (parent_dir )
214
+ config = self ._merge_configuration (parent_config , options )
215
+ else :
216
+ # No need to merge or parent dir does not exist.
217
+ config = self ._create_check_config (options )
218
+
219
+ return config
220
+
178
221
def _get_config (self , node ):
179
222
"""Get and cache the run configuration for `node`.
180
223
@@ -204,35 +247,19 @@ def _get_config(self, node):
204
247
* Set the `--add-select` and `--add-ignore` CLI configurations.
205
248
206
249
"""
207
- path = os .path .abspath (node )
208
- path = path if os .path .isdir (path ) else os .path .dirname (path )
209
-
210
- if path in self ._cache :
211
- return self ._cache [path ]
212
-
213
- config_file = self ._get_config_file_in_folder (path )
214
-
215
- if config_file is None :
216
- parent_dir , tail = os .path .split (path )
217
- if tail :
218
- # No configuration file, simply take the parent's.
219
- config = self ._get_config (parent_dir )
220
- else :
221
- # There's no configuration file and no parent directory.
222
- # Use the default configuration or the one given in the CLI.
223
- config = self ._create_check_config (self ._options )
250
+ if self ._run_conf .config is None :
251
+ log .debug ('No config file specified, discovering.' )
252
+ config = self ._get_config_by_discovery (node )
224
253
else :
225
- # There's a config file! Read it and merge if necessary.
226
- options , inherit = self ._read_configuration_file (config_file )
227
-
228
- parent_dir , tail = os .path .split (path )
229
- if tail and inherit :
230
- # There is a parent dir and we should try to merge.
231
- parent_config = self ._get_config (parent_dir )
232
- config = self ._merge_configuration (parent_config , options )
233
- else :
234
- # No need to merge or parent dir does not exist.
235
- config = self ._create_check_config (options )
254
+ log .debug ('Using config file %r' , self ._run_conf .config )
255
+ if not os .path .exists (self ._run_conf .config ):
256
+ raise IllegalConfiguration ('Configuration file {!r} specified '
257
+ 'via --config was not found.'
258
+ .format (self ._run_conf .config ))
259
+ if None in self ._cache :
260
+ return self ._cache [None ]
261
+ options , _ = self ._read_configuration_file (self ._run_conf .config )
262
+ config = self ._create_check_config (options )
236
263
237
264
# Make the CLI always win
238
265
final_config = {}
@@ -244,8 +271,19 @@ def _get_config(self, node):
244
271
config = CheckConfiguration (** final_config )
245
272
246
273
self ._set_add_options (config .checked_codes , self ._options )
247
- self ._cache [path ] = config
248
- return self ._cache [path ]
274
+
275
+ # Handle caching
276
+ if self ._run_conf .config is not None :
277
+ self ._cache [None ] = config
278
+ else :
279
+ self ._cache [self ._get_node_dir (node )] = config
280
+ return config
281
+
282
+ @staticmethod
283
+ def _get_node_dir (node ):
284
+ """Return the absolute path of the directory of a filesystem node."""
285
+ path = os .path .abspath (node )
286
+ return path if os .path .isdir (path ) else os .path .dirname (path )
249
287
250
288
def _read_configuration_file (self , path ):
251
289
"""Try to read and parse `path` as a configuration file.
@@ -365,7 +403,7 @@ def _get_section_name(cls, parser):
365
403
def _get_config_file_in_folder (cls , path ):
366
404
"""Look for a configuration file in `path`.
367
405
368
- If exists return it's full path, otherwise None.
406
+ If exists return its full path, otherwise None.
369
407
370
408
"""
371
409
if os .path .isfile (path ):
@@ -511,6 +549,8 @@ def _create_option_parser(cls):
511
549
help = 'print status information' )
512
550
option ('--count' , action = 'store_true' , default = False ,
513
551
help = 'print total number of errors to stdout' )
552
+ option ('--config' , metavar = '<path>' , default = None ,
553
+ help = 'use given config file and disable config discovery' )
514
554
515
555
# Error check options
516
556
option ('--select' , metavar = '<codes>' , default = None ,
@@ -571,4 +611,4 @@ class IllegalConfiguration(Exception):
571
611
# General configurations for pydocstyle run.
572
612
RunConfiguration = namedtuple ('RunConfiguration' ,
573
613
('explain' , 'source' , 'debug' ,
574
- 'verbose' , 'count' ))
614
+ 'verbose' , 'count' , 'config' ))
0 commit comments