syd 0.1.6__py3-none-any.whl → 0.1.7__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- syd/__init__.py +3 -3
- syd/flask_deployment/__init__.py +1 -0
- syd/flask_deployment/components.py +376 -363
- syd/flask_deployment/deployer.py +247 -283
- syd/flask_deployment/static/css/viewer.css +82 -0
- syd/flask_deployment/static/js/viewer.js +174 -0
- syd/flask_deployment/templates/base.html +23 -20
- syd/flask_deployment/templates/viewer.html +49 -95
- syd/flask_deployment/testing_principles.md +300 -0
- syd/notebook_deployment/__init__.py +1 -1
- syd/notebook_deployment/_ipympl_deployer.py +258 -0
- syd/notebook_deployment/deployer.py +126 -50
- syd/notebook_deployment/widgets.py +142 -69
- syd/parameters.py +93 -180
- syd/plotly_deployment/__init__.py +1 -0
- syd/plotly_deployment/components.py +531 -0
- syd/plotly_deployment/deployer.py +376 -0
- syd/{interactive_viewer.py → viewer.py} +152 -188
- {syd-0.1.6.dist-info → syd-0.1.7.dist-info}/METADATA +26 -12
- syd-0.1.7.dist-info/RECORD +22 -0
- syd/flask_deployment/static/css/styles.css +0 -39
- syd/flask_deployment/static/js/components.js +0 -51
- syd-0.1.6.dist-info/RECORD +0 -18
- {syd-0.1.6.dist-info → syd-0.1.7.dist-info}/WHEEL +0 -0
- {syd-0.1.6.dist-info → syd-0.1.7.dist-info}/licenses/LICENSE +0 -0
|
@@ -33,14 +33,30 @@ class BaseWidget(Generic[T, W], ABC):
|
|
|
33
33
|
_callbacks: List[Dict[str, Union[Callable, Union[str, List[str]]]]]
|
|
34
34
|
_is_action: bool = False
|
|
35
35
|
|
|
36
|
-
def __init__(
|
|
37
|
-
self
|
|
36
|
+
def __init__(
|
|
37
|
+
self,
|
|
38
|
+
parameter: T,
|
|
39
|
+
continuous: bool = False,
|
|
40
|
+
width: str = "auto",
|
|
41
|
+
margin: str = "3px 0px",
|
|
42
|
+
description_width: str = "initial",
|
|
43
|
+
):
|
|
44
|
+
self._widget = self._create_widget(
|
|
45
|
+
parameter, continuous, width, margin, description_width
|
|
46
|
+
)
|
|
38
47
|
self._updating = False # Flag to prevent circular updates
|
|
39
48
|
# List of callbacks to remember for quick disabling/enabling
|
|
40
49
|
self._callbacks = []
|
|
41
50
|
|
|
42
51
|
@abstractmethod
|
|
43
|
-
def _create_widget(
|
|
52
|
+
def _create_widget(
|
|
53
|
+
self,
|
|
54
|
+
parameter: T,
|
|
55
|
+
continuous: bool,
|
|
56
|
+
width: str = "auto",
|
|
57
|
+
margin: str = "3px 0px",
|
|
58
|
+
description_width: str = "initial",
|
|
59
|
+
) -> W:
|
|
44
60
|
"""Create and return the appropriate ipywidget."""
|
|
45
61
|
pass
|
|
46
62
|
|
|
@@ -105,36 +121,58 @@ class TextWidget(BaseWidget[TextParameter, widgets.Text]):
|
|
|
105
121
|
"""Widget for text parameters."""
|
|
106
122
|
|
|
107
123
|
def _create_widget(
|
|
108
|
-
self,
|
|
124
|
+
self,
|
|
125
|
+
parameter: TextParameter,
|
|
126
|
+
continuous: bool,
|
|
127
|
+
width: str = "auto",
|
|
128
|
+
margin: str = "3px 0px",
|
|
129
|
+
description_width: str = "initial",
|
|
109
130
|
) -> widgets.Text:
|
|
110
131
|
return widgets.Text(
|
|
111
132
|
value=parameter.value,
|
|
112
133
|
description=parameter.name,
|
|
113
134
|
continuous=continuous,
|
|
114
|
-
layout=widgets.Layout(width=
|
|
135
|
+
layout=widgets.Layout(width=width, margin=margin),
|
|
136
|
+
style={"description_width": description_width},
|
|
115
137
|
)
|
|
116
138
|
|
|
117
139
|
|
|
118
|
-
class BooleanWidget(BaseWidget[BooleanParameter, widgets.
|
|
140
|
+
class BooleanWidget(BaseWidget[BooleanParameter, widgets.ToggleButton]):
|
|
119
141
|
"""Widget for boolean parameters."""
|
|
120
142
|
|
|
121
143
|
def _create_widget(
|
|
122
|
-
self,
|
|
123
|
-
|
|
124
|
-
|
|
144
|
+
self,
|
|
145
|
+
parameter: BooleanParameter,
|
|
146
|
+
continuous: bool,
|
|
147
|
+
width: str = "auto",
|
|
148
|
+
margin: str = "3px 0px",
|
|
149
|
+
description_width: str = "initial",
|
|
150
|
+
) -> widgets.ToggleButton:
|
|
151
|
+
return widgets.ToggleButton(
|
|
152
|
+
value=parameter.value,
|
|
153
|
+
description=parameter.name,
|
|
154
|
+
layout=widgets.Layout(width=width, margin=margin),
|
|
155
|
+
style={"description_width": description_width},
|
|
156
|
+
)
|
|
125
157
|
|
|
126
158
|
|
|
127
159
|
class SelectionWidget(BaseWidget[SelectionParameter, widgets.Dropdown]):
|
|
128
160
|
"""Widget for single selection parameters."""
|
|
129
161
|
|
|
130
162
|
def _create_widget(
|
|
131
|
-
self,
|
|
163
|
+
self,
|
|
164
|
+
parameter: SelectionParameter,
|
|
165
|
+
continuous: bool,
|
|
166
|
+
width: str = "auto",
|
|
167
|
+
margin: str = "3px 0px",
|
|
168
|
+
description_width: str = "initial",
|
|
132
169
|
) -> widgets.Dropdown:
|
|
133
170
|
return widgets.Dropdown(
|
|
134
171
|
value=parameter.value,
|
|
135
172
|
options=parameter.options,
|
|
136
173
|
description=parameter.name,
|
|
137
|
-
layout=widgets.Layout(width=
|
|
174
|
+
layout=widgets.Layout(width=width, margin=margin),
|
|
175
|
+
style={"description_width": description_width},
|
|
138
176
|
)
|
|
139
177
|
|
|
140
178
|
def matches_parameter(self, parameter: SelectionParameter) -> bool:
|
|
@@ -158,14 +196,20 @@ class MultipleSelectionWidget(
|
|
|
158
196
|
"""Widget for multiple selection parameters."""
|
|
159
197
|
|
|
160
198
|
def _create_widget(
|
|
161
|
-
self,
|
|
199
|
+
self,
|
|
200
|
+
parameter: MultipleSelectionParameter,
|
|
201
|
+
continuous: bool,
|
|
202
|
+
width: str = "auto",
|
|
203
|
+
margin: str = "3px 0px",
|
|
204
|
+
description_width: str = "initial",
|
|
162
205
|
) -> widgets.SelectMultiple:
|
|
163
206
|
return widgets.SelectMultiple(
|
|
164
207
|
value=parameter.value,
|
|
165
208
|
options=parameter.options,
|
|
166
209
|
description=parameter.name,
|
|
167
210
|
rows=min(len(parameter.options), 4),
|
|
168
|
-
layout=widgets.Layout(width=
|
|
211
|
+
layout=widgets.Layout(width=width, margin=margin),
|
|
212
|
+
style={"description_width": description_width},
|
|
169
213
|
)
|
|
170
214
|
|
|
171
215
|
def matches_parameter(self, parameter: MultipleSelectionParameter) -> bool:
|
|
@@ -189,7 +233,12 @@ class IntegerWidget(BaseWidget[IntegerParameter, widgets.IntSlider]):
|
|
|
189
233
|
"""Widget for integer parameters."""
|
|
190
234
|
|
|
191
235
|
def _create_widget(
|
|
192
|
-
self,
|
|
236
|
+
self,
|
|
237
|
+
parameter: IntegerParameter,
|
|
238
|
+
continuous: bool,
|
|
239
|
+
width: str = "auto",
|
|
240
|
+
margin: str = "3px 0px",
|
|
241
|
+
description_width: str = "initial",
|
|
193
242
|
) -> widgets.IntSlider:
|
|
194
243
|
return widgets.IntSlider(
|
|
195
244
|
value=parameter.value,
|
|
@@ -197,8 +246,8 @@ class IntegerWidget(BaseWidget[IntegerParameter, widgets.IntSlider]):
|
|
|
197
246
|
max=parameter.max_value,
|
|
198
247
|
description=parameter.name,
|
|
199
248
|
continuous=continuous,
|
|
200
|
-
layout=widgets.Layout(width=
|
|
201
|
-
style={"description_width":
|
|
249
|
+
layout=widgets.Layout(width=width, margin=margin),
|
|
250
|
+
style={"description_width": description_width},
|
|
202
251
|
)
|
|
203
252
|
|
|
204
253
|
def matches_parameter(self, parameter: IntegerParameter) -> bool:
|
|
@@ -221,7 +270,12 @@ class FloatWidget(BaseWidget[FloatParameter, widgets.FloatSlider]):
|
|
|
221
270
|
"""Widget for float parameters."""
|
|
222
271
|
|
|
223
272
|
def _create_widget(
|
|
224
|
-
self,
|
|
273
|
+
self,
|
|
274
|
+
parameter: FloatParameter,
|
|
275
|
+
continuous: bool,
|
|
276
|
+
width: str = "auto",
|
|
277
|
+
margin: str = "3px 0px",
|
|
278
|
+
description_width: str = "initial",
|
|
225
279
|
) -> widgets.FloatSlider:
|
|
226
280
|
return widgets.FloatSlider(
|
|
227
281
|
value=parameter.value,
|
|
@@ -230,8 +284,8 @@ class FloatWidget(BaseWidget[FloatParameter, widgets.FloatSlider]):
|
|
|
230
284
|
step=parameter.step,
|
|
231
285
|
description=parameter.name,
|
|
232
286
|
continuous=continuous,
|
|
233
|
-
layout=widgets.Layout(width=
|
|
234
|
-
style={"description_width":
|
|
287
|
+
layout=widgets.Layout(width=width, margin=margin),
|
|
288
|
+
style={"description_width": description_width},
|
|
235
289
|
)
|
|
236
290
|
|
|
237
291
|
def matches_parameter(self, parameter: FloatParameter) -> bool:
|
|
@@ -255,7 +309,12 @@ class IntegerRangeWidget(BaseWidget[IntegerRangeParameter, widgets.IntRangeSlide
|
|
|
255
309
|
"""Widget for integer range parameters."""
|
|
256
310
|
|
|
257
311
|
def _create_widget(
|
|
258
|
-
self,
|
|
312
|
+
self,
|
|
313
|
+
parameter: IntegerRangeParameter,
|
|
314
|
+
continuous: bool,
|
|
315
|
+
width: str = "auto",
|
|
316
|
+
margin: str = "3px 0px",
|
|
317
|
+
description_width: str = "initial",
|
|
259
318
|
) -> widgets.IntRangeSlider:
|
|
260
319
|
return widgets.IntRangeSlider(
|
|
261
320
|
value=parameter.value,
|
|
@@ -263,8 +322,8 @@ class IntegerRangeWidget(BaseWidget[IntegerRangeParameter, widgets.IntRangeSlide
|
|
|
263
322
|
max=parameter.max_value,
|
|
264
323
|
description=parameter.name,
|
|
265
324
|
continuous=continuous,
|
|
266
|
-
layout=widgets.Layout(width=
|
|
267
|
-
style={"description_width":
|
|
325
|
+
layout=widgets.Layout(width=width, margin=margin),
|
|
326
|
+
style={"description_width": description_width},
|
|
268
327
|
)
|
|
269
328
|
|
|
270
329
|
def matches_parameter(self, parameter: IntegerRangeParameter) -> bool:
|
|
@@ -290,7 +349,12 @@ class FloatRangeWidget(BaseWidget[FloatRangeParameter, widgets.FloatRangeSlider]
|
|
|
290
349
|
"""Widget for float range parameters."""
|
|
291
350
|
|
|
292
351
|
def _create_widget(
|
|
293
|
-
self,
|
|
352
|
+
self,
|
|
353
|
+
parameter: FloatRangeParameter,
|
|
354
|
+
continuous: bool,
|
|
355
|
+
width: str = "auto",
|
|
356
|
+
margin: str = "3px 0px",
|
|
357
|
+
description_width: str = "initial",
|
|
294
358
|
) -> widgets.FloatRangeSlider:
|
|
295
359
|
return widgets.FloatRangeSlider(
|
|
296
360
|
value=parameter.value,
|
|
@@ -299,8 +363,8 @@ class FloatRangeWidget(BaseWidget[FloatRangeParameter, widgets.FloatRangeSlider]
|
|
|
299
363
|
step=parameter.step,
|
|
300
364
|
description=parameter.name,
|
|
301
365
|
continuous=continuous,
|
|
302
|
-
layout=widgets.Layout(width=
|
|
303
|
-
style={"description_width":
|
|
366
|
+
layout=widgets.Layout(width=width, margin=margin),
|
|
367
|
+
style={"description_width": description_width},
|
|
304
368
|
)
|
|
305
369
|
|
|
306
370
|
def matches_parameter(self, parameter: FloatRangeParameter) -> bool:
|
|
@@ -323,21 +387,22 @@ class FloatRangeWidget(BaseWidget[FloatRangeParameter, widgets.FloatRangeSlider]
|
|
|
323
387
|
self.value = (low, high)
|
|
324
388
|
|
|
325
389
|
|
|
326
|
-
class UnboundedIntegerWidget(
|
|
327
|
-
BaseWidget[UnboundedIntegerParameter, widgets.BoundedIntText]
|
|
328
|
-
):
|
|
390
|
+
class UnboundedIntegerWidget(BaseWidget[UnboundedIntegerParameter, widgets.IntText]):
|
|
329
391
|
"""Widget for unbounded integer parameters."""
|
|
330
392
|
|
|
331
393
|
def _create_widget(
|
|
332
|
-
self,
|
|
333
|
-
|
|
334
|
-
|
|
394
|
+
self,
|
|
395
|
+
parameter: UnboundedIntegerParameter,
|
|
396
|
+
continuous: bool,
|
|
397
|
+
width: str = "auto",
|
|
398
|
+
margin: str = "3px 0px",
|
|
399
|
+
description_width: str = "initial",
|
|
400
|
+
) -> widgets.IntText:
|
|
401
|
+
return widgets.IntText(
|
|
335
402
|
value=parameter.value,
|
|
336
|
-
min=parameter.min_value,
|
|
337
|
-
max=parameter.max_value,
|
|
338
403
|
description=parameter.name,
|
|
339
|
-
layout=widgets.Layout(width=
|
|
340
|
-
style={"description_width":
|
|
404
|
+
layout=widgets.Layout(width=width, margin=margin),
|
|
405
|
+
style={"description_width": description_width},
|
|
341
406
|
)
|
|
342
407
|
|
|
343
408
|
def matches_parameter(self, parameter: UnboundedIntegerParameter) -> bool:
|
|
@@ -348,28 +413,26 @@ class UnboundedIntegerWidget(
|
|
|
348
413
|
self, parameter: UnboundedIntegerParameter
|
|
349
414
|
) -> None:
|
|
350
415
|
"""Extra updates from the parameter."""
|
|
351
|
-
|
|
352
|
-
self._widget.min = parameter.min_value
|
|
353
|
-
if parameter.max_value is not None:
|
|
354
|
-
self._widget.max = parameter.max_value
|
|
416
|
+
pass
|
|
355
417
|
|
|
356
418
|
|
|
357
|
-
class UnboundedFloatWidget(
|
|
358
|
-
BaseWidget[UnboundedFloatParameter, widgets.BoundedFloatText]
|
|
359
|
-
):
|
|
419
|
+
class UnboundedFloatWidget(BaseWidget[UnboundedFloatParameter, widgets.FloatText]):
|
|
360
420
|
"""Widget for unbounded float parameters."""
|
|
361
421
|
|
|
362
422
|
def _create_widget(
|
|
363
|
-
self,
|
|
364
|
-
|
|
365
|
-
|
|
423
|
+
self,
|
|
424
|
+
parameter: UnboundedFloatParameter,
|
|
425
|
+
continuous: bool,
|
|
426
|
+
width: str = "auto",
|
|
427
|
+
margin: str = "3px 0px",
|
|
428
|
+
description_width: str = "initial",
|
|
429
|
+
) -> widgets.FloatText:
|
|
430
|
+
return widgets.FloatText(
|
|
366
431
|
value=parameter.value,
|
|
367
|
-
min=parameter.min_value,
|
|
368
|
-
max=parameter.max_value,
|
|
369
432
|
step=parameter.step,
|
|
370
433
|
description=parameter.name,
|
|
371
|
-
layout=widgets.Layout(width=
|
|
372
|
-
style={"description_width":
|
|
434
|
+
layout=widgets.Layout(width=width, margin=margin),
|
|
435
|
+
style={"description_width": description_width},
|
|
373
436
|
)
|
|
374
437
|
|
|
375
438
|
def matches_parameter(self, parameter: UnboundedFloatParameter) -> bool:
|
|
@@ -378,10 +441,6 @@ class UnboundedFloatWidget(
|
|
|
378
441
|
|
|
379
442
|
def extra_updates_from_parameter(self, parameter: UnboundedFloatParameter) -> None:
|
|
380
443
|
"""Extra updates from the parameter."""
|
|
381
|
-
if parameter.min_value is not None:
|
|
382
|
-
self._widget.min = parameter.min_value
|
|
383
|
-
if parameter.max_value is not None:
|
|
384
|
-
self._widget.max = parameter.max_value
|
|
385
444
|
self._widget.step = parameter.step
|
|
386
445
|
|
|
387
446
|
|
|
@@ -391,14 +450,18 @@ class ButtonWidget(BaseWidget[ButtonAction, widgets.Button]):
|
|
|
391
450
|
_is_action: bool = True
|
|
392
451
|
|
|
393
452
|
def _create_widget(
|
|
394
|
-
self,
|
|
453
|
+
self,
|
|
454
|
+
parameter: ButtonAction,
|
|
455
|
+
continuous: bool,
|
|
456
|
+
width: str = "auto",
|
|
457
|
+
margin: str = "3px 0px",
|
|
458
|
+
description_width: str = "initial",
|
|
395
459
|
) -> widgets.Button:
|
|
396
460
|
button = widgets.Button(
|
|
397
461
|
description=parameter.label,
|
|
398
|
-
layout=widgets.Layout(width=
|
|
399
|
-
style={"description_width":
|
|
462
|
+
layout=widgets.Layout(width=width, margin=margin),
|
|
463
|
+
style={"description_width": description_width},
|
|
400
464
|
)
|
|
401
|
-
button.on_click(parameter.callback)
|
|
402
465
|
return button
|
|
403
466
|
|
|
404
467
|
def matches_parameter(self, parameter: ButtonAction) -> bool:
|
|
@@ -407,41 +470,45 @@ class ButtonWidget(BaseWidget[ButtonAction, widgets.Button]):
|
|
|
407
470
|
|
|
408
471
|
def extra_updates_from_parameter(self, parameter: ButtonAction) -> None:
|
|
409
472
|
"""Extra updates from the parameter."""
|
|
473
|
+
# Callbacks are handled in the deployer, so the only relevant update is the label
|
|
410
474
|
self._widget.description = parameter.label
|
|
411
|
-
# Update click handler
|
|
412
|
-
self._widget.on_click(parameter.callback, remove=True) # Remove old handler
|
|
413
|
-
self._widget.on_click(parameter.callback) # Add new handler
|
|
414
475
|
|
|
415
476
|
def observe(self, callback: Callable) -> None:
|
|
416
477
|
"""Observe the widget and call the callback when the value changes."""
|
|
478
|
+
if self._callbacks:
|
|
479
|
+
raise ValueError("ButtonWidget already has a callback!")
|
|
417
480
|
self._widget.on_click(callback)
|
|
418
|
-
self._callbacks
|
|
481
|
+
self._callbacks = callback
|
|
419
482
|
|
|
420
|
-
def unobserve(self, callback: Callable
|
|
483
|
+
def unobserve(self, callback: Callable) -> None:
|
|
421
484
|
"""Unobserve the widget and stop calling the callback when the value changes."""
|
|
422
|
-
self._widget.on_click(
|
|
423
|
-
self._callbacks
|
|
485
|
+
self._widget.on_click(callback, remove=True)
|
|
486
|
+
self._callbacks = []
|
|
424
487
|
|
|
425
488
|
def reenable_callbacks(self) -> None:
|
|
426
489
|
"""Reenable all callbacks from the widget."""
|
|
427
|
-
|
|
428
|
-
self._widget.on_click(callback)
|
|
490
|
+
self._widget.on_click(self._callbacks)
|
|
429
491
|
|
|
430
492
|
def disable_callbacks(self) -> None:
|
|
431
493
|
"""Disable all callbacks from the widget."""
|
|
432
|
-
|
|
433
|
-
self._widget.on_click(None)
|
|
494
|
+
self._widget.on_click(self._callbacks, remove=True)
|
|
434
495
|
|
|
435
496
|
|
|
436
497
|
def create_widget(
|
|
437
498
|
parameter: Union[Parameter[Any], ButtonAction],
|
|
438
499
|
continuous: bool = False,
|
|
500
|
+
width: str = "auto",
|
|
501
|
+
margin: str = "3px 0px",
|
|
502
|
+
description_width: str = "initial",
|
|
439
503
|
) -> BaseWidget[Union[Parameter[Any], ButtonAction], widgets.Widget]:
|
|
440
504
|
"""Create and return the appropriate widget for the given parameter.
|
|
441
505
|
|
|
442
506
|
Args:
|
|
443
507
|
parameter: The parameter to create a widget for
|
|
444
508
|
continuous: Whether to update the widget value continuously during user interaction
|
|
509
|
+
width: Width of the widget
|
|
510
|
+
margin: Margin of the widget
|
|
511
|
+
description_width: Width of the description label
|
|
445
512
|
"""
|
|
446
513
|
widget_map = {
|
|
447
514
|
TextParameter: TextWidget,
|
|
@@ -475,4 +542,10 @@ def create_widget(
|
|
|
475
542
|
f"Available types: {[k.__name__ for k in widget_map.keys()]}"
|
|
476
543
|
)
|
|
477
544
|
|
|
478
|
-
return widget_class(
|
|
545
|
+
return widget_class(
|
|
546
|
+
parameter,
|
|
547
|
+
continuous,
|
|
548
|
+
width=width,
|
|
549
|
+
margin=margin,
|
|
550
|
+
description_width=description_width,
|
|
551
|
+
)
|