Skip to content

Commit 0923863

Browse files
apiadclaude
andcommitted
fix(app): re-export storage + dom globals into the bundle's module scope
Bug surfaced in browser while running examples/03_interactive.py: NameError: name 'store' is not defined — when @app.client.on("ready") restore() runs in Pyodide. Cause: the example imports `from violetear.storage import store` at module level, then uses `store` inside the restore handler. The bundle generator transpiles the function via inspect.getsource(), which keeps the function body but drops the surrounding module-level imports. At exec time, `store` is undefined in the bundle's globals. Compile-time tests (compile(text, "<bundle>", "exec", flags=...)) pass fine — the bundle is syntactically valid; it only fails when Pyodide actually runs it. This is exactly the class of bug we need e2e browser testing for; logged as issues/7.6. Fix: emit `from violetear.storage import store, session` after the storage module injection block, and `from violetear.dom import DOM, DOMElement, Event` after the dom injection block. User code that imported these names at module level now resolves cleanly without having to lazy-import inside every function. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 6e79486 commit 0923863

2 files changed

Lines changed: 20 additions & 0 deletions

File tree

issues/7-framework-gaps-from-canonical-examples.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,18 @@ Running log of small framework limitations and ergonomic friction discovered whi
4343

4444
**Impact.** Trips up new users; forces a context-manager-or-`HTML.*` pattern that's slightly more verbose than necessary.
4545

46+
## 7.6 — Bundle doesn't re-export common violetear globals into module scope
47+
48+
**Tier(s):** 03 (caught in browser, not by tests)
49+
50+
**Symptom.** User code writes the idiomatic `from violetear.storage import store` at module level and then uses `store` inside a `@app.client.on("ready")` handler. The bundle generator transpiles the handler via `inspect.getsource(func)`, which preserves the function body but *not* the module-level imports around it. At Pyodide exec time, `store` is undefined → `NameError: name 'store' is not defined`. The bundle compiles cleanly (no syntax error), and the existing smoke tests pass — the failure only manifests when Pyodide actually executes the bundle in a browser. **No automated test would have caught this.**
51+
52+
**Workaround applied.** Added `from violetear.storage import store, session` to the storage injection block, and `from violetear.dom import DOM, DOMElement, Event` to the dom injection block, so those names are available at the bundle's module scope without each user function needing a lazy import.
53+
54+
**Where else this could bite.** Any module-level import the user writes that isn't `dataclasses`, `datetime`, `json`, `violetear.dom.*`, or `violetear.storage.*` will silently fail at runtime. The bundle generator should ideally scan the user's source for module-level imports and replay them at the bundle's top — that's the structurally correct fix.
55+
56+
**Larger lesson.** Unit-level bundle-compile checks are insufficient. We need an end-to-end "actually load this in a real Pyodide" test to catch class-of-bug like this. See planned e2e Playwright slice.
57+
4658
## 7.5 — Bundle generator's `inspect.getsource` breaks for dynamically-loaded modules
4759

4860
**Tier(s):** 03 (smoke-test infrastructure)

violetear/app.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -663,6 +663,10 @@ def _generate_bundle(self) -> str:
663663
exec({repr(dom_source)}, m_dom.__dict__)
664664
665665
import violetear.dom
666+
# Re-export common DOM names into the bundle's module scope so user
667+
# code's `from violetear.dom import DOM` (or just bare `DOM`) resolves
668+
# without each function needing its own lazy import.
669+
from violetear.dom import DOM, DOMElement, Event
666670
"""
667671
)
668672

@@ -682,6 +686,10 @@ def _generate_bundle(self) -> str:
682686
exec({repr(storage_source)}, m_storage.__dict__)
683687
684688
import violetear.storage
689+
# Re-export the singleton storage handles into the bundle's module
690+
# scope so user code that wrote `from violetear.storage import store`
691+
# at module level (the natural Pythonic shape) doesn't NameError.
692+
from violetear.storage import store, session
685693
"""
686694
)
687695

0 commit comments

Comments
 (0)