summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAshley Whetter <ashley@awhetter.co.uk>2019-02-09 15:19:53 -0800
committerAshley Whetter <ashley@awhetter.co.uk>2019-02-09 15:33:43 -0800
commitac46a39da7a69fb6487e0277e2d82244c7dadc00 (patch)
treef68d46c88442d6d93e030c401eee9dafdeac5679
parent10fd567ebb87fa55364ac224c265e602e6c97573 (diff)
downloadpylint-git-ac46a39da7a69fb6487e0277e2d82244c7dadc00.tar.gz
Preliminary global state support
-rw-r--r--pylint/checkers/__init__.py15
-rw-r--r--pylint/checkers/imports.py41
-rw-r--r--pylint/checkers/similar.py8
-rw-r--r--pylint/lint.py20
4 files changed, 63 insertions, 21 deletions
diff --git a/pylint/checkers/__init__.py b/pylint/checkers/__init__.py
index 95c225440..40eaef680 100644
--- a/pylint/checkers/__init__.py
+++ b/pylint/checkers/__init__.py
@@ -106,10 +106,21 @@ class BaseChecker(object):
# dummy methods implementing the IChecker interface
def open(self):
- """called before visiting project (i.e set of modules)"""
+ """Called before visiting a module.
+
+ :returns: The state to store for this checker instance.
+ :rtype: object or None
+ """
def close(self):
- """called after visiting project (i.e set of modules)"""
+ """Called after visiting project (i.e set of modules)"""
+
+ def global_close(self, states):
+ """Called after visiting everything.
+
+ :param states: The state objects for every instance of this checker.
+ :type states: list(object or None)
+ """
class BaseTokenChecker(BaseChecker):
diff --git a/pylint/checkers/imports.py b/pylint/checkers/imports.py
index 07ae01fec..355fea27a 100644
--- a/pylint/checkers/imports.py
+++ b/pylint/checkers/imports.py
@@ -386,10 +386,8 @@ class ImportsChecker(BaseChecker):
def __init__(self, linter=None):
BaseChecker.__init__(self, linter)
self.stats = None
- self.import_graph = None
self._imports_stack = []
self._first_non_import_node = None
- self._module_pkg = {} # mapping of modules to the pkg they belong in
self.reports = (
("RP0401", "External dependencies", self._report_external_dependencies),
("RP0402", "Modules dependencies graph", self._report_dependencies_graph),
@@ -427,21 +425,33 @@ class ImportsChecker(BaseChecker):
self.linter.add_stats(dependencies={})
self.linter.add_stats(cycles=[])
self.stats = self.linter.stats
- self.import_graph = collections.defaultdict(set)
- self._module_pkg = {} # mapping of modules to the pkg they belong in
- self._excluded_edges = collections.defaultdict(set)
self._ignored_modules = get_global_option(self, "ignored-modules", default=[])
+ state_type = collections.namedtuple(
+ "State", ["import_graph", "module_pkg", "excluded_edges"]
+ )
+ self.state = state_type(
+ collections.defaultdict(set), {}, collections.defaultdict(set)
+ )
+ return self.state
- def _import_graph_without_ignored_edges(self):
- filtered_graph = copy.deepcopy(self.import_graph)
+ def _import_graph_without_ignored_edges(self, state):
+ filtered_graph = copy.deepcopy(state.import_graph)
for node in filtered_graph:
- filtered_graph[node].difference_update(self._excluded_edges[node])
+ filtered_graph[node].difference_update(state.excluded_edges[node])
return filtered_graph
- def close(self):
+ def global_close(self, states):
"""called before visiting project (i.e set of modules)"""
- if self.linter.is_message_enabled("cyclic-import"):
- graph = self._import_graph_without_ignored_edges()
+ if self.linter.is_message_enabled("cyclic-import") and states:
+ state = states[0]
+ for other_state in states[1:]:
+ for key, value in other_state.import_graph.items():
+ state.import_graph[key].update(value)
+ state.module_pkg.update(other_state.module_pkg)
+ for key, value in other_state.excluded_edges.items():
+ state.excluded_edges[key].update(value)
+
+ graph = self._import_graph_without_ignored_edges(state)
vertices = list(graph)
for cycle in get_cycles(graph, vertices=vertices):
self.add_message("cyclic-import", args=" -> ".join(cycle))
@@ -798,10 +808,10 @@ class ImportsChecker(BaseChecker):
elif not is_standard_module(importedmodname):
# if this is not a package __init__ module
- if base != "__init__" and context_name not in self._module_pkg:
+ if base != "__init__" and context_name not in self.state.module_pkg:
# record the module's parent, or the module itself if this is
# a top level module, as the package it belongs to
- self._module_pkg[context_name] = context_name.rsplit(".", 1)[0]
+ self.state.module_pkg[context_name] = context_name.rsplit(".", 1)[0]
# handle dependencies
importedmodnames = self.stats["dependencies"].setdefault(
@@ -811,9 +821,9 @@ class ImportsChecker(BaseChecker):
importedmodnames.add(context_name)
# update import graph
- self.import_graph[context_name].add(importedmodname)
+ self.state.import_graph[context_name].add(importedmodname)
if not self.linter.is_message_enabled("cyclic-import", line=node.lineno):
- self._excluded_edges[context_name].add(importedmodname)
+ self.state.excluded_edges[context_name].add(importedmodname)
def _check_deprecated_module(self, node, mod_path):
"""check if the module is deprecated"""
@@ -891,6 +901,7 @@ class ImportsChecker(BaseChecker):
graph = collections.defaultdict(set)
for importee, importers in self.stats["dependencies"].items():
for importer in importers:
+ # TODO: Needs a state!
package = self._module_pkg.get(importer, importer)
is_inside = importee.startswith(package)
if is_inside and internal or not is_inside and not internal:
diff --git a/pylint/checkers/similar.py b/pylint/checkers/similar.py
index 2e45ce9c6..aed489b14 100644
--- a/pylint/checkers/similar.py
+++ b/pylint/checkers/similar.py
@@ -20,6 +20,7 @@
from __future__ import print_function
import sys
from collections import defaultdict
+import itertools
from itertools import groupby
import astroid
@@ -336,7 +337,6 @@ class SimilarChecker(BaseChecker, Similar):
Similar.__init__(
self, min_lines=4, ignore_comments=True, ignore_docstrings=True
)
- self.stats = None
def set_option(self, optname, value, action=None, optdict=None):
"""method called to set an option (registered in the options list)
@@ -359,6 +359,7 @@ class SimilarChecker(BaseChecker, Similar):
self.stats = self.linter.add_stats(
nb_duplicated_lines=0, percent_duplicated_lines=0
)
+ return self.linesets
def process_module(self, node):
"""process a module
@@ -370,11 +371,12 @@ class SimilarChecker(BaseChecker, Similar):
with node.stream() as stream:
self.append_stream(self.linter.current_name, stream, node.file_encoding)
- def close(self):
+ def global_close(self, states):
"""compute and display similarities on closing (i.e. end of parsing)"""
+ self.linesets = list(itertools.chain.from_iterable(states))
total = sum(len(lineset) for lineset in self.linesets)
duplicated = 0
- stats = self.stats
+ stats = self.linter.stats
for num, couples in self._compute_sims():
msg = []
for lineset, idx in couples:
diff --git a/pylint/lint.py b/pylint/lint.py
index a24daa922..0bc2853ef 100644
--- a/pylint/lint.py
+++ b/pylint/lint.py
@@ -1598,6 +1598,7 @@ group are mutually exclusive.",
files_or_modules = (files_or_modules,)
# notify global begin
+ all_states = collections.defaultdict(list)
all_stats = [self._plugin_registry.stats]
# build ast and check modules or packages
expanded_files = utils.expand_files(
@@ -1649,7 +1650,7 @@ group are mutually exclusive.",
for checker_cls in self.prepare_checkers(linter):
checker = checker_cls(linter)
checker.linter = linter
- checker.open()
+ all_states[checker_cls].append(checker.open())
allcheckers.append(checker)
if interfaces.implements(checker, interfaces.ITokenChecker):
tokencheckers.append(checker)
@@ -1666,6 +1667,23 @@ group are mutually exclusive.",
for checker in reversed(allcheckers):
checker.close()
+ linter = PyLinter(self._global_config)
+ linter.msgs_store = self._plugin_registry.msgs_store
+ linter.reporter = self._reporter
+ for msg_ids, enable in self._global_config.msg_toggles:
+ for msg_id in msg_ids:
+ if enable:
+ linter.enable(msg_id, scope="directory")
+ else:
+ linter.disable(msg_id, scope="directory")
+ linter.open()
+ # TODO: What about checkers that have been enabled in a local config?
+ for checker_cls in reversed(self.get_checkers()):
+ checker = checker_cls(linter)
+ checker.linter = linter
+ checker.global_close(all_states[checker_cls])
+
+ all_stats.append(linter.stats)
self._plugin_registry.stats = _merge_stats(all_stats)
return module_desc.basename, linter.msg_status