Skip to content
Closed
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,24 @@ class _RFile_Put:
def __init__(self, rfile):
self._rfile = rfile

def __call__(self, name, obj):
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 @@ -70,7 +84,7 @@ def __call__(self, name, obj):
raise TypeError(f"type {objType} is not supported by ROOT I/O")
else:
className = objType.__cpp_name__
self._rfile.Put[className](name, obj)
self._rfile.Put[className](path, obj, title)

def __getitem__(self, template_arg):
"""
Expand All @@ -79,6 +93,44 @@ def __getitem__(self, template_arg):
return self._rfile._OriginalPut[template_arg]


class _RFile_Overwrite:
"""
Allow overwriting objects through the method Overwrite().
This is pythonized to allow Overwrite() to be called both with and without a template argument.
It also changes Overwrite's bitmask argument with keyword arguments.
"""

def __init__(self, rfile):
self._rfile = rfile

def __call__(self, path, obj = None, **kwargs):
"""
Non-templated Overwrite()
"""

title = kwargs['title'] if 'title' in kwargs else ""
createNewCycle = kwargs['createNewCycle'] if 'createNewCycle' in kwargs else True

objType = type(obj)
if isinstance(obj, str):
# special case: automatically convert python str to std::string
className = "std::string"
elif not hasattr(objType, "__cpp_name__"):
raise TypeError(f"type {objType} is not supported by ROOT I/O")
else:
className = objType.__cpp_name__

from ROOT import RFile
flags = (0 if createNewCycle else 1) * RFile.kOverwrite_NoNewCycle
self._rfile.Overwrite[className](path, obj, title, flags)

def __getitem__(self, template_arg):
"""
Templated Overwrite()
"""
return self._rfile._OriginalOverwrite[template_arg]


def _RFileExit(obj, exc_type, exc_val, exc_tb):
"""
Close the RFile object.
Expand All @@ -100,6 +152,8 @@ def rfile_open_wrapper(klass, *args):
rfile.Get = _RFile_Get(rfile)
rfile._OriginalPut = rfile.Put
rfile.Put = _RFile_Put(rfile)
rfile._OriginalOverwrite = rfile.Overwrite
rfile.Overwrite = _RFile_Overwrite(rfile)
return rfile

return rfile_open_wrapper
Expand Down
87 changes: 56 additions & 31 deletions io/io/inc/ROOT/RFile.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#define ROOT7_RFile

#include <Compression.h>
#include <Rtypes.h>
#include <ROOT/RError.hxx>

