Mercurial > fife-parpg
comparison clients/editor/scripts/events/saferef.py @ 255:51cc05d862f2
Merged editor_rewrite branch to trunk.
This contains changes that may break compatibility against existing clients.
For a list of changes that may affect your client, see: http://wiki.fifengine.de/Changes_to_pychan_and_FIFE_in_editor_rewrite_branch
author | cheesesucker@33b003aa-7bff-0310-803a-e67f0ece8222 |
---|---|
date | Mon, 08 Jun 2009 16:00:02 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
254:10b5f7f36dd4 | 255:51cc05d862f2 |
---|---|
1 """ | |
2 "Safe weakrefs", originally from pyDispatcher. Copied from django v1.1 beta 1 | |
3 | |
4 Provides a way to safely weakref any function, including bound methods (which | |
5 aren't handled by the core weakref module). | |
6 """ | |
7 | |
8 import weakref, traceback | |
9 | |
10 def safeRef(target, onDelete = None): | |
11 """Return a *safe* weak reference to a callable target | |
12 | |
13 target -- the object to be weakly referenced, if it's a | |
14 bound method reference, will create a BoundMethodWeakref, | |
15 otherwise creates a simple weakref. | |
16 onDelete -- if provided, will have a hard reference stored | |
17 to the callable to be called after the safe reference | |
18 goes out of scope with the reference object, (either a | |
19 weakref or a BoundMethodWeakref) as argument. | |
20 """ | |
21 if hasattr(target, 'im_self'): | |
22 if target.im_self is not None: | |
23 # Turn a bound method into a BoundMethodWeakref instance. | |
24 # Keep track of these instances for lookup by disconnect(). | |
25 assert hasattr(target, 'im_func'), """safeRef target %r has im_self, but no im_func, don't know how to create reference"""%( target,) | |
26 reference = get_bound_method_weakref( | |
27 target=target, | |
28 onDelete=onDelete | |
29 ) | |
30 return reference | |
31 if callable(onDelete): | |
32 return weakref.ref(target, onDelete) | |
33 else: | |
34 return weakref.ref( target ) | |
35 | |
36 class BoundMethodWeakref(object): | |
37 """'Safe' and reusable weak references to instance methods | |
38 | |
39 BoundMethodWeakref objects provide a mechanism for | |
40 referencing a bound method without requiring that the | |
41 method object itself (which is normally a transient | |
42 object) is kept alive. Instead, the BoundMethodWeakref | |
43 object keeps weak references to both the object and the | |
44 function which together define the instance method. | |
45 | |
46 Attributes: | |
47 key -- the identity key for the reference, calculated | |
48 by the class's calculateKey method applied to the | |
49 target instance method | |
50 deletionMethods -- sequence of callable objects taking | |
51 single argument, a reference to this object which | |
52 will be called when *either* the target object or | |
53 target function is garbage collected (i.e. when | |
54 this object becomes invalid). These are specified | |
55 as the onDelete parameters of safeRef calls. | |
56 weakSelf -- weak reference to the target object | |
57 weakFunc -- weak reference to the target function | |
58 | |
59 Class Attributes: | |
60 _allInstances -- class attribute pointing to all live | |
61 BoundMethodWeakref objects indexed by the class's | |
62 calculateKey(target) method applied to the target | |
63 objects. This weak value dictionary is used to | |
64 short-circuit creation so that multiple references | |
65 to the same (object, function) pair produce the | |
66 same BoundMethodWeakref instance. | |
67 | |
68 """ | |
69 | |
70 _allInstances = weakref.WeakValueDictionary() | |
71 | |
72 def __new__( cls, target, onDelete=None, *arguments,**named ): | |
73 """Create new instance or return current instance | |
74 | |
75 Basically this method of construction allows us to | |
76 short-circuit creation of references to already- | |
77 referenced instance methods. The key corresponding | |
78 to the target is calculated, and if there is already | |
79 an existing reference, that is returned, with its | |
80 deletionMethods attribute updated. Otherwise the | |
81 new instance is created and registered in the table | |
82 of already-referenced methods. | |
83 """ | |
84 key = cls.calculateKey(target) | |
85 current =cls._allInstances.get(key) | |
86 if current is not None: | |
87 current.deletionMethods.append( onDelete) | |
88 return current | |
89 else: | |
90 base = super( BoundMethodWeakref, cls).__new__( cls ) | |
91 cls._allInstances[key] = base | |
92 base.__init__( target, onDelete, *arguments,**named) | |
93 return base | |
94 | |
95 def __init__(self, target, onDelete=None): | |
96 """Return a weak-reference-like instance for a bound method | |
97 | |
98 target -- the instance-method target for the weak | |
99 reference, must have im_self and im_func attributes | |
100 and be reconstructable via: | |
101 target.im_func.__get__( target.im_self ) | |
102 which is true of built-in instance methods. | |
103 onDelete -- optional callback which will be called | |
104 when this weak reference ceases to be valid | |
105 (i.e. either the object or the function is garbage | |
106 collected). Should take a single argument, | |
107 which will be passed a pointer to this object. | |
108 """ | |
109 def remove(weak, self=self): | |
110 """Set self.isDead to true when method or instance is destroyed""" | |
111 methods = self.deletionMethods[:] | |
112 del self.deletionMethods[:] | |
113 try: | |
114 del self.__class__._allInstances[ self.key ] | |
115 except KeyError: | |
116 pass | |
117 for function in methods: | |
118 try: | |
119 if callable( function ): | |
120 function( self ) | |
121 except Exception, e: | |
122 try: | |
123 traceback.print_exc() | |
124 except AttributeError, err: | |
125 print '''Exception during saferef %s cleanup function %s: %s'''%( | |
126 self, function, e | |
127 ) | |
128 self.deletionMethods = [onDelete] | |
129 self.key = self.calculateKey( target ) | |
130 self.weakSelf = weakref.ref(target.im_self, remove) | |
131 self.weakFunc = weakref.ref(target.im_func, remove) | |
132 self.selfName = str(target.im_self) | |
133 self.funcName = str(target.im_func.__name__) | |
134 | |
135 def calculateKey( cls, target ): | |
136 """Calculate the reference key for this reference | |
137 | |
138 Currently this is a two-tuple of the id()'s of the | |
139 target object and the target function respectively. | |
140 """ | |
141 return (id(target.im_self),id(target.im_func)) | |
142 calculateKey = classmethod( calculateKey ) | |
143 | |
144 def __str__(self): | |
145 """Give a friendly representation of the object""" | |
146 return """%s( %s.%s )"""%( | |
147 self.__class__.__name__, | |
148 self.selfName, | |
149 self.funcName, | |
150 ) | |
151 | |
152 __repr__ = __str__ | |
153 | |
154 def __nonzero__( self ): | |
155 """Whether we are still a valid reference""" | |
156 return self() is not None | |
157 | |
158 def __cmp__( self, other ): | |
159 """Compare with another reference""" | |
160 if not isinstance (other,self.__class__): | |
161 return cmp( self.__class__, type(other) ) | |
162 return cmp( self.key, other.key) | |
163 | |
164 def __call__(self): | |
165 """Return a strong reference to the bound method | |
166 | |
167 If the target cannot be retrieved, then will | |
168 return None, otherwise returns a bound instance | |
169 method for our object and function. | |
170 | |
171 Note: | |
172 You may call this method any number of times, | |
173 as it does not invalidate the reference. | |
174 """ | |
175 target = self.weakSelf() | |
176 if target is not None: | |
177 function = self.weakFunc() | |
178 if function is not None: | |
179 return function.__get__(target) | |
180 return None | |
181 | |
182 class BoundNonDescriptorMethodWeakref(BoundMethodWeakref): | |
183 """A specialized BoundMethodWeakref, for platforms where instance methods | |
184 are not descriptors. | |
185 | |
186 It assumes that the function name and the target attribute name are the | |
187 same, instead of assuming that the function is a descriptor. This approach | |
188 is equally fast, but not 100% reliable because functions can be stored on an | |
189 attribute named differenty than the function's name such as in: | |
190 | |
191 class A: pass | |
192 def foo(self): return "foo" | |
193 A.bar = foo | |
194 | |
195 But this shouldn't be a common use case. So, on platforms where methods | |
196 aren't descriptors (such as Jython) this implementation has the advantage | |
197 of working in the most cases. | |
198 """ | |
199 def __init__(self, target, onDelete=None): | |
200 """Return a weak-reference-like instance for a bound method | |
201 | |
202 target -- the instance-method target for the weak | |
203 reference, must have im_self and im_func attributes | |
204 and be reconstructable via: | |
205 target.im_func.__get__( target.im_self ) | |
206 which is true of built-in instance methods. | |
207 onDelete -- optional callback which will be called | |
208 when this weak reference ceases to be valid | |
209 (i.e. either the object or the function is garbage | |
210 collected). Should take a single argument, | |
211 which will be passed a pointer to this object. | |
212 """ | |
213 assert getattr(target.im_self, target.__name__) == target, \ | |
214 ("method %s isn't available as the attribute %s of %s" % | |
215 (target, target.__name__, target.im_self)) | |
216 super(BoundNonDescriptorMethodWeakref, self).__init__(target, onDelete) | |
217 | |
218 def __call__(self): | |
219 """Return a strong reference to the bound method | |
220 | |
221 If the target cannot be retrieved, then will | |
222 return None, otherwise returns a bound instance | |
223 method for our object and function. | |
224 | |
225 Note: | |
226 You may call this method any number of times, | |
227 as it does not invalidate the reference. | |
228 """ | |
229 target = self.weakSelf() | |
230 if target is not None: | |
231 function = self.weakFunc() | |
232 if function is not None: | |
233 # Using curry() would be another option, but it erases the | |
234 # "signature" of the function. That is, after a function is | |
235 # curried, the inspect module can't be used to determine how | |
236 # many arguments the function expects, nor what keyword | |
237 # arguments it supports, and pydispatcher needs this | |
238 # information. | |
239 return getattr(target, function.__name__) | |
240 return None | |
241 | |
242 def get_bound_method_weakref(target, onDelete): | |
243 """Instantiates the appropiate BoundMethodWeakRef, depending on the details of | |
244 the underlying class method implementation""" | |
245 if hasattr(target, '__get__'): | |
246 # target method is a descriptor, so the default implementation works: | |
247 return BoundMethodWeakref(target=target, onDelete=onDelete) | |
248 else: | |
249 # no luck, use the alternative implementation: | |
250 return BoundNonDescriptorMethodWeakref(target=target, onDelete=onDelete) |