diff options
Diffstat (limited to 'pylint/utils.py')
-rw-r--r-- | pylint/utils.py | 500 |
1 files changed, 303 insertions, 197 deletions
diff --git a/pylint/utils.py b/pylint/utils.py index 49216b675..7c7879cae 100644 --- a/pylint/utils.py +++ b/pylint/utils.py @@ -62,25 +62,18 @@ from pylint.exceptions import InvalidMessageError, UnknownMessageError, EmptyRep MSG_TYPES = { - 'I' : 'info', - 'C' : 'convention', - 'R' : 'refactor', - 'W' : 'warning', - 'E' : 'error', - 'F' : 'fatal' - } + "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_TYPES_STATUS = {"I": 0, "C": 16, "R": 8, "W": 4, "E": 2, "F": 1} -_MSG_ORDER = 'EWRCIF' +_MSG_ORDER = "EWRCIF" MSG_STATE_SCOPE_CONFIG = 0 MSG_STATE_SCOPE_MODULE = 1 MSG_STATE_CONFIDENCE = 2 @@ -88,27 +81,50 @@ MSG_STATE_CONFIDENCE = 2 # Allow stopping after the first semicolon 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}') +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' +_SCOPE_EXEMPT = "FR" + class WarningScope: - LINE = 'line-based-msg' - NODE = 'node-based-msg' + 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']) + "_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) + 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. @@ -124,18 +140,19 @@ class Message(_MsgBase): def get_module_and_frameid(node): """return the module name and the frame id in the module""" frame = node.frame() - module, obj = '', [] + module, obj = "", [] while frame: if isinstance(frame, Module): module = frame.name else: - obj.append(getattr(frame, 'name', '<lambda>')) + obj.append(getattr(frame, "name", "<lambda>")) try: frame = frame.parent.frame() except AttributeError: frame = None obj.reverse() - return module, '.'.join(obj) + return module, ".".join(obj) + def category_id(cid): cid = cid.upper() @@ -143,14 +160,16 @@ def category_id(cid): return cid return MSG_TYPES_LONG.get(cid) + def safe_decode(line, encoding, *args, **kwargs): - '''return decoded line from encoding or decode with default encoding''' + """return decoded line from encoding or decode with default encoding""" try: return line.decode(encoding or sys.getdefaultencoding(), *args, **kwargs) except LookupError: return line.decode(sys.getdefaultencoding(), *args, **kwargs) -def decoding_stream(stream, encoding, errors='strict'): + +def decoding_stream(stream, encoding, errors="strict"): try: reader_cls = codecs.getreader(encoding or sys.getdefaultencoding()) except LookupError: @@ -163,6 +182,7 @@ def tokenize_module(module): readline = stream.readline return list(tokenize.tokenize(readline)) + def build_message_def(checker, msgid, msg_tuple): if implements(checker, (IRawChecker, ITokenChecker)): default_scope = WarningScope.LINE @@ -177,22 +197,34 @@ def build_message_def(checker, msgid, msg_tuple): # messages should have a symbol, but for backward compatibility # they may not. (msg, descr) = msg_tuple - warnings.warn("[pylint 0.26] description of message %s doesn't include " - "a symbolic name" % msgid, DeprecationWarning) + warnings.warn( + "[pylint 0.26] description of message %s doesn't include " + "a symbolic name" % msgid, + DeprecationWarning, + ) symbol = None - options.setdefault('scope', default_scope) + options.setdefault("scope", default_scope) 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): + 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) + 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)) + raise InvalidMessageError("Bad message type %s in %r" % (msgid[0], msgid)) self.msgid = msgid self.msg = msg self.descr = descr @@ -217,37 +249,37 @@ class MessageDefinition: """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 + desc += " This message belongs to the %s checker." % self.checker.name title = self.msg if self.symbol: - msgid = '%s (%s)' % (self.symbol, self.msgid) + 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])) + 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) + 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': + 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) + 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 + + __by_id_managed_msgs = [] # type: ignore def __init__(self): self._msgs_state = {} @@ -273,31 +305,36 @@ class MessagesHandlerMixIn: msg = self.msgs_store.get_message_definition(msgid) if msgid == msg.msgid: MessagesHandlerMixIn.__by_id_managed_msgs.append( - (self.current_name, msg.msgid, msg.symbol, line, is_disabled)) + (self.current_name, msg.msgid, msg.symbol, line, is_disabled) + ) except UnknownMessageError: pass - def disable(self, msgid, scope='package', line=None, ignore_unknown=False): + 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._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): + 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._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') + def _set_msg_status( + self, msgid, enable, scope="package", line=None, ignore_unknown=False + ): + assert scope in ("package", "module") - if msgid == 'all': + 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') + self.disable("python3") return # msgid is a category? @@ -317,7 +354,7 @@ class MessagesHandlerMixIn: return # msgid is report id? - if msgid.lower().startswith('rp'): + if msgid.lower().startswith("rp"): if enable: self.enable_report(msgid) else: @@ -332,19 +369,24 @@ class MessagesHandlerMixIn: return raise - if scope == 'module': + 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)) + 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] + 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 @@ -391,16 +433,23 @@ class MessagesHandlerMixIn: 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 + # 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): + if max_line_number and line > max_line_number: fallback = msgid not in self.file_state._raw_module_msgs_state 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): + 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. @@ -419,53 +468,69 @@ class MessagesHandlerMixIn: if msg_info.scope == WarningScope.LINE: if line is None: raise InvalidMessageError( - 'Message %s must provide line, got None' % msgid) + "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)) + "Message %s must only provide line, " + "got line=%s, node=%s" % (msgid, line, node) + ) elif msg_info.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) + "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? + 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) + 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 + self.stats["by_module"][self.current_name][msg_cat] += 1 try: - self.stats['by_msg'][symbol] += 1 + self.stats["by_msg"][symbol] += 1 except KeyError: - self.stats['by_msg'][symbol] = 1 + self.stats["by_msg"][symbol] = 1 # expand message ? msg = msg_info.msg if args: msg %= args # get module and object if node is None: - module, obj = self.current_name, '' + 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) + 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)) + 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""" @@ -480,28 +545,28 @@ class MessagesHandlerMixIn: by_checker = {} for checker in self.get_checkers(): - if checker.name == 'master': + if checker.name == "master": if checker.options: for section, options in checker.options_by_section(): if section is None: - title = 'General options' + title = "General options" else: - title = '%s options' % section.capitalize() + title = "%s options" % section.capitalize() print(title, file=stream) - print('~' * len(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 + 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), + "options": list(checker.options_and_values()), + "msgs": dict(checker.msgs), + "reports": list(checker.reports), } print("Pylint checkers' options and switches", file=stream) @@ -528,19 +593,19 @@ class MessagesHandlerMixIn: 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') + 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()) + 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("~" * len(checker_title), file=stream) print("", file=stream) if module: print("This checker is provided by ``%s``." % module, file=stream) @@ -548,35 +613,37 @@ class MessagesHandlerMixIn: print("", file=stream) if doc: # Provide anchor to link against - title = '{} Documentation'.format(checker_title) + title = "{} Documentation".format(checker_title) print(title, file=stream) - print('^' * len(title), file=stream) + print("^" * len(title), file=stream) print(cleandoc(doc), file=stream) print("", file=stream) if options: - title = '{} Options'.format(checker_title) + title = "{} Options".format(checker_title) print(title, file=stream) - print('^' * len(title), file=stream) + print("^" * len(title), file=stream) _rest_format_section(stream, None, options) print("", file=stream) if msgs: - title = '{} Messages'.format(checker_title) + 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])): + 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) + title = "{} Reports".format(checker_title) print(title, file=stream) - print('^' * len(title), file=stream) + print("^" * len(title), file=stream) for report in reports: - print(':%s: %s' % report[:2], file=stream) + print(":%s: %s" % report[:2], file=stream) print("", file=stream) print("", file=stream) + class FileState: """Hold internal state specific to the currently analyzed file""" @@ -621,8 +688,10 @@ class FileState: # # 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): + if ( + isinstance(node, (nodes.Module, nodes.ClassDef, nodes.FunctionDef)) + and node.body + ): firstchildlineno = node.body[0].fromlineno else: firstchildlineno = last @@ -640,11 +709,11 @@ class FileState: else: first_ = lineno last_ = last - for line in range(first_, last_+1): + 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 + if line in lines: # state change in the same block state = lines[line] original_lineno = line if not state: @@ -663,8 +732,9 @@ class FileState: 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 + 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, @@ -682,13 +752,16 @@ class FileState: 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),) + 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_) + 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 @@ -794,7 +867,6 @@ class MessagesStore: checker_id = message.msgid[1:3] existing_ids.append(message.msgid) - def _register_alternative_name(self, msg, msgid, symbol): """helper for register_message()""" self._check_id_and_symbol_consistency(msgid, symbol) @@ -874,7 +946,9 @@ class MessagesStore: :param str other_msgid: Other offending msgid :raises InvalidMessageError: when a msgid is duplicated. """ - error_message = "Message symbol '{symbol}' cannot be used for ".format(symbol=symbol) + error_message = "Message symbol '{symbol}' cannot be used for ".format( + symbol=symbol + ) error_message += "'{other_msgid}' and '{msgid}' at the same time.".format( other_msgid=other_msgid, msgid=msgid ) @@ -896,7 +970,9 @@ class MessagesStore: except KeyError: pass raise UnknownMessageError( - 'No such message id {msgid_or_symbol}'.format(msgid_or_symbol=msgid_or_symbol) + "No such message id {msgid_or_symbol}".format( + msgid_or_symbol=msgid_or_symbol + ) ) def get_msg_display_string(self, msgid): @@ -931,6 +1007,7 @@ 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 = {} @@ -970,8 +1047,7 @@ class ReportsHandlerMixIn: def make_reports(self, stats, old_stats): """render registered reports""" - sect = Section('Report', - '%s statements analysed.'% (self.stats['statement'])) + 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): @@ -990,12 +1066,13 @@ class ReportsHandlerMixIn: raise an AssertionError if there is a key conflict """ for key, value in kwargs.items(): - if key[-1] == '_': + 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 @@ -1011,11 +1088,14 @@ def _basename_in_blacklist_re(base_name, black_list_re): return True return False + def _modpath_from_file(filename, is_namespace): def _is_package_cb(path, parts): return modutils.check_modpath_has_init(path, parts) or is_namespace - return modutils.modpath_from_file_with_callback(filename, is_package_cb=_is_package_cb) + return modutils.modpath_from_file_with_callback( + filename, is_package_cb=_is_package_cb + ) def expand_modules(files_or_modules, black_list, black_list_re): @@ -1032,28 +1112,28 @@ def expand_modules(files_or_modules, black_list, black_list_re): if exists(something): # this is a file or a directory try: - modname = '.'.join(modutils.modpath_from_file(something)) + modname = ".".join(modutils.modpath_from_file(something)) except ImportError: modname = splitext(basename(something))[0] if isdir(something): - filepath = join(something, '__init__.py') + filepath = join(something, "__init__.py") else: filepath = something else: # suppose it's a module or package modname = something try: - filepath = modutils.file_from_modpath(modname.split('.')) + filepath = modutils.file_from_modpath(modname.split(".")) if filepath is None: continue except (ImportError, SyntaxError) as ex: # FIXME p3k : the SyntaxError is a Python bug and should be # removed as soon as possible http://bugs.python.org/issue10588 - errors.append({'key': 'fatal', 'mod': modname, 'ex': ex}) + errors.append({"key": "fatal", "mod": modname, "ex": ex}) continue filepath = normpath(filepath) - modparts = (modname or something).split('.') + modparts = (modname or something).split(".") try: spec = modutils.file_info_from_modpath(modparts, path=sys.path) @@ -1066,30 +1146,45 @@ def expand_modules(files_or_modules, black_list, black_list_re): is_directory = modutils.is_directory(spec) if not is_namespace: - result.append({'path': filepath, 'name': modname, 'isarg': True, - 'basepath': filepath, 'basename': modname}) - - has_init = (not (modname.endswith('.__init__') or modname == '__init__') - and basename(filepath) == '__init__.py') + result.append( + { + "path": filepath, + "name": modname, + "isarg": True, + "basepath": filepath, + "basename": modname, + } + ) + + has_init = ( + not (modname.endswith(".__init__") or modname == "__init__") + and basename(filepath) == "__init__.py" + ) if has_init or is_namespace or is_directory: - for subfilepath in modutils.get_module_files(dirname(filepath), black_list, - list_all=is_namespace): + for subfilepath in modutils.get_module_files( + dirname(filepath), black_list, list_all=is_namespace + ): if filepath == subfilepath: continue if _basename_in_blacklist_re(basename(subfilepath), black_list_re): continue modpath = _modpath_from_file(subfilepath, is_namespace) - submodname = '.'.join(modpath) - result.append({'path': subfilepath, 'name': submodname, - 'isarg': False, - 'basepath': filepath, 'basename': modname}) + submodname = ".".join(modpath) + result.append( + { + "path": subfilepath, + "name": submodname, + "isarg": False, + "basepath": filepath, + "basename": modname, + } + ) return result, errors class PyLintASTWalker: - def __init__(self, linter): # callbacks per node types self.nbstatements = 0 @@ -1098,7 +1193,7 @@ class PyLintASTWalker: self.linter = linter def _is_method_enabled(self, method): - if not hasattr(method, 'checks_msgs'): + if not hasattr(method, "checks_msgs"): return True for msg_desc in method.checks_msgs: if self.linter.is_message_enabled(msg_desc): @@ -1114,21 +1209,21 @@ class PyLintASTWalker: leaves = self.leave_events for member in dir(checker): cid = member[6:] - if cid == 'default': + if cid == "default": continue - if member.startswith('visit_'): + 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_'): + 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) + visit_default = getattr(checker, "visit_default", None) if visit_default: for cls in nodes.ALL_NODE_CLASSES: cid = cls.__name__.lower() @@ -1161,7 +1256,8 @@ class PyLintASTWalker: cb(astroid) -PY_EXTS = ('.py', '.pyc', '.pyo', '.pyw', '.so', '.dll') +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 @@ -1170,23 +1266,28 @@ def register_plugins(linter, directory): imported = {} for filename in os.listdir(directory): base, extension = splitext(filename) - if base in imported or base == '__pycache__': + if base in imported or base == "__pycache__": continue - if extension in PY_EXTS and base != '__init__' or ( - not extension and isdir(join(directory, base))): + if ( + extension in PY_EXTS + and base != "__init__" + or (not extension and isdir(join(directory, base))) + ): try: module = modutils.load_module_from_file(join(directory, filename)) except ValueError: # empty module name (usually emacs auto-save files) continue except ImportError as exc: - print("Problem importing module %s: %s" % (filename, exc), - file=sys.stderr) + print( + "Problem importing module %s: %s" % (filename, exc), file=sys.stderr + ) else: - if hasattr(module, 'register'): + if hasattr(module, "register"): module.register(linter) imported[base] = 1 + def get_global_option(checker, option, default=None): """ Retrieve an option defined by the given *checker* or by all known option providers. @@ -1209,25 +1310,27 @@ def get_global_option(checker, option, default=None): return default -def deprecated_option(shortname=None, opt_type=None, help_msg=None, deprecation_msg=None): - def _warn_deprecated(option, optname, *args): # pylint: disable=unused-argument +def deprecated_option( + shortname=None, opt_type=None, help_msg=None, deprecation_msg=None +): + def _warn_deprecated(option, optname, *args): # pylint: disable=unused-argument if deprecation_msg: sys.stderr.write(deprecation_msg % (optname,)) option = { - 'help': help_msg, - 'hide': True, - 'type': opt_type, - 'action': 'callback', - 'callback': _warn_deprecated, - 'deprecated': True + "help": help_msg, + "hide": True, + "type": opt_type, + "action": "callback", + "callback": _warn_deprecated, + "deprecated": True, } if shortname: - option['shortname'] = shortname + option["shortname"] = shortname return option -def _splitstrip(string, sep=','): +def _splitstrip(string, sep=","): """return a list of stripped string by splitting the string given as argument on `sep` (',' by default). Empty string are discarded. @@ -1261,17 +1364,20 @@ def _unquote(string): """ if not string: return string - if string[0] in '"\'': + if string[0] in "\"'": string = string[1:] - if string[-1] in '"\'': + if string[-1] in "\"'": string = string[:-1] return string -def _normalize_text(text, line_len=80, indent=''): +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)) + return "\n".join( + textwrap.wrap( + text, width=line_len, initial_indent=indent, subsequent_indent=indent + ) + ) def _check_csv(value): @@ -1283,20 +1389,20 @@ def _check_csv(value): def _comment(string): """return string as a comment""" lines = [line.strip() for line in string.splitlines()] - return '# ' + ('%s# ' % os.linesep).join(lines) + return "# " + ("%s# " % os.linesep).join(lines) def _format_option_value(optdict, value): """return the user input's value from a 'compiled' value""" if isinstance(value, (list, tuple)): - value = ','.join(_format_option_value(optdict, item) for item in value) + value = ",".join(_format_option_value(optdict, item) for item in value) elif isinstance(value, dict): - value = ','.join('%s:%s' % (k, v) for k, v in value.items()) - elif hasattr(value, 'match'): # optdict.get('type') == 'regexp' + value = ",".join("%s:%s" % (k, v) for k, v in value.items()) + elif hasattr(value, "match"): # optdict.get('type') == 'regexp' # compiled regexp value = value.pattern - elif optdict.get('type') == 'yn': - value = 'yes' if value else 'no' + elif optdict.get("type") == "yn": + value = "yes" if value else "no" elif isinstance(value, str) and value.isspace(): value = "'%s'" % value return value @@ -1306,7 +1412,7 @@ def _ini_format_section(stream, section, options, doc=None): """format an options section using the INI format""" if doc: print(_comment(doc), file=stream) - print('[%s]' % section, file=stream) + print("[%s]" % section, file=stream) _ini_format(stream, options) @@ -1314,24 +1420,24 @@ def _ini_format(stream, options): """format options using the INI format""" for optname, optdict, value in options: value = _format_option_value(optdict, value) - help_opt = optdict.get('help') + 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: print(file=stream) if value is None: - print('#%s=' % optname, file=stream) + print("#%s=" % optname, file=stream) else: value = str(value).strip() - if re.match(r'^([\w-]+,)+[\w-]+$', str(value)): - separator = '\n ' + ' ' * len(optname) - value = separator.join( - x + ',' for x in str(value).split(',')) + if re.match(r"^([\w-]+,)+[\w-]+$", str(value)): + separator = "\n " + " " * len(optname) + value = separator.join(x + "," for x in str(value).split(",")) # remove trailing ',' from last element of the list value = value[:-1] - print('%s=%s' % (optname, value), file=stream) + print("%s=%s" % (optname, value), file=stream) + format_section = _ini_format_section @@ -1339,17 +1445,17 @@ format_section = _ini_format_section def _rest_format_section(stream, section, options, doc=None): """format an options section using as ReST formatted output""" if section: - print('%s\n%s' % (section, "'"*len(section)), file=stream) + 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) + 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)) print(file=stream) - print(' Default: ``%s``' % value.replace("`` ", "```` ``"), file=stream) + print(" Default: ``%s``" % value.replace("`` ", "```` ``"), file=stream) |