-
-
Save jfbercher/f5918b5a97786f26ef9971e395b88742 to your computer and use it in GitHub Desktop.
Local storage access in Streamlit - st_local_storage
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # NOTE: This feature uses browser local storage! AKA it stores data on a viewer's | |
| # machine. This may have privacy and compliance implications for your app. Be sure | |
| # to take that into account with any usage. | |
| import json | |
| import time | |
| import uuid | |
| from typing import Any, Optional | |
| from streamlit.time_util import time_to_seconds | |
| # Requires `pip install streamlit-js` | |
| # https://github.com/toolittlecakes/streamlit_js | |
| from streamlit_js import st_js | |
| import streamlit as st | |
| KEY_PREFIX = "st_localstorage_" | |
| # Keep track of a UUID for each key to enable reruns | |
| if "_ls_unique_keys" not in st.session_state: | |
| st.session_state["_ls_unique_keys"] = {} | |
| _ls_keys = st.session_state["_ls_unique_keys"] | |
| # Hide the JS iframes | |
| st.html(""" | |
| <style> | |
| .element-container:has(iframe[height="0"]) { | |
| display: none; | |
| } | |
| </style> | |
| """) | |
| class StLocalStorage: | |
| """ | |
| A dict-like wrapper around browser local storage. | |
| Values are stored JSON encoded. | |
| It allows you to store data in the browser's local storage with an optional TTL (Time to Live). | |
| TTL is the maximum time to keep an entry in the cache. Can be one of: | |
| - `None` if cache entries should never expire (default). | |
| - A number specifying the time in seconds. | |
| - A string specifying the time in a format supported by Pandas's Timedelta constructor, e.g. `"1d"`, `"1.5 days"`, or `"1h23s"`. | |
| - A `timedelta` object from Python's built-in datetime library, e.g. `timedelta(days=1)`. | |
| Examples | |
| -------- | |
| Without TTL | |
| >>> st_local_storage = StLocalStorage() | |
| >>> st_local_storage['key'] = 'value' | |
| >>> st.write(st_local_storage['key']) | |
| With TTL in seconds | |
| >>> st_local_storage = StLocalStorage() | |
| >>> st_local_storage['key'] = 'value', 10.0 | |
| >>> st.write(st_local_storage['key']) # Will return None after 10 seconds | |
| With TTL in string format | |
| >>> st_local_storage = StLocalStorage() | |
| >>> st_local_storage['key'] = 'value', '10s' | |
| >>> st.write(st_local_storage['key']) # Will return None after 10 seconds | |
| """ | |
| def __getitem__(self, key: str) -> Optional[Any]: | |
| if key not in _ls_keys: | |
| _ls_keys[key] = str(uuid.uuid4()) | |
| code = f""" | |
| // The UUID changes on save, which causes this to rerun and update | |
| console.debug('{_ls_keys[key]}'); | |
| return localStorage.getItem('{KEY_PREFIX + key}'); | |
| """ | |
| result = st_js(code) | |
| if result and result[0]: | |
| data = json.loads(result[0]) | |
| if 'expiry' in data and data['expiry'] is not None and data['expiry'] < time.time(): | |
| self.__delitem__(key) # Remove expired item | |
| return None | |
| return data['value'] | |
| return None | |
| def __setitem__(self, key: str, value: Any) -> None: | |
| ttl = None # Default TTL to None | |
| if isinstance(value, tuple): | |
| value, ttl = value | |
| ttl = time_to_seconds(ttl) | |
| expiry = time.time() + ttl if ttl is not None else None | |
| data = {'value': value, 'expiry': expiry} | |
| data_json = json.dumps(data) | |
| _ls_keys[key] = str(uuid.uuid4()) | |
| code = f"localStorage.setItem('{KEY_PREFIX + key}', {json.dumps(data_json)});" | |
| print(f"Setting {key} to {value} with expiry {expiry}. Current time: {time.time()}") | |
| st_js(code) | |
| def __delitem__(self, key: str) -> None: | |
| _ls_keys[key] = str(uuid.uuid4()) | |
| code = f"localStorage.removeItem('{KEY_PREFIX + key}');" | |
| st_js(code) | |
| def __contains__(self, key: str) -> bool: | |
| return self.__getitem__(key) is not None | |
| st_local_storage = StLocalStorage() | |
| if __name__ == "__main__": | |
| st.title("st_local_storage with TTL") | |
| "Any values you save will be available after leaving / refreshing the tab, unless they expire." | |
| key = st.text_input("Key") | |
| value = st.text_input("Value") | |
| ttl = None | |
| if st.toggle("Set TTL?"): | |
| if st.checkbox("Use seconds"): | |
| ttl = st.number_input("TTL in seconds", 10.0) | |
| else: | |
| ttl = st.text_input("TTL as string, e.g. 1d, 1h30m", "10s") | |
| if st.button("Save"): | |
| st_local_storage[key] = value, ttl | |
| if key: | |
| f"Current value for {key} is:" | |
| st.write(st_local_storage[key]) | |
| if st.button("Delete"): | |
| del st_local_storage[key] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment