[h5py] 50/455: API changes and redesign for 0.2 mostly complete

Ghislain Vaillant ghisvail-guest at moszumanska.debian.org
Thu Jul 2 18:19:16 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 5cc9201de74f3c89fdfca1f3818eb06bf3989a7f
Author: andrewcollette <andrew.collette at gmail.com>
Date:   Fri Jun 13 05:10:29 2008 +0000

    API changes and redesign for 0.2 mostly complete
---
 h5py/h5a.pyx                 |   8 +--
 h5py/h5d.pyx                 |  10 +--
 h5py/h5f.pyx                 |   4 +-
 h5py/h5p.pyx                 |   4 +-
 h5py/h5s.pyx                 |   9 +--
 h5py/h5t.pyx                 | 131 +++++++++++++++++++++-----------------
 h5py/h5z.pyx                 |   2 +-
 h5py/highlevel.py            | 146 ++++++++++++++++++++++---------------------
 h5py/std_defs.pxi            |   2 +
 h5py/std_inline.pyx          |  19 ------
 h5py/tests/__init__.py       |   6 +-
 h5py/tests/test_h5a.py       |   4 +-
 h5py/tests/test_highlevel.py |  59 +++++++++++++++++
 h5py/utils.pxd               |   1 +
 h5py/utils.pyx               |   7 +++
 setup.py                     |   2 +-
 16 files changed, 241 insertions(+), 173 deletions(-)

diff --git a/h5py/h5a.pyx b/h5py/h5a.pyx
index b2afc0e..d69ed32 100644
--- a/h5py/h5a.pyx
+++ b/h5py/h5a.pyx
@@ -92,7 +92,7 @@ def read(hid_t attr_id, ndarray arr_obj):
     space_id = 0
 
     try:
-        mtype_id = h5t.py_dtype_to_h5t(arr_obj.dtype)
+        mtype_id = h5t.py_translate_dtype(arr_obj.dtype)
         space_id = H5Aget_space(attr_id)
         check_numpy_write(arr_obj, space_id)
 
@@ -121,7 +121,7 @@ def write(hid_t attr_id, ndarray arr_obj):
     space_id = 0
 
     try:
-        mtype_id = h5t.py_dtype_to_h5t(arr_obj.dtype)
+        mtype_id = h5t.py_translate_dtype(arr_obj.dtype)
         space_id = H5Aget_space(attr_id)
         check_numpy_read(arr_obj, space_id)
 
@@ -254,7 +254,7 @@ def py_create(hid_t loc_id, char* name, object dtype_in, object shape):
 
     try:
         sid = h5s.create_simple(shape)
-        type_id = h5t.py_dtype_to_h5t(dtype_in)
+        type_id = h5t.py_translate_dtype(dtype_in)
 
         return create(loc_id, name, type_id, sid)
 
@@ -294,7 +294,7 @@ def py_dtype(hid_t attr_id):
     
     try:
         type_id = H5Aget_type(attr_id)
-        return h5t.py_h5t_to_dtype(type_id)
+        return h5t.py_translate_h5t(type_id)
     finally:
         if type_id:
             PY_H5Tclose(type_id)
