Skip to content

Commit

Permalink
Add GitHub annotations format for --output
Browse files Browse the repository at this point in the history
  • Loading branch information
edgarrmondragon committed Sep 13, 2024
1 parent 72c413d commit 70c8feb
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 1 deletion.
18 changes: 17 additions & 1 deletion mypy/error_formatter.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,20 @@ def report_error(self, error: "MypyError") -> str:
)


OUTPUT_CHOICES = {"json": JSONFormatter()}
class GitHubFormatter(ErrorFormatter):
"""Formatter for GitHub Actions output format."""

def report_error(self, error: "MypyError") -> str:
"""Prints out the errors as GitHub Actions annotations."""
command = "error" if error.severity == "error" else "notice"
code = f"(`{error.errorcode.code}`) " if error.errorcode is not None else ""

result = f"::{command} file={error.file_path},line={error.line},col={error.column}::{code}{error.message}"
if len(error.hints) > 0:
# TODO: Add hints to the output?
pass

return result


OUTPUT_CHOICES = {"json": JSONFormatter(), "github": GitHubFormatter()}
39 changes: 39 additions & 0 deletions mypy/test/testoutput.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,42 @@ def test_output_json(testcase: DataDrivenTestCase) -> None:
normalized_output = [line.replace(test_temp_dir + json_os_separator, "") for line in output]

assert normalized_output == testcase.output


class OutputGitHubsuite(DataSuite):
files = ["outputgithub.test"]

def run_case(self, testcase: DataDrivenTestCase) -> None:
test_output_github(testcase)


def test_output_github(testcase: DataDrivenTestCase) -> None:
"""Run Mypy in a subprocess, and ensure that `--output=github` works as intended."""
mypy_cmdline = ["--output=github"]
mypy_cmdline.append(f"--python-version={'.'.join(map(str, PYTHON3_VERSION))}")

# Write the program to a file.
program_path = os.path.join(test_temp_dir, "main")
mypy_cmdline.append(program_path)
with open(program_path, "w", encoding="utf8") as file:
for s in testcase.input:
file.write(f"{s}\n")

output = []
# Type check the program.
out, err, returncode = api.run(mypy_cmdline)
# split lines, remove newlines, and remove directory of test case
for line in (out + err).rstrip("\n").splitlines():
if line.startswith(test_temp_dir + os.sep):
output.append(line[len(test_temp_dir + os.sep) :].rstrip("\r\n"))
else:
output.append(line.rstrip("\r\n"))

if returncode > 1:
output.append("!!! Mypy crashed !!!")

# Remove temp file.
os.remove(program_path)

normalized_output = [line.replace(test_temp_dir + os.sep, "") for line in output]
assert normalized_output == testcase.output
44 changes: 44 additions & 0 deletions test-data/unit/outputgithub.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
-- Test cases for `--output=json`.
-- These cannot be run by the usual unit test runner because of the backslashes
-- in the output, which get normalized to forward slashes by the test suite on
-- Windows.

[case testOutputGitHubNoIssues]
# flags: --output=github
def foo() -> None:
pass

foo()
[out]

[case testOutputGitHubSimple]
# flags: --output=github
def foo() -> None:
pass

foo(1)
[out]
::error file=main,line=5,col=0::(`call-arg`) Too many arguments for "foo"

[case testOutputGitHubWithHint]
# flags: --output=json
from typing import Optional, overload

@overload
def foo() -> None: ...
@overload
def foo(x: int) -> None: ...

def foo(x: Optional[int] = None) -> None:
...

reveal_type(foo)

foo('42')

def bar() -> None: ...
bar('42')
[out]
::notice file=main,line=12,col=12::(`misc`) Revealed type is "Overload(def (), def (x: builtins.int))"
::error file=main,line=14,col=0::(`call-overload`) No overload variant of "foo" matches argument type "str"
::error file=main,line=17,col=0::(`call-arg`) Too many arguments for "bar"

0 comments on commit 70c8feb

Please sign in to comment.