@@ -47,6 +47,11 @@ def __lt__(self, other):
4747 return NotImplemented
4848 def __hash__ (self ):
4949 return hash (self .arg )
50+ def some_method (self ):
51+ return 4
52+ def other_method (self ):
53+ return 5
54+
5055
5156class RefCycle :
5257 def __init__ (self ):
@@ -880,6 +885,140 @@ def callback(w):
880885 self .assertEqual (self .cbcalled , 0 )
881886
882887
888+ class WeakMethodTestCase (unittest .TestCase ):
889+
890+ def _subclass (self ):
891+ """Return a Object subclass overriding `some_method`."""
892+ class C (Object ):
893+ def some_method (self ):
894+ return 6
895+ return C
896+
897+ def test_alive (self ):
898+ o = Object (1 )
899+ r = weakref .WeakMethod (o .some_method )
900+ self .assertIsInstance (r , weakref .ReferenceType )
901+ self .assertIsInstance (r (), type (o .some_method ))
902+ self .assertIs (r ().__self__ , o )
903+ self .assertIs (r ().__func__ , o .some_method .__func__ )
904+ self .assertEqual (r ()(), 4 )
905+
906+ def test_object_dead (self ):
907+ o = Object (1 )
908+ r = weakref .WeakMethod (o .some_method )
909+ del o
910+ gc .collect ()
911+ self .assertIs (r (), None )
912+
913+ def test_method_dead (self ):
914+ C = self ._subclass ()
915+ o = C (1 )
916+ r = weakref .WeakMethod (o .some_method )
917+ del C .some_method
918+ gc .collect ()
919+ self .assertIs (r (), None )
920+
921+ def test_callback_when_object_dead (self ):
922+ # Test callback behaviour when object dies first.
923+ C = self ._subclass ()
924+ calls = []
925+ def cb (arg ):
926+ calls .append (arg )
927+ o = C (1 )
928+ r = weakref .WeakMethod (o .some_method , cb )
929+ del o
930+ gc .collect ()
931+ self .assertEqual (calls , [r ])
932+ # Callback is only called once.
933+ C .some_method = Object .some_method
934+ gc .collect ()
935+ self .assertEqual (calls , [r ])
936+
937+ def test_callback_when_method_dead (self ):
938+ # Test callback behaviour when method dies first.
939+ C = self ._subclass ()
940+ calls = []
941+ def cb (arg ):
942+ calls .append (arg )
943+ o = C (1 )
944+ r = weakref .WeakMethod (o .some_method , cb )
945+ del C .some_method
946+ gc .collect ()
947+ self .assertEqual (calls , [r ])
948+ # Callback is only called once.
949+ del o
950+ gc .collect ()
951+ self .assertEqual (calls , [r ])
952+
953+ @support .cpython_only
954+ def test_no_cycles (self ):
955+ # A WeakMethod doesn't create any reference cycle to itself.
956+ o = Object (1 )
957+ def cb (_ ):
958+ pass
959+ r = weakref .WeakMethod (o .some_method , cb )
960+ wr = weakref .ref (r )
961+ del r
962+ self .assertIs (wr (), None )
963+
964+ def test_equality (self ):
965+ def _eq (a , b ):
966+ self .assertTrue (a == b )
967+ self .assertFalse (a != b )
968+ def _ne (a , b ):
969+ self .assertTrue (a != b )
970+ self .assertFalse (a == b )
971+ x = Object (1 )
972+ y = Object (1 )
973+ a = weakref .WeakMethod (x .some_method )
974+ b = weakref .WeakMethod (y .some_method )
975+ c = weakref .WeakMethod (x .other_method )
976+ d = weakref .WeakMethod (y .other_method )
977+ # Objects equal, same method
978+ _eq (a , b )
979+ _eq (c , d )
980+ # Objects equal, different method
981+ _ne (a , c )
982+ _ne (a , d )
983+ _ne (b , c )
984+ _ne (b , d )
985+ # Objects unequal, same or different method
986+ z = Object (2 )
987+ e = weakref .WeakMethod (z .some_method )
988+ f = weakref .WeakMethod (z .other_method )
989+ _ne (a , e )
990+ _ne (a , f )
991+ _ne (b , e )
992+ _ne (b , f )
993+ del x , y , z
994+ gc .collect ()
995+ # Dead WeakMethods compare by identity
996+ refs = a , b , c , d , e , f
997+ for q in refs :
998+ for r in refs :
999+ self .assertEqual (q == r , q is r )
1000+ self .assertEqual (q != r , q is not r )
1001+
1002+ def test_hashing (self ):
1003+ # Alive WeakMethods are hashable if the underlying object is
1004+ # hashable.
1005+ x = Object (1 )
1006+ y = Object (1 )
1007+ a = weakref .WeakMethod (x .some_method )
1008+ b = weakref .WeakMethod (y .some_method )
1009+ c = weakref .WeakMethod (y .other_method )
1010+ # Since WeakMethod objects are equal, the hashes should be equal.
1011+ self .assertEqual (hash (a ), hash (b ))
1012+ ha = hash (a )
1013+ # Dead WeakMethods retain their old hash value
1014+ del x , y
1015+ gc .collect ()
1016+ self .assertEqual (hash (a ), ha )
1017+ self .assertEqual (hash (b ), ha )
1018+ # If it wasn't hashed when alive, a dead WeakMethod cannot be hashed.
1019+ self .assertRaises (TypeError , hash , c )
1020+
1021+
8831022class MappingTestCase (TestBase ):
8841023
8851024 COUNT = 10
@@ -1455,6 +1594,7 @@ def _reference(self):
14551594def test_main ():
14561595 support .run_unittest (
14571596 ReferencesTestCase ,
1597+ WeakMethodTestCase ,
14581598 MappingTestCase ,
14591599 WeakValueDictionaryTestCase ,
14601600 WeakKeyDictionaryTestCase ,
0 commit comments