Skip to content

Commit

Permalink
Change API in CodeExercise related to cue_outputs
Browse files Browse the repository at this point in the history
`cue_outputs` has been changed to `outputs`.

The `CodeExercice` has a new property `output` which is the first
element of `outputs`:.

Now non `CueOutput` objects can be passed that are converted to
`CueFigure` or `CueObject` depending on the type.
  • Loading branch information
agoscinski committed Nov 28, 2024
1 parent ee0e7b7 commit c077b5b
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 21 deletions.
54 changes: 38 additions & 16 deletions src/scwidgets/exercise/_widget_code_exercise.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from typing import Any, Callable, Dict, List, Optional, Union

from ipywidgets import HTML, Box, HBox, HTMLMath, Layout, VBox, Widget
from matplotlib.figure import Figure
from widget_code_input import WidgetCodeInput
from widget_code_input.utils import CodeValidationError

Expand All @@ -18,6 +19,8 @@
from ..cue import (
CheckCueBox,
CheckResetCueButton,
CueFigure,
CueObject,
CueOutput,
SaveCueBox,
SaveResetCueButton,
Expand Down Expand Up @@ -47,8 +50,8 @@ class CodeExercise(VBox, CheckableWidget, ExerciseWidget):
Determines how the parameters are refreshed on changes of the code input
or parameters
:param cue_outputs:
List of CueOuputs that are drawn an refreshed
:param outputs:
List of CueOuputs that are drawn and refreshed
:param update_func:
A function that is run during the update process. The function takes as argument
Expand All @@ -66,7 +69,7 @@ def __init__(
Union[Dict[str, Union[Check.FunInParamT, Widget]], ParameterPanel]
] = None,
update_mode: str = "release",
cue_outputs: Union[None, CueOutput, List[CueOutput]] = None,
outputs: Union[None, Figure, CueOutput, List[CueOutput]] = None,
update_func: Optional[
Union[
Callable[[CodeExercise], Union[Any, Check.FunOutParamsT]],
Expand Down Expand Up @@ -170,11 +173,6 @@ def __init__(
"Code and parameters do no match: " + compatibility_result
)

if cue_outputs is None:
cue_outputs = []
elif not (isinstance(cue_outputs, list)):
cue_outputs = [cue_outputs]

CheckableWidget.__init__(self, check_registry, exercise_key)
ExerciseWidget.__init__(self, exercise_registry, exercise_key)

Expand All @@ -190,7 +188,20 @@ def __init__(
self._parameter_panel = None

self._cue_code = self._code
self._cue_outputs = cue_outputs

if outputs is None:
outputs = []
elif not (isinstance(outputs, list)):
outputs = [outputs]

self._cue_outputs: List[CueOutput] = []
for output in outputs:
if isinstance(output, Figure):
self._cue_outputs.append(CueFigure(output))
elif isinstance(output, CueOutput):
self._cue_outputs.append(output)
else:
self._cue_outputs.append(CueObject(output))

if self._check_registry is None or self._code is None:
self._check_button = None
Expand Down Expand Up @@ -289,17 +300,24 @@ def __init__(
cue_output._widgets_to_observe = [
self._code
] + self._parameter_panel.parameters_widget
cue_output._traits_to_observe = [
"function_body"
] + self._parameter_panel.parameters_trait
# fmt: off
cue_output._traits_to_observe = (

[ # type: ignore[assignment]
"function_body"
]
+ self._parameter_panel.parameters_trait
)
# fmt: on

cue_output.observe_widgets()
else:
# TODO this has to be made public
cue_output._widgets_to_observe = (
self._parameter_panel.parameters_widget
)
cue_output._traits_to_observe = (
self._parameter_panel.parameters_trait
self._parameter_panel.parameters_trait # type: ignore[assignment] # noqa: E501
)
cue_output.observe_widgets()
elif self._code is not None:
Expand Down Expand Up @@ -659,7 +677,11 @@ def code(self):
return self._code

@property
def cue_outputs(self):
def output(self) -> CueOutput | None:
return self._cue_outputs[0] if len(self._cue_outputs) > 0 else None

@property
def outputs(self) -> List[CueOutput]:
return self._cue_outputs

def _on_click_update_action(self) -> bool:
Expand All @@ -668,7 +690,7 @@ def _on_click_update_action(self) -> bool:
# runs code and displays output
with self._output:
try:
for cue_output in self.cue_outputs:
for cue_output in self.outputs:
if hasattr(cue_output, "clear_display"):
cue_output.clear_display(wait=True)

Expand All @@ -680,7 +702,7 @@ def _on_click_update_action(self) -> bool:
elif self._code is not None:
self.run_code(**self.params)

for cue_output in self.cue_outputs:
for cue_output in self.outputs:
if hasattr(cue_output, "draw_display"):
cue_output.draw_display()

Expand Down
10 changes: 5 additions & 5 deletions tests/test_code.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,19 +123,19 @@ def get_code_exercise(

def update_print():
output = code_ex.run_code(**code_ex.params)
code_ex.cue_outputs[0].display_object = f"Output:\n{output}"
code_ex.output.display_object = f"Output:\n{output}"

else:

def update_print(code_ex: CodeExercise):
output = code_ex.run_code(**code_ex.params)
code_ex.cue_outputs[0].display_object = f"Output:\n{output}"
code_ex.output.display_object = f"Output:\n{output}"

code_ex = CodeExercise(
code=code_input,
check_registry=CheckRegistry() if include_checks is True else None,
parameters=parameters if include_params is True else None,
cue_outputs=[CueObject("Not initialized")],
outputs=[CueObject("Not initialized")],
update_func=update_print,
update_mode=update_mode,
)
Expand Down Expand Up @@ -260,7 +260,7 @@ def test_save_registry(self, function):
"""

def print_success(code_ex: CodeExercise | None):
code_ex.cue_outputs[0].display_object = "Success"
code_ex.output.display_object = "Success"

cue_output = CueObject("Not initialized")
exercise_registry = ExerciseRegistry()
Expand Down Expand Up @@ -310,7 +310,7 @@ def mock_clear_output(wait):
# Be aware that the raised error is captured in the code_ex._output
# To debug failures in the test you have to manually run it in debug
# mode and execute `code_ex._update_button.click()` Redirecting stderr
# does des not workc
# does des not work
code_ex.run_update()
assert "Output:\n0" in buffer.getvalue()

Expand Down

0 comments on commit c077b5b

Please sign in to comment.