#include <deque>
Expand Down Expand Up @@ -255,11 +256,11 @@ class RFile final {
friend TFile *Internal::GetRFileTFile(RFile &rfile);

/// Flags used in PutInternal()
enum PutFlags {
enum EPutFlags {
/// When encountering an object at the specified path, overwrite it with the new one instead of erroring out.
kPutAllowOverwrite = 0x1,
kPutFlag_AllowOverwrite = 0x1,
/// When overwriting an object, preserve the existing one and create a new cycle, rather than removing it.
kPutOverwriteKeepCycle = 0x2,
kPutFlag_OverwriteKeepCycle = 0x2,
};

std::unique_ptr<TFile> fFile;
Expand All @@ -273,13 +274,13 @@ class RFile final {
std::variant<const char *, std::reference_wrapper<const std::type_info>> type) const;

/// Writes `obj` to file, without taking its ownership.
void PutUntyped(std::string_view path, const std::type_info &type, const void *obj, std::uint32_t flags);
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)
void PutInternal(std::string_view path, const T &obj, std::uint32_t flags, std::string_view title)
{
PutUntyped(path, typeid(T), &obj, flags);
PutUntyped(path, typeid(T), &obj, flags, title);
}

/// Given `path`, returns the TKey corresponding to the object at that path (assuming the path is fully split, i.e.
Expand All @@ -289,9 +290,12 @@ class RFile final {

public:
enum EListKeyFlags {
kListObjects = 1 << 0,
kListDirs = 1 << 1,
kListRecursive = 1 << 2,
kListObjects = BIT(0),
kListDirs = BIT(1),
kListRecursive = BIT(2),
};
enum EOverwriteFlags {
kOverwrite_NoNewCycle = BIT(0)
};

struct RRecreateOptions {
Expand Down Expand Up @@ -348,54 +352,75 @@ public:
/// Retrieves an object from the file.
/// `path` should be a string such that `IsValidPath(path) == true`, otherwise an exception will be thrown.
/// See \ref ValidateAndNormalizePath() for info about valid path names.
/// If the object is not there returns a null pointer.
/// \return A copy of the object at `path`, or `nullptr` if there is no object of type `T` at the specified path.
template <typename T>
std::unique_ptr<T> Get(std::string_view path) const
{
void *obj = GetUntyped(path, typeid(T));
return std::unique_ptr<T>(static_cast<T *>(obj));
}

/// Puts an object into the file.
/// The application retains ownership of the object.
/// Puts object `obj` into the file and gives it a title.
/// 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.
///
/// `path` should be a string such that `IsValidPath(path) == true`, otherwise an exception will be thrown.
/// See \ref ValidateAndNormalizePath() for info about valid path names.
///
/// Throws a RException if `path` already identifies a valid object or directory.
/// Throws a RException if the file was opened in read-only mode.
/// \throws ROOT::RException if `path` already identifies a valid object or directory.
/// \throws ROOT::RException if the file was opened in read-only mode.
template <typename T>
void Put(std::string_view path, const T &obj)
void Put(std::string_view path, const T &obj, std::string_view title = "")
{
static_assert(!std::is_base_of_v<TTree, T>, "Writing TTree with RFile is unsupported.");
PutInternal(path, obj, /* flags = */ 0);
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.
/// The application retains ownership of the object.
/// See Put for more details.
///
/// If an object already exists at that path, it is kept as a backup cycle unless `backupPrevious` is false.
/// Note that even if `backupPrevious` is false, any existing cycle except the latest will be preserved.
/// `overwriteFlags` must contain values from the `EOverwriteFlags` enum.
/// If an object already exists at that path,AND it has type `T`, it is kept as a backup cycle
/// unless `overwriteFlags` contains `kOverwrite_NoNewCycle`.
/// If the existing object's type differs from `T`, an exception is thrown.
/// Note that even if no new cycle is created, only the *latest* cycle will be deleted.
///
/// Throws a RException if `path` is already the path of a directory.
/// Throws a RException if the file was opened in read-only mode.
/// \throws ROOT::RException if `path` is already the path of a directory.
/// \throws ROOT::RException if the file was opened in read-only mode.
template <typename T>
void Overwrite(std::string_view path, const T &obj, bool backupPrevious = true)
void Overwrite(std::string_view path, const T &obj, std::uint32_t overwriteFlags = 0)
{
static_assert(!std::is_base_of_v<TTree, T>, "Writing TTree with RFile is unsupported.");
std::uint32_t flags = kPutAllowOverwrite;
flags |= backupPrevious * kPutOverwriteKeepCycle;
PutInternal(path, obj, flags);
Overwrite(path, obj, "", overwriteFlags);
}

/// Like Overwrite(std::string_view, const T&, bool) but allows giving a title to the object.
template <typename T>
void Overwrite(std::string_view path, const T &obj, std::string_view title, std::uint32_t overwriteFlags = 0)
{
std::uint32_t flags = kPutFlag_AllowOverwrite;
const bool createNewCycle = !(overwriteFlags & kOverwrite_NoNewCycle);
flags |= createNewCycle * kPutFlag_OverwriteKeepCycle;
PutInternal(path, obj, flags, title);
}

/// Writes all objects and the file structure to disk.
/// Returns the number of bytes written.
/// \return the number of bytes written.
size_t Flush();

/// Flushes the RFile if needed and closes it, disallowing any further reading or writing.
void Close();

/// Returns an iterable over all keys of objects and/or directories written into this RFile starting at path
/// `basePath` (defaulting to include the content of all subdirectories).
/// The iterable yields values of type ROOT::Experimental::RKeyInfo.
///
/// By default, keys referring to directories are not returned: only those referring to leaf objects are.
/// If `basePath` is the path of a leaf object, only `basePath` itself will be returned.
/// If `basePath` is the path of a directory, it won't appear in the listing.
Expand All @@ -408,17 +433,17 @@ public:
/// Example usage:
/// ~~~{.cpp}
/// for (RKeyInfo key : file->ListKeys()) {
/// /* iterate over all objects in the RFile */
/// // iterate over all objects in the RFile
/// cout << key.GetPath() << ";" << key.GetCycle() << " of type " << key.GetClassName() << "\n";
/// }
/// for (RKeyInfo key : file->ListKeys("", kListDirs|kListObjects|kListRecursive)) {
/// /* iterate over all objects and directories in the RFile */
/// // iterate over all objects and directories in the RFile
/// }
/// for (RKeyInfo key : file->ListKeys("a/b", kListObjects)) {
/// /* iterate over all objects that are immediate children of directory "a/b" */
/// // iterate over all objects that are immediate children of directory "a/b"
/// }
/// for (RKeyInfo key : file->ListKeys("foo", kListDirs|kListRecursive)) {
/// /* iterate over all directories under directory "foo", recursively */
/// // iterate over all directories under directory "foo", recursively
/// }
/// ~~~
RFileKeyIterable ListKeys(std::string_view basePath = "", std::uint32_t flags = kListObjects | kListRecursive) const
Expand Down
1 change: 1 addition & 0 deletions io/io/inc/TDirectoryFile.h
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ 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);
void WriteDirHeader() override;
void WriteKeys() override;

Expand Down
22 changes: 17 additions & 5 deletions 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,7 +329,14 @@ void *RFile::GetUntyped(std::string_view path,
return obj;
}

void RFile::PutUntyped(std::string_view pathSV, const std::type_info &type, const void *obj, std::uint32_t flags)
void RFile::Put(const TNamed &namedObj, std::string_view dirPath)
{
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)
{
const TClass *cls = TClass::GetClass(type);
if (!cls)
Expand Down Expand Up @@ -379,8 +387,8 @@ void RFile::PutUntyped(std::string_view pathSV, const std::type_info &type, cons
}
}

const bool allowOverwrite = (flags & kPutAllowOverwrite) != 0;
const bool backupCycle = (flags & kPutOverwriteKeepCycle) != 0;
const bool allowOverwrite = (flags & kPutFlag_AllowOverwrite) != 0;
const bool backupCycle = (flags & kPutFlag_OverwriteKeepCycle) != 0;
const Option_t *writeOpts = "";
if (!allowOverwrite) {
const TKey *existing = dir->GetKey(tokens[tokens.size() - 1].c_str());
Expand All @@ -392,7 +400,10 @@ void RFile::PutUntyped(std::string_view pathSV, const std::type_info &type, cons
writeOpts = "WriteDelete";
}

int success = dir->WriteObjectAny(obj, cls, tokens[tokens.size() - 1].c_str(), writeOpts);
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);

if (!success) {
throw ROOT::RException(R__FAIL(std::string("Failed to write ") + path + " to file"));
Expand Down Expand Up @@ -572,7 +583,8 @@ std::optional<ROOT::RKeyInfo> RFile::GetKeyInfo(std::string_view path) const

void *ROOT::Internal::RFile_GetObjectFromKey(RFile &file, const RKeyInfo &key)
{
void *obj = file.GetUntyped(key.GetPath(), key.GetClassName().c_str());
const auto nameCycle = std::string(key.GetPath()) + ";" + std::to_string(key.GetCycle());
void *obj = file.GetUntyped(nameCycle, key.GetClassName().c_str());
return obj;
}

Expand Down
Loading
Loading