Skip to content
Draft
78 changes: 76 additions & 2 deletions src/uproot/reading.py
Original file line number Diff line number Diff line change
Expand Up @@ -555,6 +555,52 @@ def __init__(
self._streamers = None
self._streamer_rules = None

import types

from rootfilespec.bootstrap import ROOTFile
from rootfilespec.buffer import ReadBuffer
from rootfilespec.dispatch import DICTIONARY
from rootfilespec.dynamic import streamerinfo_to_classes

initial_read_size = 512
path = Path(file_path)
with path.open("rb") as filehandle:

def fetch_data(seek: int, size: int):
filehandle.seek(seek)
return ReadBuffer(memoryview(filehandle.read(size)), seek, 0)

buffer = fetch_data(0, initial_read_size)
file, _ = ROOTFile.read(buffer)

# Read root directory object, which should be contained in the initial buffer
def fetch_cached(seek: int, size: int):
if seek + size <= len(buffer):
return buffer[seek : seek + size]
msg = "Didn't find data in initial read buffer"
raise ValueError(msg)

# rootdir = file.get_TFile(fetch_cached).rootdir

# List to collect NotImplementedError messages
failures: list[str] = []

def fail_cb(_: bytes, ex: NotImplementedError):
print(f"NotImplementedError: {ex}")
failures.append(str(ex))

# Read all StreamerInfo (class definitions) from the file
streamerinfo = file.get_StreamerInfo(fetch_data)

# Render the class definitions into python code
classes = streamerinfo_to_classes(streamerinfo)

# Evaluate the python code to create the classes and add them to the DICTIONARY
oldkeys = set(DICTIONARY)
module = types.ModuleType(f"rootfilespec.generated_{id(streamerinfo)}")
sys.modules[module.__name__] = module
exec(classes, module.__dict__)

self.hook_before_create_source()

source_cls, file_path = uproot._util.file_path_to_source_class(
Expand Down Expand Up @@ -2513,10 +2559,38 @@ def get(self):
if re.match(r"(std\s*::\s*)?string", self._fClassName):
return cursor.string(chunk, context)

cls = self._file.class_named(self._fClassName)
from rootfilespec.bootstrap.uproot import (
create_adapter_class,
)
from rootfilespec.buffer import ReadBuffer
from rootfilespec.dispatch import DICTIONARY

try:
out = cls.read(chunk, cursor, context, self._file, selffile, parent)
fClassName = self._fClassName

if fClassName not in DICTIONARY:
cls = self._file.class_named(self._fClassName)
return cls.read(
chunk, cursor, context, self._file, selffile, parent
)

cls = DICTIONARY[fClassName]

# construct a ReadBuffer from the chunk data
buf = ReadBuffer(
memoryview(chunk.raw_data), chunk.start, -start_cursor.origin
)
obj, buf = cls.read(buf)

uproot_cls = self._file.class_named(fClassName)
if len(uproot_cls.known_versions) > 0:
uproot_cls = uproot_cls.class_of_version(
max(uproot_cls.known_versions)
)
AdapterClass = create_adapter_class(uproot_cls)
out = AdapterClass(obj)

out._file = self._file

except uproot.deserialization.DeserializationError:
breadcrumbs = context.get("breadcrumbs")
Expand Down
21 changes: 21 additions & 0 deletions tests/test_1474_rootfilespec.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# BSD 3-Clause License; see https://github.com/scikit-hep/uproot5/blob/main/LICENSE

import skhep_testdata

import uproot


def test_UprootModelAdapter():
with uproot.open(skhep_testdata.data_path("uproot-mc10events.root")) as f:
blah = f["Events"]
print(blah.all_members)
print(blah.bases)
print(blah.closed)
print(blah.concrete)
print(blah.cursor)
print(blah.file)
print(blah.instance_version)
print(blah.is_memberwise)
print(blah.members)
print(blah.num_bytes)
print(blah.parent)