From 0a6314f249f02d928bf5cf29def470e180898e0d Mon Sep 17 00:00:00 2001 From: Jacob Kaplan-Moss Date: Mon, 25 Aug 2008 18:24:05 +0000 Subject: [PATCH] Fixed #8285: signal handlers that aren't functions work under DEBUG. This slightly loosens the sanity check, but things that are valid under production shouldn't fail under debug. git-svn-id: http://code.djangoproject.com/svn/django/trunk@8546 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/dispatch/dispatcher.py | 20 ++++++++++++++++++-- tests/modeltests/signals/models.py | 10 +++++++--- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/django/dispatch/dispatcher.py b/django/dispatch/dispatcher.py index ff659d3005..74fdd20b90 100644 --- a/django/dispatch/dispatcher.py +++ b/django/dispatch/dispatcher.py @@ -63,10 +63,26 @@ class Signal(object): """ from django.conf import settings + # If DEBUG is on, check that we got a good receiver if settings.DEBUG: import inspect - assert inspect.getargspec(receiver)[2] is not None, \ - "Signal receivers must accept keyword arguments (**kwargs)." + assert callable(receiver), "Signal receivers must be callable." + + # Check for **kwargs + # Not all callables are inspectable with getargspec, so we'll + # try a couple different ways but in the end fall back on assuming + # it is -- we don't want to prevent registration of valid but weird + # callables. + try: + argspec = inspect.getargspec(receiver) + except TypeError: + try: + argspec = inspect.getargspec(receiver.__call__) + except (TypeError, AttributeError): + argspec = None + if argspec: + assert argspec[2] is not None, \ + "Signal receivers must accept keyword arguments (**kwargs)." if dispatch_uid: lookup_key = (dispatch_uid, _make_id(sender)) diff --git a/tests/modeltests/signals/models.py b/tests/modeltests/signals/models.py index 23d1f2d87d..5e038f9516 100644 --- a/tests/modeltests/signals/models.py +++ b/tests/modeltests/signals/models.py @@ -30,9 +30,13 @@ def pre_delete_test(signal, sender, instance, **kwargs): print 'pre_delete signal,', instance print 'instance.id is not None: %s' % (instance.id != None) -def post_delete_test(signal, sender, instance, **kwargs): - print 'post_delete signal,', instance - print 'instance.id is None: %s' % (instance.id == None) +# #8285: signals can be any callable +class PostDeleteHandler(object): + def __call__(self, signal, sender, instance, **kwargs): + print 'post_delete signal,', instance + print 'instance.id is None: %s' % (instance.id == None) + +post_delete_test = PostDeleteHandler() __test__ = {'API_TESTS':""" >>> models.signals.pre_save.connect(pre_save_test)