syd 1.2.0__py3-none-any.whl → 1.2.2__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 CHANGED
@@ -1,4 +1,4 @@
1
- __version__ = "1.2.0"
1
+ __version__ = "1.2.2"
2
2
 
3
3
  from .viewer import make_viewer, Viewer
4
4
  from .support import show_open_servers, close_servers
@@ -252,6 +252,7 @@ class FlaskDeployer:
252
252
  with plot_context():
253
253
  # Use the viewer's plot method with its current state
254
254
  fig = self.viewer.plot(self.viewer.state)
255
+ self.viewer._figure = fig
255
256
  if not isinstance(fig, mpl.figure.Figure):
256
257
  raise TypeError(
257
258
  f"viewer.plot() must return a matplotlib Figure, but got {type(fig)}"
@@ -310,6 +311,7 @@ class FlaskDeployer:
310
311
  return jsonify({"error": f"Parameter '{name}' not found"}), 404
311
312
 
312
313
  parameter = self.viewer.parameters[name]
314
+ replot = True
313
315
 
314
316
  # Optionally suppress warnings during updates
315
317
  with warnings.catch_warnings():
@@ -323,6 +325,7 @@ class FlaskDeployer:
323
325
  if isinstance(parameter, ButtonAction) and parameter.callback:
324
326
  # Pass the current state dictionary to the callback
325
327
  parameter.callback(self.viewer.state)
328
+ replot = parameter.replot
326
329
  else:
327
330
  app.logger.warning(
328
331
  f"Received action request for non-action parameter: {name}"
@@ -351,7 +354,12 @@ class FlaskDeployer:
351
354
  for name, param in self.viewer.parameters.items()
352
355
  }
353
356
  return jsonify(
354
- {"success": True, "state": final_state, "params": final_param_info}
357
+ {
358
+ "success": True,
359
+ "state": final_state,
360
+ "params": final_param_info,
361
+ "replot": replot,
362
+ }
355
363
  )
356
364
 
357
365
  except Exception as e:
@@ -48,7 +48,7 @@ export async function updateParameterOnServer(name, value, action = false) {
48
48
  if (!response.ok) {
49
49
  throw new Error(`HTTP error! status: ${response.status}`);
50
50
  }
51
- return await response.json(); // Return the server response (likely includes updated state)
51
+ return await response.json(); // Return the server response
52
52
  } catch (error) {
53
53
  console.error('Error updating parameter:', error);
54
54
  updateStatus('Error updating parameter');
@@ -76,8 +76,9 @@ export function handleButtonClick(name) {
76
76
  } else {
77
77
  // Update state with any changes from callbacks
78
78
  updateStateFromServer(data.state, data.params);
79
- // Update plot if needed (plot.js handles this now)
80
- updatePlot();
79
+ if (data.replot) {
80
+ updatePlot();
81
+ }
81
82
  updateStatus('Ready!');
82
83
  }
83
84
  })
@@ -244,15 +244,18 @@ class NotebookDeployer:
244
244
  # If the component is an action, call the callback
245
245
  parameter = self.viewer.parameters[name]
246
246
  parameter.callback(self.viewer.state)
247
+ replot = parameter.replot
247
248
  else:
248
249
  # Otherwise, update the parameter value
249
250
  self.viewer.set_parameter_value(name, component.value)
251
+ replot = True
250
252
 
251
253
  # Update any components that changed due to dependencies
252
254
  self.sync_components_with_state()
253
255
 
254
256
  # Update the plot
255
- self.update_plot()
257
+ if replot:
258
+ self.update_plot()
256
259
 
257
260
  def sync_components_with_state(self, exclude: Optional[str] = None) -> None:
258
261
  """Sync component values with viewer state."""
@@ -270,6 +273,7 @@ class NotebookDeployer:
270
273
 
271
274
  with plot_context():
272
275
  figure = self.viewer.plot(state)
276
+ self.viewer._figure = figure
273
277
 
274
278
  # Update components if plot function updated a parameter
