[h5py] 150/455: HL and test suite improvements

Ghislain Vaillant ghisvail-guest at moszumanska.debian.org
Thu Jul 2 18:19:27 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 aa2ae6fed05050673807cbff0b27954a48272f12
Author: andrewcollette <andrew.collette at gmail.com>
Date:   Thu Oct 30 04:17:35 2008 +0000

    HL and test suite improvements
---
 h5py/highlevel.py                          | 324 +++++++++++++++++------------
 h5py/tests/data/attributes.hdf5            | Bin 4536 -> 4536 bytes
 h5py/tests/data/smpl_compound_chunked.hdf5 | Bin 7768 -> 5314 bytes
 h5py/tests/test_h5a.py                     |   2 +-
 h5py/tests/test_h5t.py                     |  71 +++++--
 h5py/tests/testfiles.py                    | 148 +++++++++++++
 6 files changed, 390 insertions(+), 155 deletions(-)

diff --git a/h5py/highlevel.py b/h5py/highlevel.py
index 9f6feba..2a0ab6a 100644
--- a/h5py/highlevel.py
+++ b/h5py/highlevel.py
@@ -86,6 +86,9 @@ class HLObject(LockableObject):
         id:     Low-level identifer, compatible with the h5py.h5* modules.
         name:   (Some) name of this object in the HDF5 file.
         attrs:  HDF5 attributes of this object.  See the AttributeManager docs.
+
+        Equality comparison and hashing are based on native HDF5 object
+        identity.
     """
 
     @property
@@ -101,6 +104,13 @@ class HLObject(LockableObject):
     def __nonzero__(self):
         return self.id.__nonzero__()
 
+    def __hash__(self):
+        return hash(self.id)
+    def __eq__(self, other):
+        if hasattr(other, 'id'):
+            return self.id == other.id
+        return False
+
 class DictCompat(object):
 
     """
@@ -167,9 +177,9 @@ class Group(HLObject, DictCompat):
     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" 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.
         """
         with parent_object._lock:
             if create:
@@ -183,30 +193,28 @@ class Group(HLObject, DictCompat):
         """ Add the given object to the group.  The action taken depends on
             the type of object assigned:
 
-            1. Named HDF5 object (Dataset, Group, Datatype):
-                A hard link is created in this group which points to the
-                given object.
+        1. Named HDF5 object (Dataset, Group, Datatype):
+            A hard link is created in this group which points to the
+            given object.
 
-            2. Numpy ndarray:
-                The array is converted to a dataset object, with default
-                settings (contiguous storage, etc.).
+        2. Numpy ndarray:
+            The array is converted to a dataset object, with default
+            settings (contiguous storage, etc.).
 
-            3. Numpy dtype:
-                Commit a copy of the datatype as a named datatype in the file.
-
-            4. 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 a group member of the same name already exists, the assignment
-            will fail.  You can check by using the Python __contains__ syntax:
+        3. Numpy dtype:
+            Commit a copy of the datatype as a named datatype in the file.
 
-                if "name" in grp:
-                    del grp["name"]
-                grp["name"] = <whatever>
+        4. 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 a group member of the same name already exists, the assignment
+        will fail.  You can check by using the Python __contains__ syntax:
 
-            This limitation is intentional, and may be lifted in the future.
+            if "name" in grp:
+                del grp["name"]
+            grp["name"] = <whatever>
         """
         with self._lock:
             if isinstance(obj, Group) or isinstance(obj, Dataset) or isinstance(obj, Datatype):
@@ -221,8 +229,6 @@ class Group(HLObject, DictCompat):
 
     def __getitem__(self, name):
         """ Open an object attached to this group. 
-
-            Currently can open groups, datasets, and named types.
         """
         with self._lock:
             info = h5g.get_objinfo(self.id, name)
@@ -257,13 +263,14 @@ class Group(HLObject, DictCompat):
     def create_group(self, name):
         """ Create and return a subgroup.
 
