violation.py 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. """Contains the Violation error class used internally."""
  2. import functools
  3. import linecache
  4. import logging
  5. from typing import Dict
  6. from typing import Match
  7. from typing import NamedTuple
  8. from typing import Optional
  9. from typing import Set
  10. from flake8 import defaults
  11. from flake8 import utils
  12. LOG = logging.getLogger(__name__)
  13. @functools.lru_cache(maxsize=512)
  14. def _find_noqa(physical_line: str) -> Optional[Match[str]]:
  15. return defaults.NOQA_INLINE_REGEXP.search(physical_line)
  16. class Violation(NamedTuple):
  17. """Class representing a violation reported by Flake8."""
  18. code: str
  19. filename: str
  20. line_number: int
  21. column_number: int
  22. text: str
  23. physical_line: Optional[str]
  24. def is_inline_ignored(self, disable_noqa: bool) -> bool:
  25. """Determine if a comment has been added to ignore this line.
  26. :param disable_noqa:
  27. Whether or not users have provided ``--disable-noqa``.
  28. :returns:
  29. True if error is ignored in-line, False otherwise.
  30. """
  31. physical_line = self.physical_line
  32. # TODO(sigmavirus24): Determine how to handle stdin with linecache
  33. if disable_noqa:
  34. return False
  35. if physical_line is None:
  36. physical_line = linecache.getline(self.filename, self.line_number)
  37. noqa_match = _find_noqa(physical_line)
  38. if noqa_match is None:
  39. LOG.debug("%r is not inline ignored", self)
  40. return False
  41. codes_str = noqa_match.groupdict()["codes"]
  42. if codes_str is None:
  43. LOG.debug("%r is ignored by a blanket ``# noqa``", self)
  44. return True
  45. codes = set(utils.parse_comma_separated_list(codes_str))
  46. if self.code in codes or self.code.startswith(tuple(codes)):
  47. LOG.debug(
  48. "%r is ignored specifically inline with ``# noqa: %s``",
  49. self,
  50. codes_str,
  51. )
  52. return True
  53. LOG.debug(
  54. "%r is not ignored inline with ``# noqa: %s``", self, codes_str
  55. )
  56. return False
  57. def is_in(self, diff: Dict[str, Set[int]]) -> bool:
  58. """Determine if the violation is included in a diff's line ranges.
  59. This function relies on the parsed data added via
  60. :meth:`~StyleGuide.add_diff_ranges`. If that has not been called and
  61. we are not evaluating files in a diff, then this will always return
  62. True. If there are diff ranges, then this will return True if the
  63. line number in the error falls inside one of the ranges for the file
  64. (and assuming the file is part of the diff data). If there are diff
  65. ranges, this will return False if the file is not part of the diff
  66. data or the line number of the error is not in any of the ranges of
  67. the diff.
  68. :returns:
  69. True if there is no diff or if the error is in the diff's line
  70. number ranges. False if the error's line number falls outside
  71. the diff's line number ranges.
  72. """
  73. if not diff:
  74. return True
  75. # NOTE(sigmavirus24): The parsed diff will be a defaultdict with
  76. # a set as the default value (if we have received it from
  77. # flake8.utils.parse_unified_diff). In that case ranges below
  78. # could be an empty set (which is False-y) or if someone else
  79. # is using this API, it could be None. If we could guarantee one
  80. # or the other, we would check for it more explicitly.
  81. line_numbers = diff.get(self.filename)
  82. if not line_numbers:
  83. return False
  84. return self.line_number in line_numbers