I am trying to convert some of my C++ macros into python and I have problem with ownership.
Assuming I have the following code in a file calles Test.py
import ROOT
class Test:
def __init__(self):
pass
def draw(self):
histogram = ROOT.TH2F("test","", 10, 0, 10, 10, 0, 10)
for j in range(0, 10):
for i in range(0, 10):
histogram.SetBinContent(i, j, i * 10 + j)
histogram.Draw("text")
# raw_input()
if I launch python and write
from Test import Test
a = Test()
a.draw()
Then I end up with an empty TCanvas. If, on the other hand I uncomment the “raw_input()” in the Test.py file, then I can see my histogram. But after pressing “enter” then my histogram is gone.
It looks like that the TCanvas is owned by ROOT (and hence is not destroyed) , whereas the Histogram is not and hence destroyed at the end of the method "draw"
If I create the TCanvas inside the draw() method, the the TCanvas is not drawn!! This is a very different behaviour from a C++ macro.
So, how can I make my histogram / canvas not disappear and have the same behaviour than expected in a C++ macro?
I could do that. But that is contrary to the ownership’s strategy ROOT uses (Chapter 8 in the manual). As I said, this behavior is very different from the C++ macros.
It looks like to me that the Python’s garbage collector is not aware of the ROOT ownership’s trategy (using gDirectory and gROOT). This is why my TCanvas is not destroyed : it hasn’t been created through python, so the garbage collector would do nothing for it. But the histogram is created through the python’s interface and the garbage collector enters in action.
I am at home right now, not having access to ROOT nor Python, so I cannot make tests. But I am pretty sure that gDirectory stll holds a pointer to a deleted object (a dreading dangling pointer!)
I have to check what happens if I destroy the histogram with the mouse and I have a member holding it in my class. How can my class be aware of it? But I bet that this event sends a signal so that gDirectory knows about it. So no dangling pointer in C++ ROOT.
This seems to me that there is a serious memory problem here. Not easy to solve except if the python wrapper’s generator for the classes owned by ROOT (gDirectory, gROOT…) have their del method overloaded to do nothing. No garbage collector, just let ROOT manage the object lifetime. Of course one has to take into account that this behavior can be overrided through the AddDirectory(kFALSE)
static method.
Am I correct with those statements? Is it easy to fix?
no worries about dangling pointers and such-like: the communication kicks in when objects get deleted (the same issue exists in RINT, after all). The reason that objects get owned by the side that created it is the most naturally default choice, in line with the language chosen. With an extra step: “ROOT.SetOwnership( histogram, False )” you can release the python ref-counting if you want. Easier to work with, however, is to add the reference to the object to which it is associated, which would be the canvas (if you created one yourself instead of relying on the default).
Thanks for your answer. It makes perfectly sense. I have to rethink my scripts since it was based on the ownership I use to deal with.
I also tried your ROOT.SetOwnership( histogram, False ) but the symbol “histogram” is not recognisez (I tried also ROOT.histogram and ROOT.TH2F without success). Where can I find information about the “SetOwnership”?
“histogram” is the variable name that you used in your example … and it isn’t set class-wide: it’s on a per-object basis.
I’ve modified trunk to allow setting on individual constructors of classes to not be creators of python-owned objects:ROOT.TH2F.__init__._creates = Falsewill now make it so that all TH2F objects created on the python side will not be python-owned. That said, I still recommend doing this on individual objects to minimize side-effects if your code is run as a module by somebody else.