Changeset 8223
- Timestamp:
- 08/06/08 10:32:46 (1 year 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 response = receiver(signal=self, sender=sender, **named) 134 responses.append((receiver, response)) 135 return responses 136 137 def send_robust(self, sender, **named): 138 """Send signal from sender to all connected receivers catching errors 139 140 sender -- the sender of the signal 141 Can be any python object (normally one registered with 142 a connect if you actually want something to occur). 143 144 named -- named arguments which will be passed to receivers. 145 These arguments must be a subset of the argument names 146 defined in providing_args. 147 148 Return a list of tuple pairs [(receiver, response), ... ], 149 may raise DispatcherKeyError 150 151 if any receiver raises an error (specifically any subclass of Exception), 152 the error instance is returned as the result for that receiver. 153 """ 154 155 responses = [] 156 if not self.receivers: 157 return responses 158 159 # Call each receiver with whatever arguments it can accept. 160 # Return a list of tuple pairs [(receiver, response), ... ]. 161 for receiver in self._live_receivers(_make_id(sender)): 162 try: 163 response = receiver(signal=self, sender=sender, **named) 164 except Exception, err: 165 responses.append((receiver, err)) 166 else: 167 responses.append((receiver, response)) 168 return responses 169 170 def _live_receivers(self, senderkey): 171 """Filter sequence of receivers to get resolved, live receivers 172 173 This checks for weak references 174 and resolves them, then returning only live 175 receivers. 176 """ 177 none_senderkey = _make_id(None) 178 179 for (receiverkey, r_senderkey), receiver in self.receivers: 180 if r_senderkey == none_senderkey or r_senderkey == senderkey: 305 181 if isinstance(receiver, WEAKREF_TYPES): 182 # Dereference the weak reference. 306 183 receiver = receiver() 307 # this should only (rough guess) be possible if somehow, deref'ing 308 # triggered a wipe. 309 if receiver is None: 310 continue 311 receivers[receiver] = 1 312 yield receiver 313 except TypeError: 314 # dead weakrefs raise TypeError on hash... 315 pass 316 317 def send(signal=Any, sender=Anonymous, *arguments, **named): 318 """Send signal from sender to all connected receivers. 319 320 signal -- (hashable) signal value, see connect for details 321 322 sender -- the sender of the signal 323 324 if Any, only receivers registered for Any will receive 325 the message. 326 327 if Anonymous, only receivers registered to receive 328 messages from Anonymous or Any will receive the message 329 330 Otherwise can be any python object (normally one 331 registered with a connect if you actually want 332 something to occur). 333 334 arguments -- positional arguments which will be passed to 335 *all* receivers. Note that this may raise TypeErrors 336 if the receivers do not allow the particular arguments. 337 Note also that arguments are applied before named 338 arguments, so they should be used with care. 339 340 named -- named arguments which will be filtered according 341 to the parameters of the receivers to only provide those 342 acceptable to the receiver. 343 344 Return a list of tuple pairs [(receiver, response), ... ] 345 346 if any receiver raises an error, the error propagates back 347 through send, terminating the dispatch loop, so it is quite 348 possible to not have all receivers called if a raises an 349 error. 350 """ 351 # Call each receiver with whatever arguments it can accept. 352 # Return a list of tuple pairs [(receiver, response), ... ]. 353 responses = [] 354 for receiver in getAllReceivers(sender, signal): 355 response = robustapply.robustApply( 356 receiver, 357 signal=signal, 358 sender=sender, 359 *arguments, 360 **named 361 ) 362 responses.append((receiver, response)) 363 return responses 364 365 366 def sendExact(signal=Any, sender=Anonymous, *arguments, **named ): 367 """Send signal only to those receivers registered for exact message 368 369 sendExact allows for avoiding Any/Anonymous registered 370 handlers, sending only to those receivers explicitly 371 registered for a particular signal on a particular 372 sender. 373 """ 374 responses = [] 375 for receiver in liveReceivers(getReceivers(sender, signal)): 376 response = robustapply.robustApply( 377 receiver, 378 signal=signal, 379 sender=sender, 380 *arguments, 381 **named 382 ) 383 responses.append((receiver, response)) 384 return responses 385 386 387 def _removeReceiver(receiver): 388 """Remove receiver from connections.""" 389 if not sendersBack: 390 # During module cleanup the mapping will be replaced with None 391 return False 392 backKey = id(receiver) 393 for senderkey in sendersBack.get(backKey,()): 394 try: 395 signals = connections[senderkey].keys() 396 except KeyError,err: 397 pass 398 else: 399 for signal in signals: 400 try: 401 receivers = connections[senderkey][signal] 402 except KeyError: 403 pass 184 if receiver is not None: 185 yield receiver 404 186 else: 405 try: 406 receivers.remove(receiver) 407 except Exception, err: 408 pass 409 _cleanupConnections(senderkey, signal) 410 try: 411 del sendersBack[ backKey ] 412 except KeyError: 413 pass 414 415 def _cleanupConnections(senderkey, signal): 416 """Delete any empty signals for senderkey. Delete senderkey if empty.""" 417 try: 418 receivers = connections[senderkey][signal] 419 except: 420 pass 421 else: 422 if not receivers: 423 # No more connected receivers. Therefore, remove the signal. 424 try: 425 signals = connections[senderkey] 426 except KeyError: 427 pass 428 else: 429 del signals[signal] 430 if not signals: 431 # No more signal connections. Therefore, remove the sender. 432 _removeSender(senderkey) 433 434 def _removeSender(senderkey): 435 """Remove senderkey from connections.""" 436 _removeBackrefs(senderkey) 437 438 connections.pop(senderkey, None) 439 senders.pop(senderkey, None) 440 441 442 def _removeBackrefs(senderkey): 443 """Remove all back-references to this senderkey""" 444 for receiver_list in connections.pop(senderkey, {}).values(): 445 for receiver in receiver_list: 446 _killBackref(receiver, senderkey) 447 448 449 def _removeOldBackRefs(senderkey, signal, receiver, receivers): 450 """Kill old sendersBack references from receiver 451 452 This guards against multiple registration of the same 453 receiver for a given signal and sender leaking memory 454 as old back reference records build up. 455 456 Also removes old receiver instance from receivers 457 """ 458 try: 459 index = receivers.index(receiver) 460 # need to scan back references here and remove senderkey 461 except ValueError: 462 return False 463 else: 464 oldReceiver = receivers[index] 465 del receivers[index] 466 found = 0 467 signals = connections.get(signal) 468 if signals is not None: 469 for sig,recs in connections.get(signal,{}).iteritems(): 470 if sig != signal: 471 for rec in recs: 472 if rec is oldReceiver: 473 found = 1 474 break 475 if not found: 476 _killBackref(oldReceiver, senderkey) 477 return True 478 return False 479 480 481 def _killBackref(receiver, senderkey): 482 """Do the actual removal of back reference from receiver to senderkey""" 483 receiverkey = id(receiver) 484 receivers_list = sendersBack.get(receiverkey, ()) 485 while senderkey in receivers_list: 486 try: 487 receivers_list.remove(senderkey) 488 except: 489 break 490 if not receivers_list: 491 try: 492 del sendersBack[ receiverkey ] 493 except KeyError: 494 pass 495 return True 187 yield receiver 188 189 def _remove_receiver(self, receiver): 190 """Remove dead receivers from connections.""" 191 192 to_remove = [] 193 for key, connected_receiver in self.receivers: 194 if connected_receiver == receiver: 195 to_remove.append(key) 196 for key in to_remove: 197 for idx, (r_key, _) in enumerate(self.receivers): 198 if r_key == key: 199 del self.receivers[idx] 200 201 def connect(receiver, signal, sender=None, weak=True): 202 """ 203 For backward compatibility only. See Signal.connect() 204 """ 205 warnings.warn( 206 category = DeprecationWarning, 207 message = "dispatcher.connect() is deprecated; use Signal.connect() instead.", 208 stacklevel = 2 209 ) 210 return signal.connect(receiver, sender, weak) 211 212 def disconnect(receiver, signal, sender=None, weak=True): 213 """ 214 For backward compatibility only. See Signal.disconnect() 215 """ 216 warnings.warn( 217 category = DeprecationWarning, 218 message = "dispatcher.disconnect() is deprecated; use Signal.disconnect() instead.", 219 stacklevel = 2 220 ) 221 signal.disconnect(receiver, sender, weak) 222 223 def send(signal, sender=None, **named): 224 """ 225 For backward compatibility only. See Signal.send() 226 """ 227 warnings.warn( 228 category = DeprecationWarning, 229 message = "dispatcher.send() is deprecated; use Signal.send() instead.", 230 stacklevel = 2 231 ) 232 return signal.send(sender=sender, **named) 233 234 def sendExact(signal, sender, **named ): 235 """ 236 This function is deprecated, as it now has the same meaning as send. 237 """ 238 warnings.warn( 239 category = DeprecationWarning, 240 message = "dispatcher.sendExact() is deprecated; use Signal.send() instead.", 241 stacklevel = 2 242 ) 243 return signal.send(sender=sender, **named) django/trunk/django/dispatch/__init__.py
r4265 r8223 1 1 """Multi-consumer multi-producer dispatching mechanism 2 3 Originally based on pydispatch (BSD) http://pypi.python.org/pypi/PyDispatcher/2.0.1 4 See license.txt for original license. 5 6 Heavily modified for Django's purposes. 2 7 """ 3 __version__ = "1.0.0"4 __author__ = "Patrick K. O'Brien"5 __license__ = "BSD-style, see license.txt for details"6 8 9 from django.dispatch.dispatcher import Signal django/trunk/django/dispatch/license.txt
r3415 r8223 1 PyDispatcher License 1 django.dispatch was originally forked from PyDispatcher. 2 3 PyDispatcher License: 2 4 3 5 Copyright (c) 2001-2003, Patrick K. O'Brien and Contributors django/trunk/django/dispatch/saferef.py
r8046 r8223 1 """Refactored "safe reference" from dispatcher.py""" 1 """ 2 "Safe weakrefs", originally from pyDispatcher. 3 4 Provides a way to safely weakref any function, including bound methods (which 5 aren't handled by the core weakref module). 6 """ 7 2 8 import weakref, traceback 3 9 … … 61 67 62 68 """ 69 63 70 _allInstances = weakref.WeakValueDictionary() 71 64 72 def __new__( cls, target, onDelete=None, *arguments,**named ): 65 73 """Create new instance or return current instance … … 84 92 base.__init__( target, onDelete, *arguments,**named) 85 93 return base 94 86 95 def __init__(self, target, onDelete=None): 87 96 """Return a weak-reference-like instance for a bound method … … 123 132 self.selfName = str(target.im_self) 124 133 self.funcName = str(target.im_func.__name__) 134 125 135 def calculateKey( cls, target ): 126 136 """Calculate the reference key for this reference … … 131 141 return (id(target.im_self),id(target.im_func)) 132 142 calculateKey = classmethod( calculateKey ) 143 133 144 def __str__(self): 134 145 """Give a friendly representation of the object""" … … 138 149 self.funcName, 139 150 ) 151 140 152 __repr__ = __str__ 153 141 154 def __nonzero__( self ): 142 155 """Whether we are still a valid reference""" 143 156 return self() is not None 157 144 158 def __cmp__( self, other ): 145 159 """Compare with another reference""" … … 147 161 return cmp( self.__class__, type(other) ) 148 162 return cmp( self.key, other.key) 163 149 164 def __call__(self): 150 165 """Return a strong reference to the bound method … … 225 240 return None 226 241 227 228 242 def get_bound_method_weakref(target, onDelete): 229 243 """Instantiates the appropiate BoundMethodWeakRef, depending on the details of … … 235 249 # no luck, use the alternative implementation: 236 250 return BoundNonDescriptorMethodWeakref(target=target, onDelete=onDelete) 237 django/trunk/django/test/client.py
r8015 r8223 12 12 from django.core.handlers.wsgi import WSGIRequest 13 13 from django.core.signals import got_request_exception 14 from django.dispatch import dispatcher15 14 from django.http import SimpleCookie, HttpRequest 16 15 from django.template import TemplateDoesNotExist … … 60 59 self.load_middleware() 61 60 62 dispatcher.send(signal=signals.request_started)61 signals.request_started.send(sender=self.__class__) 63 62 try: 64 63 request = WSGIRequest(environ) … … 70 69 response = self.apply_response_fixes(request, response) 71 70 finally: 72 dispatcher.send(signal=signals.request_finished)71 signals.request_finished.send(sender=self.__class__) 73 72 74 73 return response 75 74 76 def store_rendered_templates(store, signal, sender, template, context ):75 def store_rendered_templates(store, signal, sender, template, context, **kwargs): 77 76 """ 78 77 Stores templates and contexts that are rendered. … … 161 160 self.exc_info = None 162 161 163 def store_exc_info(self, * args, **kwargs):162 def store_exc_info(self, **kwargs): 164 163 """ 165 164 Stores exceptions when they are generated by a view. … … 203 202 data = {} 204 203 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) 206 205 207 206 # 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) 209 208 210 209 try: django/trunk/django/test/signals.py
r4265 r8223 1 template_rendered = object() 1 from django.dispatch import Signal 2 3 template_rendered = Signal(providing_args=["template", "context"]) django/trunk/django/test/utils.py
r7949 r8223 4 4 from django.core import mail 5 5 from django.core.management import call_command 6 from django.dispatch import dispatcher7 6 from django.test import signals 8 7 from django.template import Template … … 18 17 that can be intercepted by the test system Client 19 18 """ 20 dispatcher.send(signal=signals.template_rendered,sender=self, template=self, context=context)19 signals.template_rendered.send(sender=self, template=self, context=context) 21 20 return self.nodelist.render(context) 22 21 django/trunk/tests/modeltests/signals/models.py
r7477 r8223 4 4 5 5 from django.db import models 6 from django.dispatch import dispatcher7 6 8 7 class Person(models.Model): … … 13 12 return u"%s %s" % (self.first_name, self.last_name) 14 13 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): 14 def pre_save_test(signal, sender, instance, **kwargs): 23 15 print 'pre_save signal,', instance 24 16 if kwargs.get('raw'): 25 17 print 'Is raw' 26 18 27 def post_save_test(s ender, instance, **kwargs):19 def post_save_test(signal, sender, instance, **kwargs): 28 20 print 'post_save signal,', instance 29 21 if 'created' in kwargs: … … 35 27 print 'Is raw' 36 28 37 def pre_delete_test(s ender, instance, **kwargs):29 def pre_delete_test(signal, sender, instance, **kwargs): 38 30 print 'pre_delete signal,', instance 39 31 print 'instance.id is not None: %s' % (instance.id != None) 40 32 41 def post_delete_test(s ender, instance, **kwargs):33 def post_delete_test(signal, sender, instance, **kwargs): 42 34 print 'post_delete signal,', instance 43 35 print 'instance.id is None: %s' % (instance.id == None) 44 36 45 37 __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) 52 42 53 43 >>> p1 = Person(first_name='John', last_name='Smith') 54 44 >>> p1.save() 55 pre_save_nokwargs signal56 45 pre_save signal, John Smith 57 post_save_nokwargs signal58 46 post_save signal, John Smith 59 47 Is created … … 61 49 >>> p1.first_name = 'Tom' 62 50 >>> p1.save() 63 pre_save_nokwargs signal64 51 pre_save signal, Tom Smith 65 post_save_nokwargs signal66 52 post_save signal, Tom Smith 67 53 Is updated … … 69 55 # Calling an internal method purely so that we can trigger a "raw" save. 70 56 >>> p1.save_base(raw=True) 71 pre_save_nokwargs signal72 57 pre_save signal, Tom Smith 73 58 Is raw 74 post_save_nokwargs signal75 59 post_save signal, Tom Smith 76 60 Is updated … … 86 70 >>> p2.id = 99999 87 71 >>> p2.save() 88 pre_save_nokwargs signal89 72 pre_save signal, James Jones 90 post_save_nokwargs signal91 73 post_save signal, James Jones 92 74 Is created … … 94 76 >>> p2.id = 99998 95 77 >>> p2.save() 96 pre_save_nokwargs signal97 78 pre_save signal, James Jones 98 post_save_nokwargs signal99 79 post_save signal, James Jones 100 80 Is created … … 109 89 [<Person: James Jones>] 110 90 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) 117 95 """} django/trunk/tests/regressiontests/dispatch/tests/__init__.py
r5876 r8223 3 3 """ 4 4 5 from test_saferef import * 5 6 from test_dispatcher import * 6 from test_robustapply import *7 from test_saferef import *django/trunk/tests/regressiontests/dispatch/tests/test_dispatcher.py
r8004 r8223 1 from django.dispatch.dispatcher import * 2 from django.dispatch import dispatcher, robust 1 from django.dispatch import Signal 3 2 import unittest 4 3 import copy … … 16 15 gc.collect() 17 16 18 def x(a): 19 return a 20 21 class Dummy(object): 22 pass 17 def receiver_1_arg(val, **kwargs): 18 return val 23 19 24 20 class Callable(object): 25 def __call__(self, a):26 return a21 def __call__(self, val, **kwargs): 22 return val 27 23 28 def a(self, a): 29 return a 24 def a(self, val, **kwargs): 25 return val 26 27 a_signal = Signal(providing_args=["val"]) 30 28 31 29 class DispatcherTests(unittest.TestCase): 32 30 """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): 42 33 """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 = [] 46 38 47 39 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") 53 43 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") 64 51 self.assertEqual(result, expected) 65 disconnect(x, signal) 66 self.assertEqual(list(getAllReceivers(None,signal)), []) 67 self._testIsClean() 68 69 def testAnyRegistration(self): 70 a = Dummy() 71 signal = 'this' 72 connect(x, signal, Any) 73 expected = [(x,a)] 74 result = send('this',object(), a=a) 75 self.assertEqual(result, expected) 76 disconnect(x, signal, Any) 77 expected = [] 78 result = send('this',object(), a=a) 79 self.assertEqual(result, expected) 80 self.assertEqual(list(getAllReceivers(Any,signal)), []) 81 82 self._testIsClean() 83 84 def testAnyRegistration2(self): 85 a = Dummy() 86 signal = 'this' 87 connect(x, Any, a) 88 expected = [(x,a)] 89 result = send('this',a, a=a) 90 self.assertEqual(result, expected) 91 disconnect(x, Any, a) 92 self.assertEqual(list(getAllReceivers(a,Any)), []) 93 self._testIsClean() 52 a_signal.disconnect(receiver_1_arg) 53 self._testIsClean(a_signal) 94 54 95 55 def testGarbageCollected(self): 96 56 a = Callable() 97 b = Dummy() 98 signal = 'this' 99 connect(a.a, signal, b) 57 a_signal.connect(a.a, sender=self) 100 58 expected = [] 101 59 del a 102 60 garbage_collect() 103 result = send('this',b, a=b)61 result = a_signal.send(sender=self, val="test") 104 62 self.assertEqual(result, expected) 105 self.assertEqual(list(getAllReceivers(b,signal)), []) 106 self._testIsClean() 107 108 def testGarbageCollectedObj(self): 109 class x: 110 def __call__(self, a): 111 return a 112 a = Callable() 113 b = Dummy() 114 signal = 'this' 115 connect(a, signal, b) 116 expected = [] 117 del a 118 garbage_collect() 119 result = send('this',b, a=b) 120 self.assertEqual(result, expected) 121 self.assertEqual(list(getAllReceivers(b,signal)), []) 122 self._testIsClean() 123 63 self._testIsClean(a_signal) 124 64 125 65 def testMultipleRegistration(self): 126 66 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") 136 74 self.assertEqual(len(result), 1) 137 self.assertEqual(len( list(getAllReceivers(b,signal))), 1)75 self.assertEqual(len(a_signal.receivers), 1) 138 76 del a 139 del b140 77 del result 141 78 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) 143 93 144 94 def testRobust(self): 145 95 """Test the sendRobust function""" 146 def fails( ):96 def fails(val, **kwargs): 147 97 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") 152 100 err = result[0][1] 153 101 self.assert_(isinstance(err, ValueError)) 154 102 self.assertEqual(err.args, ('this',)) 103 a_signal.disconnect(fails) 104 self._testIsClean(a_signal) 155 105 156 106 def getSuite():
