Skip to content

Commit 34a3bd5

Browse files
committed
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
1 parent d06b474 commit 34a3bd5

File tree

33 files changed

+369
-837
lines changed

33 files changed

+369
-837
lines changed

django/contrib/auth/management/__init__.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
Creates permissions for all installed apps that need permissions.
33
"""
44

5-
from django.dispatch import dispatcher
65
from django.db.models import get_models, signals
76
from django.contrib.auth import models as auth_app
87

@@ -16,7 +15,7 @@ def _get_all_permissions(opts):
1615
perms.append((_get_permission_codename(action, opts), u'Can %s %s' % (action, opts.verbose_name_raw)))
1716
return perms + list(opts.permissions)
1817

19-
def create_permissions(app, created_models, verbosity):
18+
def create_permissions(app, created_models, verbosity, **kwargs):
2019
from django.contrib.contenttypes.models import ContentType
2120
from django.contrib.auth.models import Permission
2221
app_models = get_models(app)
@@ -45,7 +44,7 @@ def create_superuser(app, created_models, verbosity, **kwargs):
4544
call_command("createsuperuser", interactive=True)
4645
break
4746

48-
if 'create_permissions' not in [i.__name__ for i in dispatcher.getAllReceivers(signal=signals.post_syncdb)]:
49-
dispatcher.connect(create_permissions, signal=signals.post_syncdb)
50-
if 'create_superuser' not in [i.__name__ for i in dispatcher.getAllReceivers(signal=signals.post_syncdb, sender=auth_app)]:
51-
dispatcher.connect(create_superuser, sender=auth_app, signal=signals.post_syncdb)
47+
signals.post_syncdb.connect(create_permissions,
48+
dispatch_uid = "django.contrib.auth.management.create_permissions")
49+
signals.post_syncdb.connect(create_superuser,
50+
sender=auth_app, dispatch_uid = "django.contrib.auth.management.create_superuser")

django/contrib/contenttypes/generic.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
from django.db.models import signals
99
from django.db.models.fields.related import RelatedField, Field, ManyToManyRel
1010
from django.db.models.loading import get_model
11-
from django.dispatch import dispatcher
1211
from django.utils.functional import curry
1312

1413
class GenericForeignKey(object):
@@ -29,12 +28,12 @@ def contribute_to_class(self, cls, name):
2928
self.cache_attr = "_%s_cache" % name
3029

3130
# For some reason I don't totally understand, using weakrefs here doesn't work.
32-
dispatcher.connect(self.instance_pre_init, signal=signals.pre_init, sender=cls, weak=False)
31+
signals.pre_init.connect(self.instance_pre_init, sender=cls, weak=False)
3332

3433
# Connect myself as the descriptor for this field
3534
setattr(cls, name, self)
3635

37-
def instance_pre_init(self, signal, sender, args, kwargs):
36+
def instance_pre_init(self, signal, sender, args, kwargs, **_kwargs):
3837
"""
3938
Handles initializing an object with the generic FK instaed of
4039
content-type/object-id fields.

django/contrib/contenttypes/management.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
from django.contrib.contenttypes.models import ContentType
2-
from django.dispatch import dispatcher
32
from django.db.models import get_apps, get_models, signals
43
from django.utils.encoding import smart_unicode
54

6-
def update_contenttypes(app, created_models, verbosity=2):
5+
def update_contenttypes(app, created_models, verbosity=2, **kwargs):
76
"""
87
Creates content types for models in the given app, removing any model
98
entries that no longer have a matching model class.
@@ -37,7 +36,7 @@ def update_all_contenttypes(verbosity=2):
3736
for app in get_apps():
3837
update_contenttypes(app, None, verbosity)
3938

40-
dispatcher.connect(update_contenttypes, signal=signals.post_syncdb)
39+
signals.post_syncdb.connect(update_contenttypes)
4140

4241
if __name__ == "__main__":
4342
update_all_contenttypes()

django/contrib/sites/management.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,16 @@
22
Creates the default Site object.
33
"""
44

5-
from django.dispatch import dispatcher
65
from django.db.models import signals
76
from django.contrib.sites.models import Site
87
from django.contrib.sites import models as site_app
98

10-
def create_default_site(app, created_models, verbosity):
9+
def create_default_site(app, created_models, verbosity, **kwargs):
1110
if Site in created_models:
1211
if verbosity >= 2:
1312
print "Creating example.com Site object"
1413
s = Site(domain="example.com", name="example.com")
1514
s.save()
1615
Site.objects.clear_cache()
1716

