[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