-            Fails if the group already exists.
+        Fails if the group already exists.
         """
         return Group(self, name, create=True)
 
     def require_group(self, name):
-        """ Check if a group exists, and create it if not. Raise H5Error if
-            an incompatible object exists.
+        """ Check if a group exists, and create it if not.
+
+        Raises H5Error if an incompatible object exists.
         """
         if not name in self:
             return self.create_group(name)
@@ -276,41 +283,43 @@ class Group(HLObject, DictCompat):
     def create_dataset(self, name, *args, **kwds):
         """ Create and return a new dataset, attached to this group.
 
-            create_dataset(name, shape, [dtype=<Numpy dtype>], **kwds)
-            create_dataset(name, data=<Numpy array>, **kwds)
+        create_dataset(name, shape, [dtype=<Numpy dtype>], **kwds)
+        create_dataset(name, data=<Numpy array>, **kwds)
 
-            If "dtype" is not specified, the default is single-precision
-            floating point, with native byte order ("=f4").
+        If "dtype" is not specified, the default is single-precision
+        floating point, with native byte order ("=f4").
 
-            Creating a dataset will fail if another of the same name already 
-            exists. Additional keywords are:
+        Creating a dataset will fail if another of the same name already 
+        exists. Additional keywords are:
 
-            chunks:        Tuple of chunk dimensions or None*
-            compression:   DEFLATE (gzip) compression level, int or None*
-            shuffle:       Use the shuffle filter? (requires compression) T/F*
-            fletcher32:    Enable Fletcher32 error detection? T/F*
-            maxshape:      Tuple giving dataset maximum dimensions or None*.
-                           You can grow each axis up to this limit using
-                           extend().  For each unlimited axis, provide None.
+        chunks:        Tuple of chunk dimensions or None*
+        compression:   DEFLATE (gzip) compression level, int or None*
+        shuffle:       Use the shuffle filter? (requires compression) T/F*
+        fletcher32:    Enable Fletcher32 error detection? T/F*
+        maxshape:      Tuple giving dataset maximum dimensions or None*.
+                       You can grow each axis up to this limit using
+                       extend().  For each unlimited axis, provide None.
 
-            All these options require chunking.  If a chunk tuple is not
-            provided, the constructor will guess an appropriate chunk shape.
-            Please note none of these are allowed for scalar datasets.
+        All these options require chunking.  If a chunk tuple is not
+        provided, the constructor will guess an appropriate chunk shape.
+        Please note none of these are allowed for scalar datasets.
         """
         return Dataset(self, name, *args, **kwds)
 
     def require_dataset(self, name, shape, dtype, exact=False, **kwds):
-        """ Check if a dataset with compatible shape and dtype exists, and
-            create one if it doesn't.  Raises H5Error if an incompatible
-            dataset (or group) already exists.  
+        """Open a dataset, or create it if it doesn't exist.
+
+        Checks if a dataset with compatible shape and dtype exists, and
+        creates one if it doesn't.  Raises H5Error if an incompatible
+        dataset (or group) already exists.  
 
-            By default, datatypes are compared for loss-of-precision only.
-            To require an exact match, set keyword "exact" to True.  Shapes
-            are always compared exactly.
+        By default, datatypes are compared for loss-of-precision only.
+        To require an exact match, set keyword "exact" to True.  Shapes
+        are always compared exactly.
 
