[h5py] 67/455: Many fixes and new tests; fix exit loop linked to reference counts. Close() functions now private.

Ghislain Vaillant ghisvail-guest at moszumanska.debian.org
Thu Jul 2 18:19:18 UTC 2015


This is an automated email from the git hooks/post-receive script.

ghisvail-guest pushed a commit to annotated tag 1.3.0
in repository h5py.

commit 9eaf22566ced6a39b49a0ddcb3c359196ce7ebb4
Author: andrewcollette <andrew.collette at gmail.com>
Date:   Mon Jul 7 00:53:10 2008 +0000

    Many fixes and new tests; fix exit loop linked to reference counts. Close() functions now private.
---
 h5py/browse.py         | 149 +++++++++++++++++++++++-------------
 h5py/h5.pxd            |   7 +-
 h5py/h5.pyx            |  43 ++++++++++-
 h5py/h5a.pyx           |  17 +----
 h5py/h5d.pyx           |  25 ++----
 h5py/h5f.pyx           |  40 +++++++---
 h5py/h5g.pyx           |   2 +-
 h5py/h5p.pyx           |   2 +-
 h5py/h5s.pyx           |   2 +-
 h5py/h5t.pxd           |   1 -
 h5py/h5t.pyx           | 203 +++++++++++++++++++++++++++----------------------
 h5py/highlevel.py      |  45 +++++++++--
 h5py/tests/__init__.py |   6 +-
 h5py/tests/common.py   |   1 -
 h5py/tests/test_h5a.py |  14 +---
 h5py/tests/test_h5d.py |   4 +-
 h5py/tests/test_h5g.py |   6 +-
 h5py/tests/test_h5i.py |   2 +-
 h5py/tests/test_h5s.py |   2 +-
 19 files changed, 347 insertions(+), 224 deletions(-)

diff --git a/h5py/browse.py b/h5py/browse.py
index cb8e011..10e7088 100644
--- a/h5py/browse.py
+++ b/h5py/browse.py
@@ -10,66 +10,54 @@
 # 
 #-
 
-import cmd
-from getopt import gnu_getopt
+from cmd import Cmd
+from posixpath import join, basename, dirname, normpath, isabs
+from getopt import gnu_getopt, GetoptError
+import shlex
 import os
+import re
+import sys
+
 from utils_hl import hbasename
-from posixpath import join, basename, dirname, normpath, isabs
 
-from h5py.highlevel import File, Group, Dataset, Datatype
+
 from h5py import h5g
 
 NAMES = {h5g.DATASET: "Dataset", h5g.GROUP: "Group", h5g.TYPE: "Named Type"}
-LS_FORMAT = " %-10s    %-10s"
+LS_FORMAT = " %-20s    %-10s"
+
+class CmdError(StandardError):
+    pass
 
-class _H5Browser(cmd.Cmd):
+# Why the hell doesn't Cmd inherit from object?  Properties don't work!
+class _H5Browser(Cmd, object):
 
     """
         HDF5 file browser class which holds state between sessions.
     """
+    def _setpath(self, path):
+        self.prompt = "HDF5: %s> " % (hbasename(path))
+        self._path = path
 
-    def __init__(self):
-        """ Create a new browser instance.
-        """
-        cmd.Cmd.__init__(self)
-        self.path = '/'
-        self.known_paths = {}
-        self.file = None #: Holds a File instance while executing, the file name otherwise.
+    path = property(lambda self: self._path, _setpath)
 
-    def __call__(self, what=None, mode='r', importdict=None):
+    def __init__(self, fileobj, path=None, importdict=None):
         """ Browse the file, putting any imported names into importdict. """
+        Cmd.__init__(self)
+        self.file = fileobj
 
-        if what is None:
-            if self.file is None:
-                raise ValueError("Either a file name or File object must be supplied.")
-            else:
-                self.file = File(self.file, mode=mode)
-
-        elif isinstance(what, File):
-            self.file = what
-
-        elif isinstance(what, str):
-            self.file = File(what, mode=mode)
-
-        else:
-            raise ValueError("Only a string file name or an File object may be supplied.")
-
-        # Now self.file is a File object, no matter what
-
-        try:
-            self.path = self.known_paths[os.path.abspath(self.file.name)]
-        except KeyError:
-            self.path = '/'
+        self.path = path if path is not None else '/'
 
         self.importdict = importdict
         self.cmdloop('Browsing "%s". Type "help" for commands, "exit" to exit.' % os.path.basename(self.file.name))
-        self.importdict = None  # don't hold a reference to this between browse sessions
-
-        self.known_paths[os.path.abspath(self.file.name)] = self.path
-        self.file = self.file.name
 
