diff options
author | Ashley Whetter <ashley@awhetter.co.uk> | 2019-02-09 15:19:53 -0800 |
---|---|---|
committer | Ashley Whetter <ashley@awhetter.co.uk> | 2019-02-09 15:33:43 -0800 |
commit | ac46a39da7a69fb6487e0277e2d82244c7dadc00 (patch) | |
tree | f68d46c88442d6d93e030c401eee9dafdeac5679 | |
parent | 10fd567ebb87fa55364ac224c265e602e6c97573 (diff) | |
download | pylint-git-ac46a39da7a69fb6487e0277e2d82244c7dadc00.tar.gz |
Preliminary global state support
-rw-r--r-- | pylint/checkers/__init__.py | 15 | ||||
-rw-r--r-- | pylint/checkers/imports.py | 41 | ||||
-rw-r--r-- | pylint/checkers/similar.py | 8 | ||||
-rw-r--r-- | pylint/lint.py | 20 |
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 |