275
279
  self.sync_components_with_state()
@@ -296,7 +300,13 @@ class NotebookDeployer:
296
300
  display(figure.canvas)
297
301
 
298
302
  else:
299
- raise ValueError(f"Unsupported backend type: {self.backend_type}")
303
+ display(figure)
304
+ plt.close(figure)
305
+ print(
306
+ f"Backend type: ({self.backend_type}) is not explicitly supported."
307
+ "If you encounter weird behavior, try restarting with '%matplotlib inline' or '%matplotlib widget'."
308
+ "And please report this issue on github please :)."
309
+ )
300
310
 
301
311
  if store_figure:
302
312
  self._last_figure = figure
syd/parameters.py CHANGED
@@ -1305,12 +1305,15 @@ class ButtonAction(Parameter[None]):
1305
1305
  Text to display on the button (default is the button's name)
1306
1306
  callback : callable
1307
1307
  Function to execute when the button is clicked
1308
+ replot : bool, optional
1309
+ Whether to replot the figure after the callback is called.
1310
+ (default: True)
1308
1311
 
1309
1312
  Examples
1310
1313
  --------
1311
1314
  >>> def print_hello():
1312
1315
  ... print("Hello!")
1313
- >>> button = ButtonAction("greeting", label="Say Hello", callback=print_hello)
1316
+ >>> button = ButtonAction("greeting", label="Say Hello", callback=print_hello, replot=False)
1314
1317
  >>> button.callback() # Simulates clicking the button
1315
1318
  Hello!
1316
1319
  >>> # Update the button's label and callback
@@ -1323,14 +1326,14 @@ class ButtonAction(Parameter[None]):
1323
1326
  Notes
1324
1327
  -----
1325
1328
  Unlike other Parameter types, ButtonAction:
1326
- - Has no value (always None)
1327
- - Is marked as an action (_is_action = True)
1329
+ - Has no value (always None, therefore cannot be updated through the value property
1328
1330
  - Executes code directly rather than storing state
1329
- - Cannot be updated through the value property
1331
+ - Has an option to turn off replotting after the callback is called for cases where you want to access the last figure only.
1330
1332
  """
1331
1333
 
1332
1334
  label: str
1333
1335
  callback: Callable
1336
+ replot: bool
1334
1337
  value: None = field(default=None, repr=False)
1335
1338
  _is_action: bool = field(default=True, repr=False)
1336
1339
 
@@ -1339,6 +1342,7 @@ class ButtonAction(Parameter[None]):
1339
1342
  name: str,
1340
1343
  label: Union[str, NoInitialValue],
1341
1344
  callback: Callable,
1345
+ replot: bool = True,
1342
1346
  ):
1343
1347
  """
1344
1348
  Initialize a button.
@@ -1351,12 +1355,16 @@ class ButtonAction(Parameter[None]):
1351
1355
  Text to display on the button (default is the button's name)
1352
1356
  callback : callable
1353
1357
  Function to execute when the button is clicked
1358
+ replot : bool, optional
1359
+ Whether to replot the figure after the callback is called.
1360
+ (default: True)
1354
1361
  """
1355
1362
  self.name = name
1356
1363
  if isinstance(label, NoInitialValue):
1357
1364
  label = name
1358
1365
  self.label = label
1359
1366
  self.callback = callback
1367
+ self.replot = replot
1360
1368
  self._value = None
1361
1369
 
1362
1370
  def _validate(self, new_value: Any) -> None:
@@ -1371,6 +1379,12 @@ class ButtonAction(Parameter[None]):
1371
1379
  type(self).__name__,
1372
1380
  f"Callback {self.callback} is not callable",
1373
1381
  )
1382
+ if not isinstance(self.replot, bool):
1383
+ raise ParameterUpdateError(
1384
+ self.name,
1385
+ type(self).__name__,
1386
+ f"Replet must be a boolean, got {type(self.replot)}",
1387
+ )
1374
1388
  try:
1375
1389
  str(self.label)
1376
1390
  except Exception:
syd/support.py CHANGED
@@ -1,5 +1,5 @@
1
1
  from abc import ABCMeta
2
- from typing import Any, List
2
+ from typing import Any, List, Union
3
3
  from warnings import warn
4
4
  from contextlib import contextmanager
5
5
  import matplotlib.pyplot as plt
@@ -12,7 +12,7 @@ def show_open_servers():
12
12
  print(server_manager.servers)
13
13
 
14
14
 
15
- def close_servers(port: int | None = None):
15
+ def close_servers(port: Union[int, None] = None):
16
16
  """Close any Flask servers running on a given port (or all of them).
17
17
 
18
18
  Parameters
syd/viewer.py CHANGED
@@ -157,12 +157,14 @@ class Viewer:
157
157
  >>> viewer = MyViewer()
158
158
  >>> viewer.add_float('x', value=1.0, min=0, max=10)
159
159
  >>> viewer.on_change('x', viewer.update_based_on_x)
160
+ >>> viewer.show()
160
161
  """
161
162
 
162
163
  parameters: Dict[str, Parameter]
163
164
  callbacks: Dict[str, List[Callable]]
164
165
  _app_deployed: bool
165
166
  _in_callbacks: bool
167
+ _figure: Figure
166
168
 
167
169
  def __new__(cls, *args, **kwargs):
168
170
  instance = super().__new__(cls)
@@ -170,6 +172,7 @@ class Viewer:
170
172
  instance.callbacks = {}
171
173
  instance._app_deployed = False
172
174
  instance._in_callbacks = False
175
+ instance._figure = None
173
176
  return instance
174
177
 
175
178
  @property
@@ -196,6 +199,13 @@ class Viewer:
196
199
  if not param._is_action
197
200
  }
198
201
 
202
+ @property
203
+ def figure(self) -> Figure:
204
+ """
205
+ Get the last opened figure. Returns None if no figure has been opened yet.
206
+ """
207
+ return self._figure
208
+
199
209
  def plot(self, state: Dict[str, Any]) -> Figure:
200
210
  """Create and return a matplotlib figure.
201
211
 
@@ -942,6 +952,7 @@ class Viewer:
942
952
  *,
943
953
  label: Union[str, NoInitialValue] = NO_INITIAL_VALUE,
944
954
  callback: Callable[[], None],
955
+ replot: bool = True,
945
956
  ) -> None:
946
957
  """
947
958
  Add a button parameter to the viewer.
@@ -959,16 +970,24 @@ class Viewer:
959
970
  If not provided, the parameter's label will be set to the name.
960
971
  callback : callable
961
972
  Function to call when the button is clicked (takes state as a single argument)
973
+ replot : bool, optional
974
+ Whether to replot the figure after the callback is called.
975
+ (default: True)
962
976
 
963
977
  Examples
964
978
  --------
965
- >>> def reset_plot(state):
966
- ... print("Resetting plot...")
967
- >>> viewer.add_button('reset', label='Reset Plot', callback=reset_plot)
979
+ >>> def save_figure(state):
980
+ ... print("Saving figure...")
981
+ ... viewer.figure.savefig('last_figure.png')
982
+ >>> viewer.add_button('save', label='Save Figure', callback=save_figure, replot=False)
968
983
 
969
984
  >>> def print_plot_info(state):
970
985
  ... print(f"Current plot info: {state['plot_info']}")
971
- >>> viewer.add_button('print_info', label='Print Plot Info', callback=print_plot_info)
986
+ >>> viewer.add_button('print_info', label='Print Plot Info', callback=print_plot_info, replot=False)
987
+
988
+ >>> def reset_plot(state):
989
+ ... print("Resetting plot...")
990
+ >>> viewer.add_button('reset', label='Reset Plot', callback=reset_plot)
972
991
  """
973
992
  try:
974
993
  callback = self._prepare_function(
@@ -976,7 +995,7 @@ class Viewer:
976
995
  context="Setting button callback:",
977
996
  )
978
997
 
979
- new_param = ActionType.button.value(name, label, callback)
998
+ new_param = ActionType.button.value(name, label, callback, replot)
980
999
  except Exception as e:
981
1000
  raise ParameterAddError(name, "button", str(e)) from e
982
1001
  else:
@@ -1398,6 +1417,7 @@ class Viewer:
1398
1417
  *,
1399
1418
  label: Union[str, NoUpdate] = NO_UPDATE,
1400
1419
  callback: Union[Callable[[], None], NoUpdate] = NO_UPDATE,
1420
+ replot: Union[bool, NoUpdate] = NO_UPDATE,
1401
1421
  ) -> None:
1402
1422
  """
1403
1423
  Update a button parameter's label and/or callback function.
@@ -1413,6 +1433,9 @@ class Viewer:
1413
1433
  New text to display on the button (if not provided, no change)
1414
1434
  callback : Union[callable, NoUpdate], optional
1415
1435
  New function to call when clicked (if not provided, no change)
1436
+ replot : Union[bool, NoUpdate], optional
1437
+ Whether to replot the figure after the callback is called.
1438
+ (default: True)
1416
1439
 
1417
1440
  Examples
1418
1441
  --------
@@ -1420,7 +1443,8 @@ class Viewer:
1420
1443
  ... print("New action...")
1421
1444
  >>> viewer.update_button('reset',
1422
1445
  ... label='New Action!',
1423
- ... callback=new_callback)
1446
+ ... callback=new_callback,
1447
+ ... replot=False)
1424
1448
  """
1425
1449
  updates = {}
1426
1450
  if not label == NO_UPDATE:
@@ -1439,5 +1463,7 @@ class Viewer:
1439
1463
  ) from e
1440
1464
  else:
1441
1465
  updates["callback"] = callback
1466
+ if not replot == NO_UPDATE:
1467
+ updates["replot"] = replot
1442
1468
  if updates:
1443
1469
  self.parameters[name].update(updates)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: syd
3
- Version: 1.2.0
3
+ Version: 1.2.2
4
4
  Summary: A Python package for making GUIs for data science easy.
5
5
  Project-URL: Homepage, https://github.com/landoskape/syd
6
6
  Author-email: Andrew Landau <andrew+tyler+landau+getridofthisanddtheplusses@gmail.com>
@@ -1,16 +1,16 @@
1
- syd/__init__.py,sha256=ln2sVWmAbfLzF0oqYPE0g2TYdIqms9_Va984I0eBfiU,117
2
- syd/parameters.py,sha256=dlnYOVsi1CDtC2toVECf0kNBRipVrtUjr6XVX86b5MA,42886
3
- syd/support.py,sha256=WVvcKKHWChZWJewc9-vwSChGwmzXobxU5pNmrjrsC3k,6770
4
- syd/viewer.py,sha256=c53tJlnbnTCGDJEYhiuKZ76tU7Dcd7S8VWY_1O8aAN0,51692
1
+ syd/__init__.py,sha256=mg2549tBmySVzaA-BQPGC2aEUzur6Cw_ppeM_najE8g,117
2
+ syd/parameters.py,sha256=lzCHSMGYEdQ_FMbcXnntdPF1XvCm7fybB6CNy3TFn04,43543
3
+ syd/support.py,sha256=LoHOsTN0byyDxk9uibjM0BKnI-vAJ4aU-tWILMCXz1Y,6783
4
+ syd/viewer.py,sha256=d1-uca7WPEoH8InDUg1osK5uQU6RQ0QXydwS_81EME0,52681
5
5
  syd/flask_deployment/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
