When I tried to disconnect that signal handler temporarily in TestCase, I
couldn't achieve that. The lookup key generated in dispatcher (lookup_key
= (_make_id(receiver), _make_id(sender))) in order to match function which
should be disconnected has other value than it should have so the function
can't be found in self.receivers and handler doesn't get disconnected.
That's probably because in self.receivers there is a lookup key generated
based on "wrapper" function which had been taken to generate that lookup
when registering handler, not the original one, that's why importing
original function and passing it to .disconnect() doesn't work.
When signal connected without using @receiver but using pre_save.connect
it works as expected.
--
Ticket URL: <https://code.djangoproject.com/ticket/22657>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.
* needs_docs: => 0
* needs_better_patch: => 0
* needs_tests: => 0
--
Ticket URL: <https://code.djangoproject.com/ticket/22657#comment:1>
* status: new => closed
* resolution: => worksforme
Comment:
Hi,
I've tried to reproduce your issue but couldn't manage to get the results
you're describing.
I used an empty `Foo` model (no fields except for the auto-generated `id`
one) and the following code:
{{{#!python
from django.db.models.signals import pre_save
from django.dispatch import receiver
from bug22657.models import Foo
@receiver(pre_save)
def callback(sender, **kwargs):
print('Hello!')
Foo.objects.create() # Prints "Hello"
pre_save.disconnect(callback)
Foo.objects.create() # Prints nothing
}}}
I've tried both `master` and the 1.6 branch and I get the same result in
both cases.
Could you show us a piece of code that demonstrates the issue (and reopen
this ticket when you do so)?
Thanks
--
Ticket URL: <https://code.djangoproject.com/ticket/22657#comment:2>
Comment (by anonymous):
Replying to [comment:2 bmispelon]:
Hi,
I just reproduced the issue. It's related to parameter sender, when I
don't provide that parameter, receiver is not disconnected. I know it
works for you but maybe something else influence my results, i.e. it
occurs in test case. Also the function is imported from different app. But
now I'm 100% sure, i'll paste my results.
code part:
pre_save.disconnect(calculate_current_equity)
for s in Subscription.objects.all():
# simulate real world behaviour - closing transactions one by
one in correct order
for st in
s.subscription_transactions.order_by('mt_close_time'):
st.mt_open_price =
QuoteGenerator.get_symbol_sl_value(st.symbol.name, st.type)
st.mt_close_price =
QuoteGenerator.get_symbol_sl_value(st.symbol.name, st.type)
st.mt_profit = Decimal(randint(1000, 100000) / 100)
st.save()
result:
File "[...]/webapp/signals/models.py", line 149, in
'''calculate_current_equity'''
raise CustomRuntimeError("Closed transaction saved again, although
calculations already done.")
CustomRuntimeError: Closed transaction saved again, although calculations
already done.
pre_save.disconnect(calculate_current_equity,
SubscriptionTransaction)
for s in Subscription.objects.all():
# simulate real world behaviour - closing transactions one by
one in correct order
for st in
s.subscription_transactions.order_by('mt_close_time'):
st.mt_open_price =
QuoteGenerator.get_symbol_sl_value(st.symbol.name, st.type)
st.mt_close_price =
QuoteGenerator.get_symbol_sl_value(st.symbol.name, st.type)
st.mt_profit = Decimal(randint(1000, 100000) / 100)
st.save()
result:
test fails from different reasons.
I know it's lame "proof", but what else I can do? Pls try to disconnect
receiver in test (and maybe from another app).
Regards,
Mike
> Hi,
>
> I've tried to reproduce your issue but couldn't manage to get the
results you're describing.
>
> I used an empty `Foo` model (no fields except for the auto-generated
`id` one) and the following code:
> {{{#!python
> from django.db.models.signals import pre_save
> from django.dispatch import receiver
>
> from bug22657.models import Foo
>
> @receiver(pre_save)
> def callback(sender, **kwargs):
> print('Hello!')
>
> Foo.objects.create() # Prints "Hello"
> pre_save.disconnect(callback)
> Foo.objects.create() # Prints nothing
> }}}
>
> I've tried both `master` and the 1.6 branch and I get the same result in
both cases.
>
>
> Could you show us a piece of code that demonstrates the issue (and
reopen this ticket when you do so)?
>
> Thanks
--
Ticket URL: <https://code.djangoproject.com/ticket/22657#comment:3>
Comment (by aldnav):
Replying to [comment:3 anonymous]:
Might be good to shed some light in this 2 year old issue.
@Mike
result:
test fails from different reasons.
One of the possible causes this signal-handler-dependent test fails is
that the '''TestCase''' that issued __''disconnect''__ did not
re__''connect''__ the muted signal handler and TestCases that follow fail.
There are a couple of things that you can do to justify/correct other
independent test cases: (cleaner/recommended) __reconnect__ on
__tearDown__; or (works but may have side effects to other tests)
__reload__ the signal handler from the other test cases; or __import__ the
signal handler from the other test case.
Say we have too models '''Foo''' and '''Bar'''. We hook '''pre_save'''
signal with sender '''Foo''' to create a '''Bar''' instance.
{{{#!python
from django.db.models.signals import pre_save
from django.dispatch import receiver
from bug22657.models import Foo, Bar
@receiver(pre_save, sender=Foo)
def callback(sender, **kwargs):
Bar.objects.create()
print 'Hello!'
}}}
Then, we have two tests: ''test_model'' and ''test_signal''.
{{{#!python
### test_model.py ###
from django.test import TestCase
from bug22657.models import Foo, Bar
# a safe re-import/reload of the signal handler
# in expense of an unused import
from bug22657.signals import handlers
class FooModelTestCase(TestCase):
def test_create_model(self):
Foo.objects.create()
self.assertEquals(Foo.objects.count(), 1)
self.assertEquals(Bar.objects.count(), 1)
### test_signal.py ###
from django.db.models.signals import pre_save
from django.test import TestCase
from bug22657.models import Foo, Bar
from bug22657.signals.handlers import callback
class FooSignalTestCase(TestCase):
def setUp(self):
pre_save.disconnect(callback, sender=Foo)
def test_create_signal_off(self):
Foo.objects.create()
self.assertEquals(Foo.objects.count(), 1)
self.assertEquals(Bar.objects.count(), 0)
# comment this tearDown and other tests may fail
def tearDown(self):
pre_save.connect(callback, sender=Foo)
}}}
For the benefit of it, if you have not noticed the code comments, running
the test suite will be successful. However, commenting
''test_signal.FooSignalTestCase.tearDown'' will fail the other following
test cases, in this particular example the ''test_model'' will fail on its
second assertion or no Bar object is created.
To avoid further failures that relates to signals, temporary disabling of
signals must be use with caution if you have other test cases that depends
upon the signal mechanism.
--
Ticket URL: <https://code.djangoproject.com/ticket/22657#comment:4>
Comment (by James Addison):
Note this comment made in #28169:
https://code.djangoproject.com/ticket/28169#comment:1 It shows that there
are cases where `.disconnect()` isn't as easy as it 'should' be, but it
can be made to work.
--
Ticket URL: <https://code.djangoproject.com/ticket/22657#comment:5>