views:

418

answers:

2

The Django docs say this on the subject:

Note also that Django stores signal handlers as weak references by default, so if your handler is a local function, it may be garbage collected. To prevent this, pass weak=False when you call the signal’s connect().

I haven't been able to find any justification for why this is the default, and I don't understand why you would ever want a signal that you explicitly registered to implicitly disappear. So what is the use-case for weak references here? And why is it the default?

I realize it probably doesn't matter either way in 99% of cases, but clearly there's something I don't understand here, and I want to know if there's any "gotchas" lurking that might bite me someday.

+4  A: 

Signals handlers are stored as weak references to avoid the object they reference from not being garbage collected (for example after explicit deletion of the signal handler), just because a signal is still flying around.

Luper Rouch
But surely if the signal handler was explicitly disconnected, the signal handler wouldn't be referenced anywhere, and it wouldn't prevent the garbage collection of anything in that case. It seems like using weak references would only make a difference when the signal is *not* explicitly disconnected, and why would you want the signal to be disconnected if you didn't ask for it?
Jason Creighton
You are right, so for a signal handler to be garbage collected, without weak references you would have to do two things: delete the signal handler, and disconnect the signal. This may not be obvious for everyone, so I think this is why weak references are used by default.
Luper Rouch
Sorry to be so dense, but I still don't understand. I think I understand what you mean by "disconnect" a signal: Call the `disconnect` method on the `Signal` instance, passing in the handler as an argument. But what do you mean by "delete" the signal handler?
Jason Creighton
Your signal handler may be for example a method of an object. If the object goes out of scope (or you del it, etc...) you normally assume that it will get garbage collected, but if a non weakref signal is connected to it, it will not.
Luper Rouch
In the end, I think the source of my confusion is having a different expectation than most people...I think that it would make the most sense to have to explicitly unregister signals in all cases, but apparently there's some use-case for the weakref behavior.
Jason Creighton
Oops I fixed a big mistake in my answer: "Signals handlers are stored as weak references to avoid the object they reference from *not* being garbage collected [...]". Sorry if I introduced additional confusion :)
Luper Rouch
+2  A: 

Bound methods keep a reference to the object they belong to (otherwise, they cannot fill self, cf. the Python documentation). Consider the following code:

import gc
class SomeLargeObject(object):
    def on_foo(self): pass

slo = SomeLargeObject()
callbacks = [slo.on_foo]

print [o for o in gc.get_objects() if isinstance(o, SomeLargeObject)]
del slo
print [o for o in gc.get_objects() if isinstance(o, SomeLargeObject)]
callbacks = []
print [o for o in gc.get_objects() if isinstance(o, SomeLargeObject)]

The output:

[<__main__.SomeLargeObject object at 0x15001d0>]
[<__main__.SomeLargeObject object at 0x15001d0>]
[]

One important thing to know when keeping weakrefs on callbacks is that you cannot weakref bound methods directly, because they are always created on the fly:

>>> class SomeLargeObject(object):
...  def on_foo(self): pass
>>> import weakref
>>> def report(o):
...  print "about to collect"
>>> slo = SomeLargeObject()
>>> #second argument: function that is called when weakref'ed object is finalized
>>> weakref.proxy(slo.on_foo, report)
about to collect
<weakproxy at 0x7f9abd3be208 to NoneType at 0x72ecc0>
Torsten Marek
I was skimming through the Django implementation and saw there is indeed special effort to provide weak references to bound methods. But how about unbound methods? Where are references to them kept? What would trigger their garbage collection? When you del the module where they were defined?
obvio171