MakeProject bug in python ROOT bindings

Hi,
I’d like to report the following problem (at least in ROOT 5.28, 5.30 and 5.34).

Put any root file as “/tmp/my_file.root”, then “cd” to “/some/working/subdirectory” (should be different from “/tmp”), then try to: import os import ROOT print "before cd ... " + os.getcwd() + " ... " + os.getenv("PWD") os.chdir("/tmp") print "after cd ... " + os.getcwd() + " ... " + os.getenv("PWD") root_file = ROOT.TFile("my_file.root", "READ") print "after ROOT.TFile ... " + os.getcwd() + " ... " + os.getenv("PWD") root_file.MakeProject("my_lib", "*", "recreate++") print "after MakeProject ... " + os.getcwd() + " ... " + os.getenv("PWD") root_file.Close() print "after Close ... " + os.getcwd() + " ... " + os.getenv("PWD")
What you will get is something like: [code]>>> import os

import ROOT
print “before cd … " + os.getcwd() + " … " + os.getenv(“PWD”)
before cd … /some/working/subdirectory … /some/working/subdirectory
os.chdir(”/tmp")
print "after cd … " + os.getcwd() + " … " + os.getenv(“PWD”)
after cd … /tmp … /some/working/subdirectory
root_file = ROOT.TFile(“my_file.root”, “READ”)
(…)
print "after ROOT.TFile … " + os.getcwd() + " … " + os.getenv(“PWD”)
after ROOT.TFile … /tmp … /some/working/subdirectory
root_file.MakeProject(“my_lib”, “*”, “recreate++”)
MakeProject has generated 41 classes in my_lib
my_lib/MAKEP file has been generated
Shared lib my_lib/my_lib.so has been generated
Error in TUnixSystem::DynamicPathName: my_lib/my_lib.so does not exist in /ROOT/prefix/directory/lib/root:.:/ROOT/prefix/directory/lib/root::/ROOT/prefix/directory/lib/root/cint/cint/stl
print "after MakeProject … " + os.getcwd() + " … " + os.getenv(“PWD”)
after MakeProject … /some/working/subdirectory … /some/working/subdirectory
root_file.Close()
print "after Close … " + os.getcwd() + " … " + os.getenv(“PWD”)
after Close … /some/working/subdirectory … /some/working/subdirectory[/code]
Note what happened … the “MakeProject” properly generated the “/tmp/my_lib/my_lib.so”, but then it unconditionally changed the working directory back to “${PWD}” (which was not “/tmp” but “/some/working/subdirectory”), and so it failed to load the newly created shared library.

What’s even more dangerous … if “MakeProject” finds “my_lib/my_lib.so” somewhere in the “LD_LIBRARY_PATH" (e.g. if some old version "{PWD}/my_lib/my_lib.so” exists), it will load that shared library INSTEAD of the newly created “/tmp/my_lib/my_lib.so”.

Could one, please, fix this behaviour? I believe “MakeProject” should NOT change the working directory at all (it should remain in the same subdirectory in which it was “started”, as the user’s script will probably want to “continue” working there).

For anybody who is affected by this problem … I think I found a “brutal fix”: current_dir = os.getcwd() root_file.MakeProject(current_dir + "/my_lib", "*", "recreate++") os.chdir(current_dir)

Hi Pepe,

Is this really related/restricted to PyROOT? If not, can please report this issue as a bug Savannah?

Thanks,
Philippe.

I do NOT think that the problem exists in the original ROOT’s C++ TFIle::MakeProject … I tried the following ROOT script: { cout << "before cd ... " << gSystem->pwd() << " ... " << gSystem->Getenv("PWD") << endl; gSystem->cd("/tmp"); cout << "after cd ... " << gSystem->pwd() << " ... " << gSystem->Getenv("PWD") << endl; TFile f("my_file.root", "READ"); cout << "after TFile ... " << gSystem->pwd() << " ... " << gSystem->Getenv("PWD") << endl; f.MakeProject("my_lib", "*", "recreate++"); cout << "after MakeProject ... " << gSystem->pwd() << " ... " << gSystem->Getenv("PWD") << endl; f.Close(); cout << "after Close ... " << gSystem->pwd() << " ... " << gSystem->Getenv("PWD") << endl; } and here’s what I got: before cd ... /some/working/subdirectory ... /some/working/subdirectory after cd ... /tmp ... /some/working/subdirectory (...) after TFile ... /tmp ... /some/working/subdirectory MakeProject has generated 41 classes in my_lib my_lib/MAKEP file has been generated Shared lib my_lib/my_lib.so has been generated Shared lib my_lib/my_lib.so has been dynamically linked after MakeProject ... /tmp ... /some/working/subdirectory after Close ... /tmp ... /some/working/subdirectory

Hi,

there are quite a few differences between the C++ script you post, and what you do for python, but the strangest thing is that it is only an issue when running the code from the interactive python prompt, there’s no issue when running the code as a script (I tried both PyPy and CPython, and both have the same behavior).

I’ll try to have a more detailed look …

Cheers,
Wim

Hi,

no, I’m being silly: the reason why it was a problem with interactive, but not with the script, is b/c I still had a leftover gSystem.cd() in there.

So yes, the difference is purely in that you use os.chdir() in python, but gSystem.cd() in the C++ script. Use gSystem.cd() in either, and all will work as expected. At issue is that gSystem.WorkingDirectory(), which is used in MakeProject(), caches the result of ::getcwd(), which python uses underneath, in a data member fWdpath. A call to gSystem.cd() invalidates the fWdpath data member, a call to chdir() obviously does not.

Cheers,
Wim

What you say now is that it’s actually the ROOT C++ code that misbehaves. Maybe you could post a bug report?

The original problem was noticed inside of a python script, not while working interactively.
It would be too much work to fix calls to “os.chdir” to “gSystem.cd” just because “MakeProject” fails.

Hi,

okay, just submitted #96278, but it’s highly likely that there’s some deep reason why the cwd is begin cached …

Cheers,
Wim