Ownership in Python

Hi ROOTERS

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?

Thanks a lot

Hi,

You should add a data member to you Test class to ‘hold on to’ the histogram so that it is not garbage collected at the end of the function.

Cheers,
Philippe.

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?

Thanks

Matthieu,

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).

Cheers,
Wim

Hi Wim

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”?

Thanks

Matthieu,

“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.

Cheers,
Wim

Thanks a lot Wim

It works fine. And I agree with you, it is best to do this on a object basis.

Reagrds