mirror of
https://github.com/PyCQA/flake8.git
synced 2026-03-29 10:36:53 +00:00
break type checking cycles
This commit is contained in:
parent
f6267dd4d7
commit
fa4c31fb97
10 changed files with 142 additions and 150 deletions
|
|
@ -6,13 +6,10 @@ from typing import IO
|
|||
from typing import List
|
||||
from typing import Optional
|
||||
from typing import Tuple
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from flake8.formatting import _windows_color
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from flake8.statistics import Statistics
|
||||
from flake8.style_guide import Violation
|
||||
from flake8.statistics import Statistics
|
||||
from flake8.violation import Violation
|
||||
|
||||
|
||||
class BaseFormatter:
|
||||
|
|
@ -98,9 +95,9 @@ class BaseFormatter:
|
|||
|
||||
:param error:
|
||||
This will be an instance of
|
||||
:class:`~flake8.style_guide.Violation`.
|
||||
:class:`~flake8.violation.Violation`.
|
||||
:type error:
|
||||
flake8.style_guide.Violation
|
||||
flake8.violation.Violation
|
||||
"""
|
||||
line = self.format(error)
|
||||
source = self.show_source(error)
|
||||
|
|
@ -113,9 +110,9 @@ class BaseFormatter:
|
|||
|
||||
:param error:
|
||||
This will be an instance of
|
||||
:class:`~flake8.style_guide.Violation`.
|
||||
:class:`~flake8.violation.Violation`.
|
||||
:type error:
|
||||
flake8.style_guide.Violation
|
||||
flake8.violation.Violation
|
||||
:returns:
|
||||
The formatted error string.
|
||||
:rtype:
|
||||
|
|
@ -163,9 +160,9 @@ class BaseFormatter:
|
|||
|
||||
:param error:
|
||||
This will be an instance of
|
||||
:class:`~flake8.style_guide.Violation`.
|
||||
:class:`~flake8.violation.Violation`.
|
||||
:type error:
|
||||
flake8.style_guide.Violation
|
||||
flake8.violation.Violation
|
||||
:returns:
|
||||
The formatted error string if the user wants to show the source.
|
||||
If the user does not want to show the source, this will return
|
||||
|
|
|
|||
|
|
@ -1,12 +1,9 @@
|
|||
"""Default formatting class for Flake8."""
|
||||
from typing import Optional
|
||||
from typing import Set
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from flake8.formatting import base
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from flake8.style_guide import Violation
|
||||
from flake8.violation import Violation
|
||||
|
||||
COLORS = {
|
||||
"bold": "\033[1m",
|
||||
|
|
|
|||
|
|
@ -4,10 +4,8 @@ from typing import Generator
|
|||
from typing import List
|
||||
from typing import NamedTuple
|
||||
from typing import Optional
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from flake8.style_guide import Violation
|
||||
from flake8.violation import Violation
|
||||
|
||||
|
||||
class Statistics:
|
||||
|
|
@ -34,7 +32,7 @@ class Statistics:
|
|||
The Violation instance containing the information about the
|
||||
violation.
|
||||
:type error:
|
||||
flake8.style_guide.Violation
|
||||
flake8.violation.Violation
|
||||
"""
|
||||
key = Key.create_from(error)
|
||||
if key not in self._store:
|
||||
|
|
@ -86,7 +84,7 @@ class Key(NamedTuple):
|
|||
|
||||
@classmethod
|
||||
def create_from(cls, error: "Violation") -> "Key":
|
||||
"""Create a Key from :class:`flake8.style_guide.Violation`."""
|
||||
"""Create a Key from :class:`flake8.violation.Violation`."""
|
||||
return cls(filename=error.filename, code=error.code)
|
||||
|
||||
def matches(self, prefix: str, filename: Optional[str]) -> bool:
|
||||
|
|
@ -127,7 +125,7 @@ class Statistic:
|
|||
|
||||
@classmethod
|
||||
def create_from(cls, error: "Violation") -> "Statistic":
|
||||
"""Create a Statistic from a :class:`flake8.style_guide.Violation`."""
|
||||
"""Create a Statistic from a :class:`flake8.violation.Violation`."""
|
||||
return cls(
|
||||
error_code=error.code,
|
||||
filename=error.filename,
|
||||
|
|
|
|||
|
|
@ -5,13 +5,10 @@ import copy
|
|||
import enum
|
||||
import functools
|
||||
import itertools
|
||||
import linecache
|
||||
import logging
|
||||
from typing import Dict
|
||||
from typing import Generator
|
||||
from typing import List
|
||||
from typing import Match
|
||||
from typing import NamedTuple
|
||||
from typing import Optional
|
||||
from typing import Sequence
|
||||
from typing import Set
|
||||
|
|
@ -22,6 +19,7 @@ from flake8 import defaults
|
|||
from flake8 import statistics
|
||||
from flake8 import utils
|
||||
from flake8.formatting import base as base_formatter
|
||||
from flake8.violation import Violation
|
||||
|
||||
__all__ = ("StyleGuide",)
|
||||
|
||||
|
|
@ -49,98 +47,6 @@ class Decision(enum.Enum):
|
|||
Selected = "selected error"
|
||||
|
||||
|
||||
@functools.lru_cache(maxsize=512)
|
||||
def find_noqa(physical_line: str) -> Optional[Match[str]]:
|
||||
return defaults.NOQA_INLINE_REGEXP.search(physical_line)
|
||||
|
||||
|
||||
class Violation(NamedTuple):
|
||||
"""Class representing a violation reported by Flake8."""
|
||||
|
||||
code: str
|
||||
filename: str
|
||||
line_number: int
|
||||
column_number: int
|
||||
text: str
|
||||
physical_line: Optional[str]
|
||||
|
||||
def is_inline_ignored(self, disable_noqa: bool) -> bool:
|
||||
"""Determine if a comment has been added to ignore this line.
|
||||
|
||||
:param bool disable_noqa:
|
||||
Whether or not users have provided ``--disable-noqa``.
|
||||
:returns:
|
||||
True if error is ignored in-line, False otherwise.
|
||||
:rtype:
|
||||
bool
|
||||
"""
|
||||
physical_line = self.physical_line
|
||||
# TODO(sigmavirus24): Determine how to handle stdin with linecache
|
||||
if disable_noqa:
|
||||
return False
|
||||
|
||||
if physical_line is None:
|
||||
physical_line = linecache.getline(self.filename, self.line_number)
|
||||
noqa_match = find_noqa(physical_line)
|
||||
if noqa_match is None:
|
||||
LOG.debug("%r is not inline ignored", self)
|
||||
return False
|
||||
|
||||
codes_str = noqa_match.groupdict()["codes"]
|
||||
if codes_str is None:
|
||||
LOG.debug("%r is ignored by a blanket ``# noqa``", self)
|
||||
return True
|
||||
|
||||
codes = set(utils.parse_comma_separated_list(codes_str))
|
||||
if self.code in codes or self.code.startswith(tuple(codes)):
|
||||
LOG.debug(
|
||||
"%r is ignored specifically inline with ``# noqa: %s``",
|
||||
self,
|
||||
codes_str,
|
||||
)
|
||||
return True
|
||||
|
||||
LOG.debug(
|
||||
"%r is not ignored inline with ``# noqa: %s``", self, codes_str
|
||||
)
|
||||
return False
|
||||
|
||||
def is_in(self, diff: Dict[str, Set[int]]) -> bool:
|
||||
"""Determine if the violation is included in a diff's line ranges.
|
||||
|
||||
This function relies on the parsed data added via
|
||||
:meth:`~StyleGuide.add_diff_ranges`. If that has not been called and
|
||||
we are not evaluating files in a diff, then this will always return
|
||||
True. If there are diff ranges, then this will return True if the
|
||||
line number in the error falls inside one of the ranges for the file
|
||||
(and assuming the file is part of the diff data). If there are diff
|
||||
ranges, this will return False if the file is not part of the diff
|
||||
data or the line number of the error is not in any of the ranges of
|
||||
the diff.
|
||||
|
||||
:returns:
|
||||
True if there is no diff or if the error is in the diff's line
|
||||
number ranges. False if the error's line number falls outside
|
||||
the diff's line number ranges.
|
||||
:rtype:
|
||||
bool
|
||||
"""
|
||||
if not diff:
|
||||
return True
|
||||
|
||||
# NOTE(sigmavirus24): The parsed diff will be a defaultdict with
|
||||
# a set as the default value (if we have received it from
|
||||
# flake8.utils.parse_unified_diff). In that case ranges below
|
||||
# could be an empty set (which is False-y) or if someone else
|
||||
# is using this API, it could be None. If we could guarantee one
|
||||
# or the other, we would check for it more explicitly.
|
||||
line_numbers = diff.get(self.filename)
|
||||
if not line_numbers:
|
||||
return False
|
||||
|
||||
return self.line_number in line_numbers
|
||||
|
||||
|
||||
class DecisionEngine:
|
||||
"""A class for managing the decision process around violations.
|
||||
|
||||
|
|
|
|||
107
src/flake8/violation.py
Normal file
107
src/flake8/violation.py
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
"""Contains the Violation error class used internally."""
|
||||
import functools
|
||||
import linecache
|
||||
import logging
|
||||
from typing import Dict
|
||||
from typing import Match
|
||||
from typing import NamedTuple
|
||||
from typing import Optional
|
||||
from typing import Set
|
||||
|
||||
from flake8 import defaults
|
||||
from flake8 import utils
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@functools.lru_cache(maxsize=512)
|
||||
def _find_noqa(physical_line: str) -> Optional[Match[str]]:
|
||||
return defaults.NOQA_INLINE_REGEXP.search(physical_line)
|
||||
|
||||
|
||||
class Violation(NamedTuple):
|
||||
"""Class representing a violation reported by Flake8."""
|
||||
|
||||
code: str
|
||||
filename: str
|
||||
line_number: int
|
||||
column_number: int
|
||||
text: str
|
||||
physical_line: Optional[str]
|
||||
|
||||
def is_inline_ignored(self, disable_noqa: bool) -> bool:
|
||||
"""Determine if a comment has been added to ignore this line.
|
||||
|
||||
:param bool disable_noqa:
|
||||
Whether or not users have provided ``--disable-noqa``.
|
||||
:returns:
|
||||
True if error is ignored in-line, False otherwise.
|
||||
:rtype:
|
||||
bool
|
||||
"""
|
||||
physical_line = self.physical_line
|
||||
# TODO(sigmavirus24): Determine how to handle stdin with linecache
|
||||
if disable_noqa:
|
||||
return False
|
||||
|
||||
if physical_line is None:
|
||||
physical_line = linecache.getline(self.filename, self.line_number)
|
||||
noqa_match = _find_noqa(physical_line)
|
||||
if noqa_match is None:
|
||||
LOG.debug("%r is not inline ignored", self)
|
||||
return False
|
||||
|
||||
codes_str = noqa_match.groupdict()["codes"]
|
||||
if codes_str is None:
|
||||
LOG.debug("%r is ignored by a blanket ``# noqa``", self)
|
||||
return True
|
||||
|
||||
codes = set(utils.parse_comma_separated_list(codes_str))
|
||||
if self.code in codes or self.code.startswith(tuple(codes)):
|
||||
LOG.debug(
|
||||
"%r is ignored specifically inline with ``# noqa: %s``",
|
||||
self,
|
||||
codes_str,
|
||||
)
|
||||
return True
|
||||
|
||||
LOG.debug(
|
||||
"%r is not ignored inline with ``# noqa: %s``", self, codes_str
|
||||
)
|
||||
return False
|
||||
|
||||
def is_in(self, diff: Dict[str, Set[int]]) -> bool:
|
||||
"""Determine if the violation is included in a diff's line ranges.
|
||||
|
||||
This function relies on the parsed data added via
|
||||
:meth:`~StyleGuide.add_diff_ranges`. If that has not been called and
|
||||
we are not evaluating files in a diff, then this will always return
|
||||
True. If there are diff ranges, then this will return True if the
|
||||
line number in the error falls inside one of the ranges for the file
|
||||
(and assuming the file is part of the diff data). If there are diff
|
||||
ranges, this will return False if the file is not part of the diff
|
||||
data or the line number of the error is not in any of the ranges of
|
||||
the diff.
|
||||
|
||||
:returns:
|
||||
True if there is no diff or if the error is in the diff's line
|
||||
number ranges. False if the error's line number falls outside
|
||||
the diff's line number ranges.
|
||||
:rtype:
|
||||
bool
|
||||
"""
|
||||
if not diff:
|
||||
return True
|
||||
|
||||
# NOTE(sigmavirus24): The parsed diff will be a defaultdict with
|
||||
# a set as the default value (if we have received it from
|
||||
# flake8.utils.parse_unified_diff). In that case ranges below
|
||||
# could be an empty set (which is False-y) or if someone else
|
||||
# is using this API, it could be None. If we could guarantee one
|
||||
# or the other, we would check for it more explicitly.
|
||||
line_numbers = diff.get(self.filename)
|
||||
if not line_numbers:
|
||||
return False
|
||||
|
||||
return self.line_number in line_numbers
|
||||
|
|
@ -5,9 +5,9 @@ from unittest import mock
|
|||
|
||||
import pytest
|
||||
|
||||
from flake8 import style_guide
|
||||
from flake8.formatting import _windows_color
|
||||
from flake8.formatting import base
|
||||
from flake8.violation import Violation
|
||||
|
||||
|
||||
def options(**kwargs):
|
||||
|
|
@ -48,7 +48,7 @@ def test_format_needs_to_be_implemented():
|
|||
formatter = base.BaseFormatter(options())
|
||||
with pytest.raises(NotImplementedError):
|
||||
formatter.format(
|
||||
style_guide.Violation("A000", "file.py", 1, 1, "error text", None)
|
||||
Violation("A000", "file.py", 1, 1, "error text", None)
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -57,9 +57,7 @@ def test_show_source_returns_nothing_when_not_showing_source():
|
|||
formatter = base.BaseFormatter(options(show_source=False))
|
||||
assert (
|
||||
formatter.show_source(
|
||||
style_guide.Violation(
|
||||
"A000", "file.py", 1, 1, "error text", "line"
|
||||
)
|
||||
Violation("A000", "file.py", 1, 1, "error text", "line")
|
||||
)
|
||||
== ""
|
||||
)
|
||||
|
|
@ -70,7 +68,7 @@ def test_show_source_returns_nothing_when_there_is_source():
|
|||
formatter = base.BaseFormatter(options(show_source=True))
|
||||
assert (
|
||||
formatter.show_source(
|
||||
style_guide.Violation("A000", "file.py", 1, 1, "error text", None)
|
||||
Violation("A000", "file.py", 1, 1, "error text", None)
|
||||
)
|
||||
== ""
|
||||
)
|
||||
|
|
@ -99,7 +97,7 @@ def test_show_source_returns_nothing_when_there_is_source():
|
|||
def test_show_source_updates_physical_line_appropriately(line1, line2, column):
|
||||
"""Ensure the error column is appropriately indicated."""
|
||||
formatter = base.BaseFormatter(options(show_source=True))
|
||||
error = style_guide.Violation("A000", "file.py", 1, column, "error", line1)
|
||||
error = Violation("A000", "file.py", 1, column, "error", line1)
|
||||
output = formatter.show_source(error)
|
||||
assert output == line1 + line2
|
||||
|
||||
|
|
@ -208,7 +206,7 @@ def test_handle_formats_the_error():
|
|||
"""Verify that a formatter will call format from handle."""
|
||||
formatter = FormatFormatter(options(show_source=False))
|
||||
filemock = formatter.output_fd = mock.Mock()
|
||||
error = style_guide.Violation(
|
||||
error = Violation(
|
||||
code="A001",
|
||||
filename="example.py",
|
||||
line_number=1,
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
"""Tests for the FilenameOnly formatter object."""
|
||||
import argparse
|
||||
|
||||
from flake8 import style_guide
|
||||
from flake8.formatting import default
|
||||
from flake8.violation import Violation
|
||||
|
||||
|
||||
def options(**kwargs):
|
||||
|
|
@ -18,16 +18,14 @@ def test_caches_filenames_already_printed():
|
|||
formatter = default.FilenameOnly(options())
|
||||
assert formatter.filenames_already_printed == set()
|
||||
|
||||
formatter.format(
|
||||
style_guide.Violation("code", "file.py", 1, 1, "text", "l")
|
||||
)
|
||||
formatter.format(Violation("code", "file.py", 1, 1, "text", "l"))
|
||||
assert formatter.filenames_already_printed == {"file.py"}
|
||||
|
||||
|
||||
def test_only_returns_a_string_once_from_format():
|
||||
"""Verify format ignores the second error with the same filename."""
|
||||
formatter = default.FilenameOnly(options())
|
||||
error = style_guide.Violation("code", "file.py", 1, 1, "text", "1")
|
||||
error = Violation("code", "file.py", 1, 1, "text", "1")
|
||||
|
||||
assert formatter.format(error) == "file.py"
|
||||
assert formatter.format(error) is None
|
||||
|
|
@ -36,6 +34,6 @@ def test_only_returns_a_string_once_from_format():
|
|||
def test_show_source_returns_nothing():
|
||||
"""Verify show_source returns nothing."""
|
||||
formatter = default.FilenameOnly(options())
|
||||
error = style_guide.Violation("code", "file.py", 1, 1, "text", "1")
|
||||
error = Violation("code", "file.py", 1, 1, "text", "1")
|
||||
|
||||
assert formatter.show_source(error) is None
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
"""Tests for the Nothing formatter obbject."""
|
||||
import argparse
|
||||
|
||||
from flake8 import style_guide
|
||||
from flake8.formatting import default
|
||||
from flake8.violation import Violation
|
||||
|
||||
|
||||
def options(**kwargs):
|
||||
|
|
@ -16,7 +16,7 @@ def options(**kwargs):
|
|||
def test_format_returns_nothing():
|
||||
"""Verify Nothing.format returns None."""
|
||||
formatter = default.Nothing(options())
|
||||
error = style_guide.Violation("code", "file.py", 1, 1, "text", "1")
|
||||
error = Violation("code", "file.py", 1, 1, "text", "1")
|
||||
|
||||
assert formatter.format(error) is None
|
||||
|
||||
|
|
@ -24,6 +24,6 @@ def test_format_returns_nothing():
|
|||
def test_show_source_returns_nothing():
|
||||
"""Verify Nothing.show_source returns None."""
|
||||
formatter = default.Nothing(options())
|
||||
error = style_guide.Violation("code", "file.py", 1, 1, "text", "1")
|
||||
error = Violation("code", "file.py", 1, 1, "text", "1")
|
||||
|
||||
assert formatter.show_source(error) is None
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
import pytest
|
||||
|
||||
from flake8 import statistics as stats
|
||||
from flake8 import style_guide
|
||||
from flake8.violation import Violation
|
||||
|
||||
DEFAULT_ERROR_CODE = "E100"
|
||||
DEFAULT_FILENAME = "file.py"
|
||||
|
|
@ -16,7 +16,7 @@ def make_error(**kwargs):
|
|||
kwargs.setdefault("line_number", 1)
|
||||
kwargs.setdefault("column_number", 1)
|
||||
kwargs.setdefault("text", DEFAULT_TEXT)
|
||||
return style_guide.Violation(**kwargs, physical_line=None)
|
||||
return Violation(**kwargs, physical_line=None)
|
||||
|
||||
|
||||
def test_key_creation():
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
"""Tests for the flake8.style_guide.Violation class."""
|
||||
"""Tests for the flake8.violation.Violation class."""
|
||||
from unittest import mock
|
||||
|
||||
import pytest
|
||||
|
||||
from flake8 import style_guide
|
||||
from flake8.violation import Violation
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
|
|
@ -33,9 +33,7 @@ from flake8 import style_guide
|
|||
)
|
||||
def test_is_inline_ignored(error_code, physical_line, expected_result):
|
||||
"""Verify that we detect inline usage of ``# noqa``."""
|
||||
error = style_guide.Violation(
|
||||
error_code, "filename.py", 1, 1, "error text", None
|
||||
)
|
||||
error = Violation(error_code, "filename.py", 1, 1, "error text", None)
|
||||
# We want `None` to be passed as the physical line so we actually use our
|
||||
# monkey-patched linecache.getline value.
|
||||
|
||||
|
|
@ -45,9 +43,7 @@ def test_is_inline_ignored(error_code, physical_line, expected_result):
|
|||
|
||||
def test_disable_is_inline_ignored():
|
||||
"""Verify that is_inline_ignored exits immediately if disabling NoQA."""
|
||||
error = style_guide.Violation(
|
||||
"E121", "filename.py", 1, 1, "error text", "line"
|
||||
)
|
||||
error = Violation("E121", "filename.py", 1, 1, "error text", "line")
|
||||
|
||||
with mock.patch("linecache.getline") as getline:
|
||||
assert error.is_inline_ignored(True) is False
|
||||
|
|
@ -67,13 +63,8 @@ def test_disable_is_inline_ignored():
|
|||
)
|
||||
def test_violation_is_in_diff(violation_file, violation_line, diff, expected):
|
||||
"""Verify that we find violations within a diff."""
|
||||
violation = style_guide.Violation(
|
||||
"E001",
|
||||
violation_file,
|
||||
violation_line,
|
||||
1,
|
||||
"warning",
|
||||
"line",
|
||||
violation = Violation(
|
||||
"E001", violation_file, violation_line, 1, "warning", "line"
|
||||
)
|
||||
|
||||
assert violation.is_in(diff) is expected
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue