You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I've been building a toy extension of Anywidget that will eventually be used in another project. During development, while testing in VSCode, I've noticed that a Dict traitlet I'm using only syncs to the backend on the 2nd and subsequent updates to the traitlet. In other words, the first time the traitlet is updated from the frontend, it does not sync to the backend (despite a call to model.save_changes()) but it does sync on subsequent calls to model.save_changes(). The reference to the dictionary (object on the frontend) is being updated (via structuredClone()).
Even stranger, if the 2nd change doesn't actually change the contents of the dictionary (IE setting a key to the value it already has), the sync will not occur on the 2nd change, even if the 1st change actually altered a value in the dictionary. This persists for repeated meaningless changes; if you continue to set key-value pairs that already exist in the dictionary, no sync will occur even though the first key was substantively changed, so the frontend and backend remain out of sync (again, model.save_changes() is called each time) I'm confused by this behavior, as the way that I've wrapped the Anywidget model always changes the reference to the dictionary, so a new object with one different key-value pair is passed to the traitlet on each call to save_changes().
I understand this could be a traitlet bug; LMK if I should submit it there instead.
I've written up a README there which should walk you through the installation and build for that repo; LMK if you have any issues and I'll get back to you ASAP.
I neglected to push my logging up but it's pretty easy: if you look at DataStoreWidget.data_changed(), you can add logging to see whether the traitlet value on the backend changes or not. Once you add logging where you feel like, run the first two cells of test.ipynb. You'll see a widget with two text fields and a set button. The first text field is the key, 2nd is the value. Clicking 'set' syncs to the backend and saves the dictionary in a file. If you do that, then restart your kernel, then try to set the same key to a different value, you'll notice no sync occurs (even though I've set this up to load your old dict in from the file). You can validate that the dict changes in the frontend by logging in data_store.ts. LMK if you have any issues with that too!
Finally, the latest commit on that repo is my workaround fix: on receipt of the model object from the backend, I simply set the dict to {key: 'value'}, run save_changes(), set the dict back to what it came as, then run save_changes() again. This skips the first non-syncing change and gets things flowing. Incredibly, I've had to filter that change out in the data_changed handler on the backend, as it does come through as a first change to the traitlet. If you remove that (lines 50-51 of DataStoreWidget.py) you'll see the first change come through... but only in that scenario. Figured I'd include all that as the workaround may help you isolate the bug
Describe the bug
I've been building a toy extension of Anywidget that will eventually be used in another project. During development, while testing in VSCode, I've noticed that a
Dict
traitlet I'm using only syncs to the backend on the 2nd and subsequent updates to the traitlet. In other words, the first time the traitlet is updated from the frontend, it does not sync to the backend (despite a call tomodel.save_changes()
) but it does sync on subsequent calls tomodel.save_changes()
. The reference to the dictionary (object on the frontend) is being updated (viastructuredClone()
).Even stranger, if the 2nd change doesn't actually change the contents of the dictionary (IE setting a key to the value it already has), the sync will not occur on the 2nd change, even if the 1st change actually altered a value in the dictionary. This persists for repeated meaningless changes; if you continue to set key-value pairs that already exist in the dictionary, no sync will occur even though the first key was substantively changed, so the frontend and backend remain out of sync (again,
model.save_changes()
is called each time) I'm confused by this behavior, as the way that I've wrapped the Anywidget model always changes the reference to the dictionary, so a new object with one different key-value pair is passed to the traitlet on each call tosave_changes()
.I understand this could be a traitlet bug; LMK if I should submit it there instead.
Reproduction
My toy widget with the bug can be found at https://github.com/NateLanza/Jupyter-file-storage/tree/f2d81ac63dc42e9953ee627797cd8a4c86e773e8
I've written up a README there which should walk you through the installation and build for that repo; LMK if you have any issues and I'll get back to you ASAP.
I neglected to push my logging up but it's pretty easy: if you look at
DataStoreWidget.data_changed()
, you can add logging to see whether the traitlet value on the backend changes or not. Once you add logging where you feel like, run the first two cells oftest.ipynb
. You'll see a widget with two text fields and a set button. The first text field is the key, 2nd is the value. Clicking 'set' syncs to the backend and saves the dictionary in a file. If you do that, then restart your kernel, then try to set the same key to a different value, you'll notice no sync occurs (even though I've set this up to load your old dict in from the file). You can validate that the dict changes in the frontend by logging indata_store.ts
. LMK if you have any issues with that too!Finally, the latest commit on that repo is my workaround fix: on receipt of the
model
object from the backend, I simply set the dict to{key: 'value'}
, runsave_changes()
, set the dict back to what it came as, then runsave_changes()
again. This skips the first non-syncing change and gets things flowing. Incredibly, I've had to filter that change out in thedata_changed
handler on the backend, as it does come through as a first change to the traitlet. If you remove that (lines 50-51 ofDataStoreWidget.py
) you'll see the first change come through... but only in that scenario. Figured I'd include all that as the workaround may help you isolate the bugLogs
System Info
Severity
annoyance
The text was updated successfully, but these errors were encountered: