How to get a non-changing copy of gDirectory in Python

Dear ROOTers,

I have difficulty in getting a non-changing copy of gDirectory in python.

ROOT [] TFile* file = new TFile("test.root","RECREATE");
ROOT [] TPython::Prompt();
>>> from ROOT import gDirectory
>>> topDir = gDirectory
>>> print topDir
<ROOT.TDirectory* object ("test.root") at 0xf3af00>

>>> subDir = topDir.mkdir("gamDir")
>>> subDir.cd()
>>> print topDir
<ROOT.TDirectory* object ("gamDir") at 0xf3af00>

>>> subDir2 = subDir.mkdir("gamDir2")
>>> subDir2.cd()
>>> print topDir
<ROOT.TDirectory* object ("gamDir2") at 0xf3af00>

That is, the content of topDir is always changing. How can I get a current directory that would not be changed?

–Shuwei[/code]

Shuwei,

does this work for you:topDir = gDirectory.GetDirectory( gDirectory.GetPath() )
Cheers,
Wim

Hi Wim,

topDir = gDirectory.GetDirectory( gDirectory.GetPath() )

It does not work either. I think that the cause of this problem is that topDir is a reference object of a pointer to a TDirectory. If it is a reference to a TDirectory, subDir in the following example,

topDir = gDirectory.GetDirectory( gDirectory.GetPath() ) subDir = topDir.mkdir("gamDir") print subDir <ROOT.TDirectory object ("gamDir") at 0xa6adff0>

its content will not be changed.

So the question is: how can I get a reference instead of a pointer to current directory?

–Shuwei

Shuwei,

are you sure? What is the output of this session for you:[code]>>> from ROOT import *

topDir = gDirectory.GetDirectory( gDirectory.GetPath() )
print topDir, gDirectory
<ROOT.TROOT object (“PyROOT”) at 0xb7a38f80> <ROOT.TDirectory* object (“PyROOT”) at 0xb7a38178>
f = TFile( ‘test.root’, ‘RECREATE’ )
print topDir, gDirectory
<ROOT.TROOT object (“PyROOT”) at 0xb7a38f80> <ROOT.TDirectory* object (“test.root”) at 0xb7a38178>[/code]
which shows that although gDirectory moves, topDir no longer does?

The underlying to gDirectory is a TDirectory**, the underlying to the result of GetDirectory() is a TDirectory*, so I’m not quite sure how it can’t work.

Cheers,
Wim

Hi Wim,

I tried again. It is depended on where the file is opened. If I start ROOT, open file there and then run "TPython::Prompt()", both topDir and gDirectory are TDirectory* object in python regardless of [b]topDir=gDirectory[/b] or [b]topDir=gDirectory.GetDirectory(gDirectory.GetPath())[/b].

If I start python, topDir would be a TDirectory object for [b]topDir=gDirectory.GetDirectory(gDirectory.GetPath())[/b].

–Shuwei

Shuwei,

yes, of course: gDirectory always points to the current directory, so after opening a file, the current directory is that file (gDirectory will still be TDirectory** in python, though). If you want to save the top directory from gDirectory, you’d have to do that before opening the file, and the GetDirectory() code snippet does just that. Also, if you start out in root, you could simply do:[code]root [0] topDir = gDirectory;
root [1] f = new TFile( “test.root”, “RECREATE” );
root [2] TPython::Prompt()

print ROOT.gDirectory, ROOT.topDir
<ROOT.TDirectory* object (“test.root”) at 0xb7ec4178> <ROOT.TDirectory* object (“Rint”) at 0x80a56e0>[/code]
so now, gDirectory has moved, but its by-value copy (the semantics of that statement are different in C++ than they are in python) topDir from before opening the file has not. Both topDir and gDirectory are TDirectory** here (in python; C++ types are, of course, still TDirectory*), meaning that if the actual pointer changes on the CINT side, the python side changes with it (compare that with GetDirectory()). E.g., to continue the above session:[code]>>> ^D
root [3] topDir = 0;
root [4] TPython::Prompt()
ROOT.topDir.GetName()
Traceback (most recent call last):
File “/home/wlav/rootdev/root/lib/”, line 1, in

ReferenceError: attempt to access a null-pointer

[/code]
So, … I don’t see any problems?

Cheers,
Wim

Hi Wim,

I like to get the top directory in python:

[code]root [0] f = new TFile( “test.root”, “RECREATE” );
root [1] TPython::Prompt()

topDir = gDirectory
dir1 = topDir.mkdir(“dir1”)
dir1.cd()

define histograms under dir1/

dir2= topDir.mkdir(“dir2”)
dir2.cd()[/code]

But I lost the original top directory topDir after I executed “dir1.cd()” in python. This is the problem I like to solve.

–Shuwei

Shuwei,

the top directory is always gROOT:>>> print ROOT.gROOT
Cheers,
Wim

Hi Wim,

It seems I did not make it clear. In order to make multiple same-level sub-directories, I need the original content of topDir (not top directory: gROOT) in the following example:

[code]root [0] f = new TFile( “test.root”, “RECREATE” );
root [1] TPython::Prompt()

topDir = gDirectory
dir1 = topDir.mkdir(“dir1”)
dir1.cd()

define histograms under dir1/

dir2= topDir.mkdir(“dir2”)
dir2.cd()
[/code]

But topDir is changing!

–Shuwei

Shuwei,

ok, I get it … what is going on is the following: PyROOT keeps track of C++ objects, in case the C++ side decides to delete any of them, so that it can nullify them on the python side (and prevent a crash). These tracked objects are recycled where possible, so the GetDirectory() after the file is returning the recycled gDirectory (which was seen before b/c of the GetPath() call). One option is to delete gDirectory prior to GetDirectory(), and to call GetDirectory() on gROOT instead of gDirectory:[code]>>> cwd = gDirectory.GetPath()

del gDirectory
topDir = gROOT.GetDirectory( cwd )[/code]
but that is rather brittle, as your code may not be the only one having a reference to gDirectory.

The best alternative that I can come up with is:[code]root [0] f = new TFile( “test.root”, “RECREATE” );
root [1] TPython::Prompt()

gROOT.ProcessLine( “topDir = gDirectory;” )
dir1 = topDir.mkdir(“dir1”)
dir1.cd()

define histograms under dir1/

dir2= topDir.mkdir(“dir2”)
dir2.cd() [/code]
So, with gROOT.ProcessLine, create a value copy on the C++ side.
HTH,
Wim

Ouch, I ran into this exact problem - it caused me hours of headaches.

I saw this sort of idiom before in ROOT, and transliterated straight into python:

TDirectory *prevDir = gDirectory; TFile blah("blah.root", "recreate"); if ( prevDir ) prevDir->cd();
i.e.prevDir = ROOT.gDirectory blah = TFile( "blah.root", "recreate" ) if prevDir: prevDir.cd()

For the moment I’m going with something like this, unless there is any better way:

prevDir = ROOT.gDirectory.GetPath() blah = ... if prevDir: ROOT.gROOT.GetDirectory( prevDir ).cd()

Sorry about the headache, but making gDirectory behave the way it does (i.e. as TDirectory**) seemed to be the most natural at the time. How about this one:prevDir = gDirectory.GetDirectory('')
Cheers,
Wim

Hmm, I’m having yet more problems with this:

[code] from ROOT import TFile
f = TFile(“test.root”, “recreate”)
f.mkdir(“Hello”).cd()
from ROOT import gDirectory
print “f gDirectory:”, gDirectory
f.Close()

print "f.Closed() gDirectory:", gDirectory

f1 = TFile("test1.root","recreate")
f1.mkdir("Hello1").cd()
print "f1 gDirectory:", gDirectory

from os import unlink
unlink("test.root")
unlink("test1.root")

[/code]

Produces the output:

f gDirectory: <ROOT.TDirectory* object ("Hello") at 0xb76ec378> f.Closed() gDirectory: None f1 gDirectory: None

I really expected f1 gDirectory to be Hello1?

How can I work around this…? I use gDirectory to retrieve objects later.

  • Pete

Weird. If I import gDirectory before the first mkdir, it works as expected.

Is this a bug?

Pete,

sorry I can’t reproduce it. I get, as expected:

f gDirectory: <ROOT.TDirectory* object ("Hello") at 0xb79e3698> f.Closed() gDirectory: <ROOT.TDirectory* object ("PyROOT") at 0xb79e3698> f1 gDirectory: <ROOT.TDirectory* object ("Hello1") at 0xb79e3698>
Which version are you running? Thanks.

Cheers,
Wim

Tried with 5.20 and 5.21, I believe. I haven’t had a chance to try with 5.22 yet.

Pete,

Just tried 5.18 myself, and that version does have the problem. What appears to happen, is that the TDirectoryFile that results from mkdir() goes away and signals that, making gDirectory being set to None. Not sure whether that is appropriate, but it doesn’t happen in HEAD. In both cases is the TDirectoryFile properly deleted (and, at that point, signalled).

Cheers,
Wim

Hi,

figured it out; fixed now.

Cheers,
Wim