Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.python > #4166

Reliably call code after object no longer exists or is "unreachable"?

Path csiph.com!x330-a1.tempe.blueboxinc.net!usenet.pasdenom.info!aioe.org!feeder.news-service.com!newsfeed.xs4all.nl!newsfeed6.news.xs4all.nl!xs4all!post.news.xs4all.nl!not-for-mail
Return-Path <ms419@freezone.co.uk>
X-Original-To python-list@python.org
Delivered-To python-list@mail.python.org
X-Spam-Status OK 0.000
X-Spam-Evidence '*H*': 1.00; '*S*': 0.00; 'python,': 0.01; 'circular': 0.03; 'handled': 0.03; 'sys': 0.04; 'formed': 0.07; 'method,': 0.07; 'pep': 0.07; 'python)': 0.07; 'python': 0.07; "(it's": 0.09; 'called.': 0.09; 'cleaned': 0.09; 'garbage': 0.09; 'python-level': 0.09; 'referenced': 0.09; 'restriction': 0.09; 'subject:object': 0.09; 'def': 0.13; '(below).': 0.16; '(excluding': 0.16; 'callback': 0.16; 'default),': 0.16; 'destructor': 0.16; 'leaks': 0.16; 'received:96.54': 0.16; 'scope,': 0.16; 'subject:exists': 0.16; 'invalid': 0.16; 'exists': 0.19; 'object,': 0.19; 'variable': 0.21; 'code': 0.22; 'e.g.': 0.22; 'subject:code': 0.23; 'world!': 0.23; 'objects': 0.24; 'example': 0.24; 'memory': 0.24; 'skip:# 10': 0.25; 'suspect': 0.25; 'object': 0.27; "doesn't": 0.28; 'class': 0.29; "python's": 0.29; 'cycles': 0.31; 'detector': 0.31; 'enabled': 0.31; 'received:96': 0.31; "can't": 0.31; 'import': 0.32; 'called': 0.32; 'to:addr:python-list': 0.32; 'option': 0.33; 'creates': 0.33; 'reference': 0.34; 'received:192': 0.34; 'skip:" 10': 0.34; 'received:70': 0.34; 'there': 0.35; 'print': 0.35; 'point': 0.35; 'received:192.168': 0.37; 'some': 0.37; 'however': 0.37; 'self': 0.37; 'should': 0.37; 'cycle': 0.38; 'references': 0.38; 'but': 0.38; 'current': 0.38; 'signal': 0.39; 'to:addr:python.org': 0.39; 'header:Mime-Version:1': 0.39; 'how': 0.39; 'works': 0.40; 'behavior': 0.40; "it's": 0.40; 'subject': 0.61; 'order': 0.61; 'cause': 0.65; 'below,': 0.71; 'exotic': 0.84; 'off"': 0.84; 'reachable': 0.84; 'reference?': 0.84; 'tricky': 0.84; 'involved.': 0.91; 'killed': 0.91; 'subject:call': 0.91
X-Cloudmark-SP-Filtered true
X-Cloudmark-SP-Result v=1.1 cv=l0hTUOwXBBZC3xuFbE1ochJnP84ph5EBJB8ZSEz2E7A= c=1 sm=1 a=ce0y7WToGDYA:10 a=qWYXSweIvAIA:10 a=BLceEmwcHowA:10 a=LYYm-LO_OfoA:10 a=RBKHmHSr9-IA:10 a=IkcTkHD0fZMA:10 a=b0KxtKZxmKbTALECVfpD+Q==:17 a=ix6Hxt0jKgOGdwMXI3EA:9 a=iI5xN9I5EabL2H6nA7AA:7 a=QEXdDO2ut3YA:10 a=HpAAvcLHHh0Zw7uRqdWCyQ==:117
X-IronPort-AV E=Sophos;i="4.64,276,1301896800"; d="scan'208";a="427297414"
X-reinject true
Subject Reliably call code after object no longer exists or is "unreachable"?
From Jack Bates <ms419@freezone.co.uk>
To python-list@python.org
Content-Type text/plain; charset="UTF-8"
Content-Transfer-Encoding quoted-printable
Date Wed, 27 Apr 2011 09:48:42 -0700
Mime-Version 1.0
X-Mailer Evolution 2.32.2
X-BeenThere python-list@python.org
X-Mailman-Version 2.1.12
Precedence list
List-Id General discussion list for the Python programming language <python-list.python.org>
List-Unsubscribe <http://mail.python.org/mailman/options/python-list>, <mailto:python-list-request@python.org?subject=unsubscribe>
List-Archive <http://mail.python.org/pipermail/python-list>
List-Post <mailto:python-list@python.org>
List-Help <mailto:python-list-request@python.org?subject=help>
List-Subscribe <http://mail.python.org/mailman/listinfo/python-list>, <mailto:python-list-request@python.org?subject=subscribe>
Newsgroups comp.lang.python
Message-ID <mailman.912.1303940829.9059.python-list@python.org> (permalink)
Lines 133
NNTP-Posting-Host 82.94.164.166
X-Trace 1303940830 news.xs4all.nl 81473 [::ffff:82.94.164.166]:45635
X-Complaints-To abuse@xs4all.nl
Xref x330-a1.tempe.blueboxinc.net comp.lang.python:4166

