Skip to content

Commit 2246984

Browse files
committed
Support track order on group.get()
1 parent bcecf25 commit 2246984

File tree

4 files changed

+113
-24
lines changed

4 files changed

+113
-24
lines changed

h5pyd/_hl/attrs.py

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -343,8 +343,16 @@ def __len__(self):
343343
def __iter__(self):
344344
""" Iterate over the names of attributes. """
345345
if self._objdb_attributes is not None:
346+
if self._parent._track_order:
347+
attrs = sorted(self._objdb_attributes.items(), key=lambda x: x[1]['created'])
348+
else:
349+
attrs = sorted(self._objdb_attributes.items())
350+
351+
ordered_attrs = {}
352+
for a in attrs:
353+
ordered_attrs[a[0]] = a[1]
346354

347-
for name in self._objdb_attributes:
355+
for name in ordered_attrs:
348356
yield name
349357

350358
else:
@@ -384,3 +392,34 @@ def __repr__(self):
384392
if not self._parent.id.id:
385393
return "<Attributes of closed HDF5 object>"
386394
return "<Attributes of HDF5 object at %s>" % id(self._parent.id)
395+
396+
def __reversed__(self):
397+
""" Iterate over the names of attributes in reverse order. """
398+
if self._objdb_attributes is not None:
399+
if self._parent._track_order:
400+
attrs = sorted(self._objdb_attributes.items(), key=lambda x: x[1]['created'])
401+
else:
402+
attrs = sorted(self._objdb_attributes.items())
403+
404+
ordered_attrs = {}
405+
for a in attrs:
406+
ordered_attrs[a[0]] = a[1]
407+
408+
for name in reversed(ordered_attrs):
409+
yield name
410+
411+
412+
else:
413+
# make server request
414+
req = self._req_prefix
415+
# backup over the trailing slash in req
416+
req = req[:-1]
417+
rsp = self._parent.GET(req, params={"CreateOrder": "1" if self._parent._track_order else "0"})
418+
attributes = rsp['attributes']
419+
420+
attrlist = []
421+
for attr in attributes:
422+
attrlist.append(attr['name'])
423+
424+
for name in reversed(attrlist):
425+
yield name

h5pyd/_hl/dataset.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -717,7 +717,7 @@ def allocated_size(self):
717717
self._getVerboseInfo()
718718
return self._allocated_size
719719

720-
def __init__(self, bind):
720+
def __init__(self, bind, track_order=False):
721721
"""Create a new Dataset object by binding to a low-level DatasetID."""
722722

723723
if not isinstance(bind, DatasetID):
@@ -732,6 +732,7 @@ def __init__(self, bind):
732732
# make a numpy dtype out of the type json
733733
self._dtype = createDataType(self.id.type_json)
734734
self._item_size = getItemSize(self.id.type_json)
735+
self._track_order = track_order
735736

736737
self._shape = self.get_shape()
737738

h5pyd/_hl/group.py

Lines changed: 36 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -550,7 +550,7 @@ def require_group(self, name):
550550
raise TypeError("Incompatible object (%s) already exists" % grp.__class__.__name__)
551551
return grp
552552

553-
def getObjByUuid(self, uuid, collection_type=None):
553+
def getObjByUuid(self, uuid, collection_type=None, track_order=False):
554554
""" Utility method to get an obj based on collection type and uuid """
555555
self.log.debug(f"getObjByUuid({uuid})")
556556
obj_json = None
@@ -585,10 +585,10 @@ def getObjByUuid(self, uuid, collection_type=None):
585585
# will need to get JSON from server
586586
req = f"/{collection_type}/{uuid}"
587587
# make server request
588-
obj_json = self.GET(req, params={"CreateOrder": "1" if self._track_order else "0"})
588+
obj_json = self.GET(req, params={"CreateOrder": "1" if track_order else "0"})
589589

