web/lib/django/dispatch/dispatcher.py
changeset 38 77b6da96e6f1
equal deleted inserted replaced
37:8d941af65caf 38:77b6da96e6f1
       
     1 import weakref
       
     2 
       
     3 from django.dispatch import saferef
       
     4 
       
     5 WEAKREF_TYPES = (weakref.ReferenceType, saferef.BoundMethodWeakref)
       
     6 
       
     7 def _make_id(target):
       
     8     if hasattr(target, 'im_func'):
       
     9         return (id(target.im_self), id(target.im_func))
       
    10     return id(target)
       
    11 
       
    12 class Signal(object):
       
    13     """
       
    14     Base class for all signals
       
    15     
       
    16     Internal attributes:
       
    17     
       
    18         receivers
       
    19             { receriverkey (id) : weakref(receiver) }
       
    20     """
       
    21     
       
    22     def __init__(self, providing_args=None):
       
    23         """
       
    24         Create a new signal.
       
    25         
       
    26         providing_args
       
    27             A list of the arguments this signal can pass along in a send() call.
       
    28         """
       
    29         self.receivers = []
       
    30         if providing_args is None:
       
    31             providing_args = []
       
    32         self.providing_args = set(providing_args)
       
    33 
       
    34     def connect(self, receiver, sender=None, weak=True, dispatch_uid=None):
       
    35         """
       
    36         Connect receiver to sender for signal.
       
    37     
       
    38         Arguments:
       
    39         
       
    40             receiver
       
    41                 A function or an instance method which is to receive signals.
       
    42                 Receivers must be hashable objects.
       
    43 
       
    44                 if weak is True, then receiver must be weak-referencable (more
       
    45                 precisely saferef.safeRef() must be able to create a reference
       
    46                 to the receiver).
       
    47         
       
    48                 Receivers must be able to accept keyword arguments.
       
    49 
       
    50                 If receivers have a dispatch_uid attribute, the receiver will
       
    51                 not be added if another receiver already exists with that
       
    52                 dispatch_uid.
       
    53 
       
    54             sender
       
    55                 The sender to which the receiver should respond Must either be
       
    56                 of type Signal, or None to receive events from any sender.
       
    57 
       
    58             weak
       
    59                 Whether to use weak references to the receiver By default, the
       
    60                 module will attempt to use weak references to the receiver
       
    61                 objects. If this parameter is false, then strong references will
       
    62                 be used.
       
    63         
       
    64             dispatch_uid
       
    65                 An identifier used to uniquely identify a particular instance of
       
    66                 a receiver. This will usually be a string, though it may be
       
    67                 anything hashable.
       
    68         """
       
    69         from django.conf import settings
       
    70         
       
    71         # If DEBUG is on, check that we got a good receiver
       
    72         if settings.DEBUG:
       
    73             import inspect
       
    74             assert callable(receiver), "Signal receivers must be callable."
       
    75             
       
    76             # Check for **kwargs
       
    77             # Not all callables are inspectable with getargspec, so we'll
       
    78             # try a couple different ways but in the end fall back on assuming
       
    79             # it is -- we don't want to prevent registration of valid but weird
       
    80             # callables.
       
    81             try:
       
    82                 argspec = inspect.getargspec(receiver)
       
    83             except TypeError:
       
    84                 try:
       
    85                     argspec = inspect.getargspec(receiver.__call__)
       
    86                 except (TypeError, AttributeError):
       
    87                     argspec = None
       
    88             if argspec:
       
    89                 assert argspec[2] is not None, \
       
    90                     "Signal receivers must accept keyword arguments (**kwargs)."
       
    91         
       
    92         if dispatch_uid:
       
    93             lookup_key = (dispatch_uid, _make_id(sender))
       
    94         else:
       
    95             lookup_key = (_make_id(receiver), _make_id(sender))
       
    96 
       
    97         if weak:
       
    98             receiver = saferef.safeRef(receiver, onDelete=self._remove_receiver)
       
    99 
       
   100         for r_key, _ in self.receivers:
       
   101             if r_key == lookup_key:
       
   102                 break
       
   103         else:
       
   104             self.receivers.append((lookup_key, receiver))
       
   105 
       
   106     def disconnect(self, receiver=None, sender=None, weak=True, dispatch_uid=None):
       
   107         """
       
   108         Disconnect receiver from sender for signal.
       
   109 
       
   110         If weak references are used, disconnect need not be called. The receiver
       
   111         will be remove from dispatch automatically.
       
   112     
       
   113         Arguments:
       
   114         
       
   115             receiver
       
   116                 The registered receiver to disconnect. May be none if
       
   117                 dispatch_uid is specified.
       
   118             
       
   119             sender
       
   120                 The registered sender to disconnect
       
   121             
       
   122             weak
       
   123                 The weakref state to disconnect
       
   124             
       
   125             dispatch_uid
       
   126                 the unique identifier of the receiver to disconnect
       
   127         """
       
   128         if dispatch_uid:
       
   129             lookup_key = (dispatch_uid, _make_id(sender))
       
   130         else:
       
   131             lookup_key = (_make_id(receiver), _make_id(sender))
       
   132         
       
   133         for index in xrange(len(self.receivers)):
       
   134             (r_key, _) = self.receivers[index]
       
   135             if r_key == lookup_key:
       
   136                 del self.receivers[index]
       
   137                 break
       
   138 
       
   139     def send(self, sender, **named):
       
   140         """
       
   141         Send signal from sender to all connected receivers.
       
   142 
       
   143         If any receiver raises an error, the error propagates back through send,
       
   144         terminating the dispatch loop, so it is quite possible to not have all
       
   145         receivers called if a raises an error.
       
   146 
       
   147         Arguments:
       
   148         
       
   149             sender
       
   150                 The sender of the signal Either a specific object or None.
       
   151     
       
   152             named
       
   153                 Named arguments which will be passed to receivers.
       
   154 
       
   155         Returns a list of tuple pairs [(receiver, response), ... ].
       
   156         """
       
   157         responses = []
       
   158         if not self.receivers:
       
   159             return responses
       
   160 
       
   161         for receiver in self._live_receivers(_make_id(sender)):
       
   162             response = receiver(signal=self, sender=sender, **named)
       
   163             responses.append((receiver, response))
       
   164         return responses
       
   165 
       
   166     def send_robust(self, sender, **named):
       
   167         """
       
   168         Send signal from sender to all connected receivers catching errors.
       
   169 
       
   170         Arguments:
       
   171         
       
   172             sender
       
   173                 The sender of the signal Can be any python object (normally one
       
   174                 registered with a connect if you actually want something to
       
   175                 occur).
       
   176 
       
   177             named
       
   178                 Named arguments which will be passed to receivers. These
       
   179                 arguments must be a subset of the argument names defined in
       
   180                 providing_args.
       
   181 
       
   182         Return a list of tuple pairs [(receiver, response), ... ]. May raise
       
   183         DispatcherKeyError.
       
   184 
       
   185         if any receiver raises an error (specifically any subclass of
       
   186         Exception), the error instance is returned as the result for that
       
   187         receiver.
       
   188         """
       
   189         responses = []
       
   190         if not self.receivers:
       
   191             return responses
       
   192 
       
   193         # Call each receiver with whatever arguments it can accept.
       
   194         # Return a list of tuple pairs [(receiver, response), ... ].
       
   195         for receiver in self._live_receivers(_make_id(sender)):
       
   196             try:
       
   197                 response = receiver(signal=self, sender=sender, **named)
       
   198             except Exception, err:
       
   199                 responses.append((receiver, err))
       
   200             else:
       
   201                 responses.append((receiver, response))
       
   202         return responses
       
   203 
       
   204     def _live_receivers(self, senderkey):
       
   205         """
       
   206         Filter sequence of receivers to get resolved, live receivers.
       
   207 
       
   208         This checks for weak references and resolves them, then returning only
       
   209         live receivers.
       
   210         """
       
   211         none_senderkey = _make_id(None)
       
   212         receivers = []
       
   213 
       
   214         for (receiverkey, r_senderkey), receiver in self.receivers:
       
   215             if r_senderkey == none_senderkey or r_senderkey == senderkey:
       
   216                 if isinstance(receiver, WEAKREF_TYPES):
       
   217                     # Dereference the weak reference.
       
   218                     receiver = receiver()
       
   219                     if receiver is not None:
       
   220                         receivers.append(receiver)
       
   221                 else:
       
   222                     receivers.append(receiver)
       
   223         return receivers
       
   224 
       
   225     def _remove_receiver(self, receiver):
       
   226         """
       
   227         Remove dead receivers from connections.
       
   228         """
       
   229 
       
   230         to_remove = []
       
   231         for key, connected_receiver in self.receivers:
       
   232             if connected_receiver == receiver:
       
   233                 to_remove.append(key)
       
   234         for key in to_remove:
       
   235             for idx, (r_key, _) in enumerate(self.receivers):
       
   236                 if r_key == key:
       
   237                     del self.receivers[idx]