Changeset 8223
- Timestamp:
- 08/06/08 10:32:46 (4 months ago)
- Files:
-
- django/trunk/django/contrib/auth/management/__init__.py (modified) (3 diffs)
- django/trunk/django/contrib/contenttypes/generic.py (modified) (2 diffs)
- django/trunk/django/contrib/contenttypes/management.py (modified) (2 diffs)
- django/trunk/django/contrib/sites/management.py (modified) (2 diffs)
- django/trunk/django/core/handlers/base.py (modified) (2 diffs)
- django/trunk/django/core/handlers/modpython.py (modified) (3 diffs)
- django/trunk/django/core/handlers/wsgi.py (modified) (3 diffs)
- django/trunk/django/core/management/commands/flush.py (modified) (1 diff)
- django/trunk/django/core/management/sql.py (modified) (1 diff)
- django/trunk/django/core/signals.py (modified) (1 diff)
- django/trunk/django/db/__init__.py (modified) (2 diffs)
- django/trunk/django/db/models/base.py (modified) (6 diffs)
- django/trunk/django/db/models/fields/__init__.py (modified) (2 diffs)
- django/trunk/django/db/models/fields/related.py (modified) (3 diffs)
- django/trunk/django/db/models/manager.py (modified) (2 diffs)
- django/trunk/django/db/models/manipulators.py (modified) (2 diffs)
- django/trunk/django/db/models/query.py (modified) (3 diffs)
- django/trunk/django/db/models/signals.py (modified) (1 diff)
- django/trunk/django/db/models/sql/query.py (modified) (3 diffs)
- django/trunk/django/dispatch/dispatcher.py (modified) (1 diff)
- django/trunk/django/dispatch/errors.py (deleted)
- django/trunk/django/dispatch/__init__.py (modified) (1 diff)
- django/trunk/django/dispatch/license.txt (modified) (1 diff)
- django/trunk/django/dispatch/robustapply.py (deleted)
- django/trunk/django/dispatch/robust.py (deleted)
- django/trunk/django/dispatch/saferef.py (modified) (9 diffs)
- django/trunk/django/test/client.py (modified) (5 diffs)
- django/trunk/django/test/signals.py (modified) (1 diff)
- django/trunk/django/test/utils.py (modified) (2 diffs)
- django/trunk/tests/modeltests/signals/models.py (modified) (8 diffs)
- django/trunk/tests/regressiontests/dispatch/tests/__init__.py (modified) (1 diff)
- django/trunk/tests/regressiontests/dispatch/tests/test_dispatcher.py (modified) (2 diffs)
- django/trunk/tests/regressiontests/dispatch/tests/test_robustapply.py (deleted)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
django/trunk/django/contrib/auth/management/__init__.py
r7598 r8223 3 3 """ 4 4 5 from django.dispatch import dispatcher6 5 from django.db.models import get_models, signals 7 6 from django.contrib.auth import models as auth_app … … 17 16 return perms + list(opts.permissions) 18 17 19 def create_permissions(app, created_models, verbosity ):18 def create_permissions(app, created_models, verbosity, **kwargs): 20 19 from django.contrib.contenttypes.models import ContentType 21 20 from django.contrib.auth.models import Permission … … 46 45 break 47 46 48 if 'create_permissions' not in [i.__name__ for i in dispatcher.getAllReceivers(signal=signals.post_syncdb)]: 49 dispatch er.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)47 signals.post_syncdb.connect(create_permissions, 48 dispatch_uid = "django.contrib.auth.management.create_permissions") 49 signals.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 9 9 from django.db.models.fields.related import RelatedField, Field, ManyToManyRel 10 10 from django.db.models.loading import get_model 11 from django.dispatch import dispatcher12 11 from django.utils.functional import curry 13 12 … … 30 29 31 30 # 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) 33 32 34 33 # Connect myself as the descriptor for this field 35 34 setattr(cls, name, self) 36 35 37 def instance_pre_init(self, signal, sender, args, kwargs ):36 def instance_pre_init(self, signal, sender, args, kwargs, **_kwargs): 38 37 """ 39 38 Handles initializing an object with the generic FK instaed of django/trunk/django/contrib/contenttypes/management.py
r6287 r8223 1 1 from django.contrib.contenttypes.models import ContentType 2 from django.dispatch import dispatcher3 2 from django.db.models import get_apps, get_models, signals 4 3 from django.utils.encoding import smart_unicode 5 4 6 def update_contenttypes(app, created_models, verbosity=2 ):5 def update_contenttypes(app, created_models, verbosity=2, **kwargs): 7 6 """ 8 7 Creates content types for models in the given app, removing any model … … 38 37 update_contenttypes(app, None, verbosity) 39 38 40 dispatcher.connect(update_contenttypes, signal=signals.post_syncdb)39 signals.post_syncdb.connect(update_contenttypes) 41 40 42 41 if __name__ == "__main__": django/trunk/django/contrib/sites/management.py
r7723 r8223 3 3 """ 4 4 5 from django.dispatch import dispatcher6 5 from django.db.models import signals 7 6 from django.contrib.sites.models import Site 8 7 from django.contrib.sites import models as site_app 9 8 10 def create_default_site(app, created_models, verbosity ):9 def create_default_site(app, created_models, verbosity, **kwargs): 11 10 if Site in created_models: 12 11 if verbosity >= 2: … … 16 15 Site.objects.clear_cache() 17 16 18 dispatcher.connect(create_default_site, sender=site_app, signal=signals.post_syncdb)17 signals.post_syncdb.connect(create_default_site, sender=site_app) django/trunk/django/core/handlers/base.py
r8015 r8223 3 3 from django import http 4 4 from django.core import signals 5 from django.dispatch import dispatcher6 5 from django.utils.encoding import force_unicode 7 6 … … 123 122 # Get the exception info now, in case another exception is thrown later. 124 123 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) 126 125 return self.handle_uncaught_exception(request, resolver, exc_info) 127 126 django/trunk/django/core/handlers/modpython.py
r8032 r8223 6 6 from django.core.handlers.base import BaseHandler 7 7 from django.core.urlresolvers import set_script_prefix 8 from django.dispatch import dispatcher9 8 from django.utils import datastructures 10 9 from django.utils.encoding import force_unicode, smart_str … … 175 174 176 175 set_script_prefix(req.get_options().get('django.root', '')) 177 dispatcher.send(signal=signals.request_started)176 signals.request_started.send(sender=self.__class__) 178 177 try: 179 178 try: … … 189 188 response = self.apply_response_fixes(request, response) 190 189 finally: 191 dispatcher.send(signal=signals.request_finished)190 signals.request_finished.send(sender=self.__class__) 192 191 193 192 # Convert our custom HttpResponse object back into the mod_python req. django/trunk/django/core/handlers/wsgi.py
r8032 r8223 10 10 from django.core.handlers import base 11 11 from django.core.urlresolvers import set_script_prefix 12 from django.dispatch import dispatcher13 12 from django.utils import datastructures 14 13 from django.utils.encoding import force_unicode … … 208 207 209 208 set_script_prefix(base.get_script_name(environ)) 210 dispatcher.send(signal=signals.request_started)209 signals.request_started.send(sender=self.__class__) 211 210 try: 212 211 try: … … 222 221 response = self.apply_response_fixes(request, response) 223 222 finally: 224 dispatcher.send(signal=signals.request_finished)223 signals.request_finished.send(sender=self.__class__) 225 224 226 225 try: django/trunk/django/core/management/commands/flush.py
r7294 r8223 16 16 from django.conf import settings 17 17 from django.db import connection, transaction, models 18 from django.dispatch import dispatcher19 18 from django.core.management.sql import sql_flush, emit_post_sync_signal 20 19 django/trunk/django/core/management/sql.py
r8157 r8223 493 493 if verbosity >= 2: 494 494 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() 1 from django.dispatch import Signal 2 3 request_started = Signal() 4 request_finished = Signal() 5 got_request_exception = Signal(providing_args=["request"]) django/trunk/django/db/__init__.py
r7477 r8223 3 3 from django.core import signals 4 4 from django.core.exceptions import ImproperlyConfigured 5 from django.dispatch import dispatcher6 5 from django.utils.functional import curry 7 6 … … 59 58 # Register an event that closes the database connection 60 59 # when a Django request is finished. 61 dispatcher.connect(connection.close, signal=signals.request_finished) 60 def close_connection(**kwargs): 61 connection.close() 62 signals.request_finished.connect(close_connection) 62 63 63 64 # Register an event that resets connection.queries 64 65 # when a Django request is started. 65 def reset_queries( ):66 def reset_queries(**kwargs): 66 67 connection.queries = [] 67 dispatcher.connect(reset_queries, signal=signals.request_started)68 signals.request_started.connect(reset_queries) 68 69 69 70 # Register an event that rolls back the connection 70 71 # when a Django request has an exception. 71 def _rollback_on_exception( ):72 def _rollback_on_exception(**kwargs): 72 73 from django.db import transaction 73 74 transaction.rollback_unless_managed() 74 dispatcher.connect(_rollback_on_exception, signal=signals.got_request_exception)75 signals.got_request_exception.connect(_rollback_on_exception) django/trunk/django/db/models/base.py
r8217 r8223 20 20 from django.db.models import signals 21 21 from django.db.models.loading import register_models, get_model 22 from django.dispatch import dispatcher23 22 from django.utils.functional import curry 24 23 from django.utils.encoding import smart_str, force_unicode, smart_unicode … … 162 161 cls.get_absolute_url = curry(get_absolute_url, opts, cls.get_absolute_url) 163 162 164 dispatcher.send(signal=signals.class_prepared,sender=cls)163 signals.class_prepared.send(sender=cls) 165 164 166 165 … … 169 168 170 169 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) 172 171 173 172 # There is a rather weird disparity here; if kwargs, it's set, then args … … 240 239 if kwargs: 241 240 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) 243 242 244 243 def __repr__(self): … … 289 288 meta = self._meta 290 289 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) 293 291 else: 294 292 meta = cls._meta … … 352 350 353 351 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) 356 354 357 355 save_base.alters_data = True django/trunk/django/db/models/fields/__init__.py
r8191 r8223 11 11 from django.db.models import signals 12 12 from django.db.models.query_utils import QueryWrapper 13 from django.dispatch import dispatcher14 13 from django.conf import settings 15 14 from django.core import validators … … 820 819 setattr(cls, 'get_%s_size' % self.name, curry(cls._get_FIELD_size, field=self)) 821 820 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): 825 824 if getattr(instance, self.attname): 826 825 file_name = getattr(instance, 'get_%s_filename' % self.name)() django/trunk/django/db/models/fields/related.py
r8212 r8223 10 10 from django import oldforms 11 11 from django import forms 12 from django.dispatch import dispatcher13 12 14 13 try: … … 75 74 pending_lookups.setdefault(key, []).append(value) 76 75 77 def do_pending_lookups(sender ):76 def do_pending_lookups(sender, **kwargs): 78 77 """ 79 78 Handle any pending relations to the sending model. Sent from class_prepared. … … 83 82 operation(field, sender, cls) 84 83 85 dispatcher.connect(do_pending_lookups, signal=signals.class_prepared)84 signals.class_prepared.connect(do_pending_lookups) 86 85 87 86 def manipulator_valid_rel_key(f, self, field_data, all_data): django/trunk/django/db/models/manager.py
r7477 r8223 2 2 3 3 from django.db.models.query import QuerySet, EmptyQuerySet, insert_query 4 from django.dispatch import dispatcher5 4 from django.db.models import signals 6 5 from django.db.models.fields import FieldDoesNotExist 7 6 8 def ensure_default_manager(sender ):7 def ensure_default_manager(sender, **kwargs): 9 8 cls = sender 10 9 if not getattr(cls, '_default_manager', None) and not cls._meta.abstract: … … 17 16 cls.add_to_class('objects', Manager()) 18 17 19 dispatcher.connect(ensure_default_manager, signal=signals.class_prepared)18 signals.class_prepared.connect(ensure_default_manager) 20 19 21 20 class Manager(object): django/trunk/django/db/models/manipulators.py
r7967 r8223 3 3 from django.core import validators 4 4 from django.db.models.fields import FileField, AutoField 5 from django.dispatch import dispatcher6 5 from django.db.models import signals 7 6 from django.utils.functional import curry … … 12 11 from django.utils import datetime_safe 13 12 14 def add_manipulators(sender ):13 def add_manipulators(sender, **kwargs): 15 14 cls = sender 16 15 cls.add_to_class('AddManipulator', AutomaticAddManipulator) 17 16 cls.add_to_class('ChangeManipulator', AutomaticChangeManipulator) 18 17 19 dispatcher.connect(add_manipulators, signal=signals.class_prepared)18 signals.class_prepared.connect(add_manipulators) 20 19 21 20 class ManipulatorDescriptor(object): django/trunk/django/db/models/query.py
r8199 r8223 8 8 from django.db.models.query_utils import Q, select_related_descend 9 9 from django.db.models import signals, sql 10 from django.dispatch import dispatcher11 10 from django.utils.datastructures import SortedDict 12 11 … … 811 810 # Pre-notify all instances to be deleted. 812 811 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) 815 813 816 814 pk_list = [pk for pk,instance in items] … … 846 844 setattr(instance, field.attname, None) 847 845 848 dispatcher.send(signal=signals.post_delete, sender=cls, 849 instance=instance) 846 signals.post_delete.send(sender=cls, instance=instance) 850 847 setattr(instance, cls._meta.pk.attname, None) 851 848 django/trunk/django/db/models/signals.py
r4265 r8223 1 class_prepared = object() 1 from django.dispatch import Signal 2 2 3 pre_init= object() 4 post_init = object() 3 class_prepared = Signal(providing_args=["class"]) 5 4 6 pre_ save = object()7 post_ save = object()5 pre_init = Signal(providing_args=["instance", "args", "kwargs"]) 6 post_init = Signal(providing_args=["instance"]) 8 7 9 pre_ delete = object()10 post_ delete = object()8 pre_save = Signal(providing_args=["instance", "raw"]) 9 post_save = Signal(providing_args=["instance", "raw", "created"]) 11 10 12 post_syncdb = object() 11 pre_delete = Signal(providing_args=["instance"]) 12 post_delete = Signal(providing_args=["instance"]) 13 14 post_syncdb = Signal(providing_args=["class", "app", "created_models", "verbosity", "interactive"]) django/trunk/django/db/models/sql/query.py
r8112 r8223 12 12 from django.utils.tree import Node 13 13 from django.utils.datastructures import SortedDict 14 from django.dispatch import dispatcher15 14 from django.db import connection 16 15 from django.db.models import signals … … 1671 1670 yield [r[:-trim] for r in rows] 1672 1671 1673 def setup_join_cache(sender ):1672 def setup_join_cache(sender, **kwargs): 1674 1673 """ 1675 1674 The information needed to join between model fields is something that is … … 1681 1680 sender._meta._join_cache = {} 1682 1681 1683 dispatcher.connect(setup_join_cache, signal=signals.class_prepared)1684 1682 signals.class_prepared.connect(setup_join_cache) 1683 django/trunk/django/dispatch/dispatcher.py
r8046 r8223 1 """Multiple-producer-multiple-consumer signal-dispatching2 3 dispatcher is the core of the PyDispatcher system,4 providing the primary API and the core logic for the5 system.6 7 Module attributes of note:8 9 Any -- Singleton used to signal either "Any Sender" or10 "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 represent16 weak references to receivers, and thus must be de-17 referenced on retrieval to retrieve the callable18 object19 connections -- { senderkey (id) : { signal : [receivers...]}}20 senders -- { senderkey (id) : weakref(sender) }21 used for cleaning up sender references on sender22 deletion23 sendersBack -- { receiverkey (id) : [senderkey (id)...] }24 used for cleaning up receiver references on receiver25 deletion, (considerably speeds up the cleanup process26 vs. the original code.)27 """28 1 import 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() 2 import warnings 3 try: 4 set 5 except NameError: 6 from sets import Set as set # Python 2.3 fallback 7 8 from django.dispatch import saferef 70 9 71 10 WEAKREF_TYPES = (weakref.ReferenceType, saferef.BoundMethodWeakref) 72 11 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 12 def _make_id(target): 13 if hasattr(target, 'im_func'): 14 return (id(target.im_self), id(target.im_func)) 15 return id(target) 16 17 class 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)) 266 74 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
