[h5py] 147/455: Start nailing down API for upcoming release

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


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

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

commit 89285780f22d168dd7f0c1e39762ec5a4cf1df5a
Author: andrewcollette <andrew.collette at gmail.com>
Date:   Tue Oct 28 03:48:40 2008 +0000

    Start nailing down API for upcoming release
---
 h5py/h5o.pyx         |   2 +-
 h5py/highlevel.py    | 310 ++++++++++++++++++++++++++++-----------------------
 h5py/tests/common.py |   5 +-
 3 files changed, 170 insertions(+), 147 deletions(-)

diff --git a/h5py/h5o.pyx b/h5py/h5o.pyx
index 8575d63..182161a 100644
--- a/h5py/h5o.pyx
+++ b/h5py/h5o.pyx
@@ -291,7 +291,7 @@ def visit(ObjectID loc not None, object func, *,
     anything else aborts iteration and returns that value.  Keywords:
 
     BOOL info (False)
-        Callbask is func(STRING, Objinfo)
+        Callback is func(STRING, Objinfo)
 
     STRING obj_name (".")
         Visit a subgroup of "loc" instead
diff --git a/h5py/highlevel.py b/h5py/highlevel.py
index fe51aeb..2427052 100644
--- a/h5py/highlevel.py
+++ b/h5py/highlevel.py
@@ -54,7 +54,7 @@ import sys
 
 from h5py import h5, h5f, h5g, h5s, h5t, h5d, h5a, h5p, h5z, h5i
 from h5py.h5 import H5Error
-from utils_hl import slice_select, hbasename, strhdr, strlist, guess_chunk
+from utils_hl import slice_select, hbasename, guess_chunk
 from utils_hl import CoordsList
 from browse import _H5Browser
 
@@ -65,14 +65,7 @@ if config.API_18:
 __all__ = ["File", "Group", "Dataset",
            "Datatype", "AttributeManager", "CoordsList"]
 
-try:
-    # For interactive File.browse() capability
-    import readline
-    if not hasattr(readline, 'get_current_history_length'):
-        readline = None
-except ImportError:
-    readline = None
-
+# === Base classes ============================================================
 
 class LockableObject(object):
 
@@ -95,16 +88,60 @@ class HLObject(LockableObject):
         attrs:  HDF5 attributes of this object.  See the AttributeManager docs.
     """
 
-    name = property(lambda self: h5i.get_name(self.id),
-        doc = "Name of this object in the HDF5 file.  Not necessarily unique.")
-    attrs = property(lambda self: self._attrs,
-        doc = "Provides access to HDF5 attributes. See AttributeManager.")
+    @property
+    def name(self):
+        """Name of this object in the HDF5 file.  Not necessarily unique."""
+        return h5i.get_name(self.id)
+
+    @property
+    def attrs(self):
+        """Provides access to HDF5 attributes. See AttributeManager."""
+        return self._attrs
 
     def __nonzero__(self):
         return self.id.__nonzero__()
+"Numpy dtype equivalent for this datatype"
+class DictCompat(object):
 
+    """
+        Contains dictionary-style compatibility methods for groups and
+        attributes.
+    """
 
-class Group(HLObject):
+    def listnames(self):
+        """ Get a list containing member names """
+        with self._lock:
+            return list(self)
+
+    def iternames(self):
+        """ Get an iterator over member names """
+        with self._lock:
+            return iter(self)
+
+    def listobjects(self):
+        """ Get a list containing members """
+        with self._lock:
+            return [self[x] for x in self]
+
+    def iterobjects(self):
+        """ Get an iterator over members """
+        with self._lock:
+            for x in self:
+                yield self[x]
+
+    def listitems(self):
+        """ Get a list of tuples containing (name, object) pairs """
+        with self._lock:
+            return [(x, self[x]) for x in self]
+
+    def iteritems(self):
+        """ Get an iterator over (name, object) pairs """
+        with self._lock:
+            for x in self:
+                yield (x, self[x])
+
+
+class Group(HLObject, DictCompat):
 
     """ Represents an HDF5 group.
 
@@ -224,6 +261,18 @@ class Group(HLObject):
         """
         return Group(self, name, create=True)
 
+    def require_group(self, name):
+        """ Check if a group exists, and create it if not. Raise H5Error if
+            an incompatible object exists.
+        """
+        if not name in self:
+            return self.create_group(name)
+        else:
+            grp = self[name]
+            if not isinstance(grp, Group):
+                raise H5Error("Incompatible object (%s) already exists" % grp.__class__.__name__)
+            return grp
+
     def create_dataset(self, name, *args, **kwds):
         """ Create and return a new dataset, attached to this group.
 
@@ -250,57 +299,40 @@ class Group(HLObject):
         """
         return Dataset(self, name, *args, **kwds)
 
+    def require_dataset(self, name, shape, dtype, exact=False, **kwds):
+        """ Check if a dataset with compatible shape and dtype exists, and
+            create one if it doesn't.  Raises H5Error if an incompatible
+            dataset (or group) already exists.  
 
-    def desc(self):
-        """ Extended (multi-line) description of this group, as a string.
-        """
-        with self._lock:
-            outstr = 'Group "%s" in file "%s":' % \
-                    (hbasename(h5i.get_name(self.id)), os.path.basename(h5f.get_name(self.id)))
-            outstr = strhdr(outstr)
-            infodct = {"Members": len(self)}
-            grpinfo = h5g.get_objinfo(self.id, '.')
-            infodct["mtime"] = grpinfo.mtime
-            outstr += strlist([(name, infodct[name]) for name in ("Members", "mtime")])
-            
-            cmnt = self.id.get_comment('.')
-            if cmnt != '':
-                outstr += '\nComment:\n'+cmnt
-            return outstr
-
-    # Dictionary compatibility methods
+            By default, datatypes are compared for loss-of-precision only.
+            To require an exact match, set keyword "exact" to True.  Shapes
+            are always compared exactly.
 
-    def listnames(self):
-        """ Get a list containing member names """
-        with self._lock:
-            return list(self)
+            Keyword arguments are only used when creating a new dataset; they
+            are ignored if an dataset with matching shape and dtype already
+            exists.  See create_dataset for a list of legal keywords.
+        """
+        dtype = numpy.dtype(dtype)
 
-    def iternames(self):
-        """ Get an iterator over member names """
         with self._lock:
-            return iter(self)
+            if not name in self:
+                return self.create_dataset(name, *(shape, dtype), **kwds)
 
-    def listobjects(self):
-        """ Get a list containing member objects """
-        with self._lock:
-            return [self[x] for x in self]
+            dset = self[name]
+            if not isinstance(dset, Dataset):
+                raise H5Error("Incompatible object (%s) already exists" % dset.__class__.__name__)
 
-    def iterobjects(self):
-        """ Get an iterator over member objects """
-        with self._lock:
-            for x in self:
-                yield self[x]
+            if not shape == dset.shape:
+                raise H5Error("Shapes do not match (existing %s vs new %s)" % (dset.shape, shape))
 
-    def listitems(self):
-        """ Get a list of tuples containing (name, object) pairs """
-        with self._lock:
-            return [(x, self[x]) for x in self]
+            if exact:
+                if not dtype == dset.dtype:
+                    raise H5Error("Datatypes do not exactly match (existing %s vs new %s)" % (dset.dtype, dtype))
+            elif not numpy.can_cast(dtype, dset.dtype):
+                raise H5Error("Datatypes cannot be safely cast (existing %s vs new %s)" % (dset.dtype, dtype))
+            
+            return dset
 
-    def iteritems(self):
-        """ Get an iterator over (name, object) pairs """
-        with self._lock:
-            for x in self:
-                yield (x, self[x])
 
     # New 1.8.X methods
 
@@ -329,10 +361,7 @@ class Group(HLObject):
             raise NotImplementedError("This feature is only available with HDF5 1.8.0 and later")
     
         with self._lock:
-            def call_proxy(name, info):
-                return func(name)
-
-            return h5o.visit(self.id, call_proxy)
+            return h5o.visit(self.id, func)
 
     def visititems(self, func):
         """ Recursively visit names and objects in this group and subgroups.
@@ -363,17 +392,17 @@ class Group(HLObject):
             raise NotImplementedError("This feature is only available with HDF5 1.8.0 and later")
 
         with self._lock:
-            def call_proxy(name, info):
+            def call_proxy(name):
                 return func(name, self[name])
-
             return h5o.visit(self.id, call_proxy)
 
     def __repr__(self):
         with self._lock:
             try:
-                return 'Group "%s" (%d members)' % (hbasename(self.name), len(self))
-            except:
-                return "Invalid group"
+                return '<HDF5 group "%s" (%d members)>' % \
+                    (hbasename(self.name), len(self))
+            except Exception:
+                return "<Closed HDF5 group>"
 
 
 class File(Group):
@@ -394,7 +423,7 @@ class File(Group):
         like command history and tab completion.
 
         This object supports the Python context manager protocol, when used
-        in a "with" block:
+        in a "with" block::
 
             with File(...) as f:
                 ... do stuff with f...
@@ -404,10 +433,15 @@ class File(Group):
         exceptions raised. 
     """
 
-    name = property(lambda self: self._name,
-        doc = "File name on disk")
-    mode = property(lambda self: self._mode,
-        doc = "Python mode used to open file")
+    @property
+    def name(self):
+        """File name on disk"""
+        return self._name
+
+    @property
+    def mode(self):
+        """Python mode used to open file"""
+        return self._mode
 
     # --- Public interface (File) ---------------------------------------------
 
@@ -444,8 +478,6 @@ 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 objects will be invalidated.
@@ -470,44 +502,10 @@ class File(Group):
     def __repr__(self):
         with self._lock:
             try:
-                return 'File "%s", %d root members' % (self.name, len(self))
-            except:
-                return "Invalid file"
-
-    def browse(self, dict=None):
-        """ Open a command line shell to browse this file. If dict is not
-            specified, any imported objects will be placed in the caller's
-            global() dictionary.
-        """
-        if not self.id._valid:
-            print "Can't browse: this file is closed."
-            return
-
-        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()+1)]
-            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?
-        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 readline:
-                self._rlhist.extend(gethist())
-                readline.clear_history()
-                for x in hist:
-                    readline.add_history(x)
-        self._path = browser.path
-
+                return '<HDF5 file "%s" (mode %s, %d root members)>' % \
+                    (os.path.basename(self.name), self.mode, len(self))
+            except Exception:
+                return "<Closed HDF5 file>"
 
 class Dataset(HLObject):
 
@@ -520,29 +518,58 @@ class Dataset(HLObject):
         more than one), is supported.  The object returned is always a
         Numpy ndarray.
 
-        Additionally, the following properties are provided:
+        Among others, the following properties are provided:
           shape:    Numpy-style shape tuple of dimsensions
           dtype:    Numpy dtype representing the datatype
           value:    Copy of the full dataset, as either a Numpy array or a
                      Numpy/Python scalar, depending on the shape.
     """
 
-    shape = property(lambda self: self.id.shape,
-        doc = "Numpy-style shape tuple giving dataset dimensions")
+    @property
+    def shape(self):
+        """Numpy-style shape tuple giving dataset dimensions"""
+        return self.id.shape
 
-    dtype = property(lambda self: self.id.dtype,
-        doc = "Numpy dtype representing the datatype")
+    @property
+    def dtype(self):
+        """Numpy dtype representing the datatype"""
+        return self.id.dtype
 
-    def _getval(self):
+    @property
+    def value(self):
+        """The entire dataset, as an array or scalar depending on the shape"""
         with self._lock:
             arr = self[...]
             if arr.shape == ():
                 return numpy.asscalar(arr)
             return arr
 
-    value = property(_getval,
-        doc = "The entire dataset, as an array or scalar depending on the shape.")
-
+    @property
+    def chunks(self):
+        """Dataset chunks (or None)"""
+        try:
+            return self._plist.get_chunk()
+        except H5Error:
+            return None
+
+    @property
+    def compression(self):
+        """Compression level (or None)"""
+        filt = self._plist.get_filter_by_id(h5z.FILTER_DEFLATE)
+        if filt is not None:
+            return filt[1]
+        return None
+
+    @property
+    def shuffle(self):
+        """Shuffle filter present (T/F)"""
+        return self._plist.get_filter_by_id(h5z.FILTER_SHUFFLE) is not None
+
+    @property
+    def fletcher32(self):
+        """Fletcher32 filter is present (T/F)"""
+        return self._plist.get_filter_by_id(h5z.FILTER_FLETCHER32) is not None
+        
     def __init__(self, group, name,
                     shape=None, dtype=None, data=None,
                     chunks=None, compression=None, shuffle=False,
@@ -639,6 +666,7 @@ class Dataset(HLObject):
                     self.id.write(h5s.ALL, h5s.ALL, data)
 
             self._attrs = AttributeManager(self)
+            self._plist = self.id.get_create_plist()
 
     def extend(self, shape):
         """ Resize the dataset so it's at least as big as "shape".
@@ -659,7 +687,11 @@ class Dataset(HLObject):
         return size
 
     def len(self):
-        """ The size of the first axis.  TypeError if scalar. """
+        """ The size of the first axis.  TypeError if scalar. 
+
+            Use of this method is preferred to len(dset), as Python's built-in
+            len() cannot handle values greater then 2**32 on 32-bit systems.
+        """
         shape = self.shape
         if len(shape) == 0:
             raise TypeError("Attempt to take len() of scalar dataset")
@@ -763,12 +795,12 @@ class Dataset(HLObject):
     def __repr__(self):
         with self._lock:
             try:
-                return 'Dataset "%s": %s %s' % \
+                return '<HDF5 dataset "%s": shape %s, type "%s">' % \
                     (hbasename(self.name), self.shape, self.dtype.str)
-            except:
-                return "Invalid dataset"
+            except Exception:
+                return "<Closed HDF5 dataset>"
 
-class AttributeManager(LockableObject):
+class AttributeManager(LockableObject, DictCompat):
 
     """ Allows dictionary-style access to an HDF5 object's attributes.
 
@@ -793,9 +825,6 @@ class AttributeManager(LockableObject):
         understand.
     """
 
-    names = property(lambda self: tuple(self),
-        doc = "Tuple of attribute names")
-
     def __init__(self, parent):
         """ Private constructor; you should not create these.
         """
@@ -857,12 +886,6 @@ class AttributeManager(LockableObject):
             for name in attrlist:
                 yield name
 
-    def iteritems(self):
-        """ Iterate over (name, value) tuples. """
-        with self._lock:
-            for name in self:
-                yield (name, self[name])
-
     def __contains__(self, name):
         """ Determine if an attribute exists, by name. """
         return h5a.exists(self.id, name)
@@ -870,10 +893,10 @@ class AttributeManager(LockableObject):
     def __repr__(self):
         with self._lock:
             try:
-                return 'Attributes of "%s" (%d)' % \
+                return '<Attributes of HDF5 object "%s" (%d)>' % \
                     (hbasename(h5i.get_name(self.id)), len(self))
-            except:
-                return "Invalid attributes object"
+            except Exception:
+                return "<Attributes of closed HDF5 object>"
 
 
 class Datatype(HLObject):
@@ -893,8 +916,10 @@ class Datatype(HLObject):
             * will create hard link to an existing type
     """
 
-    dtype = property(lambda self: self.id.dtype,
-        doc = "Numpy dtype equivalent for this datatype")
+    @property
+    def dtype(self):
+        """Numpy dtype equivalent for this datatype"""
+        return self.id.dtype
 
     def __init__(self, grp, name):
         """ Private constructor; you should not create these.
@@ -906,9 +931,10 @@ class Datatype(HLObject):
     def __repr__(self):
         with self._lock:
             try:
-                return "Named datatype object (%s)" % self.dtype.str
-            except:
-                return "Invalid datatype object"
+                return '<HDF5 named type "%s" (dtype %s)>' % \
+                    (hbasename(self.name), self.dtype.str)
+            except Exception:
+                return "<Closed HDF5 named type>"
 
 
 
diff --git a/h5py/tests/common.py b/h5py/tests/common.py
index c8281ae..4f7dbc4 100644
--- a/h5py/tests/common.py
+++ b/h5py/tests/common.py
@@ -33,10 +33,7 @@ def api_18(func):
 class HDF5TestCase(unittest.TestCase):
 
     """
-        Base test for unit test classes which deal with a particular
-        HDF5 file.  These should declare a class-level attribute HDFNAME
-        representing the appropriate file.  This should be a basename; the
-        TestBase class will figure out the correct directory.
+        Base test for unit test classes.
     """
 
     h5py_verbosity = 0

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