6
- syd/flask_deployment/deployer.py,sha256=8W51sUZx7iUtFTseNacRa2XOgQVOGGR96nBf8NZS2Qg,29287
6
+ syd/flask_deployment/deployer.py,sha256=GruWJ3pA6NI8tmMavQo4Sl8_jvZbFi1k8LGPPd2JkmU,29554
7
7
  syd/flask_deployment/testing_principles.md,sha256=GyULM97sDeie8h3tSPoduOckdMNGyWuwm1RdHo5jzK0,10130
8
8
  syd/flask_deployment/static/__init__.py,sha256=ieWE8NKR-APw7h4Ge0ooZGk6wZrneSSs_1cMyTPbQSA,65
9
9
  syd/flask_deployment/static/css/styles.css,sha256=GQgIPbWMdfTgU-HN80FCObA8bd1FPiwa1Dq0yRANsPc,5944
10
10
  syd/flask_deployment/static/css/viewer.css,sha256=rViWJ4w6vahuOr0RIpowgJfg7HN4TzMAuYrQmqhi1V0,805
11
11
  syd/flask_deployment/static/js/old_viewer.js,sha256=aIh0gxrwMWUYVge4taj0pucg2RiCcWVuV9ekBvUVsPU,40594
12
12
  syd/flask_deployment/static/js/viewer.js,sha256=D-I0hUZQPMSIgB9b8taWNVIsBBUeK1VpLjWI1KtOgSo,2672
