15
15
Set ,
16
16
Tuple ,
17
17
)
18
- from weakref import ReferenceType , ref
18
+ from weakref import ref
19
19
20
20
from jsonpatch import apply_patch , make_patch
21
21
from typing_extensions import TypedDict
@@ -125,7 +125,7 @@ async def _model_state_by_component_id(
125
125
126
126
def _create_layout_update (self , component : AbstractComponent ) -> LayoutUpdate :
127
127
old_state = self ._model_state_by_component_id [id (component )]
128
- new_state = old_state .new ()
128
+ new_state = old_state .new (None , component )
129
129
130
130
self ._render_component (old_state , new_state , component )
131
131
changes = make_patch (getattr (old_state , "model" , {}), new_state .model ).patch
@@ -269,26 +269,33 @@ def _render_model_children(
269
269
270
270
old_keys = set (old_state .children_by_key ).difference (raw_typed_children_by_key )
271
271
old_child_states = {key : old_state .children_by_key [key ] for key in old_keys }
272
- if old_child_states :
272
+ if old_keys :
273
273
self ._unmount_model_states (list (old_child_states .values ()))
274
274
275
275
new_children = new_state .model ["children" ] = []
276
276
for index , (key , (child_type , child )) in enumerate (
277
277
raw_typed_children_by_key .items ()
278
278
):
279
279
if child_type is DICT_TYPE :
280
- child_state = _ModelState (ref (new_state ), index , key , None )
281
- self ._render_model (old_child_states .get (key ), child_state , child )
282
- new_children .append (child_state .model )
283
- new_state .children_by_key [key ] = child_state
280
+ old_child_state = old_state .children_by_key .get (key )
281
+ if old_child_state is not None :
282
+ new_child_state = old_child_state .new (new_state , None )
283
+ else :
284
+ new_child_state = _ModelState (new_state , index , key , None )
285
+ self ._render_model (old_child_state , new_child_state , child )
286
+ new_children .append (new_child_state .model )
287
+ new_state .children_by_key [key ] = new_child_state
284
288
elif child_type is COMPONENT_TYPE :
285
- key = getattr (child , "key" , "" ) or hex (id (child ))
286
- life_cycle_hook = LifeCycleHook (child , self )
287
- child_state = _ModelState (ref (new_state ), index , key , life_cycle_hook )
288
- self ._render_component (old_child_states .get (key ), child_state , child )
289
- new_children .append (child_state .model )
290
- new_state .children_by_key [key ] = child_state
291
- self ._model_state_by_component_id [id (child )] = child_state
289
+ old_child_state = old_state .children_by_key .get (key )
290
+ if old_child_state is not None :
291
+ new_child_state = old_child_state .new (new_state , child )
292
+ else :
293
+ hook = LifeCycleHook (child , self )
294
+ new_child_state = _ModelState (new_state , index , key , hook )
295
+ self ._render_component (old_child_state , new_child_state , child )
296
+ new_children .append (new_child_state .model )
297
+ new_state .children_by_key [key ] = new_child_state
298
+ self ._model_state_by_component_id [id (child )] = new_child_state
292
299
else :
293
300
new_children .append (child )
294
301
@@ -299,14 +306,14 @@ def _render_model_children_without_old_state(
299
306
for index , child in enumerate (raw_children ):
300
307
if isinstance (child , dict ):
301
308
key = child .get ("key" ) or hex (id (child ))
302
- child_state = _ModelState (ref ( new_state ) , index , key , None )
309
+ child_state = _ModelState (new_state , index , key , None )
303
310
self ._render_model (None , child_state , child )
304
311
new_children .append (child_state .model )
305
312
new_state .children_by_key [key ] = child_state
306
313
elif isinstance (child , AbstractComponent ):
307
314
key = getattr (child , "key" , "" ) or hex (id (child ))
308
315
life_cycle_hook = LifeCycleHook (child , self )
309
- child_state = _ModelState (ref ( new_state ) , index , key , life_cycle_hook )
316
+ child_state = _ModelState (new_state , index , key , life_cycle_hook )
310
317
self ._render_component (None , child_state , child )
311
318
new_children .append (child_state .model )
312
319
new_state .children_by_key [key ] = child_state
@@ -322,6 +329,10 @@ def _unmount_model_states(self, old_states: List[_ModelState]) -> None:
322
329
hook = state .life_cycle_hook
323
330
hook .component_will_unmount ()
324
331
del self ._model_state_by_component_id [id (hook .component )]
332
+ import gc
333
+
334
+ print (state )
335
+ print (gc .get_referrers (hook ))
325
336
to_unmount .extend (state .children_by_key .values ())
326
337
327
338
def __repr__ (self ) -> str :
@@ -333,7 +344,7 @@ class _ModelState:
333
344
__slots__ = (
334
345
"index" ,
335
346
"key" ,
336
- "parent_ref " ,
347
+ "_parent_ref " ,
337
348
"life_cycle_hook" ,
338
349
"patch_path" ,
339
350
"key_path" ,
@@ -347,40 +358,49 @@ class _ModelState:
347
358
348
359
def __init__ (
349
360
self ,
350
- parent_ref : Optional [ReferenceType [ _ModelState ] ],
361
+ parent : Optional [_ModelState ],
351
362
index : int ,
352
363
key : str ,
353
364
life_cycle_hook : Optional [LifeCycleHook ],
354
365
) -> None :
355
366
self .index = index
356
367
self .key = key
357
368
358
- if parent_ref is not None :
359
- self .parent_ref = parent_ref
360
- # temporarilly hydrate for use below
361
- parent = parent_ref ()
369
+ if parent is not None :
370
+ self ._parent_ref = ref ( parent )
371
+ self . key_path = f" { parent . key_path } / { key } "
372
+ self . patch_path = f" { parent . patch_path } /children/ { index } "
362
373
else :
363
- parent = None
374
+ self . key_path = self . patch_path = ""
364
375
365
376
if life_cycle_hook is not None :
366
377
self .life_cycle_hook = life_cycle_hook
367
378
368
- if parent is None :
369
- self .key_path = self .patch_path = ""
370
- else :
371
- self .key_path = f"{ parent .key_path } /{ key } "
372
- self .patch_path = f"{ parent .patch_path } /children/{ index } "
373
-
374
379
self .event_targets : Set [str ] = set ()
375
380
self .children_by_key : Dict [str , _ModelState ] = {}
376
381
377
- def new (self ) -> _ModelState :
378
- return _ModelState (
379
- getattr (self , "parent_ref" , None ),
380
- self .index ,
381
- self .key ,
382
- getattr (self , "life_cycle_hook" , None ),
383
- )
382
+ @property
383
+ def parent (self ) -> _ModelState :
384
+ # An AttributeError here is ok. It's synonymous
385
+ # with the existance of 'parent' attribute
386
+ p = self ._parent_ref ()
387
+ assert p is not None , "detached model state"
388
+ return p
389
+
390
+ def new (
391
+ self ,
392
+ new_parent : Optional [_ModelState ],
393
+ component : Optional [AbstractComponent ],
394
+ ) -> _ModelState :
395
+ if new_parent is None :
396
+ new_parent = getattr (self , "parent" , None )
397
+ if hasattr (self , "life_cycle_hook" ):
398
+ assert component is not None
399
+ life_cycle_hook = self .life_cycle_hook
400
+ life_cycle_hook .component = component
401
+ else :
402
+ life_cycle_hook = None
403
+ return _ModelState (new_parent , self .index , self .key , life_cycle_hook )
384
404
385
405
def iter_children (self , include_self : bool = True ) -> Iterator [_ModelState ]:
386
406
to_yield = [self ] if include_self else []
@@ -390,6 +410,28 @@ def iter_children(self, include_self: bool = True) -> Iterator[_ModelState]:
390
410
to_yield .extend (node .children_by_key .values ())
391
411
392
412
413
+ class _ComponentQueue :
414
+
415
+ __slots__ = "_loop" , "_queue" , "_pending"
416
+
417
+ def __init__ (self ) -> None :
418
+ self ._loop = asyncio .get_event_loop ()
419
+ self ._queue : "asyncio.Queue[AbstractComponent]" = asyncio .Queue ()
420
+ self ._pending : Set [int ] = set ()
421
+
422
+ def put (self , component : AbstractComponent ) -> None :
423
+ component_id = id (component )
424
+ if component_id not in self ._pending :
425
+ self ._pending .add (component_id )
426
+ self ._loop .call_soon_threadsafe (self ._queue .put_nowait , component )
427
+ return None
428
+
429
+ async def get (self ) -> AbstractComponent :
430
+ component = await self ._queue .get ()
431
+ self ._pending .remove (id (component ))
432
+ return component
433
+
434
+
393
435
class _ModelEventTarget (TypedDict ):
394
436
target : str
395
437
preventDefault : bool # noqa
@@ -415,25 +457,3 @@ class _ModelVdomRequired(TypedDict, total=True):
415
457
416
458
class _ModelVdom (_ModelVdomRequired , _ModelVdomOptional ):
417
459
"""A VDOM dictionary model specifically for use with a :class:`Layout`"""
418
-
419
-
420
- class _ComponentQueue :
421
-
422
- __slots__ = "_loop" , "_queue" , "_pending"
423
-
424
- def __init__ (self ) -> None :
425
- self ._loop = asyncio .get_event_loop ()
426
- self ._queue : "asyncio.Queue[AbstractComponent]" = asyncio .Queue ()
427
- self ._pending : Set [int ] = set ()
428
-
429
- def put (self , component : AbstractComponent ) -> None :
430
- component_id = id (component )
431
- if component_id not in self ._pending :
432
- self ._pending .add (component_id )
433
- self ._loop .call_soon_threadsafe (self ._queue .put_nowait , component )
434
- return None
435
-
436
- async def get (self ) -> AbstractComponent :
437
- component = await self ._queue .get ()
438
- self ._pending .remove (id (component ))
439
- return component
0 commit comments