Source code for lena.input.read_root_file
# needs ROOT installed
import copy
import inspect
import sys
import lena
[docs]class ReadROOTFile():
"""Read ROOT files from flow."""
def __init__(self, types=None, keys=None, selector=None):
"""Keyword arguments specify which objects should be read
from ROOT files.
*types* sets the list of possible objects types.
*keys* specifies a list of allowed objects' names.
Only simple keys are currently allowed (no regular expressions).
If both *types* and *keys* are provided, then
objects that satisfy any of *types* or *keys*
are read.
*selector* is a general function that accepts
an object from a ROOT file and returns a boolean.
If *selector* is given, both *types* and *keys* must
be omitted, or :exc:`.LenaTypeError` is raised.
"""
try:
import ROOT
except ImportError:
raise ImportError("ROOT not installed")
if selector is not None:
if keys or types:
raise lena.core.LenaTypeError(
"if selector is provided, keys and types "
"must not be passed"
)
if not callable(selector):
raise lena.core.LenaTypeError(
"selector must be callable"
)
self._selector = selector
return
if keys is not None:
if not isinstance(keys, list):
raise lena.core.LenaTypeError(
"keys must be a list of strings"
)
# ROOT keys can have unicode names
if (sys.version[0] == 2 and
any((not isinstance(key, basestring) for key in keys))) or \
(sys.version[0] > 2 and
any((not isinstance(key, str) for key in keys))):
raise lena.core.LenaTypeError(
"keys must contain only strings"
)
# todo: allow regular expressions
# todo: allow ROOT object versions
keys_selector = [lambda obj: obj.GetName() == key
for key in keys]
if types is not None:
if not isinstance(types, list):
raise lena.core.LenaTypeError(
"types must be a list of types"
)
# maybe inspect is needed only for Python 2 types
# not derived from object. Otherwise use isinstance(_, type)
if any((not inspect.isclass(tp) for tp in types)):
raise lena.core.LenaTypeError(
"types must must contain only types"
)
# in Lena "and" means a list, while "or" means a tuple.
# In Python isinstance requires a tuple.
types = tuple(types)
types_selector = lambda obj: isinstance(obj, types)
if types is None and keys is None:
self._selector = None
elif keys:
if types:
self._selector = lena.flow.Selector(
[types_selector, keys_selector]
)
else:
self._selector = lena.flow.Selector(keys_selector)
[docs] def run(self, flow):
"""Read ROOT files from *flow* and yield objects they contain.
For file to be read,
data part of the value must be a string (file's path) and
*context.input.read_root_file* must not be `False`.
Other values pass unchanged.
After all entries from the file are yielded, it is closed.
*context.input.root_file_path* is updated
with the path to the ROOT file.
Warning
=======
After a ROOT file is closed,
all its contained objects are destroyed.
Make all processing within one flow:
don't save yielded values to a list,
or save copies of them.
"""
from ROOT import TFile
from lena.context import get_recursively, update_recursively
from lena.flow import get_data_context
from copy import deepcopy
for val in flow:
data, context = get_data_context(val)
# skip not ROOT files
if sys.version[0] == 2:
str_type = basestring
else:
str_type = str
if not isinstance(data, str_type) or not \
get_recursively(context, "input.read_root_file",
True):
yield val
continue
root_file = TFile(data, "read")
# context of separate keys shall be updated
# when they are transformed to other types
# in other elements
update_recursively(
context, {"input": {"root_file_path": data}}
)
def get_key_names(fil):
return [key.GetName() for key in fil.GetListOfKeys()]
key_names = get_key_names(root_file)
for key_name in key_names:
# result of TFile.Get is not a TKey, but a proper type
obj = root_file.Get(key_name)
if self._selector:
if not self._selector(obj):
continue
yield (obj, deepcopy(context))
# will be closed after
# following elements used its data
root_file.Close()