18-
dispatcher.connect(create_default_site, sender=site_app, signal=signals.post_syncdb)
17+
signals.post_syncdb.connect(create_default_site, sender=site_app)

django/core/handlers/base.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
from django import http
44
from django.core import signals
5-
from django.dispatch import dispatcher
65
from django.utils.encoding import force_unicode
76

87
class BaseHandler(object):
@@ -122,7 +121,7 @@ def get_response(self, request):
122121
except: # Handle everything else, including SuspiciousOperation, etc.
123122
# Get the exception info now, in case another exception is thrown later.
124123
exc_info = sys.exc_info()
125-
receivers = dispatcher.send(signal=signals.got_request_exception, request=request)
124+
receivers = signals.got_request_exception.send(sender=self.__class__, request=request)
126125
return self.handle_uncaught_exception(request, resolver, exc_info)
127126

128127
def handle_uncaught_exception(self, request, resolver, exc_info):

django/core/handlers/modpython.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
from django.core import signals
66
from django.core.handlers.base import BaseHandler
77
from django.core.urlresolvers import set_script_prefix
8-
from django.dispatch import dispatcher
98
from django.utils import datastructures
109
from django.utils.encoding import force_unicode, smart_str
1110

@@ -174,7 +173,7 @@ def __call__(self, req):
174173
self.load_middleware()
175174

176175
set_script_prefix(req.get_options().get('django.root', ''))
177-
dispatcher.send(signal=signals.request_started)
176+
signals.request_started.send(sender=self.__class__)
178177
try:
179178
try:
180179
request = self.request_class(req)
@@ -188,7 +187,7 @@ def __call__(self, req):
188187
response = middleware_method(request, response)
189188
response = self.apply_response_fixes(request, response)
190189
finally:
191-
dispatcher.send(signal=signals.request_finished)
190+
signals.request_finished.send(sender=self.__class__)
192191

193192
# Convert our custom HttpResponse object back into the mod_python req.
194193
req.content_type = response['Content-Type']

django/core/handlers/wsgi.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
from django.core import signals
1010
from django.core.handlers import base
1111
from django.core.urlresolvers import set_script_prefix
12-
from django.dispatch import dispatcher
1312
from django.utils import datastructures
1413
from django.utils.encoding import force_unicode
1514

@@ -207,7 +206,7 @@ def __call__(self, environ, start_response):
207206
self.initLock.release()
208207

209208
set_script_prefix(base.get_script_name(environ))
210-
dispatcher.send(signal=signals.request_started)
209+
signals.request_started.send(sender=self.__class__)
211210
try:
212211
try:
213212
request = self.request_class(environ)
@@ -221,7 +220,7 @@ def __call__(self, environ, start_response):
221220
response = middleware_method(request, response)
222221
response = self.apply_response_fixes(request, response)
223222
finally:
224-
dispatcher.send(signal=signals.request_finished)
223+
signals.request_finished.send(sender=self.__class__)
225224

226225
try:
227226
status_text = STATUS_CODE_TEXT[response.status_code]

django/core/management/commands/flush.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ class Command(NoArgsCommand):
1515
def handle_noargs(self, **options):
1616
from django.conf import settings
1717
from django.db import connection, transaction, models
18-
from django.dispatch import dispatcher
1918
from django.core.management.sql import sql_flush, emit_post_sync_signal
2019

2120
verbosity = int(options.get('verbosity', 1))

django/core/management/sql.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,6 @@ def emit_post_sync_signal(created_models, verbosity, interactive):
492492
app_name = app.__name__.split('.')[-2]
493493
if verbosity >= 2:
494494
print "Running post-sync handlers for application", app_name
495-
dispatcher.send(signal=models.signals.post_syncdb, sender=app,
496-
app=app, created_models=created_models,
497-
verbosity=verbosity, interactive=interactive)
495+
models.signals.post_syncdb.send(sender=app, app=app,
496+
created_models=created_models, verbosity=verbosity,
497+
interactive=interactive)

django/core/signals.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1-
request_started = object()
2-
request_finished = object()
3-
got_request_exception = object()
1+
from django.dispatch import Signal
2+
3+
request_started = Signal()
4+
request_finished = Signal()
5+
got_request_exception = Signal(providing_args=["request"])

django/db/__init__.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
from django.conf import settings
33
from django.core import signals
44
from django.core.exceptions import ImproperlyConfigured
5-
from django.dispatch import dispatcher
65
from django.utils.functional import curry
76