-    def _error(self, msg):
-        print "Error: "+str(msg)
+    def onecmd(self, line):
+        retval = False
+        try:
+            retval = Cmd.onecmd(self, line)
+        except (CmdError, GetoptError), e:
+            print "Error: "+e.args[0]
+        return retval
 
     def abspath(self, path):
         """ Correctly interpret the given path fragment, relative to the
@@ -78,27 +66,34 @@ class _H5Browser(cmd.Cmd):
         return normpath(join(self.path,path))
 
     def do_exit(self, line):
+        """ Exit back to Python """
         return True
 
     def do_EOF(self, line):
+        """ (Ctrl-D) Exit back to Python """
         return True
 
     def do_pwd(self, line):
+        """ Print name of current group """
         print self.path
 
     def do_cd(self, line):
-        path = line.strip()
-        if path == '': path = '/'
+        """ cd [group] """
+        args = shlex.split(line)
+        if len(args) > 1:
+            raise CmdError("Too many arguments")
+        path = args[0] if len(args) == 1 else ''
+
         path = self.abspath(path)
         dname = dirname(path)
         bname = basename(path)
         try:
             if bname != '' and not self.file[dname].id.get_objinfo(bname).type == h5g.GROUP:
-                self._error('"%s" is not an HDF5 group' % bname)
+                raise CmdError('"%s" is not an HDF5 group' % bname)
             else:
                 self.path = path
         except:
-            self._error('Can\'t open group "%s"' % path)
+            raise CmdError('Can\'t open group "%s"' % path)
 
     def complete_cd(self, text, line, begidx, endidx):
         text = text.strip()
@@ -110,16 +105,12 @@ class _H5Browser(cmd.Cmd):
                     if x.find(targetname) == 0 and \
                     grp.id.get_objinfo(x).type == h5g.GROUP]
         return rval
-    
+
     def do_ls(self, line):
-        """ List contents of the specified group, or this one """
+        """ ls [-l] [group] """
 
         LONG_STYLE = False
-        try:
-            opts, args = gnu_getopt(line.split(), 'l')
-        except GetoptError, e:
-            self._error(e.msg.capitalize())
-            return
+        opts, args = gnu_getopt(shlex.split(line), 'l')
 
         if '-l' in [ opt[0] for opt in opts]:
             LONG_STYLE = True
@@ -134,6 +125,7 @@ class _H5Browser(cmd.Cmd):
         try:
             grp = self.file[grpname]
             if LONG_STYLE:
+                print 'Group "%s" in file "%s":' % (hbasename(grpname), os.path.basename(self.file.name))
                 print LS_FORMAT % ("Name", "Type")
                 print LS_FORMAT % ("----", "----")
             for name in grp:
@@ -144,7 +136,58 @@ class _H5Browser(cmd.Cmd):
                 else:
                     print pname
         except:
-            self._error('Can\'t list contents of group "%s"' % hbasename(grpname))
+            raise CmdError('Can\'t list contents of group "%s"' % hbasename(grpname))
+
+    def do_import(self, line):
+        """ import name [as python_name] 
+ import name1 name2 name3 name4 ...
+        """
+        if self.importdict is None:
+            raise CmdError("No import dictionary provided")
+
+        opts, args = gnu_getopt(shlex.split(line),'')
+        
+        pynames = []
+        hnames = []
+
+        importdict = {}   # [Python name] => HDF5 object
+
+        if len(args) == 3 and args[1] == 'as':
+            pynames.append(args[2])
+            hnames.append(args[0])
+        else:
+            for arg in args:
+                absname = self.abspath(arg)
+                pynames.append(basename(absname))
+                hnames.append(absname)
+
+        for pyname, hname in zip(pynames, hnames):
+            try:
+                obj = self.file[hname]
+            except Exception, e:
+                raise CmdError("Can't import %s: %s" % (name, e.args[0].splitlines()[0]))
+
+            if len(re.sub('[A-Za-z_][A-Za-z0-9_]*','',pyname)) != 0:
+                raise CmdError("%s is not a valid Python identifier" % pyname)
+
+            if pyname in self.importdict:
+                if not raw_input("Name %s already in use. Really import (y/N)?  " % pyname).strip().lower().startswith('y'):
+                    continue
+
+            importdict[pyname] = obj
+
+        self.importdict.update(importdict)
+
+    def complete_import(self, text, line, begidx, endidx):
+        text = text.strip()
+        grpname = self.abspath(dirname(text))
+        targetname = basename(text)
+
+        grp = self.file[grpname]
+        rval = [join(grpname,x) for x in grp \
+                    if x.find(targetname) == 0]
+        return rval
+
 
     def complete_ls(self, *args):
         return self.complete_cd(*args)
diff --git a/h5py/h5.pxd b/h5py/h5.pxd
index 4e90b59..464013a 100644
--- a/h5py/h5.pxd
+++ b/h5py/h5.pxd
@@ -15,7 +15,7 @@
 # directory.
 
 include "conditions.pxi"
-from defs_c cimport size_t, ssize_t
+from defs_c cimport size_t, ssize_t, malloc, free
 
 # Common structs and types from HDF5
 cdef extern from "hdf5.h":
@@ -227,7 +227,6 @@ cdef extern from "hdf5.h":
 
   # --- Error handling --------------------------------------------------------
 
-
   char      *H5Eget_major(H5E_major_t n)
   char      *H5Eget_minor(H5E_minor_t n)
   herr_t    H5Eclear() except *
@@ -237,6 +236,10 @@ cdef extern from "hdf5.h":
   ctypedef herr_t (*H5E_walk_t)(int n, H5E_error_t *err_desc, void* client_data)  
   herr_t    H5Ewalk(H5E_direction_t direction, H5E_walk_t func, void* client_data  )
 
+  int       H5Fget_obj_count(hid_t file_id, unsigned int types) except *
+  int       H5Fget_obj_ids(hid_t file_id, unsigned int types, int max_objs, hid_t *obj_id_list) except *
+  int       H5F_OBJ_ALL
+
 # === Custom C extensions =====================================================
 
 ctypedef H5E_auto_t err_c
diff --git a/h5py/h5.pyx b/h5py/h5.pyx
index 76b8231..c75f4a8 100644
--- a/h5py/h5.pyx
+++ b/h5py/h5.pyx
@@ -25,6 +25,8 @@
 include "conditions.pxi"
 from python cimport PyErr_SetObject
 
+import atexit
+
 # Logging is only enabled when compiled with H5PY_DEBUG nonzero
 IF H5PY_DEBUG:
     import logging
@@ -53,6 +55,15 @@ hdf5_version = "%d.%d.%d" % hdf5_version_tuple
 api_version_tuple = (H5PY_API_MAJ, H5PY_API_MIN)
 api_version = H5PY_API
 
+def _close():
+    """ Internal function; do not call unless you want to lose all your data.
+    """
+    H5close()
+
+def _open():
+    """ Internal function; do not call unless you want to lose all your data.
+    """
+    H5open()
 
 class DDict(dict):
     """ Internal class.
