summaryrefslogtreecommitdiff
path: root/pylint/utils/utils.py
diff options
context:
space:
mode:
authorPierre Sassoulas <pierre.sassoulas@gmail.com>2018-12-09 14:35:39 +0100
committerClaudiu Popa <pcmanticore@gmail.com>2019-03-09 11:09:29 +0100
commit16e98598c6d8afc826e3cc8f5612618e24d3ec6a (patch)
treeb14e53435a1c7a8831a3db9d114e3f7e7be6222d /pylint/utils/utils.py
parent20976c36f1e48061e72379979411bc695564a518 (diff)
downloadpylint-git-16e98598c6d8afc826e3cc8f5612618e24d3ec6a.tar.gz
Refactor - Create a file for each class in utils
Diffstat (limited to 'pylint/utils/utils.py')
-rw-r--r--pylint/utils/utils.py852
1 files changed, 12 insertions, 840 deletions
diff --git a/pylint/utils/utils.py b/pylint/utils/utils.py
index 5c429652f..6c6cd269b 100644
--- a/pylint/utils/utils.py
+++ b/pylint/utils/utils.py
@@ -1,5 +1,8 @@
# -*- coding: utf-8 -*-
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/master/COPYING
+
from __future__ import print_function
import codecs
@@ -7,91 +10,16 @@ import collections
import os
import re
import sys
-import textwrap
import tokenize
import warnings
-from inspect import cleandoc
from os.path import basename, dirname, exists, isdir, join, normpath, splitext
-from astroid import Module, modutils, nodes
-from pylint.exceptions import EmptyReportError, InvalidMessageError, UnknownMessageError
-from pylint.interfaces import UNDEFINED, IRawChecker, ITokenChecker, implements
-from pylint.reporters.ureports.nodes import Section
-
-MSG_TYPES = {
- "I": "info",
- "C": "convention",
- "R": "refactor",
- "W": "warning",
- "E": "error",
- "F": "fatal",
-}
-MSG_TYPES_LONG = {v: k for k, v in MSG_TYPES.items()}
-
-MSG_TYPES_STATUS = {"I": 0, "C": 16, "R": 8, "W": 4, "E": 2, "F": 1}
-
-_MSG_ORDER = "EWRCIF"
-MSG_STATE_SCOPE_CONFIG = 0
-MSG_STATE_SCOPE_MODULE = 1
-MSG_STATE_CONFIDENCE = 2
-
-# Allow stopping after the first semicolon/hash encountered,
-# so that an option can be continued with the reasons
-# why it is active or disabled.
-OPTION_RGX = re.compile(r"\s*#.*\bpylint:\s*([^;#]+)[;#]{0,1}")
-
-# The line/node distinction does not apply to fatal errors and reports.
-_SCOPE_EXEMPT = "FR"
-
-
-class WarningScope:
- LINE = "line-based-msg"
- NODE = "node-based-msg"
-
-
-_MsgBase = collections.namedtuple(
- "_MsgBase",
- [
- "msg_id",
- "symbol",
- "msg",
- "C",
- "category",
- "confidence",
- "abspath",
- "path",
- "module",
- "obj",
- "line",
- "column",
- ],
-)
-
-
-class Message(_MsgBase):
- """This class represent a message to be issued by the reporters"""
-
- def __new__(cls, msg_id, symbol, location, msg, confidence):
- return _MsgBase.__new__(
- cls,
- msg_id,
- symbol,
- msg,
- msg_id[0],
- MSG_TYPES[msg_id[0]],
- confidence,
- *location
- )
-
- def format(self, template):
- """Format the message according to the given template.
-
- The template format is the one of the format method :
- cf. http://docs.python.org/2/library/string.html#formatstrings
- """
- # For some reason, _asdict on derived namedtuples does not work with
- # Python 3.4. Needs some investigation.
- return template.format(**dict(zip(self._fields, self)))
+from astroid import Module, modutils
+from pylint.interfaces import IRawChecker, ITokenChecker, implements
+from pylint.utils.constants import MSG_TYPES, MSG_TYPES_LONG, PY_EXTS
+from pylint.utils.message_definition import MessageDefinition
+from pylint.utils.normalize_text import normalize_text
+from pylint.utils.warning_scope import WarningScope
def get_module_and_frameid(node):
@@ -164,678 +92,6 @@ def build_message_def(checker, msgid, msg_tuple):
return MessageDefinition(checker, msgid, msg, descr, symbol, **options)
-class MessageDefinition:
- def __init__(
- self,
- checker,
- msgid,
- msg,
- descr,
- symbol,
- scope,
- minversion=None,
- 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:
- raise InvalidMessageError("Bad message type %s in %r" % (msgid[0], msgid))
- self.msgid = msgid
- self.msg = msg
- self.descr = descr
- self.symbol = symbol
- self.scope = scope
- self.minversion = minversion
- self.maxversion = maxversion
- self.old_names = old_names or []
-
- def __repr__(self):
- return "MessageDefinition:{}".format(self.__dict__)
-
- def may_be_emitted(self):
- """return True if message may be emitted using the current interpreter"""
- if self.minversion is not None and self.minversion > sys.version_info:
- return False
- if self.maxversion is not None and self.maxversion <= sys.version_info:
- return False
- return True
-
- def format_help(self, checkerref=False):
- """return the help string for the given message id"""
- desc = self.descr
- if checkerref:
- desc += " This message belongs to the %s checker." % self.checker.name
- title = self.msg
- if self.symbol:
- msgid = "%s (%s)" % (self.symbol, self.msgid)
- else:
- msgid = self.msgid
- if self.minversion or self.maxversion:
- restr = []
- if self.minversion:
- restr.append("< %s" % ".".join([str(n) for n in self.minversion]))
- if self.maxversion:
- restr.append(">= %s" % ".".join([str(n) for n in self.maxversion]))
- restr = " or ".join(restr)
- if checkerref:
- desc += " It can't be emitted when using Python %s." % restr
- else:
- desc += " This message can't be emitted when using Python %s." % restr
- desc = _normalize_text(" ".join(desc.split()), indent=" ")
- if title != "%s":
- title = title.splitlines()[0]
-
- return ":%s: *%s*\n%s" % (msgid, title.rstrip(" "), desc)
- return ":%s:\n%s" % (msgid, desc)
-
-
-class MessagesHandlerMixIn:
- """a mix-in class containing all the messages related methods for the main
- lint class
- """
-
- __by_id_managed_msgs = [] # type: ignore
-
- def __init__(self):
- self._msgs_state = {}
- self.msg_status = 0
-
- def _checker_messages(self, checker):
- for known_checker in self._checkers[checker.lower()]:
- for msgid in known_checker.msgs:
- yield msgid
-
- @classmethod
- def clear_by_id_managed_msgs(cls):
- cls.__by_id_managed_msgs.clear()
-
- @classmethod
- def get_by_id_managed_msgs(cls):
- return cls.__by_id_managed_msgs
-
- def _register_by_id_managed_msg(self, msgid, line, is_disabled=True):
- """If the msgid is a numeric one, then register it to inform the user
- it could furnish instead a symbolic msgid."""
- try:
- message_definitions = self.msgs_store.get_message_definitions(msgid)
- for message_definition in message_definitions:
- if msgid == message_definition.msgid:
- MessagesHandlerMixIn.__by_id_managed_msgs.append(
- (
- self.current_name,
- message_definition.msgid,
- message_definition.symbol,
- line,
- is_disabled,
- )
- )
- except UnknownMessageError:
- pass
-
- def disable(self, msgid, scope="package", line=None, ignore_unknown=False):
- """don't output message of the given id"""
- self._set_msg_status(
- msgid, enable=False, scope=scope, line=line, ignore_unknown=ignore_unknown
- )
- self._register_by_id_managed_msg(msgid, line)
-
- def enable(self, msgid, scope="package", line=None, ignore_unknown=False):
- """reenable message of the given id"""
- self._set_msg_status(
- msgid, enable=True, scope=scope, line=line, ignore_unknown=ignore_unknown
- )
- self._register_by_id_managed_msg(msgid, line, is_disabled=False)
-
- def _set_msg_status(
- self, msgid, enable, scope="package", line=None, ignore_unknown=False
- ):
- assert scope in ("package", "module")
-
- if msgid == "all":
- for _msgid in MSG_TYPES:
- self._set_msg_status(_msgid, enable, scope, line, ignore_unknown)
- if enable and not self._python3_porting_mode:
- # Don't activate the python 3 porting checker if it wasn't activated explicitly.
- self.disable("python3")
- return
-
- # msgid is a category?
- catid = category_id(msgid)
- if catid is not None:
- for _msgid in self.msgs_store._msgs_by_category.get(catid):
- self._set_msg_status(_msgid, enable, scope, line)
- return
-
- # msgid is a checker name?
- if msgid.lower() in self._checkers:
- msgs_store = self.msgs_store
- for checker in self._checkers[msgid.lower()]:
- for _msgid in checker.msgs:
- if _msgid in msgs_store._alternative_names:
- self._set_msg_status(_msgid, enable, scope, line)
- return
-
- # msgid is report id?
- if msgid.lower().startswith("rp"):
- if enable:
- self.enable_report(msgid)
- else:
- self.disable_report(msgid)
- return
-
- try:
- # msgid is a symbolic or numeric msgid.
- message_definitions = self.msgs_store.get_message_definitions(msgid)
- except UnknownMessageError:
- if ignore_unknown:
- return
- raise
- for message_definition in message_definitions:
- self._set_one_msg_status(scope, message_definition, line, enable)
-
- def _set_one_msg_status(self, scope, msg, line, enable):
- if scope == "module":
- self.file_state.set_msg_status(msg, line, enable)
- if not enable and msg.symbol != "locally-disabled":
- self.add_message(
- "locally-disabled", line=line, args=(msg.symbol, msg.msgid)
- )
- else:
- msgs = self._msgs_state
- msgs[msg.msgid] = enable
- # sync configuration object
- self.config.enable = [
- self._message_symbol(mid) for mid, val in sorted(msgs.items()) if val
- ]
- self.config.disable = [
- self._message_symbol(mid)
- for mid, val in sorted(msgs.items())
- if not val
- ]
-
- def _message_symbol(self, msgid):
- """Get the message symbol of the given message id
-
- Return the original message id if the message does not
- exist.
- """
- try:
- return [md.symbol for md in self.msgs_store.get_message_definitions(msgid)]
- except UnknownMessageError:
- return msgid
-
- 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:
- return MSG_STATE_CONFIDENCE
- try:
- if line in self.file_state._module_msgs_state[msgid]:
- return MSG_STATE_SCOPE_MODULE
- except (KeyError, TypeError):
- return MSG_STATE_SCOPE_CONFIG
- return None
-
- def is_message_enabled(self, msg_descr, line=None, confidence=None):
- """return true if the message associated to the given message id is
- enabled
-
- msgid may be either a numeric or symbolic message id.
- """
- if self.config.confidence and confidence:
- if confidence.name not in self.config.confidence:
- return False
- try:
- message_definitions = self.msgs_store.get_message_definitions(msg_descr)
- msgids = [md.msgid for md in message_definitions]
- except UnknownMessageError:
- # The linter checks for messages that are not registered
- # due to version mismatch, just treat them as message IDs
- # for now.
- msgids = [msg_descr]
- for msgid in msgids:
- if self.is_one_message_enabled(msgid, line):
- return True
- return False
-
- def is_one_message_enabled(self, msgid, line):
- if line is None:
- return self._msgs_state.get(msgid, True)
- try:
- return self.file_state._module_msgs_state[msgid][line]
- except KeyError:
- # Check if the message's line is after the maximum line existing in ast tree.
- # This line won't appear in the ast tree and won't be referred in
- #  self.file_state._module_msgs_state
- # This happens for example with a commented line at the end of a module.
- max_line_number = self.file_state.get_effective_max_line_number()
- if max_line_number and line > max_line_number:
- fallback = True
- lines = self.file_state._raw_module_msgs_state.get(msgid, {})
-
- # Doesn't consider scopes, as a disable can be in a different scope
- # than that of the current line.
- closest_lines = reversed(
- [
- (message_line, enable)
- for message_line, enable in lines.items()
- if message_line <= line
- ]
- )
- last_line, is_enabled = next(closest_lines, (None, None))
- if last_line is not None:
- fallback = is_enabled
-
- return self._msgs_state.get(msgid, fallback)
- return self._msgs_state.get(msgid, True)
-
- def add_message(
- self,
- msg_descr,
- line=None,
- node=None,
- args=None,
- confidence=UNDEFINED,
- col_offset=None,
- ):
- """Adds a message given by ID or name.
-
- If provided, the message string is expanded using args.
-
- AST checkers must provide the node argument (but may optionally
- provide line if the line number is different), raw and token checkers
- must provide the line argument.
- """
- message_definitions = self.msgs_store.get_message_definitions(msg_descr)
- for message_definition in message_definitions:
- self.add_one_message(
- message_definition, line, node, args, confidence, col_offset
- )
-
- def add_one_message(
- self, message_definition, line, node, args, confidence, col_offset
- ):
- msgid = message_definition.msgid
- # backward compatibility, message may not have a symbol
- symbol = message_definition.symbol or msgid
- # Fatal messages and reports are special, the node/scope distinction
- # does not apply to them.
- if msgid[0] not in _SCOPE_EXEMPT:
- if message_definition.scope == WarningScope.LINE:
- if line is None:
- raise InvalidMessageError(
- "Message %s must provide line, got None" % msgid
- )
- if node is not None:
- raise InvalidMessageError(
- "Message %s must only provide line, "
- "got line=%s, node=%s" % (msgid, line, node)
- )
- elif message_definition.scope == WarningScope.NODE:
- # Node-based warnings may provide an override line.
- if node is None:
- raise InvalidMessageError(
- "Message %s must provide Node, got None" % msgid
- )
-
- if line is None and node is not None:
- line = node.fromlineno
- if col_offset is None and hasattr(node, "col_offset"):
- col_offset = (
- node.col_offset
- ) # XXX measured in bytes for utf-8, divide by two for chars?
-
- # should this message be displayed
- if not self.is_message_enabled(msgid, line, confidence):
- self.file_state.handle_ignored_message(
- self.get_message_state_scope(msgid, line, confidence),
- msgid,
- line,
- node,
- args,
- confidence,
- )
- return
- # update stats
- msg_cat = MSG_TYPES[msgid[0]]
- self.msg_status |= MSG_TYPES_STATUS[msgid[0]]
- self.stats[msg_cat] += 1
- self.stats["by_module"][self.current_name][msg_cat] += 1
- try:
- self.stats["by_msg"][symbol] += 1
- except KeyError:
- self.stats["by_msg"][symbol] = 1
- # expand message ?
- msg = message_definition.msg
- if args:
- msg %= args
- # get module and object
- if node is None:
- module, obj = self.current_name, ""
- abspath = self.current_file
- else:
- module, obj = get_module_and_frameid(node)
- abspath = node.root().file
- path = abspath.replace(self.reporter.path_strip_prefix, "", 1)
- # add the message
- self.reporter.handle_message(
- Message(
- msgid,
- symbol,
- (abspath, path, module, obj, line or 1, col_offset or 0),
- msg,
- confidence,
- )
- )
-
- def print_full_documentation(self, stream=None):
- """output a full documentation in ReST format"""
- if not stream:
- stream = sys.stdout
-
- print("Pylint global options and switches", file=stream)
- print("----------------------------------", file=stream)
- print("", file=stream)
- print("Pylint provides global options and switches.", file=stream)
- print("", file=stream)
-
- by_checker = {}
- for checker in self.get_checkers():
- if checker.name == "master":
- if checker.options:
- for section, options in checker.options_by_section():
- if section is None:
- title = "General options"
- else:
- title = "%s options" % section.capitalize()
- print(title, file=stream)
- print("~" * len(title), file=stream)
- _rest_format_section(stream, None, options)
- print("", file=stream)
- else:
- name = checker.name
- try:
- by_checker[name]["options"] += checker.options_and_values()
- by_checker[name]["msgs"].update(checker.msgs)
- by_checker[name]["reports"] += checker.reports
- except KeyError:
- by_checker[name] = {
- "options": list(checker.options_and_values()),
- "msgs": dict(checker.msgs),
- "reports": list(checker.reports),
- }
-
- print("Pylint checkers' options and switches", file=stream)
- print("-------------------------------------", file=stream)
- print("", file=stream)
- print("Pylint checkers can provide three set of features:", file=stream)
- print("", file=stream)
- print("* options that control their execution,", file=stream)
- print("* messages that they can raise,", file=stream)
- print("* reports that they can generate.", file=stream)
- print("", file=stream)
- print("Below is a list of all checkers and their features.", file=stream)
- print("", file=stream)
-
- for checker, info in sorted(by_checker.items()):
- self._print_checker_doc(checker, info, stream=stream)
-
- @staticmethod
- def _print_checker_doc(checker_name, info, stream=None):
- """Helper method for print_full_documentation.
-
- Also used by doc/exts/pylint_extensions.py.
- """
- if not stream:
- stream = sys.stdout
-
- doc = info.get("doc")
- module = info.get("module")
- msgs = info.get("msgs")
- options = info.get("options")
- reports = info.get("reports")
-
- checker_title = "%s checker" % (checker_name.replace("_", " ").title())
-
- if module:
- # Provide anchor to link against
- print(".. _%s:\n" % module, file=stream)
- print(checker_title, file=stream)
- print("~" * len(checker_title), file=stream)
- print("", file=stream)
- if module:
- print("This checker is provided by ``%s``." % module, file=stream)
- print("Verbatim name of the checker is ``%s``." % checker_name, file=stream)
- print("", file=stream)
- if doc:
- # Provide anchor to link against
- title = "{} Documentation".format(checker_title)
- print(title, file=stream)
- print("^" * len(title), file=stream)
- print(cleandoc(doc), file=stream)
- print("", file=stream)
- if options:
- title = "{} Options".format(checker_title)
- print(title, file=stream)
- print("^" * len(title), file=stream)
- _rest_format_section(stream, None, options)
- print("", file=stream)
- if msgs:
- title = "{} Messages".format(checker_title)
- print(title, file=stream)
- print("^" * len(title), file=stream)
- for msgid, msg in sorted(
- msgs.items(), key=lambda kv: (_MSG_ORDER.index(kv[0][0]), kv[1])
- ):
- msg = build_message_def(checker_name, msgid, msg)
- print(msg.format_help(checkerref=False), file=stream)
- print("", file=stream)
- if reports:
- title = "{} Reports".format(checker_title)
- print(title, file=stream)
- print("^" * len(title), file=stream)
- for report in reports:
- print(":%s: %s" % report[:2], file=stream)
- print("", file=stream)
- print("", file=stream)
-
-
-class FileState:
- """Hold internal state specific to the currently analyzed file"""
-
- def __init__(self, modname=None):
- self.base_name = modname
- self._module_msgs_state = {}
- self._raw_module_msgs_state = {}
- self._ignored_msgs = collections.defaultdict(set)
- self._suppression_mapping = {}
- self._effective_max_line_number = None
-
- def collect_block_lines(self, msgs_store, module_node):
- """Walk the AST to collect block level options line numbers."""
- for msg, lines in self._module_msgs_state.items():
- self._raw_module_msgs_state[msg] = lines.copy()
- orig_state = self._module_msgs_state.copy()
- self._module_msgs_state = {}
- self._suppression_mapping = {}
- self._effective_max_line_number = module_node.tolineno
- self._collect_block_lines(msgs_store, module_node, orig_state)
-
- def _collect_block_lines(self, msgs_store, node, msg_state):
- """Recursively walk (depth first) AST to collect block level options
- line numbers.
- """
- for child in node.get_children():
- self._collect_block_lines(msgs_store, child, msg_state)
- first = node.fromlineno
- last = node.tolineno
- # first child line number used to distinguish between disable
- # which are the first child of scoped node with those defined later.
- # For instance in the code below:
- #
- # 1. def meth8(self):
- # 2. """test late disabling"""
- # 3. # pylint: disable=E1102
- # 4. print self.blip
- # 5. # pylint: disable=E1101
- # 6. print self.bla
- #
- # E1102 should be disabled from line 1 to 6 while E1101 from line 5 to 6
- #
- # this is necessary to disable locally messages applying to class /
- # function using their fromlineno
- if (
- isinstance(node, (nodes.Module, nodes.ClassDef, nodes.FunctionDef))
- and node.body
- ):
- firstchildlineno = node.body[0].fromlineno
- else:
- firstchildlineno = last
- for msgid, lines in msg_state.items():
- for lineno, state in list(lines.items()):
- original_lineno = lineno
- if first > lineno or last < lineno:
- continue
- # Set state for all lines for this block, if the
- # warning is applied to nodes.
- message_definitions = msgs_store.get_message_definitions(msgid)
- for message_definition in message_definitions:
- if message_definition.scope == WarningScope.NODE:
- if lineno > firstchildlineno:
- state = True
- first_, last_ = node.block_range(lineno)
- else:
- first_ = lineno
- last_ = last
- for line in range(first_, last_ + 1):
- # do not override existing entries
- if line in self._module_msgs_state.get(msgid, ()):
- continue
- if line in lines: # state change in the same block
- state = lines[line]
- original_lineno = line
- if not state:
- self._suppression_mapping[(msgid, line)] = original_lineno
- try:
- self._module_msgs_state[msgid][line] = state
- except KeyError:
- self._module_msgs_state[msgid] = {line: state}
- del lines[lineno]
-
- def set_msg_status(self, msg, line, status):
- """Set status (enabled/disable) for a given message at a given line"""
- assert line > 0
- try:
- self._module_msgs_state[msg.msgid][line] = status
- except KeyError:
- self._module_msgs_state[msg.msgid] = {line: status}
-
- def handle_ignored_message(
- self, state_scope, msgid, line, node, args, confidence
- ): # pylint: disable=unused-argument
- """Report an ignored message.
-
- state_scope is either MSG_STATE_SCOPE_MODULE or MSG_STATE_SCOPE_CONFIG,
- depending on whether the message was disabled locally in the module,
- or globally. The other arguments are the same as for add_message.
- """
- if state_scope == MSG_STATE_SCOPE_MODULE:
- try:
- orig_line = self._suppression_mapping[(msgid, line)]
- self._ignored_msgs[(msgid, orig_line)].add(line)
- except KeyError:
- pass
-
- def iter_spurious_suppression_messages(self, msgs_store):
- for warning, lines in self._raw_module_msgs_state.items():
- for line, enable in lines.items():
- if not enable and (warning, line) not in self._ignored_msgs:
- yield "useless-suppression", line, (
- msgs_store.get_msg_display_string(warning),
- )
- # don't use iteritems here, _ignored_msgs may be modified by add_message
- for (warning, from_), lines in list(self._ignored_msgs.items()):
- for line in lines:
- yield "suppressed-message", line, (
- msgs_store.get_msg_display_string(warning),
- from_,
- )
-
- def get_effective_max_line_number(self):
- return self._effective_max_line_number
-
-
-class ReportsHandlerMixIn:
- """a mix-in class containing all the reports and stats manipulation
- related methods for the main lint class
- """
-
- def __init__(self):
- self._reports = collections.defaultdict(list)
- self._reports_state = {}
-
- def report_order(self):
- """ Return a list of reports, sorted in the order
- in which they must be called.
- """
- return list(self._reports)
-
- def register_report(self, reportid, r_title, r_cb, checker):
- """register a report
-
- reportid is the unique identifier for the report
- r_title the report's title
- r_cb the method to call to make the report
- checker is the checker defining the report
- """
- reportid = reportid.upper()
- self._reports[checker].append((reportid, r_title, r_cb))
-
- def enable_report(self, reportid):
- """disable the report of the given id"""
- reportid = reportid.upper()
- self._reports_state[reportid] = True
-
- def disable_report(self, reportid):
- """disable the report of the given id"""
- reportid = reportid.upper()
- self._reports_state[reportid] = False
-
- def report_is_enabled(self, reportid):
- """return true if the report associated to the given identifier is
- enabled
- """
- return self._reports_state.get(reportid, True)
-
- def make_reports(self, stats, old_stats):
- """render registered reports"""
- sect = Section("Report", "%s statements analysed." % (self.stats["statement"]))
- for checker in self.report_order():
- for reportid, r_title, r_cb in self._reports[checker]:
- if not self.report_is_enabled(reportid):
- continue
- report_sect = Section(r_title)
- try:
- r_cb(report_sect, stats, old_stats)
- except EmptyReportError:
- continue
- report_sect.report_id = reportid
- sect.append(report_sect)
- return sect
-
- 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
-
-
def _basename_in_blacklist_re(base_name, black_list_re):
"""Determines if the basename is matched in a regex blacklist
@@ -947,81 +203,6 @@ def expand_modules(files_or_modules, black_list, black_list_re):
return result, errors
-class PyLintASTWalker:
- def __init__(self, linter):
- # callbacks per node types
- self.nbstatements = 0
- self.visit_events = collections.defaultdict(list)
- self.leave_events = collections.defaultdict(list)
- self.linter = linter
-
- def _is_method_enabled(self, method):
- if not hasattr(method, "checks_msgs"):
- return True
- for msg_desc in method.checks_msgs:
- if self.linter.is_message_enabled(msg_desc):
- return True
- return False
-
- def add_checker(self, checker):
- """walk to the checker's dir and collect visit and leave methods"""
- # XXX : should be possible to merge needed_checkers and add_checker
- vcids = set()
- lcids = set()
- visits = self.visit_events
- leaves = self.leave_events
- for member in dir(checker):
- cid = member[6:]
- if cid == "default":
- continue
- if member.startswith("visit_"):
- v_meth = getattr(checker, member)
- # don't use visit_methods with no activated message:
- if self._is_method_enabled(v_meth):
- visits[cid].append(v_meth)
- vcids.add(cid)
- elif member.startswith("leave_"):
- l_meth = getattr(checker, member)
- # don't use leave_methods with no activated message:
- if self._is_method_enabled(l_meth):
- leaves[cid].append(l_meth)
- lcids.add(cid)
- visit_default = getattr(checker, "visit_default", None)
- if visit_default:
- for cls in nodes.ALL_NODE_CLASSES:
- cid = cls.__name__.lower()
- if cid not in vcids:
- visits[cid].append(visit_default)
- # for now we have no "leave_default" method in Pylint
-
- def walk(self, astroid):
- """call visit events of astroid checkers for the given node, recurse on
- its children, then leave events.
- """
- cid = astroid.__class__.__name__.lower()
-
- # Detect if the node is a new name for a deprecated alias.
- # In this case, favour the methods for the deprecated
- # alias if any, in order to maintain backwards
- # compatibility.
- visit_events = self.visit_events.get(cid, ())
- leave_events = self.leave_events.get(cid, ())
-
- if astroid.is_statement:
- self.nbstatements += 1
- # generate events for this node on each checker
- for cb in visit_events or ():
- cb(astroid)
- # recurse on children
- for child in astroid.get_children():
- self.walk(child)
- for cb in leave_events or ():
- cb(astroid)
-
-
-PY_EXTS = (".py", ".pyc", ".pyo", ".pyw", ".so", ".dll")
-
-
def register_plugins(linter, directory):
"""load all module and package in the given directory, looking for a
'register' function in each one, used to register pylint checkers
@@ -1134,15 +315,6 @@ def _unquote(string):
return string
-def _normalize_text(text, line_len=80, indent=""):
- """Wrap the text on the given line length."""
- return "\n".join(
- textwrap.wrap(
- text, width=line_len, initial_indent=indent, subsequent_indent=indent
- )
- )
-
-
def _check_csv(value):
if isinstance(value, (list, tuple)):
return value
@@ -1185,7 +357,7 @@ def _ini_format(stream, options):
value = _format_option_value(optdict, value)
help_opt = optdict.get("help")
if help_opt:
- help_opt = _normalize_text(help_opt, line_len=79, indent="# ")
+ help_opt = normalize_text(help_opt, line_len=79, indent="# ")
print(file=stream)
print(help_opt, file=stream)
else:
@@ -1210,13 +382,13 @@ def _rest_format_section(stream, section, options, doc=None):
if section:
print("%s\n%s" % (section, "'" * len(section)), file=stream)
if doc:
- print(_normalize_text(doc, line_len=79, indent=""), file=stream)
+ print(normalize_text(doc, line_len=79, indent=""), file=stream)
print(file=stream)
for optname, optdict, value in options:
help_opt = optdict.get("help")
print(":%s:" % optname, file=stream)
if help_opt:
- help_opt = _normalize_text(help_opt, line_len=79, indent=" ")
+ help_opt = normalize_text(help_opt, line_len=79, indent=" ")
print(help_opt, file=stream)
if value:
value = str(_format_option_value(optdict, value))