-            Keyword arguments are only used when creating a new dataset; they
-            are ignored if an dataset with matching shape and dtype already
-            exists.  See create_dataset for a list of legal keywords.
+        Keyword arguments are only used when creating a new dataset; they
+        are ignored if an dataset with matching shape and dtype already
+        exists.  See create_dataset for a list of legal keywords.
         """
         dtype = numpy.dtype(dtype)
 
@@ -336,26 +345,66 @@ class Group(HLObject, DictCompat):
 
     # New 1.8.X methods
 
+    def copy(self, source, dest):
+        """ Copy an object or group.
+
+        The source can be a path, Group, Dataset, or Datatype object.  The
+        destination can be either a path or a Group object.  The source and
+        destinations need not be in the same file.
+
+        Example:
+
+        >>> f = File('myfile.hdf5')
+        >>> f.listnames()
+        ['MyGroup']
+        >>> f.copy('MyGroup', 'MyCopy')
+        >>> f.listnames()
+        ['MyGroup', 'MyCopy']
+
+        """
+        if not config.API_18:
+            raise NotImplementedError("This feature is only available with HDF5 1.8.0 and later")
+
+        with self._lock:
+
+            if isinstance(source, HLObject):
+                source_path = '.'
+            else:
+                # Interpret source as a path relative to this group
+                source_path = source
+                source = self
+
+            if isinstance(dest, Group):
+                dest_path = '.'
+            elif isinstance(dest, HLObject):
+                raise TypeError("Destination must be path or Group object")
+            else:
+                # Interpret destination as a path relative to this group
+                dest_path = dest
+                dest = self
+
+            h5o.copy(source.id, source_path, dest.id, dest_path)
+
     def visit(self, func):
         """ Recursively visit all names in this group and subgroups.
 
-            You supply a callable (function, method or callable object); it
-            will be called exactly once for each link in this group and every
-            group below it. Your callable must conform to the signature:
+        You supply a callable (function, method or callable object); it
+        will be called exactly once for each link in this group and every
+        group below it. Your callable must conform to the signature:
 
-                func(<member name>) => <None or return value>
+            func(<member name>) => <None or return value>
 
-            Returning None continues iteration, returning anything else stops
-            and immediately returns that value from the visit method.
+        Returning None continues iteration, returning anything else stops
+        and immediately returns that value from the visit method.
 
-            Example:
+        Example:
 
-            # List the entire contents of the file
-            >>> f = File("foo.hdf5")
-            >>> list_of_names = []
-            >>> f.visit(list_of_names.append)
+        >>> # List the entire contents of the file
+        >>> f = File("foo.hdf5")
+        >>> list_of_names = []
+        >>> f.visit(list_of_names.append)
 
-            Only available with HDF5 1.8.X.
+        Only available with HDF5 1.8.X.
         """
         if not config.API_18:
             raise NotImplementedError("This feature is only available with HDF5 1.8.0 and later")
@@ -366,27 +415,27 @@ class Group(HLObject, DictCompat):
     def visititems(self, func):
         """ Recursively visit names and objects in this group and subgroups.
 
-            You supply a callable (function, method or callable object); it
-            will be called exactly once for each link in this group and every
-            group below it. Your callable must conform to the signature:
+        You supply a callable (function, method or callable object); it
+        will be called exactly once for each link in this group and every
+        group below it. Your callable must conform to the signature:
 
-                func(<member name>, <object>) => <None or return value>
+            func(<member name>, <object>) => <None or return value>
 
-            Returning None continues iteration, returning anything else stops
-            and immediately returns that value from the visit method.
+        Returning None continues iteration, returning anything else stops
+        and immediately returns that value from the visit method.
 
-            Example:
+        Example:
 
-            # Get a list of all datasets in the file
-            >>> mylist = []
-            >>> def func(name, obj):
-            ...     if isinstance(obj, Dataset):
-            ...         mylist.append(name)
-            ...
-            >>> f = File('foo.hdf5')
-            >>> f.visititems(func)
-   
-            Only available with HDF5 1.8.X.
+        # Get a list of all datasets in the file
+        >>> mylist = []
+        >>> def func(name, obj):
+        ...     if isinstance(obj, Dataset):
+        ...         mylist.append(name)
+        ...
+        >>> f = File('foo.hdf5')
+        >>> f.visititems(func)
+
+        Only available with HDF5 1.8.X.
         """
         if not config.API_18:
             raise NotImplementedError("This feature is only available with HDF5 1.8.0 and later")
@@ -404,7 +453,6 @@ class Group(HLObject, DictCompat):
             except Exception:
                 return "<Closed HDF5 group>"
 
-
 class File(Group):
 
     """ Represents an HDF5 file on disk.
@@ -507,6 +555,15 @@ class File(Group):
             except Exception:
                 return "<Closed HDF5 file>"
 
