Coverage for src / competitive_verifier / log.py: 60%
52 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-03-05 16:00 +0000
« prev ^ index » next coverage.py v7.13.1, created at 2026-03-05 16:00 +0000
1import pathlib
2import sys
3from contextlib import contextmanager
4from dataclasses import asdict, dataclass
5from logging import (
6 DEBUG,
7 ERROR,
8 INFO,
9 NOTSET,
10 WARNING,
11 Handler,
12 LogRecord,
13 basicConfig,
14)
15from typing import TextIO
17import colorlog
18from colorama import Fore, Style
20from competitive_verifier import github
23@dataclass
24class GitHubMessageParams:
25 title: str | None = None
26 file: str | pathlib.Path | None = None
27 col: int | None = None
28 end_column: int | None = None
29 line: int | None = None
30 end_line: int | None = None
33class GitHubActionsHandler(Handler):
34 def __init__(self, level: int | str = NOTSET, stream: TextIO = sys.stderr) -> None:
35 super().__init__(level)
36 self.stream = stream
38 def emit(self, record: LogRecord) -> None:
39 if record.levelno >= ERROR:
40 command = "error"
41 elif record.levelno >= WARNING:
42 command = "warning"
43 elif record.levelno >= INFO:
44 command = "notice"
45 else:
46 if record.levelno >= DEBUG:
47 other_name = (
48 ""
49 if record.name.startswith("competitive_verifier")
50 else f"{record.name}:{record.lineno}:"
51 )
52 github.debug(other_name + record.getMessage(), stream=self.stream)
53 return
55 if (gh := getattr(record, "github", None)) is None or not isinstance(
56 gh, GitHubMessageParams
57 ):
58 return
60 if not gh.title:
61 gh.title = record.name
63 github.message(command, record.getMessage(), stream=sys.stderr, **asdict(gh))
66def configure_stderr_logging(
67 default_level: int | None = None,
68) -> None: # pragma: no cover
69 colorlog_handler = colorlog.StreamHandler(sys.stderr)
70 colorlog_handler.setLevel(default_level or WARNING)
71 colorlog_handler.setFormatter(
72 colorlog.ColoredFormatter(
73 "%(log_color)s%(levelname)s%(reset)s:%(name)s:%(lineno)d:%(message)s"
74 )
75 )
77 handlers: list[Handler] = [colorlog_handler]
79 if github.env.is_in_github_actions():
80 handlers.append(GitHubActionsHandler())
82 basicConfig(
83 level=NOTSET,
84 handlers=handlers,
85 )
88def _console_group(category: str, *, title: str, file: TextIO | None):
89 print(
90 (
91 f"<------------- {Fore.CYAN}{category}:"
92 f"{Fore.YELLOW}{title}"
93 f"{Style.RESET_ALL} ------------->"
94 ),
95 file=file,
96 )
99@contextmanager
100def group(title: str, *, stream: TextIO | None = None):
101 if stream is None:
102 stream = sys.stderr
103 if github.env.is_in_github_actions():
104 try:
105 github.begin_group(title, stream=stream)
106 yield
107 finally:
108 github.end_group(stream=stream)
109 else:
110 try:
111 _console_group(" Start group", title=title, file=stream)
112 yield
113 finally:
114 _console_group("Finish group", title=title, file=stream)