Switch off Python memory management alltogether?

Hi all,

when working with PyROOT, I keep having problem with my graph objects being deleted. Most of the time this happens when returning from methods:

def make_hist():
  h = ROOT.TH1F( "well_h", "well", 10, 0, 10 )
  h.Fill( 2.0 )
  canv = ROOT.TCanvas( 'well_c' )
  h.Draw()
  return canv

dummy = make_hist()
ROOT.gApplication.Run()

Without the dummy assignment I would not have seen the canvas, but even so, the histogram gets deleted, because it is not referenced any more. One workaround is to assign h also to some dummy, like so:

  canv.h = h
  return canv

This quickly gets tedious and is also quite redundant with the Draw command.

I understand that a real solution for this would be to make ROOT garbage collector aware, but that is, of course, a very complicated task. One hack could be to have a python method look through the canvas and add all found objects to the python side as well, or to call SetOwnership( o, False ) on every object.

In practice, however, for all my drawing tasks, I can well live with PyROOT never owning objects at all. Is there a way to switch its memory management off altogether? In a way, this would be the opposite of strict. Thanks in advance.

Cheers
Jan

Jan,

But it is aware: that is why, when ‘h’ is deleted, it gets cleanly removed from ‘canv’ rather than experiencing a segfault later when ‘canv’ is drawn. All ROOT objects created on the python side are tracked in “MemoryRegulator” and communication is handled through TObject’s RecursiveRemove() method.

That said, there’s no such thing as a global setting not to delete any C++ objects. In fact, I’ve been trying to change the model to have memory related code become more local rather than rely on the global settings, and so all proxied methods carry a _mempolicy and _creates pseudo-data member.

However, when _creates is set to False, it will revert to the default behavior, not to the inverse of _creates. I can change that, but it won’t help this particular case as the C++ constructor is mapped onto init only, not onto new and init, and init does not “create” nor return the new object.

In the range of simpler solutions, how about declaring those variables that need a longer life global at the beginning of the function?

Cheers,
Wim

Hi Wim,

thanks for the helpful comment.

Sorry, I had not realized this. In that case I should rephrase this point: What I would have expected naively is that as soon as some graphics objects uses another one, i.e. a canvas receives a histogram by being the gPad when the histogram is being drawn, it should receive (shared) ownership. This way, I only have to pass around one object, instead of all of them. The garbage collector can then delete the histogram as soon as the last reference is gone. Currently, histograms are deleted when the Python side Pointers go out of scope, even if there are C++ side pointers left. Although this is being done in a safe manner, it seems to me that this in not what you would usually want.

This is similar to the option of just adding them as data members to the one owning object. To save some typing I wrote a simple function to relinquish control of all contained primitives from an object:

def release_primitives( obj, and_self=True ):
  """
  Go through the list of primitives and relinquish ownership of all primitives
  and optionally also of given object. This is a memory leak of course. One
  could also add all primitives on the python side and return them or something
  along those lines, which reduces the memory leak.
  """

  for prim in obj.GetListOfPrimitives():
    ROOT.SetOwnership( prim, False )

  if and_self:
    ROOT.SetOwnership( obj, False )

Cheers
Jan

Jan,

ah, okay I understand better now. However, I’m not aware of a way of finding out given a graphic object, who is holding on to it (the RecursiveRemove does not work on a locally held list, but broadcasts to listeners who have previously registered interest, that the object is going away).

Cheers,
Wim

Hi Wim,

one could add behaviour on the PyROOT side to automatically handle a couple of common cases, perhaps like so:

def hold_pointers_to_implicit_members( obj ):
  if not hasattr( obj, '_implicit_members' ):
    obj._implicit_members = []
  if hasattr( obj, 'GetListOfPrimitives' ):
    for prim in obj.GetListOfPrimitives():
      obj._implicit_members.append( prim )

Then again, if there are no hooks to automatically update this, there is probably not much point.

For the particular simple task my original posting referred to, the small solutions are fine. Thank you for the discussion.

Cheers
Jan