Django

Code

Ticket #6814: faster_signals.diff

File faster_signals.diff, 33.1 kB (added by keithb, 10 months ago)

Initial attempt at the signal refactor

  • django/core/signals.py

    old new  
    1 request_started = object() 
    2 request_finished = object() 
    3 got_request_exception = object() 
     1from django.dispatch.dispatcher import Signal 
     2 
     3request_started = Signal() 
     4request_finished = Signal() 
     5got_request_exception = Signal(argnames=["request"]) 
  • django/db/models/signals.py

    old new  
    1 class_prepared = object() 
     1from django.dispatch.dispatcher import Signal 
    22 
    3 pre_init= object() 
    4 post_init = object() 
     3class_prepared = Signal(argnames=["class"]) 
    54 
    6 pre_save = object(
    7 post_save = object(
     5pre_init = Signal(argnames=["instance", "args", "kwargs"]
     6post_init = Signal(argnames=["instance"]
    87 
    9 pre_delete = object(
    10 post_delete = object(
     8pre_save = Signal(argnames=["instance", "raw"]
     9post_save = Signal(argnames=["instance", "raw", "created"]
    1110 
    12 post_syncdb = object() 
     11pre_delete = Signal(argnames=["instance"]) 
     12post_delete = Signal(argnames=["instance"]) 
     13 
     14post_syncdb = Signal(argnames=["class", "app", "created_models", "verbosity", "interactive"]) 
  • django/dispatch/dispatcher.py

    old new  
    44providing the primary API and the core logic for the 
    55system. 
    66 
    7 Module attributes of note: 
    8  
    9     Any -- Singleton used to signal either "Any Sender" or 
    10         "Any Signal".  See documentation of the _Any class. 
    11     Anonymous -- Singleton used to signal "Anonymous Sender" 
    12         See documentation of the _Anonymous class. 
    13  
    147Internal attributes: 
    158    WEAKREF_TYPES -- tuple of types/classes which represent 
    169        weak references to receivers, and thus must be de- 
    1710        referenced on retrieval to retrieve the callable 
    1811        object 
    19     connections -- { senderkey (id) : { signal : [receivers...]}} 
    20     senders -- { senderkey (id) : weakref(sender) } 
    21         used for cleaning up sender references on sender 
    22         deletion 
    23     sendersBack -- { receiverkey (id) : [senderkey (id)...] } 
    24         used for cleaning up receiver references on receiver 
    25         deletion, (considerably speeds up the cleanup process 
    26         vs. the original code.) 
    2712""" 
    28 import types, weakref 
    29 from django.dispatch import saferef, robustapply, errors 
    3013 
    3114__author__ = "Patrick K. O'Brien <pobrien@orbtech.com>" 
    3215__cvsid__ = "$Id: dispatcher.py,v 1.9 2005/09/17 04:55:57 mcfletch Exp $" 
    3316__version__ = "$Revision: 1.9 $"[11:-2] 
    3417 
     18import weakref 
     19from django.dispatch import saferef, errors 
    3520 
    36 class _Parameter: 
    37     """Used to represent default parameter values.""" 
    38     def __repr__(self): 
    39         return self.__class__.__name__ 
    40  
    41 class _Any(_Parameter): 
    42     """Singleton used to signal either "Any Sender" or "Any Signal" 
    43  
    44     The Any object can be used with connect, disconnect, 
    45     send, or sendExact to signal that the parameter given 
    46     Any should react to all senders/signals, not just 
    47     a particular sender/signal. 
    48     """ 
    49 Any = _Any() 
    50  
    51 class _Anonymous(_Parameter): 
    52     """Singleton used to signal "Anonymous Sender" 
    53  
    54     The Anonymous object is used to signal that the sender 
    55     of a message is not specified (as distinct from being 
    56     "any sender").  Registering callbacks for Anonymous 
    57     will only receive messages sent without senders.  Sending 
    58     with anonymous will only send messages to those receivers 
    59     registered for Any or Anonymous. 
    60  
    61     Note: 
    62         The default sender for connect is Any, while the 
    63         default sender for send is Anonymous.  This has 
    64         the effect that if you do not specify any senders 
    65         in either function then all messages are routed 
    66         as though there was a single sender (Anonymous) 
    67         being used everywhere. 
    68     """ 
    69 Anonymous = _Anonymous() 
    70  
    7121WEAKREF_TYPES = (weakref.ReferenceType, saferef.BoundMethodWeakref) 
    7222 
    73 connections = {} 
    74 senders = {} 
    75 sendersBack = {} 
    76  
    77  
    78 def connect(receiver, signal=Any, sender=Any, weak=True): 
    79     """Connect receiver to sender for signal 
    80  
    81     receiver -- a callable Python object which is to receive 
    82         messages/signals/events.  Receivers must be hashable 
    83         objects. 
    84  
    85         if weak is True, then receiver must be weak-referencable 
    86         (more precisely saferef.safeRef() must be able to create 
    87         a reference to the receiver). 
     23class Signal(object): 
     24    """Base class for all signals 
    8825     
    89         Receivers are fairly flexible in their specification, 
    90         as the machinery in the robustApply module takes care 
    91         of most of the details regarding figuring out appropriate 
    92         subsets of the sent arguments to apply to a given 
    93         receiver. 
    94  
    95         Note: 
    96             if receiver is itself a weak reference (a callable), 
    97             it will be de-referenced by the system's machinery, 
    98             so *generally* weak references are not suitable as 
    99             receivers, though some use might be found for the 
    100             facility whereby a higher-level library passes in 
    101             pre-weakrefed receiver references. 
    102  
    103     signal -- the signal to which the receiver should respond 
     26    Internal attributes: 
     27        receivers -- { receriverkey (id) : (weakref(receiver), 
     28            acceptable_named_args, accepts_kwargs } 
     29        sendersBack -- { receiverkey (id) : [senderkey (id)...] } 
     30            used for cleaning up receiver references on receiver 
     31            deletion, (considerably speeds up the cleanup process 
     32            vs. the original code.) 
     33    """ 
    10434     
    105         if Any, receiver will receive any signal from the 
    106         indicated sender (which might also be Any, but is not 
    107         necessarily Any). 
    108          
    109         Otherwise must be a hashable Python object other than 
    110         None (DispatcherError raised on None). 
    111          
    112     sender -- the sender to which the receiver should respond 
     35    def __init__(self, argnames=[]): 
     36        """argnames -- A list of the arguments this signal can pass along in 
     37                       a send() call. 
     38        """ 
     39        self.receivers = {} 
     40        self.sendersBack = {} 
     41        self.argnames = set(argnames) 
     42    
     43    def connect(self, receiver, sender=None, weak=True): 
     44        """Connect receiver to sender for signal 
    11345     
    114         if Any, receiver will receive the indicated signals 
    115         from any sender. 
     46        receiver -- a function or an instance method which is to 
     47            receive messages/signals/events.  Receivers must be 
     48            hashable objects. 
     49     
     50            if weak is True, then receiver must be weak-referencable 
     51            (more precisely saferef.safeRef() must be able to create 
     52            a reference to the receiver). 
    11653         
    117         if Anonymous, receiver will only receive indicated 
    118         signals from send/sendExact which do not specify a 
    119         sender, or specify Anonymous explicitly as the sender. 
     54            Receivers must be able to accept the named arguments 
     55            declared in argnames. 
     56     
     57            Note: 
     58                if receiver is itself a weak reference (a callable), 
     59                it will be de-referenced by the system's machinery, 
     60                so *generally* weak references are not suitable as 
     61                receivers, though some use might be found for the 
     62                facility whereby a higher-level library passes in 
     63                pre-weakrefed receiver references. 
     64             
     65        sender -- the sender to which the receiver should respond 
     66            Must either be of type Signal, or None to receive events 
     67            from any sender. 
     68             
     69        weak -- whether to use weak references to the receiver 
     70            By default, the module will attempt to use weak 
     71            references to the receiver objects.  If this parameter 
     72            is false, then strong references will be used. 
     73     
     74        returns None 
     75        """ 
     76        receiverkey = id(receiver) 
     77        allowed_args = receiver.func_code.co_varnames 
     78        accepts_kwargs = receiver.func_code.co_flags & 8 
    12079 
    121         Otherwise can be any python object. 
    122          
    123     weak -- whether to use weak references to the receiver 
    124         By default, the module will attempt to use weak 
    125         references to the receiver objects.  If this parameter 
    126         is false, then strong references will be used. 
     80        if weak: 
     81            receiver = saferef.safeRef(receiver, onDelete=self._removeReceiver) 
     82             
     83        senderkey = id(sender) 
    12784 
    128     returns None, may raise DispatcherTypeError 
    129     """ 
    130     if signal is None: 
    131         raise errors.DispatcherTypeError( 
    132             'Signal cannot be None (receiver=%r sender=%r)'%( receiver,sender) 
    133         ) 
    134     if weak: 
    135         receiver = saferef.safeRef(receiver, onDelete=_removeReceiver) 
    136     senderkey = id(sender) 
    137  
    138     signals = connections.setdefault(senderkey, {}) 
    139  
    140     # Keep track of senders for cleanup. 
    141     # Is Anonymous something we want to clean up? 
    142     if sender not in (None, Anonymous, Any): 
    143         def remove(object, senderkey=senderkey): 
    144             _removeSender(senderkey=senderkey) 
    145         # Skip objects that can not be weakly referenced, which means 
    146         # they won't be automatically cleaned up, but that's too bad. 
    14785        try: 
    148             weakSender = weakref.ref(sender, remove) 
    149             senders[senderkey] = weakSender 
    150         except: 
     86            current = self.sendersBack.get( receiverkey ) 
     87            if current is None: 
     88                self.sendersBack[ receiverkey ] = current = [] 
     89            if senderkey not in current: 
     90                current.append(senderkey) 
     91        except Exception, e: 
    15192            pass 
     93     
     94        self.receivers[receiverkey] = (receiver, set(allowed_args), accepts_kwargs) 
    15295         
    153     receiverID = id(receiver) 
    154     # get current set, remove any current references to 
    155     # this receiver in the set, including back-references 
    156     if signals.has_key(signal): 
    157         receivers = signals[signal] 
    158         _removeOldBackRefs(senderkey, signal, receiver, receivers) 
    159     else: 
    160         receivers = signals[signal] = [] 
    161     try: 
    162         current = sendersBack.get( receiverID ) 
    163         if current is None: 
    164             sendersBack[ receiverID ] = current = [] 
    165         if senderkey not in current: 
    166             current.append(senderkey) 
    167     except: 
    168         pass 
    169  
    170     receivers.append(receiver) 
    171  
    172  
    173  
    174 def disconnect(receiver, signal=Any, sender=Any, weak=True): 
    175     """Disconnect receiver from sender for signal 
    176  
    177     receiver -- the registered receiver to disconnect 
    178     signal -- the registered signal to disconnect 
    179     sender -- the registered sender to disconnect 
    180     weak -- the weakref state to disconnect 
    181  
    182     disconnect reverses the process of connect, 
    183     the semantics for the individual elements are 
    184     logically equivalent to a tuple of 
    185     (receiver, signal, sender, weak) used as a key 
    186     to be deleted from the internal routing tables. 
    187     (The actual process is slightly more complex 
    188     but the semantics are basically the same). 
    189  
    190     Note: 
    191         Using disconnect is not required to cleanup 
    192         routing when an object is deleted, the framework 
    193         will remove routes for deleted objects 
    194         automatically.  It's only necessary to disconnect 
    195         if you want to stop routing to a live object. 
     96    def disconnect(self, receiver, sender=None, weak=True): 
     97        """Disconnect receiver from sender for signal 
     98     
     99        receiver -- the registered receiver to disconnect 
     100        sender -- the registered sender to disconnect 
     101        weak -- the weakref state to disconnect 
     102     
     103        disconnect reverses the process of connect, 
     104        the semantics for the individual elements are 
     105        logically equivalent to a tuple of 
     106        (receiver, signal, sender, weak) used as a key 
     107        to be deleted from the internal routing tables. 
     108        (The actual process is slightly more complex 
     109        but the semantics are basically the same). 
     110     
     111        Note: 
     112            Using disconnect is not required to cleanup 
     113            routing when an object is deleted, the framework 
     114            will remove routes for deleted objects 
     115            automatically.  It's only necessary to disconnect 
     116            if you want to stop routing to a live object. 
     117             
     118        returns None, may raise DispatcherKeyError 
     119        """ 
     120        receiverkey = id(receiver) 
     121        if weak: 
     122            receiver = saferef.safeRef(receiver) 
    196123         
    197     returns None, may raise DispatcherTypeError or 
    198         DispatcherKeyError 
    199     """ 
    200     if signal is None: 
    201         raise errors.DispatcherTypeError( 
    202             'Signal cannot be None (receiver=%r sender=%r)'%( receiver,sender) 
    203         ) 
    204     if weak: receiver = saferef.safeRef(receiver) 
    205     senderkey = id(sender) 
    206     try: 
    207         signals = connections[senderkey] 
    208         receivers = signals[signal] 
    209     except KeyError: 
    210         raise errors.DispatcherKeyError( 
    211             """No receivers found for signal %r from sender %r""" %( 
    212                 signal, 
    213                 sender 
    214             ) 
    215         ) 
    216     try: 
    217         # also removes from receivers 
    218         _removeOldBackRefs(senderkey, signal, receiver, receivers) 
    219     except ValueError: 
    220         raise errors.DispatcherKeyError( 
    221             """No connection to receiver %s for signal %s from sender %s""" %( 
    222                 receiver, 
    223                 signal, 
    224                 sender 
    225             ) 
    226         ) 
    227     _cleanupConnections(senderkey, signal) 
    228  
    229 def getReceivers( sender = Any, signal = Any ): 
    230     """Get list of receivers from global tables 
    231  
    232     This utility function allows you to retrieve the 
    233     raw list of receivers from the connections table 
    234     for the given sender and signal pair. 
    235  
    236     Note: 
    237         there is no guarantee that this is the actual list 
    238         stored in the connections table, so the value 
    239         should be treated as a simple iterable/truth value 
    240         rather than, for instance a list to which you 
    241         might append new records. 
    242  
    243     Normally you would use liveReceivers( getReceivers( ...)) 
    244     to retrieve the actual receiver objects as an iterable 
    245     object. 
    246     """ 
    247     existing = connections.get(id(sender)) 
    248     if existing is not None: 
    249         return existing.get(signal, []) 
    250     return [] 
    251  
    252 def liveReceivers(receivers): 
    253     """Filter sequence of receivers to get resolved, live receivers 
    254  
    255     This is a generator which will iterate over 
    256     the passed sequence, checking for weak references 
    257     and resolving them, then returning all live 
    258     receivers. 
    259     """ 
    260     for receiver in receivers: 
    261         if isinstance( receiver, WEAKREF_TYPES): 
    262             # Dereference the weak reference. 
    263             receiver = receiver() 
    264             if receiver is not None: 
    265                 yield receiver 
    266         else: 
    267             yield receiver 
    268  
    269  
    270  
    271 def getAllReceivers( sender = Any, signal = Any ): 
    272     """Get list of all receivers from global tables 
    273  
    274     This gets all dereferenced receivers which should receive 
    275     the given signal from sender, each receiver should 
    276     be produced only once by the resulting generator 
    277     """ 
    278     receivers = {} 
    279     # Get receivers that receive *this* signal from *this* sender. 
    280     # Add receivers that receive *any* signal from *this* sender. 
    281     # Add receivers that receive *this* signal from *any* sender. 
    282     # Add receivers that receive *any* signal from *any* sender. 
    283     l = [] 
    284     i = id(sender) 
    285     if i in connections: 
    286         sender_receivers = connections[i] 
    287         if signal in sender_receivers: 
    288             l.extend(sender_receivers[signal]) 
    289         if signal is not Any and Any in sender_receivers: 
    290             l.extend(sender_receivers[Any]) 
    291  
    292     if sender is not Any: 
    293         i = id(Any) 
    294         if i in connections: 
    295             sender_receivers = connections[i] 
    296             if sender_receivers is not None: 
    297                 if signal in sender_receivers: 
    298                     l.extend(sender_receivers[signal]) 
    299                 if signal is not Any and Any in sender_receivers: 
    300                     l.extend(sender_receivers[Any]) 
    301  
    302     for receiver in l: 
     124        # remove the backref to the sender 
    303125        try: 
    304             if not receiver in receivers: 
    305                 if isinstance(receiver, WEAKREF_TYPES): 
    306                     receiver = receiver() 
    307                     # this should only (rough guess) be possible if somehow, deref'ing 
    308                     # triggered a wipe. 
    309                     if receiver is None: 
    310                         continue 
    311                 receivers[receiver] = 1 
    312                 yield receiver 
    313         except TypeError: 
    314             # dead weakrefs raise TypeError on hash... 
    315             pass 
     126            senderkeys = self.sendersBack[receiverkey] 
     127            del senderkeys[senderkeys.index(id(sender))] 
     128        except (KeyError, ValueError): 
     129            return False 
    316130 
    317 def send(signal=Any, sender=Anonymous, *arguments, **named): 
    318     """Send signal from sender to all connected receivers. 
     131        # if there are no more backrefs left, clean up the receiver 
     132        try: 
     133            if not senderkeys: 
     134                del self.sendersBack[receiverkey] 
     135                del self.receivers[receiverkey] 
     136        except KeyError: 
     137            return False 
     138         
     139    def send(self, sender=None, **named): 
     140        """Send signal from sender to all connected receivers. 
     141         
     142        sender -- the sender of the signal 
     143            Can be any python object (normally one registered with 
     144            a connect if you actually want something to occur). 
    319145     
    320     signal -- (hashable) signal value, see connect for details 
    321  
    322     sender -- the sender of the signal 
     146        named -- named arguments which will be passed to receivers. 
     147            These arguments must be a subset of the argument names 
     148            defined in argnames, or a DispatcherTypeError will be 
     149            raised. 
    323150     
    324         if Any, only receivers registered for Any will receive 
    325         the message. 
     151        Return a list of tuple pairs [(receiver, response), ... ], 
     152        may raise DispatcherKeyError 
     153         
     154        if any receiver raises an error, the error propagates back 
     155        through send, terminating the dispatch loop, so it is quite 
     156        possible to not have all receivers called if a raises an 
     157        error. 
     158        """ 
     159        # enforce the API 
     160        for arg in named.keys(): 
     161            if arg not in self.argnames: 
     162                raise errors.DispatcherTypeError, \ 
     163                    "send got an unexpected keyword argument '%s'" % arg 
    326164 
    327         if Anonymous, only receivers registered to receive 
    328         messages from Anonymous or Any will receive the message 
    329  
    330         Otherwise can be any python object (normally one 
    331         registered with a connect if you actually want 
    332         something to occur). 
    333  
    334     arguments -- positional arguments which will be passed to 
    335         *all* receivers. Note that this may raise TypeErrors 
    336         if the receivers do not allow the particular arguments. 
    337         Note also that arguments are applied before named 
    338         arguments, so they should be used with care. 
    339  
    340     named -- named arguments which will be filtered according 
    341         to the parameters of the receivers to only provide those 
    342         acceptable to the receiver. 
    343  
    344     Return a list of tuple pairs [(receiver, response), ... ] 
    345  
    346     if any receiver raises an error, the error propagates back 
    347     through send, terminating the dispatch loop, so it is quite 
    348     possible to not have all receivers called if a raises an 
    349     error. 
    350     """ 
    351     # Call each receiver with whatever arguments it can accept. 
    352     # Return a list of tuple pairs [(receiver, response), ... ]. 
    353     responses = [] 
    354     for receiver in getAllReceivers(sender, signal): 
    355         response = robustapply.robustApply( 
    356             receiver, 
    357             signal=signal, 
    358             sender=sender, 
    359             *arguments, 
    360             **named 
    361         ) 
    362         responses.append((receiver, response)) 
    363     return responses 
    364  
    365  
    366 def sendExact( signal=Any, sender=Anonymous, *arguments, **named ): 
    367     """Send signal only to those receivers registered for exact message 
    368  
    369     sendExact allows for avoiding Any/Anonymous registered 
    370     handlers, sending only to those receivers explicitly 
    371     registered for a particular signal on a particular 
    372     sender. 
    373     """ 
    374     responses = [] 
    375     for receiver in liveReceivers(getReceivers(sender, signal)): 
    376         response = robustapply.robustApply( 
    377             receiver, 
    378             signal=signal, 
    379             sender=sender, 
    380             *arguments, 
    381             **named 
    382         ) 
    383         responses.append((receiver, response)) 
    384     return responses 
     165        senderkey = id(sender) 
     166        none_senderkey = id(None) 
     167         
     168        # Call each receiver. 
     169        # Return a list of tuple pairs [(receiver, response), ... ]. 
     170        responses = [] 
     171        for receiverkey, receiver in self.liveReceivers(): 
     172            senderkeys = self.sendersBack[receiverkey] 
     173            if none_senderkey in senderkeys or senderkey in senderkeys: 
     174                response = self._callReceiver( 
     175                    receiver, 
     176                    receiverkey, 
     177                    sender=sender, 
     178                    signal=self, 
     179                    **named) 
     180                responses.append((receiver, response)) 
     181        return responses 
    385182     
     183    def sendRobust(self, sender, **named): 
     184        """Send signal from sender to all connected receivers catching errors 
     185         
     186        sender -- the sender of the signal 
     187            Can be any python object (normally one registered with 
     188            a connect if you actually want something to occur). 
     189     
     190        named -- named arguments which will be passed to receivers. 
     191            These arguments must be a subset of the argument names 
     192            defined in argnames, or a DispatcherTypeError will be 
     193            raised. 
     194     
     195        Return a list of tuple pairs [(receiver, response), ... ], 
     196        may raise DispatcherKeyError 
     197     
     198        if any receiver raises an error (specifically any subclass of Exception), 
     199        the error instance is returned as the result for that receiver. 
     200        """ 
     201        # enforce the API 
     202        for arg in named.keys(): 
     203            if arg not in self.argnames: 
     204                raise DispatcherTypeError, "send got an unexpected keyword argument '%s'" % arg 
    386205 
    387 def _removeReceiver(receiver): 
    388     """Remove receiver from connections.""" 
    389     if not sendersBack: 
    390         # During module cleanup the mapping will be replaced with None 
    391         return False 
    392     backKey = id(receiver) 
    393     for senderkey in sendersBack.get(backKey,()): 
    394         try: 
    395             signals = connections[senderkey].keys() 
    396         except KeyError,err: 
    397             pass 
    398         else: 
    399             for signal in signals: 
    400                 try: 
    401                     receivers = connections[senderkey][signal] 
    402                 except KeyError: 
    403                     pass 
    404                 else: 
    405                     try: 
    406                         receivers.remove( receiver ) 
    407                     except Exception, err: 
    408                         pass 
    409                 _cleanupConnections(senderkey, signal) 
    410     try: 
    411         del sendersBack[ backKey ] 
    412     except KeyError: 
    413         pass 
    414              
    415 def _cleanupConnections(senderkey, signal): 
    416     """Delete any empty signals for senderkey. Delete senderkey if empty.""" 
    417     try: 
    418         receivers = connections[senderkey][signal] 
    419     except: 
    420         pass 
    421     else: 
    422         if not receivers: 
    423             # No more connected receivers. Therefore, remove the signal. 
     206        # Call each receiver with whatever arguments it can accept. 
     207        # Return a list of tuple pairs [(receiver, response), ... ]. 
     208        responses = [] 
     209        for receiverkey, receiver in self.liveReceivers(): 
    424210            try: 
    425                 signals = connections[senderkey] 
    426             except KeyError: 
    427                 pass 
     211                response = self._callReceiver( 
     212                    receiver, 
     213                    receiverkey, 
     214                    sender=sender, 
     215                    signal=self, 
     216                    **named) 
     217            except Exception, err: 
     218                responses.append((receiver, err)) 
    428219            else: 
    429                 del signals[signal] 
    430                 if not signals: 
    431                     # No more signal connections. Therefore, remove the sender. 
    432                     _removeSender(senderkey) 
     220                responses.append((receiver, response)) 
     221        return responses 
    433222 
    434 def _removeSender(senderkey): 
    435     """Remove senderkey from connections.""" 
    436     _removeBackrefs(senderkey) 
     223    def liveReceivers(self): 
     224        """Filter sequence of receivers to get resolved, live receivers 
     225     
     226        This is a generator which will iterate over 
     227        the passed sequence, checking for weak references 
     228        and resolving them, then returning all live 
     229        receivers. 
     230        """ 
     231        for (receiverkey, receiver) in self.receivers.items(): 
     232            receiver = receiver[0] 
     233            if isinstance(receiver, WEAKREF_TYPES): 
     234                # Dereference the weak reference. 
     235                receiver = receiver() 
     236                if receiver is not None: 
     237                    yield receiverkey, receiver 
     238            else: 
     239                yield receiverkey, receiver 
    437240 
    438     connections.pop(senderkey, None) 
    439     senders.pop(senderkey, None) 
    440  
    441  
    442 def _removeBackrefs( senderkey): 
    443     """Remove all back-references to this senderkey""" 
    444     for receiver_list in connections.pop(senderkey, {}).values(): 
    445         for receiver in receiver_list: 
    446             _killBackref( receiver, senderkey ) 
    447  
    448  
    449 def _removeOldBackRefs(senderkey, signal, receiver, receivers): 
    450     """Kill old sendersBack references from receiver 
    451  
    452     This guards against multiple registration of the same 
    453     receiver for a given signal and sender leaking memory 
    454     as old back reference records build up. 
    455  
    456     Also removes old receiver instance from receivers 
    457     """ 
    458     try: 
    459         index = receivers.index(receiver) 
    460         # need to scan back references here and remove senderkey 
    461     except ValueError: 
    462         return False 
    463     else: 
    464         oldReceiver = receivers[index] 
    465         del receivers[index] 
    466         found = 0 
    467         signals = connections.get(signal) 
    468         if signals is not None: 
    469             for sig,recs in connections.get(signal,{}).iteritems(): 
    470                 if sig != signal: 
    471                     for rec in recs: 
    472                         if rec is oldReceiver: 
    473                             found = 1 
    474                             break 
    475         if not found: 
    476             _killBackref( oldReceiver, senderkey ) 
    477             return True 
    478         return False 
     241    def _callReceiver(self, receiver, receiverkey, **named): 
     242        receiver_info = self.receivers[receiverkey] 
     243        if not receiver_info[2]: 
     244            acceptable_args = receiver_info[1] 
     245            for arg in named.keys(): 
     246                if arg not in acceptable_args: 
     247                    del named[arg] 
     248        return receiver(**named) 
    479249         
     250    def _removeReceiver(self, receiver): 
     251        """Remove receiver from connections.""" 
     252        if not self.sendersBack: 
     253            # TODO: Is this still true? 
     254            # During module cleanup the mapping will be replaced with None 
     255            return False 
     256        backKey = id(receiver) 
    480257         
    481 def _killBackref( receiver, senderkey ): 
    482     """Do the actual removal of back reference from receiver to senderkey""" 
    483     receiverkey = id(receiver) 
    484     receivers_list = sendersBack.get( receiverkey, () ) 
    485     while senderkey in receivers_list: 
    486258        try: 
    487             receivers_list.remove( senderkey ) 
    488         except: 
    489             break 
    490     if not receivers_list: 
     259            del self.sendersBack[backKey] 
     260        except KeyError: 
     261            pass 
    491262        try: 
    492             del sendersBack[ receiverkey
     263            del self.receivers[backKey
    493264        except KeyError: 
    494265            pass 
    495     return True 
     266 
     267def connect(receiver, signal, sender=None, weak=True): 
     268    """For backward compatibility only. See Signal.connect() 
     269    """ 
     270    return signal.connect(receiver, sender, weak) 
     271 
     272def disconnect(receiver, signal, sender=None, weak=True): 
     273    """For backward compatibility only. See Signal.disconnect() 
     274    """ 
     275    signal.disconnect(receiver, sender, weak) 
     276 
     277def send(signal, sender=None, **named): 
     278    """For backward compatibility only. See Signal.send() 
     279    """ 
     280    return signal.send(sender=sender, **named) 
     281 
     282def sendExact(signal, sender, **named ): 
     283    """This function is deprecated, as it now has the same 
     284    meaning as send. 
     285    """ 
     286    return signal.send(sender=sender, **named) 
     287     
  • django/dispatch/robust.py

    old new  
    11"""Module implementing error-catching version of send (sendRobust)""" 
    2 from django.dispatch.dispatcher import Any, Anonymous, liveReceivers, getAllReceivers 
    3 from django.dispatch.robustapply import robustApply 
     2from django.dispatch.dispatcher import Signal 
    43 
    5 def sendRobust( 
    6     signal=Any,  
    7     sender=Anonymous,  
    8     *arguments, **named 
    9 ): 
    10     """Send signal from sender to all connected receivers catching errors 
    11      
    12     signal -- (hashable) signal value, see connect for details 
    13  
    14     sender -- the sender of the signal 
    15      
    16         if Any, only receivers registered for Any will receive 
    17         the message. 
    18  
    19         if Anonymous, only receivers registered to receive 
    20         messages from Anonymous or Any will receive the message 
    21  
    22         Otherwise can be any python object (normally one 
    23         registered with a connect if you actually want 
    24         something to occur). 
    25  
    26     arguments -- positional arguments which will be passed to 
    27         *all* receivers. Note that this may raise TypeErrors 
    28         if the receivers do not allow the particular arguments. 
    29         Note also that arguments are applied before named 
    30         arguments, so they should be used with care. 
    31  
    32     named -- named arguments which will be filtered according 
    33         to the parameters of the receivers to only provide those 
    34         acceptable to the receiver. 
    35  
    36     Return a list of tuple pairs [(receiver, response), ... ] 
    37  
    38     if any receiver raises an error (specifically any subclass of Exception), 
    39     the error instance is returned as the result for that receiver. 
     4def sendRobust(signal, sender, **named): 
     5    """For backward compatibility only. See Signal.sendRobust() 
    406    """ 
    41     # Call each receiver with whatever arguments it can accept. 
    42     # Return a list of tuple pairs [(receiver, response), ... ]. 
    43     responses = [] 
    44     for receiver in liveReceivers(getAllReceivers(sender, signal)): 
    45         try: 
    46             response = robustApply( 
    47                 receiver, 
    48                 signal=signal, 
    49                 sender=sender, 
    50                 *arguments, 
    51                 **named 
    52             ) 
    53         except Exception, err: 
    54             responses.append((receiver, err)) 
    55         else: 
    56             responses.append((receiver, response)) 
    57     return responses 
     7    return signal.sendRobust(sender=sender, **named) 
  • django/dispatch/saferef.py

    old new  
    33from django.utils.functional import curry 
    44 
    55def safeRef(target, onDelete = None): 
    6     """Return a *safe* weak reference to a callable target 
     6    """Return a tuple of a *safe* weak reference to a callable 
     7    target and the id of that object. 
    78 
    89    target -- the object to be weakly referenced, if it's a 
    910        bound method reference, will create a BoundMethodWeakref, 
     
    2324                onDelete=onDelete 
    2425            ) 
    2526            return reference 
     27    # If a weakref is created using  
    2628    if callable(onDelete): 
    2729        return weakref.ref(target, onDelete) 
    2830    else: 
     
    234236        return BoundMethodWeakref(target=target, onDelete=onDelete) 
    235237    else: 
    236238        # no luck, use the alternative implementation: 
    237         return BoundNonDescriptorMethodWeakref(target=target, onDelete=onDelete) 
    238  
     239        return BoundNonDescriptorMethodWeakref(target=target, onDelete=onDelete) 
  • django/test/signals.py

    old new  
  • tests/regressiontests/dispatch/tests/__init__.py

    old new  
    22Unit-tests for the dispatch project 
    33""" 
    44 
    5 from test_dispatcher import * 
    6 from test_robustapply import * 
    75from test_saferef import *