@@ -119,14 +130,14 @@ cdef class ObjectID:
         if self._valid:
             ref = str(H5Iget_ref(self.id))
         else:
-            ref = "INVALID"
+            ref = "X"
 
         if self._locked:
-            lstr = "locked"
+            lstr = "L"
         else:
-            lstr = "unlocked"
+            lstr = "U"
 
-        return "%d [%s] (%s) %s" % (self.id, ref, lstr, self.__class__.__name__)
+        return "%9d [%s] (%s) %s" % (self.id, ref, lstr, self.__class__.__name__)
 
     def __repr__(self):
         return self.__str__()
@@ -513,10 +524,34 @@ cdef int resume_errors(err_c cookie) except -1:
 
 # === Library init ============================================================
 
+def _exithack():
+    """ Internal function; do not call unless you want to lose all your data.
+    """
+    # If any identifiers have reference counts > 1 when the library closes,
+    # it freaks out and dumps a message to stderr.  So we have Python dec_ref
+    # everything when the interpreter's about to exit.
+
+    cdef int count
+    cdef int i
+    cdef hid_t *objs
+
+    count = H5Fget_obj_count(H5F_OBJ_ALL, H5F_OBJ_ALL)
+    
+    if count > 0:
+        objs = <hid_t*>malloc(sizeof(hid_t)*count)
+        try:
+            H5Fget_obj_ids(H5F_OBJ_ALL, H5F_OBJ_ALL, count, objs)
+            for i from 0<=i<count:
+                while H5Iget_ref(objs[i]) > 1:
+                    H5Idec_ref(objs[i])
+        finally:
+            free(objs)
+
 cdef int import_hdf5() except -1:
     if H5open() < 0:
         raise RuntimeError("Failed to initialize the HDF5 library.")
     _enable_exceptions()
+    atexit.register(_exithack)
     return 0
 
 import_hdf5()
diff --git a/h5py/h5a.pyx b/h5py/h5a.pyx
index 765aa25..8860961 100644
--- a/h5py/h5a.pyx
+++ b/h5py/h5a.pyx
@@ -162,13 +162,8 @@ cdef class AttrID(ObjectID):
         def __get__(self):
 
             cdef SpaceID space
-            space = None
-            try:
-                space = self.get_space()
-                return space.get_simple_extent_dims()
-            finally:
-                if space is not None:
-                    space.close()
+            space = self.get_space()
+            return space.get_simple_extent_dims()
 
     property dtype:
         """ A Numpy-stype dtype object representing the attribute's datatype
@@ -179,7 +174,7 @@ cdef class AttrID(ObjectID):
             tid = typewrap(H5Aget_type(self.id))
             return tid.py_dtype()
 
-    def close(self):
+    def _close(self):
         """ ()
 
             Close this attribute and release resources.  You don't need to
@@ -200,7 +195,6 @@ cdef class AttrID(ObjectID):
         """
         cdef TypeID mtype
         cdef hid_t space_id
-        mtype = None
         space_id = 0
 
         try:
@@ -214,8 +208,6 @@ cdef class AttrID(ObjectID):
         finally:
             if space_id:
                 H5Sclose(space_id)
-            if mtype is not None:
-                mtype.close()
 
     def write(self, ndarray arr_obj not None):
         """ (NDARRAY arr_obj)
@@ -229,7 +221,6 @@ cdef class AttrID(ObjectID):
         """
         cdef TypeID mtype
         cdef hid_t space_id
-        mtype_id = None
         space_id = 0
 
         try:
@@ -242,8 +233,6 @@ cdef class AttrID(ObjectID):
         finally:
             if space_id:
                 H5Sclose(space_id)
-            if mtype is not None:
-                mtype.close()
 
     def get_name(self):
         """ () => STRING name
diff --git a/h5py/h5d.pyx b/h5py/h5d.pyx
index 29dc9a3..64092c8 100644
--- a/h5py/h5d.pyx
+++ b/h5py/h5d.pyx
@@ -116,7 +116,7 @@ cdef class DatasetID(ObjectID):
             sid = self.get_space()
             return sid.get_simple_extent_ndims()
 
-    def close(self):
+    def _close(self):
         """ ()
 
             Terminate access through this identifier.  You shouldn't have to
@@ -146,18 +146,13 @@ cdef class DatasetID(ObjectID):
         """
         cdef TypeID mtype
         cdef hid_t plist_id
-        mtype = None
         plist_id = pdefault(plist)
 
-        try:
-            mtype = h5t.py_create(arr_obj.dtype)
-            check_numpy_write(arr_obj, -1)
+        mtype = h5t.py_create(arr_obj.dtype)
+        check_numpy_write(arr_obj, -1)
 
-            H5Dread(self.id, mtype.id, mspace.id, fspace.id, plist_id, PyArray_DATA(arr_obj))
+        H5Dread(self.id, mtype.id, mspace.id, fspace.id, plist_id, PyArray_DATA(arr_obj))
 
-        finally:
-            if mtype is not None:
-                mtype.close()
         
     def write(self, SpaceID mspace not None, SpaceID fspace not None, 
                     ndarray arr_obj not None, PropDXID plist=None):