590590
if collection_type == 'groups':
591-
tgt = Group(GroupID(self, obj_json))
591+
tgt = Group(GroupID(self, obj_json), track_order=track_order)
592592
elif collection_type == 'datatypes':
593593
tgt = Datatype(TypeID(self, obj_json))
594594
elif collection_type == 'datasets':
@@ -598,13 +598,13 @@ def getObjByUuid(self, uuid, collection_type=None):
598598
if "dims" in shape_json and len(shape_json["dims"]) == 1 and dtype_json["class"] == 'H5T_COMPOUND':
599599
tgt = Table(DatasetID(self, obj_json))
600600
else:
601-
tgt = Dataset(DatasetID(self, obj_json))
601+
tgt = Dataset(DatasetID(self, obj_json), track_order=track_order)
602602
else:
603603
raise IOError(f"Unexpected collection_type: {collection_type}")
604604

605605
return tgt
606606

607-
def __getitem__(self, name):
607+
def __getitem__(self, name, track_order=False):
608608
""" Open an object in the file """
609609
# convert bytes to str for PY3
610610
if isinstance(name, bytes):
@@ -617,11 +617,11 @@ def __getitem__(self, name):
617617
if tgt is not None:
618618
return tgt # ref'd object has not been deleted
619619
if isinstance(name.id, GroupID):
620-
tgt = self.getObjByUuid(name.id.uuid, collection_type="groups")
620+
tgt = self.getObjByUuid(name.id.uuid, collection_type="groups", track_order=track_order)
621621
elif isinstance(name.id, DatasetID):
622-
tgt = self.getObjByUuid(name.id.uuid, collection_type="datasets")
622+
tgt = self.getObjByUuid(name.id.uuid, collection_type="datasets", track_order=track_order)
623623
elif isinstance(name.id, TypeID):
624-
tgt = self.getObjByUuid(name.id.uuid, collection_type="datasets")
624+
tgt = self.getObjByUuid(name.id.uuid, collection_type="datasets", track_order=track_order)
625625
else:
626626
raise IOError("Unexpected Error - ObjectID type: " + name.__class__.__name__)
627627
return tgt
@@ -634,11 +634,11 @@ def __getitem__(self, name):
634634
link_class = link_json['class']
635635

636636
if link_class == 'H5L_TYPE_HARD':
637-
tgt = self.getObjByUuid(link_json['id'], collection_type=link_json['collection'])
637+
tgt = self.getObjByUuid(link_json['id'], collection_type=link_json['collection'], track_order=track_order)
638638
elif link_class == 'H5L_TYPE_SOFT':
639639
h5path = link_json['h5path']
640640
soft_parent_uuid, soft_json = self._get_link_json(h5path)
641-
tgt = self.getObjByUuid(soft_json['id'], collection_type=soft_json['collection'])
641+
tgt = self.getObjByUuid(soft_json['id'], collection_type=soft_json['collection'], track_order=track_order)
642642

643643
elif link_class == 'H5L_TYPE_EXTERNAL':
644644
# try to get a handle to the file and return the linked object...
@@ -654,7 +654,8 @@ def __getitem__(self, name):
654654
endpoint = self.id.http_conn.endpoint
655655
username = self.id.http_conn.username
656656
password = self.id.http_conn.password
657-
f = File(external_domain, endpoint=endpoint, username=username, password=password, mode='r')
657+
f = File(external_domain, endpoint=endpoint, username=username, password=password, mode='r',
658+
track_order=track_order)
658659
except IOError:
659660
# unable to find external link
660661
raise KeyError("Unable to open file: " + link_json['h5domain'])
@@ -678,7 +679,7 @@ def __getitem__(self, name):
678679
tgt._name = name
679680
return tgt
680681

681-
def get(self, name, default=None, getclass=False, getlink=False):
682+
def get(self, name, default=None, getclass=False, getlink=False, track_order=False):
682683
""" Retrieve an item or other information.
683684
684685
"name" given only:
@@ -702,18 +703,17 @@ def get(self, name, default=None, getclass=False, getlink=False):
702703
>>> if cls == SoftLink:
703704
... print '"foo" is a soft link!'
704705
"""
705-
706706
if not (getclass or getlink):
707707
try:
708-
return self[name]
708+
return self.__getitem__(name, track_order)
709709
except KeyError:
710710
return default
711711

712712
if name not in self:
713713
return default
714714

