This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Author gvanrossum
Recipients gvanrossum
Date 2013-05-07.16:02:22
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1367942543.4.0.296722706025.issue17927@psf.upfronthosting.co.za>
In-reply-to
Content
This came out of some investigations into Tulip reference cycles.  I've only confirmed this with 3.3 and 3.4, but I suspect it's a problem in earlier versions too.

The scenario is as follows.

Consider a object with a method that ends up catching an exception and storing the exception on the object.  We know that the __traceback__ attribute of the exception references the stack frame where the exception was caught, so there is a cycle: self -> exception -> traceback -> frame -> self.  To break this cycle without disturbing the __traceback__ on the exception, the method sets "self = None" before it returns.  (The point of breaking the cycle is that at some later point when the object is deleted the traceback can be printed by the __del__ method.)

This works beautifully...  Except if the function happens to contain a nested function or a lambda that references 'self'.  *Even if the function is never created* (e.g. "if 0: lambda: self").  Then setting "self = None" does not break the cycle.  It's not a real leak, because gc.collect() will collect the cycle.  But it's still annoying that I can't break the cycle (I don't want to break it at any other point because it would reduce the usefulness of the exception stored on the object).

After two days of investigations and thinking about it I found the cause: the presence of the lambda cause a cell to be created into which self is copied, but the original self argument is still referenced by the frame.  Setting "self = None" clears the cell but doesn't affect the original self argument.  (FWIW, this indicates it's not specifically about self, it's about any argument that gets copied into a cell.)

I thought I had a one-line fix (see cellfree.diff attached) but it breaks argument-less super(), which looks at the original first argument.  I think I can fix super() (it must introspect the code object to find out into which cell self has been copied, if it finds it NULL), but I have to think about that more.  If anyone wants to jump in and suggest an approach to that I'd appreciate it.
History
Date User Action Args
2013-05-07 16:02:23gvanrossumsetrecipients: + gvanrossum
2013-05-07 16:02:23gvanrossumsetmessageid: <1367942543.4.0.296722706025.issue17927@psf.upfronthosting.co.za>
2013-05-07 16:02:23gvanrossumlinkissue17927 messages
2013-05-07 16:02:22gvanrossumcreate