@@ -435,38 +435,45 @@ def test_deepcopy_reflexive_dict(self):
435435 self .assertEqual (len (y ), 1 )
436436
437437 def test_deepcopy_keepalive (self ):
438- mutable = []
439- def count_refs ():
440- return sys .getrefcount (mutable )
441- class C :
438+ class Target :
439+ pass
440+ class Dropper :
441+ def __init__ (self , to_clear ):
442+ self .to_clear = to_clear
442443 def __deepcopy__ (self , memo ):
443- self .recorded = count_refs ()
444+ self .to_clear . clear ()
444445 return self
445- x = [mutable , 42 , observer := C ()]
446- y = copy .deepcopy (x )
446+ class Checker :
447+ def __init__ (self , ref ):
448+ self .ref = ref
449+ self .was_alive = None
450+ def __deepcopy__ (self , memo ):
451+ support .gc_collect () # For PyPy or other GCs.
452+ self .was_alive = self .ref () is not None
453+ return self
454+ weak_ref = weakref .ref (target := Target ())
455+ holder = [target ]
456+ del target # holder[0] is now only strong ref
457+ container = [holder , Dropper (holder ), * ((1 ,) * 1000 ), (checker := Checker (weak_ref ))]
458+ copy .deepcopy (container )
459+ self .assertTrue (checker .was_alive )
447460 support .gc_collect () # For PyPy or other GCs.
448- # Additional keepalive ref dropped after copy:
449- self .assertGreater (observer .recorded , count_refs ())
450- self .assertEqual (observer .recorded - count_refs (), 1 )
461+ self .assertIsNone (weak_ref ())
451462
452463 def test_deepcopy_dont_memo_immutable (self ):
453- def run_case (immutable ):
454- def count_refs ():
455- return sys .getrefcount (immutable )
456- class C :
457- def __deepcopy__ (self , memo ):
458- self .recorded = count_refs ()
459- return self
460- x = [immutable , 42 , observer := C ()]
461- y = copy .deepcopy (x )
462- support .gc_collect () # For PyPy or other GCs
463- self .assertIs (y [0 ], x [0 ])
464- self .assertEqual (count_refs (), observer .recorded )
465- with self .subTest (kind = "string" ):
466- run_case (f"mortal_immutable_{ id (object ())} " )
464+ class ByRef :
465+ def __init__ (self ):
466+ self .copied = 0
467+ def __deepcopy__ (self , memo ):
468+ self .copied += 1
469+ return self
470+ y = copy .deepcopy (x := [br := ByRef (), br ])
471+ self .assertEqual (br .copied , 2 )
472+ self .assertEqual (y , x )
467473 # Tuples with immutable contents are immutable for deepcopy.
468- with self .subTest (kind = "tuple_with_string" ):
469- run_case ((f"mortal_immutable_{ id (object ())} " ,))
474+ y = copy .deepcopy (x := [(br := ByRef ()), (br ,)])
475+ self .assertEqual (br .copied , 2 )
476+ self .assertEqual (y , x )
470477
471478 def test_deepcopy_inst_vanilla (self ):
472479 class C :
0 commit comments