Skip to content

Commit 7c393b6

Browse files
committed
revert to using members for dtype enums
1 parent af4d46a commit 7c393b6

File tree

6 files changed

+125
-39
lines changed

6 files changed

+125
-39
lines changed

src/h5json/hdf5db.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,14 @@ def is_dirty(self, obj_id):
9999
return True
100100
return obj_id in self._dirty_objects
101101

102+
@property
103+
def new_objects(self):
104+
return self._new_objects
105+
106+
@property
107+
def dirty_objects(self):
108+
return self._dirty_objects
109+
102110
def make_dirty(self, obj_id):
103111
""" Mark the object as dirty and update the lastModified timestamp """
104112
if self.is_new(obj_id):

src/h5json/hdf5dtype.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -424,9 +424,11 @@ def getTypeItem(dt, metadata=None):
424424
if dt.base.byteorder == ">":
425425
byteorder = "BE"
426426
# this mapping is an h5py convention for boolean support
427-
mapping = {"FALSE": 0, "TRUE": 1}
427+
bool_false = {"name": "FALSE", "value": 0}
428+
bool_true = {"name": "TRUE", "value": 1}
429+
members = [bool_false, bool_true]
428430
type_info["class"] = "H5T_ENUM"
429-
type_info["mapping"] = mapping
431+
type_info["members"] = members
430432
base_info = {"class": "H5T_INTEGER"}
431433
base_info["base"] = "H5T_STD_I8" + byteorder
432434
type_info["base"] = base_info
@@ -456,7 +458,13 @@ def getTypeItem(dt, metadata=None):
456458
# yes, this is an enum!
457459
mapping = metadata["enum"]
458460
type_info["class"] = "H5T_ENUM"
459-
type_info["mapping"] = mapping
461+
members = []
462+
for name in mapping:
463+
value = mapping[name]
464+
item = {"name": name, "value": value}
465+
members.append(item)
466+
type_info["members"] = members
467+
#type_info["mapping"] = mapping
460468
if dt.name not in predefined_int_types:
461469
raise TypeError("Unexpected integer type: " + dt.name)
462470
# maps to one of the HDF5 predefined types

src/h5json/reader/h5py_reader.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ def getAttribute(self, obj_id, name, include_data=True):
9696
addr = h5py.h5o.get_info(typeid).addr
9797
type_uuid = self.getObjIdByAddress(addr)
9898
committedType = self._id_map[type_uuid]
99-
type_item = committedType["type"].copy()
99+
type_item = getTypeItem(committedType.dtype)
100100
type_item["id"] = type_uuid
101101
else:
102102
type_item = getTypeItem(attrObj.dtype)
@@ -353,7 +353,7 @@ def getObjectById(self, obj_id, include_attrs=True, include_links=True):
353353
elif isinstance(h5obj, h5py.Dataset):
354354
obj_json = self._getDataset(h5obj)
355355
elif isinstance(h5obj, h5py.Datatype):
356-
obj_json = self._getDataType(h5obj)
356+
obj_json = self._getDatatype(h5obj)
357357
else:
358358
raise TypeError(f"unexpected object type: {type(h5obj)}")
359359

src/h5json/writer/h5py_writer.py

