[h5py] 63/455: More unit tests, new highlevel classes
Ghislain Vaillant
ghisvail-guest at moszumanska.debian.org
Thu Jul 2 18:19:18 UTC 2015
This is an automated email from the git hooks/post-receive script.
ghisvail-guest pushed a commit to annotated tag 1.3.0
in repository h5py.
commit 3a3c9866cb97a12c558d865bf2f8c4bd82f0642e
Author: andrewcollette <andrew.collette at gmail.com>
Date: Thu Jul 3 01:14:32 2008 +0000
More unit tests, new highlevel classes
---
h5py/__init__.py | 3 +-
h5py/h5.pyx | 7 +-
h5py/h5a.pyx | 20 ++
h5py/h5f.pyx | 20 +-
h5py/h5g.pyx | 34 +++
h5py/h5s.pyx | 2 +-
h5py/h5z.pyx | 20 --
h5py/highlevel.py | 584 +++++++++++++++----------------------------------
h5py/tests/__init__.py | 8 +-
h5py/tests/test_h5.py | 5 +-
h5py/tests/test_h5a.py | 22 ++
h5py/tests/test_h5f.py | 2 +
h5py/tests/test_h5g.py | 5 +-
h5py/tests/test_h5s.py | 167 +++++---------
h5py/utils_hl.py | 108 +++++++++
15 files changed, 449 insertions(+), 558 deletions(-)
diff --git a/h5py/__init__.py b/h5py/__init__.py
index a398ff4..cfb9ece 100644
--- a/h5py/__init__.py
+++ b/h5py/__init__.py
@@ -22,7 +22,8 @@ __doc__ = \
See the docstring for the "version" module for a longer introduction.
"""
-import utils, h5, h5f, h5g, h5s, h5t, h5d, h5a, h5p, h5z, h5i
+import utils, h5, h5f, h5g, h5s, h5t, h5d, h5a, h5p, h5z, h5i, highlevel
import version
__doc__ = __doc__ % version.version
+
diff --git a/h5py/h5.pyx b/h5py/h5.pyx
index 92c7b7d..3add6ad 100644
--- a/h5py/h5.pyx
+++ b/h5py/h5.pyx
@@ -61,11 +61,16 @@ cdef class ObjectID:
def __dealloc__(self):
""" Automatically decrefs the ID, if it's valid. """
+ print "Dealloc"
if (not self._locked) and (H5Iget_type(self.id) != H5I_BADID):
H5Idec_ref(self.id)
def __copy__(self):
- """ Create another object wrapper which points to the same id. """
+ """ Create another object wrapper which points to the same id.
+
+ WARNING: Locks (i.e. datatype lock() methods) do NOT work correctly
+ across copies.
+ """
cdef ObjectID copy
copy = type(self)(self.id)
assert typecheck(copy, ObjectID), "ObjectID copy encountered invalid type"
diff --git a/h5py/h5a.pyx b/h5py/h5a.pyx
index f89de2a..765aa25 100644
--- a/h5py/h5a.pyx
+++ b/h5py/h5a.pyx
@@ -112,6 +112,26 @@ def iterate(ObjectID loc not None, object func, object data=None, int startidx=0
H5Aiterate(loc.id, &i, <H5A_operator_t>iter_cb, int_tpl)
+cdef herr_t list_cb(hid_t loc_id, char *attr_name, object listin):
+
+ cdef list thelist
+ thelist = listin
+
+ thelist.append(attr_name)
+ return 0
+
+def py_listattrs(ObjectID loc not None):
+ """ (ObjectID loc) => LIST attr_names
+
+ Get a list of the names of the attributes attached to an object.
+ """
+ cdef list retlist
+ cdef unsigned int i
+ i = 0
+ retlist = []
+ H5Aiterate(loc.id, &i, <H5A_operator_t>list_cb, retlist)
+ return retlist
+
# === Attribute class & methods ===============================================
cdef class AttrID(ObjectID):
diff --git a/h5py/h5f.pyx b/h5py/h5f.pyx
index 7765eb9..3c25fda 100644
--- a/h5py/h5f.pyx
+++ b/h5py/h5f.pyx
@@ -77,14 +77,6 @@ def create(char* name, int flags=H5F_ACC_TRUNC, PropFCID createlist=None,
access_id = pdefault(accesslist)
return FileID(H5Fcreate(name, flags, create_id, access_id))
-def is_hdf5(char* name):
- """ (STRING name) => BOOL is_hdf5
-
- Determine if a given file is an HDF5 file. Note this raises an
- exception if the file doesn't exist.
- """
- return pybool(H5Fis_hdf5(name))
-
def flush(ObjectID obj not None, int scope=H5F_SCOPE_LOCAL):
""" (ObjectID obj, INT scope=SCOPE_LOCAL)
@@ -96,6 +88,14 @@ def flush(ObjectID obj not None, int scope=H5F_SCOPE_LOCAL):
"""
H5Fflush(obj.id, <H5F_scope_t>scope)
+def is_hdf5(char* name):
+ """ (STRING name) => BOOL is_hdf5
+
+ Determine if a given file is an HDF5 file. Note this raises an
+ exception if the file doesn't exist.
+ """
+ return pybool(H5Fis_hdf5(name))
+
def mount(ObjectID loc not None, char* name, FileID fid not None,
PropMID mountlist=None):
""" (ObjectID loc, STRING name, FileID fid, PropMID mountlist=None)
@@ -207,6 +207,10 @@ cdef class FileID(ObjectID):
"""
Represents an HDF5 file identifier.
"""
+ property name:
+ """ File name on disk (according to h5f.get_name()) """
+ def __get__(self):
+ return get_name(self)
def close(self):
""" ()
diff --git a/h5py/h5g.pyx b/h5py/h5g.pyx
index 6a8a0a8..c12768a 100644
--- a/h5py/h5g.pyx
+++ b/h5py/h5g.pyx
@@ -55,6 +55,33 @@ cdef class GroupStat:
cdef readonly time_t mtime
cdef readonly size_t linklen
+cdef class GroupIter:
+
+ """
+ Iterator over the names of group members. After this iterator is
+ exhausted, it releases its reference to the group ID.
+ """
+
+ cdef unsigned long idx
+ cdef unsigned long nobjs
+ cdef GroupID grp
+
+ def __init__(self, GroupID grp not None):
+ self.idx = 0
+ self.grp = grp
+ self.nobjs = grp.get_num_objs()
+
+ def __iter__(self):
+ return self
+
+ def __next__(self):
+ if self.idx == self.nobjs:
+ self.grp = None
+ raise StopIteration
+
+ retval = self.grp.get_objname_by_idx(self.idx)
+ self.idx = self.idx + 1
+ return retval
# === Basic group management ==================================================
@@ -315,4 +342,11 @@ cdef class GroupID(ObjectID):
return False
return True
+ def py_iter(self):
+ """ () => ITERATOR
+
+ Return an iterator over the names of group members.
+ """
+ return GroupIter(self)
+
diff --git a/h5py/h5s.pyx b/h5py/h5s.pyx
index 2deb75f..e3944a8 100644
--- a/h5py/h5s.pyx
+++ b/h5py/h5s.pyx
@@ -63,7 +63,7 @@ SEL_ALL = H5S_SEL_ALL
def create(int class_code):
""" (INT class_code) => SpaceID
- Create a new HDF5 dataspace object, of the given class.
+ Create a new HDF5 dataspace object, of the given class.
Legal values are SCALAR and SIMPLE.
"""
return SpaceID(H5Screate(<H5S_class_t>class_code))
diff --git a/h5py/h5z.pyx b/h5py/h5z.pyx
index d662da3..39a6938 100644
--- a/h5py/h5z.pyx
+++ b/h5py/h5z.pyx
@@ -88,26 +88,6 @@ def get_filter_info(int filter_code):
H5Zget_filter_info(<H5Z_filter_t>filter_id, &flags)
return flags
-# === Python extensions =======================================================
-
-PY_FILTER = DDict({ H5Z_FILTER_ERROR: 'ERROR', H5Z_FILTER_NONE: 'NONE',
- H5Z_FILTER_ALL: 'ALL', H5Z_FILTER_DEFLATE: 'DEFLATE',
- H5Z_FILTER_SHUFFLE: 'SHUFFLE', H5Z_FILTER_FLETCHER32: 'FLETCHER32',
- H5Z_FILTER_SZIP: 'SZIP', H5Z_FILTER_RESERVED: 'RESERVED'})
-
-PY_FLAG = DDict({ H5Z_FLAG_DEFMASK: 'DEFMASK', H5Z_FLAG_MANDATORY: 'MANDATORY',
- H5Z_FLAG_OPTIONAL: 'OPTIONAL', H5Z_FLAG_INVMASK: 'INVMASK',
- H5Z_FLAG_REVERSE: 'REVERSE', H5Z_FLAG_SKIP_EDC: 'SKIP EDC' })
-
-PY_FILTER_CONFIG = DDict({H5Z_FILTER_CONFIG_ENCODE_ENABLED: 'ENCODE ENABLED',
- H5Z_FILTER_CONFIG_DECODE_ENABLED: 'ENCODE DISABLED'})
-
-PY_EDC = DDict({H5Z_ERROR_EDC: 'ERROR', H5Z_DISABLE_EDC: 'DISABLE EDC',
- H5Z_ENABLE_EDC: 'ENABLE EDC', H5Z_NO_EDC: 'NO EDC' })
-
-
-
-
diff --git a/h5py/highlevel.py b/h5py/highlevel.py
index 975a9c5..dcd5327 100644
--- a/h5py/highlevel.py
+++ b/h5py/highlevel.py
@@ -10,63 +10,164 @@
#
#-
-"""
- Provides high-level Python objects for HDF5 files, groups, and datasets.
+import copy
+import numpy
+
+from h5py import h5, h5f, h5g, h5s, h5t, h5d, h5a, h5p, h5z, h5i
+from utils_hl import slicer
- Highlights:
- - Groups provide dictionary-like __getitem__ access to their members, and
- allow iteration over member names. File objects also perform these
- operations, implicitly on the root ('/') group.
+class Group(object):
+
+ #: Provides access to HDF5 attributes. See AttributeManager docstring.
+ attrs = property(lambda self: self._attrs)
+
+ def __init__(self, parent_object, name, create=False):
+ """ Create a new Group object, from a parent object and a name.
- - Datasets support Numpy dtype and shape access, along with read/write
- access to the underlying HDF5 dataset (including slicing for partial I/0),
- and reading/writing specified fields of a compound type object.
+ If "create" is False (default), try to open the given group,
+ raising an exception if it doesn't exist. If "create" is True,
+ create a new HDF5 group and link it into the parent group.
+ """
+ if create:
+ self.id = h5g.create(parent_object.id, name)
+ else:
+ self.id = h5g.open(parent_object.id, name)
- - Both group and dataset attributes can be accessed via a dictionary-style
- attribute manager (group_obj.attrs and dataset_obj.attrs). See the
- highlevel.AttributeManager docstring for more information.
+ self._attrs = AttributeManager(self)
- - Named datatypes are reached through the NamedType class, which allows
- attribute access like Group and Dataset objects.
+ def __setitem__(self, name, obj):
+ """ Add the given object to the group. Here are the rules:
- - An interactive command-line utility "browse(filename)" allows access to
- HDF5 datasets, with commands like "cd, ls", etc. Also allows you to
- import groups and datasets directly into Python as highlevel.Group and
- highlevel.Dataset objects.
+ 1. If "obj" is a Dataset or Group object, a hard link is created
+ in this group which points to the given object.
- - There are no "h5py.highlevel" exceptions; classes defined here raise
- native Python exceptions. However, they will happily propagate
- exceptions from the underlying h5py.h5* modules, which are (mostly)
- subclasses of h5py.errors.H5Error.
-"""
+ 2. If "obj" is a Numpy ndarray, it is converted to a dataset
+ object, with default settings (contiguous storage, etc.).
-__revision__ = "$Id$"
+ 3. If "obj" is anything else, attempt to convert it to an ndarray
+ and store it. Scalar values are stored as scalar datasets.
+ Raise ValueError if we can't understand the resulting array
+ dtype.
+ """
+ if isinstance(obj, Group) or isinstance(obj, Dataset):
+ self.id.link(name, h5i.get_name(obj.id), link_type=h5g.LINK_HARD)
+ else:
+ if not isinstance(obj, numpy.ndarray):
+ obj = numpy.array(obj)
+ dset = Dataset(self, name, data=obj)
+ dset.close()
-import os
-import cmd
-import random
-import string
-import numpy
+ def __getitem__(self, name):
+ """ Open an object attached to this group. """
+
+ info = self.id.get_objinfo(name)
+
+ if info.type == h5g.DATASET:
+ dset = Dataset(self, name)
+ if dset.shape == ():
+ return dset[...]
+ return dset
+
+ elif info.type == h5g.GROUP:
+ return Group(self, name)
+
+ elif info.type == h5g.TYPE:
+ return Datatype(self, name)
+
+ raise ValueError("Don't know how to open object of type %d" % info.type)
+
+ def __delitem__(self, name):
+ """ Delete (unlink) an item from this group. """
+ self.id.unlink(name)
+
+ def __len__(self):
+ return self.id.get_num_objs()
+
+ def __iter__(self):
+ return self.id.py_iter()
+
+ def __str__(self):
+ return 'Group (%d members): ' % len(self) + ', '.join(['"%s"' % name for name in self])
+
+ def __repr__(self):
+ return str(self)
+
+ def iteritems(self):
+ for name in self:
+ return (name, self[name])
-import h5f
-import h5g
-import h5i
-import h5d
-import h5t
-import h5a
-import h5p
-from h5e import H5Error
+class File(Group):
-# === Main classes (Dataset/Group/File) =======================================
+ """ Represents an HDF5 file on disk.
+
+ Created with standard Python syntax File(name, mode), where mode may be
+ one of r, r+, w, w+, a.
+
+ File objects inherit from Group objects; Group-like methods all
+ operate on the HDF5 root group ('/'). Like Python file objects, you
+ must close the file ("obj.close()") when you're done with it.
+ """
+ name = property(lambda self: self._name)
+ mode = property(lambda self: self._mode)
+
+ _modes = ('r','r+','w','w+','a')
+
+ # --- Public interface (File) ---------------------------------------------
+
+ def __init__(self, name, mode, noclobber=False):
+ """ Create a new file object.
+
+ Valid modes (like Python's file() modes) are:
+ - 'r' Readonly, file must exist
+ - 'r+' Read/write, file must exist
+ - 'w' Write, create/truncate file
+ - 'w+' Read/write, create/truncate file
+ - 'a' Read/write, file must exist (='r+')
+
+ If "noclobber" is specified, file truncation (w/w+) will fail if
+ the file already exists. Note this is NOT the default.
+ """
+ if not mode in self._modes:
+ raise ValueError("Invalid mode; must be one of %s" % ', '.join(self._modes))
+
+ plist = h5p.create(h5p.FILE_ACCESS)
+ plist.set_fclose_degree(h5f.CLOSE_STRONG)
+ if mode == 'r':
+ self.fid = h5f.open(name, h5f.ACC_RDONLY, accesslist=plist)
+ elif 'r' in mode or 'a' in mode:
+ self.fid = h5f.open(name, h5f.ACC_RDWR, accesslist=plist)
+ elif noclobber:
+ self.fid = h5f.create(name, h5f.ACC_EXCL, accesslist=plist)
+ else:
+ self.fid = h5f.create(name, h5f.ACC_TRUNC, accesslist=plist)
+
+ self.id = self.fid # So the Group constructor can find it.
+ Group.__init__(self, self, '/')
+
+ self._name = name
+ self._mode = mode
+
+ def close(self):
+ """ Close this HDF5 file. All open identifiers will become invalid.
+ """
+ self.id.close()
+ self.fid.close()
+
+ def flush(self):
+ h5f.flush(self.fid)
+
+ def __str__(self):
+ return 'File "%s", root members: %s' % (self.name, ', '.join(['"%s"' % name for name in self]))
+
+ def __repr_(self):
+ return str(self)
class Dataset(object):
""" High-level interface to an HDF5 dataset
"""
- # --- Properties (Dataset) ------------------------------------------------
-
shape = property(lambda self: self.id.shape,
doc = "Numpy-style shape tuple giving dataset dimensions")
@@ -76,8 +177,6 @@ class Dataset(object):
attrs = property(lambda self: self._attrs,
doc = "Provides access to HDF5 attributes")
- # --- Public interface (Dataset) ------------------------------------------
-
def __init__(self, group, name,
data=None, dtype=None, shape=None,
chunks=None, compression=None, shuffle=False, fletcher32=False):
@@ -175,11 +274,11 @@ class Dataset(object):
fspace.select_hyperslab(start, count, stride)
mspace = h5s.create_simple(count)
- arr = ndarray(count, mtype.dtype)
+ arr = numpy.ndarray(count, mtype.dtype)
self.id.read(mspace, fspace, arr)
- if len(names) == 1
+ if len(names) == 1:
# Match Numpy convention for recarray indexing
return arr[names[0]]
return arr
@@ -191,225 +290,26 @@ class Dataset(object):
array's datatype.
"""
val = args[-1]
- start, count, stride, names = slicer(val.shape, args[:-1])
- if len(names) > 0:
- raise ValueError("Field names are not allowed for write.")
-
- self.id.
- h5d.py_write_slab(self.id, args[-1], start, stride)
-
-
- def __str__(self):
- return 'Dataset: '+str(self.shape)+' '+repr(self.dtype)
-
- def __repr__(self):
- return self.__str__()
-
-class Group(object):
- """ Represents an HDF5 group object
-
- Group members are accessed through dictionary-style syntax. Iterating
- over a group yields the names of its members, while the method
- iteritems() yields (name, value) pairs. Examples:
-
- highlevel_obj = group_obj["member_name"]
- member_list = list(group_obj)
- member_dict = dict(group_obj.iteritems())
-
- - Accessing items: generally a Group or Dataset object is returned. In
- the special case of a scalar dataset, a Numpy array scalar is
- returned.
-
- - Setting items:
- 1. Existing Group or Dataset: create a hard link in this group
- 2. Numpy array: create a new dataset here, overwriting any old one
- 3. Anything else: try to create a Numpy array. Also works with
- Python scalars which have Numpy type equivalents.
-
- - Deleting items: unlinks the object from this group.
-
- - Attribute access: through the property obj.attrs. See the
- AttributeManager class documentation for more information.
-
- - len(obj) returns the number of group members
- """
-
- #: Provides access to HDF5 attributes. See AttributeManager docstring.
- attrs = property(lambda self: self._attrs)
-
- # --- Public interface (Group) --------------------------------------------
-
- def __init__(self, parent_object, name, create=False):
- """ Create a new Group object, from a parent object and a name.
-
- If "create" is False (default), try to open the given group,
- raising an exception if it doesn't exist. If "create" is True,
- create a new HDF5 group and link it into the parent group.
- """
- if create:
- self.id = h5g.create(parent_object.id, name)
- else:
- self.id = h5g.open(parent_object.id, name)
-
- #: Group attribute access (dictionary-style)
- self._attrs = AttributeManager(self)
-
- def __delitem__(self, name):
- """ Unlink a member from the HDF5 group.
- """
- h5g.unlink(self.id, name)
-
- def __setitem__(self, name, obj):
- """ Add the given object to the group. Here are the rules:
-
- 1. If "obj" is a Dataset or Group object, a hard link is created
- in this group which points to the given object.
- 2. If "obj" is a Numpy ndarray, it is converted to a dataset
- object, with default settings (contiguous storage, etc.).
- 3. If "obj" is anything else, attempt to convert it to an ndarray
- and store it. Scalar values are stored as scalar datasets.
- Raise ValueError if we can't understand the resulting array
- dtype.
- """
- if isinstance(obj, Group) or isinstance(obj, Dataset):
- h5g.link(self.id, name, h5i.get_name(obj.id), link_type=h5g.LINK_HARD)
+ args = args[0:-1]
- else:
- if not isinstance(obj, numpy.ndarray):
- obj = numpy.array(obj)
- if h5t.py_can_convert_dtype(obj.dtype):
- dset = Dataset(self, name, data=obj)
- dset.close()
- else:
- raise ValueError("Don't know how to store data of this type in a dataset: " + repr(obj.dtype))
-
-
- def __getitem__(self, name):
- """ Retrive the Group or Dataset object. If the Dataset is scalar,
- returns its value instead.
- """
- retval = _open_arbitrary(self, name)
- if isinstance(retval, Dataset) and retval.shape == ():
- value = h5d.py_read_slab(retval.id, (), ())
- value = value.astype(value.dtype.type)
- retval.close()
- return value
- return retval
-
- def __iter__(self):
- """ Yield the names of group members.
- """
- return h5g.py_iternames(self.id)
-
- def iteritems(self):
- """ Yield 2-tuples of (member_name, member_value).
- """
- for name in self:
- yield (name, self[name])
+ start, count, stride, names = slicer(self.shape, args)
+ if len(names) != 0:
+ raise ValueError("Field name selections are not allowed for write.")
- def __len__(self):
- return h5g.get_num_objs(self.id)
+ if count != val.shape:
+ raise ValueError("Selection shape (%s) must match target shape (%s)" % (str(count), str(val.shape)))
- def close(self):
- """ Immediately close the underlying HDF5 object. Further operations
- on this Group object will raise an exception. You don't typically
- have to use this, as these objects are automatically closed when
- their Python equivalents are deallocated.
- """
- h5g.close(self.id)
+ fspace = self.id.get_space()
+ fspace.select_hyperslab(start, count, stride)
+ mspace = h5s.create_simple(val.shape)
- def __del__(self):
- if h5i.get_type(self.id) == h5i.GROUP:
- h5g.close(self.id)
+ self.id.write(mspace, fspace, array(val))
def __str__(self):
- return 'Group (%d members): ' % self.nmembers + ', '.join(['"%s"' % name for name in self])
+ return 'Dataset: '+str(self.shape)+' '+repr(self.dtype)
def __repr__(self):
- return self.__str__()
-
-class File(Group):
-
- """ Represents an HDF5 file on disk.
-
- Created with standard Python syntax File(name, mode), where mode may be
- one of r, r+, w, w+, a.
-
- File objects inherit from Group objects; Group-like methods all
- operate on the HDF5 root group ('/'). Like Python file objects, you
- must close the file ("obj.close()") when you're done with it.
- """
-
- _modes = ('r','r+','w','w+','a')
-
- # --- Public interface (File) ---------------------------------------------
-
- def __init__(self, name, mode, noclobber=False):
- """ Create a new file object.
-
- Valid modes (like Python's file() modes) are:
- - 'r' Readonly, file must exist
- - 'r+' Read/write, file must exist
- - 'w' Write, create/truncate file
- - 'w+' Read/write, create/truncate file
- - 'a' Read/write, file must exist (='r+')
-
- If "noclobber" is specified, file truncation (w/w+) will fail if
- the file already exists. Note this is NOT the default.
- """
- if not mode in self._modes:
- raise ValueError("Invalid mode; must be one of %s" % ', '.join(self._modes))
-
- plist = h5p.create(h5p.FILE_ACCESS)
- try:
- h5p.set_fclose_degree(plist, h5f.CLOSE_STRONG)
- if mode == 'r':
- self.fid = h5f.open(name, h5f.ACC_RDONLY, access_id=plist)
- elif 'r' in mode or 'a' in mode:
- self.fid = h5f.open(name, h5f.ACC_RDWR, access_id=plist)
- elif noclobber:
- self.fid = h5f.create(name, h5f.ACC_EXCL, access_id=plist)
- else:
- self.fid = h5f.create(name, h5f.ACC_TRUNC, access_id=plist)
- finally:
- h5p.close(plist)
-
- self.id = self.fid # So the Group constructor can find it.
- Group.__init__(self, self, '/')
-
- # For __str__ and __repr__
- self.filename = name
- self.mode = mode
- self.noclobber = noclobber
-
- def close(self):
- """ Close this HDF5 object. Note that any further access to objects
- defined in this file will raise an exception.
- """
- if h5i.get_type(self.fid) != h5i.FILE:
- raise IOError("File is already closed.")
-
- Group.close(self)
- h5f.close(self.fid)
-
- def flush(self):
- """ Instruct the HDF5 library to flush disk buffers for this file.
- """
- h5f.flush(self.fid)
-
- def __del__(self):
- """ This docstring is here to remind you that THE HDF5 FILE IS NOT
- AUTOMATICALLY CLOSED WHEN IT'S GARBAGE COLLECTED. YOU MUST
- CALL close() WHEN YOU'RE DONE WITH THE FILE.
- """
- pass
-
- def __str__(self):
- return 'File "%s", root members: %s' % (self.filename, ', '.join(['"%s"' % name for name in self]))
-
- def __repr_(self):
- return 'File("%s", "%s", noclobber=%s)' % (self.filename, self.mode, str(self.noclobber))
-
+ return str(self)
class AttributeManager(object):
@@ -431,21 +331,34 @@ class AttributeManager(object):
- len(obj.attrs) returns the number of attributes.
"""
- def __init__(self, parent_object):
- self.id = parent_object.id
+ def __init__(self, parent):
+
+ self.id = parent.id
def __getitem__(self, name):
- obj = h5a.py_get(self.id, name)
- if len(obj.shape) == 0:
- return obj.dtype.type(obj)
- return obj
+ attr = h5a.open_name(self.id, name)
+
+ arr = numpy.ndarray(attr.shape, dtype=attr.dtype)
+ attr.read(arr)
+
+ if len(arr.shape) == 0:
+ return numpy.asscalar(arr)
+ return arr
def __setitem__(self, name, value):
if not isinstance(value, numpy.ndarray):
value = numpy.array(value)
- if h5a.py_exists(self.id, name):
+
+ space = h5s.create_simple(value.shape)
+ htype = h5t.py_create(value.dtype)
+
+ # TODO: some kind of transaction safeguard here
+ try:
h5a.delete(self.id, name)
- h5a.py_set(self.id, name, value)
+ except H5Error:
+ pass
+ attr = h5a.py_create(self.id, name, htype, space)
+ attr.write(value)
def __delitem__(self, name):
h5a.delete(self.id, name)
@@ -464,155 +377,10 @@ class AttributeManager(object):
def __str__(self):
return "Attributes: "+', '.join(['"%s"' % x for x in self])
-class NamedType(object):
-
- """ Represents a named datatype, stored in a file.
-
- HDF5 datatypes are typically represented by their Numpy dtype
- equivalents; this class exists only to provide access to attributes
- stored on HDF5 named types. Properties:
- dtype: Equivalent Numpy dtype for this HDF5 type
- attrs: AttributeManager instance for attribute access
-
- Like dtype objects, these are immutable; the worst you can do it
- unlink them from their parent group.
- """
-
- def _get_dtype(self):
- if self._dtype is None:
- self._dtype = h5t.py_translate_h5t(self.id)
- return self._dtype
-
- dtype = property(_get_dtype)
-
- def __init__(self, group, name, dtype=None):
- """ Open an existing HDF5 named type, or create one.
-
- If no value is provided for "dtype", try to open a named type
- called "name" under the given group. If "dtype" is anything
- which can be converted to a Numpy dtype, create a new datatype
- based on it and store it in the group.
- """
- self.id = None
- self._dtype = None # Defer initialization; even if the named type
- # isn't Numpy-compatible, we can still get at the
- # attributes.
-
- if dtype is not None:
- dtype = numpy.dtype(dtype)
- tid = h5t.py_translate_dtype(dtype)
- try:
- h5t.commit(group.id, name, tid)
- finally:
- h5t.close(tid)
-
- self.id = h5t.open(group.id, name)
- self.attrs = AttributeManager(self)
-
- def close(self):
- """ Force the library to close this object. It will still exist
- in the file.
- """
- if self.id is not None:
- h5t.close(self.id)
- self.id = None
-
- def __del__(self):
- if self.id is not None:
- h5t.close(self.id)
-
-# === Utility functions =======================================================
-
-def slicer(shape, args):
- """
- Parse Numpy-style extended slices. Correctly handle:
- 1. Recarray-style field strings (more than one!)
- 2. Slice objects
- 3. Ellipsis objects
- """
- rank = len(shape)
- if not isinstance(args, tuple):
- args = (args,)
- args = list(args)
- slices = []
- names = []
-
- # Sort arguments
- for entry in args[:]:
- if isinstance(entry, str):
- names.append(entry)
- else:
- slices.append(entry)
-
- start = []
- count = []
- stride = []
-
- # Hack to allow Numpy-style row indexing
- if len(slices) == 1:
- args.append(Ellipsis)
-
- # Expand integers and ellipsis arguments to slices
- for dim, arg in enumerate(slices):
-
- if isinstance(arg, int) or isinstance(arg, long):
- if arg < 0:
- raise ValueError("Negative indices are not allowed.")
- start.append(arg)
- count.append(1)
- stride.append(1)
-
- elif isinstance(arg, slice):
-
- # slice.indices() method clips, so do it the hard way...
-
- # Start
- if arg.start is None:
- ss=0
- else:
- if arg.start < 0:
- raise ValueError("Negative dimensions are not allowed")
- ss=arg.start
-
- # Stride
- if arg.step is None:
- st = 1
- else:
- if arg.step <= 0:
- raise ValueError("Only positive step sizes allowed")
- st = arg.step
-
- # Count
- if arg.stop is None:
- cc = shape[dim]/st
- else:
- if arg.stop < 0:
- raise ValueError("Negative dimensions are not allowed")
- cc = (arg.stop-ss)/st
- if cc == 0:
- raise ValueError("Zero-length selections are not allowed")
-
- start.append(ss)
- stride.append(st)
- count.append(cc)
-
- elif arg == Ellipsis:
- nslices = rank-(len(slices)-1)
- if nslices <= 0:
- continue
- for x in range(nslices):
- idx = dim+x
- start.append(0)
- count.append(shape[dim+x])
- stride.append(1)
-
- else:
- raise ValueError("Bad slice type %s" % repr(arg))
- return (start, count, stride, names)
diff --git a/h5py/tests/__init__.py b/h5py/tests/__init__.py
index 9650cfa..01372de 100644
--- a/h5py/tests/__init__.py
+++ b/h5py/tests/__init__.py
@@ -12,13 +12,15 @@
import unittest
import sys
-import test_h5a, test_h5d, test_h5f, test_h5g, test_h5i, test_h5p
+import test_h5a, test_h5d, test_h5f, \
+ test_h5g, test_h5i, test_h5p, \
+ test_h5s, test_h5
from h5py import h5a, h5f, h5g, h5d, h5s, h5i, h5z, h5p
TEST_CASES = (test_h5a.TestH5A, test_h5d.TestH5D, test_h5f.TestH5F,
- test_h5g.TestH5G, test_h5i.TestH5I, test_h5p.TestH5P)#, test_h5.TestH5,
- #test_h5s.TestH5S, test_highlevel.TestHighlevel)
+ test_h5g.TestH5G, test_h5i.TestH5I, test_h5p.TestH5P,
+ test_h5s.TestH5S, test_h5.TestH5)
def buildsuite(cases):
diff --git a/h5py/tests/test_h5.py b/h5py/tests/test_h5.py
index 1e47990..9908bea 100644
--- a/h5py/tests/test_h5.py
+++ b/h5py/tests/test_h5.py
@@ -21,6 +21,5 @@ class TestH5(unittest.TestCase):
self.assertEqual(tpl, h5.HDF5_VERS_TPL)
self.assertEqual("%d.%d.%d" % tpl, h5.HDF5_VERS)
- self.assertEqual(h5.API_VERS, '1.6')
- self.assertEqual(h5.API_VERS_TPL, (1,6))
-
+ h5.API_VERS
+ h5.API_VERS_TPL
diff --git a/h5py/tests/test_h5a.py b/h5py/tests/test_h5a.py
index d55333d..6b43d2e 100644
--- a/h5py/tests/test_h5a.py
+++ b/h5py/tests/test_h5a.py
@@ -181,7 +181,29 @@ class TestH5A(unittest.TestCase):
h5a.iterate(self.obj, iterate_two, namelist, 1)
self.assertEqual(namelist, ATTRIBUTES_ORDER[1:3])
+ def test_prop_name(self):
+
+ for name in ATTRIBUTES:
+ attr = h5a.open_name(self.obj, name)
+ self.assertEqual(attr.name, name)
+
+ def test_prop_shape(self):
+
+ for name, (val, dt, shape) in ATTRIBUTES.iteritems():
+ attr = h5a.open_name(self.obj, name)
+ self.assertEqual(attr.shape, shape)
+
+ def test_prop_dtype(self):
+
+ for name, (val, dt, shape) in ATTRIBUTES.iteritems():
+ attr = h5a.open_name(self.obj, name)
+ self.assertEqual(attr.dtype, dt)
+
+ def test_py_listattrs(self):
+
+ attrlist = h5a.py_listattrs(self.obj)
+ self.assertEqual(attrlist, ATTRIBUTES_ORDER)
diff --git a/h5py/tests/test_h5f.py b/h5py/tests/test_h5f.py
index 8c9c63f..cb2d1c4 100644
--- a/h5py/tests/test_h5f.py
+++ b/h5py/tests/test_h5f.py
@@ -91,6 +91,8 @@ class TestH5F(unittest.TestCase):
self.assert_(isinstance(idlist, list))
self.assertRaises(H5Error, h5f.get_obj_ids, -1, h5f.OBJ_ALL)
+ def test_py(self):
+ self.assertEqual(self.fid.name, HDFNAME)
diff --git a/h5py/tests/test_h5g.py b/h5py/tests/test_h5g.py
index 62fd9d5..e280dcb 100644
--- a/h5py/tests/test_h5g.py
+++ b/h5py/tests/test_h5g.py
@@ -163,7 +163,10 @@ class TestH5G(unittest.TestCase):
self.assert_(self.obj.py_exists(TEST_GROUPS[0]))
self.assert_(not self.obj.py_exists('Something else'))
-
+ def test_py_iter(self):
+
+ namelist = list(self.obj.py_iter())
+ self.assertEqual(namelist, TEST_GROUPS)
diff --git a/h5py/tests/test_h5s.py b/h5py/tests/test_h5s.py
index 4aebbd7..a9cb5b5 100644
--- a/h5py/tests/test_h5s.py
+++ b/h5py/tests/test_h5s.py
@@ -14,7 +14,7 @@ import unittest
import h5py
from h5py import h5s, h5i
-from h5py.h5e import H5Error
+from h5py.h5 import H5Error
spaces = [(10,10), (1,1), (1,), ()]
max_spaces = [(10,10), (3,4), (h5s.UNLIMITED,), ()]
@@ -25,174 +25,118 @@ class TestH5S(unittest.TestCase):
def test_create_close(self):
sid = h5s.create(h5s.SCALAR)
self.assertEqual(h5i.get_type(sid), h5i.DATASPACE)
- h5s.close(sid)
+ sid.close()
self.assertEqual(h5i.get_type(sid), h5i.BADID)
self.assertRaises(H5Error, h5s.create, -1)
- self.assertRaises(H5Error, h5s.close, -1)
def test_copy(self):
sid = h5s.create(h5s.SCALAR)
- sid2 = h5s.copy(sid)
+ sid2 = sid.copy()
self.assertEqual(h5i.get_type(sid2), h5i.DATASPACE)
- h5s.close(sid2)
- h5s.close(sid)
-
- self.assertRaises(H5Error, h5s.copy, -1)
def test_simple(self):
# Tests create_simple, get_simple_extent_dims, get_simple_extent_ndims
for space, max_space in zip(spaces, max_spaces):
sid = h5s.create_simple(space,max_space)
- self.assertEqual(h5s.get_simple_extent_dims(sid), space)
- self.assertEqual(h5s.get_simple_extent_dims(sid, True), max_space)
- self.assertEqual(h5s.get_simple_extent_ndims(sid), len(space))
- h5s.close(sid)
+ self.assertEqual(sid.get_simple_extent_dims(), space)
+ self.assertEqual(sid.get_simple_extent_dims(maxdims=True), max_space)
+ self.assertEqual(sid.get_simple_extent_ndims(), len(space))
- # Bad input
self.assertRaises(ValueError, h5s.create_simple, None)
- self.assertRaises(H5Error, h5s.get_simple_extent_dims, -1)
- self.assertRaises(H5Error, h5s.get_simple_extent_ndims, -1)
-
- # Illegal input
self.assertRaises(H5Error, h5s.create_simple, (10,10), (10,9))
self.assertRaises(ValueError, h5s.create_simple, (10,10), (10,))
def test_is_simple(self):
# According to HDF5 docs, all dataspaces are "simple," even scalar ones.
sid = h5s.create(h5s.SCALAR)
- self.assert_(h5s.is_simple(sid))
- h5s.close(sid)
+ self.assert_(sid.is_simple())
sid = h5s.create(h5s.SIMPLE)
- self.assert_(h5s.is_simple(sid))
- h5s.close(sid)
-
- # I think this should be H5Error but the library disagrees.
- self.assertRaises(H5Error, h5s.is_simple, -1)
+ self.assert_(sid.is_simple())
def test_offset_simple(self):
sid = h5s.create_simple((100,100))
- h5s.select_hyperslab(sid, (0,0), (10,10))
- self.assertEqual(h5s.get_select_bounds(sid), ((0,0),(9,9)))
- h5s.offset_simple(sid,(2,2))
- self.assertEqual(h5s.get_select_bounds(sid), ((2,2),(11,11)))
- h5s.offset_simple(sid, None)
- self.assertEqual(h5s.get_select_bounds(sid), ((0,0),(9,9)))
-
- self.assertRaises(H5Error, h5s.offset_simple, -1, (10,10))
- self.assertRaises(ValueError, h5s.offset_simple, sid, (10,))
-
- h5s.close(sid)
+ sid.select_hyperslab((0,0), (10,10))
+ self.assertEqual(sid.get_select_bounds(), ((0,0),(9,9)))
+ sid.offset_simple((2,2))
+ self.assertEqual(sid.get_select_bounds(), ((2,2),(11,11)))
+ sid.offset_simple(None)
+ self.assertEqual(sid.get_select_bounds(), ((0,0),(9,9)))
def test_get_simple_extent_npoints(self):
sid = h5s.create_simple((100,100))
- self.assertEqual(h5s.get_simple_extent_npoints(sid), 100*100)
- h5s.close(sid)
-
- self.assertRaises(H5Error, h5s.get_simple_extent_npoints, -1)
+ self.assertEqual(sid.get_simple_extent_npoints(), 100*100)
def test_get_simple_extent_type(self):
sid = h5s.create(h5s.SIMPLE)
sid2 = h5s.create(h5s.SCALAR)
- self.assertEqual(h5s.get_simple_extent_type(sid), h5s.SIMPLE)
- self.assertEqual(h5s.get_simple_extent_type(sid2), h5s.SCALAR)
- h5s.close(sid)
- h5s.close(sid2)
-
- self.assertRaises(H5Error, h5s.get_simple_extent_type, -1)
+ self.assertEqual(sid.get_simple_extent_type(), h5s.SIMPLE)
+ self.assertEqual(sid2.get_simple_extent_type(), h5s.SCALAR)
def test_extent_copy(self):
sid = h5s.create(h5s.SIMPLE)
sid2 = h5s.create_simple((35,42))
- h5s.extent_copy(sid, sid2)
- self.assertEqual(h5s.get_simple_extent_dims(sid2), (35,42))
- h5s.close(sid)
- h5s.close(sid2)
-
- self.assertRaises(H5Error, h5s.extent_copy, -1, -1)
+ sid.extent_copy(sid2)
+ self.assertEqual(sid.get_simple_extent_dims(), (35,42))
def test_set_extent_simple(self):
for space, max_space in zip(spaces, max_spaces):
sid = h5s.create_simple((10,10))
- h5s.set_extent_simple(sid, space, max_space)
- self.assertEqual(h5s.get_simple_extent_dims(sid), space)
- self.assertEqual(h5s.get_simple_extent_dims(sid, True), max_space)
- h5s.close(sid)
-
- self.assertRaises(H5Error, h5s.set_extent_simple, -1, (10,10))
+ sid.set_extent_simple(space, max_space)
+ self.assertEqual(sid.get_simple_extent_dims(), space)
+ self.assertEqual(sid.get_simple_extent_dims(True), max_space)
def test_set_extent_none(self):
sid = h5s.create_simple((10,10))
- self.assertEqual(h5s.get_simple_extent_type(sid), h5s.SIMPLE)
- h5s.set_extent_none(sid)
- self.assertEqual(h5s.get_simple_extent_type(sid), h5s.NO_CLASS)
- h5s.close(sid)
-
- self.assertRaises(H5Error, h5s.set_extent_none, -1)
-
+ sid.set_extent_none()
+ self.assertEqual(sid.get_simple_extent_type(), h5s.NO_CLASS)
def test_get_select_type_npoints(self):
sid = h5s.create_simple((10,10))
- h5s.select_hyperslab(sid, (0,0), (5,5))
- self.assertEqual(h5s.get_select_type(sid), h5s.SEL_HYPERSLABS)
- self.assertEqual(h5s.get_select_npoints(sid), 25)
- h5s.close(sid)
-
- self.assertRaises(H5Error, h5s.get_select_type, -1)
- self.assertRaises(H5Error, h5s.get_select_npoints, -1)
+ sid.select_hyperslab((0,0), (5,5))
+ self.assertEqual(sid.get_select_type(), h5s.SEL_HYPERSLABS)
+ self.assertEqual(sid.get_select_npoints(), 25)
def test_get_select_bounds(self):
sid = h5s.create_simple((100,100))
- h5s.select_all(sid)
- self.assertEqual(h5s.get_select_bounds(sid), ((0,0), (99,99)))
- h5s.select_hyperslab(sid, (10,10), (13,17))
- self.assertEqual(h5s.get_select_bounds(sid), ((10,10), (22,26)))
- h5s.close(sid)
-
- self.assertRaises(H5Error, h5s.get_select_bounds, -1)
+ sid.select_all()
+ self.assertEqual(sid.get_select_bounds(), ((0,0), (99,99)))
+ sid.select_hyperslab((10,10), (13,17))
+ self.assertEqual(sid.get_select_bounds(), ((10,10), (22,26)))
def test_select(self):
+ # all, none, valid
sid = h5s.create_simple((100,100))
- h5s.select_none(sid)
- self.assertEqual(h5s.get_select_npoints(sid), 0)
- h5s.select_all(sid)
- self.assertEqual(h5s.get_select_npoints(sid), 100*100)
- h5s.select_none(sid)
- self.assertEqual(h5s.get_select_npoints(sid), 0)
-
- h5s.select_hyperslab(sid, (0,0), (10,10))
- self.assert_(h5s.select_valid(sid))
- h5s.select_hyperslab(sid, (0,0), (200,200))
- self.assert_(not h5s.select_valid(sid))
-
- h5s.close(sid)
-
- self.assertRaises(H5Error, h5s.select_none, -1)
- self.assertRaises(H5Error, h5s.select_all, -1)
- self.assertRaises(H5Error, h5s.select_valid, -1)
+ sid.select_none()
+ self.assertEqual(sid.get_select_npoints(), 0)
+ sid.select_all()
+ self.assertEqual(sid.get_select_npoints(), 100*100)
+ sid.select_none()
+ self.assertEqual(sid.get_select_npoints(), 0)
+
+ sid.select_hyperslab((0,0), (10,10))
+ self.assert_(sid.select_valid())
+ sid.select_hyperslab((0,0), (200,200))
+ self.assert_(not sid.select_valid())
def test_elements(self):
pointlist= [(0,0), (15,98), (4,17), (67,32)]
sid = h5s.create_simple((100,100))
- h5s.select_elements(sid, pointlist)
- self.assertEqual(h5s.get_select_elem_npoints(sid), len(pointlist))
- self.assertEqual(h5s.get_select_elem_pointlist(sid), pointlist)
-
- self.assertRaises(H5Error, h5s.select_elements, -1, [])
- self.assertRaises(H5Error, h5s.get_select_elem_npoints, -1)
- self.assertRaises(H5Error, h5s.get_select_elem_pointlist, -1)
+ sid.select_elements(pointlist)
+ self.assertEqual(sid.get_select_elem_npoints(), len(pointlist))
+ self.assertEqual(sid.get_select_elem_pointlist(), pointlist)
def test_get_blocks(self):
@@ -200,19 +144,18 @@ class TestH5S(unittest.TestCase):
count = [ (5,5), (13,17) ]
sid = h5s.create_simple((100,100))
- h5s.select_hyperslab(sid, start[0], count[0], op=h5s.SELECT_SET)
- h5s.select_hyperslab(sid, start[1], count[1], op=h5s.SELECT_OR)
+ sid.select_hyperslab(start[0], count[0], op=h5s.SELECT_SET)
+ sid.select_hyperslab(start[1], count[1], op=h5s.SELECT_OR)
- self.assertEqual(h5s.get_select_hyper_nblocks(sid), 2)
- blocklist = h5s.get_select_hyper_blocklist(sid)
+ self.assertEqual(sid.get_select_hyper_nblocks(), 2)
+ blocklist = sid.get_select_hyper_blocklist()
self.assertEqual(blocklist, [( (0,0), (4,4) ), ( (50,60), (62,76) )])
- h5s.close(sid)
-
- self.assertRaises(H5Error, h5s.get_select_hyper_nblocks, -1)
- self.assertRaises(H5Error, h5s.get_select_hyper_blocklist, -1)
-
-
+ def test_py(self):
+
+ for space in spaces:
+ sid = h5s.create_simple(space)
+ self.assertEqual(sid.shape, sid.get_simple_extent_dims())
diff --git a/h5py/utils_hl.py b/h5py/utils_hl.py
new file mode 100644
index 0000000..3ae00b9
--- /dev/null
+++ b/h5py/utils_hl.py
@@ -0,0 +1,108 @@
+
+"""
+ Utility functions for high-level modules.
+"""
+
+def slicer(shape, args):
+ """ Parse a tuple containing Numpy-style extended slices.
+ Shape should be a Numpy-style shape tuple.
+
+ Arguments may contain:
+
+ 1. Integer/long indices
+ 2. Slice objects
+ 3. Exactly one ellipsis object
+ 4. Strings representing field names (zero or more, in any order)
+
+ Return is a 4-tuple containing sub-tuples:
+ (start, count, stride, names)
+
+ start: tuple with starting indices
+ count: how many elements to select along each axis
+ stride: selection pitch for each axis
+ names: field names (i.e. for compound types)
+ """
+
+ rank = len(shape)
+
+ if not isinstance(args, tuple):
+ args = (args,)
+ args = list(args)
+
+ slices = []
+ names = []
+
+ # Sort arguments
+ for entry in args[:]:
+ if isinstance(entry, str):
+ names.append(entry)
+ else:
+ slices.append(entry)
+
+ start = []
+ count = []
+ stride = []
+
+ # Hack to allow Numpy-style row indexing
+ if len(slices) == 1 and slices[0] != Ellipsis:
+ args.append(Ellipsis)
+
+ # Expand integers and ellipsis arguments to slices
+ for dim, arg in enumerate(slices):
+
+ if isinstance(arg, int) or isinstance(arg, long):
+ if arg < 0:
+ raise ValueError("Negative indices are not allowed.")
+ start.append(arg)
+ count.append(1)
+ stride.append(1)
+
+ elif isinstance(arg, slice):
+
+ # slice.indices() method clips, so do it the hard way...
+
+ # Start
+ if arg.start is None:
+ ss=0
+ else:
+ if arg.start < 0:
+ raise ValueError("Negative dimensions are not allowed")
+ ss=arg.start
+
+ # Stride
+ if arg.step is None:
+ st = 1
+ else:
+ if arg.step <= 0:
+ raise ValueError("Only positive step sizes allowed")
+ st = arg.step
+
+ # Count
+ if arg.stop is None:
+ cc = shape[dim]/st
+ else:
+ if arg.stop < 0:
+ raise ValueError("Negative dimensions are not allowed")
+ cc = (arg.stop-ss)/st
+ if cc == 0:
+ raise ValueError("Zero-length selections are not allowed")
+
+ start.append(ss)
+ stride.append(st)
+ count.append(cc)
+
+ elif arg == Ellipsis:
+ nslices = rank-(len(slices)-1)
+ if nslices <= 0:
+ continue
+ for x in range(nslices):
+ idx = dim+x
+ start.append(0)
+ count.append(shape[dim+x])
+ stride.append(1)
+
+ else:
+ raise ValueError("Bad slice type %s" % repr(arg))
+
+ return (tuple(start), tuple(count), tuple(stride), tuple(names))
+
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-science/packages/h5py.git
More information about the debian-science-commits
mailing list