Skip to content

Commit 111bda3

Browse files
committed
remove abstract layout and rename Layout.trigger to Layout.dispatch
1 parent e521acb commit 111bda3

6 files changed

Lines changed: 39 additions & 82 deletions

File tree

docs/source/core-concepts.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ ever be removed from the model. Then you'll just need to call and await a
7373
The layout also handles the triggering event handlers. Normally this is done
7474
automatically by a :ref:`Renderer <Layout Renderer>`, but for now we'll to it manually.
7575
To do use we can use a trick to hard-code the ``event_handler_id`` so we can pass it,
76-
and a fake event, to the layout's :meth:`~idom.core.layout.Layout.trigger` method. Then
76+
and a fake event, to the layout's :meth:`~idom.core.layout.Layout.dispatch` method. Then
7777
we just have to re-render the layout and see what changed:
7878

7979
.. testcode::
@@ -102,7 +102,7 @@ we just have to re-render the layout and see what changed:
102102
patch_1 = await layout.render()
103103

104104
fake_event = LayoutEvent(event_handler_id, [{}])
105-
await layout.trigger(fake_event)
105+
await layout.dispatch(fake_event)
106106
patch_2 = await layout.render()
107107

108108
for change in patch_2.changes:

idom/core/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
from .element import element, Element, AbstractElement, ElementConstructor
22
from .events import event, Events, EventHandler
3-
from .layout import Layout, AbstractLayout
3+
from .layout import Layout, Layout
44
from .render import AbstractRenderer, SharedStateRenderer, SingleStateRenderer
55
from .vdom import vdom
66

