Skip to content

Commit a47fbe0

Browse files
committed
Add local storage wrapper
1 parent 0d15b2b commit a47fbe0

3 files changed

Lines changed: 20 additions & 11 deletions

File tree

examples/quickstart.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ async def handle_change(event: Event):
7171

7272

7373
@app.startup
74-
def init_counter():
74+
async def init_counter():
7575
"""
7676
Runs automatically when the page loads (Client-Side).
7777
Restores the counter from Local Storage.
@@ -82,7 +82,7 @@ def init_counter():
8282
# Check if we have a saved count
8383
saved_count = store.count
8484

85-
if saved_count:
85+
if saved_count is not None:
8686
Document.find("display").text = str(saved_count)
8787
print(f"Restored count: {saved_count}")
8888

violetear/app.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ def get_bundle():
6161

6262
def client(self, func: Callable):
6363
"""Decorator to mark a function to be compiled to the client."""
64+
if not inspect.iscoroutinefunction(func):
65+
raise ValueError("func must be async")
66+
6467
self.client_functions[func.__name__] = func
6568
return func
6669

@@ -69,6 +72,9 @@ def startup(self, func: Callable):
6972
Decorator to mark a function to run automatically when the client loads.
7073
Also registers it as a client function.
7174
"""
75+
if not inspect.iscoroutinefunction(func):
76+
raise ValueError("func must be async")
77+
7278
self.client(func)
7379
self.startup_functions.append(func.__name__)
7480
return func
@@ -78,6 +84,9 @@ def server(self, func: Callable):
7884
Decorator to expose a function as a server-side RPC endpoint.
7985
The client bundle will receive a stub that calls this endpoint via fetch.
8086
"""
87+
if not inspect.iscoroutinefunction(func):
88+
raise ValueError("func must be async")
89+
8190
self.server_functions[func.__name__] = func
8291

8392
# 1. Introspect the function to find arguments
@@ -206,7 +215,7 @@ def _generate_bundle(self) -> str:
206215
Generates the Python bundle to run in the browser.
207216
"""
208217
# 1. Mock the 'app' object
209-
header = "class MockApp:\n def client(self, f): return f\n def server(self, f): return f\napp = MockApp()\n\nclass Event: pass"
218+
header = "class Event: pass"
210219

211220
# 2. Inject violetear.dom module
212221
# This allows 'from violetear.dom import Document' to work in the browser
@@ -251,7 +260,9 @@ def _generate_bundle(self) -> str:
251260
# 4. Extract User Functions
252261
user_code = []
253262
for name, func in self.client_functions.items():
254-
user_code.append(inspect.getsource(func))
263+
code = inspect.getsource(func).split("\n")
264+
code = [c for c in code if not c.startswith("@")] # remove decorators
265+
user_code.append("\n".join(code))
255266

256267
# 5. Generate Server Stubs
257268
server_stubs = self._generate_server_stubs()
@@ -261,7 +272,7 @@ def _generate_bundle(self) -> str:
261272

262273
# Run startup functions
263274
for func_name in self.startup_functions:
264-
init_code += f"\n{func_name}()"
275+
init_code += f"\nawait {func_name}()"
265276

266277
return "\n\n".join(
267278
[

violetear/storage.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
if IS_BROWSER:
88
from js import localStorage, sessionStorage
9+
from pyodide.ffi import JsNull
910

1011

1112
class Thing:
@@ -126,10 +127,7 @@ def _del_raw(self, key):
126127

127128
def __getattr__(self, key: str) -> Any:
128129
# Enables 'store.user' syntax
129-
try:
130-
return self[key]
131-
except KeyError:
132-
return None
130+
return self[key]
133131

134132
def __setattr__(self, key: str, value: Any):
135133
if key.startswith("_"):
@@ -139,8 +137,8 @@ def __setattr__(self, key: str, value: Any):
139137

140138
def __getitem__(self, key: str) -> Any:
141139
raw = self._get_raw(key)
142-
if raw is None:
143-
raise KeyError(key)
140+
if isinstance(raw, JsNull):
141+
return None
144142

145143
try:
146144
data = json.loads(raw)

0 commit comments

Comments
 (0)