diff options
author | Ashley Whetter <ashley@awhetter.co.uk> | 2018-07-29 18:00:28 -0700 |
---|---|---|
committer | Ashley Whetter <ashley@awhetter.co.uk> | 2019-02-09 13:40:54 -0800 |
commit | 10fd567ebb87fa55364ac224c265e602e6c97573 (patch) | |
tree | 1d1853ae82bcf2b4730ee0941c33f28a0b6e9035 | |
parent | f19b9ff7c5a2363cbcaff1da350128c1435e092f (diff) | |
download | pylint-git-10fd567ebb87fa55364ac224c265e602e6c97573.tar.gz |
Can have per directory configurations!
Closes #2357
Closes #1889
-rw-r--r-- | pylint/config.py | 73 | ||||
-rw-r--r-- | pylint/lint.py | 36 |
2 files changed, 49 insertions, 60 deletions
diff --git a/pylint/config.py b/pylint/config.py index a043520dc..f718bfe98 100644 --- a/pylint/config.py +++ b/pylint/config.py @@ -284,8 +284,9 @@ class Configuration(object): result.add_options(self._option_definitions.items()) for option in self._option_definitions: - value = getattr(self, option) - setattr(result, option, value) + if hasattr(self, option): + value = getattr(self, option) + setattr(result, option, value) return result @@ -297,25 +298,24 @@ class Configuration(object): def __iadd__(self, other): self._option_definitions.update(other._option_definitions) - for option in other._option_definitions: + copied = set() + for option, definition in self._option_definitions.items(): option = option.replace("-", "_") - value = getattr(other, option) - setattr(self, option, value) + dest = definition.get("dest", option) + if dest not in copied and hasattr(other, dest): + value = getattr(other, dest) + if definition.get("action") == "append": + value = getattr(self, dest, []) + value + setattr(self, dest, value) + copied.add(dest) return self class ConfigurationStore(object): - def __init__(self, global_config): - """A class to store configuration objects for many paths. - - :param global_config: The global configuration object. - :type global_config: Configuration - """ - self.global_config = global_config - + def __init__(self): + """A class to store configuration objects for many paths.""" self._store = {} - self._cache = {} def add_config_for(self, path, config): """Add a configuration object to the store. @@ -329,54 +329,20 @@ class ConfigurationStore(object): path = os.path.abspath(path) self._store[path] = config - self._cache = {} - - def _get_parent_configs(self, path): - """Get the config objects for all parent directories. - - :param path: The absolute path to get the parent configs for. - :type path: str - - :returns: The config objects for all parent directories. - :rtype: generator(Configuration) - """ - for cfg_dir in utils.walk_up(path): - if cfg_dir in self._cache: - yield self._cache[cfg_dir] - break - elif cfg_dir in self._store: - yield self._store[cfg_dir] def get_config_for(self, path): """Get the configuration object for a file or directory. - This will merge the global config with all of the config objects from - the root directory to the given path. :param path: The file or directory to the get configuration object for. :type path: str :returns: The configuration object for the given file or directory. - :rtype: Configuration + :rtype: Configuration or None """ - # TODO: Until we turn on local pylintrc searching, - # this is always going to be the global config - return self.global_config - path = os.path.expanduser(path) path = os.path.abspath(path) - config = self._cache.get(path) - - if not config: - config = self.global_config.copy() - - parent_configs = self._get_parent_configs(path) - for parent_config in reversed(list(parent_configs)): - config += parent_config - - self._cache["path"] = config - - return config + return self._store.get(path) def __getitem__(self, path): return self.get_config_for(path) @@ -424,8 +390,6 @@ class CLIParser(ConfigParser): # Only set the arguments that are specified. argument_default=argparse.SUPPRESS, ) - # TODO: Let this be definable elsewhere - self._parser.add_argument("module_or_package", nargs=argparse.REMAINDER) def add_option_definitions(self, option_definitions): self._option_definitions.update(option_definitions) @@ -466,7 +430,10 @@ class CLIParser(ConfigParser): if "short" in definition: args.append("-{0}".format(definition["short"])) - args.append("--{0}".format(option)) + if definition.get("positional", False): + args.append(option) + else: + args.append("--{0}".format(option)) copy_keys = ("action", "default", "dest", "help", "metavar", "level", "version") kwargs = {k: definition[k] for k in copy_keys if k in definition} diff --git a/pylint/lint.py b/pylint/lint.py index 80430e3a0..a24daa922 100644 --- a/pylint/lint.py +++ b/pylint/lint.py @@ -60,6 +60,7 @@ """ from __future__ import print_function +import argparse import collections import contextlib import operator @@ -1091,6 +1092,7 @@ class Runner(object): class CLIRunner(Runner): option_definitions = ( + ("module_or_package", {"positional": True, "nargs": argparse.REMAINDER}), ( "rcfile", { @@ -1233,6 +1235,7 @@ group are mutually exclusive.", self._plugin_registry = PluginRegistry(self._global_config) self._loaded_plugins = set() self._reporter = None + self._config_parsers = [] def run(self, args): # Phase 1: Preprocessing @@ -1298,6 +1301,8 @@ group are mutually exclusive.", file_parser.add_option_definitions(options) self._plugin_registry.register_options = register_options + self._file_parser = file_parser + self._cli_parser = parser checkers.initialize(self._plugin_registry) @@ -1317,7 +1322,9 @@ group are mutually exclusive.", if rcfile: file_parser.parse(rcfile, self._global_config) # Fully load CLI into global config - parser.parse(args, self._global_config) + self._cli_config = config.Configuration() + parser.parse(args, self._cli_config) + self._global_config += self._cli_config if self._global_config.generate_rcfile: file_parser.write() @@ -1442,10 +1449,7 @@ group are mutually exclusive.", # XXX code below needs refactoring to be more reporter agnostic self._reporter.on_close(self._plugin_registry.stats, previous_stats) if self._global_config.reports: - # TODO: The Runner should make reports, not the registry. - sect = self._plugin_registry.make_reports( - self._plugin_registry.stats, previous_stats - ) + sect = self.make_reports(self._plugin_registry.stats, previous_stats) else: sect = report_nodes.Section() @@ -1602,6 +1606,22 @@ group are mutually exclusive.", self._global_config.black_list, self._global_config.black_list_re, ) + config_store = config.ConfigurationStore() + for module_desc in expanded_files: + directory = os.path.dirname(module_desc.path) + + local_config = config_store[directory] + if not local_config: + local_file = config.find_nearby_pylintrc(directory) + if not local_file: + local_config = self._global_config + else: + local_config = self._global_config.copy() + self._file_parser.parse(local_file, local_config) + local_config += self._cli_config + + config_store[directory] = local_config + for module_desc in expanded_files: modname = module_desc.name filepath = module_desc.path @@ -1610,9 +1630,11 @@ group are mutually exclusive.", ): continue - linter = PyLinter(self._global_config) + directory = os.path.dirname(module_desc.path) + local_config = config_store.get_config_for(directory) + linter = PyLinter(local_config) linter.msgs_store = self._plugin_registry.msgs_store - for msg_ids, enable in self._global_config.msg_toggles: + for msg_ids, enable in local_config.msg_toggles: for msg_id in msg_ids: if enable: linter.enable(msg_id, scope="directory") |