@@ -173,18 +168,12 @@ cdef class DatasetID(ObjectID):
         """
         cdef TypeID mtype
         cdef hid_t plist_id
-        mtype = None
         plist_id = pdefault(plist)
 
-        try:
-            mtype = h5t.py_create(arr_obj.dtype)
-            check_numpy_read(arr_obj, -1)
+        mtype = h5t.py_create(arr_obj.dtype)
+        check_numpy_read(arr_obj, -1)
 
-            H5Dwrite(self.id, mtype.id, mspace.id, fspace.id, plist_id, PyArray_DATA(arr_obj))
-
-        finally:
-            if mtype is not None:
-                mtype.close()
+        H5Dwrite(self.id, mtype.id, mspace.id, fspace.id, plist_id, PyArray_DATA(arr_obj))
 
     def extend(self, object shape):
         """ (TUPLE shape)
diff --git a/h5py/h5f.pyx b/h5py/h5f.pyx
index 3c25fda..0f3f402 100644
--- a/h5py/h5f.pyx
+++ b/h5py/h5f.pyx
@@ -16,6 +16,12 @@
 
 # Pyrex compile-time imports
 from h5p cimport propwrap, pdefault, PropFAID, PropFCID, PropMID
+from h5t cimport typewrap
+from h5a cimport AttrID
+from h5d cimport DatasetID
+from h5g cimport GroupID
+from h5i cimport H5Iget_type, H5Iinc_ref, H5I_type_t, \
+                 H5I_FILE, H5I_GROUP, H5I_ATTR, H5I_DATASET, H5I_DATATYPE
 from utils cimport emalloc, efree, pybool
 
 # Runtime imports
@@ -158,6 +164,29 @@ def get_obj_count(object where=OBJ_ALL, int types=H5F_OBJ_ALL):
 
     return H5Fget_obj_count(where_id, types)
 
+cdef object wrap_identifier(hid_t ident):
+    # Support function for get_obj_ids
+
+    cdef H5I_type_t typecode
+    cdef ObjectID obj
+    typecode = H5Iget_type(ident)
+    if typecode == H5I_FILE:
+        obj = FileID(ident)
+    elif typecode == H5I_DATASET:
+        obj = DatasetID(ident)
+    elif typecode == H5I_GROUP:
+        obj = GroupID(ident)
+    elif typecode == H5I_ATTR:
+        obj = AttrID(ident)
+    elif typecode == H5I_DATATYPE:
+        obj = typewrap(ident)
+    else:
+        raise ValueError("Unrecognized type code %d" % typecode)
+
+    # The HDF5 function doesn't seem to inc_ref these identifiers.
+    H5Iinc_ref(ident)
+    return obj
+
 def get_obj_ids(object where=OBJ_ALL, int types=H5F_OBJ_ALL):
     """ (OBJECT where=OBJ_ALL, types=OBJ_ALL) => LIST open_ids
 
@@ -194,7 +223,7 @@ def get_obj_ids(object where=OBJ_ALL, int types=H5F_OBJ_ALL):
 
         H5Fget_obj_ids(where_id, types, count, obj_list)
         for i from 0<=i<count:
-            py_obj_list.append(obj_list[i])
+            py_obj_list.append(wrap_identifier(obj_list[i]))
         return py_obj_list
 
     finally:
@@ -222,7 +251,6 @@ cdef class FileID(ObjectID):
         """
         H5Fclose(self.id)
 
-
     def reopen(self):
         """ () => FileID
 
@@ -232,7 +260,6 @@ cdef class FileID(ObjectID):
         """
         return FileID(H5Freopen(self.id))
 
-
     def get_filesize(self):
         """ () => LONG size
 
@@ -267,10 +294,3 @@ cdef class FileID(ObjectID):
         return H5Fget_freespace(self.id)
 
 
-
-
-
-
-
-
-    
diff --git a/h5py/h5g.pyx b/h5py/h5g.pyx
index 561d59e..32823aa 100644
--- a/h5py/h5g.pyx
+++ b/h5py/h5g.pyx
@@ -151,7 +151,7 @@ cdef class GroupID(ObjectID):
         Represents an HDF5 group identifier
     """
 
-    def close(self):
+    def _close(self):
         """ ()
 
             Terminate access through this identifier.  You shouldn't have to
diff --git a/h5py/h5p.pyx b/h5py/h5p.pyx
index 03cadd5..813f82b 100644
--- a/h5py/h5p.pyx
+++ b/h5py/h5p.pyx
@@ -117,7 +117,7 @@ cdef class PropInstanceID(PropID):
         """
         return type(self)(H5Pcopy(self.id))
 
-    def close(self):
+    def _close(self):
         """ ()
     
             Terminate access through this identifier.  You shouldn't have to
diff --git a/h5py/h5s.pyx b/h5py/h5s.pyx
index e3944a8..499ebbc 100644
--- a/h5py/h5s.pyx
+++ b/h5py/h5s.pyx
@@ -119,7 +119,7 @@ cdef class SpaceID(ObjectID):
         def __get__(self):
             return self.get_simple_extent_dims()
 
-    def close(self):
+    def _close(self):
         """ ()
 
             Terminate access through this identifier.  You shouldn't have to
diff --git a/h5py/h5t.pxd b/h5py/h5t.pxd
index 7147003..7bb395c 100644
--- a/h5py/h5t.pxd
+++ b/h5py/h5t.pxd
@@ -18,7 +18,6 @@ include "std_defs.pxi"
 from h5 cimport class ObjectID
 
 cdef class TypeID(ObjectID):
-    cdef object _complex_names
 
     cdef object py_dtype(self)
 
diff --git a/h5py/h5t.pyx b/h5py/h5t.pyx
index ee9211e..70654ed 100644
--- a/h5py/h5t.pyx
+++ b/h5py/h5t.pyx
@@ -73,6 +73,8 @@ cdef object lockid(hid_t id_in):
 
 # === Public constants and data structures ====================================
 
