Skip to content

Commit 03f83e7

Browse files
committed
Fix hook stack errors
1 parent 87ec4a7 commit 03f83e7

File tree

4 files changed

+54
-68
lines changed

4 files changed

+54
-68
lines changed

src/reactpy/core/_life_cycle_hook.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,10 @@ class _HookStack(Singleton): # nocov
3333
)
3434

3535
def get(self) -> list[LifeCycleHook]:
36-
return self._state.get()
36+
try:
37+
return self._state.get()
38+
except LookupError:
39+
return []
3740

3841
def initialize(self) -> Token[list[LifeCycleHook]] | None:
3942
return None if isinstance(self._state, ThreadLocal) else self._state.set([])

src/reactpy/core/layout.py

Lines changed: 39 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
REACTPY_CHECK_VDOM_SPEC,
3535
REACTPY_DEBUG,
3636
)
37-
from reactpy.core._life_cycle_hook import LifeCycleHook
37+
from reactpy.core._life_cycle_hook import HOOK_STACK, LifeCycleHook
3838
from reactpy.core.vdom import validate_vdom_json
3939
from reactpy.types import (
4040
BaseLayout,
@@ -162,43 +162,47 @@ async def _parallel_render(self) -> LayoutUpdateMessage:
162162
async def _create_layout_update(
163163
self, old_state: _ModelState
164164
) -> LayoutUpdateMessage:
165-
component = old_state.life_cycle_state.component
165+
token = HOOK_STACK.initialize()
166166
try:
167-
parent: _ModelState | None = old_state.parent
168-
except AttributeError:
169-
parent = None
170-
171-
async with AsyncExitStack() as exit_stack:
172-
new_state = await self._render_component(
173-
exit_stack,
174-
old_state,
175-
parent,
176-
old_state.index,
177-
old_state.key,
178-
component,
179-
)
167+
component = old_state.life_cycle_state.component
168+
try:
169+
parent: _ModelState | None = old_state.parent
170+
except AttributeError:
171+
parent = None
172+
173+
async with AsyncExitStack() as exit_stack:
174+
new_state = await self._render_component(
175+
exit_stack,
176+
old_state,
177+
parent,
178+
old_state.index,
179+
old_state.key,
180+
component,
181+
)
180182

181-
if parent is not None:
182-
parent.children_by_key[new_state.key] = new_state
183-
old_parent_model = parent.model.current
184-
old_parent_children = old_parent_model.setdefault("children", [])
185-
parent.model.current = {
186-
**old_parent_model,
187-
"children": [
188-
*old_parent_children[: new_state.index],
189-
new_state.model.current,
190-
*old_parent_children[new_state.index + 1 :],
191-
],
183+
if parent is not None:
184+
parent.children_by_key[new_state.key] = new_state
185+
old_parent_model = parent.model.current
186+
old_parent_children = old_parent_model.setdefault("children", [])
187+
parent.model.current = {
188+
**old_parent_model,
189+
"children": [
190+
*old_parent_children[: new_state.index],
191+
new_state.model.current,
192+
*old_parent_children[new_state.index + 1 :],
193+
],
194+
}
195+
196+
if REACTPY_CHECK_VDOM_SPEC.current:
197+
validate_vdom_json(new_state.model.current)
198+
199+
return {
200+
"type": "layout-update",
201+
"path": new_state.patch_path,
202+
"model": new_state.model.current,
192203
}
193-
194-
if REACTPY_CHECK_VDOM_SPEC.current:
195-
validate_vdom_json(new_state.model.current)
196-
197-
return {
198-
"type": "layout-update",
199-
"path": new_state.patch_path,
200-
"model": new_state.model.current,
201-
}
204+
finally:
205+
HOOK_STACK.reset(token)
202206

203207
async def _render_component(
204208
self,

src/reactpy/core/serve.py

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -45,22 +45,18 @@ async def _single_outgoing_loop(
4545
send: SendCoroutine,
4646
) -> None:
4747
while True:
48-
token = HOOK_STACK.initialize()
48+
update = await layout.render()
4949
try:
50-
update = await layout.render()
51-
try:
52-
await send(update)
53-
except Exception: # nocov
54-
if not REACTPY_DEBUG.current:
55-
msg = (
56-
"Failed to send update. More info may be available "
57-
"if you enabling debug mode by setting "
58-
"`reactpy.config.REACTPY_DEBUG.current = True`."
59-
)
60-
logger.error(msg)
61-
raise
62-
finally:
63-
HOOK_STACK.reset(token)
50+
await send(update)
51+
except Exception: # nocov
52+
if not REACTPY_DEBUG.current:
53+
msg = (
54+
"Failed to send update. More info may be available "
55+
"if you enabling debug mode by setting "
56+
"`reactpy.config.REACTPY_DEBUG.current = True`."
57+
)
58+
logger.error(msg)
59+
raise
6460

6561

6662
async def _single_incoming_loop(

tests/conftest.py

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -54,23 +54,6 @@ def rebuild():
5454
subprocess.run(["hatch", "build", "-t", "wheel"], check=True, env=env) # noqa: S607
5555

5656

57-
@pytest.fixture(autouse=True, scope="function")
58-
def create_hook_state():
59-
"""This fixture is a bug fix related to `pytest_asyncio`.
60-
61-
Usually the hook stack is created automatically within the display fixture, but context
62-
variables aren't retained within `pytest_asyncio` async fixtures. As a workaround,
63-
this fixture ensures that the hook stack is created before each test is run.
64-
65-
Ref: https://github.com/pytest-dev/pytest-asyncio/issues/127
66-
"""
67-
from reactpy.core._life_cycle_hook import HOOK_STACK
68-
69-
token = HOOK_STACK.initialize()
70-
yield token
71-
HOOK_STACK.reset(token)
72-
73-
7457
@pytest.fixture
7558
async def display(server, page):
7659
async with DisplayFixture(server, page) as display:

0 commit comments

Comments
 (0)