715715
elif getclass and not getlink:
716-
obj = self.__getitem__(name)
716+
obj = self.__getitem__(name, track_order)
717717
if obj is None:
718718
return None
719719
if obj.id.__class__ is GroupID:
@@ -891,7 +891,16 @@ def __iter__(self):
891891
for x in links:
892892
yield x['title']
893893
else:
894-
for name in links:
894+
if self._track_order:
895+
links = sorted(links.items(), key=lambda x: x[1]['created'])
896+
else:
897+
links = sorted(links.items())
898+
899+
ordered_links = {}
900+
for link in links:
901+
ordered_links[link[0]] = link[1]
902+
903+
for name in ordered_links:
895904
yield name
896905

897906
def __contains__(self, name):
@@ -1151,14 +1160,23 @@ def __reversed__(self):
11511160

11521161
# reset the link cache
11531162
self._link_db = {}
1154-
for link in reversed(links):
1163+
for link in links:
11551164
name = link["title"]
11561165
self._link_db[name] = link
11571166

11581167
for x in reversed(links):
11591168
yield x['title']
11601169
else:
1161-
for name in links:
1170+
if self._track_order:
1171+
links = sorted(links.items(), key=lambda x: x[1]['created'])
1172+
else:
1173+
links = sorted(links.items())
1174+
1175+
ordered_links = {}
1176+
for link in links:
1177+
ordered_links[link[0]] = link[1]
1178+
1179+
for name in reversed(ordered_links):
11621180
yield name
11631181

11641182

test/hl/test_group.py

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -342,15 +342,18 @@ def test_get_dataset_track_order(self):
342342
print(f"filename: {filename}")
343343
self.f = h5py.File(filename, 'w')
344344
g = self.f.create_group('order')
345-
# create dataset, close file, re-open and get dataset
345+
346346
dset = g.create_dataset('dset', (10,), dtype='i4')
347347
dset2 = g.create_dataset('dset2', (10,), dtype='i4')
348348

349+
self.populate_attrs(dset)
350+
self.populate_attrs(dset2)
351+
349352
self.f.close()
350-
f = h5py.File(filename, 'r')
351-
g = f['order']
352-
d = g.get('dset', track_order=True)
353+
self.f = h5py.File(filename, 'r')
354+
g = self.f['order']
353355

356+
d = g.get('dset', track_order=True)
354357
ref = [str(i) for i in range(100)]
355358
self.assertEqual(list(d.attrs), ref)
356359
self.assertEqual(list(reversed(d.attrs)), list(reversed(ref)))
@@ -360,6 +363,34 @@ def test_get_dataset_track_order(self):
360363
self.assertEqual(list(d2.attrs), ref)
361364
self.assertEqual(list(reversed(d2.attrs)), list(reversed(ref)))
362365

366+
def test_get_group_track_order(self):
367+
filename = self.getFileName("test_get_group_track_order")
368+
print(f"filename: {filename}")
369+
self.f = h5py.File(filename, 'w')
370+
g = self.f.create_group('order')
371+
372+
# create subgroup and populate it with links
373+
g.create_group('subgroup')
374+
self.populate(g['subgroup'])
375+
376+
self.f.close()
377+
self.f = h5py.File(filename, 'r')
378+
g = self.f['order']
379+
380+
subg = g.get('subgroup', track_order=True)
381+
ref = [str(i) for i in range(100)]
382+
self.assertEqual(list(subg), ref)
383+
self.assertEqual(list(reversed(subg)), list(reversed(ref)))
384+
385+
self.f.close()
386+
self.f = h5py.File(filename, 'r')
387+
g = self.f['order']
388+
subg2 = g.get('subgroup', track_order=False)
389+
ref = sorted([str(i) for i in range(100)])
390+
self.assertEqual(list(subg2), ref)
391+
self.assertEqual(list(reversed(subg2)), list(reversed(ref)))
392+
393+
363394
if __name__ == '__main__':
364395
loglevel = logging.ERROR
365396
logging.basicConfig(format='%(asctime)s %(message)s', level=loglevel)

0 commit comments

Comments
 (0)