diff --git a/src/uproot/reading.py b/src/uproot/reading.py index 2c12f748c..8bc70a09e 100644 --- a/src/uproot/reading.py +++ b/src/uproot/reading.py @@ -553,6 +553,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( @@ -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") diff --git a/tests/test_1474_rootfilespec.py b/tests/test_1474_rootfilespec.py new file mode 100644 index 000000000..8620cf1d9 --- /dev/null +++ b/tests/test_1474_rootfilespec.py @@ -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)