Django

Code

Ticket #6814: faster_signals-2.diff

File faster_signals-2.diff, 53.4 kB (added by jdunck, 10 months ago)

tentative implementation: "strong_list" in timings.

  • django/test/signals.py

    old new  
  • 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() 
    54 
    6 pre_save = object(
    7 post_save = object(
     5pre_init = Signal(providing_args=["args", "kwargs"]
     6post_init = Signal(providing_args=["instance"]
    87 
    9 pre_delete = object(
    10 post_delete = object(
     8pre_save = Signal(providing_args=["instance", "raw"]
     9post_save = Signal(providing_args=["instance", "raw", "created"]
    1110 
    12 post_syncdb = object() 
     11pre_delete = Signal(providing_args=["instance"]) 
     12post_delete = Signal(providing_args=["instance"]) 
     13 
     14post_syncdb = Signal(providing_args=["app", "created_models", "verbosity", "interactive"]) 
  • 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(providing_args=["request"]) 
  • django/dispatch/license.txt

    old new  
    1 PyDispatcher License 
    2  
    3     Copyright (c) 2001-2003, Patrick K. O'Brien and Contributors 
    4     All rights reserved. 
    5      
    6     Redistribution and use in source and binary forms, with or without 
    7     modification, are permitted provided that the following conditions 
    8     are met: 
    9      
    10         Redistributions of source code must retain the above copyright 
    11         notice, this list of conditions and the following disclaimer. 
    12      
    13         Redistributions in binary form must reproduce the above 
    14         copyright notice, this list of conditions and the following 
    15         disclaimer in the documentation and/or other materials 
    16         provided with the distribution. 
    17      
    18         The name of Patrick K. O'Brien, or the name of any Contributor, 
    19         may not be used to endorse or promote products derived from this  
    20         software without specific prior written permission. 
    21      
    22     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
    23     ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
    24     LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
    25     FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
    26     COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
    27     INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
    28     (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
    29     SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
    30     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 
    31     STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
    32     ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 
    33     OF THE POSSIBILITY OF SUCH DAMAGE.  
    34  
  • 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/dispatch/errors.py

    old new  
    33 
    44class DispatcherError(Exception): 
    55    """Base class for all Dispatcher errors""" 
    6 class DispatcherKeyError(KeyError, DispatcherError): 
    7     """Error raised when unknown (sender,signal) set specified""" 
    86class DispatcherTypeError(TypeError, DispatcherError): 
    97    """Error raised when inappropriate signal-type specified (None)""" 
    108 
  • django/dispatch/robust.py

    old new  
    1 """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 
    4  
    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. 
    40     """ 
    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 
  • django/dispatch/__init__.py

    old new  
    44__author__ = "Patrick K. O'Brien" 
    55__license__ = "BSD-style, see license.txt for details" 
    66 
     7""" 
     8new - weak_dict 
     90 receivers test 1.70392489433 
     101 receiver test 11.3397159576 1.23367714882 
     11""" 
     12""" 
     13new - strong dict 
     140 receivers test 2.07081604004 
     151 receiver test 7.21637010574 1.23387694359 
     1610 receivers test 24.1335361004 4.61771893501 
     17""" 
  • django/dispatch/dispatcher_weak_dict.py

    old new  
     1import weakref 
     2import inspect 
     3 
     4try: 
     5    set 
     6except NameError: 
     7    from sets import Set as set     # For Python 2.3 
     8 
     9from django.dispatch import saferef, errors 
     10 
     11WEAKREF_TYPES = (weakref.ReferenceType, saferef.BoundMethodWeakref) 
     12 
     13class Signal(object): 
     14    """Base class for all signals 
     15     
     16    Internal attributes: 
     17        receivers -- { receiver_key (id) : (receiver,  
     18                                           acceptable_named_args,  
     19                                           accepts_kwargs) } 
     20    """ 
     21    def __init__(self, providing_args=[]): 
     22        """providing_args -- A list of the arguments this signal can pass along in 
     23                       a send() call. 
     24        """ 
     25        self._receivers = {} 
     26        self.providing_args = set(providing_args) 
     27     
     28    def connect(self, receiver, sender=None, weak=False): 
     29        """Connect receiver to sender for signal 
     30     
     31        receiver -- a function or an instance method which is to 
     32            receive messages/signals/events.  Receivers must be 
     33            hashable objects. 
     34     
     35            if weak is True, then receiver must be weak-referencable 
     36            (more precisely saferef.safeRef() must be able to create 
     37            a reference to the receiver). 
     38         
     39            Receivers must be able to accept the named arguments 
     40            declared in providing_args. 
     41     
     42            Note: 
     43                if receiver is itself a weak reference (a callable), 
     44                it will be de-referenced by the system's machinery, 
     45                so *generally* weak references are not suitable as 
     46                receivers, though some use might be found for the 
     47                facility whereby a higher-level library passes in 
     48                pre-weakrefed receiver references. 
     49             
     50        sender -- the sender to which the receiver should respond 
     51            Must either be of type Signal, or None to receive events 
     52            from any sender. 
     53             
     54        weak -- whether to use weak references to the receiver 
     55            By default, the module will attempt to use weak 
     56            references to the receiver objects.  If this parameter 
     57            is false, then strong references will be used. 
     58     
     59        returns None 
     60        """ 
     61        allowed_args, _, kw_var, _ = inspect.getargspec(receiver) 
     62        accepts_kwargs = kw_var is not None 
     63         
     64        receiver_key = (id(sender), id(receiver)) 
     65         
     66        if weak: 
     67            receiver = saferef.safeRef(receiver) 
     68         
     69        self._receivers[receiver_key] = (receiver, set(allowed_args), accepts_kwargs) 
     70         
     71    def disconnect(self, receiver, sender=None, weak=True): 
     72        """Disconnect receiver from sender for signal 
     73     
     74        receiver -- the registered receiver to disconnect 
     75        sender -- the registered sender to disconnect, or None  
     76                  if the receiver should be disconnected from all. 
     77        weak -- deprecated and ignored. 
     78     
     79        disconnect reverses the process of connect. 
     80     
     81        Note: 
     82            Using disconnect is not required to cleanup 
     83            routing when an object is deleted, the framework 
     84            will skip routes for deleted objects 
     85            automatically.  It's only necessary to disconnect 
     86            if you want to stop routing to a live object. 
     87             
     88        returns None 
     89        """ 
     90        receiver_key = (id(sender), id(receiver)) 
     91        try: 
     92            del self._receivers[receiver_key] 
     93        except KeyError: 
     94            return False 
     95     
     96    def send(self, sender, **named): 
     97        """ 
     98        Send signal from sender to all connected receivers. 
     99         
     100        sender -- the sender of the signal 
     101            Can be any python object (normally one registered with 
     102            a connect if you actually want something to occur). 
     103     
     104        named -- named arguments which will be passed to receivers. 
     105            These arguments must be a subset of the argument names 
     106            defined in providing_args, or a DispatcherTypeError will be 
     107            raised. 
     108     
     109        Return a list of tuple pairs [(receiver, response), ... ]. 
     110 
     111        An exception from any receiver halts dispatch and propagates 
     112        back through send. 
     113        """ 
     114        responses = [] 
     115        if not self._receivers: 
     116            return responses 
     117 
     118        self._enforce_send_api(named) #FIXME: 20% speedup in send if we take this out 
     119        for receiver_key, receiver_info in self.get_live_receivers(sender): 
     120            receiver, allowed_args, accepts_kwargs = receiver_info 
     121 
     122            allowed = named.copy() 
     123            allowed['sender'] = sender 
     124                         
     125            if not accepts_kwargs: 
     126                for arg in allowed.keys(): 
     127                    if arg not in allowed_args: 
     128                        del allowed[arg] 
     129 
     130            responses.append((receiver, receiver(**allowed))) 
     131        return responses 
     132         
     133    def get_live_receivers(self, sender): 
     134        """ 
     135        Returns an iterable over all live receivers for the signal. 
     136        """ 
     137        listening_to_all = id(None) 
     138        sending_from = id(sender) 
     139        for ((listen_to_id, receiver_id), receiver_info) in self._receivers.items(): 
     140            if listen_to_id != listening_to_all and sending_from != listen_to_id: 
     141                continue 
     142            receiver_, allowed_args, accepts_kwargs = receiver_info 
     143            if not self._receiver_is_live(receiver_): 
     144                del self._receivers[(listen_to_id, receiver_id)] 
     145                continue 
     146            yield (sending_from, receiver_id), (self._get_receiver(receiver_), allowed_args, accepts_kwargs) 
     147             
     148 
     149    def _get_receiver(self, receiver): 
     150        if isinstance(receiver, WEAKREF_TYPES): 
     151            return receiver() 
     152        else: 
     153            return receiver 
     154         
     155    def _receiver_is_live(self, receiver): 
     156        return bool(self._get_receiver(receiver) is not None) 
     157 
     158    def _enforce_send_api(self, sent_names): 
     159        """ 
     160        Ensures that send calls are made with the parameters declared  
     161          in Signal construction. 
     162        """ 
     163        sent_names_set = set(sent_names.keys()) 
     164 
     165        if sent_names_set != self.providing_args: 
     166            pluralize = lambda qty: qty != 1 and 's' or '' 
     167            flatten = lambda s: ",".join([str(item) for item in s]) 
     168 
     169            extras = sent_names_set - self.providing_args 
     170            missing = self.providing_args - sent_names_set 
     171             
     172            message = "Signal.send" 
     173            if extras: 
     174                message += " got unexpected argment%s: %s" % (pluralize(len(extras)), flatten(extras)) 
     175            if missing: 
     176                if extras: 
     177                    message += " and" 
     178                message += " missing expected argment%s: %s" % (pluralize(len(missing)), flatten(missing)) 
     179             
     180            raise errors.DispatcherTypeError, message 
     181 
     182 
     183def connect(receiver, signal, sender=None, weak=True): 
     184    """For backward compatibility only. See Signal.connect() 
     185    """ 
     186    return signal.connect(receiver, sender, weak) 
     187 
     188def disconnect(receiver, signal, sender=None, weak=True): 
     189    """For backward compatibility only. See Signal.disconnect() 
     190    """ 
     191    signal.disconnect(receiver, sender, weak) 
     192 
     193def send(signal, sender=None, **named): 
     194    """For backward compatibility only. See Signal.send() 
     195    """ 
     196    return signal.send(sender=sender, **named) 
     197 
     198def sendExact(signal, sender, **named ): 
     199    """This function is deprecated, as it now has the same 
     200    meaning as send. 
     201    """ 
     202    return signal.send(sender=sender, **named) 
  • django/dispatch/robustapply.py

    old new  
    1 """Robust apply mechanism 
    2  
    3 Provides a function "call", which can sort out 
    4 what arguments a given callable object can take, 
    5 and subset the given arguments to match only 
    6 those which are acceptable. 
    7 """ 
    8  
    9 def function( receiver ): 
    10     """Get function-like callable object for given receiver 
    11  
    12     returns (function_or_method, codeObject, fromMethod) 
    13  
    14     If fromMethod is true, then the callable already 
    15     has its first argument bound 
    16     """ 
    17     if hasattr(receiver, '__call__'): 
    18         # receiver is a class instance; assume it is callable. 
    19         # Reassign receiver to the actual method that will be called. 
    20         if hasattr( receiver.__call__, 'im_func') or hasattr( receiver.__call__, 'im_code'): 
    21             receiver = receiver.__call__ 
    22     if hasattr( receiver, 'im_func' ): 
    23         # an instance-method... 
    24         return receiver, receiver.im_func.func_code, 1 
    25     elif not hasattr( receiver, 'func_code'): 
    26         raise ValueError('unknown reciever type %s %s'%(receiver, type(receiver))) 
    27     return receiver, receiver.func_code, 0 
    28  
    29 def robustApply(receiver, *arguments, **named): 
    30     """Call receiver with arguments and an appropriate subset of named 
    31     """ 
    32     receiver, codeObject, startIndex = function( receiver ) 
    33     acceptable = codeObject.co_varnames[startIndex+len(arguments):codeObject.co_argcount] 
    34     for name in codeObject.co_varnames[startIndex:startIndex+len(arguments)]: 
    35         if named.has_key( name ): 
    36             raise TypeError( 
    37                 """Argument %r specified both positionally and as a keyword for calling %r"""% ( 
    38                     name, receiver, 
    39                 ) 
    40             ) 
    41     if not (codeObject.co_flags & 8): 
    42         # fc does not have a **kwds type parameter, therefore  
    43         # remove unacceptable arguments. 
    44         for arg in named.keys(): 
    45             if arg not in acceptable: 
    46                 del named[arg] 
    47     return receiver(*arguments, **named) 
  • django/dispatch/dispatcher_strong_dict.py

    old new  
     1import inspect 
     2 
     3try: 
     4    set 
     5except NameError: 
     6    from sets import Set as set     # For Python 2.3 
     7 
     8from django.dispatch import errors 
     9 
     10class Signal(object): 
     11    """Base class for all signals 
     12     
     13    Internal attributes: 
     14        receivers -- { receiver_key (id) : (receiver,  
     15                                           acceptable_named_args,  
     16                                           accepts_kwargs) } 
     17    """ 
     18     
     19    def __init__(self, providing_args=[]): 
     20        """providing_args -- A list of the arguments this signal can pass along in 
     21                       a send() call. 
     22        """ 
     23        self._receivers = {} 
     24        self.providing_args = set(providing_args) 
     25     
     26    def connect(self, receiver, sender=None): 
     27        """Connect receiver to sender for signal 
     28     
     29        receiver -- a function or an instance method which is to 
     30            receive messages/signals/events.  Receivers must be 
     31            hashable objects. 
     32     
     33            Receivers must be able to accept the named arguments 
     34            declared in providing_args. 
     35     
     36        sender -- the sender to which the receiver should respond 
     37            Must either be of type Signal, or None to receive events 
     38            from any sender. 
     39             
     40        returns None 
     41        """ 
     42        allowed_args, _, kw_var, _ = inspect.getargspec(receiver) 
     43        accepts_kwargs = kw_var is not None 
     44         
     45        receiver_key = (id(sender), id(receiver)) 
     46         
     47        self._receivers[receiver_key] = (receiver, set(allowed_args), accepts_kwargs) 
     48         
     49    def disconnect(self, receiver, sender=None): 
     50        """Disconnect receiver from sender for signal 
     51     
     52        receiver -- the registered receiver to disconnect 
     53        sender -- the registered sender to disconnect, or None  
     54                  if the receiver should be disconnected from all. 
     55     
     56        disconnect reverses the process of connect. 
     57     
     58        Note: 
     59            Using disconnect is not required to cleanup 
     60            routing when an object is deleted, the framework 
     61            will skip routes for deleted objects 
     62            automatically.  It's only necessary to disconnect 
     63            if you want to stop routing to a live object. 
     64             
     65        returns None 
     66        """ 
     67        receiver_key = (id(sender), id(receiver)) 
     68        try: 
     69            del self._receivers[receiver_key] 
     70        except KeyError: 
     71            return False 
     72     
     73    def send(self, sender, **named): 
     74        """ 
     75        Send signal from sender to all connected receivers. 
     76         
     77        sender -- the sender of the signal 
     78            Can be any python object (normally one registered with 
     79            a connect if you actually want something to occur). 
     80     
     81        named -- named arguments which will be passed to receivers. 
     82            These arguments must be a subset of the argument names 
     83            defined in providing_args, or a DispatcherTypeError will be 
     84            raised. 
     85     
     86        Return a list of tuple pairs [(receiver, response), ... ]. 
     87 
     88        An exception from any receiver halts dispatch and propagates 
     89        back through send. 
     90        """ 
     91        responses = [] 
     92        if len(self._receivers) == 0: 
     93            return responses 
     94 
     95        self._enforce_send_api(named) #FIXME: 20% speedup in send if we take this out 
     96 
     97        listening_to_all = id(None) 
     98        sending_from = id(sender) 
     99         
     100        for ((listen_to_id, receiver_id), receiver_info) in self._receivers.items(): 
     101            if listen_to_id != listening_to_all and sending_from != listen_to_id: 
     102                continue 
     103 
     104            receiver, allowed_args, accepts_kwargs = receiver_info 
     105 
     106            allowed = named.copy() 
     107            allowed['sender'] = sender 
     108                         
     109            if not accepts_kwargs: 
     110                for arg in allowed.keys(): 
     111                    if arg not in allowed_args: 
     112                        del allowed[arg] 
     113 
     114            responses.append((receiver, receiver(**allowed))) 
     115        return responses 
     116         
     117    def _enforce_send_api(self, sent_names): 
     118        """ 
     119        Ensures that send calls are made with the parameters declared  
     120          in Signal construction. 
     121        """ 
     122        sent_names_set = set(sent_names.keys()) 
     123 
     124        if sent_names_set != self.providing_args: 
     125            pluralize = lambda qty: qty != 1 and 's' or '' 
     126            flatten = lambda s: ",".join([str(item) for item in s]) 
     127 
     128            extras = sent_names_set - self.providing_args 
     129            missing = self.providing_args - sent_names_set 
     130             
     131            message = "Signal.send" 
     132            if extras: 
     133                message += " got unexpected argment%s: %s" % (pluralize(len(extras)), flatten(extras)) 
     134            if missing: 
     135                if extras: 
     136                    message += " and" 
     137                message += " missing expected argment%s: %s" % (pluralize(len(missing)), flatten(missing)) 
     138             
     139            raise errors.DispatcherTypeError, message 
     140 
     141 
     142def connect(receiver, signal, sender=None): 
     143    """For backward compatibility only. See Signal.connect() 
     144    """ 
     145    return signal.connect(receiver, sender) 
     146 
     147def disconnect(receiver, signal, sender=None): 
     148    """For backward compatibility only. See Signal.disconnect() 
     149    """ 
     150    signal.disconnect(receiver, sender) 
     151 
     152def send(signal, sender=None, **named): 
     153    """For backward compatibility only. See Signal.send() 
     154    """ 
     155    return signal.send(sender=sender, **named) 
     156 
     157def sendExact(signal, sender, **named): 
     158    """This function is deprecated, as it now has the same 
     159    meaning as send. 
     160    """ 
     161    return signal.send(sender=sender, **named) 
  • django/dispatch/dispatcher.py

    old new  
    1 """Multiple-producer-multiple-consumer signal-dispatching 
     1import inspect 
    22 
    3 dispatcher is the core of the PyDispatcher system, 
    4 providing the primary API and the core logic for the 
    5 system. 
     3try: 
     4    set 
     5except NameError: 
     6    from sets import Set as set     # For Python 2.3 
    67 
    7 Module attributes of note: 
     8from django.dispatch import errors 
    89 
    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. 
     10pluralize = lambda qty: qty != 1 and 's' or '' 
     11flatten = lambda s: ",".join([str(item) for item in s]) 
    1312 
    14 Internal attributes: 
    15     WEAKREF_TYPES -- tuple of types/classes which represent 
    16         weak references to receivers, and thus must be de- 
    17         referenced on retrieval to retrieve the callable 
    18         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.) 
    27 """ 
    28 import types, weakref 
    29 from django.dispatch import saferef, robustapply, errors 
    30  
    31 __author__ = "Patrick K. O'Brien <pobrien@orbtech.com>" 
    32 __cvsid__ = "$Id: dispatcher.py,v 1.9 2005/09/17 04:55:57 mcfletch Exp $" 
    33 __version__ = "$Revision: 1.9 $"[11:-2] 
    34  
    35  
    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. 
     13class Signal(object): 
    4814    """ 
    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. 
     15    Internal attributes: 
     16        _receivers -- { receiver_key (id) : (sender_id, 
     17                                           receiver,  
     18                                           acceptable_named_args,  
     19                                           accepts_kwargs) } 
    6820    """ 
    69 Anonymous = _Anonymous() 
    70  
    71 WEAKREF_TYPES = (weakref.ReferenceType, saferef.BoundMethodWeakref) 
    72  
    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). 
    8821     
    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 
     22    def __init__(self, providing_args=[]): 
     23        """providing_args -- A list of the arguments this signal will pass along in 
     24                       a send() call. 
     25        """ 
     26        self._receivers = [] 
     27        self.providing_args = set(providing_args) 
    10428     
    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 
     29    def connect(self, receiver, sender=None): 
     30        """Connect receiver to sender for signal 
    11331     
    114         if Any, receiver will receive the indicated signals 
    115         from any sender. 
     32        receiver -- a function or an instance method which is to 
     33            receive messages/signals/events.  Receivers must be 
     34            hashable objects. 
     35     
     36            Receivers must be able to accept the named arguments 
     37            declared in providing_args. 
     38     
     39        sender -- the sender to which the receiver should respond 
     40            Must either be of type Signal, or None to receive events 
     41            from any sender. 
     42             
     43        returns None 
     44        """ 
     45        allowed_args, _, kw_var, _ = inspect.getargspec(receiver) 
     46        allowed_args = set(allowed_args)  
     47        accepts_kwargs = kw_var is not None 
    11648         
    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. 
    120  
    121         Otherwise can be any python object. 
     49        if not accepts_kwargs: 
     50            self._enforce_receive_api(receiver, allowed_args) 
    12251         
    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. 
     52        sender_id = id(sender)  
     53        receiver_id = id(receiver) 
    12754 
    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. 
    147         try: 
    148             weakSender = weakref.ref(sender, remove) 
    149             senders[senderkey] = weakSender 
    150         except: 
    151             pass 
     55        for existing_sender_id, existing_receiver, _, _ in self._receivers: 
     56            if self._receiver_match(existing_sender_id, id(existing_receiver), sender_id, receiver_id): 
     57                return 
     58        receiver_info = (sender_id, receiver, allowed_args, accepts_kwargs) 
     59        self._receivers.append(receiver_info) 
    15260         
    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 
     61    def disconnect(self, receiver, sender=None): 
     62        """Disconnect receiver from sender for signal 
     63     
     64        receiver -- the registered receiver to disconnect 
     65        sender -- the registered sender to disconnect, or None  
     66                  if the receiver should be disconnected from all. 
     67     
     68        disconnect reverses the process of connect. 
     69     
     70        Note: 
     71            Using disconnect is not required to cleanup 
     72            routing when an object is deleted, the framework 
     73            will skip routes for deleted objects 
     74            automatically.  It's only necessary to disconnect 
     75            if you want to stop routing to a live object. 
     76             
     77        returns None 
     78        """ 
     79        requested_sender_id = id(sender) 
     80        requested_receiver_id = id(receiver) 
    16981 
    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. 
     82        for i, (sender_id, receiver, _, _) in enumerate(self._receivers): 
     83            if self._receiver_match(requested_sender_id, 
     84                                    requested_receiver_id, 
     85                                    sender_id, 
     86                                    id(receiver)): 
     87                self._receivers.pop(i) 
     88                return True 
     89        return False 
     90     
     91    def _receiver_match(self, requested_sender_id, requested_receiver_id,  
     92            sender_id, receiver_id): 
     93        return bool(requested_sender_id == sender_id and requested_receiver_id == receiver_id) 
     94     
     95    def send(self, sender, **named): 
     96        """ 
     97        Send signal from sender to all connected receivers. 
    19698         
    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: 
    303