+_complex_names = ('r','i')
+
 # Enumeration H5T_class_t
 NO_CLASS  = H5T_NO_CLASS
 INTEGER   = H5T_INTEGER
@@ -201,15 +203,16 @@ _sign_map  = { H5T_SGN_NONE: 'u', H5T_SGN_2: 'i' }
 # === General datatype operations =============================================
 
 def create(int classtype, size_t size):
-    """ (INT classtype, INT size) => TypeID
+    """ (INT classtype, UINT size) => TypeID
         
         Create a new HDF5 type object.  Legal class values are 
-        COMPOUND, OPAQUE, and ENUM.
+        COMPOUND and OPAQUE.  Use enum_create for enums.
     """
     # If it's not one of these, the library SEGFAULTS. Thanks, guys.
-    if classtype != H5T_COMPOUND and classtype != H5T_OPAQUE and \
-        classtype != H5T_ENUM:
-        raise ValueError("Class must be COMPOUND, OPAQUE or ENUM")
+    # Also, creating ENUM types doesn't work right.
+    if classtype != H5T_COMPOUND and classtype != H5T_OPAQUE:
+        raise ValueError("Class must be COMPOUND or OPAQUE.")
+
     return typewrap(H5Tcreate(<H5T_class_t>classtype, size))
 
 def open(ObjectID group not None, char* name):
@@ -266,31 +269,12 @@ cdef class TypeID(ObjectID):
         operations.
     """
 
-    def __init__(self, hid_t id_):
-        self._complex_names = ('r', 'i')
-
     def __copy__(self):
         cdef TypeID cpy
         cpy = ObjectID.__copy__(self)
         assert typecheck(cpy, TypeID), "TypeID copy encounted invalid type"
-        cpy._complex_names = self._complex_names
         return cpy
 
-    property complex_names:
-        """ Either () or a 2-tuple (real, imag) determining how complex types
-            are read/written using HDF5 compound types.
-        """
-        def __get__(self):
-            return self._complex_names
-        def __set__(self, item):
-            if not typecheck(item, tuple) or (len(item) != 0 and len(item) != 2):
-                raise ValueError("py_complex_names must be either () or a 2-tuple of strings")
-            for entry in item:
-                if not typecheck(entry, str):
-                    raise ValueError("py_complex_names must be a 2-tuple of strings")
-
-            self._complex_names = item
-
     property dtype:
         """ A Numpy-style dtype object representing this object.
         """
@@ -301,7 +285,12 @@ cdef class TypeID(ObjectID):
         raise NotImplementedError("Don't know how to convert %s objects to Numpy" % self.__class__.__name__)
 
     def __repr__(self):
-        return str(self)+" "+str(self.dtype)
+        try:
+            dts = str(self.dtype)
+        except:
+            dts = "(Numpy type unknown)"
+
+        return str(self)+" "+dts
 
     def commit(self, ObjectID group not None, char* name):
         """ (ObjectID group, STRING name)
@@ -327,8 +316,8 @@ cdef class TypeID(ObjectID):
     def equal(self, TypeID typeid):
         """ (TypeID typeid) => BOOL
 
-            Test whether two identifiers point to the same datatype object.  
-            Note this does NOT perform any kind of logical comparison.
+            Test whether two identifiers refer to the same datatype.  Seems
+            to perform a logical comparison.
         """
         return pybool(H5Tequal(self.id, typeid.id))
 
@@ -369,21 +358,6 @@ cdef class TypeID(ObjectID):
         """
         return typewrap(H5Tget_super(self.id))
 
-    def get_native_type(self, int direction=H5T_DIR_DEFAULT):
-        """ (INT direction=DIR_DEFAULT) => TypeID
-
-            Determine the native C equivalent for the given datatype.
-            Legal values for "direction" are:
-              DIR_DEFAULT*
-              DIR_ASCEND
-              DIR_DESCEND
-            These determine which direction the list of native datatypes is
-            searched; see the HDF5 docs for a definitive list.
-
-            The returned datatype is always an unlocked copy of one of NATIVE_*
-        """
-        return typewrap(H5Tget_native_type(self.id, <H5T_direction_t>direction))
-
     def detect_class(self, int classtype):
         """ (INT classtype) => BOOL class_is_present
 
@@ -392,8 +366,11 @@ cdef class TypeID(ObjectID):
         """
         return pybool(H5Tdetect_class(self.id, <H5T_class_t>classtype))
 
-    def close(self):
+    def _close(self):
         """ Close this datatype.  If it's locked, nothing happens.
+
+            You shouldn't ordinarily need to call this function; datatype
+            objects are automatically closed when they're deallocated.
         """
         if not self._locked:
             H5Tclose(self.id)
@@ -436,11 +413,9 @@ cdef class TypeArrayID(TypeID):
         # Numpy translation function for array types
         cdef TypeID tmp_type
         tmp_type = self.get_super()
-        tmp_type.complex_names = self.complex_names
-        try:
-            base_dtype = tmp_type.py_dtype()
-        finally:
-            tmp_type.close()
+
+        base_dtype = tmp_type.py_dtype()
+
         shape = self.get_array_dims()
         return dtype( (base_dtype, shape) )
 
