| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121 |
- r"""
- Each tool has its own method of ignoring errors and warnings.
- For example, pylint requires a comment of the form
- # pylint disable=<error codes>
- PEP8 will not warn on lines with
- # noqa
- Additionally, flake8 follows that convention for pyflakes errors,
- but pyflakes itself does not.
- Finally, an entire file is ignored by flake8 if this line is found
- in the file:
- # flake8\: noqa (the \ is needed to stop prospector ignoring this file :))
- This module's job is to attempt to collect all of these methods into
- a single coherent list of error suppression locations.
- """
- import re
- import warnings
- from collections import defaultdict
- from pathlib import Path
- from typing import List
- from prospector import encoding
- from prospector.exceptions import FatalProspectorException
- from prospector.message import Message
- _FLAKE8_IGNORE_FILE = re.compile(r"flake8[:=]\s*noqa", re.IGNORECASE)
- _PEP8_IGNORE_LINE = re.compile(r"#\s+noqa", re.IGNORECASE)
- _PYLINT_SUPPRESSED_MESSAGE = re.compile(r"^Suppressed \'([a-z0-9-]+)\' \(from line \d+\)$")
- def get_noqa_suppressions(file_contents):
- """
- Finds all pep8/flake8 suppression messages
- :param file_contents:
- A list of file lines
- :return:
- A pair - the first is whether to ignore the whole file, the
- second is a set of (0-indexed) line numbers to ignore.
- """
- ignore_whole_file = False
- ignore_lines = set()
- for line_number, line in enumerate(file_contents):
- if _FLAKE8_IGNORE_FILE.search(line):
- ignore_whole_file = True
- if _PEP8_IGNORE_LINE.search(line):
- ignore_lines.add(line_number + 1)
- return ignore_whole_file, ignore_lines
- _PYLINT_EQUIVALENTS = {
- # TODO: blending has this info already?
- "unused-import": (
- ("pyflakes", "FL0001"),
- ("frosted", "E101"),
- )
- }
- def _parse_pylint_informational(messages: List[Message]):
- ignore_files = set()
- ignore_messages: dict = defaultdict(lambda: defaultdict(list))
- for message in messages:
- if message.source == "pylint":
- if message.code == "suppressed-message":
- # this is a message indicating that a message was raised
- # by pylint but suppressed by configuration in the file
- match = _PYLINT_SUPPRESSED_MESSAGE.match(message.message)
- if not match:
- raise FatalProspectorException(f"Could not parsed suppressed message from {message.message}")
- suppressed_code = match.group(1)
- line_dict = ignore_messages[message.location.path]
- line_dict[message.location.line].append(suppressed_code)
- elif message.code == "file-ignored":
- ignore_files.add(message.location.path)
- return ignore_files, ignore_messages
- def get_suppressions(filepaths: List[Path], messages):
- """
- Given every message which was emitted by the tools, and the
- list of files to inspect, create a list of files to ignore,
- and a map of filepath -> line-number -> codes to ignore
- """
- paths_to_ignore = set()
- lines_to_ignore: dict = defaultdict(set)
- messages_to_ignore: dict = defaultdict(lambda: defaultdict(set))
- # first deal with 'noqa' style messages
- for filepath in filepaths:
- try:
- file_contents = encoding.read_py_file(filepath).split("\n")
- except encoding.CouldNotHandleEncoding as err:
- # TODO: this output will break output formats such as JSON
- warnings.warn(f"{err.path}: {err.__cause__}", ImportWarning)
- continue
- ignore_file, ignore_lines = get_noqa_suppressions(file_contents)
- if ignore_file:
- paths_to_ignore.add(filepath)
- lines_to_ignore[filepath] |= ignore_lines
- # now figure out which messages were suppressed by pylint
- pylint_ignore_files, pylint_ignore_messages = _parse_pylint_informational(messages)
- paths_to_ignore |= pylint_ignore_files
- for filepath, line in pylint_ignore_messages.items():
- for line_number, codes in line.items():
- for code in codes:
- messages_to_ignore[filepath][line_number].add(("pylint", code))
- if code in _PYLINT_EQUIVALENTS:
- for equivalent in _PYLINT_EQUIVALENTS[code]:
- messages_to_ignore[filepath][line_number].add(equivalent)
- return paths_to_ignore, lines_to_ignore, messages_to_ignore
|