@@ -34,9 +34,10 @@ class ConfigurationParser(object):
3434
3535 Run Configurations:
3636 ------------------
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.
4041
4142 Check Configurations:
4243 --------------------
@@ -175,6 +176,48 @@ def _get_ignore_decorators(config):
175176
176177 # --------------------------- Private Methods -----------------------------
177178
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+
178221 def _get_config (self , node ):
179222 """Get and cache the run configuration for `node`.
180223
@@ -204,35 +247,19 @@ def _get_config(self, node):
204247 * Set the `--add-select` and `--add-ignore` CLI configurations.
205248
206249 """
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 )
224253 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 )
236263
237264 # Make the CLI always win
238265 final_config = {}
@@ -244,8 +271,19 @@ def _get_config(self, node):
244271 config = CheckConfiguration (** final_config )
245272
246273 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 )
249287
250288 def _read_configuration_file (self , path ):
251289 """Try to read and parse `path` as a configuration file.
@@ -365,7 +403,7 @@ def _get_section_name(cls, parser):
365403 def _get_config_file_in_folder (cls , path ):
366404 """Look for a configuration file in `path`.
367405
368- If exists return it's full path, otherwise None.
406+ If exists return its full path, otherwise None.
369407
370408 """
371409 if os .path .isfile (path ):
@@ -511,6 +549,8 @@ def _create_option_parser(cls):
511549 help = 'print status information' )
512550 option ('--count' , action = 'store_true' , default = False ,
513551 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' )
514554
515555 # Error check options
516556 option ('--select' , metavar = '<codes>' , default = None ,
@@ -571,4 +611,4 @@ class IllegalConfiguration(Exception):
571611# General configurations for pydocstyle run.
572612RunConfiguration = namedtuple ('RunConfiguration' ,
573613 ('explain' , 'source' , 'debug' ,
574- 'verbose' , 'count' ))
614+ 'verbose' , 'count' , 'config' ))
0 commit comments