diff --git a/h5py/h5d.pyx b/h5py/h5d.pyx
index 92c55c6..8875d53 100644
--- a/h5py/h5d.pyx
+++ b/h5py/h5d.pyx
@@ -117,7 +117,7 @@ def read(hid_t dset_id, hid_t mspace_id, hid_t fspace_id, ndarray arr_obj,
     mtype_id = 0
 
     try:
-        mtype_id = h5t.py_dtype_to_h5t(arr_obj.dtype)
+        mtype_id = h5t.py_translate_dtype(arr_obj.dtype)
         check_numpy_write(arr_obj, -1)
 
         H5Dread(dset_id, mtype_id, mspace_id, fspace_id, plist, PyArray_DATA(arr_obj))
@@ -143,7 +143,7 @@ def write(hid_t dset_id, hid_t mspace_id, hid_t fspace_id, ndarray arr_obj,
     mtype_id = 0
 
     try:
-        mtype_id = h5t.py_dtype_to_h5t(arr_obj.dtype)
+        mtype_id = h5t.py_translate_dtype(arr_obj.dtype)
         check_numpy_read(arr_obj, -1)
 
         H5Dwrite(dset_id, mtype_id, mspace_id, fspace_id, plist, PyArray_DATA(arr_obj))
@@ -282,7 +282,7 @@ def py_create(hid_t parent_id, char* name, object data=None, object dtype=None,
         else:
             space_id = h5s.create_simple(shape)
 
-        type_id = h5t.py_dtype_to_h5t(dtype)
+        type_id = h5t.py_translate_dtype(dtype)
     
         if( chunks or compression or shuffle or fletcher32):
             plist = h5p.create(H5P_DATASET_CREATE)
@@ -354,7 +354,7 @@ def py_read_slab(hid_t ds_id, object start, object count,
         # Obtain the Numpy dtype of the array
         if dtype is None:
             type_id = H5Dget_type(ds_id)
-            dtype = h5t.py_h5t_to_dtype(type_id)
+            dtype = h5t.py_translate_h5t(type_id)
 
         file_space = H5Dget_space(ds_id)
         space_type = H5Sget_simple_extent_type(file_space)
@@ -499,7 +499,7 @@ def py_dtype(hid_t dset_id):
 
     try:
         type_id = H5Dget_type(dset_id)
-        return h5t.py_h5t_to_dtype(type_id)
+        return h5t.py_translate_h5t(type_id)
     finally:
         if type_id:
             PY_H5Tclose(type_id)
diff --git a/h5py/h5f.pyx b/h5py/h5f.pyx
index 20e316c..b9ba0bd 100644
--- a/h5py/h5f.pyx
+++ b/h5py/h5f.pyx
@@ -16,7 +16,7 @@
 
 # Pyrex compile-time imports
 from h5p cimport H5P_DEFAULT
-from utils cimport emalloc, efree
+from utils cimport emalloc, efree, pybool
 
 # Runtime imports
 import h5
@@ -95,7 +95,7 @@ def is_hdf5(char* name):
         Determine if a given file is an HDF5 file.  Note this raises an 
         exception if the file doesn't exist.
     """
-    return bool(H5Fis_hdf5(name))
+    return pybool(H5Fis_hdf5(name))
 
 def reopen(hid_t file_id):
     """ (INT file_id) => INT new_file_id
diff --git a/h5py/h5p.pyx b/h5py/h5p.pyx
index 20864bb..33bf204 100644
--- a/h5py/h5p.pyx
+++ b/h5py/h5p.pyx
@@ -20,7 +20,7 @@ from h5d cimport H5D_layout_t
 from h5z cimport H5Z_filter_t
 
 from utils cimport  require_tuple, convert_dims, convert_tuple, \
-                    emalloc, efree
+                    emalloc, efree, pybool
 
 # Runtime imports
 import h5
@@ -77,7 +77,7 @@ def equal(hid_t plist1, hid_t plist2):
 
         Compare two existing property lists or classes for equality.
     """
-    return bool(H5Pequal(plist1, plist2))
+    return pybool(H5Pequal(plist1, plist2))
 
 # === File creation ===========================================================
 
diff --git a/h5py/h5s.pyx b/h5py/h5s.pyx
index 999a3cc..a5cb8d6 100644
--- a/h5py/h5s.pyx
+++ b/h5py/h5s.pyx
@@ -12,14 +12,11 @@
 
 """
     Low-level interface to the "H5S" family of data-space functions.
-
-    This module is incomplete; it currently only implements hyperslab and 
-    scalar operations.
 """
 
 # Pyrex compile-time imports
 from utils cimport  require_tuple, require_list, convert_dims, convert_tuple, \
-                    emalloc, efree
+                    emalloc, efree, pybool
 
 # Runtime imports
 import h5
@@ -118,7 +115,7 @@ def is_simple(hid_t space_id):
         Determine if an existing dataspace is "simple" (including scalar
         dataspaces). Currently all HDF5 dataspaces are simple.
     """
-    return bool(H5Sis_simple(space_id))
+    return pybool(H5Sis_simple(space_id))
 
 def offset_simple(hid_t space_id, object offset=None):
     """ (INT space_id, TUPLE offset=None)
@@ -317,7 +314,7 @@ def select_valid(hid_t space_id):
         
         Determine if the current selection falls within the dataspace extent.
     """
-    return bool(H5Sselect_valid(space_id))
+    return pybool(H5Sselect_valid(space_id))
 
 # === Point selection functions ===============================================
 
diff --git a/h5py/h5t.pyx b/h5py/h5t.pyx
index adea4d3..121a8ff 100644
--- a/h5py/h5t.pyx
+++ b/h5py/h5t.pyx
@@ -18,37 +18,49 @@
     dtype objects.  Constants are also defined in this module for a variety
     of HDF5 native types and classes. Points of interest:
 
-    (1) Enumerations
-    There is no native Numpy or Python type for enumerations.  Since an
-    enumerated type is simply a mapping between string names and integer
-    values, I have implemented enum support through dictionaries.  An HDF5
-    H5T_ENUM type is converted to the appropriate Numpy integer type (e.g.
-    <u4, etc.), and a dictionary mapping names to values is also generated.
-    Since dtype objects cannot be subclassed (why?) and have no provision
-    for directly attached metadata, the dtype is given a single named field 
-    ("enum") and the dictionary stored in the metadata for that field. An
-    example dtype declaration for this is:
+    1. Translation
+
+        The functions py_translate_h5t and py_translate_dtype do the heavy
+        lifting required to go between HDF5 datatype objects and Numpy dtypes.
+
+        Since the HDF5 library can represent a greater range of types than
+        Numpy, the conversion is asymmetric.  Attempting to convert an HDF5
+        type to a Numpy dtype will result in a dtype object which matches
+        as closely as possible.  In contrast, converting from a Numpy dtype
+        to an HDF5 type will always result in a precise, byte-compatible 
+        description of the Numpy data layout.
+
+    2. Complex numbers
+
+        Since HDF5 has no native complex types, and the native Numpy
+        representation is a struct with two floating-point members, complex
+        numbers are saved as HDF5 compound objects.
+
+        These compound objects have exactly two fields, with IEEE 32- or 64-
+        bit format, and default names "r" and "i".  Since other conventions
+        exist for field naming, and in fact may be essential for compatibility
+        with external tools, new names can be specified as arguments to
+        both py_translate_* functions.
+
+    3. Enumerations
+
+        There is no native Numpy or Python type for enumerations.  Since an
+        enumerated type is simply a mapping between string names and integer
+        values, I have implemented enum support through dictionaries.  
+
+        An HDF5 H5T_ENUM type is converted to the appropriate Numpy integer 
+        type (e.g. <u4, etc.), and a dictionary mapping names to values is also 
+        generated. This dictionary is attached to the dtype object via the
+        functions py_enum_attach and py_enum_recover.
+
+        The exact dtype declaration is given below; howeve, the py_enum*
+        functions should encapsulate almost all meaningful operations.
 
         enum_dict = {'RED': 0L, 'GREEN': 1L}
 
         dtype( ('<i4', [ ( (enum_dict, 'enum'),   '<i4' )] ) )
                   ^             ^         ^         ^
              (main type)  (metadata) (field name) (field type)
-
-    The functions py_attach_enum and py_recover_enum simplify the attachment
-    and recovery of enumeration dictionaries from integer dtype objects.
-
-    (2) Complex numbers
-    Since HDF5 has no native complex types defined, and the native Numpy
-    representation is a struct with two floating-point members, complex
-    numbers are saved as HDF5 compound objects with IEEE 32/64 floating point
-    and field names (by default) "r" and "i".  Complex numbers can be auto-
-    recovered from HDF5 objects provided they match this format and have
-    compatible field names.  Since other people may have named their fields
-    e.g. "img" and "real", these names can be changed.  The API functions
-    py_dtype_to_h5t and py_h5t_to_dtype take arguments which specify these
-    names.
-
 """
 
 # Pyrex compile-time imports
@@ -57,7 +69,7 @@ from h5p cimport H5P_DEFAULT
 from h5e cimport err_c, pause_errors, resume_errors
 from numpy cimport dtype, ndarray
 
-from utils cimport  emalloc, efree, \
+from utils cimport  emalloc, efree, pybool, \
                     create_ieee_complex64, create_ieee_complex128, \
                     require_tuple, convert_dims, convert_tuple
 
@@ -209,7 +221,7 @@ def equal(hid_t typeid_1, hid_t typeid_2):
         Test whether two identifiers point to the same datatype object.  
         Note this does NOT perform any kind of logical comparison.
     """
-    return bool(H5Tequal(typeid_1, typeid_2))
+    return pybool(H5Tequal(typeid_1, typeid_2))
 
 def lock(hid_t type_id):
     """ (INT type_id)
@@ -262,7 +274,7 @@ def detect_class(hid_t type_id, int classtype):
         Determine if a member of the given class exists in a compound
         datatype.  The search is recursive.
     """
-    return bool(H5Tdetect_class(type_id, <H5T_class_t>classtype))
+    return pybool(H5Tdetect_class(type_id, <H5T_class_t>classtype))
 
 def close(hid_t type_id, int force=1):
     """ (INT type_id, BOOL force=True)
@@ -332,7 +344,7 @@ def is_variable_str(hid_t type_id):
         Please note that reading/writing data in this format is impossible;
         only fixed-length strings are currently supported.
     """
-    return bool(H5Tis_variable_str(type_id))
+    return pybool(H5Tis_variable_str(type_id))
 
 # === Compound datatype operations ============================================
 
@@ -640,15 +652,16 @@ def _validate_names(names):
     raise ValueError("Compound names must be given as a tuple of strings.")
 
 
-def py_h5t_to_dtype(hid_t type_id, object byteorder=None, 
+def py_translate_h5t(hid_t type_id, object byteorder=None, 
                         object compound_names=None, object complex_names=None):
     """ (INT type_id, STRING byteorder=None, TUPLE compound_names=None.
          TUPLE complex_names=None)
         => DTYPE
 
         Create a Numpy dtype object as similar as possible to the given HDF5
-        datatype object.  The result is not guaranteed to be memory-compatible
-        with the original datatype object.
+        datatype object.  The result is guaranteed to be logically compatible
+        with the original object, with no loss of precision, but may not
+        implement the same memory layout as the HDF5 type.
 
         Optional arguments:
 
@@ -734,7 +747,7 @@ def py_h5t_to_dtype(hid_t type_id, object byteorder=None,
             try:
                 tmp_name = get_member_name(type_id, i)
                 field_names.append(tmp_name)
-                field_types.append(py_h5t_to_dtype(tmp_id, byteorder,
+                field_types.append(py_translate_h5t(tmp_id, byteorder,
                                         None, complex_names))
             finally:
                 PY_H5Tclose(tmp_id)
@@ -772,16 +785,16 @@ def py_h5t_to_dtype(hid_t type_id, object byteorder=None,
         # enum field entry carrying a dictionary as metadata
         super_tid = H5Tget_super(type_id)
         try:
-            edct = py_enum_to_dict(type_id)
+            edct = py_translate_enum(type_id)
             # Superclass must be an integer, so only provide byteorder.
-            typeobj = py_attach_enum(edct, py_h5t_to_dtype(super_tid, byteorder))
+            typeobj = py_enum_attach(edct, py_translate_h5t(super_tid, byteorder))
         finally:
             PY_H5Tclose(super_tid)
 
     elif classtype == H5T_ARRAY:
         super_tid = get_super(type_id)
         try:
-            base_dtype = py_h5t_to_dtype(super_tid, byteorder, compound_names, complex_names)
+            base_dtype = py_translate_h5t(super_tid, byteorder, compound_names, complex_names)
         finally:
             PY_H5Tclose(super_tid)
         shape = get_array_dims(type_id)
@@ -794,7 +807,7 @@ def py_h5t_to_dtype(hid_t type_id, object byteorder=None,
 
     return typeobj
 
-def py_dtype_to_h5t(dtype dtype_in not None, object complex_names=None):
+def py_translate_dtype(dtype dtype_in not None, object complex_names=None):
     """ ( DTYPE dtype_in, TUPLE complex_names=None) => INT type_id
 
         Given a Numpy dtype object, generate a byte-for-byte memory-compatible
@@ -838,14 +851,14 @@ def py_dtype_to_h5t(dtype dtype_in not None, object complex_names=None):
         # Check for enumerated type first
         if (kind == c'u' or kind == c'i') and len(names) == 1 and names[0] == 'enum':
             basetype = _code_map[dtype_in.str]
-            type_out = py_dict_to_enum(py_recover_enum(dtype_in), basetype)
+            type_out = py_translate_dict(py_enum_recover(dtype_in), basetype)
 
         # Otherwise it's just a compound type
         else:
             type_out = create(H5T_COMPOUND, length)
             for name in dtype_in.names:
                 dt, offset = dtype_in.fields[name]
-                tmp = py_dtype_to_h5t(dt, complex_names)
+                tmp = py_translate_dtype(dt, complex_names)
                 try:
                     insert(type_out, name, offset, tmp)
                 finally:
@@ -875,7 +888,7 @@ def py_dtype_to_h5t(dtype dtype_in not None, object complex_names=None):
     elif kind == c'V':
 
         if dtype_in.subdtype:
-            basetype = py_dtype_to_h5t(dtype_in.subdtype[0], complex_names)
+            basetype = py_translate_dtype(dtype_in.subdtype[0], complex_names)
             try:
                 type_out = array_create(basetype, dtype_in.subdtype[1])
             finally:
@@ -894,7 +907,7 @@ def py_dtype_to_h5t(dtype dtype_in not None, object complex_names=None):
     return type_out
 
 
-def py_enum_to_dict(hid_t type_id):
+def py_translate_enum(hid_t type_id):
     """ (INT type_id) => DICT enum
         
         Produce a dictionary in the format [STRING] => LONG from
@@ -911,11 +924,11 @@ def py_enum_to_dict(hid_t type_id):
 
     return dictout
 
-def py_dict_to_enum(object enumdict, hid_t basetype):
-    """ (DICT enum, INT base_type_id) => INT new_type_id
+def py_translate_dict(object enumdict, hid_t basetype):
+    """ (DICT enumdict, INT basetype) => INT new_type_id
 
-        Create a new HDF5 enumeration from a Python dictionary in the format
-        [string name] => long value, and an HDF5 base type
+        Create a new HDF5 enumeration from a Python dictionary in the 
+        format [string name] => long value, and an HDF5 base type.
     """
     cdef hid_t type_id
     type_id = enum_create(basetype)
@@ -924,18 +937,19 @@ def py_dict_to_enum(object enumdict, hid_t basetype):
 
     return type_id
 
-def py_attach_enum(object enumdict, object basetype):
+def py_enum_attach(object enumdict, object base_dtype):
     """ (DICT enum, DTYPE base_dtype) => DTYPE new_dtype
 
         Convert a Python dictionary in the format [string] => integer to a 
-        Numpy dtype with associated enum dictionary.
+        Numpy dtype with associated enum dictionary.  Returns a new
+        dtype object; does not mutate the original.
     """
-    return dtype( (basetype, [( (enumdict, 'enum'), basetype )] ) )
+    return dtype( (base_dtype, [( (enumdict, 'enum'), base_dtype )] ) )
 
-def py_recover_enum(dtype dtype_in):
+def py_enum_recover(dtype dtype_in):
     """ (DTYPE dtype_with_enum) => DICT enum
 
-        Extract the enum dictionary from a Numpy dtype object
+        Get the enum dictionary from a Numpy dtype object.
     """
     cdef object names
     names = dtype_in.names
@@ -949,8 +963,8 @@ def py_recover_enum(dtype dtype_in):
 def py_list_compound_names(hid_t type_in):
     """ (INT type_id) => LIST compound_names
    
-        Obtain a Python list of member names for a compound or enumeration
-        type.
+        Obtain a Python list of member names for a compound or
+        enumeration type.
     """
     cdef int nmem
     cdef int i
@@ -968,21 +982,22 @@ def py_can_convert_dtype(object dt, object complex_names=None):
 
         Test whether the given Numpy dtype can be converted to the appropriate
         memory-compatible HDF5 datatype.  complex_names works as in the
-        function h5t.py_dtype_to_h5t.
+        function h5t.py_translate_dtype.
     """
     cdef hid_t tid
     tid = 0
-    can_convert = False
+
+    retval = None
     try:
-        tid = py_dtype_to_h5t(dt, complex_names)
-        can_convert = True
+        tid = py_translate_dtype(dt, complex_names)
+        retval = True
     except ValueError:
-        pass
+        retval = False
 
     if tid:
         PY_H5Tclose(tid)
 
-    return can_convert
+    return retval
 
 PY_SIGN = DDict({H5T_SGN_NONE: "UNSIGNED", H5T_SGN_2: "SIGNED"})
 
diff --git a/h5py/h5z.pyx b/h5py/h5z.pyx
index fb29c01..18365fb 100644
--- a/h5py/h5z.pyx
+++ b/h5py/h5z.pyx
@@ -15,7 +15,7 @@
     Filter API and constants
 """
 # Pyrex compile-time imports
-include "std_inline.pyx"
+from utils cimport pybool
 
 # Runtime imports
 import h5
diff --git a/h5py/highlevel.py b/h5py/highlevel.py
index 09e8c2a..4f862b1 100644
--- a/h5py/highlevel.py
+++ b/h5py/highlevel.py
@@ -56,7 +56,7 @@ import h5d
 import h5t
 import h5a
 import h5p
-from errors import H5Error
+from h5e import H5Error
 
 # === Main classes (Dataset/Group/File) =======================================
 
@@ -78,7 +78,8 @@ class Dataset(object):
         dtype       A Numpy dtype representing the array data-type.
 
         Writable properties:
-        cnames:     HDF5 compound names used for complex I/O
+        cnames:     HDF5 compound names used for complex I/O.  This can be
+                    None, (), or a 2-tuple with ("realname", "imgname").
     """
 
     # --- Properties (Dataset) ------------------------------------------------
@@ -110,24 +111,25 @@ class Dataset(object):
 
     # --- Public interface (Dataset) ------------------------------------------
 
-    def __init__(self, group, name, create=False,
+    def __init__(self, group, name,
                     data=None, dtype=None, shape=None, 
                     chunks=None, compression=None, shuffle=False, fletcher32=False):
         """ Create a new Dataset object.  There are two modes of operation:
 
             1.  Open an existing dataset
-                If "create" is false, open an existing dataset.  An exception
-                will be raised if it doesn't exist.
+                If you only supply the required parameters "group" and "name",
+                the object will attempt to open an existing HDF5 dataset.
 
             2.  Create a dataset
-                If "create" is True, create a new dataset.  You must supply
-                *either* "data", which must be a Numpy array from which the 
-                shape, dtype and initial contents will be determined, or *both* 
-                "dtype" (Numpy dtype object) and "shape" (tuple of dimensions).
-                Chunks/compression/shuffle/fletcher32 can also be specified.
+                You can supply either:
+                - Keyword "data"; a Numpy array from which the shape, dtype and
+                    initial contents will be determined.
+                - Both "dtype" (Numpy dtype object) and "shape" (tuple of 
+                    dimensions).
 
-                Creating a dataset will fail if another of the same name
-                already exists.
+            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.
 
             Creation keywords (* is default):
 
@@ -136,13 +138,13 @@ class Dataset(object):
             shuffle:       Use the shuffle filter? (requires compression) T/F*
             fletcher32:    Enable Fletcher32 error detection? T/F*
         """
-        if create:
-            self.id = h5d.py_create(group.id, name, data, shape, 
-                                    chunks, compression, shuffle, fletcher32)
-        else:
+        if data is None and dtype is None and shape is None:
             if any((data,dtype,shape,chunks,compression,shuffle,fletcher32)):
                 raise ValueError('You cannot specify keywords when opening a dataset.')
             self.id = h5d.open(group.id, name)
+        else:
+            self.id = h5d.py_create(group.id, name, data, shape, 
+                                    chunks, compression, shuffle, fletcher32)
 
         self._attrs = AttributeManager(self)
         self._byteorder = None
@@ -158,24 +160,27 @@ class Dataset(object):
             ds[:] => All elements, regardless of dimension.
 
             ds[0:3, 1:4, "a", "b"] => (3 x 3) slice, only including compound
-                                      elements "a" and "b".
+                                      elements "a" and "b", in that order.
         """
         start, count, stride, names = slicer(self.shape, args)
 
         if names is not None and self.dtype.names is None:
-            raise ValueError('This dataset has no named fields (requested "%s")' % ", ".join(names))
-
+            raise ValueError('This dataset has no named fields.')
         tid = 0
         try:
             tid = h5d.get_type(self.id)
-            dt = h5t.py_h5t_to_dtype(tid, byteorder=self._byteorder,
+            dt = h5t.py_translate_h5t(tid, byteorder=self._byteorder,
                                      compound_names=names,
                                      complex_names=self._cnames)
         finally:
             if tid != 0:
-                h5t.close(tid, force=True)
+                h5t.close(tid)
 
-        return h5d.py_read_slab(self.id, start, count, stride, dtype=dt)
+        arr = h5d.py_read_slab(self.id, start, count, stride, dtype=dt)
+        if names is not None and len(names) == 1:
+            # Match Numpy convention for recarray indexing
+            return arr[names[0]]
+        return arr
 
     def __setitem__(self, args):
         """ Write to the underlying array from an existing Numpy array.  The
@@ -191,17 +196,15 @@ class Dataset(object):
         h5d.py_write_slab(self.id, args[-1], start, stride)
 
     def close(self):
-        """ Force the HDF5 library to close and free this object.  You 
-            shouldn't need to do this in normal operation; HDF5 objects are 
-            automatically closed when their Python counterparts are deallocated.
+        """ Force the HDF5 library to close and free this object. This
+            will be called automatically when the object is garbage collected,
+            if it hasn't already.
         """
         h5d.close(self.id)
 
     def __del__(self):
-        try:
+        if h5i.get_type(self.id) == h5i.DATASET:
             h5d.close(self.id)
-        except H5Error:
-            pass
 
     def __str__(self):
         return 'Dataset: '+str(self.shape)+'  '+repr(self.dtype)
@@ -224,7 +227,7 @@ class Group(object):
           the special case of a scalar dataset, a Numpy array scalar is
           returned.
 
-        - Setting items: See the __setitem__ docstring; the rules are:
+        - 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
@@ -238,6 +241,9 @@ class Group(object):
         - 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):
@@ -247,14 +253,13 @@ class Group(object):
             raising an exception if it doesn't exist.  If "create" is True,
             create a new HDF5 group and link it into the parent group.
         """
-        self.id = 0
         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)
+        self._attrs = AttributeManager(self)
 
     def __delitem__(self, name):
         """ Unlink a member from the HDF5 group.
@@ -276,20 +281,15 @@ class Group(object):
         if isinstance(obj, Group) or isinstance(obj, Dataset):
             h5g.link(self.id, name, h5i.get_name(obj.id), link_type=h5g.LINK_HARD)
 
-        elif isinstance(obj, numpy.ndarray):
+        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, create=True)
+                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))
 
-        else:
-            arr = numpy.array(obj)
-            if h5t.py_can_convert_dtype(arr.dtype):
-                dset = Dataset(self, name, data=arr, create=True)
-                dset.close()
-            else:
-                raise ValueError("Don't know how to store data of this type in a dataset: " + repr(arr.dtype))
 
     def __getitem__(self, name):
         """ Retrive the Group or Dataset object.  If the Dataset is scalar,
@@ -326,10 +326,8 @@ class Group(object):
         h5g.close(self.id)
 
     def __del__(self):
-        try:
+        if h5i.get_type(self.id) == h5i.GROUP:
             h5g.close(self.id)
-        except H5Error:
-            pass
 
     def __str__(self):
         return 'Group (%d members): ' % self.nmembers + ', '.join(['"%s"' % name for name in self])
@@ -341,13 +339,16 @@ 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):
@@ -366,20 +367,23 @@ class File(Group):
         if not mode in self._modes:
             raise ValueError("Invalid mode; must be one of %s" % ', '.join(self._modes))
               
-        plist = h5p.create(h5p.CLASS_FILE_ACCESS)
+        plist = h5p.create(h5p.FILE_ACCESS)
         try:
             h5p.set_fclose_degree(plist, h5f.CLOSE_STRONG)
             if mode == 'r':
-                self.id = h5f.open(name, h5f.ACC_RDONLY, access_id=plist)
+                self.fid = h5f.open(name, h5f.ACC_RDONLY, access_id=plist)
             elif 'r' in mode or 'a' in mode:
-                self.id = h5f.open(name, h5f.ACC_RDWR, access_id=plist)
+                self.fid = h5f.open(name, h5f.ACC_RDWR, access_id=plist)
             elif noclobber:
-                self.id = h5f.create(name, h5f.ACC_EXCL, access_id=plist)
+                self.fid = h5f.create(name, h5f.ACC_EXCL, access_id=plist)
             else:
-                self.id = h5f.create(name, h5f.ACC_TRUNC, access_id=plist)
+                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
@@ -389,12 +393,16 @@ class File(Group):
         """ Close this HDF5 object.  Note that any further access to objects
             defined in this file will raise an exception.
         """
-        h5f.close(self.id)
+        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.id)
+        h5f.flush(self.fid)
 
     def __del__(self):
         """ This docstring is here to remind you that THE HDF5 FILE IS NOT 
@@ -453,7 +461,8 @@ class AttributeManager(object):
         return h5a.get_num_attrs(self.id)
 
     def __iter__(self):
-        return h5a.py_listattrs(self.id)
+        for name in h5a.py_listattrs(self.id):
+            yield name
 
     def iteritems(self):
         for name in self:
@@ -467,19 +476,19 @@ class NamedType(object):
     """ Represents a named datatype, stored in a file.  
 
         HDF5 datatypes are typically represented by their Numpy dtype
-        equivalents; this class exists mainly to provide access to attributes
+        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
 
-        Mutating the returned dtype object has no effect on the underlying
-        HDF5 datatype.
+        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_h5t_to_dtype(self.id)
+            self._dtype = h5t.py_translate_h5t(self.id)
         return self._dtype
 
     dtype = property(_get_dtype)
@@ -499,7 +508,7 @@ class NamedType(object):
 
         if dtype is not None:
             dtype = numpy.dtype(dtype)
-            tid = h5t.py_dtype_to_h5t(dtype)
+            tid = h5t.py_translate_dtype(dtype)
             try:
                 h5t.commit(group.id, name, tid)
             finally:
@@ -509,18 +518,16 @@ class NamedType(object):
         self.attrs = AttributeManager(self)
 
     def close(self):
-        """ Force the library to close this object.  Not ordinarily required.
+        """ 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:
-            try:
-                h5t.close(self.id)
-            except H5Error:
-                pass
-    
+            h5t.close(self.id)
 
 
 # === Browsing and interactivity ==============================================
@@ -614,7 +621,7 @@ class _H5Cmd(cmd.Cmd):
         for name in self.group:
             outstring = name
             type_code = h5g.get_objinfo(self.group.id, name).type
-            if type_code == h5g.OBJ_GROUP:
+            if type_code == h5g.GROUP:
                 outstring += "/"
 
             if extended:
@@ -664,7 +671,7 @@ class _H5Cmd(cmd.Cmd):
 
     def complete_cd(self, text, line, begidx, endidx):
         return [x for x in self.group if x.find(text)==0 \
-                    and h5g.get_objinfo(self.group.id,x).type == h5g.OBJ_GROUP]
+                    and h5g.get_objinfo(self.group.id,x).type == h5g.GROUP]
 
     def help_cd(self):
         print ""
@@ -706,16 +713,16 @@ def _open_arbitrary(group_obj, name):
     """
     info = h5g.get_objinfo(group_obj.id, name)
 
-    if info.type == h5g.OBJ_GROUP:      # group
+    if info.type == h5g.GROUP:      # group
         return Group(group_obj, name)
 
-    elif info.type == h5g.OBJ_DATASET:  # dataset
+    elif info.type == h5g.DATASET:  # dataset
         return Dataset(group_obj, name)
 
-    elif info.type == h5g.OBJ_DATATYPE: # named type
+    elif info.type == h5g.DATATYPE: # named type
         return NamedDatatype(group_obj, name)
 
-    raise NotImplementedError('Object type "%s" unsupported by the high-level interface.' % h5g.OBJ_MAPPER[info.type])
+    raise NotImplementedError('Object type "%s" unsupported by the high-level interface.' % h5g.PY_TYPE[info.type])
 
 def slicer(shape, args):
     """ Processes arguments to __getitem__ methods.  
@@ -815,7 +822,6 @@ def slicer(shape, args):
             start.append(ss)
             stride.append(st)
             count.append(cc)
-            slices.append(arg)
 
     return (tuple(start), tuple(count), tuple(stride), names)
 
diff --git a/h5py/std_defs.pxi b/h5py/std_defs.pxi
index 13aeb16..1dd4ab5 100644
--- a/h5py/std_defs.pxi
+++ b/h5py/std_defs.pxi
@@ -12,6 +12,8 @@
 
 # "Boilerplate" includes which are so common I don't want to repeat them
 # in every file.  These include all basic HDF5 and C typedefs.
+# This file is designed to be included in *.pxd files; only definitions
+# are allowed.
 
 from h5 cimport hid_t, hbool_t, herr_t, htri_t, hsize_t, \
                 hssize_t, haddr_t, hvl_t
diff --git a/h5py/std_inline.pyx b/h5py/std_inline.pyx
deleted file mode 100644
index 375288f..0000000
--- a/h5py/std_inline.pyx
+++ /dev/null
@@ -1,19 +0,0 @@
-#+
-# 
-# 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$
-# 
-#-
-
-# Simple defs which aren't worth putting in their own module.
-
-cdef object pybool(long val):
-    # It seems Pyrex's bool() actually returns some sort of int.
-    if val:
-        return True
-    return False
diff --git a/h5py/tests/__init__.py b/h5py/tests/__init__.py
index 0b2c914..f7f5501 100644
--- a/h5py/tests/__init__.py
+++ b/h5py/tests/__init__.py
@@ -13,13 +13,13 @@
 import unittest
 import sys
 import test_h5a, test_h5f, test_h5i, test_h5d, \
-        test_h5g, test_h5, test_h5s, test_h5p
+        test_h5g, test_h5, test_h5s, test_h5p, test_highlevel
 
-from h5py import h5a, h5f, h5g, h5d, h5s, h5i, h5z, h5p#, highlevel
+from h5py import h5a, h5f, h5g, h5d, h5s, h5i, h5z, h5p, highlevel
 
 TEST_CASES = (test_h5a.TestH5A, test_h5f.TestH5F, test_h5g.TestH5G,
               test_h5i.TestH5I, test_h5d.TestH5D, test_h5.TestH5,
-              test_h5s.TestH5S, test_h5p.TestH5P)
+              test_h5s.TestH5S, test_h5p.TestH5P, test_highlevel.TestHighlevel)
 
 def buildsuite(cases):
 
diff --git a/h5py/tests/test_h5a.py b/h5py/tests/test_h5a.py
index ba7c1d8..e8beb4e 100644
--- a/h5py/tests/test_h5a.py
+++ b/h5py/tests/test_h5a.py
@@ -54,7 +54,7 @@ class TestH5A(unittest.TestCase):
             arr_fail = ones((15,15), dtype=dt)
 
             sid = h5s.create(h5s.SCALAR)
-            tid = h5t.py_dtype_to_h5t(dt)
+            tid = h5t.py_translate_dtype(dt)
 
             aid = h5a.create(obj, name, tid, sid)
             self.assert_(self.is_attr(aid))
@@ -163,7 +163,7 @@ class TestH5A(unittest.TestCase):
         for name, (value, dt, shape) in ATTRIBUTES.iteritems():
             aid = h5a.open_name(self.obj, name)
             tid = h5a.get_type(aid)
-            supposed_dtype = h5t.py_h5t_to_dtype(tid)
+            supposed_dtype = h5t.py_translate_h5t(tid)
             self.assertEqual(supposed_dtype, dt)
             h5t.close(tid)
             h5a.close(aid)
diff --git a/h5py/tests/test_highlevel.py b/h5py/tests/test_highlevel.py
new file mode 100644
index 0000000..e4c0996
--- /dev/null
+++ b/h5py/tests/test_highlevel.py
@@ -0,0 +1,59 @@
+#+
+# 
+# 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$
+# 
+#-
+
+import unittest
+import os
+from numpy import all
+
+import h5py
+from h5py.highlevel import *
+
+# --- Description of the PyTables test file smpl_compound_chunked -------------
+
+HDFNAME2 = os.path.join(os.path.dirname(h5py.__file__), 'tests/data/smpl_compound_chunked.hdf5')
+DTYPE = numpy.dtype([('a_name','>i4'),
+                     ('c_name','|S6'),
+                     ('d_name', numpy.dtype( ('>i2', (5,10)) )),
+                     ('e_name', '>f4'),
+                     ('f_name', numpy.dtype( ('>f8', (10,)) )),
+                     ('g_name', '<u1')])
+SHAPE = (6,)
+
+basearray = numpy.ndarray(SHAPE, dtype=DTYPE)
+for i in range(SHAPE[0]):
+    basearray[i]["a_name"] = i,
+    basearray[i]["c_name"] = "Hello!"
+    basearray[i]["d_name"][:] = numpy.sum(numpy.indices((5,10)),0) + i # [:] REQUIRED for some stupid reason
+    basearray[i]["e_name"] = 0.96*i
+    basearray[i]["f_name"][:] = numpy.array((1024.9637*i,)*10)
+    basearray[i]["g_name"] = 109
+
+names = ("a_name","c_name","d_name","e_name","f_name","g_name")
+
+class TestHighlevel(unittest.TestCase):
+
+
+    def test_ds(self):
+        myfile = File(HDFNAME2,'r')
+        try:
+            ds = myfile["CompoundChunked"]
+            for i in range(6):
+                self.assert_(all(ds[i]==basearray[i]), "%d"%i)
+            for name in names:
+                self.assert_(all(ds[name]==basearray[name]))
+        finally:
+            myfile.close()
+            
+
+
+
+
diff --git a/h5py/utils.pxd b/h5py/utils.pxd
index 7a88189..96bd74e 100644
--- a/h5py/utils.pxd
+++ b/h5py/utils.pxd
@@ -36,4 +36,5 @@ cdef extern from "utils_low.h":
 
 cdef int require_tuple(object tpl, int none_allowed, int size, char* name) except -1
 cdef int require_list(object lst, int none_allowed, int size, char* name) except -1
+cdef object pybool(long long val)
 
diff --git a/h5py/utils.pyx b/h5py/utils.pyx
index 38c2da7..02a54a1 100644
--- a/h5py/utils.pyx
+++ b/h5py/utils.pyx
@@ -50,6 +50,13 @@ cdef int require_list(object lst, int none_allowed, int size, char* name) except
     PyErr_SetString(ValueError, msg)
     return -1
 
+cdef object pybool(long long val):
+    # It seems Pyrex's bool() actually returns some sort of int.
+    # This is OK for C, but ugly in Python.
+    if val:
+        return True
+    return False
+
 
 
 
diff --git a/setup.py b/setup.py
index 39bfbe7..81e7c76 100644
--- a/setup.py
+++ b/setup.py
@@ -205,7 +205,7 @@ pyx_library_dirs = ['/usr/lib', '/usr/local/lib']
 pyx_library_dirs.extend(custom_library_dirs)
 
 # Additional compiler flags for Pyrex code
-pyx_extra_args = ['-Wno-unused', '-DH5_USE_16_API']
+pyx_extra_args = ['-Wno-unused', '-Wno-uninitialized', '-DH5_USE_16_API']
 
 extra_link_args = []
 extra_compile_args = pyx_extra_args

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