Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,12 @@ def _TDirectoryFile_Get(self, namecycle):
if key:
class_name = key.GetClassName()
address = self.GetObjectChecked(namecycle, class_name)
return ROOT._cppyy.bind_object(address, class_name)
obj = ROOT._cppyy.bind_object(address, class_name)
# Objects that are not registered in the directory (e.g. TGraph or
# non-TObjects like ROOT::RNTuple) are owned by the caller
if address and not (isinstance(obj, ROOT.TObject) and self.GetList().FindObject(obj)):
ROOT.SetOwnership(obj, True)
return obj
# no key? for better or worse, call normal Get()
return self._Get(namecycle)

Expand Down
47 changes: 47 additions & 0 deletions bindings/pyroot/pythonizations/test/tdirectory_tfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,53 @@ def test_keys_title(self):
finput.Close()


class TFileGetOwnership(unittest.TestCase):
"""
Test that the objects returned by Get are owned by Python unless they are
owned by the directory (#16178)
"""

filename = "tfilegetownership.root"

@classmethod
def setUpClass(cls):
with TFile(cls.filename, "recreate") as f:
ROOT.TH1F("h", "h", 10, 0, 1).Write()
ROOT.TTree("t", "t").Write()
f.mkdir("dir")
ROOT.TGraph(10).Write("g")
ROOT.TObjString("s").Write("s")
f.WriteObject(ROOT.std.vector["double"](10), "v")

@classmethod
def tearDownClass(cls):
os.remove(cls.filename)

def test_owned_by_directory(self):
with TFile(self.filename) as f:
for name in ["h", "t", "dir"]:
obj = f.Get(name)
# Whether these objects are registered in the directory may
# depend on ROOT::Experimental::ObjectAutoRegistrationEnabled()
# in the future, so check the ownership against the actual
# registration
registered = bool(f.GetList().FindObject(name))
self.assertEqual(obj.__python_owns__, not registered, msg=name)
# The object is still alive
self.assertEqual(f.Get(name).GetName(), name)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has changed meaning now: obj is kept in the scope of the loop, so this check will always be True. Before, you were checking that after deleting obj the file was still holding it alive, which depends on whether it's registered with the file or not.


def test_owned_by_python(self):
with TFile(self.filename) as f:
objs = {name: f.Get(name) for name in ["g", "s", "v"]}
for name, obj in objs.items():
self.assertTrue(obj.__python_owns__, msg=name)
self.assertFalse(f.GetList().FindObject(name), msg=name)
# The objects are still usable after the file is closed
self.assertEqual(objs["g"].GetN(), 10)
self.assertEqual(objs["s"].GetString(), "s")
self.assertEqual(objs["v"].size(), 10)


class TFileConstructor(unittest.TestCase):
"""
Test for the TFile constructor
Expand Down
Loading