[Python-Dev] PEP 342/343 status?

Guido van Rossum gvanrossum at gmail.com
Tue May 31 22:30:30 CEST 2005


[Guido]
> >Yes, the generator does clear its f_back when it's suspended.

[Phillip]
> I realize this won't fix all your worries; I just want to rule out
> this one *particular* form of cycle as a possibility; i.e., to show
> that mere reference to a generator-iterator in a frame does not
> create a cycle:
> 
> 
>      callerframe ---------> traceback2
>         |     ^                |  |
>         |     |                |  |
>         |     +----------------+  |
>         v                         v
>      geniter -> genframe -> traceback1
>                     ^          |
>                     |          |
>                     +----------+
> 
> As you can see, the geniter itself doesn't have a reference to its
> calling frame, so as soon as the highest-level traceback object is
> released, the cycle collector should release the upper cycle,
> allowing the geniter to complete, and releasing the lower cycle.
> 
> The scenario assumes, by the way, that the traceback object
> referenced by a frame includes a pointer to that same frame, which
> I'm not sure is the case.  I was under the impression that the
> current frame is only added to the traceback when the frame is
> exited, in which case the two cycles shown above wouldn't even
> exist; each traceback would be pointing to the *next* frame down,
> and there would be no cycles at all.  It seems to me that this would
> almost have to be the design, since tracebacks existed before cyclic
> GC did.

Alas, your assumption is valid; this would indeed cause a cycle, much
to the despair of early Python programmers.  There used to be a whole
body of literature about the best way to avoid this (never save a
traceback, or if you do, clear it when you're done with it before
exiting the frame).  When you raise and immediately catch an
exception, there is a single traceback object that references the
current frame (the frame where it was raised *and* caught).  So if you
store sys.exc_info() in a local, you have a cycle already:

  try:
      raise Exception
  except:
      x = sys.exc_info()[2]   # save the traceback

Now we have the following cycle (with slightyl more detail than your
diagram):

             tb_frame
    frame <------------- traceback
      |                       ^
      |                       |
      v         'x'           |
   f_locals ------------------+

BTW, note the repercussions this has for Ping's PEP 344 -- because
the Exception instance references the traceback in that proposal, all
code that catches an exception into a variable creates a cycle, like
this:

  try:
      raise Exception
  except Exception, err:
      pass

This would creates the following cycle:

             tb_frame
    frame <------------- traceback
      |                       ^
      |                       | __traceback__
      v                       |
   f_locals ---------------> err

The good news in all this is that none of these objects has a __del__
method in the proposal; only the 'geniter' object would have one, and
getting it involved in a cycle does seem like rather unlikely.  I
hereby declare my worries unwarranted and will happily add language to
the revamped PEP 343 that a geniter object should have a tp_del slot
and a corresponding __del__ attribute.  This further complicates
has_finalizer() in gcmodule.c, to the point where the latter might
have to be turned into an internal slot.

> >Sure. But I still have some reservations, since cycles can pop up
> >in the strangest of places (especially when tracebacks are involved
> >-- tracebacks have been causing problems due to cycles and keeping
> >variables alive almost since Python's inception). I posted about
> >this a while ago, but don't recall seeing a response that took my
> >fear away.
> 
> Well, I can't prove that it's not possible to create such cycles,
> certainly.  But maybe we should finally get rid of the deprecated
> sys.exc_type/value/traceback variables, so that they can't root any
> cycles?

I sort of doubt that these are the main source of live cycles.  After
all, they are reset whenever a frame is popped (grep the sources for
reset_exc_info).

-- 
--Guido van Rossum (home page: http://www.python.org/~guido/)


More information about the Python-Dev mailing list