@@ -14,33 +14,37 @@ def test_write_read(self):
1414 model .MakeField ["int" ]("f" )
1515 model .MakeField ["std::string" ]("mystr" )
1616
17+ nentries = 2
1718 with ROOT .RNTupleWriter .Recreate (model , "ntpl" , "test_ntuple_py_write_read.root" ) as writer :
1819 entry = writer .CreateEntry ()
19- entry ["f" ] = 42
20- entry ["mystr" ] = "string stored in RNTuple"
21- writer .Fill (entry )
20+ for i in range (nentries ):
21+ entry ["f" ] = i
22+ entry ["mystr" ] = f"{ i } string stored in RNTuple"
23+ writer .Fill (entry )
2224 # The model should not have been destroyed (a clone has been used).
2325 self .assertFalse (model .IsFrozen ())
2426
25- # Accessing the writer after the context manager is an error
26- with self .assertRaisesRegex (RuntimeError , "cannot access RNTupleWriter after " ):
27+ # Upon exiting the context, the writer is destructed
28+ with self .assertRaisesRegex (ReferenceError , "attempt to access a null-pointer " ):
2729 writer .GetNEntries ()
2830
2931 with ROOT .RNTupleReader .Open ("ntpl" , "test_ntuple_py_write_read.root" ) as reader :
30- self .assertEqual (reader .GetNEntries (), 1 )
32+ self .assertEqual (reader .GetNEntries (), nentries )
3133 entry = reader .CreateEntry ()
32- reader .LoadEntry (0 , entry )
33- self .assertEqual (entry ["f" ], 42 )
34- self .assertEqual (entry ["mystr" ], "string stored in RNTuple" )
35-
36- # Entry values are still accessible after the reader is gone
37- self .assertEqual (entry ["f" ], 42 )
38- self .assertEqual (entry ["mystr" ], "string stored in RNTuple" )
39-
40- # Accessing the reader after the context manager is an error
41- with self .assertRaisesRegex (RuntimeError , "cannot access RNTupleReader after" ):
34+ for i in reader :
35+ reader .LoadEntry (i , entry )
36+ with self .subTest (i = i ):
37+ self .assertEqual (entry ["f" ], i )
38+ self .assertEqual (entry ["mystr" ], f"{ i } string stored in RNTuple" )
39+
40+ # Upon exiting the context, the reader is destructed
41+ with self .assertRaisesRegex (ReferenceError , "attempt to access a null-pointer" ):
4242 reader .GetNEntries ()
4343
44+ # Last entry values are still accessible after the reader is destructed
45+ self .assertEqual (entry ["f" ], nentries - 1 )
46+ self .assertEqual (entry ["mystr" ], f"{ nentries - 1 } string stored in RNTuple" )
47+
4448 def test_write_fields (self ):
4549 """Can create writer with on-the-fly model"""
4650
@@ -73,14 +77,15 @@ def test_append_open(self):
7377 self .assertFalse (model .IsFrozen ())
7478
7579 with ROOT .TFile .Open ("test_ntuple_py_append.root" ) as f :
76- reader = ROOT .RNTupleReader .Open (f ["ntpl" ])
77- self .assertEqual (reader .GetNEntries (), 1 )
78- entry = reader .CreateEntry ()
79- reader .LoadEntry (0 , entry )
80- self .assertEqual (entry ["f" ], 42 )
80+ with ROOT .RNTupleReader .Open (f ["ntpl" ]) as reader :
81+ self .assertEqual (reader .GetNEntries (), 1 )
82+ entry = reader .CreateEntry ()
83+ reader .LoadEntry (0 , entry )
84+ self .assertEqual (entry ["f" ], 42 )
8185
82- # Entry values are still accessible after the reader is gone
83- self .assertEqual (entry ["f" ], 42 )
86+ with self .subTest (repr (reader )):
87+ self .assertFalse (reader , "RNTupleReader destructed" )
88+ self .assertEqual (entry ["f" ], 42 , "Entry values still accessible" )
8489
8590 def test_read_model (self ):
8691 """Can impose a model when reading."""
@@ -100,8 +105,7 @@ def test_read_model(self):
100105 entry = reader .CreateEntry ()
101106 if not platform .system () == "Windows" :
102107 # TODO: re-enable it on Windows once the exception handling is fixed
103- with self .assertRaises (Exception ):
104- # Field f2 does not exist in imposed model
108+ with self .assertRaises (ROOT .RException , msg = "Field f2 does not exist in imposed model" ):
105109 entry ["f2" ] = 42
106110
107111 def test_forbid_writing_wrong_type (self ):
@@ -117,8 +121,12 @@ class WrongClass: ...
117121 with self .assertRaises (TypeError ):
118122 entry ["mystr" ] = WrongClass ()
119123
120- def test_nested_ctxmanager (self ):
121- """Nesting context managers of the same object is an error"""
124+ def test_singleuse_ctxmanager (self ):
125+ """RNTupleReader/RNTupleWriter context managers are single use context managers.
126+
127+ Upon exiting the context, they are destructed.
128+ They are not reentrant - cannot be used in nested 'with' statements,
129+ or are not reusable - cannot be used multiple times."""
122130
123131 try :
124132 fileName = "test_ntuple_nested_ctxmanager_py.root"
@@ -127,49 +135,21 @@ def test_nested_ctxmanager(self):
127135 writer = ROOT .RNTupleWriter .Recreate (model , "ntpl" , fileName )
128136 with writer as w1 :
129137 entry1 = w1 .CreateEntry ()
130- with self .assertRaisesRegex (RuntimeError , "cannot nest `with`" ):
131- with writer as w2 :
132- entry2 = w2 .CreateEntry ()
133- entry1 ["f" ] = 2
134- entry2 ["f" ] = 4
135- w2 .Fill (entry2 )
136- w1 .Fill (entry1 )
137-
138- reader = ROOT .RNTupleReader .Open ("ntpl" , fileName )
139- with reader as r1 :
140- with self .assertRaisesRegex (RuntimeError , "cannot nest `with`" ):
141- with reader as r2 :
142- print (r2 .GetNEntries ())
143- print (r1 .GetNEntries ())
144-
145- finally :
146- import os
147- os .remove (fileName )
148-
149- def test_weird_ctxmanager (self ):
150- """Using an existing object with a context manager"""
151-
152- try :
153- fileName = "test_ntuple_weird_ctxmanager_py.root"
154- model = ROOT .RNTupleModel .Create ()
155- model .MakeField ["int" ]("f" )
156- writer = ROOT .RNTupleWriter .Recreate (model , "ntpl" , fileName )
157- entry = writer .CreateEntry ()
158- with writer as w1 :
159- w1 .Fill (entry )
160-
161- with self .assertRaisesRegex (RuntimeError , "after the `with` statement" ):
162- writer .Fill (entry )
138+ entry1 ["f" ] = 2
139+ with writer as w2 :
140+ entry2 = w2 .CreateEntry ()
141+ entry2 ["f" ] = 4
142+ w2 .Fill (entry2 )
143+ with self .assertRaisesRegex ((ReferenceError , TypeError ), "attempt to access a null-pointer" ):
144+ w1 .Fill (entry1 )
163145
164146 reader = ROOT .RNTupleReader .Open ("ntpl" , fileName )
165147 with reader as r1 :
166- with self . assertRaisesRegex ( RuntimeError , "cannot nest `with`" ) :
167- with reader as r2 :
168- print ( r2 . GetNEntries ())
169- print (r1 .GetNEntries ())
148+ with reader as r2 :
149+ print ( r2 . GetNEntries ())
150+ with self . assertRaisesRegex ( ReferenceError , "attempt to access a null-pointer" ):
151+ print (r1 .GetNEntries ())
170152
171153 finally :
172154 import os
173155 os .remove (fileName )
174-
175-
0 commit comments