11
11
wait ,
12
12
)
13
13
from collections import Counter
14
- from collections .abc import Iterator
14
+ from collections .abc import Sequence
15
15
from contextlib import AsyncExitStack
16
16
from logging import getLogger
17
17
from typing import (
27
27
from weakref import ref as weakref
28
28
29
29
from anyio import Semaphore
30
+ from typing_extensions import TypeAlias
30
31
31
32
from reactpy .config import (
32
33
REACTPY_ASYNC_RENDERING ,
37
38
from reactpy .core .types import (
38
39
ComponentType ,
39
40
EventHandlerDict ,
41
+ Key ,
40
42
LayoutEventMessage ,
41
43
LayoutUpdateMessage ,
44
+ VdomChild ,
42
45
VdomDict ,
43
46
VdomJson ,
44
47
)
@@ -189,9 +192,7 @@ async def _render_component(
189
192
# wrap the model in a fragment (i.e. tagName="") to ensure components have
190
193
# a separate node in the model state tree. This could be removed if this
191
194
# components are given a node in the tree some other way
192
- wrapper_model : VdomDict = {"tagName" : "" }
193
- if raw_model is not None :
194
- wrapper_model ["children" ] = [raw_model ]
195
+ wrapper_model : VdomDict = {"tagName" : "" , "children" : [raw_model ]}
195
196
await self ._render_model (exit_stack , old_state , new_state , wrapper_model )
196
197
except Exception as error :
197
198
logger .exception (f"Failed to render { component } " )
@@ -329,11 +330,11 @@ async def _render_model_children(
329
330
await self ._unmount_model_states (list (old_state .children_by_key .values ()))
330
331
return None
331
332
332
- child_type_key_tuples = list ( _process_child_type_and_key ( raw_children ) )
333
+ children_info = _get_children_info ( raw_children )
333
334
334
- new_keys = {item [ 2 ] for item in child_type_key_tuples }
335
- if len (new_keys ) != len (raw_children ):
336
- key_counter = Counter (item [2 ] for item in child_type_key_tuples )
335
+ new_keys = {k for _ , _ , k in children_info }
336
+ if len (new_keys ) != len (children_info ):
337
+ key_counter = Counter (item [2 ] for item in children_info )
337
338
duplicate_keys = [key for key , count in key_counter .items () if count > 1 ]
338
339
msg = f"Duplicate keys { duplicate_keys } at { new_state .patch_path or '/' !r} "
339
340
raise ValueError (msg )
@@ -345,7 +346,7 @@ async def _render_model_children(
345
346
)
346
347
347
348
new_state .model .current ["children" ] = []
348
- for index , (child , child_type , key ) in enumerate (child_type_key_tuples ):
349
+ for index , (child , child_type , key ) in enumerate (children_info ):
349
350
old_child_state = old_state .children_by_key .get (key )
350
351
if child_type is _DICT_TYPE :
351
352
old_child_state = old_state .children_by_key .get (key )
@@ -420,17 +421,17 @@ async def _render_model_children_without_old_state(
420
421
new_state : _ModelState ,
421
422
raw_children : list [Any ],
422
423
) -> None :
423
- child_type_key_tuples = list ( _process_child_type_and_key ( raw_children ) )
424
+ children_info = _get_children_info ( raw_children )
424
425
425
- new_keys = {item [ 2 ] for item in child_type_key_tuples }
426
- if len (new_keys ) != len (raw_children ):
427
- key_counter = Counter (item [ 2 ] for item in child_type_key_tuples )
426
+ new_keys = {k for _ , _ , k in children_info }
427
+ if len (new_keys ) != len (children_info ):
428
+ key_counter = Counter (k for _ , _ , k in children_info )
428
429
duplicate_keys = [key for key , count in key_counter .items () if count > 1 ]
429
430
msg = f"Duplicate keys { duplicate_keys } at { new_state .patch_path or '/' !r} "
430
431
raise ValueError (msg )
431
432
432
433
new_state .model .current ["children" ] = []
433
- for index , (child , child_type , key ) in enumerate (child_type_key_tuples ):
434
+ for index , (child , child_type , key ) in enumerate (children_info ):
434
435
if child_type is _DICT_TYPE :
435
436
child_state = _make_element_model_state (new_state , index , key )
436
437
await self ._render_model (exit_stack , None , child_state , child )
@@ -609,7 +610,7 @@ def __init__(
609
610
key : Any ,
610
611
model : Ref [VdomJson ],
611
612
patch_path : str ,
612
- children_by_key : dict [str , _ModelState ],
613
+ children_by_key : dict [Key , _ModelState ],
613
614
targets_by_event : dict [str , str ],
614
615
life_cycle_state : _LifeCycleState | None = None ,
615
616
):
@@ -720,16 +721,17 @@ async def get(self) -> _Type:
720
721
return value
721
722
722
723
723
- def _process_child_type_and_key (
724
- children : list [Any ],
725
- ) -> Iterator [tuple [Any , _ElementType , Any ]]:
724
+ def _get_children_info (children : list [VdomChild ]) -> Sequence [_ChildInfo ]:
725
+ infos : list [_ChildInfo ] = []
726
726
for index , child in enumerate (children ):
727
- if isinstance (child , dict ):
727
+ if child is None :
728
+ continue
729
+ elif isinstance (child , dict ):
728
730
child_type = _DICT_TYPE
729
731
key = child .get ("key" )
730
732
elif isinstance (child , ComponentType ):
731
733
child_type = _COMPONENT_TYPE
732
- key = getattr ( child , " key" , None )
734
+ key = child . key
733
735
else :
734
736
child = f"{ child } "
735
737
child_type = _STRING_TYPE
@@ -738,8 +740,12 @@ def _process_child_type_and_key(
738
740
if key is None :
739
741
key = index
740
742
741
- yield ( child , child_type , key )
743
+ infos . append (( child , child_type , key ) )
742
744
745
+ return infos
746
+
747
+
748
+ _ChildInfo : TypeAlias = tuple [Any , "_ElementType" , Key ]
743
749
744
750
# used in _process_child_type_and_key
745
751
_ElementType = NewType ("_ElementType" , int )
0 commit comments