Skip to content

Commit 786e24f

Browse files
Save non-serializable data in tab storage (#4325)
This PR fixes #4313 by providing a non-persistent `ObservableDict` for `app.storage.tab` if Redis is not used. --------- Co-authored-by: Falko Schindler <[email protected]>
1 parent 7ad1a4b commit 786e24f

File tree

2 files changed

+34
-5
lines changed

2 files changed

+34
-5
lines changed

nicegui/storage.py

+14-5
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ class Storage:
6666
def __init__(self) -> None:
6767
self._general = Storage._create_persistent_dict('general')
6868
self._users: Dict[str, PersistentDict] = {}
69-
self._tabs: Dict[str, PersistentDict] = {}
69+
self._tabs: Dict[str, ObservableDict] = {}
7070

7171
@staticmethod
7272
def _create_persistent_dict(id: str) -> PersistentDict: # pylint: disable=redefined-builtin
@@ -163,13 +163,21 @@ def tab(self) -> observables.ObservableDict:
163163
async def _create_tab_storage(self, tab_id: str) -> None:
164164
"""Create tab storage for the given tab ID."""
165165
if tab_id not in self._tabs:
166-
self._tabs[tab_id] = Storage._create_persistent_dict(f'tab-{tab_id}')
167-
await self._tabs[tab_id].initialize()
166+
if Storage.redis_url:
167+
self._tabs[tab_id] = Storage._create_persistent_dict(f'tab-{tab_id}')
168+
tab = self._tabs[tab_id]
169+
assert isinstance(tab, PersistentDict)
170+
await tab.initialize()
171+
else:
172+
self._tabs[tab_id] = ObservableDict()
168173

169174
def copy_tab(self, old_tab_id: str, tab_id: str) -> None:
170175
"""Copy the tab storage to a new tab. (For internal use only.)"""
171176
if old_tab_id in self._tabs:
172-
self._tabs[tab_id] = Storage._create_persistent_dict(f'tab-{tab_id}')
177+
if Storage.redis_url:
178+
self._tabs[tab_id] = Storage._create_persistent_dict(f'tab-{tab_id}')
179+
else:
180+
self._tabs[tab_id] = ObservableDict()
173181
self._tabs[tab_id].update(self._tabs[old_tab_id])
174182

175183
async def prune_tab_storage(self) -> None:
@@ -178,7 +186,8 @@ async def prune_tab_storage(self) -> None:
178186
for tab_id, tab in list(self._tabs.items()):
179187
if time.time() > tab.last_modified + self.max_tab_storage_age:
180188
tab.clear()
181-
await tab.close()
189+
if isinstance(tab, PersistentDict):
190+
await tab.close()
182191
del self._tabs[tab_id]
183192
await asyncio.sleep(PURGE_INTERVAL)
184193

tests/test_storage.py

+20
Original file line numberDiff line numberDiff line change
@@ -315,3 +315,23 @@ def f(v):
315315

316316
screen.open('/')
317317
screen.assert_py_logger('ERROR', 'app.storage.user can only be used within a UI context')
318+
319+
320+
def test_client_storage_holds_non_serializable_objects(screen: Screen):
321+
@ui.page('/')
322+
def page():
323+
ui.button('Update storage', on_click=lambda: app.storage.client.update(x=len))
324+
325+
screen.open('/')
326+
screen.click('Update storage')
327+
screen.wait(0.5)
328+
329+
330+
def test_tab_storage_holds_non_serializable_objects(screen: Screen):
331+
@ui.page('/')
332+
def page():
333+
ui.button('Update storage', on_click=lambda: app.storage.tab.update(x=len))
334+
335+
screen.open('/')
336+
screen.click('Update storage')
337+
screen.wait(0.5)

0 commit comments

Comments
 (0)