77
__all__ = [
88
"AbstractElement",
9-
"AbstractLayout",
9+
"Layout",
1010
"AbstractRenderer",
1111
"element",
1212
"Element",

idom/core/layout.py

Lines changed: 18 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -38,15 +38,18 @@ class LayoutEvent(NamedTuple):
3838
"""A list of event data passed to the event handler."""
3939

4040

41-
class AbstractLayout(HasAsyncResources, abc.ABC):
42-
"""Renders the models generated by :class:`AbstractElement` objects.
41+
class ElementState(NamedTuple):
42+
model: Dict[str, Any]
43+
path: List[int]
44+
element_obj: AbstractElement
45+
event_handler_ids: Set[str]
46+
child_elements_ids: List[str]
47+
life_cycle_hook: LifeCycleHook
4348

44-
Parameters:
45-
root: The root element of the layout.
46-
loop: What loop the layout should be using to schedule tasks.
47-
"""
4849

49-
__slots__ = ["_loop", "_root"]
50+
class Layout(HasAsyncResources):
51+
52+
__slots__ = ["_root", "_event_handlers"]
5053

5154
if not hasattr(abc.ABC, "__weakref__"): # pragma: no cover
5255
__slots__.append("__weakref__")
@@ -55,71 +58,23 @@ def __init__(
5558
self, root: "AbstractElement", loop: Optional[asyncio.AbstractEventLoop] = None
5659
) -> None:
5760
super().__init__()
58-
if loop is None:
59-
loop = asyncio.get_event_loop()
6061
if not isinstance(root, AbstractElement):
6162
raise TypeError("Expected an AbstractElement, not %r" % root)
62-
self._loop = loop
6363
self._root = root
64-
65-
@property
66-
def loop(self) -> asyncio.AbstractEventLoop:
67-
"""The event loop the layout is using."""
68-
return self._loop
64+
self._event_handlers: Dict[str, EventHandler] = {}
6965

7066
@property
7167
def root(self) -> str:
7268
"""Id of the root element."""
7369
return self._root.id
7470

75-
@abc.abstractmethod
76-
async def render(self) -> LayoutUpdate:
77-
"""Await an update to the model."""
78-
79-
@abc.abstractmethod
80-
def update(self, element: AbstractElement) -> None:
81-
"""Schedule the element to be re-renderer."""
82-
83-
@abc.abstractmethod
84-
async def trigger(self, event: LayoutEvent) -> None:
85-
"""Trigger an event handler
86-
87-
Parameters:
88-
event: Event data passed to the event handler.
89-
"""
90-
91-
def __repr__(self) -> str:
92-
return f"{type(self).__name__}({self._root})"
93-
94-
95-
class ElementState(NamedTuple):
96-
model: Dict[str, Any]
97-
path: List[int]
98-
element_obj: AbstractElement
99-
event_handler_ids: Set[str]
100-
child_elements_ids: List[str]
101-
life_cycle_hook: LifeCycleHook
102-
103-
104-
class Layout(AbstractLayout):
105-
106-
__slots__ = "_event_handlers", "_rendering_queue"
107-
108-
def __init__(
109-
self, root: "AbstractElement", loop: Optional[asyncio.AbstractEventLoop] = None
110-
) -> None:
111-
super().__init__(root, loop)
112-
self._event_handlers: Dict[str, EventHandler] = {}
113-
self._rendering_queue = _ElementQueue()
114-
self._rendering_queue.put(self._root)
115-
11671
def update(self, element: "AbstractElement") -> None:
11772
try:
11873
self._rendering_queue.put(element)
11974
except CannotAccessResource:
12075
logger.info(f"Did not update {element} - resources of {self} are closed")
12176

122-
async def trigger(self, event: LayoutEvent) -> None:
77+
async def dispatch(self, event: LayoutEvent) -> None:
12378
# It is possible for an element in the frontend to produce an event
12479
# associated with a backend model that has been deleted. We only handle
12580
# events if the element and the handler exist in the backend. Otherwise
@@ -134,6 +89,12 @@ async def render(self) -> Dict[str, Any]:
13489
if element.id in self._element_states:
13590
return await self._create_layout_update(element)
13691

92+
@async_resource
93+
async def _rendering_queue(self) -> AsyncIterator["_ElementQueue"]:
94+
queue = _ElementQueue()
95+
queue.put(self._root)
96+
yield queue
97+
13798
@async_resource
13899
async def _element_states(self) -> AsyncIterator[ElementState]:
139100
root_element_state = self._create_element_state(self._root, "")

idom/core/render.py

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
from .layout import (
88
LayoutEvent,
99
LayoutUpdate,
10-
AbstractLayout,
11-
AbstractLayout,
10+
Layout,
11+
Layout,
1212
)
1313
from .utils import HasAsyncResources, async_resource
1414

@@ -23,12 +23,12 @@ class StopRendering(Exception):
2323
class AbstractRenderer(HasAsyncResources, abc.ABC):
2424
"""A base class for implementing :class:`~idom.core.layout.Layout` renderers."""
2525

26-
def __init__(self, layout: AbstractLayout) -> None:
26+
def __init__(self, layout: Layout) -> None:
2727
super().__init__()
2828
self._layout = layout
2929

3030
@async_resource
31-
async def layout(self) -> AsyncIterator[AbstractLayout]:
31+
async def layout(self) -> AsyncIterator[Layout]:
3232
async with self._layout as layout:
3333
yield layout
3434

@@ -40,7 +40,7 @@ async def task_group(self) -> AsyncIterator[TaskGroup]:
4040
async def run(self, send: SendCoroutine, recv: RecvCoroutine, context: Any) -> None:
4141
"""Start an unending loop which will drive the layout.
4242
43-
This will call :meth:`AbstractLayouTaskGroupTaskGroupt.render` and :meth:`AbstractLayout.trigger`
43+
This will call :meth:`AbstractLayouTaskGroupTaskGroupt.render` and :meth:`Layout.dispatch`
4444
to render new models and execute events respectively.
4545
"""
4646
await self.task_group.spawn(self._outgoing_loop, send, context)
@@ -56,13 +56,11 @@ async def _incoming_loop(self, recv: RecvCoroutine, context: Any) -> None:
5656
await self._incoming(self.layout, context, await recv())
5757

5858
@abc.abstractmethod
59-
async def _outgoing(self, layout: AbstractLayout, context: Any) -> Any:
59+
async def _outgoing(self, layout: Layout, context: Any) -> Any:
6060
...
6161

6262
@abc.abstractmethod
63-
async def _incoming(
64-
self, layout: AbstractLayout, context: Any, message: Any
65-
) -> None:
63+
async def _incoming(self, layout: Layout, context: Any, message: Any) -> None:
6664
...
6765

6866

@@ -74,17 +72,15 @@ class SingleStateRenderer(AbstractRenderer):
7472
be ``None`` since it's not used.
7573
"""
7674

77-
def __init__(self, layout: AbstractLayout) -> None:
75+
def __init__(self, layout: Layout) -> None:
7876
super().__init__(layout)
7977
self._current_model_as_json = ""
8078

81-
async def _outgoing(self, layout: AbstractLayout, context: Any) -> LayoutUpdate:
79+
async def _outgoing(self, layout: Layout, context: Any) -> LayoutUpdate:
8280
return await layout.render()
8381

84-
async def _incoming(
85-
self, layout: AbstractLayout, context: Any, event: LayoutEvent
86-
) -> None:
87-
await layout.trigger(event)
82+
async def _incoming(self, layout: Layout, context: Any, event: LayoutEvent) -> None:
83+
await layout.dispatch(event)
8884
return None
8985

9086

@@ -95,7 +91,7 @@ class SharedStateRenderer(SingleStateRenderer):
9591
:meth:`SharedStateRenderer.run`
9692
"""
9793

98-
def __init__(self, layout: AbstractLayout) -> None:
94+
def __init__(self, layout: Layout) -> None:
9995
super().__init__(layout)
10096
self._update_queues: Dict[str, asyncio.Queue[LayoutUpdate]] = {}
10197

@@ -120,7 +116,7 @@ async def _render_loop(self) -> None:
120116
for queue in self._update_queues.values():
121117
await queue.put(update)
122118

123-
async def _outgoing(self, layout: AbstractLayout, context: str) -> LayoutUpdate:
119+
async def _outgoing(self, layout: Layout, context: str) -> LayoutUpdate:
124120
return await self._updates[context].get()
125121

126122
@async_resource

idom/server/base.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from threading import Thread
55

66
from idom.core.element import ElementConstructor, AbstractElement
7-
from idom.core.layout import AbstractLayout, Layout
7+
from idom.core.layout import Layout, Layout
88
from idom.core.render import (
99
AbstractRenderer,
1010
SendCoroutine,
@@ -32,7 +32,7 @@ class AbstractRenderServer(Generic[_App, _Config]):
3232

3333
_loop: AbstractEventLoop
3434
_renderer_type: Type[AbstractRenderer]
35-
_layout_type: Type[AbstractLayout] = Layout
35+
_layout_type: Type[Layout] = Layout
3636

3737
def __init__(
3838
self, constructor: ElementConstructor, *args: Any, **kwargs: Any
@@ -142,7 +142,7 @@ def _make_layout(
142142
self,
143143
parameters: Dict[str, Any],
144144
loop: Optional[AbstractEventLoop] = None,
145-
) -> AbstractLayout:
145+
) -> Layout:
146146
return self._layout_type(self._make_root_element(parameters), loop)
147147

148148
def _make_root_element(self, parameters: Dict[str, Any]) -> AbstractElement:

tests/test_core/test_layout.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ async def Inner():
243243
# the the old `Inner` element should be deleted. Thus there should be one
244244
# changed element in the set of `live_elements` the old `Inner` deleted and new
245245
# `Inner` added.
246-
await layout.trigger(idom.core.layout.LayoutEvent("force-update", []))
246+
await layout.dispatch(idom.core.layout.LayoutEvent("force-update", []))
247247
await layout.render()
248248
assert len(live_elements - last_live_elements) == 1
249249

0 commit comments

Comments
 (0)