summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAshley Whetter <ashley@awhetter.co.uk>2018-05-17 09:03:04 -0700
committerAshley Whetter <ashley@awhetter.co.uk>2019-02-09 13:40:54 -0800
commitcd41c2b82500220e77a70f7c3143350ada6c9f25 (patch)
treead1f22651adb88c13af2eb188f73551b0fb96224
parent9276e44839c2a03563b85cad3bad29dd950a8331 (diff)
downloadpylint-git-cd41c2b82500220e77a70f7c3143350ada6c9f25.tar.gz
Linters are created per file
-rw-r--r--pylint/lint.py123
-rw-r--r--pylint/utils.py11
2 files changed, 86 insertions, 48 deletions
diff --git a/pylint/lint.py b/pylint/lint.py
index 390efa2d2..131a07e0c 100644
--- a/pylint/lint.py
+++ b/pylint/lint.py
@@ -107,6 +107,7 @@ def _get_python_path(filepath):
def _merge_stats(stats):
merged = {}
by_msg = collections.Counter()
+ nested_keys = set()
for stat in stats:
message_stats = stat.pop("by_msg", {})
by_msg.update(message_stats)
@@ -116,11 +117,17 @@ def _merge_stats(stats):
merged[key] = item
else:
if isinstance(item, dict):
+ nested_keys.add(key)
+ elif isinstance(item, set):
merged[key].update(item)
else:
merged[key] = merged[key] + item
- merged["by_msg"] = by_msg
+ for key in nested_keys:
+ merged[key] = _merge_stats(stat.get(key, {}) for stat in stats)
+
+ if by_msg:
+ merged["by_msg"] = by_msg
return merged
@@ -487,6 +494,19 @@ class PyLinter(utils.MessagesHandlerMixIn, checkers.BaseTokenChecker):
},
),
(
+ "limit-inference-results",
+ {
+ "type": "int",
+ "metavar": "<number-of-results>",
+ "default": 100,
+ "help": (
+ "Control the amount of potential inferred values when inferring "
+ "a single object. This can help the performance when dealing with "
+ "large functions or complex, nested conditions. "
+ ),
+ },
+ ),
+ (
"extension-pkg-whitelist",
{
"type": "csv",
@@ -516,7 +536,8 @@ class PyLinter(utils.MessagesHandlerMixIn, checkers.BaseTokenChecker):
(
"exit-zero",
{
- "action": "store_true",
+ "type": "yn",
+ "default": False,
"help": (
"Always return a 0 (non-error) status code, even if "
"lint errors are found. This is primarily useful in "
@@ -775,18 +796,19 @@ class PyLinter(utils.MessagesHandlerMixIn, checkers.BaseTokenChecker):
walker.walk(ast_node)
return True
- # IAstroidChecker interface #################################################
-
def open(self):
- """initialize counters"""
- self.stats.clear()
- self.stats["by_module"] = {}
- self.stats["by_msg"] = {}
- MANAGER.always_load_extensions = self.config.unsafe_load_any_extension
- MANAGER.max_inferable_values = self.config.limit_inference_results
- MANAGER.extension_package_whitelist.update(self.config.extension_pkg_whitelist)
- for msg_cat in utils.MSG_TYPES.values():
- self.stats[msg_cat] = 0
+ self._init_msg_states()
+
+ def add_stats(self, **kwargs):
+ """add some stats entries to the statistic dictionary
+ raise an AssertionError if there is a key conflict
+ """
+ for key, value in kwargs.items():
+ if key[-1] == "_":
+ key = key[:-1]
+ assert key not in self.stats
+ self.stats[key] = value
+ return self.stats
# utilities ###################################################################
@@ -929,18 +951,21 @@ class PluginRegistry(utils.MessagesHandlerMixIn, ReportRegistry):
:raises InvalidCheckerError: If the priority of the checker is
invalid.
"""
+ # Allow instances to be passed for backwards compatibility
+ if isinstance(checker, checkers.BaseChecker):
+ checker = checker.__class__
+
existing_checker_types = set(
- type(existing_checker)
+ existing_checker
for name_checkers in self._checkers.values()
for existing_checker in name_checkers
)
- checker_type = type(checker)
- if checker_type in existing_checker_types:
+ if checker in existing_checker_types:
msg_fmt = (
"Not registering checker {}. A checker of type {} has "
"already been registered."
)
- msg = msg_fmt.format(checker.name, checker_type.__name__)
+ msg = msg_fmt.format(checker.name, checker.__name__)
warnings.warn(msg)
return
@@ -1367,7 +1392,7 @@ group are mutually exclusive.",
self.generate_reports(base_name)
- if self._global_config.config.exit_zero:
+ if self._global_config.exit_zero:
sys.exit(0)
else:
sys.exit(status_code)
@@ -1569,35 +1594,26 @@ group are mutually exclusive.",
# pylint: enable=unused-argument
+ def close_registration(self):
+ """Stop registering plugins and prepare everything for checking."""
+ MANAGER.always_load_extensions = self._global_config.unsafe_load_any_extension
+ MANAGER.max_inferable_values = self._global_config.limit_inference_results
+ MANAGER.extension_package_whitelist.update(
+ self._global_config.extension_pkg_whitelist
+ )
+
def check(self, files_or_modules, checkers_):
"""main checking entry: check a list of files or modules from their
name.
"""
- # initialize msgs_state now that all messages have been registered into
- # the store
- self._plugin_registry.init_msg_states()
+ # initialize msgs_state now that all messages have been registered into the store
+ self.close_registration()
if not isinstance(files_or_modules, (list, tuple)):
files_or_modules = (files_or_modules,)
- walker = utils.PyLintASTWalker(self._plugin_registry)
- tokencheckers = [
- c for c in checkers_ if interfaces.implements(c, interfaces.ITokenChecker)
- ]
- rawcheckers = [
- c for c in checkers_ if interfaces.implements(c, interfaces.IRawChecker)
- ]
# notify global begin
- linter = PyLinter(self._global_config)
- linter.config = self._global_config
- linter.msgs_store = self._plugin_registry.msgs_store
- linter.stats = self._plugin_registry.stats
- linter.open()
- for checker in checkers_:
- checker.linter = linter
- checker.open()
- if interfaces.implements(checker, interfaces.IAstroidChecker):
- walker.add_checker(checker)
+ all_stats = [self._plugin_registry.stats]
# build ast and check modules or packages
expanded_files = utils.expand_files(
files_or_modules,
@@ -1606,6 +1622,26 @@ group are mutually exclusive.",
self._global_config.black_list_re,
)
for module_desc in expanded_files:
+ linter = PyLinter(self._global_config)
+ linter.msgs_store = self._plugin_registry.msgs_store
+ linter.open()
+
+ walker = utils.PyLintASTWalker(self._plugin_registry)
+ allcheckers = []
+ tokencheckers = [linter]
+ rawcheckers = []
+ for checker_cls in checkers_:
+ checker = checker_cls(linter)
+ checker.linter = linter
+ checker.open()
+ allcheckers.append(checker)
+ if interfaces.implements(checker, interfaces.ITokenChecker):
+ tokencheckers.append(checker)
+ if interfaces.implements(checker, interfaces.IRawChecker):
+ rawcheckers.append(checker)
+ if interfaces.implements(checker, interfaces.IAstroidChecker):
+ walker.add_checker(checker)
+
modname = module_desc.name
filepath = module_desc.path
if not module_desc.isarg and not self.should_analyze_file(
@@ -1615,12 +1651,13 @@ group are mutually exclusive.",
linter.reporter = self._reporter
linter.check(module_desc, walker, rawcheckers, tokencheckers)
+ self._plugin_registry.stats["statement"] += walker.nbstatements
+ all_stats.append(linter.stats)
+
+ for checker in reversed(allcheckers):
+ checker.close()
- # notify global end
- self._plugin_registry.stats["statement"] = walker.nbstatements
- for checker in reversed(checkers_):
- checker.close()
- linter.close()
+ self._plugin_registry.stats = _merge_stats(all_stats)
return module_desc.basename, linter.msg_status
diff --git a/pylint/utils.py b/pylint/utils.py
index 4a5966656..88c1100c5 100644
--- a/pylint/utils.py
+++ b/pylint/utils.py
@@ -203,13 +203,12 @@ def build_message_def(checker, msgid, msg_tuple):
)
symbol = None
options.setdefault("scope", default_scope)
- return MessageDefinition(checker, msgid, msg, descr, symbol, **options)
+ return MessageDefinition(msgid, msg, descr, symbol, **options)
class MessageDefinition:
def __init__(
self,
- checker,
msgid,
msg,
descr,
@@ -219,7 +218,6 @@ class MessageDefinition:
maxversion=None,
old_names=None,
):
- self.checker = checker
if len(msgid) != 5:
raise InvalidMessageError("Invalid message id %r" % msgid)
if not msgid[0] in MSG_TYPES:
@@ -287,7 +285,7 @@ class MessagesHandlerMixIn:
self.msg_status = 0
self.msgs_store = MessagesStore()
self.reporter = None
- self.stats = {"by_module": {}, "by_msg": {}}
+ self.stats = {"by_module": {}, "by_msg": {}, "statement": 0}
super().__init__()
def _checker_messages(self, checker):
@@ -418,11 +416,14 @@ class MessagesHandlerMixIn:
for msgid in msgids:
self.disable(msgid)
- def init_msg_states(self):
+ def _init_msg_states(self):
for msg in self.msgs_store.messages:
if not msg.may_be_emitted():
self._msgs_state[msg.msgid] = False
+ for msg_cat in MSG_TYPES.values():
+ self.stats[msg_cat] = 0
+
def get_message_state_scope(self, msgid, line=None, confidence=UNDEFINED):
"""Returns the scope at which a message was enabled/disabled."""
if self.config.confidence and confidence.name not in self.config.confidence: