Skip to content

Commit 47637a7

Browse files
authored
Add docstring for jupyterviz make_user_input that documents supported inputs (#1784)
* Add docstring for make_user_input that documents supported inputs * Use field name as user input fallback label; error on unsupported type * Preliminary unit tests for jupyter viz make_user_input method * Remove unused variable flagged by ruff lint
1 parent fb81c1a commit 47637a7

File tree

2 files changed

+82
-19
lines changed

2 files changed

+82
-19
lines changed

mesa/experimental/jupyter_viz.py

Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,10 @@ def JupyterViz(
3232

3333
# 1. User inputs
3434
user_inputs = {}
35-
for k, v in model_params_input.items():
36-
user_input = solara.use_reactive(v["value"])
37-
user_inputs[k] = user_input.value
38-
make_user_input(user_input, k, v)
35+
for name, options in model_params_input.items():
36+
user_input = solara.use_reactive(options["value"])
37+
user_inputs[name] = user_input.value
38+
make_user_input(user_input, name, options)
3939

4040
# 2. Model
4141
def make_model():
@@ -142,29 +142,44 @@ def check_param_is_fixed(param):
142142
return True
143143

144144

145-
def make_user_input(user_input, k, v):
146-
if v["type"] == "SliderInt":
145+
def make_user_input(user_input, name, options):
146+
"""Initialize a user input for configurable model parameters.
147+
Currently supports :class:`solara.SliderInt`, :class:`solara.SliderFloat`,
148+
and :class:`solara.Select`.
149+
150+
Args:
151+
user_input: :class:`solara.reactive` object with initial value
152+
name: field name; used as fallback for label if 'label' is not in options
153+
options: dictionary with options for the input, including label,
154+
min and max values, and other fields specific to the input type.
155+
"""
156+
# label for the input is "label" from options or name
157+
label = options.get("label", name)
158+
input_type = options.get("type")
159+
if input_type == "SliderInt":
147160
solara.SliderInt(
148-
v.get("label", "label"),
161+
label,
149162
value=user_input,
150-
min=v.get("min"),
151-
max=v.get("max"),
152-
step=v.get("step"),
163+
min=options.get("min"),
164+
max=options.get("max"),
165+
step=options.get("step"),
153166
)
154-
elif v["type"] == "SliderFloat":
167+
elif input_type == "SliderFloat":
155168
solara.SliderFloat(
156-
v.get("label", "label"),
169+
label,
157170
value=user_input,
158-
min=v.get("min"),
159-
max=v.get("max"),
160-
step=v.get("step"),
171+
min=options.get("min"),
172+
max=options.get("max"),
173+
step=options.get("step"),
161174
)
162-
elif v["type"] == "Select":
175+
elif input_type == "Select":
163176
solara.Select(
164-
v.get("label", "label"),
165-
value=v.get("value"),
166-
values=v.get("values"),
177+
label,
178+
value=options.get("value"),
179+
values=options.get("values"),
167180
)
181+
else:
182+
raise ValueError(f"{input_type} is not a supported input type")
168183

169184

170185
def make_space(model, agent_portrayal):

tests/test_jupyter_viz.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import unittest
2+
from unittest.mock import patch
3+
4+
from mesa.experimental.jupyter_viz import make_user_input
5+
6+
7+
class TestMakeUserInput(unittest.TestCase):
8+
def test_unsupported_type(self):
9+
"""unsupported input type should raise ValueError"""
10+
# bogus type
11+
with self.assertRaisesRegex(ValueError, "not a supported input type"):
12+
make_user_input(10, "input", {"type": "bogus"})
13+
# no type is specified
14+
with self.assertRaisesRegex(ValueError, "not a supported input type"):
15+
make_user_input(10, "input", {})
16+
17+
@patch("mesa.experimental.jupyter_viz.solara")
18+
def test_slider_int(self, mock_solara):
19+
value = 10
20+
name = "num_agents"
21+
options = {
22+
"type": "SliderInt",
23+
"label": "number of agents",
24+
"min": 10,
25+
"max": 20,
26+
"step": 1,
27+
}
28+
make_user_input(value, name, options)
29+
mock_solara.SliderInt.assert_called_with(
30+
options["label"],
31+
value=value,
32+
min=options["min"],
33+
max=options["max"],
34+
step=options["step"],
35+
)
36+
37+
@patch("mesa.experimental.jupyter_viz.solara")
38+
def test_label_fallback(self, mock_solara):
39+
"""name should be used as fallback label"""
40+
value = 10
41+
name = "num_agents"
42+
options = {
43+
"type": "SliderInt",
44+
}
45+
make_user_input(value, name, options)
46+
mock_solara.SliderInt.assert_called_with(
47+
name, value=value, min=None, max=None, step=None
48+
)

0 commit comments

Comments
 (0)