87
__all__ = ('backend', 'connection', 'DatabaseError', 'IntegrityError')
@@ -58,17 +57,19 @@ def get_creation_module():
5857

5958
# Register an event that closes the database connection
6059
# when a Django request is finished.
61-
dispatcher.connect(connection.close, signal=signals.request_finished)
60+
def close_connection(**kwargs):
61+
connection.close()
62+
signals.request_finished.connect(close_connection)
6263

6364
# Register an event that resets connection.queries
6465
# when a Django request is started.
65-
def reset_queries():
66+
def reset_queries(**kwargs):
6667
connection.queries = []
67-
dispatcher.connect(reset_queries, signal=signals.request_started)
68+
signals.request_started.connect(reset_queries)
6869

6970
# Register an event that rolls back the connection
7071
# when a Django request has an exception.
71-
def _rollback_on_exception():
72+
def _rollback_on_exception(**kwargs):
7273
from django.db import transaction
7374
transaction.rollback_unless_managed()
74-
dispatcher.connect(_rollback_on_exception, signal=signals.got_request_exception)
75+
signals.got_request_exception.connect(_rollback_on_exception)

django/db/models/base.py

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
from django.db import connection, transaction
2020
from django.db.models import signals
2121
from django.db.models.loading import register_models, get_model
22-
from django.dispatch import dispatcher
2322
from django.utils.functional import curry
2423
from django.utils.encoding import smart_str, force_unicode, smart_unicode
2524
from django.core.files.move import file_move_safe
@@ -161,14 +160,14 @@ def _prepare(cls):
161160
if hasattr(cls, 'get_absolute_url'):
162161
cls.get_absolute_url = curry(get_absolute_url, opts, cls.get_absolute_url)
163162

164-
dispatcher.send(signal=signals.class_prepared, sender=cls)
163+
signals.class_prepared.send(sender=cls)
165164

166165

167166
class Model(object):
168167
__metaclass__ = ModelBase
169168

170169
def __init__(self, *args, **kwargs):
171-
dispatcher.send(signal=signals.pre_init, sender=self.__class__, args=args, kwargs=kwargs)
170+
signals.pre_init.send(sender=self.__class__, args=args, kwargs=kwargs)
172171

173172
# There is a rather weird disparity here; if kwargs, it's set, then args
174173
# overrides it. It should be one or the other; don't duplicate the work
@@ -239,7 +238,7 @@ def __init__(self, *args, **kwargs):
239238
pass
240239
if kwargs:
241240
raise TypeError, "'%s' is an invalid keyword argument for this function" % kwargs.keys()[0]
242-
dispatcher.send(signal=signals.post_init, sender=self.__class__, instance=self)
241+
signals.post_init.send(sender=self.__class__, instance=self)
243242

244243
def __repr__(self):
245244
return smart_str(u'<%s: %s>' % (self.__class__.__name__, unicode(self)))
@@ -288,8 +287,7 @@ def save_base(self, raw=False, cls=None):
288287
cls = self.__class__
289288
meta = self._meta
290289
signal = True
291-
dispatcher.send(signal=signals.pre_save, sender=self.__class__,
292-
instance=self, raw=raw)
290+
signals.pre_save.send(sender=self.__class__, instance=self, raw=raw)
293291
else:
294292
meta = cls._meta
295293
signal = False
@@ -351,8 +349,8 @@ def save_base(self, raw=False, cls=None):
351349
transaction.commit_unless_managed()
352350

353351
if signal:
354-
dispatcher.send(signal=signals.post_save, sender=self.__class__,
355-
instance=self, created=(not record_exists), raw=raw)
352+
signals.post_save.send(sender=self.__class__, instance=self,
353+
created=(not record_exists), raw=raw)
356354

357355
save_base.alters_data = True
358356

django/db/models/fields/__init__.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
from django.db import connection, get_creation_module
1111
from django.db.models import signals
1212
from django.db.models.query_utils import QueryWrapper
13-
from django.dispatch import dispatcher
1413
from django.conf import settings
1514
from django.core import validators
1615
from django import oldforms
@@ -819,9 +818,9 @@ def contribute_to_class(self, cls, name):
819818
setattr(cls, 'get_%s_url' % self.name, curry(cls._get_FIELD_url, field=self))
820819
setattr(cls, 'get_%s_size' % self.name, curry(cls._get_FIELD_size, field=self))
821820
setattr(cls, 'save_%s_file' % self.name, lambda instance, filename, raw_field, save=True: instance._save_FIELD_file(self, filename, raw_field, save))
822-
dispatcher.connect(self.delete_file, signal=signals.post_delete, sender=cls)
821+
signals.post_delete.connect(self.delete_file, sender=cls)
823822

