Permalink
Browse files

Major refactoring of django.dispatch with an eye towards speed. The n…

…et 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
  • Loading branch information...
1 parent d06b474 commit 34a3bd52255a2253696b74b2d76133aace839fd2 @jacobian jacobian committed Aug 6, 2008
@@ -2,7 +2,6 @@
Creates permissions for all installed apps that need permissions.
"""
-from django.dispatch import dispatcher
from django.db.models import get_models, signals
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)))
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.auth.models import Permission
app_models = get_models(app)
@@ -45,7 +44,7 @@ def create_superuser(app, created_models, verbosity, **kwargs):
call_command("createsuperuser", interactive=True)
break
-if 'create_permissions' not in [i.__name__ for i in dispatcher.getAllReceivers(signal=signals.post_syncdb)]:
- dispatcher.connect(create_permissions, signal=signals.post_syncdb)
-if 'create_superuser' not in [i.__name__ for i in dispatcher.getAllReceivers(signal=signals.post_syncdb, sender=auth_app)]:
- dispatcher.connect(create_superuser, sender=auth_app, signal=signals.post_syncdb)
+signals.post_syncdb.connect(create_permissions,
+ dispatch_uid = "django.contrib.auth.management.create_permissions")
+signals.post_syncdb.connect(create_superuser,
+ sender=auth_app, dispatch_uid = "django.contrib.auth.management.create_superuser")
@@ -8,7 +8,6 @@
from django.db.models import signals
from django.db.models.fields.related import RelatedField, Field, ManyToManyRel
from django.db.models.loading import get_model
-from django.dispatch import dispatcher
from django.utils.functional import curry
class GenericForeignKey(object):
@@ -29,12 +28,12 @@ def contribute_to_class(self, cls, name):
self.cache_attr = "_%s_cache" % name
# 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
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
content-type/object-id fields.
@@ -1,9 +1,8 @@
from django.contrib.contenttypes.models import ContentType
-from django.dispatch import dispatcher
from django.db.models import get_apps, get_models, signals
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
entries that no longer have a matching model class.
@@ -37,7 +36,7 @@ def update_all_contenttypes(verbosity=2):
for app in get_apps():
update_contenttypes(app, None, verbosity)
-dispatcher.connect(update_contenttypes, signal=signals.post_syncdb)
+signals.post_syncdb.connect(update_contenttypes)
if __name__ == "__main__":
update_all_contenttypes()
@@ -2,17 +2,16 @@
Creates the default Site object.
"""
-from django.dispatch import dispatcher
from django.db.models import signals
from django.contrib.sites.models import Site
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 verbosity >= 2:
print "Creating example.com Site object"
s = Site(domain="example.com", name="example.com")
s.save()
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 @@
from django import http
from django.core import signals
-from django.dispatch import dispatcher
from django.utils.encoding import force_unicode
class BaseHandler(object):
@@ -122,7 +121,7 @@ def get_response(self, request):
except: # Handle everything else, including SuspiciousOperation, etc.
# Get the exception info now, in case another exception is thrown later.
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)
def handle_uncaught_exception(self, request, resolver, exc_info):
@@ -5,7 +5,6 @@
from django.core import signals
from django.core.handlers.base import BaseHandler
from django.core.urlresolvers import set_script_prefix
-from django.dispatch import dispatcher
from django.utils import datastructures
from django.utils.encoding import force_unicode, smart_str
@@ -174,7 +173,7 @@ def __call__(self, req):
self.load_middleware()
set_script_prefix(req.get_options().get('django.root', ''))
- dispatcher.send(signal=signals.request_started)
+ signals.request_started.send(sender=self.__class__)
try:
try:
request = self.request_class(req)
@@ -188,7 +187,7 @@ def __call__(self, req):
response = middleware_method(request, response)
response = self.apply_response_fixes(request, response)
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.
req.content_type = response['Content-Type']
@@ -9,7 +9,6 @@
from django.core import signals
from django.core.handlers import base
from django.core.urlresolvers import set_script_prefix
-from django.dispatch import dispatcher
from django.utils import datastructures
from django.utils.encoding import force_unicode
@@ -207,7 +206,7 @@ def __call__(self, environ, start_response):
self.initLock.release()
set_script_prefix(base.get_script_name(environ))
- dispatcher.send(signal=signals.request_started)
+ signals.request_started.send(sender=self.__class__)
try:
try:
request = self.request_class(environ)
@@ -221,7 +220,7 @@ def __call__(self, environ, start_response):
response = middleware_method(request, response)
response = self.apply_response_fixes(request, response)
finally:
- dispatcher.send(signal=signals.request_finished)
+ signals.request_finished.send(sender=self.__class__)
try:
status_text = STATUS_CODE_TEXT[response.status_code]
@@ -15,7 +15,6 @@ class Command(NoArgsCommand):
def handle_noargs(self, **options):
from django.conf import settings
from django.db import connection, transaction, models
- from django.dispatch import dispatcher
from django.core.management.sql import sql_flush, emit_post_sync_signal
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]
if verbosity >= 2:
print "Running post-sync handlers for application", app_name
- dispatcher.send(signal=models.signals.post_syncdb, sender=app,
- app=app, created_models=created_models,
- verbosity=verbosity, interactive=interactive)
+ models.signals.post_syncdb.send(sender=app, app=app,
+ created_models=created_models, verbosity=verbosity,
+ interactive=interactive)
@@ -1,3 +1,5 @@
-request_started = object()
-request_finished = object()
-got_request_exception = object()
+from django.dispatch import Signal
+
+request_started = Signal()
+request_finished = Signal()
+got_request_exception = Signal(providing_args=["request"])
View
@@ -2,7 +2,6 @@
from django.conf import settings
from django.core import signals
from django.core.exceptions import ImproperlyConfigured
-from django.dispatch import dispatcher
from django.utils.functional import curry
__all__ = ('backend', 'connection', 'DatabaseError', 'IntegrityError')
@@ -58,17 +57,19 @@ def get_creation_module():
# Register an event that closes the database connection
# 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
# when a Django request is started.
-def reset_queries():
+def reset_queries(**kwargs):
connection.queries = []
-dispatcher.connect(reset_queries, signal=signals.request_started)
+signals.request_started.connect(reset_queries)
# Register an event that rolls back the connection
# when a Django request has an exception.
-def _rollback_on_exception():
+def _rollback_on_exception(**kwargs):
from django.db import transaction
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 import connection, transaction
from django.db.models import signals
from django.db.models.loading import register_models, get_model
-from django.dispatch import dispatcher
from django.utils.functional import curry
from django.utils.encoding import smart_str, force_unicode, smart_unicode
from django.core.files.move import file_move_safe
@@ -161,14 +160,14 @@ def _prepare(cls):
if hasattr(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):
__metaclass__ = ModelBase
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
# overrides it. It should be one or the other; don't duplicate the work
@@ -239,7 +238,7 @@ def __init__(self, *args, **kwargs):
pass
if kwargs:
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):
return smart_str(u'<%s: %s>' % (self.__class__.__name__, unicode(self)))
@@ -288,8 +287,7 @@ def save_base(self, raw=False, cls=None):
cls = self.__class__
meta = self._meta
signal = True
- dispatcher.send(signal=signals.pre_save, sender=self.__class__,
- instance=self, raw=raw)
+ signals.pre_save.send(sender=self.__class__, instance=self, raw=raw)
else:
meta = cls._meta
signal = False
@@ -351,8 +349,8 @@ def save_base(self, raw=False, cls=None):
transaction.commit_unless_managed()
if signal:
- dispatcher.send(signal=signals.post_save, sender=self.__class__,
- instance=self, created=(not record_exists), raw=raw)
+ signals.post_save.send(sender=self.__class__, instance=self,
+ created=(not record_exists), raw=raw)
save_base.alters_data = True
@@ -10,7 +10,6 @@
from django.db import connection, get_creation_module
from django.db.models import signals
from django.db.models.query_utils import QueryWrapper
-from django.dispatch import dispatcher
from django.conf import settings
from django.core import validators
from django import oldforms
@@ -819,9 +818,9 @@ def contribute_to_class(self, cls, name):
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, '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):
file_name = getattr(instance, 'get_%s_filename' % self.name)()
# If the file exists and no other object of this type references it,
@@ -9,7 +9,6 @@
from django.core import validators
from django import oldforms
from django import forms
-from django.dispatch import dispatcher
try:
set
@@ -74,15 +73,15 @@ class MyModel(Model):
value = (cls, field, operation)
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.
"""
key = (sender._meta.app_label, sender.__name__)
for cls, field, operation in pending_lookups.pop(key, []):
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):
"Validates that the value is a valid foreign key"
@@ -1,11 +1,10 @@
import copy
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.fields import FieldDoesNotExist
-def ensure_default_manager(sender):
+def ensure_default_manager(sender, **kwargs):
cls = sender
if not getattr(cls, '_default_manager', None) and not cls._meta.abstract:
# Create the default manager, if needed.
@@ -16,7 +15,7 @@ def ensure_default_manager(sender):
pass
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):
# Tracks each time a Manager instance is created. Used to retain order.
@@ -2,7 +2,6 @@
from django import oldforms
from django.core import validators
from django.db.models.fields import FileField, AutoField
-from django.dispatch import dispatcher
from django.db.models import signals
from django.utils.functional import curry
from django.utils.datastructures import DotExpandedDict
@@ -11,12 +10,12 @@
from django.utils.translation import ugettext as _
from django.utils import datetime_safe
-def add_manipulators(sender):
+def add_manipulators(sender, **kwargs):
cls = sender
cls.add_to_class('AddManipulator', AutomaticAddManipulator)
cls.add_to_class('ChangeManipulator', AutomaticChangeManipulator)
-dispatcher.connect(add_manipulators, signal=signals.class_prepared)
+signals.class_prepared.connect(add_manipulators)
class ManipulatorDescriptor(object):
# This class provides the functionality that makes the default model
@@ -7,7 +7,6 @@
from django.db.models.fields import DateField
from django.db.models.query_utils import Q, select_related_descend
from django.db.models import signals, sql
-from django.dispatch import dispatcher
from django.utils.datastructures import SortedDict
@@ -810,8 +809,7 @@ def delete_objects(seen_objs):
# Pre-notify all instances to be deleted.
for pk_val, instance in items:
- dispatcher.send(signal=signals.pre_delete, sender=cls,
- instance=instance)
+ signals.pre_delete.send(sender=cls, instance=instance)
pk_list = [pk for pk,instance in items]
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:
setattr(instance, field.attname, None)
- dispatcher.send(signal=signals.post_delete, sender=cls,
- instance=instance)
+ signals.post_delete.send(sender=cls, instance=instance)
setattr(instance, cls._meta.pk.attname, None)
transaction.commit_unless_managed()
Oops, something went wrong. Retry.

0 comments on commit 34a3bd5

Please sign in to comment.