[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