@@ -494,6 +469,42 @@ cdef class TypeStringID(TypeID):
         """
         return pybool(H5Tis_variable_str(self.id))
 
+    def get_cset(self):
+        """ () => INT character_set
+
+            Retrieve the character set used for a string.  Currently only
+            CSET_ASCII is supported.
+        """
+        return <int>H5Tget_cset(self.id)
+
+    def set_cset(self, int cset):
+        """ (INT character_set)
+
+            Set the character set used for a string.  Currently only
+            CSET_ASCII is supported.
+        """
+        H5Tset_cset(self.id, <H5T_cset_t>cset)
+
+    def get_strpad(self):
+        """ () => INT padding_type
+
+            Get the padding type.  Legal values are:
+             STR_NULLTERM:  NULL termination only (C style)
+             STR_NULLPAD:   Pad buffer with NULLs
+             STR_SPACEPAD:  Pad buffer with spaces (FORTRAN style)
+        """
+        return <int>H5Tget_strpad(self.id)
+
+    def set_strpad(self, int pad):
+        """ (INT pad)
+
+            Set the padding type.  Legal values are:
+             STR_NULLTERM:  NULL termination only (C style)
+             STR_NULLPAD:   Pad buffer with NULLs
+             STR_SPACEPAD:  Pad buffer with spaces (FORTRAN style)
+        """
+        H5Tset_strpad(self.id, <H5T_str_t>pad)
+
     cdef object py_dtype(self):
         # Numpy translation function for string types
         if self.is_variable_str():
@@ -833,16 +844,13 @@ cdef class TypeCompoundID(TypeCompositeID):
         # two separate arrays.
         for i from 0 <= i < nfields:
             tmp_type = self.get_member_type(i)
-            tmp_type.complex_names = self.complex_names
-            try:
-                field_names.append(self.get_member_name(i))
-                field_types.append(tmp_type.py_dtype())
-            finally:
-                tmp_type.close()
+            field_names.append(self.get_member_name(i))
+            field_types.append(tmp_type.py_dtype())
+
 
         # 1. Check if it should be converted to a complex number
         if len(field_names) == 2                     and \
-            tuple(field_names) == self.complex_names and \
+            tuple(field_names) == _complex_names     and \
             field_types[0] == field_types[1]         and \
             field_types[0].kind == 'f':
 
@@ -947,41 +955,56 @@ cdef class TypeEnumID(TypeCompositeID):
         return val
 
     cdef object py_dtype(self):
-        # Translation function for enum types; 
+        # Translation function for enum types
+
         cdef TypeID tmp_type
         tmp_type = self.get_super()
-        try:
-            typeobj = tmp_type.py_dtype()
-        finally:
-            tmp_type.close()
+        typeobj = tmp_type.py_dtype()
+
         return typeobj
 
 # === Python extension functions ==============================================
 
 
-def py_create(dtype dt not None, object complex_names=None, enum=None):
-    """ ( DTYPE dt, TUPLE complex_names=None, DICT enum=None) => TypeID
 
-        Given a Numpy dtype object, generate a byte-for-byte memory-compatible
-        HDF5 datatype object.  The result is guaranteed to be transient and
-        unlocked.
+def py_complex_names(object realname=None, object imgname=None, reset=False):
+    """ (STRING realname=None, STRING imgname=None, reset=False)
 
-        complex_names:
-            Specifies when and how to interpret Python complex numbers as
-            HDF5 compound datatypes.  May be None or a tuple with strings
-            (real name, img name).  "None" indicates the default mapping of
-            ("r", "i").
+        Set the real and imaginary strings used to translate complex
+        numbers to and from HDF5 compound types.
 
-            This option is applied recursively to subtypes of arrays and
-            compound types.  Additionally, these names are stored in the
-            returned HDF5 type object.
+        Both realname and imgname must be supplied, or both can be omitted to
+        disable conversion completely.  Call with reset=True to reset to the
+        default pair ('r','i').
+    """
+    global _complex_names
+    if not ((realname is None and imgname is None) or \
+            (isinstance(realname, str) and isinstance(imgname, str))):
+        raise ValueError("Realname and imgname must both be specified, or both omitted.")
+
+    if reset:
+        _complex_names = ('r','i')
+    else:
+        if realname is None:
+            _complex_names = None
+        else:
+            _complex_names = (realname, imgname)
+    
+def py_create(object dtype_in, enum=None):
+    """ ( OBJECT dt, DICT enum=None) => TypeID
+
+        Given a Numpy dtype object, generate a byte-for-byte memory-compatible
+        HDF5 datatype object.  The result is guaranteed to be transient and
+        unlocked. Argument dtype_in may be a dtype object, or anything which
+        can be converted to a dtype.
 
         enum:
-            A dictionary mapping names to integer values.  If the type being
-            converted is an integer (kind i/u), the resulting HDF5 type will
-            be an enumeration with that base type, and the given values.
-            Ignored for all other types.
+            A optional dictionary mapping names to integer values.  If the
+            type being converted is an integer (Numpy kind i/u), the resulting 
+            HDF5 type will be an enumeration with that base type, and the 
+            given values. Ignored for all other types.
     """
+    cdef dtype dt
     cdef TypeID otype
     cdef TypeID base
     cdef TypeID tmp
@@ -990,10 +1013,9 @@ def py_create(dtype dt not None, object complex_names=None, enum=None):
     cdef char byteorder
     cdef int length
 
+    dt = dtype(dtype_in)
     otype = None
 
-    _complex_names = ('r','i')
-
     kind = dt.kind
     byteorder = dt.byteorder
     length = int(dt.str[2:])  # is there a better way to do this?
@@ -1003,13 +1025,11 @@ def py_create(dtype dt not None, object complex_names=None, enum=None):
     # Void types with field names are considered to be compound
     if kind == c'V' and names is not None:
         otype = create(H5T_COMPOUND, length)
+
         for name in names:
             dt_tmp, offset = dt.fields[name]
-            tmp = py_create(dt_tmp, complex_names)
-            try:
-                otype.insert(name, offset, tmp)
-            finally:
-                tmp.close()
+            tmp = py_create(dt_tmp)
+            otype.insert(name, offset, tmp)
 
     # Enums may be created out of integer types
     elif (kind == c'u' or kind == c'i') and enum is not None:
