Skip to content

Commit fb3cde1

Browse files
authored
Results of checks are now collected before handling (#64)
When multiple checks were handled only the output of the last check remained. This is because all checks were individually handled and each handling cleared the output. We now collect the check result including the exceptions and then send it for handling. We also now only print a summary of the results in the CheckRegistry as the output was too extensive.
1 parent acbd25c commit fb3cde1

File tree

6 files changed

+128
-99
lines changed

6 files changed

+128
-99
lines changed

src/scwidgets/check/__init__.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@
55
assert_shape,
66
assert_type,
77
)
8-
from ._check import AssertResult, Check, ChecksResult
8+
from ._check import AssertResult, Check, CheckResult
99
from ._widget_check_registry import CheckableWidget, CheckRegistry
1010

1111
__all__ = [
1212
"Check",
13-
"ChecksResult",
13+
"CheckResult",
1414
"AssertResult",
1515
"CheckRegistry",
1616
"CheckableWidget",

src/scwidgets/check/_check.py

+3-9
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ def compute_outputs(self):
181181
def compute_and_set_references(self):
182182
self._outputs_references = self.compute_outputs()
183183

184-
def check_function(self) -> ChecksResult:
184+
def check_function(self) -> CheckResult:
185185
"""
186186
Returns for each input (first depth list) the result message for each assert
187187
(second depth list). If a result message is empty, the assert was successful,
@@ -199,7 +199,7 @@ def check_function(self) -> ChecksResult:
199199
f"[{len(self._inputs_parameters)} != {len(self._outputs_references)}]."
200200
)
201201

202-
check_result = ChecksResult()
202+
check_result = CheckResult()
203203
for i, input_parameters in enumerate(self._inputs_parameters):
204204
output = self._function_to_check(**input_parameters)
205205
if not (isinstance(output, tuple)):
@@ -265,7 +265,7 @@ def check_function(self) -> ChecksResult:
265265
return check_result
266266

267267

268-
class ChecksResult:
268+
class CheckResult:
269269
def __init__(self):
270270
self._assert_results = []
271271
self._assert_names = []
@@ -288,12 +288,6 @@ def append(
288288
self._inputs_parameters.append(input_parameters)
289289
self._suppress_assert_messages.append(suppress_assert_message)
290290

291-
def extend(self, check_results: ChecksResult):
292-
self._assert_results.extend(check_results._assert_results)
293-
self._assert_names.extend(check_results._assert_names)
294-
self._inputs_parameters.extend(check_results._inputs_parameters)
295-
self._suppress_assert_messages.extend(check_results._suppress_assert_messages)
296-
297291
@property
298292
def successful(self):
299293
return (

src/scwidgets/check/_widget_check_registry.py

+59-41
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from ipywidgets import Button, HBox, Layout, Output, VBox, Widget
99

1010
from .._utils import Formatter
11-
from ._check import Check, ChecksResult
11+
from ._check import Check, CheckResult
1212

1313

1414
class CheckableWidget:
@@ -38,7 +38,9 @@ def compute_output_to_check(
3838
"""
3939
raise NotImplementedError("compute_output_to_check has not been implemented")
4040

41-
def handle_checks_result(self, result: Union[ChecksResult, Exception]) -> None:
41+
def handle_checks_result(
42+
self, results: List[Union[CheckResult, Exception]]
43+
) -> None:
4244
"""
4345
Function that controls how results of the checks are handled.
4446
"""
@@ -102,7 +104,7 @@ def compute_and_set_references(self):
102104

103105
self._check_registry.compute_and_set_references(self)
104106

105-
def check(self) -> Union[ChecksResult, Exception]:
107+
def check(self) -> List[Union[CheckResult, Exception]]:
106108
if self._check_registry is None:
107109
raise ValueError(
108110
"No check registry given on initialization, " "check cannot be used"
@@ -220,44 +222,47 @@ def compute_and_set_references(self, widget: Widget):
220222
try:
221223
check.compute_and_set_references()
222224
except Exception as exception:
223-
widget.handle_checks_result(exception)
225+
widget.handle_checks_result([exception])
224226
raise exception
225227

226228
def compute_outputs(self, widget: CheckableWidget):
227229
for check in self._checks[widget]:
228230
try:
229231
return check.compute_outputs()
230232
except Exception as exception:
231-
widget.handle_checks_result(exception)
233+
widget.handle_checks_result([exception])
232234
raise exception
233235

234236
def compute_and_set_all_references(self):
235237
for widget in self._checks.keys():
236238
self.compute_and_set_references(widget)
237239

238-
def check_widget(self, widget: CheckableWidget) -> Union[ChecksResult, Exception]:
240+
def check_widget(
241+
self, widget: CheckableWidget
242+
) -> List[Union[CheckResult, Exception]]:
243+
checks_result = []
239244
try:
240-
results = ChecksResult()
241245
for check in self._checks[widget]:
242246
result = check.check_function()
243-
results.extend(result)
244-
widget.handle_checks_result(result)
245-
return results
247+
checks_result.append(result)
248+
widget.handle_checks_result(checks_result)
249+
return checks_result
246250
except Exception as exception:
247-
widget.handle_checks_result(exception)
248-
return exception
251+
checks_result.append(exception)
252+
widget.handle_checks_result(checks_result)
253+
return checks_result
249254

250255
def check_all_widgets(
251256
self,
252-
) -> OrderedDict[CheckableWidget, Union[ChecksResult, Exception]]:
253-
messages: OrderedDict[CheckableWidget, Union[ChecksResult, Exception]] = (
257+
) -> OrderedDict[CheckableWidget, List[Union[CheckResult, Exception]]]:
258+
messages: OrderedDict[CheckableWidget, List[Union[CheckResult, Exception]]] = (
254259
OrderedDict()
255260
)
256261
for widget in self._checks.keys():
257262
try:
258263
messages[widget] = self.check_widget(widget)
259264
except Exception as exception:
260-
messages[widget] = exception
265+
messages[widget] = [exception]
261266
return messages
262267

263268
def _on_click_set_all_references_button(self, change: dict):
@@ -276,47 +281,60 @@ def _on_click_check_all_widgets_button(self, change: dict):
276281
widgets_results = self.check_all_widgets()
277282
for widget, widget_results in widgets_results.items():
278283
with self._output:
279-
if isinstance(widget_results, Exception):
284+
if wrong_types := [
285+
result
286+
for result in widget_results
287+
if not (
288+
isinstance(result, Exception)
289+
or isinstance(result, CheckResult)
290+
)
291+
]:
292+
raise ValueError(
293+
f"Not supported result type {type(wrong_types[0])}. "
294+
"Only results of type `Exception` and `CheckResult` "
295+
"are supported."
296+
)
297+
elif [
298+
result
299+
for result in widget_results
300+
if isinstance(result, Exception)
301+
]:
280302
print(
281303
Formatter.color_error_message(
282304
Formatter.format_title_message(
283-
f"Widget {self._names[widget]} " f"raised error:"
305+
f"Widget {self._names[widget]} raised error."
284306
)
285307
)
286308
)
287-
raise widget_results
288-
elif isinstance(widget_results, ChecksResult):
289-
if widget_results.successful:
290-
print(
291-
Formatter.color_success_message(
292-
Formatter.format_title_message(
293-
f"Widget {self._names[widget]} all checks "
294-
f"were successful"
295-
)
309+
310+
elif not [
311+
result
312+
for result in widget_results
313+
if isinstance(result, CheckResult) and not result.successful
314+
]:
315+
print(
316+
Formatter.color_success_message(
317+
Formatter.format_title_message(
318+
f"Widget {self._names[widget]} all checks "
319+
f"were successful."
296320
)
297321
)
298-
print(widget_results.message())
299-
else:
300-
print(
301-
Formatter.color_error_message(
302-
Formatter.format_title_message(
303-
f"Widget {self._names[widget]} not all checks "
304-
"were successful:"
305-
)
322+
)
323+
else:
324+
print(
325+
Formatter.color_error_message(
326+
Formatter.format_title_message(
327+
f"Widget {self._names[widget]} not all checks "
328+
"were successful."
306329
)
307330
)
308-
print(widget_results.message())
309-
else:
310-
raise ValueError(
311-
f"Not supported result type {type(widget_results)}. "
312-
"Only results of type `Exception` and `CheckResult` "
313-
"are supported."
314331
)
315332
except Exception as exception:
316333
with self._output:
317334
print(
318335
Formatter.color_error_message(
319336
"Error raised while checking widgets:"
320-
)
337+
),
338+
exception,
321339
)
322340
raise exception

src/scwidgets/exercise/_widget_code_exercise.py

+24-17
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from widget_code_input.utils import CodeValidationError
1212

1313
from .._utils import Formatter
14-
from ..check import Check, CheckableWidget, CheckRegistry, ChecksResult
14+
from ..check import Check, CheckableWidget, CheckRegistry, CheckResult
1515
from ..code._widget_code_input import CodeInput
1616
from ..code._widget_parameter_panel import ParameterPanel
1717
from ..cue import (
@@ -527,7 +527,7 @@ def _on_click_check_action(self) -> bool:
527527
raised_error = False
528528
with self._output:
529529
try:
530-
self.check()
530+
self._check()
531531
except Exception as e:
532532
raised_error = True
533533
if python_version() >= "3.11":
@@ -571,29 +571,35 @@ def _on_click_load_action(self) -> bool:
571571
raise e
572572
return not (raised_error)
573573

574-
def check(self) -> Union[ChecksResult, Exception]:
575-
self._output.clear_output(wait=True)
574+
def _check(self) -> List[Union[CheckResult, Exception]]:
576575
return CheckableWidget.check(self)
577576

577+
def run_check(self) -> None:
578+
if self._check_button is not None:
579+
self._check_button.click()
580+
else:
581+
self._on_click_check_action()
582+
578583
def compute_output_to_check(self, *args, **kwargs) -> Check.FunOutParamsT:
579584
return self.run_code(*args, **kwargs)
580585

581-
def handle_checks_result(self, result: Union[ChecksResult, Exception]):
586+
def handle_checks_result(self, results: List[Union[CheckResult, Exception]]):
582587
self._output.clear_output(wait=True)
583588
with self._output:
584-
if isinstance(result, Exception):
585-
raise result
586-
elif isinstance(result, ChecksResult):
587-
if result.successful:
588-
print(Formatter.color_success_message("Check was successful"))
589-
print(Formatter.color_success_message("--------------------"))
590-
print(result.message())
589+
for result in results:
590+
if isinstance(result, Exception):
591+
raise result
592+
elif isinstance(result, CheckResult):
593+
if result.successful:
594+
print(Formatter.color_success_message("Check was successful"))
595+
print(Formatter.color_success_message("--------------------"))
596+
print(result.message())
597+
else:
598+
print(Formatter.color_error_message("Check failed"))
599+
print(Formatter.color_error_message("------------"))
600+
print(result.message())
591601
else:
592-
print(Formatter.color_error_message("Check failed"))
593-
print(Formatter.color_error_message("------------"))
594-
print(result.message())
595-
else:
596-
print(result)
602+
print(result)
597603

598604
def handle_save_result(self, result: Union[str, Exception]):
599605
self._output.clear_output(wait=True)
@@ -671,6 +677,7 @@ def run_update(self):
671677
parameter is changed
672678
"""
673679
if self._update_button is not None:
680+
# to also invoke the reset cue action, we click the cued button
674681
self._update_button.click()
675682
else:
676683
# we might be in update_mode "release" or "continuous" where no button is

0 commit comments

Comments
 (0)