Django

Code

Changeset 8223

Show
Ignore:
Timestamp:
08/06/08 10:32:46 (4 months ago)
Author:
jacob
Message:

Major refactoring of django.dispatch with an eye towards speed. The net result is that signals are up to 90% faster.

Though some attempts and backwards-compatibility were made, speed trumped compatibility. Thus, as usual, check BackwardsIncompatibleChanges for the complete list of backwards-incompatible changes.

Thanks to Jeremy Dunck and Keith Busell for the bulk of the work; some ideas from Brian Herring's previous work (refs #4561) were incorporated.

Documentation is, sigh, still forthcoming.

Fixes #6814 and #3951 (with the new dispatch_uid argument to connect).

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • django/trunk/django/contrib/auth/management/__init__.py

    r7598 r8223  
    33""" 
    44 
    5 from django.dispatch import dispatcher 
    65from django.db.models import get_models, signals 
    76from django.contrib.auth import models as auth_app 
     
    1716    return perms + list(opts.permissions) 
    1817 
    19 def create_permissions(app, created_models, verbosity): 
     18def create_permissions(app, created_models, verbosity, **kwargs): 
    2019    from django.contrib.contenttypes.models import ContentType 
    2120    from django.contrib.auth.models import Permission 
     
    4645            break 
    4746 
    48 if 'create_permissions' not in [i.__name__ for i in dispatcher.getAllReceivers(signal=signals.post_syncdb)]: 
    49     dispatcher.connect(create_permissions, signal=signals.post_syncdb
    50 if 'create_superuser' not in [i.__name__ for i in dispatcher.getAllReceivers(signal=signals.post_syncdb, sender=auth_app)]: 
    51     dispatcher.connect(create_superuser, sender=auth_app, signal=signals.post_syncdb
     47signals.post_syncdb.connect(create_permissions, 
     48    dispatch_uid = "django.contrib.auth.management.create_permissions"
     49signals.post_syncdb.connect(create_superuser, 
     50    sender=auth_app, dispatch_uid = "django.contrib.auth.management.create_superuser"
  • django/trunk/django/contrib/contenttypes/generic.py

    r8170 r8223  
    99from django.db.models.fields.related import RelatedField, Field, ManyToManyRel 
    1010from django.db.models.loading import get_model 
    11 from django.dispatch import dispatcher 
    1211from django.utils.functional import curry 
    1312 
     
    3029 
    3130        # For some reason I don't totally understand, using weakrefs here doesn't work. 
    32         dispatcher.connect(self.instance_pre_init, signal=signals.pre_init, sender=cls, weak=False) 
     31        signals.pre_init.connect(self.instance_pre_init, sender=cls, weak=False) 
    3332 
    3433        # Connect myself as the descriptor for this field 
    3534        setattr(cls, name, self) 
    3635 
    37     def instance_pre_init(self, signal, sender, args, kwargs): 
     36    def instance_pre_init(self, signal, sender, args, kwargs, **_kwargs): 
    3837        """ 
    3938        Handles initializing an object with the generic FK instaed of 
  • django/trunk/django/contrib/contenttypes/management.py

    r6287 r8223  
    11from django.contrib.contenttypes.models import ContentType 
    2 from django.dispatch import dispatcher 
    32from django.db.models import get_apps, get_models, signals 
    43from django.utils.encoding import smart_unicode 
    54 
    6 def update_contenttypes(app, created_models, verbosity=2): 
     5def update_contenttypes(app, created_models, verbosity=2, **kwargs): 
    76    """ 
    87    Creates content types for models in the given app, removing any model 
     
    3837        update_contenttypes(app, None, verbosity) 
    3938 
    40 dispatcher.connect(update_contenttypes, signal=signals.post_syncdb
     39signals.post_syncdb.connect(update_contenttypes
    4140 
    4241if __name__ == "__main__": 
  • django/trunk/django/contrib/sites/management.py

    r7723 r8223  
    33""" 
    44 
    5 from django.dispatch import dispatcher 
    65from django.db.models import signals 
    76from django.contrib.sites.models import Site 
    87from django.contrib.sites import models as site_app 
    98 
    10 def create_default_site(app, created_models, verbosity): 
     9def create_default_site(app, created_models, verbosity, **kwargs): 
    1110    if Site in created_models: 
    1211        if verbosity >= 2: 
     
    1615    Site.objects.clear_cache() 
    1716 
    18 dispatcher.connect(create_default_site, sender=site_app, signal=signals.post_syncdb
     17signals.post_syncdb.connect(create_default_site, sender=site_app
  • django/trunk/django/core/handlers/base.py

    r8015 r8223  
    33from django import http 
    44from django.core import signals 
    5 from django.dispatch import dispatcher 
    65from django.utils.encoding import force_unicode 
    76 
     
    123122            # Get the exception info now, in case another exception is thrown later. 
    124123            exc_info = sys.exc_info() 
    125             receivers = dispatcher.send(signal=signals.got_request_exception, request=request) 
     124            receivers = signals.got_request_exception.send(sender=self.__class__, request=request) 
    126125            return self.handle_uncaught_exception(request, resolver, exc_info) 
    127126 
  • django/trunk/django/core/handlers/modpython.py

    r8032 r8223  
    66from django.core.handlers.base import BaseHandler 
    77from django.core.urlresolvers import set_script_prefix 
    8 from django.dispatch import dispatcher 
    98from django.utils import datastructures 
    109from django.utils.encoding import force_unicode, smart_str 
     
    175174 
    176175        set_script_prefix(req.get_options().get('django.root', '')) 
    177         dispatcher.send(signal=signals.request_started
     176        signals.request_started.send(sender=self.__class__
    178177        try: 
    179178            try: 
     
    189188                response = self.apply_response_fixes(request, response) 
    190189        finally: 
    191             dispatcher.send(signal=signals.request_finished
     190            signals.request_finished.send(sender=self.__class__
    192191 
    193192        # Convert our custom HttpResponse object back into the mod_python req. 
  • django/trunk/django/core/handlers/wsgi.py

    r8032 r8223  
    1010from django.core.handlers import base 
    1111from django.core.urlresolvers import set_script_prefix 
    12 from django.dispatch import dispatcher 
    1312from django.utils import datastructures 
    1413from django.utils.encoding import force_unicode 
     
    208207 
    209208        set_script_prefix(base.get_script_name(environ)) 
    210         dispatcher.send(signal=signals.request_started
     209        signals.request_started.send(sender=self.__class__
    211210        try: 
    212211            try: 
     
    222221                response = self.apply_response_fixes(request, response) 
    223222        finally: 
    224             dispatcher.send(signal=signals.request_finished
     223            signals.request_finished.send(sender=self.__class__
    225224 
    226225        try: 
  • django/trunk/django/core/management/commands/flush.py

    r7294 r8223  
    1616        from django.conf import settings 
    1717        from django.db import connection, transaction, models 
    18         from django.dispatch import dispatcher 
    1918        from django.core.management.sql import sql_flush, emit_post_sync_signal 
    2019 
  • django/trunk/django/core/management/sql.py

    r8157 r8223  
    493493        if verbosity >= 2: 
    494494            print "Running post-sync handlers for application", app_name 
    495         dispatcher.send(signal=models.signals.post_syncdb, sender=app, 
    496             app=app, created_models=created_models
    497             verbosity=verbosity, interactive=interactive) 
     495        models.signals.post_syncdb.send(sender=app, app=app, 
     496            created_models=created_models, verbosity=verbosity
     497            interactive=interactive) 
  • django/trunk/django/core/signals.py

    r4265 r8223  
    1 request_started = object() 
    2 request_finished = object() 
    3 got_request_exception = object() 
     1from django.dispatch import Signal 
     2 
     3request_started = Signal() 
     4request_finished = Signal() 
     5got_request_exception = Signal(providing_args=["request"]) 
  • django/trunk/django/db/__init__.py

    r7477 r8223  
    33from django.core import signals 
    44from django.core.exceptions import ImproperlyConfigured 
    5 from django.dispatch import dispatcher 
    65from django.utils.functional import curry 
    76 
     
    5958# Register an event that closes the database connection 
    6059# when a Django request is finished. 
    61 dispatcher.connect(connection.close, signal=signals.request_finished) 
     60def close_connection(**kwargs): 
     61    connection.close() 
     62signals.request_finished.connect(close_connection) 
    6263 
    6364# Register an event that resets connection.queries 
    6465# when a Django request is started. 
    65 def reset_queries(): 
     66def reset_queries(**kwargs): 
    6667    connection.queries = [] 
    67 dispatcher.connect(reset_queries, signal=signals.request_started
     68signals.request_started.connect(reset_queries
    6869 
    6970# Register an event that rolls back the connection 
    7071# when a Django request has an exception. 
    71 def _rollback_on_exception(): 
     72def _rollback_on_exception(**kwargs): 
    7273    from django.db import transaction 
    7374    transaction.rollback_unless_managed() 
    74 dispatcher.connect(_rollback_on_exception, signal=signals.got_request_exception) 
     75signals.got_request_exception.connect(_rollback_on_exception) 
  • django/trunk/django/db/models/base.py

    r8217 r8223  
    2020from django.db.models import signals 
    2121from django.db.models.loading import register_models, get_model 
    22 from django.dispatch import dispatcher 
    2322from django.utils.functional import curry 
    2423from django.utils.encoding import smart_str, force_unicode, smart_unicode 
     
    162161            cls.get_absolute_url = curry(get_absolute_url, opts, cls.get_absolute_url) 
    163162 
    164         dispatcher.send(signal=signals.class_prepared, sender=cls) 
     163        signals.class_prepared.send(sender=cls) 
    165164 
    166165 
     
    169168 
    170169    def __init__(self, *args, **kwargs): 
    171         dispatcher.send(signal=signals.pre_init, sender=self.__class__, args=args, kwargs=kwargs) 
     170        signals.pre_init.send(sender=self.__class__, args=args, kwargs=kwargs) 
    172171 
    173172        # There is a rather weird disparity here; if kwargs, it's set, then args 
     
    240239            if kwargs: 
    241240                raise TypeError, "'%s' is an invalid keyword argument for this function" % kwargs.keys()[0] 
    242         dispatcher.send(signal=signals.post_init, sender=self.__class__, instance=self) 
     241        signals.post_init.send(sender=self.__class__, instance=self) 
    243242 
    244243    def __repr__(self): 
     
    289288            meta = self._meta 
    290289            signal = True 
    291             dispatcher.send(signal=signals.pre_save, sender=self.__class__, 
    292                     instance=self, raw=raw) 
     290            signals.pre_save.send(sender=self.__class__, instance=self, raw=raw) 
    293291        else: 
    294292            meta = cls._meta 
     
    352350 
    353351        if signal: 
    354             dispatcher.send(signal=signals.post_save, sender=self.__class__
    355                     instance=self, created=(not record_exists), raw=raw) 
     352            signals.post_save.send(sender=self.__class__, instance=self
     353                created=(not record_exists), raw=raw) 
    356354 
    357355    save_base.alters_data = True 
  • django/trunk/django/db/models/fields/__init__.py

    r8191 r8223  
    1111from django.db.models import signals 
    1212from django.db.models.query_utils import QueryWrapper 
    13 from django.dispatch import dispatcher 
    1413from django.conf import settings 
    1514from django.core import validators 
     
    820819        setattr(cls, 'get_%s_size' % self.name, curry(cls._get_FIELD_size, field=self)) 
    821820        setattr(cls, 'save_%s_file' % self.name, lambda instance, filename, raw_field, save=True: instance._save_FIELD_file(self, filename, raw_field, save)) 
    822         dispatcher.connect(self.delete_file, signal=signals.post_delete, sender=cls) 
    823  
    824     def delete_file(self, instance): 
     821        signals.post_delete.connect(self.delete_file, sender=cls) 
     822 
     823    def delete_file(self, instance, **kwargs): 
    825824        if getattr(instance, self.attname): 
    826825            file_name = getattr(instance, 'get_%s_filename' % self.name)() 
  • django/trunk/django/db/models/fields/related.py

    r8212 r8223  
    1010from django import oldforms 
    1111from django import forms 
    12 from django.dispatch import dispatcher 
    1312 
    1413try: 
     
    7574        pending_lookups.setdefault(key, []).append(value) 
    7675 
    77 def do_pending_lookups(sender): 
     76def do_pending_lookups(sender, **kwargs): 
    7877    """ 
    7978    Handle any pending relations to the sending model. Sent from class_prepared. 
     
    8382        operation(field, sender, cls) 
    8483 
    85 dispatcher.connect(do_pending_lookups, signal=signals.class_prepared
     84signals.class_prepared.connect(do_pending_lookups
    8685 
    8786def manipulator_valid_rel_key(f, self, field_data, all_data): 
  • django/trunk/django/db/models/manager.py

    r7477 r8223  
    22 
    33from django.db.models.query import QuerySet, EmptyQuerySet, insert_query 
    4 from django.dispatch import dispatcher 
    54from django.db.models import signals 
    65from django.db.models.fields import FieldDoesNotExist 
    76 
    8 def ensure_default_manager(sender): 
     7def ensure_default_manager(sender, **kwargs): 
    98    cls = sender 
    109    if not getattr(cls, '_default_manager', None) and not cls._meta.abstract: 
     
    1716        cls.add_to_class('objects', Manager()) 
    1817 
    19 dispatcher.connect(ensure_default_manager, signal=signals.class_prepared
     18signals.class_prepared.connect(ensure_default_manager
    2019 
    2120class Manager(object): 
  • django/trunk/django/db/models/manipulators.py

    r7967 r8223  
    33from django.core import validators 
    44from django.db.models.fields import FileField, AutoField 
    5 from django.dispatch import dispatcher 
    65from django.db.models import signals 
    76from django.utils.functional import curry 
     
    1211from django.utils import datetime_safe 
    1312 
    14 def add_manipulators(sender): 
     13def add_manipulators(sender, **kwargs): 
    1514    cls = sender 
    1615    cls.add_to_class('AddManipulator', AutomaticAddManipulator) 
    1716    cls.add_to_class('ChangeManipulator', AutomaticChangeManipulator) 
    1817 
    19 dispatcher.connect(add_manipulators, signal=signals.class_prepared
     18signals.class_prepared.connect(add_manipulators
    2019 
    2120class ManipulatorDescriptor(object): 
  • django/trunk/django/db/models/query.py

    r8199 r8223  
    88from django.db.models.query_utils import Q, select_related_descend 
    99from django.db.models import signals, sql 
    10 from django.dispatch import dispatcher 
    1110from django.utils.datastructures import SortedDict 
    1211 
     
    811810        # Pre-notify all instances to be deleted. 
    812811        for pk_val, instance in items: 
    813             dispatcher.send(signal=signals.pre_delete, sender=cls, 
    814                     instance=instance) 
     812            signals.pre_delete.send(sender=cls, instance=instance) 
    815813 
    816814        pk_list = [pk for pk,instance in items] 
     
    846844                    setattr(instance, field.attname, None) 
    847845 
    848             dispatcher.send(signal=signals.post_delete, sender=cls, 
    849                     instance=instance) 
     846            signals.post_delete.send(sender=cls, instance=instance) 
    850847            setattr(instance, cls._meta.pk.attname, None) 
    851848 
  • django/trunk/django/db/models/signals.py

    r4265 r8223  
    1 class_prepared = object() 
     1from django.dispatch import Signal 
    22 
    3 pre_init= object() 
    4 post_init = object() 
     3class_prepared = Signal(providing_args=["class"]) 
    54 
    6 pre_save = object(
    7 post_save = object(
     5pre_init = Signal(providing_args=["instance", "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=["class", "app", "created_models", "verbosity", "interactive"]) 
  • django/trunk/django/db/models/sql/query.py

    r8112 r8223  
    1212from django.utils.tree import Node 
    1313from django.utils.datastructures import SortedDict 
    14 from django.dispatch import dispatcher 
    1514from django.db import connection 
    1615from django.db.models import signals 
     
    16711670        yield [r[:-trim] for r in rows] 
    16721671 
    1673 def setup_join_cache(sender): 
     1672def setup_join_cache(sender, **kwargs): 
    16741673    """ 
    16751674    The information needed to join between model fields is something that is 
     
    16811680    sender._meta._join_cache = {} 
    16821681 
    1683 dispatcher.connect(setup_join_cache, signal=signals.class_prepared
    1684  
     1682signals.class_prepared.connect(setup_join_cache
     1683 
  • django/trunk/django/dispatch/dispatcher.py

    r8046 r8223  
    1 """Multiple-producer-multiple-consumer signal-dispatching 
    2  
    3 dispatcher is the core of the PyDispatcher system, 
    4 providing the primary API and the core logic for the 
    5 system. 
    6  
    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  
    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 """ 
    281import 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. 
    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() 
     2import warnings 
     3try: 
     4    set 
     5except NameError: 
     6    from sets import Set as set # Python 2.3 fallback 
     7 
     8from django.dispatch import saferef 
    709 
    7110WEAKREF_TYPES = (weakref.ReferenceType, saferef.BoundMethodWeakref) 
    7211 
    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). 
    88      
    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 
    104      
    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 
    113      
    114         if Any, receiver will receive the indicated signals 
    115         from any sender. 
    116          
    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. 
    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. 
    127  
    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 
    152          
    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. 
    196          
    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 
     12def _make_id(target): 
     13    if hasattr(target, 'im_func'): 
     14        return (id(target.im_self), id(target.im_func)) 
     15    return id(target) 
     16 
     17class Signal(object): 
     18    """Base class for all signals 
     19     
     20    Internal attributes: 
     21        receivers -- { receriverkey (id) : weakref(receiver) } 
     22    """ 
     23     
     24    def __init__(self, providing_args=None): 
     25        """providing_args -- A list of the arguments this signal can pass along in 
     26                       a send() call. 
     27        """ 
     28        self.receivers = [] 
     29        if providing_args is None: 
     30            providing_args = [] 
     31        self.providing_args = set(providing_args) 
     32 
     33    def connect(self, receiver, sender=None, weak=True, dispatch_uid=None): 
     34        """Connect receiver to sender for signal 
     35     
     36        receiver -- a function or an instance method which is to 
     37            receive signals.  Receivers must be 
     38            hashable objects. 
     39 
     40            if weak is True, then receiver must be weak-referencable 
     41            (more precisely saferef.safeRef() must be able to create 
     42            a reference to the receiver). 
     43         
     44            Receivers must be able to accept keyword arguments. 
     45 
     46            If receivers have a dispatch_uid attribute, the receiver will 
     47              not be added if another receiver already exists with that 
     48              dispatch_uid. 
     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        dispatch_uid -- an identifier used to uniquely identify a particular 
     60            instance of a receiver. This will usually be a string, though it 
     61            may be anything hashable. 
     62 
     63        returns None 
     64        """ 
     65        from django.conf import settings 
     66         
     67        if settings.DEBUG: 
     68            import inspect 
     69            assert inspect.getargspec(receiver)[2] is not None, \ 
     70                "Signal receivers must accept keyword arguments (**kwargs)." 
     71         
     72        if dispatch_uid: 
     73            lookup_key = (dispatch_uid, _make_id(sender)) 
    26674        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         try: 
    304             if not receiver in receivers: 
     75            lookup_key = (_make_id(receiver), _make_id(sender)) 
     76 
     77        if weak: 
     78            receiver = saferef.safeRef(receiver, onDelete=self._remove_receiver) 
     79 
     80        for r_key, _ in self.receivers: 
     81            if r_key == lookup_key: 
     82                break 
     83        else: 
     84            self.receivers.append((lookup_key, receiver)) 
     85 
     86    def disconnect(self, receiver=None, sender=None, weak=True, dispatch_uid=None): 
     87        """Disconnect receiver from sender for signal 
     88     
     89        receiver -- the registered receiver to disconnect. May be none if 
     90            dispatch_uid is specified. 
     91        sender -- the registered sender to disconnect 
     92        weak -- the weakref state to disconnect 
     93        dispatch_uid -- the unique identifier of the receiver to disconnect 
     94     
     95        disconnect reverses the process of connect. 
     96 
     97        If weak references are used, disconnect need not be called. 
     98          The receiver will be remove from dispatch automatically. 
     99 
     100        returns None 
     101        """ 
     102 
     103        if dispatch_uid: 
     104            lookup_key = (dispatch_uid, _make_id(sender)) 
     105        else: 
     106            lookup_key = (_make_id(receiver), _make_id(sender)) 
     107 
     108        for idx, (r_key, _) in enumerate(self.receivers): 
     109            if r_key == lookup_key: 
     110                del self.receivers[idx] 
     111 
     112    def send(self, sender, **named): 
     113        """Send signal from sender to all connected receivers. 
     114 
     115        sender -- the sender of the signal 
     116            Either a specific object or None. 
     117     
     118        named -- named arguments which will be passed to receivers. 
     119 
     120        Returns a list of tuple pairs [(receiver, response), ... ]. 
     121 
     122        If any receiver raises an error, the error propagates back 
     123        through send, terminating the dispatch loop, so it is quite 
     124        possible to not have all receivers called if a raises an 
     125        error. 
     126        """ 
     127 
     128        responses = [] 
     129        if not self.receivers: 
     130            return responses 
     131 
     132        for receiver in self._live_receivers(_make_id(sender)): 
     133</