1111RecvCoroutine = Callable [[], Awaitable [LayoutEvent ]]
1212
1313
14+ class StopRendering (Exception ):
15+ """Raised to gracefully stop :meth:`AbstractRenderer.run`"""
16+
17+
1418class AbstractRenderer (abc .ABC ):
1519 """A base class for implementing :class:`~idom.core.layout.Layout` renderers."""
1620
@@ -23,10 +27,24 @@ async def run(self, send: SendCoroutine, recv: RecvCoroutine, context: Any) -> N
2327 This will call :meth:`AbstractLayout.render` and :meth:`AbstractLayout.trigger`
2428 to render new models and execute events respectively.
2529 """
30+ self ._running = True
31+ try :
32+ await self ._run (send , recv , context )
33+ except StopRendering :
34+ return None
35+ finally :
36+ await self ._stop ()
37+
38+ async def _run (
39+ self , send : SendCoroutine , recv : RecvCoroutine , context : Any
40+ ) -> None :
2641 await asyncio .gather (
2742 self ._outgoing_loop (send , context ), self ._incoming_loop (recv , context )
2843 )
2944
45+ async def _stop (self ) -> None :
46+ pass
47+
3048 async def _outgoing_loop (self , send : SendCoroutine , context : Any ) -> None :
3149 while True :
3250 await send (await self ._outgoing (self ._layout , context ))
@@ -58,6 +76,7 @@ async def _outgoing(self, layout: AbstractLayout, context: Any) -> Dict[str, Any
5876 try :
5977 src , new , old = await layout .render ()
6078 except RenderError as error :
79+ raise
6180 if error .partial_render is None :
6281 raise
6382 logger .exception ("Render failed" )
@@ -84,10 +103,12 @@ def __init__(self, layout: AbstractLayout) -> None:
84103 self ._updates : Dict [str , asyncio .Queue [LayoutUpdate ]] = {}
85104 self ._render_task = asyncio .ensure_future (self ._render_loop (), loop = layout .loop )
86105
87- async def run (self , send : SendCoroutine , recv : RecvCoroutine , context : str ) -> None :
106+ async def _run (
107+ self , send : SendCoroutine , recv : RecvCoroutine , context : str
108+ ) -> None :
88109 self ._updates [context ] = asyncio .Queue ()
89110 try :
90- await asyncio .gather (super ().run (send , recv , context ), self ._render_task )
111+ await asyncio .gather (super ()._run (send , recv , context ), self ._render_task )
91112 except Exception :
92113 del self ._updates [context ]
93114 raise
@@ -127,5 +148,5 @@ async def _outgoing(self, layout: AbstractLayout, context: str) -> Dict[str, Any
127148 src , new , old = await self ._updates [context ].get ()
128149 return {"root" : layout .root , "src" : src , "new" : new , "old" : old }
129150
130- def __del__ (self ) -> None :
151+ async def _stop (self ) -> None :
131152 self ._render_task .cancel ()
0 commit comments