Lines changed: 51 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,8 @@ def __init__(
4545
def _createGroup(self, parent, grp_json, name=None):
4646
""" create the group and any links it contains """
4747
grp = parent.create_group(name)
48-
if "links" in grp_json:
49-
grp_links = grp_json["links"]
50-
self._createObjects(grp, grp_links)
48+
return grp
49+
5150

5251
def _createDataset(self, parent, dset_json, name=None):
5352
""" create a dataset object """
@@ -156,58 +155,85 @@ def _createDataset(self, parent, dset_json, name=None):
156155
else:
157156
self.log.info(f"Unexpected filter name: {filter_alias}, ignoring")
158157

159-
parent.create_dataset(name, **kwargs)
158+
dset = parent.create_dataset(name, **kwargs)
159+
return dset
160160

161161
def _createDatatype(self, parent, ctype_json, name=None):
162162
""" create a datatype object """
163163

164164
type_item = ctype_json["type"]
165165
dtype = createDataType(type_item)
166166
parent[name] = dtype
167+
return parent[name]
167168

168169

169-
def _createObjects(self, parent, links_json):
170+
def _createObjects(self, parent, links_json, visited=set()):
170171
""" create child object in the given group, recurse for any sub-groups """
172+
171173
for title in links_json:
172-
if title in parent:
173-
# TBD: this will do the wrong thing if the link tgt has changed
174-
continue
174+
#if title in parent:
175+
# # TBD: this will do the wrong thing if the link tgt has changed
176+
# continue
175177
link_json = links_json[title]
176178
link_class = link_json["class"]
177-
if link_class == "H5L_TYPE_SOFT":
179+
if link_class == "H5L_TYPE_SOFT" and title not in parent:
178180
h5path = link_json["h5path"]
179181
parent[title] = h5py.SoftLink(h5path)
180-
elif link_class == "H5L_TYPE_EXTERNAL":
182+
elif link_class == "H5L_TYPE_EXTERNAL" and title not in parent:
181183
h5path = link_json["h5path"]
182184
filename = link_json["file"]
183185
parent[title] = h5py.ExternalLink(filename, h5path)
184-
elif link_class == "H5L_TYPE_USER_DEFINED":
186+
elif link_class == "H5L_TYPE_USER_DEFINED" and title not in parent:
185187
self.log.warning("unable to create user-defined link: {title}")
186188
elif link_class == "H5L_TYPE_HARD":
187189
tgt_id = link_json["id"]
190+
"""
191+
if tgt_id in visited:
192+
# we've already processed this object
193+
if title not in parent:
194+
if tgt_id in self._id_map:
195+
tgt_obj = self._id_map[tgt_id]
196+
parent[title] = tgt_obj
197+
else:
198+
self.log.warning("h5py_writer - expected to find {tgt_id} in id_map")
199+
continue
200+
"""
201+
202+
collection = getCollectionForId(tgt_id)
203+
204+
obj_json = self.db.getObjectById(tgt_id)
205+
188206
if tgt_id in self._id_map:
207+
# object has already been created
189208
tgt_path = self._id_map[tgt_id]
190209
tgt_obj = parent[tgt_path]
191-
parent[title] = tgt_obj
210+
if title not in parent:
211+
parent[title] = tgt_obj
212+
if collection == "groups" and tgt_id not in visited:
213+
# recurse over sub-objects to pick up any new links
214+
grp_links = obj_json["links"]
215+
visited.add(tgt_id)
216+
self._createObjects(tgt_obj, grp_links, visited=visited)
192217
else:
193-
obj_json = self.db.getObjectById(tgt_id)
194218
parent_path = parent.name
195219
if parent_path[-1] != '/':
196220
parent_path += '/'
197221
self._id_map[tgt_id] = parent_path + title
198-
collection = getCollectionForId(tgt_id)
199222
kwds = {"name": title}
200223
if collection == "groups":
201-
tgt_obj = self._createGroup(parent, obj_json, **kwds)
224+
tgt_grp = self._createGroup(parent, obj_json, **kwds)
225+
if "links" in obj_json:
226+
grp_links = obj_json["links"]
227+
visited.add(tgt_id)
228+
self._createObjects(tgt_grp, grp_links, visited=visited)
202229
elif collection == "datasets":
203-
tgt_obj = self._createDataset(parent, obj_json, **kwds)
230+
self._createDataset(parent, obj_json, **kwds)
204231
elif collection == "datatypes":
205-
tgt_obj = self._createDatatype(parent, obj_json, **kwds)
232+
self._createDatatype(parent, obj_json, **kwds)
206233
else:
207234
self.log.warning(f"unexpected collection: {collection}")
208-
tgt_obj = None
209-
if tgt_obj:
210-
parent[title] = tgt_obj
235+
visited.add(tgt_id)
236+
211237
else:
212238
self.log.warning(f"unexpected link class: {link_class}")
213239

@@ -231,7 +257,6 @@ def updateDatasetValues(self, dset_id, dset):
231257

232258
def createAttribute(self, obj, name, attr_json):
233259
""" add the given attribute to obj """
234-
print(f"h5py_writer.createAttribute {obj.name}: {name}")
235260

236261
dtype = createDataType(attr_json["type"])
237262
shape_json = attr_json["shape"]
@@ -276,10 +301,11 @@ def flush(self):
276301
root_id = self.db.root_id
277302
self._id_map[root_id] = "/"
278303
with h5py.File(self._filepath, mode=self._mode) as f:
279-
root_json = self.db.getObjectById(root_id)
280-
if "links" in root_json:
281-
root_links = root_json["links"]
282-
self._createObjects(f, root_links)
304+
if self.db.new_objects:
305+
root_json = self.db.getObjectById(root_id)
306+
if "links" in root_json:
307+
root_links = root_json["links"]
308+
self._createObjects(f, root_links, visited=set(root_id))
283309
# update attributes, dataset values
284310
for obj_id in self._id_map:
285311
if self.db.is_dirty(obj_id):

test/unit/h5py_writer_test.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,44 @@ def testGroup(self):
9090
g2 = f["g2"]
9191
self.assertTrue("extlink" in g2)
9292
self.assertTrue("slink" in g2)
93+
94+
db.createAttribute(g1_id, "a2", "bye-bye")
95+
db.flush()
96+
97+
with h5py.File(filepath) as f:
98+
g1 = f["g1"]
99+
self.assertEqual(len(g1.attrs), 2)
100+
self.assertTrue("a1" in g1.attrs)
101+
self.assertTrue("a2" in g1.attrs)
102+
103+
print("create group /g2/g2.1")
104+
g21 = db.createGroup()
105+
db.createHardLink(g2_id, "g2.1", g21)
106+
db.flush()
107+
108+
with h5py.File(filepath) as f:
109+
g2 = f["g2"]
110+
self.assertTrue("g2.1" in g2)
111+
112+
sel = selections.select((10, 10), (slice(4, 5), slice(4, 5)))
113+
arr = np.zeros((), dtype=np.int32)
114+
arr[()] = 42
115+
db.setDatasetValues(dset_111_id, sel, arr)
116+
db.flush()
117+
118+
with h5py.File(filepath) as f:
119+
dset = f["/g1/g1.1/dset1.1.1"]
120+
for i in range(10):
121+
for j in range(10):
122+
if i == 4 and j == 4:
123+
# this is the one element that was updated
124+
expected = 42
125+
else:
126+
expected = i * j
127+
self.assertEqual(dset[i, j], expected)
128+
129+
130+
93131

94132

95133

test/unit/hdf5dtype_test.py

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -125,8 +125,10 @@ def testBaseEnumTypeItem(self):
125125
baseItem = typeItem["base"]
126126
self.assertEqual(baseItem["class"], "H5T_INTEGER")
127127
self.assertEqual(baseItem["base"], "H5T_STD_I8LE")
128-
self.assertTrue("mapping" in typeItem)
129-
self.assertEqual(typeItem["mapping"]["GREEN"], 1)
128+
self.assertTrue("members" in typeItem)
129+
members = typeItem["members"]
130+
expected = [{'name': 'RED', 'value': 0}, {'name': 'GREEN', 'value': 1}, {'name': 'BLUE', 'value': 2}]
131+
self.assertEqual(members, expected)
130132
self.assertEqual(typeSize, 1)
131133

132134
def testBaseBoolTypeItem(self):
@@ -136,11 +138,11 @@ def testBaseBoolTypeItem(self):
136138
baseItem = typeItem["base"]
137139
self.assertEqual(baseItem["class"], "H5T_INTEGER")
138140
self.assertEqual(baseItem["base"], "H5T_STD_I8LE")
139-
self.assertTrue("mapping" in typeItem)
140-
mapping = typeItem["mapping"]
141-
self.assertEqual(len(mapping), 2)
142-
self.assertEqual(mapping["FALSE"], 0)
143-
self.assertEqual(mapping["TRUE"], 1)
141+
self.assertTrue("members" in typeItem)
142+
members = typeItem["members"]
143+
self.assertEqual(len(members), 2)
144+
self.assertEqual(members[0], {"name": "FALSE", "value": 0})
145+
self.assertEqual(members[1], {"name": "TRUE", "value": 1})
144146
self.assertEqual(typeSize, 1)
145147

146148
def testBaseArrayTypeItem(self):
@@ -205,8 +207,12 @@ def testEnumArrayTypeItem(self):
205207
self.assertEqual(typeItem["dims"], (2, 3))
206208
baseItem = typeItem["base"]
207209
self.assertEqual(baseItem["class"], "H5T_ENUM")
208-
self.assertTrue("mapping" in baseItem)
209-
self.assertEqual(baseItem["mapping"]["GREEN"], 1)
210+
self.assertTrue("members" in baseItem)
211+
members = baseItem["members"]
212+
self.assertEqual(len(members), 3)
213+
self.assertEqual(members[0], {"name": "RED", "value": 0})
214+
self.assertEqual(members[1], {"name": "GREEN", "value": 1})
215+
self.assertEqual(members[2], {"name": "BLUE", "value": 2})
210216
self.assertTrue("base" in baseItem)
211217
basePrim = baseItem["base"]
212218
self.assertEqual(basePrim["class"], "H5T_INTEGER")

0 commit comments

Comments
 (0)