diff --git a/nicegui/storage.py b/nicegui/storage.py index 5f7017128..b98d63cf6 100644 --- a/nicegui/storage.py +++ b/nicegui/storage.py @@ -66,7 +66,7 @@ class Storage: def __init__(self) -> None: self._general = Storage._create_persistent_dict('general') self._users: Dict[str, PersistentDict] = {} - self._tabs: Dict[str, PersistentDict] = {} + self._tabs: Dict[str, ObservableDict] = {} @staticmethod def _create_persistent_dict(id: str) -> PersistentDict: # pylint: disable=redefined-builtin @@ -163,13 +163,21 @@ def tab(self) -> observables.ObservableDict: async def _create_tab_storage(self, tab_id: str) -> None: """Create tab storage for the given tab ID.""" if tab_id not in self._tabs: - self._tabs[tab_id] = Storage._create_persistent_dict(f'tab-{tab_id}') - await self._tabs[tab_id].initialize() + if Storage.redis_url: + self._tabs[tab_id] = Storage._create_persistent_dict(f'tab-{tab_id}') + tab = self._tabs[tab_id] + assert isinstance(tab, PersistentDict) + await tab.initialize() + else: + self._tabs[tab_id] = ObservableDict() def copy_tab(self, old_tab_id: str, tab_id: str) -> None: """Copy the tab storage to a new tab. (For internal use only.)""" if old_tab_id in self._tabs: - self._tabs[tab_id] = Storage._create_persistent_dict(f'tab-{tab_id}') + if Storage.redis_url: + self._tabs[tab_id] = Storage._create_persistent_dict(f'tab-{tab_id}') + else: + self._tabs[tab_id] = ObservableDict() self._tabs[tab_id].update(self._tabs[old_tab_id]) async def prune_tab_storage(self) -> None: @@ -178,7 +186,8 @@ async def prune_tab_storage(self) -> None: for tab_id, tab in list(self._tabs.items()): if time.time() > tab.last_modified + self.max_tab_storage_age: tab.clear() - await tab.close() + if isinstance(tab, PersistentDict): + await tab.close() del self._tabs[tab_id] await asyncio.sleep(PURGE_INTERVAL) diff --git a/tests/test_storage.py b/tests/test_storage.py index 034264739..a1a2a9883 100644 --- a/tests/test_storage.py +++ b/tests/test_storage.py @@ -315,3 +315,23 @@ def f(v): screen.open('/') screen.assert_py_logger('ERROR', 'app.storage.user can only be used within a UI context') + + +def test_client_storage_holds_non_serializable_objects(screen: Screen): + @ui.page('/') + def page(): + ui.button('Update storage', on_click=lambda: app.storage.client.update(x=len)) + + screen.open('/') + screen.click('Update storage') + screen.wait(0.5) + + +def test_tab_storage_holds_non_serializable_objects(screen: Screen): + @ui.page('/') + def page(): + ui.button('Update storage', on_click=lambda: app.storage.tab.update(x=len)) + + screen.open('/') + screen.click('Update storage') + screen.wait(0.5)