+    # Fix up identity to use the file identifier, not the root group.
+    def __hash__(self):
+        return hash(self.fid)
+    def __eq__(self, other):
+        if hasattr(other, 'fid'):
+            return self.fid == other.fid
+        return False
+
+
 class Dataset(HLObject):
 
     """ High-level interface to an HDF5 dataset.
@@ -574,39 +631,41 @@ class Dataset(HLObject):
                     shape=None, dtype=None, data=None,
                     chunks=None, compression=None, shuffle=False,
                     fletcher32=False, maxshape=None):
-        """ Construct a Dataset object.  You might find it easier to use the
-            Group methods: Group["name"] or Group.create_dataset().
+        """ Open or create a new dataset in the file.
 
-            There are two modes of operation for this constructor:
+        It's recommended you use the Group methods (open via Group["name"],
+        create via Group.create_dataset), rather than calling the constructor.
 
-            1.  Open an existing dataset:
-                  Dataset(group, name)
+        There are two modes of operation for this constructor:
 
-            2.  Create a dataset:
-                  Dataset(group, name, shape, [dtype=<Numpy dtype>], **kwds)
-                or
-                  Dataset(group, name, data=<Numpy array>, **kwds)
+        1.  Open an existing dataset:
+              Dataset(group, name)
 
-                  If "dtype" is not specified, the default is single-precision
-                  floating point, with native byte order ("=f4").
+        2.  Create a dataset:
+              Dataset(group, name, shape, [dtype=<Numpy dtype>], **kwds)
+            or
+              Dataset(group, name, data=<Numpy array>, **kwds)
 
-            Creating a dataset will fail if another of the same name already 
-            exists.  Also, chunks/compression/shuffle/fletcher32 may only be
-            specified when creating a dataset.
+              If "dtype" is not specified, the default is single-precision
+              floating point, with native byte order ("=f4").
 
-            Creation keywords (* is default):
+        Creating a dataset will fail if another of the same name already 
+        exists.  Also, chunks/compression/shuffle/fletcher32 may only be
+        specified when creating a dataset.
 
-            chunks:        Tuple of chunk dimensions, True, or None*
-            compression:   DEFLATE (gzip) compression level, int or None*
-            shuffle:       Use the shuffle filter? (requires compression) T/F*
-            fletcher32:    Enable Fletcher32 error detection? T/F*
-            maxshape:      Tuple giving dataset maximum dimensions or None*.
-                           You can grow each axis up to this limit using
-                           extend().  For each unlimited axis, provide None.
+        Creation keywords (* is default):
 
-            All these options require chunking.  If a chunk tuple is not
-            provided, the constructor will guess an appropriate chunk shape.
-            Please note none of these are allowed for scalar datasets.
+        chunks:        Tuple of chunk dimensions, True, or None*
+        compression:   DEFLATE (gzip) compression level, int or None*
+        shuffle:       Use the shuffle filter? (requires compression) T/F*
+        fletcher32:    Enable Fletcher32 error detection? T/F*
+        maxshape:      Tuple giving dataset maximum dimensions or None*.
+                       You can grow each axis up to this limit using
+                       extend().  For each unlimited axis, provide None.
+
+        All these options require chunking.  If a chunk tuple is not
+        provided, the constructor will guess an appropriate chunk shape.
+        Please note none of these are allowed for scalar datasets.
         """
         with group._lock:
             if data is None and shape is None:
@@ -671,9 +730,9 @@ class Dataset(HLObject):
     def extend(self, shape):
         """ Resize the dataset so it's at least as big as "shape".
 
-            Note that the new shape must be compatible with the "maxshape"
-            argument provided when the dataset was created.  Also, the rank of
-            the dataset cannot be changed.
+        Note that the new shape must be compatible with the "maxshape"
+        argument provided when the dataset was created.  Also, the rank of
+        the dataset cannot be changed.
         """
         with self._lock:
             self.id.extend(shape)
@@ -708,19 +767,20 @@ class Dataset(HLObject):
             yield self[i]
 
     def __getitem__(self, args):
