mirror of
https://github.com/servo/servo.git
synced 2025-07-23 15:23:42 +01:00
Introduce `python/tidy` folder in pyrefly type checker Testing: Manual testing via `./mach test-tidy` command --------- Signed-off-by: Jerens Lensun <jerensslensun@gmail.com> Signed-off-by: Mukilan Thiyagarajan <mukilan@igalia.com> Co-authored-by: Mukilan Thiyagarajan <mukilan@igalia.com>
110 lines
3.6 KiB
Python
110 lines
3.6 KiB
Python
# Copyright 2025 The Servo Project Developers. See the COPYRIGHT
|
|
# file at the top-level directory of this distribution.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
# http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
# <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
# option. This file may not be copied, modified, or distributed
|
|
# except according to those terms.
|
|
|
|
from dataclasses import dataclass
|
|
from typing import Any, Literal, Optional
|
|
|
|
|
|
@dataclass
|
|
class GithubAnnotation:
|
|
file_name: str
|
|
line_start: int
|
|
line_end: int
|
|
level: Literal["notice", "warning", "error"]
|
|
title: str
|
|
message: str
|
|
column_start: Optional[int] = None
|
|
column_end: Optional[int] = None
|
|
|
|
|
|
class GitHubAnnotationManager:
|
|
def __init__(self, annotation_prefix: str, limit: int = 10) -> None:
|
|
self.annotation_prefix: str = annotation_prefix
|
|
self.limit: int = limit
|
|
self.total_count: int = 0
|
|
|
|
def clean_path(self, path: str) -> str:
|
|
return path.removeprefix("./")
|
|
|
|
def escape(self, s: str) -> str:
|
|
return s.replace("\r", "%0D").replace("\n", "%0A")
|
|
|
|
def emit_annotation(
|
|
self,
|
|
title: str,
|
|
message: str,
|
|
file_name: str,
|
|
line_start: int,
|
|
line_end: int | None = None,
|
|
annotation_level: Literal["notice", "warning", "error"] = "error",
|
|
column_start: int | None = None,
|
|
column_end: int | None = None,
|
|
) -> None:
|
|
if self.total_count >= self.limit:
|
|
return
|
|
|
|
if line_end is None:
|
|
line_end = line_start
|
|
|
|
annotation = GithubAnnotation(
|
|
title=f"{self.annotation_prefix}: {self.escape(title)}",
|
|
message=self.escape(message),
|
|
file_name=self.clean_path(file_name),
|
|
line_start=line_start,
|
|
line_end=line_end,
|
|
level=annotation_level,
|
|
column_start=column_start,
|
|
column_end=column_end,
|
|
)
|
|
|
|
line_info = f"line={annotation.line_start},endLine={annotation.line_end},title={annotation.title}"
|
|
|
|
column_info = ""
|
|
if line_start == line_end and annotation.column_end is not None and annotation.column_start is not None:
|
|
column_info = f"col={annotation.column_start},endColumn={annotation.column_end},"
|
|
|
|
print(f"::{annotation.level} file={annotation.file_name},{column_info}{line_info}::{annotation.message}")
|
|
|
|
self.total_count += 1
|
|
|
|
def emit_annotations_for_clippy(self, data: list[dict[str, Any]]) -> None:
|
|
severenty_map: dict[str, Literal["notice", "warning", "error"]] = {
|
|
"help": "notice",
|
|
"note": "notice",
|
|
"warning": "warning",
|
|
"error": "error",
|
|
}
|
|
|
|
for item in data:
|
|
if self.total_count >= self.limit:
|
|
break
|
|
|
|
message = item.get("message")
|
|
if not message:
|
|
continue
|
|
|
|
spans = message.get("spans") or []
|
|
primary_span = next((span for span in spans if span.get("is_primary")), None)
|
|
if not primary_span:
|
|
continue
|
|
|
|
annotation_level = severenty_map.get(message.get("level"), "error")
|
|
title = message.get("message", "")
|
|
rendered_message = message.get("rendered", "")
|
|
|
|
self.emit_annotation(
|
|
title,
|
|
rendered_message,
|
|
primary_span["file_name"],
|
|
primary_span["line_start"],
|
|
primary_span["line_end"],
|
|
annotation_level,
|
|
primary_span["column_start"],
|
|
primary_span["column_end"],
|
|
)
|