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