Ticket #6814: signals_8220.diff

File signals_8220.diff, 67.7 KB (added by Keith Bussell, 16 years ago)

dispatch_uid as a parameter to connect/disconnect

  • django/contrib/auth/management/__init__.py

     
    22Creates permissions for all installed apps that need permissions.
    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
    87
     
    1615        perms.append((_get_permission_codename(action, opts), u'Can %s %s' % (action, opts.verbose_name_raw)))
    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
    2221    app_models = get_models(app)
     
    4544                call_command("createsuperuser", interactive=True)
    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)
    52  No newline at end of file
     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/contrib/contenttypes/generic.py

     
    88from django.db.models import signals
    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
    1413class GenericForeignKey(object):
     
    2928        self.cache_attr = "_%s_cache" % name
    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
    4039        content-type/object-id fields.
  • django/contrib/contenttypes/management.py

     
    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
    98    entries that no longer have a matching model class.
     
    3736    for app in get_apps():
    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__":
    4342    update_all_contenttypes()
  • django/contrib/sites/management.py

     
    22Creates the default Site object.
    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:
    1312            print "Creating example.com Site object"
     
    1514        s.save()
    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/core/handlers/base.py

     
    22
    33from django import http
    44from django.core import signals
    5 from django.dispatch import dispatcher
    65from django.utils.encoding import force_unicode
    76
    87class BaseHandler(object):
     
    122121        except: # Handle everything else, including SuspiciousOperation, etc.
    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
    128127    def handle_uncaught_exception(self, request, resolver, exc_info):
  • django/core/handlers/modpython.py

     
    55from django.core import signals
    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
    1110
     
    174173            self.load_middleware()
    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:
    180179                request = self.request_class(req)
     
    188187                    response = middleware_method(request, response)
    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.
    194193        req.content_type = response['Content-Type']
  • django/core/handlers/wsgi.py

     
    99from django.core import signals
    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
    1514
     
    207206            self.initLock.release()
    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:
    213212                request = self.request_class(environ)
     
    221220                    response = middleware_method(request, response)
    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:
    227226            status_text = STATUS_CODE_TEXT[response.status_code]
  • django/core/management/commands/flush.py

     
    1515    def handle_noargs(self, **options):
    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
    2120        verbosity = int(options.get('verbosity', 1))
  • django/core/management/sql.py

     
    492492        app_name = app.__name__.split('.')[-2]
    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/core/signals.py

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

     
    22from django.conf import settings
    33from django.core import signals
    44from django.core.exceptions import ImproperlyConfigured
    5 from django.dispatch import dispatcher
    65from django.utils.functional import curry
    76
    87__all__ = ('backend', 'connection', 'DatabaseError', 'IntegrityError')
     
    5857
    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/db/models/base.py

     
    1919from django.db import connection, transaction
    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
    2524from django.core.files.move import file_move_safe
     
    161160        if hasattr(cls, 'get_absolute_url'):
    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
    167166class Model(object):
    168167    __metaclass__ = ModelBase
    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
    174173        # overrides it. It should be one or the other; don't duplicate the work
     
    239238                    pass
    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):
    245244        return smart_str(u'<%s: %s>' % (self.__class__.__name__, unicode(self)))
     
    288287            cls = self.__class__
    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
    295293            signal = False
     
    351349        transaction.commit_unless_managed()
    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
    358356
  • django/db/models/fields/__init__.py

     
    1010from django.db import connection, get_creation_module
    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
    1615from django import oldforms
     
    819818        setattr(cls, 'get_%s_url' % self.name, curry(cls._get_FIELD_url, field=self))
    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)
     821        signals.post_delete.connect(self.delete_file, sender=cls)
    823822
    824     def delete_file(self, instance):
     823    def delete_file(self, instance, **kwargs):
    825824        if getattr(instance, self.attname):
    826825            file_name = getattr(instance, 'get_%s_filename' % self.name)()
    827826            # If the file exists and no other object of this type references it,
  • django/db/models/fields/related.py

     
    99from django.core import validators
    1010from django import oldforms
    1111from django import forms
    12 from django.dispatch import dispatcher
    1312
    1413try:
    1514    set
     
    7473        value = (cls, field, operation)
    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.
    8079    """
     
    8281    for cls, field, operation in pending_lookups.pop(key, []):
    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):
    8887    "Validates that the value is a valid foreign key"
  • django/db/models/manager.py

     
    11import copy
    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:
    1110        # Create the default manager, if needed.
     
    1615            pass
    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):
    2221    # Tracks each time a Manager instance is created. Used to retain order.
  • django/db/models/manipulators.py

     
    22from django import oldforms
    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
    87from django.utils.datastructures import DotExpandedDict
     
    1110from django.utils.translation import ugettext as _
    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):
    2221    # This class provides the functionality that makes the default model
  • django/db/models/query.py

     
    77from django.db.models.fields import DateField
    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
    1312
     
    810809
    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]
    817815        del_query = sql.DeleteQuery(cls, connection)
     
    845843                if field.rel and field.null and field.rel.to in seen_objs:
    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
    852849    transaction.commit_unless_managed()
  • django/db/models/signals.py

     
    1 class_prepared = object()
     1from django.dispatch.dispatcher 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/db/models/sql/query.py

     
    1111
    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
    1716from django.db.models.fields import FieldDoesNotExist
     
    16701669            sentinel):
    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
    16761675    invariant over the life of the model, so we cache it in the model's Options
     
    16801679    """
    16811680    sender._meta._join_cache = {}
    16821681
    1683 dispatcher.connect(setup_join_cache, signal=signals.class_prepared)
     1682signals.class_prepared.connect(setup_join_cache)
    16841683
  • django/dispatch/__init__.py

     
    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"
    6 
  • django/dispatch/dispatcher.py

     
    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
     2try:
     3    set
     4except NameError:
     5    from sets import Set as set # Python 2.3 fallback
    306
    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]
     7from django.dispatch import saferef
    348
    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()
    70 
    719WEAKREF_TYPES = (weakref.ReferenceType, saferef.BoundMethodWeakref)
    7210
    73 connections = {}
    74 senders = {}
    75 sendersBack = {}
     11def _make_id(target):
     12    if hasattr(target, 'im_func'):
     13        return (id(target.im_self), id(target.im_func))
     14    return id(target)
    7615
    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).
     16class Signal(object):
     17    """Base class for all signals
    8818   
    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
     19    Internal attributes:
     20        receivers -- { receriverkey (id) : weakref(receiver) }
     21    """
    10422   
    105         if Any, receiver will receive any signal from the
    106         indicated sender (which might also be Any, but is not
    107         necessarily Any).
     23    def __init__(self, providing_args=[]):
     24        """providing_args -- A list of the arguments this signal can pass along in
     25                       a send() call.
     26        """
     27        self.receivers = []
     28        self.providing_args = set(providing_args)
     29   
     30    def connect(self, receiver, sender=None, weak=True, dispatch_uid=None):
     31        """Connect receiver to sender for signal
     32   
     33        receiver -- a function or an instance method which is to
     34            receive signals.  Receivers must be
     35            hashable objects.
     36   
     37            if weak is True, then receiver must be weak-referencable
     38            (more precisely saferef.safeRef() must be able to create
     39            a reference to the receiver).
    10840       
    109         Otherwise must be a hashable Python object other than
    110         None (DispatcherError raised on None).
     41            Receivers must be able to accept keyword arguments.
     42           
     43            If receivers have a dispatch_uid attribute, the receiver will
     44              not be added if another receiver already exists with that
     45              dispatch_uid.
     46           
     47        sender -- the sender to which the receiver should respond
     48            Must either be of type Signal, or None to receive events
     49            from any sender.
     50           
     51        weak -- whether to use weak references to the receiver
     52            By default, the module will attempt to use weak
     53            references to the receiver objects.  If this parameter
     54            is false, then strong references will be used.
    11155       
    112     sender -- the sender to which the receiver should respond
     56        dispatch_uid -- an identifier used to uniquely identify a particular
     57            instance of a receiver. This will usually be a string, though it
     58            may be anything hashable.
    11359   
    114         if Any, receiver will receive the indicated signals
    115         from any sender.
     60        returns None
     61        """
     62        from django.conf import settings
    11663       
    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.
     64        if settings.DEBUG:
     65            import inspect
     66            assert inspect.getargspec(receiver)[2] is not None, \
     67                "Signal receivers should accept keyword arguments (**kwargs)."
    12268       
    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.
     69        if dispatch_uid:
     70            lookup_key = (dispatch_uid, _make_id(sender))
     71        else:
     72            lookup_key = (_make_id(receiver), _make_id(sender))
    12773
    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)
     74        if weak:
     75            receiver = saferef.safeRef(receiver, onDelete=self._remove_receiver)
    13776
    138     signals = connections.setdefault(senderkey, {})
     77        for r_key, _ in self.receivers:
     78            if r_key == lookup_key:
     79                break
     80        else:
     81            self.receivers.append((lookup_key, receiver))
    13982
    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
     83    def disconnect(self, receiver=None, sender=None, weak=True, dispatch_uid=None):
     84        """Disconnect receiver from sender for signal
     85   
     86        receiver -- the registered receiver to disconnect. May be none if
     87            dispatch_uid is specified.
     88        sender -- the registered sender to disconnect
     89        weak -- the weakref state to disconnect
     90        dispatch_uid -- the unique identifier of the receiver to disconnect
     91   
     92        disconnect reverses the process of connect.
     93   
     94        If weak references are used, disconnect need not be called.
     95          The receiver will be remove from dispatch automatically.
     96           
     97        returns None
     98        """
    15299       
    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
     100        if dispatch_uid:
     101            lookup_key = (dispatch_uid, _make_id(sender))
     102        else:
     103            lookup_key = (_make_id(receiver), _make_id(sender))
    169104
    170     receivers.append(receiver)
     105        for idx, (r_key, _) in enumerate(self.receivers):
     106            if r_key == lookup_key:
     107                del self.receivers[idx]
     108       
     109    def send(self, sender, **named):
     110        """Send signal from sender to all connected receivers.
     111       
     112        sender -- the sender of the signal
     113            Either a specific object or None.
     114   
     115        named -- named arguments which will be passed to receivers.
    171116
    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.
     117        Returns a list of tuple pairs [(receiver, response), ... ].
    196118       
    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)
     119        If any receiver raises an error, the error propagates back
     120        through send, terminating the dispatch loop, so it is quite
     121        possible to not have all receivers called if a raises an
     122        error.
     123        """
    228124
    229 def getReceivers(sender=Any, signal=Any):
    230     """Get list of receivers from global tables
     125        responses = []
     126        if not self.receivers:
     127            return responses
    231128
    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.
     129        for receiver in self._live_receivers(_make_id(sender)):
     130            response = receiver(signal=self, sender=sender, **named)
     131            responses.append((receiver, response))
     132        return responses
    235133
    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.
     134    def send_robust(self, sender, **named):
     135        """Send signal from sender to all connected receivers catching errors
    242136
    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 []
     137        sender -- the sender of the signal
     138            Can be any python object (normally one registered with
     139            a connect if you actually want something to occur).
    251140
    252 def liveReceivers(receivers):
    253     """Filter sequence of receivers to get resolved, live receivers
     141        named -- named arguments which will be passed to receivers.
     142            These arguments must be a subset of the argument names
     143            defined in providing_args, or a DispatcherTypeError will be
     144            raised.
    254145
    255     This is a generator which will iterate over
    256     the passed sequence, checking for weak references
    257     and resolving them, then returning all live
    258     receivers.
    259     """
    260     for receiver in receivers:
    261         if isinstance(receiver, WEAKREF_TYPES):
    262             # Dereference the weak reference.
    263             receiver = receiver()
    264             if receiver is not None:
    265                 yield receiver
    266         else:
    267             yield receiver
     146        Return a list of tuple pairs [(receiver, response), ... ],
     147        may raise DispatcherKeyError
    268148
     149        if any receiver raises an error (specifically any subclass of Exception),
     150        the error instance is returned as the result for that receiver.
     151        """
    269152
     153        responses = []
     154        if not self.receivers:
     155            return responses
    270156
    271 def getAllReceivers(sender=Any, signal=Any):
    272     """Get list of all receivers from global tables
     157        # Call each receiver with whatever arguments it can accept.
     158        # Return a list of tuple pairs [(receiver, response), ... ].
     159        for receiver in self._live_receivers(_make_id(sender)):
     160            try:
     161                response = receiver(signal=self, sender=sender, **named)
     162            except Exception, err:
     163                responses.append((receiver, err))
     164            else:
     165                responses.append((receiver, response))
     166        return responses
    273167
    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])
    291168
    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:
     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
     184                    if receiver is not None:
     185                        yield receiver
     186                else:
     187                    yield receiver
    316188
    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
     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]
    321200
    322     sender -- the sender of the signal
    323    
    324         if Any, only receivers registered for Any will receive
    325         the message.
     201           
     202def connect(receiver, signal, sender=None, weak=True):
     203    """For backward compatibility only. See Signal.connect()
     204    """
     205    return signal.connect(receiver, sender, weak)
    326206
    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.
     207def disconnect(receiver, signal, sender=None, weak=True):
     208    """For backward compatibility only. See Signal.disconnect()
    350209    """
    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
     210    signal.disconnect(receiver, sender, weak)
    364211
    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.
     212def send(signal, sender=None, **named):
     213    """For backward compatibility only. See Signal.send()
    373214    """
    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    
     215    return signal.send(sender=sender, **named)
    386216
    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
    404                 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
     217def sendExact(signal, sender, **named ):
     218    """This function is deprecated, as it now has the same
     219    meaning as send.
    457220    """
    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
     221    return signal.send(sender=sender, **named)
  • django/dispatch/errors.py

     
    1 """Error types for dispatcher mechanism
    2 """
    3 
    4 class DispatcherError(Exception):
    5     """Base class for all Dispatcher errors"""
    6 class DispatcherKeyError(KeyError, DispatcherError):
    7     """Error raised when unknown (sender,signal) set specified"""
    8 class DispatcherTypeError(TypeError, DispatcherError):
    9     """Error raised when inappropriate signal-type specified (None)"""
    10 
  • django/dispatch/robust.py

     
    1 """Module implementing error-catching version of send (sendRobust)"""
    2 from django.dispatch.dispatcher import Any, Anonymous, liveReceivers, getAllReceivers
    3 from django.dispatch.robustapply import robustApply
    4 
    5 def sendRobust(
    6     signal=Any,
    7     sender=Anonymous,
    8     *arguments, **named
    9 ):
    10     """Send signal from sender to all connected receivers catching errors
    11    
    12     signal -- (hashable) signal value, see connect for details
    13 
    14     sender -- the sender of the signal
    15    
    16         if Any, only receivers registered for Any will receive
    17         the message.
    18 
    19         if Anonymous, only receivers registered to receive
    20         messages from Anonymous or Any will receive the message
    21 
    22         Otherwise can be any python object (normally one
    23         registered with a connect if you actually want
    24         something to occur).
    25 
    26     arguments -- positional arguments which will be passed to
    27         *all* receivers. Note that this may raise TypeErrors
    28         if the receivers do not allow the particular arguments.
    29         Note also that arguments are applied before named
    30         arguments, so they should be used with care.
    31 
    32     named -- named arguments which will be filtered according
    33         to the parameters of the receivers to only provide those
    34         acceptable to the receiver.
    35 
    36     Return a list of tuple pairs [(receiver, response), ... ]
    37 
    38     if any receiver raises an error (specifically any subclass of Exception),
    39     the error instance is returned as the result for that receiver.
    40     """
    41     # Call each receiver with whatever arguments it can accept.
    42     # Return a list of tuple pairs [(receiver, response), ... ].
    43     responses = []
    44     for receiver in liveReceivers(getAllReceivers(sender, signal)):
    45         try:
    46             response = robustApply(
    47                 receiver,
    48                 signal=signal,
    49                 sender=sender,
    50                 *arguments,
    51                 **named
    52             )
    53         except Exception, err:
    54             responses.append((receiver, err))
    55         else:
    56             responses.append((receiver, response))
    57     return responses
  • django/dispatch/robustapply.py

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

     
    1111from django.core.handlers.base import BaseHandler
    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
    1716from django.test import signals
     
    5958        if self._request_middleware is None:
    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)
    6564            response = self.get_response(request)
     
    6968                response = middleware_method(request, response)
    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.
    7978    """
     
    160159        self.cookies = SimpleCookie()
    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.
    166165        """
     
    202201        # callback function.
    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:
    211210            response = self.handler(environ)
  • django/test/signals.py

     
    1 template_rendered = object()
    2  No newline at end of file
     1from django.dispatch.dispatcher import Signal
     2
     3template_rendered = Signal(providing_args=["template", "context"])
  • django/test/utils.py

     
    33from django.db import connection, get_creation_module
    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
    98from django.utils.translation import deactivate
     
    1716    An instrumented Template render method, providing a signal
    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
    2322class TestSMTPConnection(object):
  • tests/modeltests/signals/models.py

     
    33"""
    44
    55from django.db import models
    6 from django.dispatch import dispatcher
    76
    87class Person(models.Model):
    98    first_name = models.CharField(max_length=20)
     
    1211    def __unicode__(self):
    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:
    3022        if kwargs['created']:
     
    3426    if kwargs.get('raw'):
    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
    6048
    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
    6854
    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
    7761Is raw
     
    8569>>> p2 = Person(first_name='James', last_name='Jones')
    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
    9375
    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
    10181
     
    10888>>> Person.objects.all()
    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"""}
  • tests/regressiontests/dispatch/tests/__init__.py

     
    22Unit-tests for the dispatch project
    33"""
    44
    5 from test_dispatcher import *
    6 from test_robustapply import *
    75from test_saferef import *
     6from test_dispatcher import *
  • tests/regressiontests/dispatch/tests/test_dispatcher.py

     
    1 from django.dispatch.dispatcher import *
    2 from django.dispatch import dispatcher, robust
     1from django.dispatch.dispatcher import Signal
    32import unittest
    43import copy
    54import sys
     
    1514    def garbage_collect():
    1615        gc.collect()
    1716
    18 def x(a):
    19     return a
     17def receiver_1_arg(val, **kwargs):
     18    return val
    2019
    21 class Dummy(object):
    22     pass
    23 
    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
    3026
     27a_signal = Signal(providing_args=["val"])
     28
    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()
     52        a_signal.disconnect(receiver_1_arg)
     53        self._testIsClean(a_signal)
    6854   
    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()
    94    
    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()
     63        self._testIsClean(a_signal)
    10764   
    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 
    124    
    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():
    157107    return unittest.makeSuite(DispatcherTests,'test')
  • tests/regressiontests/dispatch/tests/test_robustapply.py

     
    1 from django.dispatch.robustapply import *
    2 
    3 import unittest
    4 
    5 def noArgument():
    6     pass
    7 
    8 def oneArgument(blah):
    9     pass
    10 
    11 def twoArgument(blah, other):
    12     pass
    13 
    14 class TestCases(unittest.TestCase):
    15     def test01(self):
    16         robustApply(noArgument)
    17    
    18     def test02(self):
    19         self.assertRaises(TypeError, robustApply, noArgument, "this")
    20    
    21     def test03(self):
    22         self.assertRaises(TypeError, robustApply, oneArgument)
    23    
    24     def test04(self):
    25         """Raise error on duplication of a particular argument"""
    26         self.assertRaises(TypeError, robustApply, oneArgument, "this", blah = "that")
    27 
    28 def getSuite():
    29     return unittest.makeSuite(TestCases,'test')
    30 
    31 
    32 if __name__ == "__main__":
    33     unittest.main()
    34    
Back to Top