Ticket #29: sugar-dispatcher.patch
File sugar-dispatcher.patch, 22.7 KB (added by erikos, 15 years ago) |
---|
-
configure.ac
diff --git a/configure.ac b/configure.ac index 9400a88..a0f4330 100644
a b AC_OUTPUT([ 31 31 Makefile 32 32 src/Makefile 33 33 src/sugar/Makefile 34 src/sugar/dispatch/Makefile 34 35 po/Makefile.in 35 36 ]) -
new file po/.gitignore
diff --git a/po/.gitignore b/po/.gitignore new file mode 100644 index 0000000..becd153
- + 1 *.gmo -
src/sugar/Makefile.am
diff --git a/src/sugar/Makefile.am b/src/sugar/Makefile.am index 4da4a1e..e6db230 100644
a b 1 SUBDIRS = dispatch 2 1 3 INCLUDES = -DXDG_PREFIX=sugar_mime 2 4 3 5 sugardir = $(pythondir)/sugar -
new file src/sugar/dispatch/Makefile.am
diff --git a/src/sugar/dispatch/Makefile.am b/src/sugar/dispatch/Makefile.am new file mode 100644 index 0000000..8b0e9e6
- + 1 sugardir = $(pythondir)/sugar/dispatch 2 sugar_PYTHON = \ 3 __init__.py \ 4 dispatcher.py \ 5 saferef.py -
new file src/sugar/dispatch/__init__.py
diff --git a/src/sugar/dispatch/__init__.py b/src/sugar/dispatch/__init__.py new file mode 100644 index 0000000..776b1bc
- + 1 """Multi-consumer multi-producer dispatching mechanism 2 3 Originally based on pydispatch (BSD) http://pypi.python.org/pypi/PyDispatcher/2.0.1 4 See license.txt for original license. 5 6 Heavily modified for Django's purposes. 7 """ 8 9 from sugar.dispatch.dispatcher import Signal -
new file src/sugar/dispatch/dispatcher.py
diff --git a/src/sugar/dispatch/dispatcher.py b/src/sugar/dispatch/dispatcher.py new file mode 100644 index 0000000..9e7efc1
- + 1 import weakref 2 try: 3 set 4 except NameError: 5 from sets import Set as set # Python 2.3 fallback 6 7 from sugar.dispatch import saferef 8 9 WEAKREF_TYPES = (weakref.ReferenceType, saferef.BoundMethodWeakref) 10 11 def _make_id(target): 12 if hasattr(target, 'im_func'): 13 return (id(target.im_self), id(target.im_func)) 14 return id(target) 15 16 class Signal(object): 17 """Base class for all signals 18 19 Internal attributes: 20 receivers -- { receriverkey (id) : weakref(receiver) } 21 """ 22 23 def __init__(self, providing_args=None): 24 """providing_args -- A list of the arguments this signal can pass along in 25 a send() call. 26 """ 27 self.receivers = [] 28 if providing_args is None: 29 providing_args = [] 30 self.providing_args = set(providing_args) 31 32 def connect(self, receiver, sender=None, weak=True, dispatch_uid=None): 33 """Connect receiver to sender for signal 34 35 receiver -- a function or an instance method which is to 36 receive signals. Receivers must be 37 hashable objects. 38 39 if weak is True, then receiver must be weak-referencable 40 (more precisely saferef.safeRef() must be able to create 41 a reference to the receiver). 42 43 Receivers must be able to accept keyword arguments. 44 45 If receivers have a dispatch_uid attribute, the receiver will 46 not be added if another receiver already exists with that 47 dispatch_uid. 48 49 sender -- the sender to which the receiver should respond 50 Must either be of type Signal, or None to receive events 51 from any sender. 52 53 weak -- whether to use weak references to the receiver 54 By default, the module will attempt to use weak 55 references to the receiver objects. If this parameter 56 is false, then strong references will be used. 57 58 dispatch_uid -- an identifier used to uniquely identify a particular 59 instance of a receiver. This will usually be a string, though it 60 may be anything hashable. 61 62 returns None 63 """ 64 65 if dispatch_uid: 66 lookup_key = (dispatch_uid, _make_id(sender)) 67 else: 68 lookup_key = (_make_id(receiver), _make_id(sender)) 69 70 if weak: 71 receiver = saferef.safeRef(receiver, onDelete=self._remove_receiver) 72 73 for r_key, _ in self.receivers: 74 if r_key == lookup_key: 75 break 76 else: 77 self.receivers.append((lookup_key, receiver)) 78 79 def disconnect(self, receiver=None, sender=None, weak=True, dispatch_uid=None): 80 """Disconnect receiver from sender for signal 81 82 receiver -- the registered receiver to disconnect. May be none if 83 dispatch_uid is specified. 84 sender -- the registered sender to disconnect 85 weak -- the weakref state to disconnect 86 dispatch_uid -- the unique identifier of the receiver to disconnect 87 88 disconnect reverses the process of connect. 89 90 If weak references are used, disconnect need not be called. 91 The receiver will be remove from dispatch automatically. 92 93 returns None 94 """ 95 96 if dispatch_uid: 97 lookup_key = (dispatch_uid, _make_id(sender)) 98 else: 99 lookup_key = (_make_id(receiver), _make_id(sender)) 100 101 for idx, (r_key, _) in enumerate(self.receivers): 102 if r_key == lookup_key: 103 del self.receivers[idx] 104 105 def send(self, sender, **named): 106 """Send signal from sender to all connected receivers. 107 108 sender -- the sender of the signal 109 Either a specific object or None. 110 111 named -- named arguments which will be passed to receivers. 112 113 Returns a list of tuple pairs [(receiver, response), ... ]. 114 115 If any receiver raises an error, the error propagates back 116 through send, terminating the dispatch loop, so it is quite 117 possible to not have all receivers called if a raises an 118 error. 119 """ 120 121 responses = [] 122 if not self.receivers: 123 return responses 124 125 for receiver in self._live_receivers(_make_id(sender)): 126 response = receiver(signal=self, sender=sender, **named) 127 responses.append((receiver, response)) 128 return responses 129 130 def send_robust(self, sender, **named): 131 """Send signal from sender to all connected receivers catching errors 132 133 sender -- the sender of the signal 134 Can be any python object (normally one registered with 135 a connect if you actually want something to occur). 136 137 named -- named arguments which will be passed to receivers. 138 These arguments must be a subset of the argument names 139 defined in providing_args. 140 141 Return a list of tuple pairs [(receiver, response), ... ], 142 may raise DispatcherKeyError 143 144 if any receiver raises an error (specifically any subclass of Exception), 145 the error instance is returned as the result for that receiver. 146 """ 147 148 responses = [] 149 if not self.receivers: 150 return responses 151 152 # Call each receiver with whatever arguments it can accept. 153 # Return a list of tuple pairs [(receiver, response), ... ]. 154 for receiver in self._live_receivers(_make_id(sender)): 155 try: 156 response = receiver(signal=self, sender=sender, **named) 157 except Exception, err: 158 responses.append((receiver, err)) 159 else: 160 responses.append((receiver, response)) 161 return responses 162 163 def _live_receivers(self, senderkey): 164 """Filter sequence of receivers to get resolved, live receivers 165 166 This checks for weak references 167 and resolves them, then returning only live 168 receivers. 169 """ 170 none_senderkey = _make_id(None) 171 172 for (receiverkey, r_senderkey), receiver in self.receivers: 173 if r_senderkey == none_senderkey or r_senderkey == senderkey: 174 if isinstance(receiver, WEAKREF_TYPES): 175 # Dereference the weak reference. 176 receiver = receiver() 177 if receiver is not None: 178 yield receiver 179 else: 180 yield receiver 181 182 def _remove_receiver(self, receiver): 183 """Remove dead receivers from connections.""" 184 185 to_remove = [] 186 for key, connected_receiver in self.receivers: 187 if connected_receiver == receiver: 188 to_remove.append(key) 189 for key in to_remove: 190 for idx, (r_key, _) in enumerate(self.receivers): 191 if r_key == key: 192 del self.receivers[idx] -
new file src/sugar/dispatch/license.txt
diff --git a/src/sugar/dispatch/license.txt b/src/sugar/dispatch/license.txt new file mode 100644 index 0000000..0272c28
- + 1 sugar.dispatch was originally forked from django.dispatch 2 3 Copyright (c) Django Software Foundation and individual contributors. 4 All rights reserved. 5 6 Redistribution and use in source and binary forms, with or without modification, 7 are permitted provided that the following conditions are met: 8 9 1. Redistributions of source code must retain the above copyright notice, 10 this list of conditions and the following disclaimer. 11 12 2. Redistributions in binary form must reproduce the above copyright 13 notice, this list of conditions and the following disclaimer in the 14 documentation and/or other materials provided with the distribution. 15 16 3. Neither the name of Django nor the names of its contributors may be used 17 to endorse or promote products derived from this software without 18 specific prior written permission. 19 20 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 21 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 22 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 24 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 25 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 26 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 27 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 31 django.dispatch was originally forked from PyDispatcher. 32 33 PyDispatcher License: 34 35 Copyright (c) 2001-2003, Patrick K. O'Brien and Contributors 36 All rights reserved. 37 38 Redistribution and use in source and binary forms, with or without 39 modification, are permitted provided that the following conditions 40 are met: 41 42 Redistributions of source code must retain the above copyright 43 notice, this list of conditions and the following disclaimer. 44 45 Redistributions in binary form must reproduce the above 46 copyright notice, this list of conditions and the following 47 disclaimer in the documentation and/or other materials 48 provided with the distribution. 49 50 The name of Patrick K. O'Brien, or the name of any Contributor, 51 may not be used to endorse or promote products derived from this 52 software without specific prior written permission. 53 54 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 55 ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 56 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 57 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 58 COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 59 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 60 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 61 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 62 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 63 STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 64 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 65 OF THE POSSIBILITY OF SUCH DAMAGE. 66 -
new file src/sugar/dispatch/saferef.py
diff --git a/src/sugar/dispatch/saferef.py b/src/sugar/dispatch/saferef.py new file mode 100644 index 0000000..8bcfd8a
- + 1 """ 2 "Safe weakrefs", originally from pyDispatcher. 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)