-        """ Read a slice from the HDF5 dataset.  Takes slices and
-            recarray-style field names (more than one is allowed!) in any
-            order.  Obeys basic NumPy broadcasting rules.
+        """ Read a slice from the HDF5 dataset.
+
+        Takes slices and recarray-style field names (more than one is
+        allowed!) in any order.  Obeys basic NumPy broadcasting rules.
 
-            Also supports:
+        Also supports:
 
-            * Boolean "mask" array indexing
-            * Discrete point selection via CoordsList instance
+        * Boolean "mask" array indexing
+        * Discrete point selection via CoordsList instance
 
-            Beware; these last two techniques work by explicitly enumerating
-            the points to be selected.  In the worst case, the selection list
-            for a boolean array can be every point in the dataset, with a 
-            2x to 3x memory overhead.
+        Beware; these last two techniques work by explicitly enumerating
+        the points to be selected.  In the worst case, the selection list
+        for a boolean array can be every point in the dataset, with a 
+        2x to 3x memory overhead.
         """
         with self._lock:
 
@@ -764,9 +824,10 @@ class Dataset(HLObject):
             return arr
 
     def __setitem__(self, args, val):
-        """ Write to the HDF5 dataset from a Numpy array.  The shape of the
-            Numpy array must match the shape of the selection, and the Numpy
-            array's datatype must be convertible to the HDF5 datatype.
+        """ Write to the HDF5 dataset from a Numpy array.
+
+        The shape of the Numpy array must match the shape of the selection, and
+        the Numpy array's datatype must be convertible to the HDF5 datatype.
         """
         with self._lock:
 
@@ -831,9 +892,10 @@ class AttributeManager(LockableObject, DictCompat):
         self.id = parent.id
 
     def __getitem__(self, name):
-        """ Read the value of an attribute.  If the attribute is scalar, it
-            will be returned as a Numpy scalar.  Otherwise, it will be returned
-            as a Numpy ndarray.
+        """ Read the value of an attribute.
+    
+        If the attribute is scalar, it will be returned as a Numpy
+        scalar.  Otherwise, it will be returned as a Numpy ndarray.
         """
         with self._lock:
             attr = h5a.open(self.id, name)
@@ -847,11 +909,11 @@ class AttributeManager(LockableObject, DictCompat):
 
     def __setitem__(self, name, value):
         """ Set the value of an attribute, overwriting any previous value.
-            The value you provide must be convertible to a Numpy array or
-            scalar.
 