@@ -1027,6 +1047,9 @@ def py_create(dtype dt not None, object complex_names=None, enum=None):
     # Complex numbers are stored as HDF5 structs, with names defined at runtime
     elif kind == c'c':
 
+        if _complex_names is None:
+            raise ValueError("Complex conversion is currently disabled.  Use py_complex_names to re-enable.")
+
         if length == 8:
             otype = typewrap(create_ieee_complex64(byteorder, _complex_names[0], _complex_names[1]))
         elif length == 16:
@@ -1038,12 +1061,11 @@ def py_create(dtype dt not None, object complex_names=None, enum=None):
     elif kind == c'V':
 
         if dt.subdtype:
+
             dt_tmp, shape = dt.subdtype
-            base = py_create(dt_tmp, complex_names)
-            try:
-                otype = array_create(base, shape)
-            finally:
-                base.close()
+            base = py_create(dt_tmp)
+            otype = array_create(base, shape)
+
         else:
             otype = create(H5T_OPAQUE, length)
                 
@@ -1055,9 +1077,6 @@ def py_create(dtype dt not None, object complex_names=None, enum=None):
     else:
         raise ValueError("No conversion path for dtype: %s" % repr(dt))
 
-    if complex_names is not None:
-        otype.complex_names = complex_names
-
     IF H5PY_DEBUG:
         import logging
         logger = logging.getLogger('h5py')
diff --git a/h5py/highlevel.py b/h5py/highlevel.py
index c363cbf..f180c60 100644
--- a/h5py/highlevel.py
+++ b/h5py/highlevel.py
@@ -11,10 +11,18 @@
 #-
 
 import numpy
+import inspect
 
 from h5py import h5, h5f, h5g, h5s, h5t, h5d, h5a, h5p, h5z, h5i
 from h5py.h5 import H5Error
 from utils_hl import slicer, hbasename
+from browse import _H5Browser
+
+try:
+    # For interactive File.browse() capability
+    import readline
+except ImportError:
+    readline = None
 
 class HLObject(object):
 
@@ -172,6 +180,8 @@ class File(Group):
     
         self._name = name
         self._mode = mode
+        self._path = None
+        self._rlhist = []  # for readline nonsense
 
     def close(self):
         """ Close this HDF5 file.  All open identifiers will become invalid.
@@ -189,6 +199,36 @@ class File(Group):
             return 'File "%s", root members: %s' % (self.name, ', '.join(['"%s"' % name for name in self]))
         return "Closed file (%s)" % self.name
 
+    def browse(self, dict=None):
+        """ Browse this file. If no import dict is provided, will seize the
+            caller's global dictionary.
+        """
+        if dict is None:
+            dict = inspect.currentframe().f_back.f_globals
+
+        def gethist():
+            rlhist = [readline.get_history_item(x) for x in xrange(readline.get_current_history_length())]
+            rlhist = [x for x in rlhist if x is not None]
+            return rlhist
+
+        # The following is an ugly hack to prevent readline from mixing the cmd
+        # session with IPython's session.  Is there a better way to do this?
+        hist = None
+        if readline:
+            hist = gethist()
+            readline.clear_history()
+            for x in self._rlhist:
+                readline.add_history(x)
+        try:
+            browser = _H5Browser(self, self._path, importdict=dict)
+        finally:
+            if hist:
+                self._rlhist.extend(gethist())
+                readline.clear_history()
+                for x in hist:
+                    readline.add_history(x)
+        self._path = browser.path
+
 class Dataset(HLObject):
 
     """ High-level interface to an HDF5 dataset
@@ -458,11 +498,6 @@ class Datatype(HLObject):
             return "Named datatype object (%s)" % str(self.dtype)
         return "Closed datatype object"
 
-# Browser component.  This is here to prevent issues with circular imports
-from browse import _H5Browser
-
-browse = _H5Browser()
-
 
 
 
diff --git a/h5py/tests/__init__.py b/h5py/tests/__init__.py
index 01372de..ccbd25a 100644
--- a/h5py/tests/__init__.py
+++ b/h5py/tests/__init__.py
@@ -14,13 +14,13 @@ import unittest
 import sys
 import test_h5a, test_h5d, test_h5f, \
        test_h5g, test_h5i, test_h5p, \
-       test_h5s, test_h5
+       test_h5s, test_h5t, test_h5
 
-from h5py import h5a, h5f, h5g, h5d, h5s, h5i, h5z, h5p
+from h5py import *
 
 TEST_CASES = (test_h5a.TestH5A, test_h5d.TestH5D, test_h5f.TestH5F, 
               test_h5g.TestH5G, test_h5i.TestH5I, test_h5p.TestH5P,
-              test_h5s.TestH5S, test_h5.TestH5)
+              test_h5s.TestH5S, test_h5t.TestH5T, test_h5.TestH5)
 
 def buildsuite(cases):
 
diff --git a/h5py/tests/common.py b/h5py/tests/common.py
index 7669bc3..bcb3192 100644
--- a/h5py/tests/common.py
+++ b/h5py/tests/common.py
@@ -38,7 +38,6 @@ class HCopy(object):
         plist = h5p.create(h5p.FILE_ACCESS)
         plist.set_fclose_degree(h5f.CLOSE_STRONG)
         self.fid = h5f.open(self.tmpname, h5f.ACC_RDWR)
-        plist.close()
         return self.fid
 
     def __exit__(self, *args):
