Skip to content

Commit

Permalink
Add error code to Mypy message
Browse files Browse the repository at this point in the history
  • Loading branch information
patrick91 committed Apr 5, 2022
1 parent c84d6df commit a8ff757
Show file tree
Hide file tree
Showing 5 changed files with 37 additions and 19 deletions.
6 changes: 3 additions & 3 deletions mypy_silent/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from mypy_silent.maho import add_type_ignore_comment
from mypy_silent.maho import remove_type_ignore_comment
from mypy_silent.parser import FilePosition
from mypy_silent.parser import get_info_form_mypy_output
from mypy_silent.parser import get_info_from_mypy_output
from mypy_silent.parser import UNUSED_IGNORE_MESSAGES
from mypy_silent.utils import get_lines

Expand All @@ -17,7 +17,7 @@ def mypy_silent(
),
) -> None:
lines = get_lines(mypy_output_file)
infos = get_info_form_mypy_output(lines)
infos = get_info_from_mypy_output(lines)
processed: Set[FilePosition] = set()
for info in infos:
if info.position in processed:
Expand All @@ -29,7 +29,7 @@ def mypy_silent(
if info.message in UNUSED_IGNORE_MESSAGES:
new_content = remove_type_ignore_comment(old_content)
else:
new_content = add_type_ignore_comment(old_content)
new_content = add_type_ignore_comment(old_content, error_code=info.error_code)
file_contents[info.position.line - 1] = new_content
with open(info.position.filename, "w") as f:
f.writelines(file_contents)
Expand Down
15 changes: 12 additions & 3 deletions mypy_silent/maho.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
def add_type_ignore_comment(line: str) -> str:
from typing import Optional


def add_type_ignore_comment(line: str, error_code: Optional[str]) -> str:
# Workarounds for https://mypy.readthedocs.io/en/stable/common_issues.html#silencing-linters

type_ignore_comment = "# type: ignore"

if error_code:
type_ignore_comment += f"[{error_code}]"

if "# noqa" in line:
return line.replace("# noqa", "# type: ignore # noqa", 1)
return line.replace("# noqa", f"{type_ignore_comment} # noqa", 1)
content_without_crlf = line.rstrip("\r\n")
return content_without_crlf + " # type: ignore" + line[len(content_without_crlf) :]
return content_without_crlf + f" {type_ignore_comment}" + line[len(content_without_crlf) :]


def remove_type_ignore_comment(line: str) -> str:
Expand Down
17 changes: 9 additions & 8 deletions mypy_silent/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,11 @@
from typing import FrozenSet
from typing import Iterable
from typing import NamedTuple
from typing import Optional

from typing_extensions import Final

UNUSED_IGNORE_MESSAGES: Final[FrozenSet[str]] = frozenset(
{"error: unused 'type: ignore' comment", 'error: unused "type: ignore" comment'}
)
UNUSED_IGNORE_MESSAGES: Final[FrozenSet[str]] = frozenset({"error: unused 'type: ignore' comment", 'error: unused "type: ignore" comment'})


class FilePosition(NamedTuple):
Expand All @@ -18,20 +17,22 @@ class FilePosition(NamedTuple):
class MypyMessage(NamedTuple):
position: FilePosition
message: str
error_code: Optional[str]


_mypy_output_re = re.compile(r"^([^:]+):(\d+):(.+)$")
_mypy_output_re = re.compile(r"^(?P<filename>[^:]+):(?P<line>\d+):(?P<message>.+?)(\[(?P<error_code>[a-z-]+)\])?$")


def get_info_form_mypy_output(lines: Iterable[str]) -> Iterable[MypyMessage]:
def get_info_from_mypy_output(lines: Iterable[str]) -> Iterable[MypyMessage]:
for line in lines:
line = line.strip()
match = _mypy_output_re.match(line)
if match:
yield MypyMessage(
position=FilePosition(
filename=match.group(1).strip(),
line=int(match.group(2)),
filename=match.group("filename").strip(),
line=int(match.group("line")),
),
message=match.group(3).strip(),
message=match.group("message").strip(),
error_code=match.group("error_code"),
)
8 changes: 7 additions & 1 deletion tests/test_maho.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,13 @@
),
)
def test_add_type_ignore_comment(input: str, output: str) -> None:
assert add_type_ignore_comment(input) == output
assert add_type_ignore_comment(input, error_code=None) == output


def test_add_type_ignore_comment_with_error_code() -> None:
input = "host, port, protocol = m.groups()\r\n"
output = "host, port, protocol = m.groups() # type: ignore[misc]\r\n"
assert add_type_ignore_comment(input, error_code="misc") == output


@pytest.mark.parametrize(
Expand Down
10 changes: 6 additions & 4 deletions tests/test_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import pytest

from mypy_silent.parser import FilePosition
from mypy_silent.parser import get_info_form_mypy_output
from mypy_silent.parser import get_info_from_mypy_output
from mypy_silent.parser import MypyMessage


Expand All @@ -14,20 +14,22 @@
(
[
"utils/template.py:49: error: Cannot assign to a method",
"utils/template.py:53: error: Statement is unreachable",
"utils/template.py:53: error: Statement is unreachable [unreachable]",
],
[
MypyMessage(
position=FilePosition(filename="utils/template.py", line=49),
message="error: Cannot assign to a method",
error_code=None,
),
MypyMessage(
position=FilePosition(filename="utils/template.py", line=53),
message="error: Statement is unreachable",
error_code="unreachable",
),
],
),
),
)
def test_get_info_form_mypy_output(input: List[str], output: List[MypyMessage]) -> None:
assert list(get_info_form_mypy_output(input)) == output
def test_get_info_from_mypy_output(input: List[str], output: List[MypyMessage]) -> None:
assert list(get_info_from_mypy_output(input)) == output

0 comments on commit a8ff757

Please sign in to comment.