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 @@ -127,10 +127,24 @@ class _RFile_Put:
def __init__(self, rfile):
self._rfile = rfile

def __call__(self, name, obj, title = ""):
def __call__(self, path, obj = None, title = ""):
"""
Non-templated Put()
"""
import ROOT

if isinstance(path, ROOT.TNamed):
# TNamed-specific overload: arguments are actually Put(namedObj, dirPath).
path, obj = obj, path
fullPath = ""
if isinstance(path, str):
fullPath = f"{path}/"
elif path is not None:
raise TypeError(f"Put(TNamed, dirPath) only accepts a string as the dirPath, not a {type(path)}")
fullPath += obj.GetName()
path = fullPath
title = obj.GetTitle()

objType = type(obj)
if isinstance(obj, str):
# special case: automatically convert python str to std::string
Expand All @@ -139,7 +153,7 @@ def __call__(self, name, obj, title = ""):
raise TypeError(f"type {objType} is not supported by ROOT I/O")
else:
className = objType.__cpp_name__
self._rfile.Put[className](name, obj, title)
self._rfile.Put[className](path, obj, title)

def __getitem__(self, template_arg):
"""
Expand Down
15 changes: 10 additions & 5 deletions io/io/inc/ROOT/RFile.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -258,9 +258,9 @@ class RFile final {
/// Flags used in PutInternal()
enum EPutFlags {
/// When encountering an object at the specified path, overwrite it with the new one instead of erroring out.
kPutFlag_AllowOverwrite = 0x1,
kPutFlag_AllowOverwrite = BIT(0),
/// When overwriting an object, preserve the existing one and create a new cycle, rather than removing it.
kPutFlag_OverwriteKeepCycle = 0x2,
kPutFlag_OverwriteKeepCycle = BIT(1),
};

std::unique_ptr<TFile> fFile;
Expand All @@ -277,9 +277,8 @@ class RFile final {
void PutUntyped(std::string_view path, const std::type_info &type, const void *obj, std::uint32_t flags,
std::string_view title);

/// \see Put
template <typename T>
void PutInternal(std::string_view path, const T &obj, std::uint32_t flags, std::string_view title = "")
void PutInternal(std::string_view path, const T &obj, std::uint32_t flags, std::string_view title)
{
PutUntyped(path, typeid(T), &obj, flags, title);
}
Expand Down Expand Up @@ -361,7 +360,8 @@ public:
return std::unique_ptr<T>(static_cast<T *>(obj));
}

/// Puts object `obj` into the file, optionally giving it a title.
/// Puts object `obj` into the file and gives it a title.
/// The application retains ownership of the object.
/// The object will be effectively copied into the file, so any further modifications won't be seen by the object
/// inside the file.
/// Note that the object is not necessarily written to storage until the RFile is closed or `Flush()` is called.
Expand All @@ -378,6 +378,11 @@ public:
PutInternal(path, obj, /* flags = */ 0, title);
}

/// Puts object `namedObj` in the file under the path `dirPath/namedObj.GetName()`, giving it a title equal to
/// `namedObj.GetTitle()`.
/// If `dirPath` is empty (default), the object is put at the top level of the file.
void Put(const class TNamed &namedObj, std::string_view dirPath = "");

/// Puts an object into the file, overwriting any previously-existing object at that path.
/// See Put for more details.
///
Expand Down
3 changes: 2 additions & 1 deletion io/io/inc/TDirectoryFile.h
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,8 @@ class TDirectoryFile : public TDirectory {
Int_t WriteTObject(const TObject *obj, const char *name=nullptr, Option_t *option="", Int_t bufsize=0) override;
Int_t WriteObjectAny(const void *obj, const char *classname, const char *name, Option_t *option="", Int_t bufsize=0) override;
Int_t WriteObjectAny(const void *obj, const TClass *cl, const char *name, Option_t *option="", Int_t bufsize=0) override;
Int_t WriteObjectAny(const void *obj, const char *title, const TClass *cl, const char *name, Option_t *option="", Int_t bufsize=0);
Int_t WriteObjectAny(const void *obj, const TClass *cl, std::pair<const char *, const char *> nameAndTitle,
Option_t *option = "", Int_t bufsize = 0);
void WriteDirHeader() override;
void WriteKeys() override;

Expand Down
9 changes: 8 additions & 1 deletion io/io/src/RFile.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include <TError.h>
#include <TFile.h>
#include <TIterator.h>
#include <TNamed.h>
#include <TKey.h>
#include <TList.h>
#include <TROOT.h>
Expand Down Expand Up @@ -328,6 +329,12 @@ void *RFile::GetUntyped(std::string_view path,
return obj;
}

void RFile::Put(const TNamed &namedObj, std::string_view dirPath)
Comment thread
jblomer marked this conversation as resolved.
Resolved
Comment thread
silverweed marked this conversation as resolved.
Resolved
{
std::string completePath = (dirPath.empty() ? "" : std::string(dirPath) + '/') + namedObj.GetName();
Put(completePath, namedObj, namedObj.GetTitle());
}

void RFile::PutUntyped(std::string_view pathSV, const std::type_info &type, const void *obj, std::uint32_t flags,
std::string_view title)
{
Expand Down Expand Up @@ -396,7 +403,7 @@ void RFile::PutUntyped(std::string_view pathSV, const std::type_info &type, cons
const char *objName = tokens[tokens.size() - 1].c_str();
assert(dynamic_cast<TDirectoryFile *>(dir));
int success =
static_cast<TDirectoryFile *>(dir)->WriteObjectAny(obj, std::string(title).c_str(), cls, objName, writeOpts);
static_cast<TDirectoryFile *>(dir)->WriteObjectAny(obj, cls, {objName, std::string(title).c_str()}, writeOpts);

if (!success) {
throw ROOT::RException(R__FAIL(std::string("Failed to write ") + path + " to file"));
Expand Down
8 changes: 6 additions & 2 deletions io/io/src/TDirectoryFile.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -2011,8 +2011,12 @@ Int_t TDirectoryFile::WriteTObject(const TObject *obj, const char *name, Option_
return nbytes;
}

Int_t TDirectoryFile::WriteObjectAny(const void *obj, const char *title, const TClass *cl, const char *name, Option_t *option, Int_t bufsize)
Int_t TDirectoryFile::WriteObjectAny(const void *obj, const TClass *cl,
std::pair<const char *, const char *> nameAndTitle, Option_t *option,
Int_t bufsize)
{
const auto [name, title] = nameAndTitle;

TDirectory::TContext ctxt(this);

if (!fFile) return 0;
Expand Down Expand Up @@ -2159,7 +2163,7 @@ Int_t TDirectoryFile::WriteObjectAny(const void *obj, const char *classname, con

Int_t TDirectoryFile::WriteObjectAny(const void *obj, const TClass *cl, const char *name, Option_t *option, Int_t bufsize)
{
return WriteObjectAny(obj, /*title=*/ "", cl, name, option, bufsize);
return WriteObjectAny(obj, cl, {name, ""}, option, bufsize);
}

////////////////////////////////////////////////////////////////////////////////
Expand Down
25 changes: 25 additions & 0 deletions io/io/test/rfile.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -906,3 +906,28 @@ TEST(RFile, Compression)
EXPECT_EQ(file->GetCompressionSettings(), 0);
}
}

TEST(RFile, PutTNamed)
{
FileRaii fileGuard("test_rfile_put_tnamed.root");

{
TNamed named1("named1", "title 1"), named2("named2", "title 2");
auto file = RFile::Recreate(fileGuard.GetPath());
file->Put(named1);
// Put named2 under a dir
file->Put(named2, "my");
}

{
auto file = RFile::Open(fileGuard.GetPath());
auto named1 = file->Get<TNamed>("named1");
auto named2 = file->Get<TNamed>("my/named2");
EXPECT_STREQ(named1->GetTitle(), "title 1");
EXPECT_STREQ(named2->GetTitle(), "title 2");
auto key1 = file->GetKeyInfo("named1").value();
auto key2 = file->GetKeyInfo("my/named2").value();
EXPECT_EQ(key1.GetTitle(), "title 1");
EXPECT_EQ(key2.GetTitle(), "title 2");
}
}
28 changes: 27 additions & 1 deletion io/io/test/rfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ def test_getTree(self):

fileName = "test_rfile_gettree_py.root"
try:
with ROOT.TFile.Open(fileName, "RECREATE") as file:
with ROOT.TFile.Open(fileName, "RECREATE") as _file:
tree = ROOT.TTree("tree", "tree")
x = array("i", [0])
tree.Branch("myColumn", x, "myColumn/I")
Expand All @@ -175,6 +175,32 @@ def test_getTree(self):
finally:
os.remove(fileName)

def test_putTNamed(self):
named1 = ROOT.TNamed("named1", "title 1")
named2 = ROOT.TNamed("named2", "title 2")

fileName = "test_rfile_tnamed_py.root"
try:
with ROOT.RFile.Recreate(fileName) as file:
file.Put(named1)
# Put named2 under a dir
file.Put(named2, "my")
# If first argument is a TNamed, second argument must be a string (or None)
with self.assertRaises(TypeError):
file.Put(named2, [2, 3])

with ROOT.RFile.Open(fileName) as file:
named1 = file.Get("named1")
named2 = file.Get("my/named2")
self.assertEqual(named1.GetTitle(), "title 1")
self.assertEqual(named2.GetTitle(), "title 2")
key1 = file.GetKeyInfo("named1")
key2 = file.GetKeyInfo("my/named2")
self.assertEqual(key1.GetTitle(), "title 1")
self.assertEqual(key2.GetTitle(), "title 2")
finally:
os.remove(fileName)


def test_overwrite(self):
"""Objects can be overwritten through the Overwrite method"""
Expand Down
Loading