mirror of
				https://github.com/django/django.git
				synced 2025-10-31 09:41:08 +00:00 
			
		
		
		
	Major refactoring of django.dispatch with an eye towards speed. The net result is that signals are up to 90% faster.
Though some attempts and backwards-compatibility were made, speed trumped compatibility. Thus, as usual, check BackwardsIncompatibleChanges for the complete list of backwards-incompatible changes. Thanks to Jeremy Dunck and Keith Busell for the bulk of the work; some ideas from Brian Herring's previous work (refs #4561) were incorporated. Documentation is, sigh, still forthcoming. Fixes #6814 and #3951 (with the new dispatch_uid argument to connect). git-svn-id: http://code.djangoproject.com/svn/django/trunk@8223 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
		| @@ -2,7 +2,6 @@ | |||||||
| Creates permissions for all installed apps that need permissions. | Creates permissions for all installed apps that need permissions. | ||||||
| """ | """ | ||||||
|  |  | ||||||
| from django.dispatch import dispatcher |  | ||||||
| from django.db.models import get_models, signals | from django.db.models import get_models, signals | ||||||
| from django.contrib.auth import models as auth_app | from django.contrib.auth import models as auth_app | ||||||
|  |  | ||||||
| @@ -16,7 +15,7 @@ def _get_all_permissions(opts): | |||||||
|         perms.append((_get_permission_codename(action, opts), u'Can %s %s' % (action, opts.verbose_name_raw))) |         perms.append((_get_permission_codename(action, opts), u'Can %s %s' % (action, opts.verbose_name_raw))) | ||||||
|     return perms + list(opts.permissions) |     return perms + list(opts.permissions) | ||||||
|  |  | ||||||
| def create_permissions(app, created_models, verbosity): | def create_permissions(app, created_models, verbosity, **kwargs): | ||||||
|     from django.contrib.contenttypes.models import ContentType |     from django.contrib.contenttypes.models import ContentType | ||||||
|     from django.contrib.auth.models import Permission |     from django.contrib.auth.models import Permission | ||||||
|     app_models = get_models(app) |     app_models = get_models(app) | ||||||
| @@ -45,7 +44,7 @@ def create_superuser(app, created_models, verbosity, **kwargs): | |||||||
|                 call_command("createsuperuser", interactive=True) |                 call_command("createsuperuser", interactive=True) | ||||||
|             break |             break | ||||||
|  |  | ||||||
| if 'create_permissions' not in [i.__name__ for i in dispatcher.getAllReceivers(signal=signals.post_syncdb)]: | signals.post_syncdb.connect(create_permissions, | ||||||
|     dispatcher.connect(create_permissions, signal=signals.post_syncdb) |     dispatch_uid = "django.contrib.auth.management.create_permissions") | ||||||
| if 'create_superuser' not in [i.__name__ for i in dispatcher.getAllReceivers(signal=signals.post_syncdb, sender=auth_app)]: | signals.post_syncdb.connect(create_superuser, | ||||||
|     dispatcher.connect(create_superuser, sender=auth_app, signal=signals.post_syncdb) |     sender=auth_app, dispatch_uid = "django.contrib.auth.management.create_superuser") | ||||||
|   | |||||||
| @@ -8,7 +8,6 @@ from django.db import connection | |||||||
| from django.db.models import signals | from django.db.models import signals | ||||||
| from django.db.models.fields.related import RelatedField, Field, ManyToManyRel | from django.db.models.fields.related import RelatedField, Field, ManyToManyRel | ||||||
| from django.db.models.loading import get_model | from django.db.models.loading import get_model | ||||||
| from django.dispatch import dispatcher |  | ||||||
| from django.utils.functional import curry | from django.utils.functional import curry | ||||||
|  |  | ||||||
| class GenericForeignKey(object): | class GenericForeignKey(object): | ||||||
| @@ -29,12 +28,12 @@ class GenericForeignKey(object): | |||||||
|         self.cache_attr = "_%s_cache" % name |         self.cache_attr = "_%s_cache" % name | ||||||
|  |  | ||||||
|         # For some reason I don't totally understand, using weakrefs here doesn't work. |         # For some reason I don't totally understand, using weakrefs here doesn't work. | ||||||
|         dispatcher.connect(self.instance_pre_init, signal=signals.pre_init, sender=cls, weak=False) |         signals.pre_init.connect(self.instance_pre_init, sender=cls, weak=False) | ||||||
|  |  | ||||||
|         # Connect myself as the descriptor for this field |         # Connect myself as the descriptor for this field | ||||||
|         setattr(cls, name, self) |         setattr(cls, name, self) | ||||||
|  |  | ||||||
|     def instance_pre_init(self, signal, sender, args, kwargs): |     def instance_pre_init(self, signal, sender, args, kwargs, **_kwargs): | ||||||
|         """ |         """ | ||||||
|         Handles initializing an object with the generic FK instaed of |         Handles initializing an object with the generic FK instaed of | ||||||
|         content-type/object-id fields. |         content-type/object-id fields. | ||||||
|   | |||||||
| @@ -1,9 +1,8 @@ | |||||||
| from django.contrib.contenttypes.models import ContentType | from django.contrib.contenttypes.models import ContentType | ||||||
| from django.dispatch import dispatcher |  | ||||||
| from django.db.models import get_apps, get_models, signals | from django.db.models import get_apps, get_models, signals | ||||||
| from django.utils.encoding import smart_unicode | from django.utils.encoding import smart_unicode | ||||||
|  |  | ||||||
| def update_contenttypes(app, created_models, verbosity=2): | def update_contenttypes(app, created_models, verbosity=2, **kwargs): | ||||||
|     """ |     """ | ||||||
|     Creates content types for models in the given app, removing any model |     Creates content types for models in the given app, removing any model | ||||||
|     entries that no longer have a matching model class. |     entries that no longer have a matching model class. | ||||||
| @@ -37,7 +36,7 @@ def update_all_contenttypes(verbosity=2): | |||||||
|     for app in get_apps(): |     for app in get_apps(): | ||||||
|         update_contenttypes(app, None, verbosity) |         update_contenttypes(app, None, verbosity) | ||||||
|  |  | ||||||
| dispatcher.connect(update_contenttypes, signal=signals.post_syncdb) | signals.post_syncdb.connect(update_contenttypes) | ||||||
|  |  | ||||||
| if __name__ == "__main__": | if __name__ == "__main__": | ||||||
|     update_all_contenttypes() |     update_all_contenttypes() | ||||||
|   | |||||||
| @@ -2,12 +2,11 @@ | |||||||
| Creates the default Site object. | Creates the default Site object. | ||||||
| """ | """ | ||||||
|  |  | ||||||
| from django.dispatch import dispatcher |  | ||||||
| from django.db.models import signals | from django.db.models import signals | ||||||
| from django.contrib.sites.models import Site | from django.contrib.sites.models import Site | ||||||
| from django.contrib.sites import models as site_app | from django.contrib.sites import models as site_app | ||||||
|  |  | ||||||
| def create_default_site(app, created_models, verbosity): | def create_default_site(app, created_models, verbosity, **kwargs): | ||||||
|     if Site in created_models: |     if Site in created_models: | ||||||
|         if verbosity >= 2: |         if verbosity >= 2: | ||||||
|             print "Creating example.com Site object" |             print "Creating example.com Site object" | ||||||
| @@ -15,4 +14,4 @@ def create_default_site(app, created_models, verbosity): | |||||||
|         s.save() |         s.save() | ||||||
|     Site.objects.clear_cache() |     Site.objects.clear_cache() | ||||||
|  |  | ||||||
| dispatcher.connect(create_default_site, sender=site_app, signal=signals.post_syncdb) | signals.post_syncdb.connect(create_default_site, sender=site_app) | ||||||
|   | |||||||
| @@ -2,7 +2,6 @@ import sys | |||||||
|  |  | ||||||
| from django import http | from django import http | ||||||
| from django.core import signals | from django.core import signals | ||||||
| from django.dispatch import dispatcher |  | ||||||
| from django.utils.encoding import force_unicode | from django.utils.encoding import force_unicode | ||||||
|  |  | ||||||
| class BaseHandler(object): | class BaseHandler(object): | ||||||
| @@ -122,7 +121,7 @@ class BaseHandler(object): | |||||||
|         except: # Handle everything else, including SuspiciousOperation, etc. |         except: # Handle everything else, including SuspiciousOperation, etc. | ||||||
|             # Get the exception info now, in case another exception is thrown later. |             # Get the exception info now, in case another exception is thrown later. | ||||||
|             exc_info = sys.exc_info() |             exc_info = sys.exc_info() | ||||||
|             receivers = dispatcher.send(signal=signals.got_request_exception, request=request) |             receivers = signals.got_request_exception.send(sender=self.__class__, request=request) | ||||||
|             return self.handle_uncaught_exception(request, resolver, exc_info) |             return self.handle_uncaught_exception(request, resolver, exc_info) | ||||||
|  |  | ||||||
|     def handle_uncaught_exception(self, request, resolver, exc_info): |     def handle_uncaught_exception(self, request, resolver, exc_info): | ||||||
|   | |||||||
| @@ -5,7 +5,6 @@ from django import http | |||||||
| from django.core import signals | from django.core import signals | ||||||
| from django.core.handlers.base import BaseHandler | from django.core.handlers.base import BaseHandler | ||||||
| from django.core.urlresolvers import set_script_prefix | from django.core.urlresolvers import set_script_prefix | ||||||
| from django.dispatch import dispatcher |  | ||||||
| from django.utils import datastructures | from django.utils import datastructures | ||||||
| from django.utils.encoding import force_unicode, smart_str | from django.utils.encoding import force_unicode, smart_str | ||||||
|  |  | ||||||
| @@ -174,7 +173,7 @@ class ModPythonHandler(BaseHandler): | |||||||
|             self.load_middleware() |             self.load_middleware() | ||||||
|  |  | ||||||
|         set_script_prefix(req.get_options().get('django.root', '')) |         set_script_prefix(req.get_options().get('django.root', '')) | ||||||
|         dispatcher.send(signal=signals.request_started) |         signals.request_started.send(sender=self.__class__) | ||||||
|         try: |         try: | ||||||
|             try: |             try: | ||||||
|                 request = self.request_class(req) |                 request = self.request_class(req) | ||||||
| @@ -188,7 +187,7 @@ class ModPythonHandler(BaseHandler): | |||||||
|                     response = middleware_method(request, response) |                     response = middleware_method(request, response) | ||||||
|                 response = self.apply_response_fixes(request, response) |                 response = self.apply_response_fixes(request, response) | ||||||
|         finally: |         finally: | ||||||
|             dispatcher.send(signal=signals.request_finished) |             signals.request_finished.send(sender=self.__class__) | ||||||
|  |  | ||||||
|         # Convert our custom HttpResponse object back into the mod_python req. |         # Convert our custom HttpResponse object back into the mod_python req. | ||||||
|         req.content_type = response['Content-Type'] |         req.content_type = response['Content-Type'] | ||||||
|   | |||||||
| @@ -9,7 +9,6 @@ from django import http | |||||||
| from django.core import signals | from django.core import signals | ||||||
| from django.core.handlers import base | from django.core.handlers import base | ||||||
| from django.core.urlresolvers import set_script_prefix | from django.core.urlresolvers import set_script_prefix | ||||||
| from django.dispatch import dispatcher |  | ||||||
| from django.utils import datastructures | from django.utils import datastructures | ||||||
| from django.utils.encoding import force_unicode | from django.utils.encoding import force_unicode | ||||||
|  |  | ||||||
| @@ -207,7 +206,7 @@ class WSGIHandler(base.BaseHandler): | |||||||
|             self.initLock.release() |             self.initLock.release() | ||||||
|  |  | ||||||
|         set_script_prefix(base.get_script_name(environ)) |         set_script_prefix(base.get_script_name(environ)) | ||||||
|         dispatcher.send(signal=signals.request_started) |         signals.request_started.send(sender=self.__class__) | ||||||
|         try: |         try: | ||||||
|             try: |             try: | ||||||
|                 request = self.request_class(environ) |                 request = self.request_class(environ) | ||||||
| @@ -221,7 +220,7 @@ class WSGIHandler(base.BaseHandler): | |||||||
|                     response = middleware_method(request, response) |                     response = middleware_method(request, response) | ||||||
|                 response = self.apply_response_fixes(request, response) |                 response = self.apply_response_fixes(request, response) | ||||||
|         finally: |         finally: | ||||||
|             dispatcher.send(signal=signals.request_finished) |             signals.request_finished.send(sender=self.__class__) | ||||||
|  |  | ||||||
|         try: |         try: | ||||||
|             status_text = STATUS_CODE_TEXT[response.status_code] |             status_text = STATUS_CODE_TEXT[response.status_code] | ||||||
|   | |||||||
| @@ -15,7 +15,6 @@ class Command(NoArgsCommand): | |||||||
|     def handle_noargs(self, **options): |     def handle_noargs(self, **options): | ||||||
|         from django.conf import settings |         from django.conf import settings | ||||||
|         from django.db import connection, transaction, models |         from django.db import connection, transaction, models | ||||||
|         from django.dispatch import dispatcher |  | ||||||
|         from django.core.management.sql import sql_flush, emit_post_sync_signal |         from django.core.management.sql import sql_flush, emit_post_sync_signal | ||||||
|  |  | ||||||
|         verbosity = int(options.get('verbosity', 1)) |         verbosity = int(options.get('verbosity', 1)) | ||||||
|   | |||||||
| @@ -492,6 +492,6 @@ def emit_post_sync_signal(created_models, verbosity, interactive): | |||||||
|         app_name = app.__name__.split('.')[-2] |         app_name = app.__name__.split('.')[-2] | ||||||
|         if verbosity >= 2: |         if verbosity >= 2: | ||||||
|             print "Running post-sync handlers for application", app_name |             print "Running post-sync handlers for application", app_name | ||||||
|         dispatcher.send(signal=models.signals.post_syncdb, sender=app, |         models.signals.post_syncdb.send(sender=app, app=app, | ||||||
|             app=app, created_models=created_models, |             created_models=created_models, verbosity=verbosity, | ||||||
|             verbosity=verbosity, interactive=interactive) |             interactive=interactive) | ||||||
|   | |||||||
| @@ -1,3 +1,5 @@ | |||||||
| request_started = object() | from django.dispatch import Signal | ||||||
| request_finished = object() |  | ||||||
| got_request_exception = object() | request_started = Signal() | ||||||
|  | request_finished = Signal() | ||||||
|  | got_request_exception = Signal(providing_args=["request"]) | ||||||
|   | |||||||
| @@ -2,7 +2,6 @@ import os | |||||||
| from django.conf import settings | from django.conf import settings | ||||||
| from django.core import signals | from django.core import signals | ||||||
| from django.core.exceptions import ImproperlyConfigured | from django.core.exceptions import ImproperlyConfigured | ||||||
| from django.dispatch import dispatcher |  | ||||||
| from django.utils.functional import curry | from django.utils.functional import curry | ||||||
|  |  | ||||||
| __all__ = ('backend', 'connection', 'DatabaseError', 'IntegrityError') | __all__ = ('backend', 'connection', 'DatabaseError', 'IntegrityError') | ||||||
| @@ -58,17 +57,19 @@ IntegrityError = backend.IntegrityError | |||||||
|  |  | ||||||
| # Register an event that closes the database connection | # Register an event that closes the database connection | ||||||
| # when a Django request is finished. | # when a Django request is finished. | ||||||
| dispatcher.connect(connection.close, signal=signals.request_finished) | def close_connection(**kwargs): | ||||||
|  |     connection.close() | ||||||
|  | signals.request_finished.connect(close_connection) | ||||||
|  |  | ||||||
| # Register an event that resets connection.queries | # Register an event that resets connection.queries | ||||||
| # when a Django request is started. | # when a Django request is started. | ||||||
| def reset_queries(): | def reset_queries(**kwargs): | ||||||
|     connection.queries = [] |     connection.queries = [] | ||||||
| dispatcher.connect(reset_queries, signal=signals.request_started) | signals.request_started.connect(reset_queries) | ||||||
|  |  | ||||||
| # Register an event that rolls back the connection | # Register an event that rolls back the connection | ||||||
| # when a Django request has an exception. | # when a Django request has an exception. | ||||||
| def _rollback_on_exception(): | def _rollback_on_exception(**kwargs): | ||||||
|     from django.db import transaction |     from django.db import transaction | ||||||
|     transaction.rollback_unless_managed() |     transaction.rollback_unless_managed() | ||||||
| dispatcher.connect(_rollback_on_exception, signal=signals.got_request_exception) | signals.got_request_exception.connect(_rollback_on_exception) | ||||||
|   | |||||||
| @@ -19,7 +19,6 @@ from django.db.models.options import Options | |||||||
| from django.db import connection, transaction | from django.db import connection, transaction | ||||||
| from django.db.models import signals | from django.db.models import signals | ||||||
| from django.db.models.loading import register_models, get_model | from django.db.models.loading import register_models, get_model | ||||||
| from django.dispatch import dispatcher |  | ||||||
| from django.utils.functional import curry | from django.utils.functional import curry | ||||||
| from django.utils.encoding import smart_str, force_unicode, smart_unicode | from django.utils.encoding import smart_str, force_unicode, smart_unicode | ||||||
| from django.core.files.move import file_move_safe | from django.core.files.move import file_move_safe | ||||||
| @@ -161,14 +160,14 @@ class ModelBase(type): | |||||||
|         if hasattr(cls, 'get_absolute_url'): |         if hasattr(cls, 'get_absolute_url'): | ||||||
|             cls.get_absolute_url = curry(get_absolute_url, opts, cls.get_absolute_url) |             cls.get_absolute_url = curry(get_absolute_url, opts, cls.get_absolute_url) | ||||||
|  |  | ||||||
|         dispatcher.send(signal=signals.class_prepared, sender=cls) |         signals.class_prepared.send(sender=cls) | ||||||
|  |  | ||||||
|  |  | ||||||
| class Model(object): | class Model(object): | ||||||
|     __metaclass__ = ModelBase |     __metaclass__ = ModelBase | ||||||
|  |  | ||||||
|     def __init__(self, *args, **kwargs): |     def __init__(self, *args, **kwargs): | ||||||
|         dispatcher.send(signal=signals.pre_init, sender=self.__class__, args=args, kwargs=kwargs) |         signals.pre_init.send(sender=self.__class__, args=args, kwargs=kwargs) | ||||||
|  |  | ||||||
|         # There is a rather weird disparity here; if kwargs, it's set, then args |         # There is a rather weird disparity here; if kwargs, it's set, then args | ||||||
|         # overrides it. It should be one or the other; don't duplicate the work |         # overrides it. It should be one or the other; don't duplicate the work | ||||||
| @@ -239,7 +238,7 @@ class Model(object): | |||||||
|                     pass |                     pass | ||||||
|             if kwargs: |             if kwargs: | ||||||
|                 raise TypeError, "'%s' is an invalid keyword argument for this function" % kwargs.keys()[0] |                 raise TypeError, "'%s' is an invalid keyword argument for this function" % kwargs.keys()[0] | ||||||
|         dispatcher.send(signal=signals.post_init, sender=self.__class__, instance=self) |         signals.post_init.send(sender=self.__class__, instance=self) | ||||||
|  |  | ||||||
|     def __repr__(self): |     def __repr__(self): | ||||||
|         return smart_str(u'<%s: %s>' % (self.__class__.__name__, unicode(self))) |         return smart_str(u'<%s: %s>' % (self.__class__.__name__, unicode(self))) | ||||||
| @@ -288,8 +287,7 @@ class Model(object): | |||||||
|             cls = self.__class__ |             cls = self.__class__ | ||||||
|             meta = self._meta |             meta = self._meta | ||||||
|             signal = True |             signal = True | ||||||
|             dispatcher.send(signal=signals.pre_save, sender=self.__class__, |             signals.pre_save.send(sender=self.__class__, instance=self, raw=raw) | ||||||
|                     instance=self, raw=raw) |  | ||||||
|         else: |         else: | ||||||
|             meta = cls._meta |             meta = cls._meta | ||||||
|             signal = False |             signal = False | ||||||
| @@ -351,8 +349,8 @@ class Model(object): | |||||||
|         transaction.commit_unless_managed() |         transaction.commit_unless_managed() | ||||||
|  |  | ||||||
|         if signal: |         if signal: | ||||||
|             dispatcher.send(signal=signals.post_save, sender=self.__class__, |             signals.post_save.send(sender=self.__class__, instance=self, | ||||||
|                     instance=self, created=(not record_exists), raw=raw) |                 created=(not record_exists), raw=raw) | ||||||
|  |  | ||||||
|     save_base.alters_data = True |     save_base.alters_data = True | ||||||
|  |  | ||||||
|   | |||||||
| @@ -10,7 +10,6 @@ except ImportError: | |||||||
| from django.db import connection, get_creation_module | from django.db import connection, get_creation_module | ||||||
| from django.db.models import signals | from django.db.models import signals | ||||||
| from django.db.models.query_utils import QueryWrapper | from django.db.models.query_utils import QueryWrapper | ||||||
| from django.dispatch import dispatcher |  | ||||||
| from django.conf import settings | from django.conf import settings | ||||||
| from django.core import validators | from django.core import validators | ||||||
| from django import oldforms | from django import oldforms | ||||||
| @@ -819,9 +818,9 @@ class FileField(Field): | |||||||
|         setattr(cls, 'get_%s_url' % self.name, curry(cls._get_FIELD_url, field=self)) |         setattr(cls, 'get_%s_url' % self.name, curry(cls._get_FIELD_url, field=self)) | ||||||
|         setattr(cls, 'get_%s_size' % self.name, curry(cls._get_FIELD_size, field=self)) |         setattr(cls, 'get_%s_size' % self.name, curry(cls._get_FIELD_size, field=self)) | ||||||
|         setattr(cls, 'save_%s_file' % self.name, lambda instance, filename, raw_field, save=True: instance._save_FIELD_file(self, filename, raw_field, save)) |         setattr(cls, 'save_%s_file' % self.name, lambda instance, filename, raw_field, save=True: instance._save_FIELD_file(self, filename, raw_field, save)) | ||||||
|         dispatcher.connect(self.delete_file, signal=signals.post_delete, sender=cls) |         signals.post_delete.connect(self.delete_file, sender=cls) | ||||||
|  |  | ||||||
|     def delete_file(self, instance): |     def delete_file(self, instance, **kwargs): | ||||||
|         if getattr(instance, self.attname): |         if getattr(instance, self.attname): | ||||||
|             file_name = getattr(instance, 'get_%s_filename' % self.name)() |             file_name = getattr(instance, 'get_%s_filename' % self.name)() | ||||||
|             # If the file exists and no other object of this type references it, |             # If the file exists and no other object of this type references it, | ||||||
|   | |||||||
| @@ -9,7 +9,6 @@ from django.utils.functional import curry | |||||||
| from django.core import validators | from django.core import validators | ||||||
| from django import oldforms | from django import oldforms | ||||||
| from django import forms | from django import forms | ||||||
| from django.dispatch import dispatcher |  | ||||||
|  |  | ||||||
| try: | try: | ||||||
|     set |     set | ||||||
| @@ -74,7 +73,7 @@ def add_lazy_relation(cls, field, relation, operation): | |||||||
|         value = (cls, field, operation) |         value = (cls, field, operation) | ||||||
|         pending_lookups.setdefault(key, []).append(value) |         pending_lookups.setdefault(key, []).append(value) | ||||||
|  |  | ||||||
| def do_pending_lookups(sender): | def do_pending_lookups(sender, **kwargs): | ||||||
|     """ |     """ | ||||||
|     Handle any pending relations to the sending model. Sent from class_prepared. |     Handle any pending relations to the sending model. Sent from class_prepared. | ||||||
|     """ |     """ | ||||||
| @@ -82,7 +81,7 @@ def do_pending_lookups(sender): | |||||||
|     for cls, field, operation in pending_lookups.pop(key, []): |     for cls, field, operation in pending_lookups.pop(key, []): | ||||||
|         operation(field, sender, cls) |         operation(field, sender, cls) | ||||||
|  |  | ||||||
| dispatcher.connect(do_pending_lookups, signal=signals.class_prepared) | signals.class_prepared.connect(do_pending_lookups) | ||||||
|  |  | ||||||
| def manipulator_valid_rel_key(f, self, field_data, all_data): | def manipulator_valid_rel_key(f, self, field_data, all_data): | ||||||
|     "Validates that the value is a valid foreign key" |     "Validates that the value is a valid foreign key" | ||||||
|   | |||||||
| @@ -1,11 +1,10 @@ | |||||||
| import copy | import copy | ||||||
|  |  | ||||||
| from django.db.models.query import QuerySet, EmptyQuerySet, insert_query | from django.db.models.query import QuerySet, EmptyQuerySet, insert_query | ||||||
| from django.dispatch import dispatcher |  | ||||||
| from django.db.models import signals | from django.db.models import signals | ||||||
| from django.db.models.fields import FieldDoesNotExist | from django.db.models.fields import FieldDoesNotExist | ||||||
|  |  | ||||||
| def ensure_default_manager(sender): | def ensure_default_manager(sender, **kwargs): | ||||||
|     cls = sender |     cls = sender | ||||||
|     if not getattr(cls, '_default_manager', None) and not cls._meta.abstract: |     if not getattr(cls, '_default_manager', None) and not cls._meta.abstract: | ||||||
|         # Create the default manager, if needed. |         # Create the default manager, if needed. | ||||||
| @@ -16,7 +15,7 @@ def ensure_default_manager(sender): | |||||||
|             pass |             pass | ||||||
|         cls.add_to_class('objects', Manager()) |         cls.add_to_class('objects', Manager()) | ||||||
|  |  | ||||||
| dispatcher.connect(ensure_default_manager, signal=signals.class_prepared) | signals.class_prepared.connect(ensure_default_manager) | ||||||
|  |  | ||||||
| class Manager(object): | class Manager(object): | ||||||
|     # Tracks each time a Manager instance is created. Used to retain order. |     # Tracks each time a Manager instance is created. Used to retain order. | ||||||
|   | |||||||
| @@ -2,7 +2,6 @@ from django.core.exceptions import ObjectDoesNotExist | |||||||
| from django import oldforms | from django import oldforms | ||||||
| from django.core import validators | from django.core import validators | ||||||
| from django.db.models.fields import FileField, AutoField | from django.db.models.fields import FileField, AutoField | ||||||
| from django.dispatch import dispatcher |  | ||||||
| from django.db.models import signals | from django.db.models import signals | ||||||
| from django.utils.functional import curry | from django.utils.functional import curry | ||||||
| from django.utils.datastructures import DotExpandedDict | from django.utils.datastructures import DotExpandedDict | ||||||
| @@ -11,12 +10,12 @@ from django.utils.encoding import smart_str | |||||||
| from django.utils.translation import ugettext as _ | from django.utils.translation import ugettext as _ | ||||||
| from django.utils import datetime_safe | from django.utils import datetime_safe | ||||||
|  |  | ||||||
| def add_manipulators(sender): | def add_manipulators(sender, **kwargs): | ||||||
|     cls = sender |     cls = sender | ||||||
|     cls.add_to_class('AddManipulator', AutomaticAddManipulator) |     cls.add_to_class('AddManipulator', AutomaticAddManipulator) | ||||||
|     cls.add_to_class('ChangeManipulator', AutomaticChangeManipulator) |     cls.add_to_class('ChangeManipulator', AutomaticChangeManipulator) | ||||||
|  |  | ||||||
| dispatcher.connect(add_manipulators, signal=signals.class_prepared) | signals.class_prepared.connect(add_manipulators) | ||||||
|  |  | ||||||
| class ManipulatorDescriptor(object): | class ManipulatorDescriptor(object): | ||||||
|     # This class provides the functionality that makes the default model |     # This class provides the functionality that makes the default model | ||||||
|   | |||||||
| @@ -7,7 +7,6 @@ from django.db import connection, transaction, IntegrityError | |||||||
| from django.db.models.fields import DateField | from django.db.models.fields import DateField | ||||||
| from django.db.models.query_utils import Q, select_related_descend | from django.db.models.query_utils import Q, select_related_descend | ||||||
| from django.db.models import signals, sql | from django.db.models import signals, sql | ||||||
| from django.dispatch import dispatcher |  | ||||||
| from django.utils.datastructures import SortedDict | from django.utils.datastructures import SortedDict | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -810,8 +809,7 @@ def delete_objects(seen_objs): | |||||||
|  |  | ||||||
|         # Pre-notify all instances to be deleted. |         # Pre-notify all instances to be deleted. | ||||||
|         for pk_val, instance in items: |         for pk_val, instance in items: | ||||||
|             dispatcher.send(signal=signals.pre_delete, sender=cls, |             signals.pre_delete.send(sender=cls, instance=instance) | ||||||
|                     instance=instance) |  | ||||||
|  |  | ||||||
|         pk_list = [pk for pk,instance in items] |         pk_list = [pk for pk,instance in items] | ||||||
|         del_query = sql.DeleteQuery(cls, connection) |         del_query = sql.DeleteQuery(cls, connection) | ||||||
| @@ -845,8 +843,7 @@ def delete_objects(seen_objs): | |||||||
|                 if field.rel and field.null and field.rel.to in seen_objs: |                 if field.rel and field.null and field.rel.to in seen_objs: | ||||||
|                     setattr(instance, field.attname, None) |                     setattr(instance, field.attname, None) | ||||||
|  |  | ||||||
|             dispatcher.send(signal=signals.post_delete, sender=cls, |             signals.post_delete.send(sender=cls, instance=instance) | ||||||
|                     instance=instance) |  | ||||||
|             setattr(instance, cls._meta.pk.attname, None) |             setattr(instance, cls._meta.pk.attname, None) | ||||||
|  |  | ||||||
|     transaction.commit_unless_managed() |     transaction.commit_unless_managed() | ||||||
|   | |||||||
| @@ -1,12 +1,14 @@ | |||||||
| class_prepared = object() | from django.dispatch import Signal | ||||||
|  |  | ||||||
| pre_init= object() | class_prepared = Signal(providing_args=["class"]) | ||||||
| post_init = object() |  | ||||||
|  |  | ||||||
| pre_save = object() | pre_init = Signal(providing_args=["instance", "args", "kwargs"]) | ||||||
| post_save = object() | post_init = Signal(providing_args=["instance"]) | ||||||
|  |  | ||||||
| pre_delete = object() | pre_save = Signal(providing_args=["instance", "raw"]) | ||||||
| post_delete = object() | post_save = Signal(providing_args=["instance", "raw", "created"]) | ||||||
|  |  | ||||||
| post_syncdb = object() | pre_delete = Signal(providing_args=["instance"]) | ||||||
|  | post_delete = Signal(providing_args=["instance"]) | ||||||
|  |  | ||||||
|  | post_syncdb = Signal(providing_args=["class", "app", "created_models", "verbosity", "interactive"]) | ||||||
|   | |||||||
| @@ -11,7 +11,6 @@ from copy import deepcopy | |||||||
|  |  | ||||||
| from django.utils.tree import Node | from django.utils.tree import Node | ||||||
| from django.utils.datastructures import SortedDict | from django.utils.datastructures import SortedDict | ||||||
| from django.dispatch import dispatcher |  | ||||||
| from django.db import connection | from django.db import connection | ||||||
| from django.db.models import signals | from django.db.models import signals | ||||||
| from django.db.models.fields import FieldDoesNotExist | from django.db.models.fields import FieldDoesNotExist | ||||||
| @@ -1670,7 +1669,7 @@ def order_modified_iter(cursor, trim, sentinel): | |||||||
|             sentinel): |             sentinel): | ||||||
|         yield [r[:-trim] for r in rows] |         yield [r[:-trim] for r in rows] | ||||||
|  |  | ||||||
| def setup_join_cache(sender): | def setup_join_cache(sender, **kwargs): | ||||||
|     """ |     """ | ||||||
|     The information needed to join between model fields is something that is |     The information needed to join between model fields is something that is | ||||||
|     invariant over the life of the model, so we cache it in the model's Options |     invariant over the life of the model, so we cache it in the model's Options | ||||||
| @@ -1680,5 +1679,5 @@ def setup_join_cache(sender): | |||||||
|     """ |     """ | ||||||
|     sender._meta._join_cache = {} |     sender._meta._join_cache = {} | ||||||
|  |  | ||||||
| dispatcher.connect(setup_join_cache, signal=signals.class_prepared) | signals.class_prepared.connect(setup_join_cache) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,6 +1,9 @@ | |||||||
| """Multi-consumer multi-producer dispatching mechanism | """Multi-consumer multi-producer dispatching mechanism | ||||||
| """ |  | ||||||
| __version__ = "1.0.0" |  | ||||||
| __author__ = "Patrick K. O'Brien" |  | ||||||
| __license__ = "BSD-style, see license.txt for details" |  | ||||||
|  |  | ||||||
|  | Originally based on pydispatch (BSD) http://pypi.python.org/pypi/PyDispatcher/2.0.1 | ||||||
|  | See license.txt for original license. | ||||||
|  |  | ||||||
|  | Heavily modified for Django's purposes. | ||||||
|  | """ | ||||||
|  |  | ||||||
|  | from django.dispatch.dispatcher import Signal | ||||||
| @@ -1,263 +1,183 @@ | |||||||
| """Multiple-producer-multiple-consumer signal-dispatching |  | ||||||
|  |  | ||||||
| dispatcher is the core of the PyDispatcher system, |  | ||||||
| providing the primary API and the core logic for the |  | ||||||
| system. |  | ||||||
|  |  | ||||||
| Module attributes of note: |  | ||||||
|  |  | ||||||
|     Any -- Singleton used to signal either "Any Sender" or |  | ||||||
|         "Any Signal".  See documentation of the _Any class. |  | ||||||
|     Anonymous -- Singleton used to signal "Anonymous Sender" |  | ||||||
|         See documentation of the _Anonymous class. |  | ||||||
|  |  | ||||||
| Internal attributes: |  | ||||||
|     WEAKREF_TYPES -- tuple of types/classes which represent |  | ||||||
|         weak references to receivers, and thus must be de- |  | ||||||
|         referenced on retrieval to retrieve the callable |  | ||||||
|         object |  | ||||||
|     connections -- { senderkey (id) : { signal : [receivers...]}} |  | ||||||
|     senders -- { senderkey (id) : weakref(sender) } |  | ||||||
|         used for cleaning up sender references on sender |  | ||||||
|         deletion |  | ||||||
|     sendersBack -- { receiverkey (id) : [senderkey (id)...] } |  | ||||||
|         used for cleaning up receiver references on receiver |  | ||||||
|         deletion, (considerably speeds up the cleanup process |  | ||||||
|         vs. the original code.) |  | ||||||
| """ |  | ||||||
| import weakref | import weakref | ||||||
| from django.dispatch import saferef, robustapply, errors | import warnings | ||||||
|  | try: | ||||||
|  |     set | ||||||
|  | except NameError: | ||||||
|  |     from sets import Set as set # Python 2.3 fallback | ||||||
|  |  | ||||||
| __author__ = "Patrick K. O'Brien <pobrien@orbtech.com>" | from django.dispatch import saferef | ||||||
| __cvsid__ = "$Id: dispatcher.py,v 1.9 2005/09/17 04:55:57 mcfletch Exp $" |  | ||||||
| __version__ = "$Revision: 1.9 $"[11:-2] |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class _Parameter: |  | ||||||
|     """Used to represent default parameter values.""" |  | ||||||
|     def __repr__(self): |  | ||||||
|         return self.__class__.__name__ |  | ||||||
|  |  | ||||||
| class _Any(_Parameter): |  | ||||||
|     """Singleton used to signal either "Any Sender" or "Any Signal" |  | ||||||
|  |  | ||||||
|     The Any object can be used with connect, disconnect, |  | ||||||
|     send, or sendExact to signal that the parameter given |  | ||||||
|     Any should react to all senders/signals, not just |  | ||||||
|     a particular sender/signal. |  | ||||||
|     """ |  | ||||||
| Any = _Any() |  | ||||||
|  |  | ||||||
| class _Anonymous(_Parameter): |  | ||||||
|     """Singleton used to signal "Anonymous Sender" |  | ||||||
|  |  | ||||||
|     The Anonymous object is used to signal that the sender |  | ||||||
|     of a message is not specified (as distinct from being |  | ||||||
|     "any sender").  Registering callbacks for Anonymous |  | ||||||
|     will only receive messages sent without senders.  Sending |  | ||||||
|     with anonymous will only send messages to those receivers |  | ||||||
|     registered for Any or Anonymous. |  | ||||||
|  |  | ||||||
|     Note: |  | ||||||
|         The default sender for connect is Any, while the |  | ||||||
|         default sender for send is Anonymous.  This has |  | ||||||
|         the effect that if you do not specify any senders |  | ||||||
|         in either function then all messages are routed |  | ||||||
|         as though there was a single sender (Anonymous) |  | ||||||
|         being used everywhere. |  | ||||||
|     """ |  | ||||||
| Anonymous = _Anonymous() |  | ||||||
|  |  | ||||||
| WEAKREF_TYPES = (weakref.ReferenceType, saferef.BoundMethodWeakref) | WEAKREF_TYPES = (weakref.ReferenceType, saferef.BoundMethodWeakref) | ||||||
|  |  | ||||||
| connections = {} | def _make_id(target): | ||||||
| senders = {} |     if hasattr(target, 'im_func'): | ||||||
| sendersBack = {} |         return (id(target.im_self), id(target.im_func)) | ||||||
|  |     return id(target) | ||||||
|  |  | ||||||
|  | class Signal(object): | ||||||
|  |     """Base class for all signals | ||||||
|      |      | ||||||
| def connect(receiver, signal=Any, sender=Any, weak=True): |     Internal attributes: | ||||||
|  |         receivers -- { receriverkey (id) : weakref(receiver) } | ||||||
|  |     """ | ||||||
|  |      | ||||||
|  |     def __init__(self, providing_args=None): | ||||||
|  |         """providing_args -- A list of the arguments this signal can pass along in | ||||||
|  |                        a send() call. | ||||||
|  |         """ | ||||||
|  |         self.receivers = [] | ||||||
|  |         if providing_args is None: | ||||||
|  |             providing_args = [] | ||||||
|  |         self.providing_args = set(providing_args) | ||||||
|  |  | ||||||
|  |     def connect(self, receiver, sender=None, weak=True, dispatch_uid=None): | ||||||
|         """Connect receiver to sender for signal |         """Connect receiver to sender for signal | ||||||
|      |      | ||||||
|     receiver -- a callable Python object which is to receive |         receiver -- a function or an instance method which is to | ||||||
|         messages/signals/events.  Receivers must be hashable |             receive signals.  Receivers must be | ||||||
|         objects. |             hashable objects. | ||||||
|  |  | ||||||
|             if weak is True, then receiver must be weak-referencable |             if weak is True, then receiver must be weak-referencable | ||||||
|             (more precisely saferef.safeRef() must be able to create |             (more precisely saferef.safeRef() must be able to create | ||||||
|             a reference to the receiver). |             a reference to the receiver). | ||||||
|          |          | ||||||
|         Receivers are fairly flexible in their specification, |             Receivers must be able to accept keyword arguments. | ||||||
|         as the machinery in the robustApply module takes care |  | ||||||
|         of most of the details regarding figuring out appropriate |  | ||||||
|         subsets of the sent arguments to apply to a given |  | ||||||
|         receiver. |  | ||||||
|  |  | ||||||
|         Note: |             If receivers have a dispatch_uid attribute, the receiver will | ||||||
|             if receiver is itself a weak reference (a callable), |               not be added if another receiver already exists with that | ||||||
|             it will be de-referenced by the system's machinery, |               dispatch_uid. | ||||||
|             so *generally* weak references are not suitable as |  | ||||||
|             receivers, though some use might be found for the |  | ||||||
|             facility whereby a higher-level library passes in |  | ||||||
|             pre-weakrefed receiver references. |  | ||||||
|  |  | ||||||
|     signal -- the signal to which the receiver should respond |  | ||||||
|      |  | ||||||
|         if Any, receiver will receive any signal from the |  | ||||||
|         indicated sender (which might also be Any, but is not |  | ||||||
|         necessarily Any). |  | ||||||
|          |  | ||||||
|         Otherwise must be a hashable Python object other than |  | ||||||
|         None (DispatcherError raised on None). |  | ||||||
|  |  | ||||||
|         sender -- the sender to which the receiver should respond |         sender -- the sender to which the receiver should respond | ||||||
|      |             Must either be of type Signal, or None to receive events | ||||||
|         if Any, receiver will receive the indicated signals |  | ||||||
|             from any sender. |             from any sender. | ||||||
|  |  | ||||||
|         if Anonymous, receiver will only receive indicated |  | ||||||
|         signals from send/sendExact which do not specify a |  | ||||||
|         sender, or specify Anonymous explicitly as the sender. |  | ||||||
|  |  | ||||||
|         Otherwise can be any python object. |  | ||||||
|          |  | ||||||
|         weak -- whether to use weak references to the receiver |         weak -- whether to use weak references to the receiver | ||||||
|             By default, the module will attempt to use weak |             By default, the module will attempt to use weak | ||||||
|             references to the receiver objects.  If this parameter |             references to the receiver objects.  If this parameter | ||||||
|             is false, then strong references will be used. |             is false, then strong references will be used. | ||||||
|          |          | ||||||
|     returns None, may raise DispatcherTypeError |         dispatch_uid -- an identifier used to uniquely identify a particular | ||||||
|  |             instance of a receiver. This will usually be a string, though it | ||||||
|  |             may be anything hashable. | ||||||
|  |  | ||||||
|  |         returns None | ||||||
|         """ |         """ | ||||||
|     if signal is None: |         from django.conf import settings | ||||||
|         raise errors.DispatcherTypeError( |  | ||||||
|             'Signal cannot be None (receiver=%r sender=%r)' % (receiver, sender) |  | ||||||
|         ) |  | ||||||
|     if weak: |  | ||||||
|         receiver = saferef.safeRef(receiver, onDelete=_removeReceiver) |  | ||||||
|     senderkey = id(sender) |  | ||||||
|          |          | ||||||
|     signals = connections.setdefault(senderkey, {}) |         if settings.DEBUG: | ||||||
|  |             import inspect | ||||||
|  |             assert inspect.getargspec(receiver)[2] is not None, \ | ||||||
|  |                 "Signal receivers must accept keyword arguments (**kwargs)." | ||||||
|          |          | ||||||
|     # Keep track of senders for cleanup. |         if dispatch_uid: | ||||||
|     # Is Anonymous something we want to clean up? |             lookup_key = (dispatch_uid, _make_id(sender)) | ||||||
|     if sender not in (None, Anonymous, Any): |  | ||||||
|         def remove(object, senderkey=senderkey): |  | ||||||
|             _removeSender(senderkey=senderkey) |  | ||||||
|         # Skip objects that can not be weakly referenced, which means |  | ||||||
|         # they won't be automatically cleaned up, but that's too bad. |  | ||||||
|         try: |  | ||||||
|             weakSender = weakref.ref(sender, remove) |  | ||||||
|             senders[senderkey] = weakSender |  | ||||||
|         except: |  | ||||||
|             pass |  | ||||||
|          |  | ||||||
|     receiverID = id(receiver) |  | ||||||
|     # get current set, remove any current references to |  | ||||||
|     # this receiver in the set, including back-references |  | ||||||
|     if signals.has_key(signal): |  | ||||||
|         receivers = signals[signal] |  | ||||||
|         _removeOldBackRefs(senderkey, signal, receiver, receivers) |  | ||||||
|         else: |         else: | ||||||
|         receivers = signals[signal] = [] |             lookup_key = (_make_id(receiver), _make_id(sender)) | ||||||
|     try: |  | ||||||
|         current = sendersBack.get(receiverID) |  | ||||||
|         if current is None: |  | ||||||
|             sendersBack[ receiverID ] = current = [] |  | ||||||
|         if senderkey not in current: |  | ||||||
|             current.append(senderkey) |  | ||||||
|     except: |  | ||||||
|         pass |  | ||||||
|  |  | ||||||
|     receivers.append(receiver) |         if weak: | ||||||
|  |             receiver = saferef.safeRef(receiver, onDelete=self._remove_receiver) | ||||||
|  |  | ||||||
|  |         for r_key, _ in self.receivers: | ||||||
|  |             if r_key == lookup_key: | ||||||
|  |                 break | ||||||
|  |         else: | ||||||
|  |             self.receivers.append((lookup_key, receiver)) | ||||||
|  |  | ||||||
|  |     def disconnect(self, receiver=None, sender=None, weak=True, dispatch_uid=None): | ||||||
| def disconnect(receiver, signal=Any, sender=Any, weak=True): |  | ||||||
|         """Disconnect receiver from sender for signal |         """Disconnect receiver from sender for signal | ||||||
|      |      | ||||||
|     receiver -- the registered receiver to disconnect |         receiver -- the registered receiver to disconnect. May be none if | ||||||
|     signal -- the registered signal to disconnect |             dispatch_uid is specified. | ||||||
|         sender -- the registered sender to disconnect |         sender -- the registered sender to disconnect | ||||||
|         weak -- the weakref state to disconnect |         weak -- the weakref state to disconnect | ||||||
|  |         dispatch_uid -- the unique identifier of the receiver to disconnect | ||||||
|      |      | ||||||
|     disconnect reverses the process of connect, |         disconnect reverses the process of connect. | ||||||
|     the semantics for the individual elements are |  | ||||||
|     logically equivalent to a tuple of |  | ||||||
|     (receiver, signal, sender, weak) used as a key |  | ||||||
|     to be deleted from the internal routing tables. |  | ||||||
|     (The actual process is slightly more complex |  | ||||||
|     but the semantics are basically the same). |  | ||||||
|  |  | ||||||
|     Note: |         If weak references are used, disconnect need not be called. | ||||||
|         Using disconnect is not required to cleanup |           The receiver will be remove from dispatch automatically. | ||||||
|         routing when an object is deleted, the framework |  | ||||||
|         will remove routes for deleted objects |  | ||||||
|         automatically.  It's only necessary to disconnect |  | ||||||
|         if you want to stop routing to a live object. |  | ||||||
|  |  | ||||||
|     returns None, may raise DispatcherTypeError or |         returns None | ||||||
|         DispatcherKeyError |  | ||||||
|         """ |         """ | ||||||
|     if signal is None: |  | ||||||
|         raise errors.DispatcherTypeError( |  | ||||||
|             'Signal cannot be None (receiver=%r sender=%r)' % (receiver, sender) |  | ||||||
|         ) |  | ||||||
|     if weak: receiver = saferef.safeRef(receiver) |  | ||||||
|     senderkey = id(sender) |  | ||||||
|     try: |  | ||||||
|         signals = connections[senderkey] |  | ||||||
|         receivers = signals[signal] |  | ||||||
|     except KeyError: |  | ||||||
|         raise errors.DispatcherKeyError( |  | ||||||
|             """No receivers found for signal %r from sender %r""" %( |  | ||||||
|                 signal, |  | ||||||
|                 sender |  | ||||||
|             ) |  | ||||||
|         ) |  | ||||||
|     try: |  | ||||||
|         # also removes from receivers |  | ||||||
|         _removeOldBackRefs(senderkey, signal, receiver, receivers) |  | ||||||
|     except ValueError: |  | ||||||
|         raise errors.DispatcherKeyError( |  | ||||||
|             """No connection to receiver %s for signal %s from sender %s""" %( |  | ||||||
|                 receiver, |  | ||||||
|                 signal, |  | ||||||
|                 sender |  | ||||||
|             ) |  | ||||||
|         ) |  | ||||||
|     _cleanupConnections(senderkey, signal) |  | ||||||
|  |  | ||||||
| def getReceivers(sender=Any, signal=Any): |         if dispatch_uid: | ||||||
|     """Get list of receivers from global tables |             lookup_key = (dispatch_uid, _make_id(sender)) | ||||||
|  |         else: | ||||||
|  |             lookup_key = (_make_id(receiver), _make_id(sender)) | ||||||
|  |  | ||||||
|     This utility function allows you to retrieve the |         for idx, (r_key, _) in enumerate(self.receivers): | ||||||
|     raw list of receivers from the connections table |             if r_key == lookup_key: | ||||||
|     for the given sender and signal pair. |                 del self.receivers[idx] | ||||||
|  |  | ||||||
|     Note: |     def send(self, sender, **named): | ||||||
|         there is no guarantee that this is the actual list |         """Send signal from sender to all connected receivers. | ||||||
|         stored in the connections table, so the value |  | ||||||
|         should be treated as a simple iterable/truth value |  | ||||||
|         rather than, for instance a list to which you |  | ||||||
|         might append new records. |  | ||||||
|  |  | ||||||
|     Normally you would use liveReceivers(getReceivers(...)) |         sender -- the sender of the signal | ||||||
|     to retrieve the actual receiver objects as an iterable |             Either a specific object or None. | ||||||
|     object. |      | ||||||
|  |         named -- named arguments which will be passed to receivers. | ||||||
|  |  | ||||||
|  |         Returns a list of tuple pairs [(receiver, response), ... ]. | ||||||
|  |  | ||||||
|  |         If any receiver raises an error, the error propagates back | ||||||
|  |         through send, terminating the dispatch loop, so it is quite | ||||||
|  |         possible to not have all receivers called if a raises an | ||||||
|  |         error. | ||||||
|         """ |         """ | ||||||
|     existing = connections.get(id(sender)) |  | ||||||
|     if existing is not None: |  | ||||||
|         return existing.get(signal, []) |  | ||||||
|     return [] |  | ||||||
|  |  | ||||||
| def liveReceivers(receivers): |         responses = [] | ||||||
|  |         if not self.receivers: | ||||||
|  |             return responses | ||||||
|  |  | ||||||
|  |         for receiver in self._live_receivers(_make_id(sender)): | ||||||
|  |             response = receiver(signal=self, sender=sender, **named) | ||||||
|  |             responses.append((receiver, response)) | ||||||
|  |         return responses | ||||||
|  |  | ||||||
|  |     def send_robust(self, sender, **named): | ||||||
|  |         """Send signal from sender to all connected receivers catching errors | ||||||
|  |  | ||||||
|  |         sender -- the sender of the signal | ||||||
|  |             Can be any python object (normally one registered with | ||||||
|  |             a connect if you actually want something to occur). | ||||||
|  |  | ||||||
|  |         named -- named arguments which will be passed to receivers. | ||||||
|  |             These arguments must be a subset of the argument names | ||||||
|  |             defined in providing_args. | ||||||
|  |  | ||||||
|  |         Return a list of tuple pairs [(receiver, response), ... ], | ||||||
|  |         may raise DispatcherKeyError | ||||||
|  |  | ||||||
|  |         if any receiver raises an error (specifically any subclass of Exception), | ||||||
|  |         the error instance is returned as the result for that receiver. | ||||||
|  |         """ | ||||||
|  |  | ||||||
|  |         responses = [] | ||||||
|  |         if not self.receivers: | ||||||
|  |             return responses | ||||||
|  |  | ||||||
|  |         # Call each receiver with whatever arguments it can accept. | ||||||
|  |         # Return a list of tuple pairs [(receiver, response), ... ]. | ||||||
|  |         for receiver in self._live_receivers(_make_id(sender)): | ||||||
|  |             try: | ||||||
|  |                 response = receiver(signal=self, sender=sender, **named) | ||||||
|  |             except Exception, err: | ||||||
|  |                 responses.append((receiver, err)) | ||||||
|  |             else: | ||||||
|  |                 responses.append((receiver, response)) | ||||||
|  |         return responses | ||||||
|  |  | ||||||
|  |     def _live_receivers(self, senderkey): | ||||||
|         """Filter sequence of receivers to get resolved, live receivers |         """Filter sequence of receivers to get resolved, live receivers | ||||||
|  |  | ||||||
|     This is a generator which will iterate over |         This checks for weak references | ||||||
|     the passed sequence, checking for weak references |         and resolves them, then returning only live | ||||||
|     and resolving them, then returning all live |  | ||||||
|         receivers. |         receivers. | ||||||
|         """ |         """ | ||||||
|     for receiver in receivers: |         none_senderkey = _make_id(None) | ||||||
|  |  | ||||||
|  |         for (receiverkey, r_senderkey), receiver in self.receivers: | ||||||
|  |             if r_senderkey == none_senderkey or r_senderkey == senderkey: | ||||||
|                 if isinstance(receiver, WEAKREF_TYPES): |                 if isinstance(receiver, WEAKREF_TYPES): | ||||||
|                     # Dereference the weak reference. |                     # Dereference the weak reference. | ||||||
|                     receiver = receiver() |                     receiver = receiver() | ||||||
| @@ -266,230 +186,58 @@ def liveReceivers(receivers): | |||||||
|                 else: |                 else: | ||||||
|                     yield receiver |                     yield receiver | ||||||
|  |  | ||||||
|  |     def _remove_receiver(self, receiver): | ||||||
|  |         """Remove dead receivers from connections.""" | ||||||
|  |  | ||||||
|  |         to_remove = [] | ||||||
|  |         for key, connected_receiver in self.receivers: | ||||||
|  |             if connected_receiver == receiver: | ||||||
|  |                 to_remove.append(key) | ||||||
|  |         for key in to_remove: | ||||||
|  |             for idx, (r_key, _) in enumerate(self.receivers): | ||||||
|  |                 if r_key == key: | ||||||
|  |                     del self.receivers[idx] | ||||||
|  |  | ||||||
| def getAllReceivers(sender=Any, signal=Any): | def connect(receiver, signal, sender=None, weak=True): | ||||||
|     """Get list of all receivers from global tables |  | ||||||
|  |  | ||||||
|     This gets all dereferenced receivers which should receive |  | ||||||
|     the given signal from sender, each receiver should |  | ||||||
|     be produced only once by the resulting generator |  | ||||||
|     """ |     """ | ||||||
|     receivers = {} |     For backward compatibility only. See Signal.connect() | ||||||
|     # Get receivers that receive *this* signal from *this* sender. |  | ||||||
|     # Add receivers that receive *any* signal from *this* sender. |  | ||||||
|     # Add receivers that receive *this* signal from *any* sender. |  | ||||||
|     # Add receivers that receive *any* signal from *any* sender. |  | ||||||
|     l = [] |  | ||||||
|     i = id(sender) |  | ||||||
|     if i in connections: |  | ||||||
|         sender_receivers = connections[i] |  | ||||||
|         if signal in sender_receivers: |  | ||||||
|             l.extend(sender_receivers[signal]) |  | ||||||
|         if signal is not Any and Any in sender_receivers: |  | ||||||
|             l.extend(sender_receivers[Any]) |  | ||||||
|  |  | ||||||
|     if sender is not Any: |  | ||||||
|         i = id(Any) |  | ||||||
|         if i in connections: |  | ||||||
|             sender_receivers = connections[i] |  | ||||||
|             if sender_receivers is not None: |  | ||||||
|                 if signal in sender_receivers: |  | ||||||
|                     l.extend(sender_receivers[signal]) |  | ||||||
|                 if signal is not Any and Any in sender_receivers: |  | ||||||
|                     l.extend(sender_receivers[Any]) |  | ||||||
|  |  | ||||||
|     for receiver in l: |  | ||||||
|         try: |  | ||||||
|             if not receiver in receivers: |  | ||||||
|                 if isinstance(receiver, WEAKREF_TYPES): |  | ||||||
|                     receiver = receiver() |  | ||||||
|                     # this should only (rough guess) be possible if somehow, deref'ing |  | ||||||
|                     # triggered a wipe. |  | ||||||
|                     if receiver is None: |  | ||||||
|                         continue |  | ||||||
|                 receivers[receiver] = 1 |  | ||||||
|                 yield receiver |  | ||||||
|         except TypeError: |  | ||||||
|             # dead weakrefs raise TypeError on hash... |  | ||||||
|             pass |  | ||||||
|  |  | ||||||
| def send(signal=Any, sender=Anonymous, *arguments, **named): |  | ||||||
|     """Send signal from sender to all connected receivers. |  | ||||||
|      |  | ||||||
|     signal -- (hashable) signal value, see connect for details |  | ||||||
|  |  | ||||||
|     sender -- the sender of the signal |  | ||||||
|      |  | ||||||
|         if Any, only receivers registered for Any will receive |  | ||||||
|         the message. |  | ||||||
|  |  | ||||||
|         if Anonymous, only receivers registered to receive |  | ||||||
|         messages from Anonymous or Any will receive the message |  | ||||||
|  |  | ||||||
|         Otherwise can be any python object (normally one |  | ||||||
|         registered with a connect if you actually want |  | ||||||
|         something to occur). |  | ||||||
|  |  | ||||||
|     arguments -- positional arguments which will be passed to |  | ||||||
|         *all* receivers. Note that this may raise TypeErrors |  | ||||||
|         if the receivers do not allow the particular arguments. |  | ||||||
|         Note also that arguments are applied before named |  | ||||||
|         arguments, so they should be used with care. |  | ||||||
|  |  | ||||||
|     named -- named arguments which will be filtered according |  | ||||||
|         to the parameters of the receivers to only provide those |  | ||||||
|         acceptable to the receiver. |  | ||||||
|  |  | ||||||
|     Return a list of tuple pairs [(receiver, response), ... ] |  | ||||||
|  |  | ||||||
|     if any receiver raises an error, the error propagates back |  | ||||||
|     through send, terminating the dispatch loop, so it is quite |  | ||||||
|     possible to not have all receivers called if a raises an |  | ||||||
|     error. |  | ||||||
|     """ |     """ | ||||||
|     # Call each receiver with whatever arguments it can accept. |     warnings.warn( | ||||||
|     # Return a list of tuple pairs [(receiver, response), ... ]. |         category   = DeprecationWarning, | ||||||
|     responses = [] |         message    = "dispatcher.connect() is deprecated; use Signal.connect() instead.", | ||||||
|     for receiver in getAllReceivers(sender, signal): |         stacklevel = 2 | ||||||
|         response = robustapply.robustApply( |  | ||||||
|             receiver, |  | ||||||
|             signal=signal, |  | ||||||
|             sender=sender, |  | ||||||
|             *arguments, |  | ||||||
|             **named |  | ||||||
|     ) |     ) | ||||||
|         responses.append((receiver, response)) |     return signal.connect(receiver, sender, weak) | ||||||
|     return responses |  | ||||||
|  |  | ||||||
|  | def disconnect(receiver, signal, sender=None, weak=True): | ||||||
| def sendExact(signal=Any, sender=Anonymous, *arguments, **named ): |  | ||||||
|     """Send signal only to those receivers registered for exact message |  | ||||||
|  |  | ||||||
|     sendExact allows for avoiding Any/Anonymous registered |  | ||||||
|     handlers, sending only to those receivers explicitly |  | ||||||
|     registered for a particular signal on a particular |  | ||||||
|     sender. |  | ||||||
|     """ |     """ | ||||||
|     responses = [] |     For backward compatibility only. See Signal.disconnect() | ||||||
|     for receiver in liveReceivers(getReceivers(sender, signal)): |     """ | ||||||
|         response = robustapply.robustApply( |     warnings.warn( | ||||||
|             receiver, |         category   = DeprecationWarning, | ||||||
|             signal=signal, |         message    = "dispatcher.disconnect() is deprecated; use Signal.disconnect() instead.", | ||||||
|             sender=sender, |         stacklevel = 2 | ||||||
|             *arguments, |  | ||||||
|             **named |  | ||||||
|     ) |     ) | ||||||
|         responses.append((receiver, response)) |     signal.disconnect(receiver, sender, weak) | ||||||
|     return responses |  | ||||||
|  |  | ||||||
|  | def send(signal, sender=None, **named): | ||||||
| def _removeReceiver(receiver): |  | ||||||
|     """Remove receiver from connections.""" |  | ||||||
|     if not sendersBack: |  | ||||||
|         # During module cleanup the mapping will be replaced with None |  | ||||||
|         return False |  | ||||||
|     backKey = id(receiver) |  | ||||||
|     for senderkey in sendersBack.get(backKey,()): |  | ||||||
|         try: |  | ||||||
|             signals = connections[senderkey].keys() |  | ||||||
|         except KeyError,err: |  | ||||||
|             pass |  | ||||||
|         else: |  | ||||||
|             for signal in signals: |  | ||||||
|                 try: |  | ||||||
|                     receivers = connections[senderkey][signal] |  | ||||||
|                 except KeyError: |  | ||||||
|                     pass |  | ||||||
|                 else: |  | ||||||
|                     try: |  | ||||||
|                         receivers.remove(receiver) |  | ||||||
|                     except Exception, err: |  | ||||||
|                         pass |  | ||||||
|                 _cleanupConnections(senderkey, signal) |  | ||||||
|     try: |  | ||||||
|         del sendersBack[ backKey ] |  | ||||||
|     except KeyError: |  | ||||||
|         pass |  | ||||||
|              |  | ||||||
| def _cleanupConnections(senderkey, signal): |  | ||||||
|     """Delete any empty signals for senderkey. Delete senderkey if empty.""" |  | ||||||
|     try: |  | ||||||
|         receivers = connections[senderkey][signal] |  | ||||||
|     except: |  | ||||||
|         pass |  | ||||||
|     else: |  | ||||||
|         if not receivers: |  | ||||||
|             # No more connected receivers. Therefore, remove the signal. |  | ||||||
|             try: |  | ||||||
|                 signals = connections[senderkey] |  | ||||||
|             except KeyError: |  | ||||||
|                 pass |  | ||||||
|             else: |  | ||||||
|                 del signals[signal] |  | ||||||
|                 if not signals: |  | ||||||
|                     # No more signal connections. Therefore, remove the sender. |  | ||||||
|                     _removeSender(senderkey) |  | ||||||
|  |  | ||||||
| def _removeSender(senderkey): |  | ||||||
|     """Remove senderkey from connections.""" |  | ||||||
|     _removeBackrefs(senderkey) |  | ||||||
|  |  | ||||||
|     connections.pop(senderkey, None) |  | ||||||
|     senders.pop(senderkey, None) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def _removeBackrefs(senderkey): |  | ||||||
|     """Remove all back-references to this senderkey""" |  | ||||||
|     for receiver_list in connections.pop(senderkey, {}).values(): |  | ||||||
|         for receiver in receiver_list: |  | ||||||
|             _killBackref(receiver, senderkey) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def _removeOldBackRefs(senderkey, signal, receiver, receivers): |  | ||||||
|     """Kill old sendersBack references from receiver |  | ||||||
|  |  | ||||||
|     This guards against multiple registration of the same |  | ||||||
|     receiver for a given signal and sender leaking memory |  | ||||||
|     as old back reference records build up. |  | ||||||
|  |  | ||||||
|     Also removes old receiver instance from receivers |  | ||||||
|     """ |     """ | ||||||
|     try: |     For backward compatibility only. See Signal.send() | ||||||
|         index = receivers.index(receiver) |     """ | ||||||
|         # need to scan back references here and remove senderkey |     warnings.warn( | ||||||
|     except ValueError: |         category   = DeprecationWarning, | ||||||
|         return False |         message    = "dispatcher.send() is deprecated; use Signal.send() instead.", | ||||||
|     else: |         stacklevel = 2 | ||||||
|         oldReceiver = receivers[index] |     ) | ||||||
|         del receivers[index] |     return signal.send(sender=sender, **named) | ||||||
|         found = 0 |  | ||||||
|         signals = connections.get(signal) |  | ||||||
|         if signals is not None: |  | ||||||
|             for sig,recs in connections.get(signal,{}).iteritems(): |  | ||||||
|                 if sig != signal: |  | ||||||
|                     for rec in recs: |  | ||||||
|                         if rec is oldReceiver: |  | ||||||
|                             found = 1 |  | ||||||
|                             break |  | ||||||
|         if not found: |  | ||||||
|             _killBackref(oldReceiver, senderkey) |  | ||||||
|             return True |  | ||||||
|         return False |  | ||||||
|  |  | ||||||
|          | def sendExact(signal, sender, **named ): | ||||||
| def _killBackref(receiver, senderkey): |     """ | ||||||
|     """Do the actual removal of back reference from receiver to senderkey""" |     This function is deprecated, as it now has the same meaning as send. | ||||||
|     receiverkey = id(receiver) |     """ | ||||||
|     receivers_list = sendersBack.get(receiverkey, ()) |     warnings.warn( | ||||||
|     while senderkey in receivers_list: |         category   = DeprecationWarning, | ||||||
|         try: |         message    = "dispatcher.sendExact() is deprecated; use Signal.send() instead.", | ||||||
|             receivers_list.remove(senderkey) |         stacklevel = 2 | ||||||
|         except: |     ) | ||||||
|             break |     return signal.send(sender=sender, **named) | ||||||
|     if not receivers_list: |  | ||||||
|         try: |  | ||||||
|             del sendersBack[ receiverkey ] |  | ||||||
|         except KeyError: |  | ||||||
|             pass |  | ||||||
|     return True |  | ||||||
|   | |||||||
| @@ -1,10 +0,0 @@ | |||||||
| """Error types for dispatcher mechanism |  | ||||||
| """ |  | ||||||
|  |  | ||||||
| class DispatcherError(Exception): |  | ||||||
|     """Base class for all Dispatcher errors""" |  | ||||||
| class DispatcherKeyError(KeyError, DispatcherError): |  | ||||||
|     """Error raised when unknown (sender,signal) set specified""" |  | ||||||
| class DispatcherTypeError(TypeError, DispatcherError): |  | ||||||
|     """Error raised when inappropriate signal-type specified (None)""" |  | ||||||
|  |  | ||||||
| @@ -1,4 +1,6 @@ | |||||||
| PyDispatcher License | django.dispatch was originally forked from PyDispatcher. | ||||||
|  |  | ||||||
|  | PyDispatcher License: | ||||||
|  |  | ||||||
|     Copyright (c) 2001-2003, Patrick K. O'Brien and Contributors |     Copyright (c) 2001-2003, Patrick K. O'Brien and Contributors | ||||||
|     All rights reserved. |     All rights reserved. | ||||||
|   | |||||||
| @@ -1,57 +0,0 @@ | |||||||
| """Module implementing error-catching version of send (sendRobust)""" |  | ||||||
| from django.dispatch.dispatcher import Any, Anonymous, liveReceivers, getAllReceivers |  | ||||||
| from django.dispatch.robustapply import robustApply |  | ||||||
|  |  | ||||||
| def sendRobust( |  | ||||||
|     signal=Any,  |  | ||||||
|     sender=Anonymous,  |  | ||||||
|     *arguments, **named |  | ||||||
| ): |  | ||||||
|     """Send signal from sender to all connected receivers catching errors |  | ||||||
|      |  | ||||||
|     signal -- (hashable) signal value, see connect for details |  | ||||||
|  |  | ||||||
|     sender -- the sender of the signal |  | ||||||
|      |  | ||||||
|         if Any, only receivers registered for Any will receive |  | ||||||
|         the message. |  | ||||||
|  |  | ||||||
|         if Anonymous, only receivers registered to receive |  | ||||||
|         messages from Anonymous or Any will receive the message |  | ||||||
|  |  | ||||||
|         Otherwise can be any python object (normally one |  | ||||||
|         registered with a connect if you actually want |  | ||||||
|         something to occur). |  | ||||||
|  |  | ||||||
|     arguments -- positional arguments which will be passed to |  | ||||||
|         *all* receivers. Note that this may raise TypeErrors |  | ||||||
|         if the receivers do not allow the particular arguments. |  | ||||||
|         Note also that arguments are applied before named |  | ||||||
|         arguments, so they should be used with care. |  | ||||||
|  |  | ||||||
|     named -- named arguments which will be filtered according |  | ||||||
|         to the parameters of the receivers to only provide those |  | ||||||
|         acceptable to the receiver. |  | ||||||
|  |  | ||||||
|     Return a list of tuple pairs [(receiver, response), ... ] |  | ||||||
|  |  | ||||||
|     if any receiver raises an error (specifically any subclass of Exception), |  | ||||||
|     the error instance is returned as the result for that receiver. |  | ||||||
|     """ |  | ||||||
|     # Call each receiver with whatever arguments it can accept. |  | ||||||
|     # Return a list of tuple pairs [(receiver, response), ... ]. |  | ||||||
|     responses = [] |  | ||||||
|     for receiver in liveReceivers(getAllReceivers(sender, signal)): |  | ||||||
|         try: |  | ||||||
|             response = robustApply( |  | ||||||
|                 receiver, |  | ||||||
|                 signal=signal, |  | ||||||
|                 sender=sender, |  | ||||||
|                 *arguments, |  | ||||||
|                 **named |  | ||||||
|             ) |  | ||||||
|         except Exception, err: |  | ||||||
|             responses.append((receiver, err)) |  | ||||||
|         else: |  | ||||||
|             responses.append((receiver, response)) |  | ||||||
|     return responses |  | ||||||
| @@ -1,47 +0,0 @@ | |||||||
| """Robust apply mechanism |  | ||||||
|  |  | ||||||
| Provides a function "call", which can sort out |  | ||||||
| what arguments a given callable object can take, |  | ||||||
| and subset the given arguments to match only |  | ||||||
| those which are acceptable. |  | ||||||
| """ |  | ||||||
|  |  | ||||||
| def function( receiver ): |  | ||||||
|     """Get function-like callable object for given receiver |  | ||||||
|  |  | ||||||
|     returns (function_or_method, codeObject, fromMethod) |  | ||||||
|  |  | ||||||
|     If fromMethod is true, then the callable already |  | ||||||
|     has its first argument bound |  | ||||||
|     """ |  | ||||||
|     if hasattr(receiver, '__call__'): |  | ||||||
|         # receiver is a class instance; assume it is callable. |  | ||||||
|         # Reassign receiver to the actual method that will be called. |  | ||||||
|         if hasattr( receiver.__call__, 'im_func') or hasattr( receiver.__call__, 'im_code'): |  | ||||||
|             receiver = receiver.__call__ |  | ||||||
|     if hasattr( receiver, 'im_func' ): |  | ||||||
|         # an instance-method... |  | ||||||
|         return receiver, receiver.im_func.func_code, 1 |  | ||||||
|     elif not hasattr( receiver, 'func_code'): |  | ||||||
|         raise ValueError('unknown reciever type %s %s'%(receiver, type(receiver))) |  | ||||||
|     return receiver, receiver.func_code, 0 |  | ||||||
|  |  | ||||||
| def robustApply(receiver, *arguments, **named): |  | ||||||
|     """Call receiver with arguments and an appropriate subset of named |  | ||||||
|     """ |  | ||||||
|     receiver, codeObject, startIndex = function( receiver ) |  | ||||||
|     acceptable = codeObject.co_varnames[startIndex+len(arguments):codeObject.co_argcount] |  | ||||||
|     for name in codeObject.co_varnames[startIndex:startIndex+len(arguments)]: |  | ||||||
|         if named.has_key( name ): |  | ||||||
|             raise TypeError( |  | ||||||
|                 """Argument %r specified both positionally and as a keyword for calling %r"""% ( |  | ||||||
|                     name, receiver, |  | ||||||
|                 ) |  | ||||||
|             ) |  | ||||||
|     if not (codeObject.co_flags & 8): |  | ||||||
|         # fc does not have a **kwds type parameter, therefore  |  | ||||||
|         # remove unacceptable arguments. |  | ||||||
|         for arg in named.keys(): |  | ||||||
|             if arg not in acceptable: |  | ||||||
|                 del named[arg] |  | ||||||
|     return receiver(*arguments, **named) |  | ||||||
| @@ -1,4 +1,10 @@ | |||||||
| """Refactored "safe reference" from dispatcher.py""" | """ | ||||||
|  | "Safe weakrefs", originally from pyDispatcher. | ||||||
|  |  | ||||||
|  | Provides a way to safely weakref any function, including bound methods (which | ||||||
|  | aren't handled by the core weakref module). | ||||||
|  | """ | ||||||
|  |  | ||||||
| import weakref, traceback | import weakref, traceback | ||||||
|  |  | ||||||
| def safeRef(target, onDelete = None): | def safeRef(target, onDelete = None): | ||||||
| @@ -60,7 +66,9 @@ class BoundMethodWeakref(object): | |||||||
|             same BoundMethodWeakref instance. |             same BoundMethodWeakref instance. | ||||||
|  |  | ||||||
|     """ |     """ | ||||||
|  |      | ||||||
|     _allInstances = weakref.WeakValueDictionary() |     _allInstances = weakref.WeakValueDictionary() | ||||||
|  |      | ||||||
|     def __new__( cls, target, onDelete=None, *arguments,**named ): |     def __new__( cls, target, onDelete=None, *arguments,**named ): | ||||||
|         """Create new instance or return current instance |         """Create new instance or return current instance | ||||||
|  |  | ||||||
| @@ -83,6 +91,7 @@ class BoundMethodWeakref(object): | |||||||
|             cls._allInstances[key] = base |             cls._allInstances[key] = base | ||||||
|             base.__init__( target, onDelete, *arguments,**named) |             base.__init__( target, onDelete, *arguments,**named) | ||||||
|             return base |             return base | ||||||
|  |      | ||||||
|     def __init__(self, target, onDelete=None): |     def __init__(self, target, onDelete=None): | ||||||
|         """Return a weak-reference-like instance for a bound method |         """Return a weak-reference-like instance for a bound method | ||||||
|  |  | ||||||
| @@ -122,6 +131,7 @@ class BoundMethodWeakref(object): | |||||||
|         self.weakFunc = weakref.ref(target.im_func, remove) |         self.weakFunc = weakref.ref(target.im_func, remove) | ||||||
|         self.selfName = str(target.im_self) |         self.selfName = str(target.im_self) | ||||||
|         self.funcName = str(target.im_func.__name__) |         self.funcName = str(target.im_func.__name__) | ||||||
|  |      | ||||||
|     def calculateKey( cls, target ): |     def calculateKey( cls, target ): | ||||||
|         """Calculate the reference key for this reference |         """Calculate the reference key for this reference | ||||||
|  |  | ||||||
| @@ -130,6 +140,7 @@ class BoundMethodWeakref(object): | |||||||
|         """ |         """ | ||||||
|         return (id(target.im_self),id(target.im_func)) |         return (id(target.im_self),id(target.im_func)) | ||||||
|     calculateKey = classmethod( calculateKey ) |     calculateKey = classmethod( calculateKey ) | ||||||
|  |      | ||||||
|     def __str__(self): |     def __str__(self): | ||||||
|         """Give a friendly representation of the object""" |         """Give a friendly representation of the object""" | ||||||
|         return """%s( %s.%s )"""%( |         return """%s( %s.%s )"""%( | ||||||
| @@ -137,15 +148,19 @@ class BoundMethodWeakref(object): | |||||||
|             self.selfName, |             self.selfName, | ||||||
|             self.funcName, |             self.funcName, | ||||||
|         ) |         ) | ||||||
|  |      | ||||||
|     __repr__ = __str__ |     __repr__ = __str__ | ||||||
|  |      | ||||||
|     def __nonzero__( self ): |     def __nonzero__( self ): | ||||||
|         """Whether we are still a valid reference""" |         """Whether we are still a valid reference""" | ||||||
|         return self() is not None |         return self() is not None | ||||||
|  |      | ||||||
|     def __cmp__( self, other ): |     def __cmp__( self, other ): | ||||||
|         """Compare with another reference""" |         """Compare with another reference""" | ||||||
|         if not isinstance (other,self.__class__): |         if not isinstance (other,self.__class__): | ||||||
|             return cmp( self.__class__, type(other) ) |             return cmp( self.__class__, type(other) ) | ||||||
|         return cmp( self.key, other.key) |         return cmp( self.key, other.key) | ||||||
|  |      | ||||||
|     def __call__(self): |     def __call__(self): | ||||||
|         """Return a strong reference to the bound method |         """Return a strong reference to the bound method | ||||||
|  |  | ||||||
| @@ -224,7 +239,6 @@ class BoundNonDescriptorMethodWeakref(BoundMethodWeakref): | |||||||
|                 return getattr(target, function.__name__) |                 return getattr(target, function.__name__) | ||||||
|         return None |         return None | ||||||
|  |  | ||||||
|  |  | ||||||
| def get_bound_method_weakref(target, onDelete): | def get_bound_method_weakref(target, onDelete): | ||||||
|     """Instantiates the appropiate BoundMethodWeakRef, depending on the details of |     """Instantiates the appropiate BoundMethodWeakRef, depending on the details of | ||||||
|     the underlying class method implementation""" |     the underlying class method implementation""" | ||||||
| @@ -234,4 +248,3 @@ def get_bound_method_weakref(target, onDelete): | |||||||
|     else: |     else: | ||||||
|         # no luck, use the alternative implementation: |         # no luck, use the alternative implementation: | ||||||
|         return BoundNonDescriptorMethodWeakref(target=target, onDelete=onDelete) |         return BoundNonDescriptorMethodWeakref(target=target, onDelete=onDelete) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -11,7 +11,6 @@ from django.contrib.auth import authenticate, login | |||||||
| from django.core.handlers.base import BaseHandler | from django.core.handlers.base import BaseHandler | ||||||
| from django.core.handlers.wsgi import WSGIRequest | from django.core.handlers.wsgi import WSGIRequest | ||||||
| from django.core.signals import got_request_exception | from django.core.signals import got_request_exception | ||||||
| from django.dispatch import dispatcher |  | ||||||
| from django.http import SimpleCookie, HttpRequest | from django.http import SimpleCookie, HttpRequest | ||||||
| from django.template import TemplateDoesNotExist | from django.template import TemplateDoesNotExist | ||||||
| from django.test import signals | from django.test import signals | ||||||
| @@ -59,7 +58,7 @@ class ClientHandler(BaseHandler): | |||||||
|         if self._request_middleware is None: |         if self._request_middleware is None: | ||||||
|             self.load_middleware() |             self.load_middleware() | ||||||
|  |  | ||||||
|         dispatcher.send(signal=signals.request_started) |         signals.request_started.send(sender=self.__class__) | ||||||
|         try: |         try: | ||||||
|             request = WSGIRequest(environ) |             request = WSGIRequest(environ) | ||||||
|             response = self.get_response(request) |             response = self.get_response(request) | ||||||
| @@ -69,11 +68,11 @@ class ClientHandler(BaseHandler): | |||||||
|                 response = middleware_method(request, response) |                 response = middleware_method(request, response) | ||||||
|             response = self.apply_response_fixes(request, response) |             response = self.apply_response_fixes(request, response) | ||||||
|         finally: |         finally: | ||||||
|             dispatcher.send(signal=signals.request_finished) |             signals.request_finished.send(sender=self.__class__) | ||||||
|  |  | ||||||
|         return response |         return response | ||||||
|  |  | ||||||
| def store_rendered_templates(store, signal, sender, template, context): | def store_rendered_templates(store, signal, sender, template, context, **kwargs): | ||||||
|     """ |     """ | ||||||
|     Stores templates and contexts that are rendered. |     Stores templates and contexts that are rendered. | ||||||
|     """ |     """ | ||||||
| @@ -160,7 +159,7 @@ class Client: | |||||||
|         self.cookies = SimpleCookie() |         self.cookies = SimpleCookie() | ||||||
|         self.exc_info = None |         self.exc_info = None | ||||||
|  |  | ||||||
|     def store_exc_info(self, *args, **kwargs): |     def store_exc_info(self, **kwargs): | ||||||
|         """ |         """ | ||||||
|         Stores exceptions when they are generated by a view. |         Stores exceptions when they are generated by a view. | ||||||
|         """ |         """ | ||||||
| @@ -202,10 +201,10 @@ class Client: | |||||||
|         # callback function. |         # callback function. | ||||||
|         data = {} |         data = {} | ||||||
|         on_template_render = curry(store_rendered_templates, data) |         on_template_render = curry(store_rendered_templates, data) | ||||||
|         dispatcher.connect(on_template_render, signal=signals.template_rendered) |         signals.template_rendered.connect(on_template_render) | ||||||
|  |  | ||||||
|         # Capture exceptions created by the handler. |         # Capture exceptions created by the handler. | ||||||
|         dispatcher.connect(self.store_exc_info, signal=got_request_exception) |         got_request_exception.connect(self.store_exc_info) | ||||||
|  |  | ||||||
|         try: |         try: | ||||||
|             response = self.handler(environ) |             response = self.handler(environ) | ||||||
|   | |||||||
| @@ -1 +1,3 @@ | |||||||
| template_rendered = object() | from django.dispatch import Signal | ||||||
|  |  | ||||||
|  | template_rendered = Signal(providing_args=["template", "context"]) | ||||||
|   | |||||||
| @@ -3,7 +3,6 @@ from django.conf import settings | |||||||
| from django.db import connection, get_creation_module | from django.db import connection, get_creation_module | ||||||
| from django.core import mail | from django.core import mail | ||||||
| from django.core.management import call_command | from django.core.management import call_command | ||||||
| from django.dispatch import dispatcher |  | ||||||
| from django.test import signals | from django.test import signals | ||||||
| from django.template import Template | from django.template import Template | ||||||
| from django.utils.translation import deactivate | from django.utils.translation import deactivate | ||||||
| @@ -17,7 +16,7 @@ def instrumented_test_render(self, context): | |||||||
|     An instrumented Template render method, providing a signal |     An instrumented Template render method, providing a signal | ||||||
|     that can be intercepted by the test system Client |     that can be intercepted by the test system Client | ||||||
|     """ |     """ | ||||||
|     dispatcher.send(signal=signals.template_rendered, sender=self, template=self, context=context) |     signals.template_rendered.send(sender=self, template=self, context=context) | ||||||
|     return self.nodelist.render(context) |     return self.nodelist.render(context) | ||||||
|  |  | ||||||
| class TestSMTPConnection(object): | class TestSMTPConnection(object): | ||||||
|   | |||||||
| @@ -3,7 +3,6 @@ Testing signals before/after saving and deleting. | |||||||
| """ | """ | ||||||
|  |  | ||||||
| from django.db import models | from django.db import models | ||||||
| from django.dispatch import dispatcher |  | ||||||
|  |  | ||||||
| class Person(models.Model): | class Person(models.Model): | ||||||
|     first_name = models.CharField(max_length=20) |     first_name = models.CharField(max_length=20) | ||||||
| @@ -12,19 +11,12 @@ class Person(models.Model): | |||||||
|     def __unicode__(self): |     def __unicode__(self): | ||||||
|         return u"%s %s" % (self.first_name, self.last_name) |         return u"%s %s" % (self.first_name, self.last_name) | ||||||
|  |  | ||||||
|  | def pre_save_test(signal, sender, instance, **kwargs): | ||||||
| def pre_save_nokwargs_test(sender, instance): |  | ||||||
|     print 'pre_save_nokwargs signal' |  | ||||||
|  |  | ||||||
| def post_save_nokwargs_test(sender, instance): |  | ||||||
|     print 'post_save_nokwargs signal' |  | ||||||
|  |  | ||||||
| def pre_save_test(sender, instance, **kwargs): |  | ||||||
|     print 'pre_save signal,', instance |     print 'pre_save signal,', instance | ||||||
|     if kwargs.get('raw'): |     if kwargs.get('raw'): | ||||||
|         print 'Is raw' |         print 'Is raw' | ||||||
|  |  | ||||||
| def post_save_test(sender, instance, **kwargs): | def post_save_test(signal, sender, instance, **kwargs): | ||||||
|     print 'post_save signal,', instance |     print 'post_save signal,', instance | ||||||
|     if 'created' in kwargs: |     if 'created' in kwargs: | ||||||
|         if kwargs['created']: |         if kwargs['created']: | ||||||
| @@ -34,44 +26,36 @@ def post_save_test(sender, instance, **kwargs): | |||||||
|     if kwargs.get('raw'): |     if kwargs.get('raw'): | ||||||
|         print 'Is raw' |         print 'Is raw' | ||||||
|  |  | ||||||
| def pre_delete_test(sender, instance, **kwargs): | def pre_delete_test(signal, sender, instance, **kwargs): | ||||||
|     print 'pre_delete signal,', instance |     print 'pre_delete signal,', instance | ||||||
|     print 'instance.id is not None: %s' % (instance.id != None) |     print 'instance.id is not None: %s' % (instance.id != None) | ||||||
|  |  | ||||||
| def post_delete_test(sender, instance, **kwargs): | def post_delete_test(signal, sender, instance, **kwargs): | ||||||
|     print 'post_delete signal,', instance |     print 'post_delete signal,', instance | ||||||
|     print 'instance.id is None: %s' % (instance.id == None) |     print 'instance.id is None: %s' % (instance.id == None) | ||||||
|  |  | ||||||
| __test__ = {'API_TESTS':""" | __test__ = {'API_TESTS':""" | ||||||
| >>> dispatcher.connect(pre_save_nokwargs_test, signal=models.signals.pre_save) | >>> models.signals.pre_save.connect(pre_save_test) | ||||||
| >>> dispatcher.connect(post_save_nokwargs_test, signal=models.signals.post_save) | >>> models.signals.post_save.connect(post_save_test) | ||||||
| >>> dispatcher.connect(pre_save_test, signal=models.signals.pre_save) | >>> models.signals.pre_delete.connect(pre_delete_test) | ||||||
| >>> dispatcher.connect(post_save_test, signal=models.signals.post_save) | >>> models.signals.post_delete.connect(post_delete_test) | ||||||
| >>> dispatcher.connect(pre_delete_test, signal=models.signals.pre_delete) |  | ||||||
| >>> dispatcher.connect(post_delete_test, signal=models.signals.post_delete) |  | ||||||
|  |  | ||||||
| >>> p1 = Person(first_name='John', last_name='Smith') | >>> p1 = Person(first_name='John', last_name='Smith') | ||||||
| >>> p1.save() | >>> p1.save() | ||||||
| pre_save_nokwargs signal |  | ||||||
| pre_save signal, John Smith | pre_save signal, John Smith | ||||||
| post_save_nokwargs signal |  | ||||||
| post_save signal, John Smith | post_save signal, John Smith | ||||||
| Is created | Is created | ||||||
|  |  | ||||||
| >>> p1.first_name = 'Tom' | >>> p1.first_name = 'Tom' | ||||||
| >>> p1.save() | >>> p1.save() | ||||||
| pre_save_nokwargs signal |  | ||||||
| pre_save signal, Tom Smith | pre_save signal, Tom Smith | ||||||
| post_save_nokwargs signal |  | ||||||
| post_save signal, Tom Smith | post_save signal, Tom Smith | ||||||
| Is updated | Is updated | ||||||
|  |  | ||||||
| # Calling an internal method purely so that we can trigger a "raw" save. | # Calling an internal method purely so that we can trigger a "raw" save. | ||||||
| >>> p1.save_base(raw=True) | >>> p1.save_base(raw=True) | ||||||
| pre_save_nokwargs signal |  | ||||||
| pre_save signal, Tom Smith | pre_save signal, Tom Smith | ||||||
| Is raw | Is raw | ||||||
| post_save_nokwargs signal |  | ||||||
| post_save signal, Tom Smith | post_save signal, Tom Smith | ||||||
| Is updated | Is updated | ||||||
| Is raw | Is raw | ||||||
| @@ -85,17 +69,13 @@ instance.id is None: False | |||||||
| >>> p2 = Person(first_name='James', last_name='Jones') | >>> p2 = Person(first_name='James', last_name='Jones') | ||||||
| >>> p2.id = 99999 | >>> p2.id = 99999 | ||||||
| >>> p2.save() | >>> p2.save() | ||||||
| pre_save_nokwargs signal |  | ||||||
| pre_save signal, James Jones | pre_save signal, James Jones | ||||||
| post_save_nokwargs signal |  | ||||||
| post_save signal, James Jones | post_save signal, James Jones | ||||||
| Is created | Is created | ||||||
|  |  | ||||||
| >>> p2.id = 99998 | >>> p2.id = 99998 | ||||||
| >>> p2.save() | >>> p2.save() | ||||||
| pre_save_nokwargs signal |  | ||||||
| pre_save signal, James Jones | pre_save signal, James Jones | ||||||
| post_save_nokwargs signal |  | ||||||
| post_save signal, James Jones | post_save signal, James Jones | ||||||
| Is created | Is created | ||||||
|  |  | ||||||
| @@ -108,10 +88,8 @@ instance.id is None: False | |||||||
| >>> Person.objects.all() | >>> Person.objects.all() | ||||||
| [<Person: James Jones>] | [<Person: James Jones>] | ||||||
|  |  | ||||||
| >>> dispatcher.disconnect(pre_save_nokwargs_test, signal=models.signals.pre_save) | >>> models.signals.post_delete.disconnect(post_delete_test) | ||||||
| >>> dispatcher.disconnect(post_save_nokwargs_test, signal=models.signals.post_save) | >>> models.signals.pre_delete.disconnect(pre_delete_test) | ||||||
| >>> dispatcher.disconnect(post_delete_test, signal=models.signals.post_delete) | >>> models.signals.post_save.disconnect(post_save_test) | ||||||
| >>> dispatcher.disconnect(pre_delete_test, signal=models.signals.pre_delete) | >>> models.signals.pre_save.disconnect(pre_save_test) | ||||||
| >>> dispatcher.disconnect(post_save_test, signal=models.signals.post_save) |  | ||||||
| >>> dispatcher.disconnect(pre_save_test, signal=models.signals.pre_save) |  | ||||||
| """} | """} | ||||||
|   | |||||||
| @@ -2,6 +2,5 @@ | |||||||
| Unit-tests for the dispatch project | Unit-tests for the dispatch project | ||||||
| """ | """ | ||||||
|  |  | ||||||
| from test_dispatcher import * |  | ||||||
| from test_robustapply import * |  | ||||||
| from test_saferef import * | from test_saferef import * | ||||||
|  | from test_dispatcher import * | ||||||
|   | |||||||
| @@ -1,5 +1,4 @@ | |||||||
| from django.dispatch.dispatcher import * | from django.dispatch import Signal | ||||||
| from django.dispatch import dispatcher, robust |  | ||||||
| import unittest | import unittest | ||||||
| import copy | import copy | ||||||
| import sys | import sys | ||||||
| @@ -15,143 +14,94 @@ else: | |||||||
|     def garbage_collect(): |     def garbage_collect(): | ||||||
|         gc.collect() |         gc.collect() | ||||||
|  |  | ||||||
| def x(a): | def receiver_1_arg(val, **kwargs): | ||||||
|     return a |     return val | ||||||
|  |  | ||||||
| class Dummy(object): |  | ||||||
|     pass |  | ||||||
|  |  | ||||||
| class Callable(object): | class Callable(object): | ||||||
|     def __call__(self, a): |     def __call__(self, val, **kwargs): | ||||||
|         return a |         return val | ||||||
|      |      | ||||||
|     def a(self, a): |     def a(self, val, **kwargs): | ||||||
|         return a |         return val | ||||||
|  |  | ||||||
|  | a_signal = Signal(providing_args=["val"]) | ||||||
|  |  | ||||||
| class DispatcherTests(unittest.TestCase): | class DispatcherTests(unittest.TestCase): | ||||||
|     """Test suite for dispatcher (barely started)""" |     """Test suite for dispatcher (barely started)""" | ||||||
|  |  | ||||||
|     def setUp(self): |     def _testIsClean(self, signal): | ||||||
|         # track the initial state, since it's possible that others have bleed receivers in |  | ||||||
|         garbage_collect() |  | ||||||
|         self.sendersBack = copy.copy(dispatcher.sendersBack) |  | ||||||
|         self.connections = copy.copy(dispatcher.connections) |  | ||||||
|         self.senders = copy.copy(dispatcher.senders) |  | ||||||
|      |  | ||||||
|     def _testIsClean(self): |  | ||||||
|         """Assert that everything has been cleaned up automatically""" |         """Assert that everything has been cleaned up automatically""" | ||||||
|         self.assertEqual(dispatcher.sendersBack, self.sendersBack) |         self.assertEqual(signal.receivers, []) | ||||||
|         self.assertEqual(dispatcher.connections, self.connections) |  | ||||||
|         self.assertEqual(dispatcher.senders, self.senders) |         # force cleanup just in case | ||||||
|  |         signal.receivers = [] | ||||||
|      |      | ||||||
|     def testExact(self): |     def testExact(self): | ||||||
|         a = Dummy() |         a_signal.connect(receiver_1_arg, sender=self) | ||||||
|         signal = 'this' |         expected = [(receiver_1_arg,"test")] | ||||||
|         connect(x, signal, a) |         result = a_signal.send(sender=self, val="test") | ||||||
|         expected = [(x,a)] |  | ||||||
|         result = send('this',a, a=a) |  | ||||||
|         self.assertEqual(result, expected) |         self.assertEqual(result, expected) | ||||||
|         disconnect(x, signal, a) |         a_signal.disconnect(receiver_1_arg, sender=self) | ||||||
|         self.assertEqual(list(getAllReceivers(a,signal)), []) |         self._testIsClean(a_signal) | ||||||
|         self._testIsClean() |  | ||||||
|  |  | ||||||
|     def testAnonymousSend(self): |     def testIgnoredSender(self): | ||||||
|         a = Dummy() |         a_signal.connect(receiver_1_arg) | ||||||
|         signal = 'this' |         expected = [(receiver_1_arg,"test")] | ||||||
|         connect(x, signal) |         result = a_signal.send(sender=self, val="test") | ||||||
|         expected = [(x,a)] |  | ||||||
|         result = send(signal,None, a=a) |  | ||||||
|         self.assertEqual(result, expected) |         self.assertEqual(result, expected) | ||||||
|         disconnect(x, signal) |         a_signal.disconnect(receiver_1_arg) | ||||||
|         self.assertEqual(list(getAllReceivers(None,signal)), []) |         self._testIsClean(a_signal) | ||||||
|         self._testIsClean() |  | ||||||
|      |  | ||||||
|     def testAnyRegistration(self): |  | ||||||
|         a = Dummy() |  | ||||||
|         signal = 'this' |  | ||||||
|         connect(x, signal, Any) |  | ||||||
|         expected = [(x,a)] |  | ||||||
|         result = send('this',object(), a=a) |  | ||||||
|         self.assertEqual(result, expected) |  | ||||||
|         disconnect(x, signal, Any) |  | ||||||
|         expected = [] |  | ||||||
|         result = send('this',object(), a=a) |  | ||||||
|         self.assertEqual(result, expected) |  | ||||||
|         self.assertEqual(list(getAllReceivers(Any,signal)), []) |  | ||||||
|          |  | ||||||
|         self._testIsClean() |  | ||||||
|      |  | ||||||
|     def testAnyRegistration2(self): |  | ||||||
|         a = Dummy() |  | ||||||
|         signal = 'this' |  | ||||||
|         connect(x, Any, a) |  | ||||||
|         expected = [(x,a)] |  | ||||||
|         result = send('this',a, a=a) |  | ||||||
|         self.assertEqual(result, expected) |  | ||||||
|         disconnect(x, Any, a) |  | ||||||
|         self.assertEqual(list(getAllReceivers(a,Any)), []) |  | ||||||
|         self._testIsClean() |  | ||||||
|      |      | ||||||
|     def testGarbageCollected(self): |     def testGarbageCollected(self): | ||||||
|         a = Callable() |         a = Callable() | ||||||
|         b = Dummy() |         a_signal.connect(a.a, sender=self) | ||||||
|         signal = 'this' |  | ||||||
|         connect(a.a, signal, b) |  | ||||||
|         expected = [] |         expected = [] | ||||||
|         del a |         del a | ||||||
|         garbage_collect() |         garbage_collect() | ||||||
|         result = send('this',b, a=b) |         result = a_signal.send(sender=self, val="test") | ||||||
|         self.assertEqual(result, expected) |         self.assertEqual(result, expected) | ||||||
|         self.assertEqual(list(getAllReceivers(b,signal)), []) |         self._testIsClean(a_signal) | ||||||
|         self._testIsClean() |  | ||||||
|      |  | ||||||
|     def testGarbageCollectedObj(self): |  | ||||||
|         class x: |  | ||||||
|             def __call__(self, a): |  | ||||||
|                 return a |  | ||||||
|         a = Callable() |  | ||||||
|         b = Dummy() |  | ||||||
|         signal = 'this' |  | ||||||
|         connect(a, signal, b) |  | ||||||
|         expected = [] |  | ||||||
|         del a |  | ||||||
|         garbage_collect() |  | ||||||
|         result = send('this',b, a=b) |  | ||||||
|         self.assertEqual(result, expected) |  | ||||||
|         self.assertEqual(list(getAllReceivers(b,signal)), []) |  | ||||||
|         self._testIsClean() |  | ||||||
|  |  | ||||||
|      |      | ||||||
|     def testMultipleRegistration(self): |     def testMultipleRegistration(self): | ||||||
|         a = Callable() |         a = Callable() | ||||||
|         b = Dummy() |         a_signal.connect(a) | ||||||
|         signal = 'this' |         a_signal.connect(a) | ||||||
|         connect(a, signal, b) |         a_signal.connect(a) | ||||||
|         connect(a, signal, b) |         a_signal.connect(a) | ||||||
|         connect(a, signal, b) |         a_signal.connect(a) | ||||||
|         connect(a, signal, b) |         a_signal.connect(a) | ||||||
|         connect(a, signal, b) |         result = a_signal.send(sender=self, val="test") | ||||||
|         connect(a, signal, b) |  | ||||||
|         result = send('this',b, a=b) |  | ||||||
|         self.assertEqual(len(result), 1) |         self.assertEqual(len(result), 1) | ||||||
|         self.assertEqual(len(list(getAllReceivers(b,signal))), 1) |         self.assertEqual(len(a_signal.receivers), 1) | ||||||
|         del a |         del a | ||||||
|         del b |  | ||||||
|         del result |         del result | ||||||
|         garbage_collect() |         garbage_collect() | ||||||
|         self._testIsClean() |         self._testIsClean(a_signal) | ||||||
|  |  | ||||||
|  |     def testUidRegistration(self): | ||||||
|  |         def uid_based_receiver_1(**kwargs): | ||||||
|  |             pass | ||||||
|  |  | ||||||
|  |         def uid_based_receiver_2(**kwargs): | ||||||
|  |             pass | ||||||
|  |  | ||||||
|  |         a_signal.connect(uid_based_receiver_1, dispatch_uid = "uid") | ||||||
|  |         a_signal.connect(uid_based_receiver_2, dispatch_uid = "uid") | ||||||
|  |         self.assertEqual(len(a_signal.receivers), 1) | ||||||
|  |         a_signal.disconnect(dispatch_uid = "uid") | ||||||
|  |         self._testIsClean(a_signal) | ||||||
|      |      | ||||||
|     def testRobust(self): |     def testRobust(self): | ||||||
|         """Test the sendRobust function""" |         """Test the sendRobust function""" | ||||||
|         def fails(): |         def fails(val, **kwargs): | ||||||
|             raise ValueError('this') |             raise ValueError('this') | ||||||
|         a = object() |         a_signal.connect(fails) | ||||||
|         signal = 'this' |         result = a_signal.send_robust(sender=self, val="test") | ||||||
|         connect(fails, Any, a) |  | ||||||
|         result = robust.sendRobust('this',a, a=a) |  | ||||||
|         err = result[0][1] |         err = result[0][1] | ||||||
|         self.assert_(isinstance(err, ValueError)) |         self.assert_(isinstance(err, ValueError)) | ||||||
|         self.assertEqual(err.args, ('this',)) |         self.assertEqual(err.args, ('this',)) | ||||||
|  |         a_signal.disconnect(fails) | ||||||
|  |         self._testIsClean(a_signal) | ||||||
|  |  | ||||||
| def getSuite(): | def getSuite(): | ||||||
|     return unittest.makeSuite(DispatcherTests,'test') |     return unittest.makeSuite(DispatcherTests,'test') | ||||||
|   | |||||||
| @@ -1,34 +0,0 @@ | |||||||
| from django.dispatch.robustapply import * |  | ||||||
|  |  | ||||||
| import unittest |  | ||||||
|  |  | ||||||
| def noArgument(): |  | ||||||
|     pass |  | ||||||
|  |  | ||||||
| def oneArgument(blah): |  | ||||||
|     pass |  | ||||||
|  |  | ||||||
| def twoArgument(blah, other): |  | ||||||
|     pass |  | ||||||
|  |  | ||||||
| class TestCases(unittest.TestCase): |  | ||||||
|     def test01(self): |  | ||||||
|         robustApply(noArgument) |  | ||||||
|      |  | ||||||
|     def test02(self): |  | ||||||
|         self.assertRaises(TypeError, robustApply, noArgument, "this") |  | ||||||
|      |  | ||||||
|     def test03(self): |  | ||||||
|         self.assertRaises(TypeError, robustApply, oneArgument) |  | ||||||
|      |  | ||||||
|     def test04(self): |  | ||||||
|         """Raise error on duplication of a particular argument""" |  | ||||||
|         self.assertRaises(TypeError, robustApply, oneArgument, "this", blah = "that") |  | ||||||
|  |  | ||||||
| def getSuite(): |  | ||||||
|     return unittest.makeSuite(TestCases,'test') |  | ||||||
|  |  | ||||||
|  |  | ||||||
| if __name__ == "__main__": |  | ||||||
|     unittest.main() |  | ||||||
|      |  | ||||||
		Reference in New Issue
	
	Block a user