2626import traceback
2727import types
2828import warnings
29- from weakref import ref , WeakKeyDictionary
29+ import weakref
30+ from weakref import WeakMethod
3031
3132import numpy as np
3233
@@ -61,100 +62,26 @@ def unicode_safe(s):
6162 return s
6263
6364
64- class _BoundMethodProxy (object ):
65- """
66- Our own proxy object which enables weak references to bound and unbound
67- methods and arbitrary callables. Pulls information about the function,
68- class, and instance out of a bound method. Stores a weak reference to the
69- instance to support garbage collection.
65+ def _exception_printer (exc ):
66+ traceback .print_exc ()
7067
71- @organization: IBM Corporation
72- @copyright: Copyright (c) 2005, 2006 IBM Corporation
73- @license: The BSD License
7468
75- Minor bugfixes by Michael Droettboom
69+ class _StrongRef :
70+ """
71+ Wrapper similar to a weakref, but keeping a strong reference to the object.
7672 """
77- def __init__ (self , cb ):
78- self ._hash = hash (cb )
79- self ._destroy_callbacks = []
80- try :
81- try :
82- self .inst = ref (cb .__self__ , self ._destroy )
83- except TypeError :
84- self .inst = None
85- self .func = cb .__func__
86- self .klass = cb .__self__ .__class__
87- except AttributeError :
88- self .inst = None
89- self .func = cb
90- self .klass = None
91-
92- def add_destroy_callback (self , callback ):
93- self ._destroy_callbacks .append (_BoundMethodProxy (callback ))
94-
95- def _destroy (self , wk ):
96- for callback in self ._destroy_callbacks :
97- try :
98- callback (self )
99- except ReferenceError :
100- pass
101-
102- def __getstate__ (self ):
103- d = self .__dict__ .copy ()
104- # de-weak reference inst
105- inst = d ['inst' ]
106- if inst is not None :
107- d ['inst' ] = inst ()
108- return d
109-
110- def __setstate__ (self , statedict ):
111- self .__dict__ = statedict
112- inst = statedict ['inst' ]
113- # turn inst back into a weakref
114- if inst is not None :
115- self .inst = ref (inst )
116-
117- def __call__ (self , * args , ** kwargs ):
118- """
119- Proxy for a call to the weak referenced object. Take
120- arbitrary params to pass to the callable.
121-
122- Raises `ReferenceError`: When the weak reference refers to
123- a dead object
124- """
125- if self .inst is not None and self .inst () is None :
126- raise ReferenceError
127- elif self .inst is not None :
128- # build a new instance method with a strong reference to the
129- # instance
13073
131- mtd = types .MethodType (self .func , self .inst ())
74+ def __init__ (self , obj ):
75+ self ._obj = obj
13276
133- else :
134- # not a bound method, just return the func
135- mtd = self .func
136- # invoke the callable and return the result
137- return mtd (* args , ** kwargs )
77+ def __call__ (self ):
78+ return self ._obj
13879
13980 def __eq__ (self , other ):
140- """
141- Compare the held function and instance with that held by
142- another proxy.
143- """
144- try :
145- if self .inst is None :
146- return self .func == other .func and other .inst is None
147- else :
148- return self .func == other .func and self .inst () == other .inst ()
149- except Exception :
150- return False
81+ return isinstance (other , _StrongRef ) and self ._obj == other ._obj
15182
15283 def __hash__ (self ):
153- return self ._hash
154-
155-
156- def _exception_printer (exc ):
157- traceback .print_exc ()
84+ return hash (self ._obj )
15885
15986
16087class CallbackRegistry (object ):
@@ -179,20 +106,13 @@ class CallbackRegistry(object):
179106 >>> callbacks.disconnect(id_eat)
180107 >>> callbacks.process('eat', 456) # nothing will be called
181108
182- In practice, one should always disconnect all callbacks when they
183- are no longer needed to avoid dangling references (and thus memory
184- leaks). However, real code in matplotlib rarely does so, and due
185- to its design, it is rather difficult to place this kind of code.
186- To get around this, and prevent this class of memory leaks, we
187- instead store weak references to bound methods only, so when the
188- destination object needs to die, the CallbackRegistry won't keep
189- it alive. The Python stdlib weakref module can not create weak
190- references to bound methods directly, so we need to create a proxy
191- object to handle weak references to bound methods (or regular free
192- functions). This technique was shared by Peter Parente on his
193- `"Mindtrove" blog
194- <http://mindtrove.info/python-weak-references/>`_.
195-
109+ In practice, one should always disconnect all callbacks when they are
110+ no longer needed to avoid dangling references (and thus memory leaks).
111+ However, real code in Matplotlib rarely does so, and due to its design,
112+ it is rather difficult to place this kind of code. To get around this,
113+ and prevent this class of memory leaks, we instead store weak references
114+ to bound methods only, so when the destination object needs to die, the
115+ CallbackRegistry won't keep it alive.
196116
197117 Parameters
198118 ----------
@@ -211,12 +131,17 @@ def handler(exc: Exception) -> None:
211131
212132 def h(exc):
213133 traceback.print_exc()
214-
215134 """
135+
136+ # We maintain two mappings:
137+ # callbacks: signal -> {cid -> callback}
138+ # _func_cid_map: signal -> {callback -> cid}
139+ # (actually, callbacks are weakrefs to the actual callbacks).
140+
216141 def __init__ (self , exception_handler = _exception_printer ):
217142 self .exception_handler = exception_handler
218- self .callbacks = dict ()
219- self ._cid = 0
143+ self .callbacks = {}
144+ self ._cid_gen = itertools . count ()
220145 self ._func_cid_map = {}
221146
222147 # In general, callbacks may not be pickled; thus, we simply recreate an
@@ -236,18 +161,17 @@ def __setstate__(self, state):
236161 def connect (self , s , func ):
237162 """Register *func* to be called when signal *s* is generated.
238163 """
239- self ._func_cid_map .setdefault (s , WeakKeyDictionary ())
240- # Note proxy not needed in python 3.
241- # TODO rewrite this when support for python2.x gets dropped.
242- proxy = _BoundMethodProxy (func )
164+ self ._func_cid_map .setdefault (s , {})
165+ try :
166+ proxy = WeakMethod (func , self ._remove_proxy )
167+ except TypeError :
168+ proxy = _StrongRef (func )
243169 if proxy in self ._func_cid_map [s ]:
244170 return self ._func_cid_map [s ][proxy ]
245171
246- proxy .add_destroy_callback (self ._remove_proxy )
247- self ._cid += 1
248- cid = self ._cid
172+ cid = next (self ._cid_gen )
249173 self ._func_cid_map [s ][proxy ] = cid
250- self .callbacks .setdefault (s , dict () )
174+ self .callbacks .setdefault (s , {} )
251175 self .callbacks [s ][cid ] = proxy
252176 return cid
253177
@@ -257,7 +181,6 @@ def _remove_proxy(self, proxy):
257181 del self .callbacks [signal ][proxies [proxy ]]
258182 except KeyError :
259183 pass
260-
261184 if len (self .callbacks [signal ]) == 0 :
262185 del self .callbacks [signal ]
263186 del self ._func_cid_map [signal ]
@@ -284,12 +207,11 @@ def process(self, s, *args, **kwargs):
284207 All of the functions registered to receive callbacks on *s* will be
285208 called with ``*args`` and ``**kwargs``.
286209 """
287- if s in self .callbacks :
288- for cid , proxy in list (self .callbacks [s ].items ()):
210+ for cid , ref in list (self .callbacks .get (s , {}).items ()):
211+ func = ref ()
212+ if func is not None :
289213 try :
290- proxy (* args , ** kwargs )
291- except ReferenceError :
292- self ._remove_proxy (proxy )
214+ func (* args , ** kwargs )
293215 # this does not capture KeyboardInterrupt, SystemExit,
294216 # and GeneratorExit
295217 except Exception as exc :
@@ -978,10 +900,10 @@ class Grouper(object):
978900
979901 """
980902 def __init__ (self , init = ()):
981- self ._mapping = {ref (x ): [ref (x )] for x in init }
903+ self ._mapping = {weakref . ref (x ): [weakref . ref (x )] for x in init }
982904
983905 def __contains__ (self , item ):
984- return ref (item ) in self ._mapping
906+ return weakref . ref (item ) in self ._mapping
985907
986908 def clean (self ):
987909 """Clean dead weak references from the dictionary."""
@@ -996,10 +918,10 @@ def join(self, a, *args):
996918 Join given arguments into the same set. Accepts one or more arguments.
997919 """
998920 mapping = self ._mapping
999- set_a = mapping .setdefault (ref (a ), [ref (a )])
921+ set_a = mapping .setdefault (weakref . ref (a ), [weakref . ref (a )])
1000922
1001923 for arg in args :
1002- set_b = mapping .get (ref (arg ), [ref (arg )])
924+ set_b = mapping .get (weakref . ref (arg ), [weakref . ref (arg )])
1003925 if set_b is not set_a :
1004926 if len (set_b ) > len (set_a ):
1005927 set_a , set_b = set_b , set_a
@@ -1012,13 +934,14 @@ def join(self, a, *args):
1012934 def joined (self , a , b ):
1013935 """Returns True if *a* and *b* are members of the same set."""
1014936 self .clean ()
1015- return self ._mapping .get (ref (a ), object ()) is self ._mapping .get (ref (b ))
937+ return (self ._mapping .get (weakref .ref (a ), object ())
938+ is self ._mapping .get (weakref .ref (b )))
1016939
1017940 def remove (self , a ):
1018941 self .clean ()
1019- set_a = self ._mapping .pop (ref (a ), None )
942+ set_a = self ._mapping .pop (weakref . ref (a ), None )
1020943 if set_a :
1021- set_a .remove (ref (a ))
944+ set_a .remove (weakref . ref (a ))
1022945
1023946 def __iter__ (self ):
1024947 """
@@ -1034,7 +957,7 @@ def __iter__(self):
1034957 def get_siblings (self , a ):
1035958 """Returns all of the items joined with *a*, including itself."""
1036959 self .clean ()
1037- siblings = self ._mapping .get (ref (a ), [ref (a )])
960+ siblings = self ._mapping .get (weakref . ref (a ), [weakref . ref (a )])
1038961 return [x () for x in siblings ]
1039962
1040963
0 commit comments