824-
def delete_file(self, instance):
823+
def delete_file(self, instance, **kwargs):
825824
if getattr(instance, self.attname):
826825
file_name = getattr(instance, 'get_%s_filename' % self.name)()
827826
# If the file exists and no other object of this type references it,

django/db/models/fields/related.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
from django.core import validators
1010
from django import oldforms
1111
from django import forms
12-
from django.dispatch import dispatcher
1312

1413
try:
1514
set
@@ -74,15 +73,15 @@ class MyModel(Model):
7473
value = (cls, field, operation)
7574
pending_lookups.setdefault(key, []).append(value)
7675

77-
def do_pending_lookups(sender):
76+
def do_pending_lookups(sender, **kwargs):
7877
"""
7978
Handle any pending relations to the sending model. Sent from class_prepared.
8079
"""
8180
key = (sender._meta.app_label, sender.__name__)
8281
for cls, field, operation in pending_lookups.pop(key, []):
8382
operation(field, sender, cls)
8483

85-
dispatcher.connect(do_pending_lookups, signal=signals.class_prepared)
84+
signals.class_prepared.connect(do_pending_lookups)
8685

8786
def manipulator_valid_rel_key(f, self, field_data, all_data):
8887
"Validates that the value is a valid foreign key"

django/db/models/manager.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
import copy
22

33
from django.db.models.query import QuerySet, EmptyQuerySet, insert_query
4-
from django.dispatch import dispatcher
54
from django.db.models import signals
65
from django.db.models.fields import FieldDoesNotExist
76

8-
def ensure_default_manager(sender):
7+
def ensure_default_manager(sender, **kwargs):
98
cls = sender
109
if not getattr(cls, '_default_manager', None) and not cls._meta.abstract:
1110
# Create the default manager, if needed.
@@ -16,7 +15,7 @@ def ensure_default_manager(sender):
1615
pass
1716
cls.add_to_class('objects', Manager())
1817

19-
dispatcher.connect(ensure_default_manager, signal=signals.class_prepared)
18+
signals.class_prepared.connect(ensure_default_manager)
2019

2120
class Manager(object):
2221
# Tracks each time a Manager instance is created. Used to retain order.

django/db/models/manipulators.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
from django import oldforms
33
from django.core import validators
44
from django.db.models.fields import FileField, AutoField
5-
from django.dispatch import dispatcher
65
from django.db.models import signals
76
from django.utils.functional import curry
87
from django.utils.datastructures import DotExpandedDict
@@ -11,12 +10,12 @@
1110
from django.utils.translation import ugettext as _
1211
from django.utils import datetime_safe
1312

14-
def add_manipulators(sender):
13+
def add_manipulators(sender, **kwargs):
1514
cls = sender
1615
cls.add_to_class('AddManipulator', AutomaticAddManipulator)
1716
cls.add_to_class('ChangeManipulator', AutomaticChangeManipulator)
1817

19-
dispatcher.connect(add_manipulators, signal=signals.class_prepared)
18+
signals.class_prepared.connect(add_manipulators)
2019

2120
class ManipulatorDescriptor(object):
2221
# This class provides the functionality that makes the default model

django/db/models/query.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
from django.db.models.fields import DateField
88
from django.db.models.query_utils import Q, select_related_descend
99
from django.db.models import signals, sql
10-
from django.dispatch import dispatcher
1110
from django.utils.datastructures import SortedDict
1211

1312

@@ -810,8 +809,7 @@ def delete_objects(seen_objs):
810809

811810
# Pre-notify all instances to be deleted.
812811
for pk_val, instance in items:
813-
dispatcher.send(signal=signals.pre_delete, sender=cls,
814-
instance=instance)
812+
signals.pre_delete.send(sender=cls, instance=instance)
815813

816814
pk_list = [pk for pk,instance in items]
817815
del_query = sql.DeleteQuery(cls, connection)
@@ -845,8 +843,7 @@ def delete_objects(seen_objs):
845843
if field.rel and field.null and field.rel.to in seen_objs:
846844
setattr(instance, field.attname, None)
847845

848-
dispatcher.send(signal=signals.post_delete, sender=cls,
849-
instance=instance)
846+
signals.post_delete.send(sender=cls, instance=instance)
850847
setattr(instance, cls._meta.pk.attname, None)
851848

852849
transaction.commit_unless_managed()

0 commit comments

Comments
 (0)