Show key headers only | View raw


In Python, how can you reliably call code - but wait until an object no
longer exists or is "unreachable"?

I want to ensure that some code is called (excluding some exotic
situations like when the program is killed by a signal not handled by
Python) but can't call it immediately. I want to wait until there are no
references to an object - or the only references to the object are from
unreachable reference cycles


#!/usr/bin/env python

class Goodbye:
  def __del__(self):
    print 'Goodbye, world!'

ref = Goodbye()


$ ./goodbye
Goodbye, world!
$ 


Python's __del__ or destructor method works (above) - but only in the
absence of reference cycles (below). An object, with a __del__ method,
in a reference cycle, causes all objects in the cycle to be
"uncollectable". This can cause memory leaks and because the object is
never collected, its __del__ method is never called


> Circular references which are garbage are detected when the option
> cycle detector is enabled (it's on by default), but can only be
> cleaned up if there are no Python-level __del__() methods involved.


#!/usr/bin/env python

class Goodbye:
  def __del__(self):
    print 'Goodbye, world!'

class Cycle:
  def __init__(self, cycle):
    self.next = cycle
    cycle.next = self

Cycle(Goodbye())


$ ./cycle
$ 


In PEP 342 I read that an object, with a __del__ method, referenced by a
cycle but not itself participating in the cycle, doesn't cause objects
to be uncollectable. If the cycle is "collectable" then when it's
eventually collected by the garbage collector, the __del__ method is
called


> If the generator object participates in a cycle, g.__del__() may not
> be called. This is the behavior of CPython's current garbage
> collector. The reason for the restriction is that the GC code needs to
> "break" a cycle at an arbitrary point in order to collect it, and from
> then on no Python code should be allowed to see the objects that
> formed the cycle, as they may be in an invalid state. Objects "hanging
> off" a cycle are not subject to this restriction.


#!/usr/bin/env python

import sys

class Destruct:
  def __init__(self, callback):
    self.__del__ = callback

class Goodbye:
  def __init__(self):
    self.destruct = Destruct(lambda: sys.stdout.write('Goodbye, world!\n'))

class Cycle:
  def __init__(self, cycle):
    self.next = cycle
    cycle.next = self

Cycle(Goodbye())


$ ./dangle
Goodbye, world!
$ 


However it's *extremely* tricky to ensure that the object with a __del__
method doesn't participate in a cycle, e.g. in the example below, the
__del__ method is never called - I suspect because the object with a
__del__ method is reachable from the global scope, and this forms a
cycle with a frame's f_globals reference? "storing a generator object in
a global variable creates a cycle via the generator frame's f_globals
pointer"


#!/usr/bin/env python

import sys

class Destruct:
  def __init__(self, callback):
    self.__del__ = callback

class Goodbye:
  def __init__(self):
    self.destruct = Destruct(lambda: sys.stdout.write('Goodbye, world!\n'))

class Cycle:
  def __init__(self, cycle):
    self.next = cycle
    cycle.next = self

ref = Cycle(Goodbye())


$ ./global
$ 


Faced with the real potential for reference cycles, how can you reliably
call code - but wait until an object no longer exists or is
"unreachable"?

Back to comp.lang.python | Previous | NextNext in thread | Find similar


Thread

Reliably call code after object no longer exists or is "unreachable"? Jack Bates <ms419@freezone.co.uk> - 2011-04-27 09:48 -0700
  Re: Reliably call code after object no longer exists or is "unreachable"? Thomas 'PointedEars' Lahn <PointedEars@web.de> - 2011-04-28 02:54 +0200
  Re: Reliably call code after object no longer exists or is "unreachable"? Gregory Ewing <greg.ewing@canterbury.ac.nz> - 2011-04-28 19:13 +1200

csiph-web