13
- syd/flask_deployment/static/js/modules/api.js,sha256=WTx4SRIzsCLyB0yD94j836KR4npJPEJdW9pNKw5gsHc,3160
13
+ syd/flask_deployment/static/js/modules/api.js,sha256=4qZW9DKH-IqslOKj4ypV5Vju9wr9Qfhckf7BQGk1KqM,3117
14
14
  syd/flask_deployment/static/js/modules/config.js,sha256=XxvyNhOUoyXXcacmhNzawKZiy9Q473sY98y7WipofLs,850
15
15
  syd/flask_deployment/static/js/modules/plot.js,sha256=dHZAaw_hjrrMa1IGet8HhYJvB0Ze3KoFjmB6duuhFhg,2622
16
16
  syd/flask_deployment/static/js/modules/state.js,sha256=-sfQ9ppDgr0p3XO5_oN3g-waf1-j0iwSfSSGS_lIK70,3133
@@ -20,9 +20,9 @@ syd/flask_deployment/static/js/modules/utils.js,sha256=xqIBRvTrZCnm65xZ9R6mqeHFn
20
20
  syd/flask_deployment/templates/__init__.py,sha256=ieWE8NKR-APw7h4Ge0ooZGk6wZrneSSs_1cMyTPbQSA,65
21
21
  syd/flask_deployment/templates/index.html,sha256=OmGaEdEPZ_ALPq7ZHk47jw-ZX9pOUUk57M3gXT0V1Lk,1608
22
22
  syd/notebook_deployment/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
23
- syd/notebook_deployment/deployer.py,sha256=ELtYiR6ac8Rg0I1Hh8_K-w5j_oZIAXf-nDazWvbD3Uc,12225
23
+ syd/notebook_deployment/deployer.py,sha256=ovuFvj5_qXB5WToNhCxrEHmPuKYypIeLqrUg0KjovQs,12706
24
24
  syd/notebook_deployment/widgets.py,sha256=ptys7exVA6NCF4eCRZMTPJblT0ZbtPdN4o2A0Yh5Cfc,20781
25
- syd-1.2.0.dist-info/METADATA,sha256=_5dekAuzzkgmS8UjpD42Bu6dVRKfM2Jyimuv8LudNHM,14934
26
- syd-1.2.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
27
- syd-1.2.0.dist-info/licenses/LICENSE,sha256=YF6QR6Vjxcg5b_sYIyqkME7FZYau5TfEUGTG-0JeRK0,35129
28
- syd-1.2.0.dist-info/RECORD,,
25
+ syd-1.2.2.dist-info/METADATA,sha256=ZkI2pU-Mc190FwszroKAWFgai0ZnAtpMXa_Z8s3Yfts,14934
26
+ syd-1.2.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
27
+ syd-1.2.2.dist-info/licenses/LICENSE,sha256=YF6QR6Vjxcg5b_sYIyqkME7FZYau5TfEUGTG-0JeRK0,35129
28
+ syd-1.2.2.dist-info/RECORD,,
File without changes