-            Any existing value is destroyed just before the call to h5a.create.
-            If the creation fails, the data is not recoverable.
+        The value you provide must be convertible to a Numpy array or scalar.
+
+        Any existing value is destroyed just before the call to h5a.create.
+        If the creation fails, the data is not recoverable.
         """
         with self._lock:
             value = numpy.asarray(value, order='C')
diff --git a/h5py/tests/data/attributes.hdf5 b/h5py/tests/data/attributes.hdf5
index 7435e3e..08c4db8 100644
Binary files a/h5py/tests/data/attributes.hdf5 and b/h5py/tests/data/attributes.hdf5 differ
diff --git a/h5py/tests/data/smpl_compound_chunked.hdf5 b/h5py/tests/data/smpl_compound_chunked.hdf5
index e386791..d355c36 100644
Binary files a/h5py/tests/data/smpl_compound_chunked.hdf5 and b/h5py/tests/data/smpl_compound_chunked.hdf5 differ
diff --git a/h5py/tests/test_h5a.py b/h5py/tests/test_h5a.py
index b11e4cd..daf7080 100644
--- a/h5py/tests/test_h5a.py
+++ b/h5py/tests/test_h5a.py
@@ -25,7 +25,7 @@ ATTRIBUTES = {  'String Attribute': ("This is a string.", dtype('S18'), ()),
                 'Integer': (42, dtype('<i4'), ()),
                 'Integer Array': ( [0,1,2,3], dtype('<i4'), (4,) ),
                 'Byte': (-34, dtype('|i1'), ()) }
-ATTRIBUTES_ORDER = ['String Attribute', 'Integer', 'Integer Array', 'Byte']
+ATTRIBUTES_ORDER = sorted(ATTRIBUTES) # ['String Attribute', 'Integer', 'Integer Array', 'Byte']
 NEW_ATTRIBUTES = {'New float': ( 3.14, dtype('<f4'), ()) }
 
 
diff --git a/h5py/tests/test_h5t.py b/h5py/tests/test_h5t.py
index adbbad5..de4cec7 100644
--- a/h5py/tests/test_h5t.py
+++ b/h5py/tests/test_h5t.py
@@ -17,6 +17,7 @@ from numpy import dtype
 
 from h5py import *
 from h5py.h5 import H5Error
+from common import HDF5TestCase
 
 kind_map = {'i': h5t.TypeIntegerID, 'u': h5t.TypeIntegerID, 'f': h5t.TypeFloatID,
            'c': h5t.TypeCompoundID, 'S': h5t.TypeStringID, 'V': h5t.TypeOpaqueID}
@@ -30,20 +31,29 @@ simple_types = \
     "<f4", "<f8", ">f4", ">f8", "<c8", "<c16", ">c8", ">c16",
     "|S1", "|S2", "|S33", "|V1", "|V2", "|V33"]
 
-class TestH5T(unittest.TestCase):
+class TestH5T(HDF5TestCase):
+
 
     def test_create(self):
+        """ Check that it produces instances from typecodes """
+
         types = {h5t.COMPOUND: h5t.TypeCompoundID, h5t.OPAQUE: h5t.TypeOpaqueID}
         sizes = (1,4,256)
-        for typecode, typeobj in types.iteritems():
+
+        def _t_create(typecode, size):
+            """ Core test """
+            htype=h5t.create(typecode, size)
+            self.assertEqual(type(htype), types[typecode])
+            self.assertEqual(htype.get_size(), size)
+
+        for typecode in types:
             for size in sizes:
-                htype = h5t.create(typecode, size)
-                self.assertEqual(type(htype), typeobj)
-                self.assertEqual(htype.get_size(), size)
+                _t_create(typecode, size)
         
         self.assertRaises(ValueError, h5t.create, h5t.ARRAY, 4)
     
     def test_open_commit_committed(self):
+        """ Check that we can commit a named type and open it again """
         plist = h5p.create(h5p.FILE_ACCESS)
         plist.set_fclose_degree(h5f.CLOSE_STRONG)
         fname = tempfile.mktemp('.hdf5')
@@ -62,6 +72,7 @@ class TestH5T(unittest.TestCase):
             os.unlink(fname)
 
     def test_close(self):
+        """ Make sure that closing an object decrefs its identifier """
         htype = h5t.STD_I32LE.copy()
         self.assert_(htype)
         htype._close()
@@ -69,12 +80,16 @@ class TestH5T(unittest.TestCase):
 
     def test_copy(self):
 
-        for x in simple_types:
-            htype = h5t.py_create(dtype(x))
+        def test(dt):
+            """ Test copying for the given NumPy dtype"""
+            htype = h5t.py_create(dtype(dt))
             htype2 = htype.copy()
             self.assertEqual(htype.dtype, htype2.dtype)
             self.assert_(htype is not htype2)
-            self.assert_(htype == htype2)
+            self.assert_(htype == htype2)    
+
+        for x in simple_types:
+            test(x)
 
     def test_equal(self):
 
@@ -94,27 +109,40 @@ class TestH5T(unittest.TestCase):
 
     def test_get_class(self):
 
-        for x in simple_types:
-            dt = dtype(x)
+        def test(dt):
+            """ Check that getclass produces the correct code for the dtype """
+            dt = dtype(dt)
             htype = h5t.py_create(dt)
             self.assertEqual(htype.get_class(), typecode_map[dt.kind])
 
-    def test_get_size(self):
+        for x in simple_types:
+            test(x)
+
+    def test_get_set_size(self):
 
         sizes = (1,2,3,4,127,128,129,133,16385)
-        for x in sizes:
-            htype = h5t.create(h5t.OPAQUE, x)
-            self.assertEqual(htype.get_size(), x)
+
+        def test(htype, size):
+            htype.set_size(size)
+            self.assertEqual(htype.get_size(), size)  
+
+        htype = h5t.create(h5t.OPAQUE, 4)
+        for size in sizes:
+            test(htype, size)
 
     def test_get_super(self):
 
-        for x in simple_types:
-            htype = h5t.py_create(x)
+        def test(dt):
+            """ Check get_super for a given dtype """
+            htype = h5t.py_create(dt)
             atype = h5t.array_create(htype, (4,5))
             self.assert_(htype.equal(atype.get_super()))
 
-    def test_detect_class(self):
+        for x in simple_types:
+            test(x)
 
+    def test_detect_class(self):
+        
         dt = dtype([(x, x) for x in simple_types])
 
         htype = h5t.py_create(dt)
@@ -122,12 +150,6 @@ class TestH5T(unittest.TestCase):
         self.assert_(htype.detect_class(h5t.OPAQUE))
         self.assert_(not htype.detect_class(h5t.ARRAY))
 
-    def test_set_size(self):
-
-        htype = h5t.create(h5t.OPAQUE, 128)
-        self.assertEqual(htype.get_size(), 128)
-        htype.set_size(300)
-        self.assertEqual(htype.get_size(), 300)
 
     def test_set_get_order_sign(self):
         
@@ -148,12 +170,14 @@ class TestH5T(unittest.TestCase):
         self.assertEqual(htype.get_tag(), "FOOBAR")
         
     def test_array(self):
+        """ Test all array-specific features """
         htype = h5t.array_create(h5t.STD_I32LE,(4,5))
         self.assertEqual(htype.get_array_ndims(), 2)
         self.assertEqual(htype.get_array_dims(), (4,5))
         self.assertEqual(htype.dtype, dtype(('<i4',(4,5))))
 
     def test_enum(self):
+        """ Test all enum-specific routines """
         names = ("A", "B", "Name3", "Name with space", " 12A-d878dd&%2 0-1!** ")
         values = (1,2,3.0, -999, 30004.0)
         valuedict = {}
@@ -171,6 +195,7 @@ class TestH5T(unittest.TestCase):
         self.assertEqual(htype.get_nmembers(), len(names))
 
     def test_compound(self):
+        """ Test all compound datatype operations """
         names = ("A", "B", "Name3", "Name with space", " 12A-d878dd&%2 0-1!** ")
         types = (h5t.STD_I8LE, h5t.IEEE_F32BE, h5t.STD_U16BE, h5t.C_S1.copy(), h5t.FORTRAN_S1.copy())
         types[3].set_size(8)
diff --git a/h5py/tests/testfiles.py b/h5py/tests/testfiles.py
new file mode 100644
index 0000000..91f4dd6
--- /dev/null
+++ b/h5py/tests/testfiles.py
@@ -0,0 +1,148 @@
+#+
+# 
+# This file is part of h5py, a low-level Python interface to the HDF5 library.
+# 
+# Copyright (C) 2008 Andrew Collette
+# http://h5py.alfven.org
+# License: BSD  (See LICENSE.txt for full license)
+# 
+# $Date$
+# 
+#-
+
+"""
+    Contains code to "bootstrap" HDF5 files for the test suite.  Since it
+    uses the same code that will eventually be tested, the files it produces
+    are manually inspected using HDFView, and distributed with the package.
+"""
+
+import numpy as np
+import h5py
+
+class Group(object):
+
+    def __init__(self, members=None, attrs=None):
+        self.attrs = {} if attrs is None else attrs
+        self.members = {} if members is None else members
+
+class File(Group):
+
+    def __init__(self, name, *args, **kwds):
+        self.name = name
+        Group.__init__(self, *args, **kwds)
+
+class Dataset(object):
+
+    def __init__(self, shape=None, dtype=None, data=None, attrs=None, dset_kwds=None):
+        self.data = data
+        self.shape = shape
+        self.dtype = dtype
+
+        self.attrs = {} if attrs is None else attrs
+        self.dset_kwds = {} if dset_kwds is None else dset_kwds
+
+class Datatype(object):
+     
+    def __init__(self, dtype, attrs=None):
+        self.attrs = {} if attrs is None else attrs
+        self.dtype = dtype
+
+
+def compile_hdf5(fileobj):
+    """ Take a "model" HDF5 tree and write it to an actual file. """
+
+    def update_attrs(hdf_obj, attrs_dict):
+        for name in sorted(attrs_dict):
+            val = attrs_dict[name]
+            hdf_obj.attrs[name] = val
+
+    def store_dataset(group, name, obj):
+        """ Create and store a dataset in the given group """
+        kwds = obj.dset_kwds.copy()
+        kwds.update({'shape': obj.shape, 'dtype': obj.dtype, 'data': obj.data})
+        dset = group.create_dataset(name, **kwds)
+        update_attrs(dset, obj.attrs)
+
+    def store_type(group, name, obj):
+        """ Commit the given datatype to the group """
+        group[name] = obj.dtype
+        htype = group[name]
+        update_attrs(htype, obj.attrs)
+
+    def store_group(group, name, obj):
+        """ Create a new group inside this existing group. """
+
+        # First create the new group (if it's not the root group)
+        if name is not None:
+            hgroup = group.create_group(name)
+        else:
+            hgroup = group
+
+        # Now populate it
+        for new_name in sorted(obj.members):
+            new_obj = obj.members[new_name]
+
+            if isinstance(new_obj, Dataset):
+                store_dataset(hgroup, new_name, new_obj)
+            elif isinstance(new_obj, Datatype):
+                store_type(hgroup, new_name, new_obj)
+            elif isinstance(new_obj, Group):
+                store_group(hgroup, new_name, new_obj)
+
+        update_attrs(hgroup, obj.attrs)
+
+    f = h5py.File(fileobj.name, 'w')
+    store_group(f, None, fileobj)
+    f.close()
+
+
+def file_attrs():
+    """ "Attributes" test file (also used by group tests) """
+    sg1 = Group()
+    sg2 = Group()
+    sg3 = Group()
+    gattrs = {'String Attribute': np.asarray("This is a string.", '|S18'),
+              'Integer': np.asarray(42, '<i4'),
+              'Integer Array': np.asarray([0,1,2,3], '<i4'),
+              'Byte': np.asarray(-34, '|i1')}
+    grp = Group( {'Subgroup1': sg1, 'Subgroup2': sg2, 'Subgroup3': sg3}, gattrs)
+    return File('attributes.hdf5', {'Group': grp})
+
+def file_dset():
+    """ "Dataset" test file.  Bears a suspicious resemblance to a certain
+        PyTables file.
+    """
+    dtype = np.dtype(
+        [('a_name','>i4'),
+         ('c_name','|S6'),
+         ('d_name', np.dtype( ('>i2', (5,10)) )),
+         ('e_name', '>f4'),
+         ('f_name', np.dtype( ('>f8', (10,)) )),
+         ('g_name', '<u1')])
+
+    arr = np.ndarray((6,), dtype)
+    for i in range(6):
+        arr[i]["a_name"] = i,
+        arr[i]["c_name"] = "Hello!"
+        arr[i]["d_name"][:] = np.sum(np.indices((5,10)),0) + i
+        arr[i]["e_name"] = 0.96*i
+        arr[i]["f_name"][:] = np.array((1024.9637*i,)*10)
+        arr[i]["g_name"] = 109
+
+    options = {'chunks': (3,)}
+
+    dset = Dataset(data=arr, attrs={}, dset_kwds=options)
+
+    return File('smpl_compound_chunked.hdf5', {'CompoundChunked': dset})
+
+if __name__ == '__main__':
+    compile_hdf5(file_attrs())
+    compile_hdf5(file_dset())
+
+
+
+
+
+
+
+

-- 
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