26
26
import traceback
27
27
import types
28
28
import warnings
29
- from weakref import ref , WeakKeyDictionary
29
+ import weakref
30
+ from weakref import WeakMethod
30
31
31
32
import numpy as np
32
33
@@ -61,100 +62,26 @@ def unicode_safe(s):
61
62
return s
62
63
63
64
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 ()
70
67
71
- @organization: IBM Corporation
72
- @copyright: Copyright (c) 2005, 2006 IBM Corporation
73
- @license: The BSD License
74
68
75
- Minor bugfixes by Michael Droettboom
69
+ class _StrongRef :
70
+ """
71
+ Wrapper similar to a weakref, but keeping a strong reference to the object.
76
72
"""
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
130
73
131
- mtd = types .MethodType (self .func , self .inst ())
74
+ def __init__ (self , obj ):
75
+ self ._obj = obj
132
76
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
138
79
139
80
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
151
82
152
83
def __hash__ (self ):
153
- return self ._hash
154
-
155
-
156
- def _exception_printer (exc ):
157
- traceback .print_exc ()
84
+ return hash (self ._obj )
158
85
159
86
160
87
class CallbackRegistry (object ):
@@ -179,20 +106,13 @@ class CallbackRegistry(object):
179
106
>>> callbacks.disconnect(id_eat)
180
107
>>> callbacks.process('eat', 456) # nothing will be called
181
108
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.
196
116
197
117
Parameters
198
118
----------
@@ -211,12 +131,17 @@ def handler(exc: Exception) -> None:
211
131
212
132
def h(exc):
213
133
traceback.print_exc()
214
-
215
134
"""
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
+
216
141
def __init__ (self , exception_handler = _exception_printer ):
217
142
self .exception_handler = exception_handler
218
- self .callbacks = dict ()
219
- self ._cid = 0
143
+ self .callbacks = {}
144
+ self ._cid_gen = itertools . count ()
220
145
self ._func_cid_map = {}
221
146
222
147
# In general, callbacks may not be pickled; thus, we simply recreate an
@@ -236,18 +161,17 @@ def __setstate__(self, state):
236
161
def connect (self , s , func ):
237
162
"""Register *func* to be called when signal *s* is generated.
238
163
"""
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 )
243
169
if proxy in self ._func_cid_map [s ]:
244
170
return self ._func_cid_map [s ][proxy ]
245
171
246
- proxy .add_destroy_callback (self ._remove_proxy )
247
- self ._cid += 1
248
- cid = self ._cid
172
+ cid = next (self ._cid_gen )
249
173
self ._func_cid_map [s ][proxy ] = cid
250
- self .callbacks .setdefault (s , dict () )
174
+ self .callbacks .setdefault (s , {} )
251
175
self .callbacks [s ][cid ] = proxy
252
176
return cid
253
177
@@ -257,7 +181,6 @@ def _remove_proxy(self, proxy):
257
181
del self .callbacks [signal ][proxies [proxy ]]
258
182
except KeyError :
259
183
pass
260
-
261
184
if len (self .callbacks [signal ]) == 0 :
262
185
del self .callbacks [signal ]
263
186
del self ._func_cid_map [signal ]
@@ -284,12 +207,11 @@ def process(self, s, *args, **kwargs):
284
207
All of the functions registered to receive callbacks on *s* will be
285
208
called with ``*args`` and ``**kwargs``.
286
209
"""
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 :
289
213
try :
290
- proxy (* args , ** kwargs )
291
- except ReferenceError :
292
- self ._remove_proxy (proxy )
214
+ func (* args , ** kwargs )
293
215
# this does not capture KeyboardInterrupt, SystemExit,
294
216
# and GeneratorExit
295
217
except Exception as exc :
@@ -978,10 +900,10 @@ class Grouper(object):
978
900
979
901
"""
980
902
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 }
982
904
983
905
def __contains__ (self , item ):
984
- return ref (item ) in self ._mapping
906
+ return weakref . ref (item ) in self ._mapping
985
907
986
908
def clean (self ):
987
909
"""Clean dead weak references from the dictionary."""
@@ -996,10 +918,10 @@ def join(self, a, *args):
996
918
Join given arguments into the same set. Accepts one or more arguments.
997
919
"""
998
920
mapping = self ._mapping
999
- set_a = mapping .setdefault (ref (a ), [ref (a )])
921
+ set_a = mapping .setdefault (weakref . ref (a ), [weakref . ref (a )])
1000
922
1001
923
for arg in args :
1002
- set_b = mapping .get (ref (arg ), [ref (arg )])
924
+ set_b = mapping .get (weakref . ref (arg ), [weakref . ref (arg )])
1003
925
if set_b is not set_a :
1004
926
if len (set_b ) > len (set_a ):
1005
927
set_a , set_b = set_b , set_a
@@ -1012,13 +934,14 @@ def join(self, a, *args):
1012
934
def joined (self , a , b ):
1013
935
"""Returns True if *a* and *b* are members of the same set."""
1014
936
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 )))
1016
939
1017
940
def remove (self , a ):
1018
941
self .clean ()
1019
- set_a = self ._mapping .pop (ref (a ), None )
942
+ set_a = self ._mapping .pop (weakref . ref (a ), None )
1020
943
if set_a :
1021
- set_a .remove (ref (a ))
944
+ set_a .remove (weakref . ref (a ))
1022
945
1023
946
def __iter__ (self ):
1024
947
"""
@@ -1034,7 +957,7 @@ def __iter__(self):
1034
957
def get_siblings (self , a ):
1035
958
"""Returns all of the items joined with *a*, including itself."""
1036
959
self .clean ()
1037
- siblings = self ._mapping .get (ref (a ), [ref (a )])
960
+ siblings = self ._mapping .get (weakref . ref (a ), [weakref . ref (a )])
1038
961
return [x () for x in siblings ]
1039
962
1040
963
0 commit comments