10
10
import requests
11
11
from simple_term_menu import TerminalMenu # type: ignore
12
12
13
+ from . import swatbotrest
13
14
from . import swatbuild
14
15
from . import utils
15
16
from .webrequests import Session
27
28
WHITE = "\x1b [1;37m"
28
29
29
30
31
+ class _Highlight :
32
+ # pylint: disable=too-few-public-methods
33
+ def __init__ (self , keyword : str , color : str , in_menu : bool ):
34
+ self .keyword = keyword
35
+ self .color = color
36
+ self .in_menu = in_menu
37
+
38
+
39
+ class _Filter :
40
+ # pylint: disable=too-few-public-methods
41
+ def __init__ (self , pat : re .Pattern , enabled : bool , color : Optional [str ],
42
+ in_menu : bool ):
43
+ self .pat = pat
44
+ self .enabled = enabled
45
+ self .color = color
46
+ self .in_menu = in_menu
47
+
48
+ def match (self , line : str ) -> tuple [bool , Optional [_Highlight ]]:
49
+ """Check if the filter matches a given line."""
50
+ if not self .enabled :
51
+ return (False , None )
52
+
53
+ match = self .pat .match (line )
54
+ if not match :
55
+ return (False , None )
56
+
57
+ if not self .color :
58
+ return (True , None )
59
+
60
+ hl = _Highlight (match .group ("keyword" ), self .color , self .in_menu )
61
+ return (True , hl )
62
+
63
+
30
64
def show_logs_menu (build : swatbuild .Build ) -> bool :
31
65
"""Show a menu allowing to select log file to analyze."""
32
66
def get_failure_line (failure , logname ):
@@ -51,22 +85,22 @@ def get_failure_line(failure, logname):
51
85
52
86
53
87
def _format_log_line (linenum : int , text : str , colorized_line : Optional [int ],
54
- highlight_lines : dict [int , tuple [ str , str ] ]):
88
+ highlight_lines : dict [int , _Highlight ]):
55
89
if linenum == colorized_line :
56
90
if linenum in highlight_lines :
57
- linecolor = highlight_lines [linenum ][ 1 ]
91
+ linecolor = highlight_lines [linenum ]. color
58
92
else :
59
93
linecolor = CYAN
60
94
text = f"{ linecolor } { text } { RESET } "
61
95
elif linenum in highlight_lines :
62
- pat = highlight_lines [linenum ][ 0 ]
63
- color = highlight_lines [linenum ][ 1 ]
96
+ pat = highlight_lines [linenum ]. keyword
97
+ color = highlight_lines [linenum ]. color
64
98
text = re .sub (pat , f"{ color } { pat } { RESET } " , text )
65
99
return text
66
100
67
101
68
102
def _format_log_preview_line (linenum : int , text : str , colorized_line : int ,
69
- highlight_lines : dict [int , tuple [ str , str ] ]):
103
+ highlight_lines : dict [int , _Highlight ]):
70
104
preview_text = text .replace ('\t ' , ' ' )
71
105
formatted_text = _format_log_line (linenum , preview_text , colorized_line ,
72
106
highlight_lines )
@@ -85,33 +119,55 @@ def _get_preview_window(linenum: int, lines: list[str], preview_height: int
85
119
86
120
87
121
def _format_log_preview (linenum : int , lines : list [str ],
88
- highlight_lines : dict [int , tuple [ str , str ] ],
122
+ highlight_lines : dict [int , _Highlight ],
89
123
preview_height : int ) -> str :
90
124
start , end = _get_preview_window (linenum , lines , preview_height )
91
125
lines = [_format_log_preview_line (i , t , linenum , highlight_lines )
92
126
for i , t in enumerate (lines [start : end ], start = start + 1 )]
93
127
return "\n " .join (lines )
94
128
95
129
96
- def _get_log_highlights (loglines : list [str ]) -> dict [int , tuple [str , str ]]:
97
- pats = [(re .compile (r"(.*\s|^)(?P<keyword>\S*error):" , flags = re .I ),
98
- RED ),
99
- (re .compile (r"(.*\s|^)(?P<keyword>\S*warning):" , flags = re .I ),
100
- YELLOW ),
101
- ]
130
+ def _get_log_highlights (loglines : list [str ], failure : swatbuild .Failure
131
+ ) -> dict [int , _Highlight ]:
132
+ status = failure .build .status
133
+ test = failure .build .test
134
+ filters = [
135
+ # Toaster specific rules:
136
+ # - Do nothing on "except xxxError:" (likely python code output).
137
+ # - Match on "selenium .*exception:".
138
+ # - Match on generic errors, but do not show in menu.
139
+ _Filter (re .compile (r".*except\s*\S*error:" , flags = re .I ),
140
+ test == "toaster" , None , False ),
141
+ _Filter (re .compile (r"(.*\s|^)(?P<keyword>selenium\.\S*exception):" ,
142
+ flags = re .I ),
143
+ test == "toaster" , RED , status == swatbotrest .Status .ERROR ),
144
+ _Filter (re .compile (r"(.*\s|^)(?P<keyword>\S*error):" , flags = re .I ),
145
+ test == "toaster" , RED , status == swatbotrest .Status .ERROR ),
146
+
147
+ # Generic rules:
148
+ # - Match on "error:", show in menu if build status is error.
149
+ # - Match on "warning:", show in menu if build status is warning.
150
+ _Filter (re .compile (r"(.*\s|^)(?P<keyword>\S*error):" , flags = re .I ),
151
+ True , RED , status == swatbotrest .Status .ERROR ),
152
+ _Filter (re .compile (r"(.*\s|^)(?P<keyword>\S*warning):" ,
153
+ flags = re .I ),
154
+ True , YELLOW , status == swatbotrest .Status .WARNING ),
155
+ ]
102
156
103
157
highlight_lines = {}
104
158
for linenum , line in enumerate (loglines , start = 1 ):
105
- for (pat , color ) in pats :
106
- match = pat .match (line )
107
- if match :
108
- highlight_lines [linenum ] = (match .group ("keyword" ), color )
159
+ for filtr in filters :
160
+ matched , highlight = filtr .match (line )
161
+ if matched :
162
+ if highlight :
163
+ highlight_lines [linenum ] = highlight
164
+ break
109
165
110
166
return highlight_lines
111
167
112
168
113
169
def _show_log (loglines : list [str ], selected_line : Optional [int ],
114
- highlight_lines : dict [int , tuple [ str , str ] ],
170
+ highlight_lines : dict [int , _Highlight ],
115
171
preview_height : Optional [int ]):
116
172
colorlines = [_format_log_line (i , t , selected_line , highlight_lines )
117
173
for i , t in enumerate (loglines , start = 1 )]
@@ -150,20 +206,21 @@ def show_log_menu(failure: swatbuild.Failure, logname: str) -> bool:
150
206
151
207
utils .clear ()
152
208
loglines = logdata .splitlines ()
153
- highlight_lines = _get_log_highlights (loglines )
209
+ highlights = _get_log_highlights (loglines , failure )
154
210
155
211
entries = ["View entire log file|" ,
156
212
"View entire log file in default editor|" ,
157
- * [f"On line { line : 6d} : { highlight_lines [line ][0 ]} |{ line } "
158
- for line in sorted (highlight_lines )]
213
+ * [f"On line { line : 6d} : { highlights [line ].keyword } |{ line } "
214
+ for line in sorted (highlights )
215
+ if highlights [line ].in_menu ]
159
216
]
160
217
161
218
preview_size = 0.6
162
219
termheight = shutil .get_terminal_size ((80 , 20 )).lines
163
220
preview_height = int (preview_size * termheight )
164
221
165
222
def preview (line ):
166
- return _format_log_preview (int (line ), loglines , highlight_lines ,
223
+ return _format_log_preview (int (line ), loglines , highlights ,
167
224
preview_height )
168
225
169
226
title = f"{ failure .build .format_short_description ()} : " \
@@ -178,9 +235,9 @@ def preview(line):
178
235
return True
179
236
180
237
if entry == 0 :
181
- _show_log (loglines , None , highlight_lines , None )
238
+ _show_log (loglines , None , highlights , None )
182
239
elif entry == 1 :
183
240
utils .launch_in_system_defaultshow_in_less (logdata )
184
241
else :
185
242
_ , _ , num = entries [entry ].partition ('|' )
186
- _show_log (loglines , int (num ), highlight_lines , preview_height )
243
+ _show_log (loglines , int (num ), highlights , preview_height )
0 commit comments