summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAshley Whetter <ashley@awhetter.co.uk>2018-07-29 18:00:28 -0700
committerAshley Whetter <ashley@awhetter.co.uk>2019-02-09 13:40:54 -0800
commit10fd567ebb87fa55364ac224c265e602e6c97573 (patch)
tree1d1853ae82bcf2b4730ee0941c33f28a0b6e9035
parentf19b9ff7c5a2363cbcaff1da350128c1435e092f (diff)
downloadpylint-git-10fd567ebb87fa55364ac224c265e602e6c97573.tar.gz
Can have per directory configurations!
Closes #2357 Closes #1889
-rw-r--r--pylint/config.py73
-rw-r--r--pylint/lint.py36
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")