Django

Code

Changeset 8223

Show
Ignore:
Timestamp:
08/06/08 10:32:46 (1 year 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            response = receiver(signal=self, sender=sender, **named) 
     134            responses.append((receiver, response)) 
     135        return responses 
     136 
     137    def send_robust(self, sender, **named): 
     138        """Send signal from sender to all connected receivers catching errors 
     139 
     140        sender -- the sender of the signal 
     141            Can be any python object (normally one registered with 
     142            a connect if you actually want something to occur). 
     143 
     144        named -- named arguments which will be passed to receivers. 
     145            These arguments must be a subset of the argument names 
     146            defined in providing_args. 
     147 
     148        Return a list of tuple pairs [(receiver, response), ... ], 
     149        may raise DispatcherKeyError 
     150 
     151        if any receiver raises an error (specifically any subclass of Exception), 
     152        the error instance is returned as the result for that receiver. 
     153        """ 
     154 
     155        responses = [] 
     156        if not self.receivers: 
     157            return responses 
     158 
     159        # Call each receiver with whatever arguments it can accept. 
     160        # Return a list of tuple pairs [(receiver, response), ... ]. 
     161        for receiver in self._live_receivers(_make_id(sender)): 
     162            try: 
     163                response = receiver(signal=self, sender=sender, **named) 
     164            except Exception, err: 
     165                responses.append((receiver, err)) 
     166            else: 
     167                responses.append((receiver, response)) 
     168        return responses 
     169 
     170    def _live_receivers(self, senderkey): 
     171        """Filter sequence of receivers to get resolved, live receivers 
     172 
     173        This checks for weak references 
     174        and resolves them, then returning only live 
     175        receivers. 
     176        """ 
     177        none_senderkey = _make_id(None) 
     178 
     179        for (receiverkey, r_senderkey), receiver in self.receivers: 
     180            if r_senderkey == none_senderkey or r_senderkey == senderkey: 
    305181                if isinstance(receiver, WEAKREF_TYPES): 
     182                    # Dereference the weak reference. 
    306183                    receiver = receiver() 
    307                     # this should only (rough guess) be possible if somehow, deref'ing 
    308                     # triggered a wipe. 
    309                     if receiver is None: 
    310                         continue 
    311                 receivers[receiver] = 1 
    312                 yield receiver 
    313         except TypeError: 
    314             # dead weakrefs raise TypeError on hash... 
    315             pass 
    316  
    317 def send(signal=Any, sender=Anonymous, *arguments, **named): 
    318     """Send signal from sender to all connected receivers. 
    319      
    320     signal -- (hashable) signal value, see connect for details 
    321  
    322     sender -- the sender of the signal 
    323      
    324         if Any, only receivers registered for Any will receive 
    325         the message. 
    326  
    327         if Anonymous, only receivers registered to receive 
    328         messages from Anonymous or Any will receive the message 
    329  
    330         Otherwise can be any python object (normally one 
    331         registered with a connect if you actually want 
    332         something to occur). 
    333  
    334     arguments -- positional arguments which will be passed to 
    335         *all* receivers. Note that this may raise TypeErrors 
    336         if the receivers do not allow the particular arguments. 
    337         Note also that arguments are applied before named 
    338         arguments, so they should be used with care. 
    339  
    340     named -- named arguments which will be filtered according 
    341         to the parameters of the receivers to only provide those 
    342         acceptable to the receiver. 
    343  
    344     Return a list of tuple pairs [(receiver, response), ... ] 
    345  
    346     if any receiver raises an error, the error propagates back 
    347     through send, terminating the dispatch loop, so it is quite 
    348     possible to not have all receivers called if a raises an 
    349     error. 
    350     """ 
    351     # Call each receiver with whatever arguments it can accept. 
    352     # Return a list of tuple pairs [(receiver, response), ... ]. 
    353     responses = [] 
    354     for receiver in getAllReceivers(sender, signal): 
    355         response = robustapply.robustApply( 
    356             receiver, 
    357             signal=signal, 
    358             sender=sender, 
    359             *arguments, 
    360             **named 
    361         ) 
    362         responses.append((receiver, response)) 
    363     return responses 
    364  
    365  
    366 def sendExact(signal=Any, sender=Anonymous, *arguments, **named ): 
    367     """Send signal only to those receivers registered for exact message 
    368  
    369     sendExact allows for avoiding Any/Anonymous registered 
    370     handlers, sending only to those receivers explicitly 
    371     registered for a particular signal on a particular 
    372     sender. 
    373     """ 
    374     responses = [] 
    375     for receiver in liveReceivers(getReceivers(sender, signal)): 
    376         response = robustapply.robustApply( 
    377             receiver, 
    378             signal=signal, 
    379             sender=sender, 
    380             *arguments, 
    381             **named 
    382         ) 
    383         responses.append((receiver, response)) 
    384     return responses 
    385      
    386  
    387 def _removeReceiver(receiver): 
    388     """Remove receiver from connections.""" 
    389     if not sendersBack: 
    390         # During module cleanup the mapping will be replaced with None 
    391         return False 
    392     backKey = id(receiver) 
    393     for senderkey in sendersBack.get(backKey,()): 
    394         try: 
    395             signals = connections[senderkey].keys() 
    396         except KeyError,err: 
    397             pass 
    398         else: 
    399             for signal in signals: 
    400                 try: 
    401                     receivers = connections[senderkey][signal] 
    402                 except KeyError: 
    403                     pass 
     184                    if receiver is not None: 
     185                        yield receiver 
    404186                else: 
    405                     try: 
    406                         receivers.remove(receiver) 
    407                     except Exception, err: 
    408                         pass 
    409                 _cleanupConnections(senderkey, signal) 
    410     try: 
    411         del sendersBack[ backKey ] 
    412     except KeyError: 
    413         pass 
    414              
    415 def _cleanupConnections(senderkey, signal): 
    416     """Delete any empty signals for senderkey. Delete senderkey if empty.""" 
    417     try: 
    418         receivers = connections[senderkey][signal] 
    419     except: 
    420         pass 
    421     else: 
    422         if not receivers: 
    423             # No more connected receivers. Therefore, remove the signal. 
    424             try: 
    425                 signals = connections[senderkey] 
    426             except KeyError: 
    427                 pass 
    428             else: 
    429                 del signals[signal] 
    430                 if not signals: 
    431                     # No more signal connections. Therefore, remove the sender. 
    432                     _removeSender(senderkey) 
    433  
    434 def _removeSender(senderkey): 
    435     """Remove senderkey from connections.""" 
    436     _removeBackrefs(senderkey) 
    437  
    438     connections.pop(senderkey, None) 
    439     senders.pop(senderkey, None) 
    440  
    441  
    442 def _removeBackrefs(senderkey): 
    443     """Remove all back-references to this senderkey""" 
    444     for receiver_list in connections.pop(senderkey, {}).values(): 
    445         for receiver in receiver_list: 
    446             _killBackref(receiver, senderkey) 
    447  
    448  
    449 def _removeOldBackRefs(senderkey, signal, receiver, receivers): 
    450     """Kill old sendersBack references from receiver 
    451  
    452     This guards against multiple registration of the same 
    453     receiver for a given signal and sender leaking memory 
    454     as old back reference records build up. 
    455  
    456     Also removes old receiver instance from receivers 
    457     """ 
    458     try: 
    459         index = receivers.index(receiver) 
    460         # need to scan back references here and remove senderkey 
    461     except ValueError: 
    462         return False 
    463     else: 
    464         oldReceiver = receivers[index] 
    465         del receivers[index] 
    466         found = 0 
    467         signals = connections.get(signal) 
    468         if signals is not None: 
    469             for sig,recs in connections.get(signal,{}).iteritems(): 
    470                 if sig != signal: 
    471                     for rec in recs: 
    472                         if rec is oldReceiver: 
    473                             found = 1 
    474                             break 
    475         if not found: 
    476             _killBackref(oldReceiver, senderkey) 
    477             return True 
    478         return False 
    479          
    480          
    481 def _killBackref(receiver, senderkey): 
    482     """Do the actual removal of back reference from receiver to senderkey""" 
    483     receiverkey = id(receiver) 
    484     receivers_list = sendersBack.get(receiverkey, ()) 
    485     while senderkey in receivers_list: 
    486         try: 
    487             receivers_list.remove(senderkey) 
    488         except: 
    489             break 
    490     if not receivers_list: 
    491         try: 
    492             del sendersBack[ receiverkey ] 
    493         except KeyError: 
    494             pass 
    495     return True 
     187                    yield receiver 
     188 
     189    def _remove_receiver(self, receiver): 
     190        """Remove dead receivers from connections.""" 
     191 
     192        to_remove = [] 
     193        for key, connected_receiver in self.receivers: 
     194            if connected_receiver == receiver: 
     195                to_remove.append(key) 
     196        for key in to_remove: 
     197            for idx, (r_key, _) in enumerate(self.receivers): 
     198                if r_key == key: 
     199                    del self.receivers[idx] 
     200 
     201def connect(receiver, signal, sender=None, weak=True): 
     202    """ 
     203    For backward compatibility only. See Signal.connect() 
     204    """ 
     205    warnings.warn( 
     206        category   = DeprecationWarning, 
     207        message    = "dispatcher.connect() is deprecated; use Signal.connect() instead.", 
     208        stacklevel = 2 
     209    ) 
     210    return signal.connect(receiver, sender, weak) 
     211 
     212def disconnect(receiver, signal, sender=None, weak=True): 
     213    """ 
     214    For backward compatibility only. See Signal.disconnect() 
     215    """ 
     216    warnings.warn( 
     217        category   = DeprecationWarning, 
     218        message    = "dispatcher.disconnect() is deprecated; use Signal.disconnect() instead.", 
     219        stacklevel = 2 
     220    ) 
     221    signal.disconnect(receiver, sender, weak) 
     222 
     223def send(signal, sender=None, **named): 
     224    """ 
     225    For backward compatibility only. See Signal.send() 
     226    """ 
     227    warnings.warn( 
     228        category   = DeprecationWarning, 
     229        message    = "dispatcher.send() is deprecated; use Signal.send() instead.", 
     230        stacklevel = 2 
     231    ) 
     232    return signal.send(sender=sender, **named) 
     233 
     234def sendExact(signal, sender, **named ): 
     235    """ 
     236    This function is deprecated, as it now has the same meaning as send. 
     237    """ 
     238    warnings.warn( 
     239        category   = DeprecationWarning, 
     240        message    = "dispatcher.sendExact() is deprecated; use Signal.send() instead.", 
     241        stacklevel = 2 
     242    ) 
     243    return signal.send(sender=sender, **named) 
  • django/trunk/django/dispatch/__init__.py

    r4265 r8223  
    11"""Multi-consumer multi-producer dispatching mechanism 
     2 
     3Originally based on pydispatch (BSD) http://pypi.python.org/pypi/PyDispatcher/2.0.1 
     4See license.txt for original license. 
     5 
     6Heavily modified for Django's purposes. 
    27""" 
    3 __version__ = "1.0.0" 
    4 __author__ = "Patrick K. O'Brien" 
    5 __license__ = "BSD-style, see license.txt for details" 
    68 
     9from django.dispatch.dispatcher import Signal 
  • django/trunk/django/dispatch/license.txt

    r3415 r8223  
    1 PyDispatcher License 
     1django.dispatch was originally forked from PyDispatcher. 
     2 
     3PyDispatcher License: 
    24 
    35    Copyright (c) 2001-2003, Patrick K. O'Brien and Contributors 
  • django/trunk/django/dispatch/saferef.py

    r8046 r8223  
    1 """Refactored "safe reference" from dispatcher.py""" 
     1""" 
     2"Safe weakrefs", originally from pyDispatcher. 
     3 
     4Provides a way to safely weakref any function, including bound methods (which 
     5aren't handled by the core weakref module). 
     6""" 
     7 
    28import weakref, traceback 
    39 
     
    6167 
    6268    """ 
     69     
    6370    _allInstances = weakref.WeakValueDictionary() 
     71     
    6472    def __new__( cls, target, onDelete=None, *arguments,**named ): 
    6573        """Create new instance or return current instance 
     
    8492            base.__init__( target, onDelete, *arguments,**named) 
    8593            return base 
     94     
    8695    def __init__(self, target, onDelete=None): 
    8796        """Return a weak-reference-like instance for a bound method 
     
    123132        self.selfName = str(target.im_self) 
    124133        self.funcName = str(target.im_func.__name__) 
     134     
    125135    def calculateKey( cls, target ): 
    126136        """Calculate the reference key for this reference 
     
    131141        return (id(target.im_self),id(target.im_func)) 
    132142    calculateKey = classmethod( calculateKey ) 
     143     
    133144    def __str__(self): 
    134145        """Give a friendly representation of the object""" 
     
    138149            self.funcName, 
    139150        ) 
     151     
    140152    __repr__ = __str__ 
     153     
    141154    def __nonzero__( self ): 
    142155        """Whether we are still a valid reference""" 
    143156        return self() is not None 
     157     
    144158    def __cmp__( self, other ): 
    145159        """Compare with another reference""" 
     
    147161            return cmp( self.__class__, type(other) ) 
    148162        return cmp( self.key, other.key) 
     163     
    149164    def __call__(self): 
    150165        """Return a strong reference to the bound method 
     
    225240        return None 
    226241 
    227  
    228242def get_bound_method_weakref(target, onDelete): 
    229243    """Instantiates the appropiate BoundMethodWeakRef, depending on the details of 
     
    235249        # no luck, use the alternative implementation: 
    236250        return BoundNonDescriptorMethodWeakref(target=target, onDelete=onDelete) 
    237  
  • django/trunk/django/test/client.py

    r8015 r8223  
    1212from django.core.handlers.wsgi import WSGIRequest 
    1313from django.core.signals import got_request_exception 
    14 from django.dispatch import dispatcher 
    1514from django.http import SimpleCookie, HttpRequest 
    1615from django.template import TemplateDoesNotExist 
     
    6059            self.load_middleware() 
    6160 
    62         dispatcher.send(signal=signals.request_started
     61        signals.request_started.send(sender=self.__class__
    6362        try: 
    6463            request = WSGIRequest(environ) 
     
    7069            response = self.apply_response_fixes(request, response) 
    7170        finally: 
    72             dispatcher.send(signal=signals.request_finished
     71            signals.request_finished.send(sender=self.__class__
    7372 
    7473        return response 
    7574 
    76 def store_rendered_templates(store, signal, sender, template, context): 
     75def store_rendered_templates(store, signal, sender, template, context, **kwargs): 
    7776    """ 
    7877    Stores templates and contexts that are rendered. 
     
    161160        self.exc_info = None 
    162161 
    163     def store_exc_info(self, *args, **kwargs): 
     162    def store_exc_info(self, **kwargs): 
    164163        """ 
    165164        Stores exceptions when they are generated by a view. 
     
    203202        data = {} 
    204203        on_template_render = curry(store_rendered_templates, data) 
    205         dispatcher.connect(on_template_render, signal=signals.template_rendered
     204        signals.template_rendered.connect(on_template_render
    206205 
    207206        # Capture exceptions created by the handler. 
    208         dispatcher.connect(self.store_exc_info, signal=got_request_exception
     207        got_request_exception.connect(self.store_exc_info
    209208 
    210209        try: 
  • django/trunk/django/test/signals.py

    r4265 r8223  
    1 template_rendered = object() 
     1from django.dispatch import Signal 
     2 
     3template_rendered = Signal(providing_args=["template", "context"]) 
  • django/trunk/django/test/utils.py

    r7949 r8223  
    44from django.core import mail 
    55from django.core.management import call_command 
    6 from django.dispatch import dispatcher 
    76from django.test import signals 
    87from django.template import Template 
     
    1817    that can be intercepted by the test system Client 
    1918    """ 
    20     dispatcher.send(signal=signals.template_rendered, sender=self, template=self, context=context) 
     19    signals.template_rendered.send(sender=self, template=self, context=context) 
    2120    return self.nodelist.render(context) 
    2221 
  • django/trunk/tests/modeltests/signals/models.py

    r7477 r8223  
    44 
    55from django.db import models 
    6 from django.dispatch import dispatcher 
    76 
    87class Person(models.Model): 
     
    1312        return u"%s %s" % (self.first_name, self.last_name) 
    1413 
    15  
    16 def pre_save_nokwargs_test(sender, instance): 
    17     print 'pre_save_nokwargs signal' 
    18  
    19 def post_save_nokwargs_test(sender, instance): 
    20     print 'post_save_nokwargs signal' 
    21  
    22 def pre_save_test(sender, instance, **kwargs): 
     14def pre_save_test(signal, sender, instance, **kwargs): 
    2315    print 'pre_save signal,', instance 
    2416    if kwargs.get('raw'): 
    2517        print 'Is raw' 
    2618 
    27 def post_save_test(sender, instance, **kwargs): 
     19def post_save_test(signal, sender, instance, **kwargs): 
    2820    print 'post_save signal,', instance 
    2921    if 'created' in kwargs: 
     
    3527        print 'Is raw' 
    3628 
    37 def pre_delete_test(sender, instance, **kwargs): 
     29def pre_delete_test(signal, sender, instance, **kwargs): 
    3830    print 'pre_delete signal,', instance 
    3931    print 'instance.id is not None: %s' % (instance.id != None) 
    4032 
    41 def post_delete_test(sender, instance, **kwargs): 
     33def post_delete_test(signal, sender, instance, **kwargs): 
    4234    print 'post_delete signal,', instance 
    4335    print 'instance.id is None: %s' % (instance.id == None) 
    4436 
    4537__test__ = {'API_TESTS':""" 
    46 >>> dispatcher.connect(pre_save_nokwargs_test, signal=models.signals.pre_save) 
    47 >>> dispatcher.connect(post_save_nokwargs_test, signal=models.signals.post_save) 
    48 >>> dispatcher.connect(pre_save_test, signal=models.signals.pre_save) 
    49 >>> dispatcher.connect(post_save_test, signal=models.signals.post_save) 
    50 >>> dispatcher.connect(pre_delete_test, signal=models.signals.pre_delete) 
    51 >>> dispatcher.connect(post_delete_test, signal=models.signals.post_delete) 
     38>>> models.signals.pre_save.connect(pre_save_test) 
     39>>> models.signals.post_save.connect(post_save_test) 
     40>>> models.signals.pre_delete.connect(pre_delete_test) 
     41>>> models.signals.post_delete.connect(post_delete_test) 
    5242 
    5343>>> p1 = Person(first_name='John', last_name='Smith') 
    5444>>> p1.save() 
    55 pre_save_nokwargs signal 
    5645pre_save signal, John Smith 
    57 post_save_nokwargs signal 
    5846post_save signal, John Smith 
    5947Is created 
     
    6149>>> p1.first_name = 'Tom' 
    6250>>> p1.save() 
    63 pre_save_nokwargs signal 
    6451pre_save signal, Tom Smith 
    65 post_save_nokwargs signal 
    6652post_save signal, Tom Smith 
    6753Is updated 
     
    6955# Calling an internal method purely so that we can trigger a "raw" save. 
    7056>>> p1.save_base(raw=True) 
    71 pre_save_nokwargs signal 
    7257pre_save signal, Tom Smith 
    7358Is raw 
    74 post_save_nokwargs signal 
    7559post_save signal, Tom Smith 
    7660Is updated 
     
    8670>>> p2.id = 99999 
    8771>>> p2.save() 
    88 pre_save_nokwargs signal 
    8972pre_save signal, James Jones 
    90 post_save_nokwargs signal 
    9173post_save signal, James Jones 
    9274Is created 
     
    9476>>> p2.id = 99998 
    9577>>> p2.save() 
    96 pre_save_nokwargs signal 
    9778pre_save signal, James Jones 
    98 post_save_nokwargs signal 
    9979post_save signal, James Jones 
    10080Is created 
     
    10989[<Person: James Jones>] 
    11090 
    111 >>> dispatcher.disconnect(pre_save_nokwargs_test, signal=models.signals.pre_save) 
    112 >>> dispatcher.disconnect(post_save_nokwargs_test, signal=models.signals.post_save) 
    113 >>> dispatcher.disconnect(post_delete_test, signal=models.signals.post_delete) 
    114 >>> dispatcher.disconnect(pre_delete_test, signal=models.signals.pre_delete) 
    115 >>> dispatcher.disconnect(post_save_test, signal=models.signals.post_save) 
    116 >>> dispatcher.disconnect(pre_save_test, signal=models.signals.pre_save) 
     91>>> models.signals.post_delete.disconnect(post_delete_test) 
     92>>> models.signals.pre_delete.disconnect(pre_delete_test) 
     93>>> models.signals.post_save.disconnect(post_save_test) 
     94>>> models.signals.pre_save.disconnect(pre_save_test) 
    11795"""} 
  • django/trunk/tests/regressiontests/dispatch/tests/__init__.py

    r5876 r8223  
    33""" 
    44 
     5from test_saferef import * 
    56from test_dispatcher import * 
    6 from test_robustapply import * 
    7 from test_saferef import * 
  • django/trunk/tests/regressiontests/dispatch/tests/test_dispatcher.py

    r8004 r8223  
    1 from django.dispatch.dispatcher import * 
    2 from django.dispatch import dispatcher, robust 
     1from django.dispatch import Signal 
    32import unittest 
    43import copy 
     
    1615        gc.collect() 
    1716 
    18 def x(a): 
    19     return a 
    20  
    21 class Dummy(object): 
    22     pass 
     17def receiver_1_arg(val, **kwargs): 
     18    return val 
    2319 
    2420class Callable(object): 
    25     def __call__(self, a): 
    26         return a 
     21    def __call__(self, val, **kwargs): 
     22        return val 
    2723     
    28     def a(self, a): 
    29         return a 
     24    def a(self, val, **kwargs): 
     25        return val 
     26 
     27a_signal = Signal(providing_args=["val"]) 
    3028 
    3129class DispatcherTests(unittest.TestCase): 
    3230    """Test suite for dispatcher (barely started)""" 
    33      
    34     def setUp(self): 
    35         # track the initial state, since it's possible that others have bleed receivers in 
    36         garbage_collect() 
    37         self.sendersBack = copy.copy(dispatcher.sendersBack) 
    38         self.connections = copy.copy(dispatcher.connections) 
    39         self.senders = copy.copy(dispatcher.senders) 
    40      
    41     def _testIsClean(self): 
     31 
     32    def _testIsClean(self, signal): 
    4233        """Assert that everything has been cleaned up automatically""" 
    43         self.assertEqual(dispatcher.sendersBack, self.sendersBack) 
    44         self.assertEqual(dispatcher.connections, self.connections) 
    45         self.assertEqual(dispatcher.senders, self.senders) 
     34        self.assertEqual(signal.receivers, []) 
     35 
     36        # force cleanup just in case 
     37        signal.receivers = [] 
    4638     
    4739    def testExact(self): 
    48         a = Dummy() 
    49         signal = 'this' 
    50         connect(x, signal, a) 
    51         expected = [(x,a)] 
    52         result = send('this',a, a=a) 
     40        a_signal.connect(receiver_1_arg, sender=self) 
     41        expected = [(receiver_1_arg,"test")] 
     42        result = a_signal.send(sender=self, val="test") 
    5343        self.assertEqual(result, expected) 
    54         disconnect(x, signal, a) 
    55         self.assertEqual(list(getAllReceivers(a,signal)), []) 
    56         self._testIsClean() 
    57      
    58     def testAnonymousSend(self): 
    59         a = Dummy() 
    60         signal = 'this' 
    61         connect(x, signal) 
    62         expected = [(x,a)] 
    63         result = send(signal,None, a=a) 
     44        a_signal.disconnect(receiver_1_arg, sender=self) 
     45        self._testIsClean(a_signal) 
     46 
     47    def testIgnoredSender(self): 
     48        a_signal.connect(receiver_1_arg) 
     49        expected = [(receiver_1_arg,"test")] 
     50        result = a_signal.send(sender=self, val="test") 
    6451        self.assertEqual(result, expected) 
    65         disconnect(x, signal) 
    66         self.assertEqual(list(getAllReceivers(None,signal)), []) 
    67         self._testIsClean() 
    68      
    69     def testAnyRegistration(self): 
    70         a = Dummy() 
    71         signal = 'this' 
    72         connect(x, signal, Any) 
    73         expected = [(x,a)] 
    74         result = send('this',object(), a=a) 
    75         self.assertEqual(result, expected) 
    76         disconnect(x, signal, Any) 
    77         expected = [] 
    78         result = send('this',object(), a=a) 
    79         self.assertEqual(result, expected) 
    80         self.assertEqual(list(getAllReceivers(Any,signal)), []) 
    81          
    82         self._testIsClean() 
    83      
    84     def testAnyRegistration2(self): 
    85         a = Dummy() 
    86         signal = 'this' 
    87         connect(x, Any, a) 
    88         expected = [(x,a)] 
    89         result = send('this',a, a=a) 
    90         self.assertEqual(result, expected) 
    91         disconnect(x, Any, a) 
    92         self.assertEqual(list(getAllReceivers(a,Any)), []) 
    93         self._testIsClean() 
     52        a_signal.disconnect(receiver_1_arg) 
     53        self._testIsClean(a_signal) 
    9454     
    9555    def testGarbageCollected(self): 
    9656        a = Callable() 
    97         b = Dummy() 
    98         signal = 'this' 
    99         connect(a.a, signal, b) 
     57        a_signal.connect(a.a, sender=self) 
    10058        expected = [] 
    10159        del a 
    10260        garbage_collect() 
    103         result = send('this',b, a=b
     61        result = a_signal.send(sender=self, val="test"
    10462        self.assertEqual(result, expected) 
    105         self.assertEqual(list(getAllReceivers(b,signal)), []) 
    106         self._testIsClean() 
    107      
    108     def testGarbageCollectedObj(self): 
    109         class x: 
    110             def __call__(self, a): 
    111                 return a 
    112         a = Callable() 
    113         b = Dummy() 
    114         signal = 'this' 
    115         connect(a, signal, b) 
    116         expected = [] 
    117         del a 
    118         garbage_collect() 
    119         result = send('this',b, a=b) 
    120         self.assertEqual(result, expected) 
    121         self.assertEqual(list(getAllReceivers(b,signal)), []) 
    122         self._testIsClean() 
    123  
     63        self._testIsClean(a_signal) 
    12464     
    12565    def testMultipleRegistration(self): 
    12666        a = Callable() 
    127         b = Dummy() 
    128         signal = 'this' 
    129         connect(a, signal, b) 
    130         connect(a, signal, b) 
    131         connect(a, signal, b) 
    132         connect(a, signal, b) 
    133         connect(a, signal, b) 
    134         connect(a, signal, b) 
    135         result = send('this',b, a=b) 
     67        a_signal.connect(a) 
     68        a_signal.connect(a) 
     69        a_signal.connect(a) 
     70        a_signal.connect(a) 
     71        a_signal.connect(a) 
     72        a_signal.connect(a) 
     73        result = a_signal.send(sender=self, val="test") 
    13674        self.assertEqual(len(result), 1) 
    137         self.assertEqual(len(list(getAllReceivers(b,signal))), 1) 
     75        self.assertEqual(len(a_signal.receivers), 1) 
    13876        del a 
    139         del b 
    14077        del result 
    14178        garbage_collect() 
    142         self._testIsClean() 
     79        self._testIsClean(a_signal) 
     80 
     81    def testUidRegistration(self): 
     82        def uid_based_receiver_1(**kwargs): 
     83            pass 
     84 
     85        def uid_based_receiver_2(**kwargs): 
     86            pass 
     87 
     88        a_signal.connect(uid_based_receiver_1, dispatch_uid = "uid") 
     89        a_signal.connect(uid_based_receiver_2, dispatch_uid = "uid") 
     90        self.assertEqual(len(a_signal.receivers), 1) 
     91        a_signal.disconnect(dispatch_uid = "uid") 
     92        self._testIsClean(a_signal) 
    14393     
    14494    def testRobust(self): 
    14595        """Test the sendRobust function""" 
    146         def fails(): 
     96        def fails(val, **kwargs): 
    14797            raise ValueError('this') 
    148         a = object() 
    149         signal = 'this' 
    150         connect(fails, Any, a) 
    151         result = robust.sendRobust('this',a, a=a) 
     98        a_signal.connect(fails) 
     99        result = a_signal.send_robust(sender=self, val="test") 
    152100        err = result[0][1] 
    153101        self.assert_(isinstance(err, ValueError)) 
    154102        self.assertEqual(err.args, ('this',)) 
     103        a_signal.disconnect(fails) 
     104        self._testIsClean(a_signal) 
    155105 
    156106def getSuite():