Repository navigation
RooFit classes with hand-written streamers (RooRealVar, RooLinkedList, ...): implement per root-io-spec RooFit, replacing the framing-layer special cases #71
Description
🤖 AI generated content
Line item of #9: RooRealVar not declared in the StreamerInfo (uproot-issue49.root, uproot-issue-350.root), and the classes/roofit.root fixture.
Scope (maintainer, updated 2026-09-30): RooFit is in scope. The original decision was to skip RooFit until root-io-spec specified it. The spec now does, in RooFit (added upstream in 5d6a95b and already in our pinned submodule, c65c464). Its §8 erratum 1 withdraws the spec's own earlier "out of scope" decision.
What the spec covers. Five classes have hand-written Streamers (§1): RooRealVar (§2), RooLinkedList (§3), RooAbsBinning (§4.1), RooRefArray (§4.2) and RooCategory below class version 3 (§5). Every other RooFit class, RooVectorDataStore included, is driven by its streamer info. §6 gives the read steps, and §7 gives the invariants. None of the five is extending: each one ends where its byte count says, where it has one.
Our current workarounds, checked against the spec:
| Where | What it does | Spec |
|---|---|---|
src/rootfilespec/dynamic.py:87-89 |
Raises NotImplementedError for RooRealVar |
§2: specified. No file has an info for it (§2.3), so it needs a hand-written reader |
src/rootfilespec/bootstrap/streamedobject.py:257-260 |
Reads "extra data after the RooRealVar object" |
§8 erratum 2: that data is RooRealVar's own tail, inside its byte count (§2.2) |
src/rootfilespec/bootstrap/streamedobject.py:170-178 |
Reads an extra stream header before RooAbsCategory |
§8 erratum 3: the frame belongs to RooCategory, and only below class version 3 (§5) |
src/rootfilespec/container.py:212-214 |
Reads two collection frames for RooVectorDataStore::RealVector::_vec |
§8 erratum 5: one collection frame and one class frame (Collections §3.1) |
src/rootfilespec/bootstrap/assumed.py:75 RooLinkedList |
Reads TObject, _hashThresh: >h, fSize: >i, then slots |
§3.2 / §8 erratum 4: the first two bytes are the version word, _hashThresh is never written, and at versions 2–3 a TString follows the slots. The missing TString is the likely cause of the current classes/roofit.root failure: buffer not empty after reading object of type RooLinkedList |
To do:
- Put the five classes in one per-class table of hand-written readers (see Per-class table for user classes whose recorded StreamerInfo does not describe the bytes (e.g. KM3NETDAQ::JDAQ* in uproot-issue433-splitlevel*.root) #69; Bootstrap §6 says that list can't be closed). Implement §6 there, and remove the inline special cases above.
- Fix
RooLinkedListper §3, across class versions 1–3. -
classes/roofit.root: assert the values itscase.tomlpins (31 assertions; see §9), and remove itsEXPECTED_FAILURESentry. -
uproot-issue49.root,uproot-issue-350.root,stressRooFit_v522_ref.rootandstressRooFit_v534_ref.rootshould read. Per §9, 272 of the corpus's 274 RooFit records decode to exactly their byte count. - Still out of scope because the spec lists them as gaps (
spec/99-appendix/streamers.toml):RooWorkspace::CodeRepo, which leaves oneRooWorkspacerecord per stress file partial, andRooCFunction{1..4}Ref, which no corpus file contains. These should read asUninterpretedby byte count (Class missing from StreamerInfo: return Uninterpreted (skip by byte count) instead of raising #74), not raise.
Severity / size: medium, M. It's more than the original "documented skip", but every class now has a specified layout and a fixture.
Related: #9, #74, #69, #67, ariostas/root-io-spec#1 (items 7–10, corrected by the errata above).
Assisted-by: claude-code:claude-opus-5-5
Activity
Bump reference/root-io-spec to c65c464 (#124)
[-]RooFit classes with hand-written streamers (RooRealVar, ...): documented skip, and move the RooFit special cases out of the framing layer[/-][+]RooFit classes with hand-written streamers (RooRealVar, RooLinkedList, ...): implement per root-io-spec RooFit, replacing the framing-layer special cases[/+]nsmith- commentedon Sep 30, 2026
Rescoped: RooFit is now in scope, because root-io-spec specifies it in
spec/03-classes/RooFit.md, which is already in our pinned submodule (c65c464). I rewrote the body. The main changes:classes/roofit.rootfailure probably comes fromRooLinkedListmissing its trailingTString.The original decision and the notes on the
RooRealVarlayout are in this issue's edit history.Assisted-by: claude-code:claude-opus-5-5