diff --git a/h5py/tests/test_h5a.py b/h5py/tests/test_h5a.py
index 6b43d2e..28dd6c0 100644
--- a/h5py/tests/test_h5a.py
+++ b/h5py/tests/test_h5a.py
@@ -38,7 +38,7 @@ class TestH5A(unittest.TestCase):
         self.obj = h5g.open(self.fid, OBJECTNAME)
 
     def tearDown(self):
-        self.obj.close()
+        self.obj._close()
         self.fid.close()
 
     def is_attr(self, attr):
@@ -61,34 +61,28 @@ class TestH5A(unittest.TestCase):
                 self.assert_(self.is_attr(attr))
                 attr.write(arr_ref)
                 self.assertRaises(ValueError, attr.write, arr_fail)
-                attr.close()
 
                 attr = h5a.open_name(obj, name)
                 dt = attr.dtype
                 shape = attr.shape
                 arr_val = ndarray(shape, dtype=dt)
                 attr.read(arr_val)
-                attr.close()
                 self.assert_(all(arr_val == arr_ref), errstr(arr_val, arr_ref))
 
-            obj.close()
-
     def test_open_idx(self):
         for idx, name in enumerate(ATTRIBUTES_ORDER):
             attr = h5a.open_idx(self.obj, idx)
             self.assert_(self.is_attr(attr), "Open: index %d" % idx)
-            attr.close()
 
     def test_open_name(self):
         for name in ATTRIBUTES:
             attr = h5a.open_name(self.obj, name)
             self.assert_(self.is_attr(attr), 'Open: name "%s"' % name)
-            attr.close()
 
     def test_close(self):
         attr = h5a.open_idx(self.obj, 0)
         self.assert_(self.is_attr(attr))
-        attr.close()
+        attr._close()
         self.assert_(not self.is_attr(attr))
 
     def test_delete(self):
@@ -97,7 +91,7 @@ class TestH5A(unittest.TestCase):
 
             attr = h5a.open_name(obj, ATTRIBUTES_ORDER[0])
             self.assert_(self.is_attr(attr))
-            attr.close()
+            del attr
 
             h5a.delete(obj, ATTRIBUTES_ORDER[0])
             self.assertRaises(H5Error, h5a.open_name, obj, ATTRIBUTES_ORDER[0])
@@ -120,8 +114,6 @@ class TestH5A(unittest.TestCase):
             self.assert_( all(arr_holder == arr_reference),
                 errstr(arr_reference, arr_holder, 'Attr "%s"):\n' % name, ))
 
-            attr.close()
-
     # write is done by test_create_write
 
     # === Attribute inspection ================================================
diff --git a/h5py/tests/test_h5d.py b/h5py/tests/test_h5d.py
index 97bf3f9..96b721f 100644
--- a/h5py/tests/test_h5d.py
+++ b/h5py/tests/test_h5d.py
@@ -45,14 +45,14 @@ class TestH5D(unittest.TestCase):
         self.dset = h5d.open(self.fid, "CompoundChunked")
 
     def tearDown(self):
-        self.dset.close()
+        self.dset._close()
         self.fid.close()
 
     def test_open_close(self):
         with HCopy(HDFNAME) as fid:
             dset = h5d.open(fid, "CompoundChunked")
             self.assertEqual(h5i.get_type(dset), h5i.DATASET)
-            dset.close()
+            dset._close()
             self.assertEqual(h5i.get_type(dset), h5i.BADID)
 
     def test_read(self):
diff --git a/h5py/tests/test_h5g.py b/h5py/tests/test_h5g.py
index e280dcb..a0c6e74 100644
--- a/h5py/tests/test_h5g.py
+++ b/h5py/tests/test_h5g.py
@@ -33,7 +33,7 @@ class TestH5G(unittest.TestCase):
         self.obj = h5g.open(self.fid, OBJECTNAME)
 
     def tearDown(self):
-        self.obj.close()
+        self.obj._close()
         self.fid.close()
 
     def is_grp(self, item):
@@ -43,7 +43,7 @@ class TestH5G(unittest.TestCase):
         for name in TEST_GROUPS:
             grp = h5g.open(self.obj, name)
             self.assert_(self.is_grp(grp))
-            grp.close()
+            grp._close()
             self.assert_(not self.is_grp(grp))
         
         self.assertRaises(H5Error, h5g.open, self.obj, 'Some other group')
@@ -54,7 +54,7 @@ class TestH5G(unittest.TestCase):
 
             obj = h5g.open(fid, OBJECTNAME)
             grp = h5g.create(obj, 'New group')
-            grp.close()
+            grp._close()
             self.assert_(obj.py_exists('New group'))
 
     def test_link_unlink_move_linkval(self):
diff --git a/h5py/tests/test_h5i.py b/h5py/tests/test_h5i.py
index ab0a86f..0615384 100644
--- a/h5py/tests/test_h5i.py
+++ b/h5py/tests/test_h5i.py
@@ -26,7 +26,7 @@ class TestH5I(unittest.TestCase):
         self.obj = h5g.open(self.fid, OBJECTNAME)
 
     def tearDown(self):
-        self.obj.close()
+        self.obj._close()
         self.fid.close()
 
 
diff --git a/h5py/tests/test_h5s.py b/h5py/tests/test_h5s.py
index a9cb5b5..521f9b4 100644
--- a/h5py/tests/test_h5s.py
+++ b/h5py/tests/test_h5s.py
@@ -25,7 +25,7 @@ class TestH5S(unittest.TestCase):
     def test_create_close(self):
         sid = h5s.create(h5s.SCALAR)
         self.assertEqual(h5i.get_type(sid), h5i.DATASPACE)
-        sid.close()
+        sid._close()
         self.assertEqual(h5i.get_type(sid), h5i.BADID)
 
         self.assertRaises(H5Error, h5s.create, -1)

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